"""CPU functionality."""

import sys

class CPU:
    """Main CPU class."""

    def __init__(self):
        """Construct a new CPU."""
        self.ram = [0x0] * 256         # 256b of ram
        self.reg = [0x0] * 8           # 8 registers
        self.reg[7] = 0xF4             # Stack pointer
                                  # R5 is reserved as the interrupt mask (IM)
                                  # R6 is reserved as the interrupt status (IS)
                                  # R7 is reserved as the stack pointer (SP)

        # Internal Registers
        self.pc = self.ir = self.mar = self.mdr = self.fl = 0x0
        # PC: Program Counter, address of the currently executing instruction
        # IR: Instruction Register, contains a copy of the currently executing instruction
        # MAR: Memory Address Register, holds the memory address we're reading or writing
        # MDR: Memory Data Register, holds the value to write or the value just read
        # FL: Flags, see below

        # FL bits: 00000LGE

            # L Less-than: during a CMP, set to 1 if registerA is less than registerB, zero otherwise.
            # G Greater-than: during a CMP, set to 1 if registerA is greater than registerB, zero otherwise.
            # E Equal: during a CMP, set to 1 if registerA is equal to registerB, zero otherwise.

    def load(self):
        program = []
        
        """ Read program from file. """        

        if len(sys.argv) != 2:
            print("ERROR: system arguments must be equal to 2!")
            exit()

        with open(sys.argv[1]) as f:
            line = f.readline()

            while line != "":
                if line[0] == '0' or line[0] == '1':
                    program.append(int(line[:8], 2))
                line = f.readline()

        if len(program) > 256 or len(program) == 0:
            print("ERROR: Program len invalid")
            exit()

        """Load a program into memory."""

        address = 0

        for instruction in program:
            self.ram[address] = instruction
            address += 1


    def alu(self, op, reg_a, reg_b):
        """ALU operations."""

        if op == "ADD":
            self.reg[reg_a] += self.reg[reg_b]

        elif op == "AND":
            self.reg[reg_a] &= self.reg[reg_b]

        elif op == "CMP":
            valA = self.reg[reg_a]
            valB = self.reg[reg_b]

            # Clear FL bits
            self.fl = 0x0
            #fl_bin_string = bin(self.fl)[2:].zfill(8)
            fl_bin_list = ["0"] * 8

            if valA == valB:
                # Set E flag to 1
                fl_bin_list[-1] = "1"
            else:
                # set E flag to 0
                fl_bin_list[-1] = "0"

            if valA < valB:
                # Set L flag to 1
                fl_bin_list[-3] = "1"
            else:
                # Set L flag to 0
                fl_bin_list[-3] = "0"
            
            if valA > valB:
                # Set G flag to 1
                fl_bin_list[-2] = "1"
            else:
                # Set G flag to 0
                fl_bin_list[-2] = "0"

            fl_bin_string = "".join(fl_bin_list)

            # Convert fl_bin_string into int format and store the result in fl
            self.fl = int(fl_bin_string, 2)
        
        elif op == "DEC":
            self.reg[reg_a] -= 1
        
        elif op == "DIV":
            # Catch any divide-by-zero attempts
            if self.reg[reg_b] == 0:
                print("ERROR: Cannot DIVide by 0")
                exit()

            self.reg[reg_a] /= self.reg[reg_b]

        elif op == "INC":
            self.reg[reg_a] += 1
        
        elif op == "SUB":
            self.reg[reg_a] -= self.reg[reg_b]
        
        elif op == "MOD":
            # Catch any modulo-by-zero attempts
            if self.reg[reg_b] == 0:
                print("ERROR: Cannot MODulo by 0")
                exit()

            self.reg[reg_a] %= self.reg[reg_b]

        elif op == "MUL":
            self.reg[reg_a] *= self.reg[reg_b]

        elif op == "NOT":
            # Python ~ operator doesn't work for this, XOR using a bitmask for NOT effect
            self.reg[reg_a] ^= 0b11111111

        elif op == "OR":
            self.reg[reg_a] |= self.reg[reg_b]

        elif op == "SHL":
            self.reg[reg_a] = self.reg[reg_a] << self.reg[reg_b]
        
        elif op == "SHR":
            self.reg[reg_a] = self.reg[reg_b] >> self.reg[reg_b]

        elif op == "SUB":
            self.reg[reg_a] -= self.reg[reg_b]

        elif op == "XOR":
            self.reg[reg_a] ^= self.reg[reg_b]

        else:
            raise Exception("Unsupported ALU operation")
        
        # & 0xFF register
        self.reg[reg_a] &= 0xFF

    def trace(self):
        """
        Handy function to print out the CPU state. You might want to call this
        from run() if you need help debugging.
        """

        print(f"TRACE: %02X | %02X %02X %02X |" % (
            self.pc,
            #self.fl,
            #self.ie,
            self.ram_read(self.pc),
            self.ram_read(self.pc + 1),
            self.ram_read(self.pc + 2)
        ), end='')

        for i in range(8):
            print(" %02X" % self.reg[i], end='')

        print()

    def ram_read(self, addr):        # NOTE: ram methods do not bounds check
        self.mar = addr
        self.mdr = self.ram[addr]
        return self.mdr
    
    def ram_write(addr, value):
        if (value & 0xFF) != value:
            print('WARNING: ram_write() called with value > 255')

        self.mar = addr
        self.mdr = value
        self.ram[addr] = value

    def run(self):
        """Run the CPU."""
        while 1:
            # It needs to read the memory address that's stored in register PC, and store that result in IR, the Instruction Register
            # (Fetch instruction from RAM)
            self.ir = self.ram_read(self.pc)

            # Some instructions requires up to the next two bytes of data after the PC in memory to perform operations on. Sometimes the byte value is a register number, other times it's a constant value (in the case of LDI). Using ram_read(), read the bytes at PC+1 and PC+2 from RAM into variables operand_a and operand_b in case the instruction needs them.
            # NOTE: pc > 253 will attempt to have one or more operands read OOB data
            operand_a = self.ram_read(self.pc+1)
            operand_b = self.ram_read(self.pc+2)

            # Then, depending on the value of the opcode, perform the actions needed for the instruction per the LS-8 spec.

            # Decode instruction

            # Convert ir to binary:
            bin_ir =        bin(self.ir)[2:].zfill(8) 

            # Decode binary rep
            operandCount =  int(bin_ir[:2], 2)
            isALU =         int(bin_ir[2], 2)
            setsPC =        int(bin_ir[3], 2)
            identifer =     int(bin_ir[4:], 2)

            # Execute instruction

            # Maps opcode string to int representation
            t = {
                    0xA0: 'ADD',
                    0xA8: 'AND',
                    0x50: 'CALL',
                    0xA7: 'CMP',
                    0x66: 'DEC',
                    0xA3: 'DIV',
                    0x01: 'HLT',
                    0x65: 'INC',
                    0x52: 'INT',
                    0x13: 'IRET',
                    0x55: 'JEQ',
                    0x5A: 'JGE',
                    0x57: 'JGT',
                    0x59: 'JLE',
                    0x58: 'JLT',
                    0x54: 'JMP',
                    0x56: 'JNE',
                    0x83: 'LD',
                    0x82: 'LDI',
                    0xA4: 'MOD',
                    0xA2: 'MUL',
                    0x00: 'NOP',
                    0x69: 'NOT',
                    0xAA: 'OR',
                    0x46: 'POP',
                    0x48: 'PRA',
                    0x47: 'PRN',
                    0x45: 'PUSH',
                    0x11: 'RET',
                    0xAC: 'SHL',
                    0xAD: 'SHR',
                    0x84: 'ST',
                    0xA1: 'SUB',
                    0xAB: 'XOR',
                    0x55: 'JEQ',
                    }        

            # print('\t',t[self.ir])
           
            # Run ALU operation if applicable
            if isALU == 1: 
                self.alu(t[self.ir], operand_a, operand_b)
            else:
                # Non-ALU opcodes
                if t[self.ir] == 'HLT':
                    exit()

                if t[self.ir] == 'LD':
                    self.reg[operand_a] = self.ram[self.reg[operand_b]]

                elif t[self.ir] == 'LDI':
                    self.reg[operand_a] = (operand_b & 0xFF)

                elif t[self.ir] == 'PRN':
                    print(self.reg[operand_a])
        
                elif t[self.ir] == 'PUSH':
                    self.reg[7] -= 1
                    self.ram[self.reg[7]] = self.reg[operand_a]
                
                elif t[self.ir] == 'POP':
                    self.reg[operand_a] = self.ram[self.reg[7]]
                    self.reg[7] += 1
                
                elif t[self.ir] == 'CALL':
                    self.reg[7] -= 1
                    self.ram[self.reg[7]] = self.pc + 2  # Save next instruction on stack
                    self.pc = self.reg[operand_a] # Set PC to register A's val

                elif t[self.ir] == 'RET':
                    self.pc = self.ram[self.reg[7]]
                    self.reg[7] += 1
                
                elif t[self.ir] == 'ST':
                    self.reg[operand_a] = self.reg[operand_b]
                
                elif t[self.ir] == 'JEQ':
                    if self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0

                elif t[self.ir] == 'JGE':
                    if self.fl == 2 or self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0

                elif t[self.ir] == 'JGT':
                    if self.fl == 2:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0

                elif t[self.ir] == 'JLE':
                    if self.fl == 4 or self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0

                elif t[self.ir] == 'JLT':
                    if self.fl == 4:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0

                elif t[self.ir] == 'JMP':
                    self.pc = self.reg[operand_a]

                elif t[self.ir] == 'JNE':
                    if self.fl != 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                
                elif t[self.ir] == 'PRA':
                    print(chr(self.reg[operand_a]), end='')

                else:
                    print("WARNING: Unsupported opcode: 0x%02x" % self.ir)
                    exit()

            
            # Set Program Counter
            if setsPC == 0:
                self.pc += operandCount + 1

            # Stack overflow check
            if self.reg[7] == self.pc+1:
                print("ERROR: Stack overflow")
                exit()

