diff --git a/LS8-spec.md b/LS8-spec.md index fa24efe44..8ddb81ab5 100644 --- a/LS8-spec.md +++ b/LS8-spec.md @@ -15,7 +15,7 @@ ## Internal Registers -* `PC`: Program Counter, address of the currently executing instruction +* `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 diff --git a/README.md b/README.md index 20c417457..b1fb7c5c8 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,13 @@ ### Day 1: Get `print8.ls8` running -- [ ] Inventory what is here -- [ ] Implement the `CPU` constructor -- [ ] Add RAM functions `ram_read()` and `ram_write()` -- [ ] Implement the core of `run()` -- [ ] Implement the `HLT` instruction handler -- [ ] Add the `LDI` instruction -- [ ] Add the `PRN` instruction +- [x] Inventory what is here +- [x] Implement the `CPU` constructor +- [x] Add RAM functions `ram_read()` and `ram_write()` +- [x] Implement the core of `run()` +- [x] Implement the `HLT` instruction handler +- [x] Add the `LDI` instruction +- [x] Add the `PRN` instruction ### Day 2: Add the ability to load files dynamically, get `mult.ls8` running diff --git a/instructions.py b/instructions.py new file mode 100644 index 000000000..e69de29bb diff --git a/inventory.md b/inventory.md new file mode 100644 index 000000000..a6749ff49 --- /dev/null +++ b/inventory.md @@ -0,0 +1,43 @@ +Inventory Step + +CS Computer Architecture Repo asm --- ls8 --- examples folder (call, interrupts, keyboard, mult, print8, printstr, sctest, stack, stackoverflow) --- +README: LS8 emulator project details --- cpu.py: CPU class (needs to be developed) --- ls8.py: where file will be run FAQ LS8-cheatsheet.md +LS8-spec.md: Registers, Internal Registers, Flags, Stack, Interrupts, README.md - objectives for modules 1-4 + +ADD regA regB: add reg A and reg B, store in reg A +AND regA regB: bitwise-AND values in reg A and B, store in reg A +CALL register: call register +CMP regA regB: compare reg A and reg B, for equal, less than, greater than +DEC register: decrement the value in the register +DIV regA regB: reg A/reg B, store in reg A, if 0 print error and halt +HLT: halts CPU and exits emulator +INC register: increment the value in the given register +INT register: issue interrupt number stored in given register +IRET: return from interrupt handler +JEQ register: if equal flag is set to true, jump to address stored in given register +JGE register: if greater than flag or equal flag is set to true, jump to address stored in given register +JLE register: if less than or equal flag is set to true, jump to address stored in given register +JLT register: if less than flag is set to true, jump to address stored in given register +JMP register: jump to address stored in given register +JNE register: if equal flag is clear (false, 0) jump to address stored in given register +LD regA regB: loads regA with value at memory address stored in regB +LDI reg immediate: set value of a register to an interger +MOD regA regB (this instruction is handled by the ALU): divide value in the first register by the value in the second, storing the REMAINDER of the result in regA +MUL regA regB (this instruction is handled by the ALU): multiply the values in two registers together and store result in regA +NOP: no operation, do nothing for this instruction +NOT register (this instruction is handled by the ALU): perform bitwise not on value in register +OR regA regB (this is an instruction handled by the ALU): perform a bitwise-OR between values in regA and regB, storing result in regA +POP register: pop value at top of stack into given register + 1. copy value from address (pointed to by SP) to the given register + 2. increment SP +PRA register (pseudo-instruction): print alpha character value stored in the given register. print to the colsole the ASCII character corresponding to the value in the register +PRN register (pseudo-instruction): print numeric value store in the given register. print to the console the decimal integer value that is stored in the given register +PUSH register: push the value in the given register on the stack + 1. decrement the SP + 2. copy the value in the given register to the address pointed to by SP +RET: return from subroutine. pop value from top of stack and store in PC +SHL (this is an instruction handled by the ALU): shift the value in regA left by the number of bits specified in regB, filling the low bits with 0 +SHR (this is an instruction handled by the ALU): shift the value in regA right by the number of bits specified in regB, filling the highest bits with 0 +ST regA regB: store value in regB in the address stored in regA +SUB regA regB (this is an instruction handled by the ALU): subtract the value in the second reg from the first, storing result in regA +XOR regA regB: perform a bitwise-XOR between the values in regA and regB, storing result in regA \ No newline at end of file diff --git a/ls8/cpu.py b/ls8/cpu.py index 9a307496e..0a3f8d16b 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -1,65 +1,295 @@ """CPU functionality.""" import sys +import time + +HLT = 0b00000001 +LDI = 0b10000010 +PRN = 0b01000111 +MUL = 0b10100010 +ADD = 0b10100000 +PUSH = 0b01000101 +POP = 0b01000110 +CALL = 0b01010000 +RET = 0b00010001 + +# sprint challenge for MVP +CMP = 0b10100111 +JEQ = 0b01010101 +JNE = 0b01010110 +JMP = 0b01010100 + +# stretch opcodes +SHL = 0b10101100 +SHR = 0b10101101 +MOD = 0b10100100 +NOT = 0b01101001 +OR = 0b10101010 +AND = 0b10101000 +XOR = 0b10101011 + class CPU: """Main CPU class.""" def __init__(self): """Construct a new CPU.""" - pass + self.ram = [0] * 256 + self.reg = [0] * 8 + self.reg[7] = 0xF4 + # flags reg + self.reg[6] = 0 + self.pc = 0 + self.running = True + # all flags set to false on initialization + self.fl = 0b00000000 + self.branchtable = { + HLT: self.hlt, + LDI: self.ldi, + PRN: self.prn, + PUSH: self.push, + POP: self.pop, + CALL: self.call, + RET: self.ret, + JEQ: self.jeq, + JNE: self.jne, + JMP: self.jmp, + } + def load(self): """Load a program into memory.""" - - address = 0 - - # For now, we've just hardcoded a program: - - program = [ - # From print8.ls8 - 0b10000010, # LDI R0,8 - 0b00000000, - 0b00001000, - 0b01000111, # PRN R0 - 0b00000000, - 0b00000001, # HLT - ] - - for instruction in program: - self.ram[address] = instruction - address += 1 + if (len(sys.argv)) != 2: + print('remember to pass the second file name') + print('usage: python fileio.py ') + sys.exit() + try: + with open(sys.argv[1]) as f: + address = 0 + for line in f: + possible_binary = line[:line.find('#')] + # if no comment on line + if possible_binary == '': + continue # passes rest of loop + denary_int = int(possible_binary, 2) + self.ram[address] = denary_int + address += 1 + except FileNotFoundError: + print(f'Error from {sys.argv[0]}: {sys.argv[1]} not found ') + sys.exit() def alu(self, op, reg_a, reg_b): """ALU operations.""" - - if op == "ADD": + if op == ADD: # add self.reg[reg_a] += self.reg[reg_b] - #elif op == "SUB": etc + elif op == MUL: # multiply + self.reg[reg_a] *= self.reg[reg_b] + elif op == CMP: # compare + # less than + if self.reg[reg_a] < self.reg[reg_b]: + self.reg[6] = 0b00000100 + # greater than + elif self.reg[reg_a] > self.reg[reg_b]: + self.reg[6] = 0b00000010 + # equal to + else: + self.reg[6] = 0b00000001 + elif op == SHL: # shift left + decimal = self.reg[reg_a] << self.reg[reg_b] + self.reg[reg_a] = f"{decimal:#010b}" + elif op == SHR: # shift right + decimal = self.reg[reg_a] >> self.reg[reg_b] + self.reg[reg_a] = f"{decimal:#010b}" + elif op == MOD: # modulo, get remainder + # cannot divide by 0 + if self.reg[reg_b] == 0: + # halt + self.hlt(reg_a, reg_b) + self.reg[reg_a] = self.reg[reg_a] % self.reg[reg_b] + elif op == NOT: # store bitwise not + self.reg[reg_a] = ~self.reg[reg_a] + elif op == OR: # store bitwise or + self.reg[reg_a] = self.reg[reg_a] | self.reg[reg_b] + elif op == AND: # store bitwise and + self.reg[reg_a] = self.reg[reg_a] & self.reg[reg_b] + elif op == XOR: # store bitwise xor + self.reg[reg_a] = self.reg[reg_a] ^ self.reg[reg_b] else: raise Exception("Unsupported ALU operation") + + def ram_read(self, address): + return self.ram[address] + + + def ram_write(self, address, data): + self.ram[address] = data + + 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.fl, 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 run(self): """Run the CPU.""" - pass + IR = [] + self.running = True + while self.running: + IR = self.ram[self.pc] # instruction register + num_args = IR >> 6 + is_alu_op = (IR >> 5) & 0b001 + operand_a = self.ram_read(self.pc+1) + operand_b = self.ram_read(self.pc+2) + is_alu_op = (IR >> 5) & 0b001 == 1 + if is_alu_op: + self.alu(IR, operand_a, operand_b) + else: + self.branchtable[IR](operand_a, operand_b) + # check if command sets PC directly + sets_pc = (IR >> 4) & 0b0001 == 1 + if not sets_pc: + # increment pc here + self.pc += 1 + num_args + + + def hlt(self, operand_a, operand_b): + self.running = False + + + def ldi(self, operand_a, operand_b): + self.reg[operand_a] = operand_b + + + def prn(self, operand_a, operand_b): + print(self.reg[operand_a]) + + + def jmp(self, operand_a, operand_b): + reg_idx = self.reg[operand_a] + self.pc = reg_idx + + + def jeq(self, operand_a, operand_b): + # If equal flag is set (true), jump to the + # address stored in the given register + flags = self.reg[6] + equal_is_flagged = (flags << 7) & 0b10000000 == 128 + if equal_is_flagged: + # jump to address stored in reg + given_reg_value = self.reg[operand_a] + self.pc = given_reg_value + else: + # just increment + self.pc += 2 + + + def jne(self, operand_a, operand_b): + # If E flag is clear (false, 0), jump to + # the address stored in the given register + flags = self.reg[6] + equal_is_flagged = flags == 1 + if equal_is_flagged: + # just increment + self.pc += 2 + else: + # jump to address stored in reg + given_reg_value = self.reg[operand_a] + self.pc = given_reg_value + + + def push(self, operand_a, operand_b): + # decrement the SP. + self.reg[7] -= 1 + # copy the value in the given register to the address pointed to by SP + value = self.reg[operand_a] + # copy to the address at stack pointer + SP = self.reg[7] + self.ram[SP] = value + + + def pop(self, operand_a, operand_b): + # get SP + SP = self.reg[7] + # get address pointed to by SP + value = self.ram[SP] + # Copy the value from the address pointed to by SP to the given register. + self.reg[operand_a] = value + # increment SP + self.reg[7] += 1 + + + def call(self, operand_a, operand_b): + # PUSH + return_address = self.pc + 2 + # decrement the SP, stored in R7 + self.reg[7] -= 1 + # store the value at the SP address + SP = self.reg[7] + # the return address is 8 as we can see by looking ahead by 2 + self.ram[SP] = return_address + # ram (command) at 7 contains 1 + reg_idx = self.ram[self.pc+1] + # reg 1 contains 24 + subroutine_address = self.reg[reg_idx] + # pc is at 24 + self.pc = subroutine_address + + + def ret(self, operand_a, operand_b): + # Return from subroutine. + # Pop value from top of stack + SP = self.reg[7] + return_address = self.ram[SP] + #store it in the PC + self.pc = return_address + # increment stack + self.reg[7] += 1 + + + def check_inter(self): + interrupts = self.reg[self.im] & self.reg[self.isr] + for interrupt in range(8): + bit = 1 << interrupt + #if an interrupt is triggered + if interrupts & bit: + # save the old interrupt state + self.old_im = self.reg[self.im] + # disable interrupts + self.reg[self.im] = 0 + # clear the interrupt + self.reg[self.isr] &= (255 ^ bit) + # decrement the stack pointer + self.reg[self.sp] -= 1 + # push the pc to the stack + self.ram_write(self.reg[self.sp], self.pc) + #decrement the stack pointer + self.reg[self.sp] -= 1 + # push the flags to the stack + self.ram_write(self.reg[self.sp], self.fl) + # push the registers to the stack R0-R6 + for i in range(7): + self.reg[self.sp] -= 1 + self.ram_write(self.reg[self.sp], self.reg[i]) + self.pc = self.ram[0xF8 + interrupt] + # break out and stop checking interrupts + break + + +if __name__ == '__main__': + cpu = CPU() + cpu.load() + cpu.run() \ No newline at end of file diff --git a/numbers.txt b/numbers.txt new file mode 100644 index 000000000..23c0226ab --- /dev/null +++ b/numbers.txt @@ -0,0 +1,203 @@ + + + +Why different number bases? +- +Commonly used bases? +- binary: base 2 +- hexadecimal (aka hex): base 16 +- decimal: base 10 +- octal: base 8 + +BINARY +Alan Turing +John von Neumann + +At the hardware level, it's easier to store numbers as a series of 'off' and 'on' data-points, +which lends itself to binary. + +Because it's easier to represent voltage in base 2 + +binary is useful for: +yes /no +true/false +on/off + +George Boole. Boolean logic. ((true and true) or (false and false)) --> True + +Why not base 3? +Base 3. Later a prototype. zero voltage is 0, medium voltage is 1, high voltage is 2. + +HEX +Hex is easy to convert to from binary +Hex is compact (used to print hashes, often) + +computer memory works in multiples of 8 + +Base 10 +- easy for humans +- because humans have 10 fingers?? +- Arithmetic for Billy Goats + + 999 +1000 + +Place-Based Number Systems +Hindu-Arabic number system vs Roman numerals + +VII * CXLCX = ???? + +0-9 +0 +9 +10 + +BINARY +1000 + +0 0 +1 1 +2 10 +3 11 +4 100 +5 101 +6 110 +7 111 +8 1000 +9 1001 +10 1010 +11 1011 +12 1100 +13 1101 +14 1110 +15 1111 + ++------ 8's place +|+----- 4's place +||+---- 2's place +|||+--- 1's place +|||| +1010 + ++------ 8's place +|+----- 4's place +||+---- 2's place +|||+--- 1's place +|||| +1000 + + +8 bits = byte +4 bits = 1/2 byte, a nibble + + +HEXADECIMAL +6 + 10, we have 10 symbols to work with +0-9, A-F + +0 0 +1 1 +2 2 +3 3 +4 4 +5 5 +6 6 +7 7 +8 8 +9 9 +10 A +11 B +12 C +13 D +14 E +15 F + +16 10 + +17 11 +18 12 +19 13 +20 14 +21 15 +22 16 +23 17 +24 18 +25 19 +26 1A +27 1B +28 1C +29 1D +30 1E +31 1F +32 20 + +0b100101011 +0d999 +0x1F == 0d31 + +54 --> hex?? 0x36 + +3 * 16 == 48 + +Decimal to hex: divide by 16, then add back the remainder + +Hex to decimal: multiply the symbol in the 16's place by 16, then add the remaining + +0xE3 to decimal: (E * 16) + 3 = (14 * 16) + 3 = 224 + 3 = 227 + +16 +14 +--- + 64 + 16 + +224 + +0b00011010 --> decimal --> 26 +0d10 + 0d16 == 0d26 + +43 --> binary? + +0b 00101011 + +binary hex +1111 --> F + + F F +1111 1111 + + 5 5 +0101 0101 --> decimal +0x55 ---> 85 +(5 * 16) + 5 + + E 6 +1110 0110 --> decimal + +0xE6 --> decimal --> 230 + +(14 * 16) + 6 + + + +0d73 --> binary + +73 --> 0x49 --> 0b01001001 + +4 * 16 = 64 + +0x49 + 4 9 +0100 1001 + +#ff ff ff + R G B + +#00 00 00 + + + +- leading 0s are just to pad out the byte, 0b00000001 +- don't change the number +- number bases don't change underlying +- computer represents everything in binary, down deep \ No newline at end of file diff --git a/our_program01.ls8 b/our_program01.ls8 new file mode 100644 index 000000000..234a54424 --- /dev/null +++ b/our_program01.ls8 @@ -0,0 +1,30 @@ +00000001 # print tim +00000001 # print tim +01000011 # print num +00101010 # the number 42 + +10000100 # save +01100011 # 99 +00000010 # into R2 + +10000100 # save +00000001 # the number 1 +00000011 # into R3 + +10100110 # ADD (R2 += R3) +00000010 # R2 +00000011 # R3 + +01000101 # print reg +00000010 # R2 again - should be 100 now! + +01000111 # PUSH +00000010 # from R2 + +01001000 # POP <-- PC +00000100 # into R4 + +01000101 # print reg +00000100 # Register 4 - should now be the number 99 + +00000010 # but this time it's the command halt \ No newline at end of file diff --git a/simple_machine.py b/simple_machine.py new file mode 100644 index 000000000..2613bd8c5 --- /dev/null +++ b/simple_machine.py @@ -0,0 +1,78 @@ + +PRINT_TIM = 0b00000001 +HALT = 0b00000010 +PRINT_NUM = 0b00000011 # a 2-byte command, takes 1 argument +SAVE = 0b00000100 # a 3-byte command, takes 2 arguments +PRINT_REG = 0b00000101 +PRINT_SUM = 0b00000110 + + +# a data-driven machine +# function call +# a "variable" == registers for our programs to save things into + + +# RAM +memory = [ + PRINT_TIM, + PRINT_TIM, + PRINT_NUM, # print 99 or some other number + 42, + SAVE, # save 99 into register 2 # <-- PC + 99, # the number to save + 2, # the register to put it into + PRINT_REG, + 2, + PRINT_SUM, # R1 + R2 + HALT, +] + + +# registers, R0-R7 +registers = [0] * 8 + +running = True + +# program counter +pc = 0 + +while running: + command = memory[pc] + + + if command == PRINT_TIM: + print('tim!') + + elif command == PRINT_NUM: + num_to_print = memory[pc + 1] # we already incremented PC! + print(num_to_print) + + pc += 1 # but increment again + + + elif command == SAVE: + num_to_save = memory[pc + 1] + register_address = memory[pc + 2] + + registers[register_address] = num_to_save + + # shorter: + # registers[memory + 2] = memory[pc + 1] + + pc += 2 + + elif command == PRINT_REG: + reg_address = memory[pc + 1] + + saved_number = registers[reg_address] + + print(saved_number) + + # print(registers[memory[pc + 1]]) + + + elif command == HALT: + running = False + + + pc += 1 # so we don't get sucked into an infinite loop! \ No newline at end of file diff --git a/simple_machine02.py b/simple_machine02.py new file mode 100644 index 000000000..a83f7144e --- /dev/null +++ b/simple_machine02.py @@ -0,0 +1,148 @@ +import sys + +PRINT_TIM = 0b00000001 +HALT = 0b00000010 +PRINT_NUM = 0b01000011 # a 2-byte command, takes 1 argument +SAVE = 0b10000100 # a 3-byte command, takes 2 arguments +PRINT_REG = 0b01000101 +ADD = 0b10100110 +PUSH = 0b01000111 +POP = 0b01001000 + + +# a data-driven machine +# function call +# a "variable" == registers for our programs to save things into + + +# RAM +memory = [0] * 256 + +# registers, R0-R7 +registers = [0] * 8 + +# set the stack pointer +registers[7] = 0xF4 + +running = True + +# program counter +pc = 0 + +def load_ram(): + try: + if len(sys.argv) < 2: + print(f'Error from {sys.argv[0]}: missing filename argument') + print(f'Usage: python3 {sys.argv[0]} ') + sys.exit(1) + + + # add a counter that adds to memory at that index + ram_index = 0 + + with open(sys.argv[1]) as f: + for line in f: + split_line = line.split("#")[0] + stripped_split_line = split_line.strip() + + if stripped_split_line != "": + command = int(stripped_split_line, 2) + + # load command into memory + memory[ram_index] = command + + ram_index += 1 + + except FileNotFoundError: + print(f'Error from {sys.argv[0]}: {sys.argv[1]} not found') + print("(Did you double check the file name?)") + +load_ram() + +while running: + command = memory[pc] + + + if command == PRINT_TIM: + print('tim!') + + elif command == PRINT_NUM: + num_to_print = memory[pc + 1] # we already incremented PC! + print(num_to_print) + + # pc += 1 # but increment again + + + elif command == SAVE: + num_to_save = memory[pc + 1] + register_address = memory[pc + 2] + + registers[register_address] = num_to_save + + # shorter: + # registers[memory + 2] = memory[pc + 1] + + # pc += 2 + + elif command == PRINT_REG: + reg_address = memory[pc + 1] + + saved_number = registers[reg_address] + + print(saved_number) + + # print(registers[memory[pc + 1]]) + + elif command == ADD: + reg1_address = memory[pc + 1] + reg2_address = memory[pc + 2] + + registers[reg1_address] += registers[reg2_address] + + elif command == PUSH: + # decrement the SP + ## at start, the SP points to address F4 + ### can just do arithmetic on hex, like F4 - 1 + ## R7 is our SP, currently points to (aka holds) F4 + registers[7] -= 1 + + # copy value from given register into address pointed to by SP + ## value from register? + register_address = memory[pc + 1] + value = registers[register_address] + + ## copy into SP address + ### now let's copy this value into our memory + ### but where in memory??? + + # memory[SP] = value + # memory[register[7]] = value + + SP = registers[7] + memory[SP] = value + + + elif command == POP: + # Copy the value from the address pointed to by `SP` to the given register. + ## get the SP + SP = registers[7] + ## copy the value from memory at that SP address + value = memory[SP] + + ## get the target register address + ### aka, where should we put this value from RAM? + register_address = memory[pc + 1] + + ## Put the value in that register + registers[register_address] = value + + # Increment the SP (move it back up) + registers[7] += 1 + + + elif command == HALT: + running = False + + number_of_operands = command >> 6 + pc += (1 + number_of_operands) + # pc += 1 # so we don't get sucked into an infinite loop! \ No newline at end of file diff --git a/sprint complete.pdf b/sprint complete.pdf new file mode 100644 index 000000000..4d7565762 Binary files /dev/null and b/sprint complete.pdf differ diff --git a/stack_and_more_notes.txt b/stack_and_more_notes.txt new file mode 100644 index 000000000..148df47f9 --- /dev/null +++ b/stack_and_more_notes.txt @@ -0,0 +1,165 @@ + +Computer arch topics we have implemented in our project +- RAM, self.ram +- registers! +- CPU, self.run +- ALU + +Why LS8? +- 8 bits +- not the registers, as you might think +- Also, 8-bit CPU and ALU architectures are those that are based on registers, +address buses, or data buses of that size + CPU + 00000000 + 00000000 + 00000000 + 00000000 + RAM + +- with 8-bit address bus, the CPU can address 256 bytes of RAM + + +General purpose calculating machine vs specialized calculating machines + "computer" "calculator" + + Can do anything hardwired for specific calculations + Broadly applicable Faster (often) + + + +More memory? Stack! +- more variables +- function calls and nested function calls + +To make a stack? +- Push +- Pop +- Memory space +-- RAM +-- 0-255 (256 bytes) + +- a pointer to track where the top of the stack +-- variable that is a memory address + + +how to push +how to Pop +Handling 'leftovers' from push +Stack underflow +Why doesn't the CPU prevent underflow, or prevent wrapping around in memory? +- don't want the CPU to spend time/energy checking +- used to be dev's job, now it's the compiler's job + +Stack overflow +- software's job to prevent this + +Stacks and nested function calls + +self.ram = [0] * 256 + +registers[7] = F3 + +SP = F3 + +memory[SP] + +FF: 00 +FE: 00 +FD: 00 +FC: 00 +FB: 00 +FA: 00 +F9: 00 +F8: 00 +F7: 00 +F6: 00 +F5: 00 +F4: 00 +F3: 42 <--- SP +F2: 42 +F1: 42 +F0: 00 +EF: 00 +EE: 00 +ED: 00 +EC: 00 +. +. +. +2B: 42 +2A: 42 +. +. +. +10: 42 +9: 42 +8: 42 +7: JUMP +6: R3 +5: PUSH +4: R3 +3: 42 +2: SAVE +1: PRINT_TIM +0: PRINT_TIM + + +R0: 99 +R1: 42 +R3: 42 + +PUSH R0: +- decrement the SP +- copy the value from the given register + +PUSH R1: +- decrement the SP +- copy the value from the given register + +POP R3: +- copy into the register +- increment SP + +PUSH R0: +- decrement the SP +- copy the value from the given register + +POP R3: +- copy into the register +- increment SP + + + + + + +Stack +700: 4 +699: 2 +698: 3 +697: 6 +696: 6 +695: 7 <-- SP +694: 3 +693: 6 + +registers[4] = 6 + +a = 4 + +def mult(x, y): + z = x * y + + return z + +def main(): + a = 2 + + b = 3 + + c = a * b + + d = mult(a, b) + + e = 7 \ No newline at end of file