f1edb8405de9bcdd3808e9638fb3563b6d4c32f4
<mask token>


class CPU:
    <mask token>

    def __init__(self):
        """Construct a new CPU."""
        self.ram = [0] * 256
        self.reg = [0] * 8
        self.reg[7] = 244
        self.pc = self.ir = self.mar = self.mdr = self.fl = 0
    <mask token>

    def alu(self, op, reg_a, reg_b):
        """ALU operations."""
        if op == 'ADD':
            self.reg[reg_a] += self.reg[reg_b]
        elif op == 'AND':
            self.reg[reg_a] &= self.reg[reg_b]
        elif op == 'CMP':
            valA = self.reg[reg_a]
            valB = self.reg[reg_b]
            self.fl = 0
            fl_bin_list = ['0'] * 8
            if valA == valB:
                fl_bin_list[-1] = '1'
            else:
                fl_bin_list[-1] = '0'
            if valA < valB:
                fl_bin_list[-3] = '1'
            else:
                fl_bin_list[-3] = '0'
            if valA > valB:
                fl_bin_list[-2] = '1'
            else:
                fl_bin_list[-2] = '0'
            fl_bin_string = ''.join(fl_bin_list)
            self.fl = int(fl_bin_string, 2)
        elif op == 'DEC':
            self.reg[reg_a] -= 1
        elif op == 'DIV':
            if self.reg[reg_b] == 0:
                print('ERROR: Cannot DIVide by 0')
                exit()
            self.reg[reg_a] /= self.reg[reg_b]
        elif op == 'INC':
            self.reg[reg_a] += 1
        elif op == 'SUB':
            self.reg[reg_a] -= self.reg[reg_b]
        elif op == 'MOD':
            if self.reg[reg_b] == 0:
                print('ERROR: Cannot MODulo by 0')
                exit()
            self.reg[reg_a] %= self.reg[reg_b]
        elif op == 'MUL':
            self.reg[reg_a] *= self.reg[reg_b]
        elif op == 'NOT':
            self.reg[reg_a] ^= 255
        elif op == 'OR':
            self.reg[reg_a] |= self.reg[reg_b]
        elif op == 'SHL':
            self.reg[reg_a] = self.reg[reg_a] << self.reg[reg_b]
        elif op == 'SHR':
            self.reg[reg_a] = self.reg[reg_b] >> self.reg[reg_b]
        elif op == 'SUB':
            self.reg[reg_a] -= self.reg[reg_b]
        elif op == 'XOR':
            self.reg[reg_a] ^= self.reg[reg_b]
        else:
            raise Exception('Unsupported ALU operation')
        self.reg[reg_a] &= 255

    def trace(self):
        """
        Handy function to print out the CPU state. You might want to call this
        from run() if you need help debugging.
        """
        print(f'TRACE: %02X | %02X %02X %02X |' % (self.pc, self.ram_read(
            self.pc), self.ram_read(self.pc + 1), self.ram_read(self.pc + 2
            )), end='')
        for i in range(8):
            print(' %02X' % self.reg[i], end='')
        print()
    <mask token>

    def ram_write(addr, value):
        if value & 255 != value:
            print('WARNING: ram_write() called with value > 255')
        self.mar = addr
        self.mdr = value
        self.ram[addr] = value
    <mask token>

<mask token>


class CPU:
    <mask token>

    def __init__(self):
        """Construct a new CPU."""
        self.ram = [0] * 256
        self.reg = [0] * 8
        self.reg[7] = 244
        self.pc = self.ir = self.mar = self.mdr = self.fl = 0

    def load(self):
        program = []
        """ Read program from file. """
        if len(sys.argv) != 2:
            print('ERROR: system arguments must be equal to 2!')
            exit()
        with open(sys.argv[1]) as f:
            line = f.readline()
            while line != '':
                if line[0] == '0' or line[0] == '1':
                    program.append(int(line[:8], 2))
                line = f.readline()
        if len(program) > 256 or len(program) == 0:
            print('ERROR: Program len invalid')
            exit()
        """Load a program into memory."""
        address = 0
        for instruction in program:
            self.ram[address] = instruction
            address += 1

    def alu(self, op, reg_a, reg_b):
        """ALU operations."""
        if op == 'ADD':
            self.reg[reg_a] += self.reg[reg_b]
        elif op == 'AND':
            self.reg[reg_a] &= self.reg[reg_b]
        elif op == 'CMP':
            valA = self.reg[reg_a]
            valB = self.reg[reg_b]
            self.fl = 0
            fl_bin_list = ['0'] * 8
            if valA == valB:
                fl_bin_list[-1] = '1'
            else:
                fl_bin_list[-1] = '0'
            if valA < valB:
                fl_bin_list[-3] = '1'
            else:
                fl_bin_list[-3] = '0'
            if valA > valB:
                fl_bin_list[-2] = '1'
            else:
                fl_bin_list[-2] = '0'
            fl_bin_string = ''.join(fl_bin_list)
            self.fl = int(fl_bin_string, 2)
        elif op == 'DEC':
            self.reg[reg_a] -= 1
        elif op == 'DIV':
            if self.reg[reg_b] == 0:
                print('ERROR: Cannot DIVide by 0')
                exit()
            self.reg[reg_a] /= self.reg[reg_b]
        elif op == 'INC':
            self.reg[reg_a] += 1
        elif op == 'SUB':
            self.reg[reg_a] -= self.reg[reg_b]
        elif op == 'MOD':
            if self.reg[reg_b] == 0:
                print('ERROR: Cannot MODulo by 0')
                exit()
            self.reg[reg_a] %= self.reg[reg_b]
        elif op == 'MUL':
            self.reg[reg_a] *= self.reg[reg_b]
        elif op == 'NOT':
            self.reg[reg_a] ^= 255
        elif op == 'OR':
            self.reg[reg_a] |= self.reg[reg_b]
        elif op == 'SHL':
            self.reg[reg_a] = self.reg[reg_a] << self.reg[reg_b]
        elif op == 'SHR':
            self.reg[reg_a] = self.reg[reg_b] >> self.reg[reg_b]
        elif op == 'SUB':
            self.reg[reg_a] -= self.reg[reg_b]
        elif op == 'XOR':
            self.reg[reg_a] ^= self.reg[reg_b]
        else:
            raise Exception('Unsupported ALU operation')
        self.reg[reg_a] &= 255

    def trace(self):
        """
        Handy function to print out the CPU state. You might want to call this
        from run() if you need help debugging.
        """
        print(f'TRACE: %02X | %02X %02X %02X |' % (self.pc, self.ram_read(
            self.pc), self.ram_read(self.pc + 1), self.ram_read(self.pc + 2
            )), end='')
        for i in range(8):
            print(' %02X' % self.reg[i], end='')
        print()

    def ram_read(self, addr):
        self.mar = addr
        self.mdr = self.ram[addr]
        return self.mdr

    def ram_write(addr, value):
        if value & 255 != value:
            print('WARNING: ram_write() called with value > 255')
        self.mar = addr
        self.mdr = value
        self.ram[addr] = value

    def run(self):
        """Run the CPU."""
        while 1:
            self.ir = self.ram_read(self.pc)
            operand_a = self.ram_read(self.pc + 1)
            operand_b = self.ram_read(self.pc + 2)
            bin_ir = bin(self.ir)[2:].zfill(8)
            operandCount = int(bin_ir[:2], 2)
            isALU = int(bin_ir[2], 2)
            setsPC = int(bin_ir[3], 2)
            identifer = int(bin_ir[4:], 2)
            t = {(160): 'ADD', (168): 'AND', (80): 'CALL', (167): 'CMP', (
                102): 'DEC', (163): 'DIV', (1): 'HLT', (101): 'INC', (82):
                'INT', (19): 'IRET', (85): 'JEQ', (90): 'JGE', (87): 'JGT',
                (89): 'JLE', (88): 'JLT', (84): 'JMP', (86): 'JNE', (131):
                'LD', (130): 'LDI', (164): 'MOD', (162): 'MUL', (0): 'NOP',
                (105): 'NOT', (170): 'OR', (70): 'POP', (72): 'PRA', (71):
                'PRN', (69): 'PUSH', (17): 'RET', (172): 'SHL', (173):
                'SHR', (132): 'ST', (161): 'SUB', (171): 'XOR', (85): 'JEQ'}
            if isALU == 1:
                self.alu(t[self.ir], operand_a, operand_b)
            else:
                if t[self.ir] == 'HLT':
                    exit()
                if t[self.ir] == 'LD':
                    self.reg[operand_a] = self.ram[self.reg[operand_b]]
                elif t[self.ir] == 'LDI':
                    self.reg[operand_a] = operand_b & 255
                elif t[self.ir] == 'PRN':
                    print(self.reg[operand_a])
                elif t[self.ir] == 'PUSH':
                    self.reg[7] -= 1
                    self.ram[self.reg[7]] = self.reg[operand_a]
                elif t[self.ir] == 'POP':
                    self.reg[operand_a] = self.ram[self.reg[7]]
                    self.reg[7] += 1
                elif t[self.ir] == 'CALL':
                    self.reg[7] -= 1
                    self.ram[self.reg[7]] = self.pc + 2
                    self.pc = self.reg[operand_a]
                elif t[self.ir] == 'RET':
                    self.pc = self.ram[self.reg[7]]
                    self.reg[7] += 1
                elif t[self.ir] == 'ST':
                    self.reg[operand_a] = self.reg[operand_b]
                elif t[self.ir] == 'JEQ':
                    if self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JGE':
                    if self.fl == 2 or self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JGT':
                    if self.fl == 2:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JLE':
                    if self.fl == 4 or self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JLT':
                    if self.fl == 4:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JMP':
                    self.pc = self.reg[operand_a]
                elif t[self.ir] == 'JNE':
                    if self.fl != 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'PRA':
                    print(chr(self.reg[operand_a]), end='')
                else:
                    print('WARNING: Unsupported opcode: 0x%02x' % self.ir)
                    exit()
            if setsPC == 0:
                self.pc += operandCount + 1
            if self.reg[7] == self.pc + 1:
                print('ERROR: Stack overflow')
                exit()

<mask token>


class CPU:
    """Main CPU class."""

    def __init__(self):
        """Construct a new CPU."""
        self.ram = [0] * 256
        self.reg = [0] * 8
        self.reg[7] = 244
        self.pc = self.ir = self.mar = self.mdr = self.fl = 0

    def load(self):
        program = []
        """ Read program from file. """
        if len(sys.argv) != 2:
            print('ERROR: system arguments must be equal to 2!')
            exit()
        with open(sys.argv[1]) as f:
            line = f.readline()
            while line != '':
                if line[0] == '0' or line[0] == '1':
                    program.append(int(line[:8], 2))
                line = f.readline()
        if len(program) > 256 or len(program) == 0:
            print('ERROR: Program len invalid')
            exit()
        """Load a program into memory."""
        address = 0
        for instruction in program:
            self.ram[address] = instruction
            address += 1

    def alu(self, op, reg_a, reg_b):
        """ALU operations."""
        if op == 'ADD':
            self.reg[reg_a] += self.reg[reg_b]
        elif op == 'AND':
            self.reg[reg_a] &= self.reg[reg_b]
        elif op == 'CMP':
            valA = self.reg[reg_a]
            valB = self.reg[reg_b]
            self.fl = 0
            fl_bin_list = ['0'] * 8
            if valA == valB:
                fl_bin_list[-1] = '1'
            else:
                fl_bin_list[-1] = '0'
            if valA < valB:
                fl_bin_list[-3] = '1'
            else:
                fl_bin_list[-3] = '0'
            if valA > valB:
                fl_bin_list[-2] = '1'
            else:
                fl_bin_list[-2] = '0'
            fl_bin_string = ''.join(fl_bin_list)
            self.fl = int(fl_bin_string, 2)
        elif op == 'DEC':
            self.reg[reg_a] -= 1
        elif op == 'DIV':
            if self.reg[reg_b] == 0:
                print('ERROR: Cannot DIVide by 0')
                exit()
            self.reg[reg_a] /= self.reg[reg_b]
        elif op == 'INC':
            self.reg[reg_a] += 1
        elif op == 'SUB':
            self.reg[reg_a] -= self.reg[reg_b]
        elif op == 'MOD':
            if self.reg[reg_b] == 0:
                print('ERROR: Cannot MODulo by 0')
                exit()
            self.reg[reg_a] %= self.reg[reg_b]
        elif op == 'MUL':
            self.reg[reg_a] *= self.reg[reg_b]
        elif op == 'NOT':
            self.reg[reg_a] ^= 255
        elif op == 'OR':
            self.reg[reg_a] |= self.reg[reg_b]
        elif op == 'SHL':
            self.reg[reg_a] = self.reg[reg_a] << self.reg[reg_b]
        elif op == 'SHR':
            self.reg[reg_a] = self.reg[reg_b] >> self.reg[reg_b]
        elif op == 'SUB':
            self.reg[reg_a] -= self.reg[reg_b]
        elif op == 'XOR':
            self.reg[reg_a] ^= self.reg[reg_b]
        else:
            raise Exception('Unsupported ALU operation')
        self.reg[reg_a] &= 255

    def trace(self):
        """
        Handy function to print out the CPU state. You might want to call this
        from run() if you need help debugging.
        """
        print(f'TRACE: %02X | %02X %02X %02X |' % (self.pc, self.ram_read(
            self.pc), self.ram_read(self.pc + 1), self.ram_read(self.pc + 2
            )), end='')
        for i in range(8):
            print(' %02X' % self.reg[i], end='')
        print()

    def ram_read(self, addr):
        self.mar = addr
        self.mdr = self.ram[addr]
        return self.mdr

    def ram_write(addr, value):
        if value & 255 != value:
            print('WARNING: ram_write() called with value > 255')
        self.mar = addr
        self.mdr = value
        self.ram[addr] = value

    def run(self):
        """Run the CPU."""
        while 1:
            self.ir = self.ram_read(self.pc)
            operand_a = self.ram_read(self.pc + 1)
            operand_b = self.ram_read(self.pc + 2)
            bin_ir = bin(self.ir)[2:].zfill(8)
            operandCount = int(bin_ir[:2], 2)
            isALU = int(bin_ir[2], 2)
            setsPC = int(bin_ir[3], 2)
            identifer = int(bin_ir[4:], 2)
            t = {(160): 'ADD', (168): 'AND', (80): 'CALL', (167): 'CMP', (
                102): 'DEC', (163): 'DIV', (1): 'HLT', (101): 'INC', (82):
                'INT', (19): 'IRET', (85): 'JEQ', (90): 'JGE', (87): 'JGT',
                (89): 'JLE', (88): 'JLT', (84): 'JMP', (86): 'JNE', (131):
                'LD', (130): 'LDI', (164): 'MOD', (162): 'MUL', (0): 'NOP',
                (105): 'NOT', (170): 'OR', (70): 'POP', (72): 'PRA', (71):
                'PRN', (69): 'PUSH', (17): 'RET', (172): 'SHL', (173):
                'SHR', (132): 'ST', (161): 'SUB', (171): 'XOR', (85): 'JEQ'}
            if isALU == 1:
                self.alu(t[self.ir], operand_a, operand_b)
            else:
                if t[self.ir] == 'HLT':
                    exit()
                if t[self.ir] == 'LD':
                    self.reg[operand_a] = self.ram[self.reg[operand_b]]
                elif t[self.ir] == 'LDI':
                    self.reg[operand_a] = operand_b & 255
                elif t[self.ir] == 'PRN':
                    print(self.reg[operand_a])
                elif t[self.ir] == 'PUSH':
                    self.reg[7] -= 1
                    self.ram[self.reg[7]] = self.reg[operand_a]
                elif t[self.ir] == 'POP':
                    self.reg[operand_a] = self.ram[self.reg[7]]
                    self.reg[7] += 1
                elif t[self.ir] == 'CALL':
                    self.reg[7] -= 1
                    self.ram[self.reg[7]] = self.pc + 2
                    self.pc = self.reg[operand_a]
                elif t[self.ir] == 'RET':
                    self.pc = self.ram[self.reg[7]]
                    self.reg[7] += 1
                elif t[self.ir] == 'ST':
                    self.reg[operand_a] = self.reg[operand_b]
                elif t[self.ir] == 'JEQ':
                    if self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JGE':
                    if self.fl == 2 or self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JGT':
                    if self.fl == 2:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JLE':
                    if self.fl == 4 or self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JLT':
                    if self.fl == 4:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JMP':
                    self.pc = self.reg[operand_a]
                elif t[self.ir] == 'JNE':
                    if self.fl != 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'PRA':
                    print(chr(self.reg[operand_a]), end='')
                else:
                    print('WARNING: Unsupported opcode: 0x%02x' % self.ir)
                    exit()
            if setsPC == 0:
                self.pc += operandCount + 1
            if self.reg[7] == self.pc + 1:
                print('ERROR: Stack overflow')
                exit()

<mask token>
import sys


class CPU:
    """Main CPU class."""

    def __init__(self):
        """Construct a new CPU."""
        self.ram = [0] * 256
        self.reg = [0] * 8
        self.reg[7] = 244
        self.pc = self.ir = self.mar = self.mdr = self.fl = 0

    def load(self):
        program = []
        """ Read program from file. """
        if len(sys.argv) != 2:
            print('ERROR: system arguments must be equal to 2!')
            exit()
        with open(sys.argv[1]) as f:
            line = f.readline()
            while line != '':
                if line[0] == '0' or line[0] == '1':
                    program.append(int(line[:8], 2))
                line = f.readline()
        if len(program) > 256 or len(program) == 0:
            print('ERROR: Program len invalid')
            exit()
        """Load a program into memory."""
        address = 0
        for instruction in program:
            self.ram[address] = instruction
            address += 1

    def alu(self, op, reg_a, reg_b):
        """ALU operations."""
        if op == 'ADD':
            self.reg[reg_a] += self.reg[reg_b]
        elif op == 'AND':
            self.reg[reg_a] &= self.reg[reg_b]
        elif op == 'CMP':
            valA = self.reg[reg_a]
            valB = self.reg[reg_b]
            self.fl = 0
            fl_bin_list = ['0'] * 8
            if valA == valB:
                fl_bin_list[-1] = '1'
            else:
                fl_bin_list[-1] = '0'
            if valA < valB:
                fl_bin_list[-3] = '1'
            else:
                fl_bin_list[-3] = '0'
            if valA > valB:
                fl_bin_list[-2] = '1'
            else:
                fl_bin_list[-2] = '0'
            fl_bin_string = ''.join(fl_bin_list)
            self.fl = int(fl_bin_string, 2)
        elif op == 'DEC':
            self.reg[reg_a] -= 1
        elif op == 'DIV':
            if self.reg[reg_b] == 0:
                print('ERROR: Cannot DIVide by 0')
                exit()
            self.reg[reg_a] /= self.reg[reg_b]
        elif op == 'INC':
            self.reg[reg_a] += 1
        elif op == 'SUB':
            self.reg[reg_a] -= self.reg[reg_b]
        elif op == 'MOD':
            if self.reg[reg_b] == 0:
                print('ERROR: Cannot MODulo by 0')
                exit()
            self.reg[reg_a] %= self.reg[reg_b]
        elif op == 'MUL':
            self.reg[reg_a] *= self.reg[reg_b]
        elif op == 'NOT':
            self.reg[reg_a] ^= 255
        elif op == 'OR':
            self.reg[reg_a] |= self.reg[reg_b]
        elif op == 'SHL':
            self.reg[reg_a] = self.reg[reg_a] << self.reg[reg_b]
        elif op == 'SHR':
            self.reg[reg_a] = self.reg[reg_b] >> self.reg[reg_b]
        elif op == 'SUB':
            self.reg[reg_a] -= self.reg[reg_b]
        elif op == 'XOR':
            self.reg[reg_a] ^= self.reg[reg_b]
        else:
            raise Exception('Unsupported ALU operation')
        self.reg[reg_a] &= 255

    def trace(self):
        """
        Handy function to print out the CPU state. You might want to call this
        from run() if you need help debugging.
        """
        print(f'TRACE: %02X | %02X %02X %02X |' % (self.pc, self.ram_read(
            self.pc), self.ram_read(self.pc + 1), self.ram_read(self.pc + 2
            )), end='')
        for i in range(8):
            print(' %02X' % self.reg[i], end='')
        print()

    def ram_read(self, addr):
        self.mar = addr
        self.mdr = self.ram[addr]
        return self.mdr

    def ram_write(addr, value):
        if value & 255 != value:
            print('WARNING: ram_write() called with value > 255')
        self.mar = addr
        self.mdr = value
        self.ram[addr] = value

    def run(self):
        """Run the CPU."""
        while 1:
            self.ir = self.ram_read(self.pc)
            operand_a = self.ram_read(self.pc + 1)
            operand_b = self.ram_read(self.pc + 2)
            bin_ir = bin(self.ir)[2:].zfill(8)
            operandCount = int(bin_ir[:2], 2)
            isALU = int(bin_ir[2], 2)
            setsPC = int(bin_ir[3], 2)
            identifer = int(bin_ir[4:], 2)
            t = {(160): 'ADD', (168): 'AND', (80): 'CALL', (167): 'CMP', (
                102): 'DEC', (163): 'DIV', (1): 'HLT', (101): 'INC', (82):
                'INT', (19): 'IRET', (85): 'JEQ', (90): 'JGE', (87): 'JGT',
                (89): 'JLE', (88): 'JLT', (84): 'JMP', (86): 'JNE', (131):
                'LD', (130): 'LDI', (164): 'MOD', (162): 'MUL', (0): 'NOP',
                (105): 'NOT', (170): 'OR', (70): 'POP', (72): 'PRA', (71):
                'PRN', (69): 'PUSH', (17): 'RET', (172): 'SHL', (173):
                'SHR', (132): 'ST', (161): 'SUB', (171): 'XOR', (85): 'JEQ'}
            if isALU == 1:
                self.alu(t[self.ir], operand_a, operand_b)
            else:
                if t[self.ir] == 'HLT':
                    exit()
                if t[self.ir] == 'LD':
                    self.reg[operand_a] = self.ram[self.reg[operand_b]]
                elif t[self.ir] == 'LDI':
                    self.reg[operand_a] = operand_b & 255
                elif t[self.ir] == 'PRN':
                    print(self.reg[operand_a])
                elif t[self.ir] == 'PUSH':
                    self.reg[7] -= 1
                    self.ram[self.reg[7]] = self.reg[operand_a]
                elif t[self.ir] == 'POP':
                    self.reg[operand_a] = self.ram[self.reg[7]]
                    self.reg[7] += 1
                elif t[self.ir] == 'CALL':
                    self.reg[7] -= 1
                    self.ram[self.reg[7]] = self.pc + 2
                    self.pc = self.reg[operand_a]
                elif t[self.ir] == 'RET':
                    self.pc = self.ram[self.reg[7]]
                    self.reg[7] += 1
                elif t[self.ir] == 'ST':
                    self.reg[operand_a] = self.reg[operand_b]
                elif t[self.ir] == 'JEQ':
                    if self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JGE':
                    if self.fl == 2 or self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JGT':
                    if self.fl == 2:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JLE':
                    if self.fl == 4 or self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JLT':
                    if self.fl == 4:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'JMP':
                    self.pc = self.reg[operand_a]
                elif t[self.ir] == 'JNE':
                    if self.fl != 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                elif t[self.ir] == 'PRA':
                    print(chr(self.reg[operand_a]), end='')
                else:
                    print('WARNING: Unsupported opcode: 0x%02x' % self.ir)
                    exit()
            if setsPC == 0:
                self.pc += operandCount + 1
            if self.reg[7] == self.pc + 1:
                print('ERROR: Stack overflow')
                exit()

"""CPU functionality."""

import sys

class CPU:
    """Main CPU class."""

    def __init__(self):
        """Construct a new CPU."""
        self.ram = [0x0] * 256         # 256b of ram
        self.reg = [0x0] * 8           # 8 registers
        self.reg[7] = 0xF4             # Stack pointer
                                  # R5 is reserved as the interrupt mask (IM)
                                  # R6 is reserved as the interrupt status (IS)
                                  # R7 is reserved as the stack pointer (SP)

        # Internal Registers
        self.pc = self.ir = self.mar = self.mdr = self.fl = 0x0
        # PC: Program Counter, address of the currently executing instruction
        # IR: Instruction Register, contains a copy of the currently executing instruction
        # MAR: Memory Address Register, holds the memory address we're reading or writing
        # MDR: Memory Data Register, holds the value to write or the value just read
        # FL: Flags, see below

        # FL bits: 00000LGE

            # L Less-than: during a CMP, set to 1 if registerA is less than registerB, zero otherwise.
            # G Greater-than: during a CMP, set to 1 if registerA is greater than registerB, zero otherwise.
            # E Equal: during a CMP, set to 1 if registerA is equal to registerB, zero otherwise.

    def load(self):
        program = []
        
        """ Read program from file. """        

        if len(sys.argv) != 2:
            print("ERROR: system arguments must be equal to 2!")
            exit()

        with open(sys.argv[1]) as f:
            line = f.readline()

            while line != "":
                if line[0] == '0' or line[0] == '1':
                    program.append(int(line[:8], 2))
                line = f.readline()

        if len(program) > 256 or len(program) == 0:
            print("ERROR: Program len invalid")
            exit()

        """Load a program into memory."""

        address = 0

        for instruction in program:
            self.ram[address] = instruction
            address += 1


    def alu(self, op, reg_a, reg_b):
        """ALU operations."""

        if op == "ADD":
            self.reg[reg_a] += self.reg[reg_b]

        elif op == "AND":
            self.reg[reg_a] &= self.reg[reg_b]

        elif op == "CMP":
            valA = self.reg[reg_a]
            valB = self.reg[reg_b]

            # Clear FL bits
            self.fl = 0x0
            #fl_bin_string = bin(self.fl)[2:].zfill(8)
            fl_bin_list = ["0"] * 8

            if valA == valB:
                # Set E flag to 1
                fl_bin_list[-1] = "1"
            else:
                # set E flag to 0
                fl_bin_list[-1] = "0"

            if valA < valB:
                # Set L flag to 1
                fl_bin_list[-3] = "1"
            else:
                # Set L flag to 0
                fl_bin_list[-3] = "0"
            
            if valA > valB:
                # Set G flag to 1
                fl_bin_list[-2] = "1"
            else:
                # Set G flag to 0
                fl_bin_list[-2] = "0"

            fl_bin_string = "".join(fl_bin_list)

            # Convert fl_bin_string into int format and store the result in fl
            self.fl = int(fl_bin_string, 2)
        
        elif op == "DEC":
            self.reg[reg_a] -= 1
        
        elif op == "DIV":
            # Catch any divide-by-zero attempts
            if self.reg[reg_b] == 0:
                print("ERROR: Cannot DIVide by 0")
                exit()

            self.reg[reg_a] /= self.reg[reg_b]

        elif op == "INC":
            self.reg[reg_a] += 1
        
        elif op == "SUB":
            self.reg[reg_a] -= self.reg[reg_b]
        
        elif op == "MOD":
            # Catch any modulo-by-zero attempts
            if self.reg[reg_b] == 0:
                print("ERROR: Cannot MODulo by 0")
                exit()

            self.reg[reg_a] %= self.reg[reg_b]

        elif op == "MUL":
            self.reg[reg_a] *= self.reg[reg_b]

        elif op == "NOT":
            # Python ~ operator doesn't work for this, XOR using a bitmask for NOT effect
            self.reg[reg_a] ^= 0b11111111

        elif op == "OR":
            self.reg[reg_a] |= self.reg[reg_b]

        elif op == "SHL":
            self.reg[reg_a] = self.reg[reg_a] << self.reg[reg_b]
        
        elif op == "SHR":
            self.reg[reg_a] = self.reg[reg_b] >> self.reg[reg_b]

        elif op == "SUB":
            self.reg[reg_a] -= self.reg[reg_b]

        elif op == "XOR":
            self.reg[reg_a] ^= self.reg[reg_b]

        else:
            raise Exception("Unsupported ALU operation")
        
        # & 0xFF register
        self.reg[reg_a] &= 0xFF

    def trace(self):
        """
        Handy function to print out the CPU state. You might want to call this
        from run() if you need help debugging.
        """

        print(f"TRACE: %02X | %02X %02X %02X |" % (
            self.pc,
            #self.fl,
            #self.ie,
            self.ram_read(self.pc),
            self.ram_read(self.pc + 1),
            self.ram_read(self.pc + 2)
        ), end='')

        for i in range(8):
            print(" %02X" % self.reg[i], end='')

        print()

    def ram_read(self, addr):        # NOTE: ram methods do not bounds check
        self.mar = addr
        self.mdr = self.ram[addr]
        return self.mdr
    
    def ram_write(addr, value):
        if (value & 0xFF) != value:
            print('WARNING: ram_write() called with value > 255')

        self.mar = addr
        self.mdr = value
        self.ram[addr] = value

    def run(self):
        """Run the CPU."""
        while 1:
            # It needs to read the memory address that's stored in register PC, and store that result in IR, the Instruction Register
            # (Fetch instruction from RAM)
            self.ir = self.ram_read(self.pc)

            # Some instructions requires up to the next two bytes of data after the PC in memory to perform operations on. Sometimes the byte value is a register number, other times it's a constant value (in the case of LDI). Using ram_read(), read the bytes at PC+1 and PC+2 from RAM into variables operand_a and operand_b in case the instruction needs them.
            # NOTE: pc > 253 will attempt to have one or more operands read OOB data
            operand_a = self.ram_read(self.pc+1)
            operand_b = self.ram_read(self.pc+2)

            # Then, depending on the value of the opcode, perform the actions needed for the instruction per the LS-8 spec.

            # Decode instruction

            # Convert ir to binary:
            bin_ir =        bin(self.ir)[2:].zfill(8) 

            # Decode binary rep
            operandCount =  int(bin_ir[:2], 2)
            isALU =         int(bin_ir[2], 2)
            setsPC =        int(bin_ir[3], 2)
            identifer =     int(bin_ir[4:], 2)

            # Execute instruction

            # Maps opcode string to int representation
            t = {
                    0xA0: 'ADD',
                    0xA8: 'AND',
                    0x50: 'CALL',
                    0xA7: 'CMP',
                    0x66: 'DEC',
                    0xA3: 'DIV',
                    0x01: 'HLT',
                    0x65: 'INC',
                    0x52: 'INT',
                    0x13: 'IRET',
                    0x55: 'JEQ',
                    0x5A: 'JGE',
                    0x57: 'JGT',
                    0x59: 'JLE',
                    0x58: 'JLT',
                    0x54: 'JMP',
                    0x56: 'JNE',
                    0x83: 'LD',
                    0x82: 'LDI',
                    0xA4: 'MOD',
                    0xA2: 'MUL',
                    0x00: 'NOP',
                    0x69: 'NOT',
                    0xAA: 'OR',
                    0x46: 'POP',
                    0x48: 'PRA',
                    0x47: 'PRN',
                    0x45: 'PUSH',
                    0x11: 'RET',
                    0xAC: 'SHL',
                    0xAD: 'SHR',
                    0x84: 'ST',
                    0xA1: 'SUB',
                    0xAB: 'XOR',
                    0x55: 'JEQ',
                    }        

            # print('\t',t[self.ir])
           
            # Run ALU operation if applicable
            if isALU == 1: 
                self.alu(t[self.ir], operand_a, operand_b)
            else:
                # Non-ALU opcodes
                if t[self.ir] == 'HLT':
                    exit()

                if t[self.ir] == 'LD':
                    self.reg[operand_a] = self.ram[self.reg[operand_b]]

                elif t[self.ir] == 'LDI':
                    self.reg[operand_a] = (operand_b & 0xFF)

                elif t[self.ir] == 'PRN':
                    print(self.reg[operand_a])
        
                elif t[self.ir] == 'PUSH':
                    self.reg[7] -= 1
                    self.ram[self.reg[7]] = self.reg[operand_a]
                
                elif t[self.ir] == 'POP':
                    self.reg[operand_a] = self.ram[self.reg[7]]
                    self.reg[7] += 1
                
                elif t[self.ir] == 'CALL':
                    self.reg[7] -= 1
                    self.ram[self.reg[7]] = self.pc + 2  # Save next instruction on stack
                    self.pc = self.reg[operand_a] # Set PC to register A's val

                elif t[self.ir] == 'RET':
                    self.pc = self.ram[self.reg[7]]
                    self.reg[7] += 1
                
                elif t[self.ir] == 'ST':
                    self.reg[operand_a] = self.reg[operand_b]
                
                elif t[self.ir] == 'JEQ':
                    if self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0

                elif t[self.ir] == 'JGE':
                    if self.fl == 2 or self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0

                elif t[self.ir] == 'JGT':
                    if self.fl == 2:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0

                elif t[self.ir] == 'JLE':
                    if self.fl == 4 or self.fl == 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0

                elif t[self.ir] == 'JLT':
                    if self.fl == 4:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0

                elif t[self.ir] == 'JMP':
                    self.pc = self.reg[operand_a]

                elif t[self.ir] == 'JNE':
                    if self.fl != 1:
                        self.pc = self.reg[operand_a]
                    else:
                        setsPC = 0
                
                elif t[self.ir] == 'PRA':
                    print(chr(self.reg[operand_a]), end='')

                else:
                    print("WARNING: Unsupported opcode: 0x%02x" % self.ir)
                    exit()

            
            # Set Program Counter
            if setsPC == 0:
                self.pc += operandCount + 1

            # Stack overflow check
            if self.reg[7] == self.pc+1:
                print("ERROR: Stack overflow")
                exit()
