From 677a42ad7ea03632bc648fec8f449ca66c311ea0 Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 1 Nov 2020 15:56:47 -0500 Subject: [PATCH 01/21] Inventor files --- ls8/README.md | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/ls8/README.md b/ls8/README.md index d9170d968..f630eb24b 100644 --- a/ls8/README.md +++ b/ls8/README.md @@ -30,21 +30,21 @@ then prints it out: The binary numeric value on the left in the `print8.ls8` code above is either: -* the machine code value of the instruction (e.g. `10000010` for `LDI`), also +- the machine code value of the instruction (e.g. `10000010` for `LDI`), also known as the _opcode_ or -* one of the opcode's arguments (e.g. `00000000` for `R0` or `00001000` for the +- one of the opcode's arguments (e.g. `00000000` for `R0` or `00001000` for the value `8`), also known as the _operands_. This code above requires the implementation of three instructions: -* `LDI`: load "immediate", store a value in a register, or "set this register to +- `LDI`: load "immediate", store a value in a register, or "set this register to this value". -* `PRN`: a pseudo-instruction that prints the numeric value stored in a +- `PRN`: a pseudo-instruction that prints the numeric value stored in a register. -* `HLT`: halt the CPU and exit the emulator. +- `HLT`: halt the CPU and exit the emulator. See [the LS-8 spec](../LS8-spec.md) for more details. @@ -59,11 +59,13 @@ but you'll have to implement those three above instructions first! ## Step 0: IMPORTANT: inventory what is here! -* Make a list of files here. -* Write a short 3-10-word description of what each file does. -* Note what has been implemented, and what hasn't. -* Read this whole file. -* Skim the spec. +- Make a list of files here. + 1. cpu.py - Defines a CPU class that emulates a LS-8 Microcomputer + 2. ls8.py - Uses a CPU nstance to run a program +- Write a short 3-10-word description of what each file does. +- Note what has been implemented, and what hasn't. +- Read this whole file. +- Skim the spec. ## Step 1: Add the constructor to `cpu.py` @@ -134,7 +136,7 @@ name instead of by numeric value. In `run()` in your if-else block, exit the loop if a `HLT` instruction is encountered, regardless of whether or not there are more lines of code in the -LS-8 program you loaded. +LS-8 program you loaded. We can consider `HLT` to be similar to Python's `exit()` in that we stop whatever we are doing, wherever we are. @@ -151,8 +153,8 @@ value. This is a very similar process to adding `LDI`, but the handler is simpler. See the LS-8 spec. -*At this point, you should be able to run the program and have it print `8` to -the console!* +_At this point, you should be able to run the program and have it print `8` to +the console!_ ## Step 7: Un-hardcode the machine code @@ -194,7 +196,7 @@ so you can look in `sys.argv[1]` for the name of the file to load. > expect, and print an error and exit if they didn't. In `load()`, you will now want to use those command line arguments to open a -file, read in its contents line by line, and save appropriate data into RAM. +file, read in its contents line by line, and save appropriate data into RAM. As you process lines from the file, you should be on the lookout for blank lines (ignore them), and you should ignore everything after a `#`, since that's a @@ -296,10 +298,10 @@ a high address) and grows _downward_ as things are pushed on. The LS-8 is no exception to this. Implement a system stack per the spec. Add `PUSH` and `POP` instructions. Read - the beginning of the spec to see which register is the stack pointer. - -* Values themselves should be saved in the ***portion of RAM*** _that is allocated for the stack_. - - Use the stack pointer to modify the correct block of memory. +the beginning of the spec to see which register is the stack pointer. + +- Values themselves should be saved in the **_portion of RAM_** _that is allocated for the stack_. + - Use the stack pointer to modify the correct block of memory. - Make sure you update the stack pointer appropriately as you `PUSH` and `POP` items to and from the stack. If you run `python3 ls8.py examples/stack.ls8` you should see the output: @@ -320,21 +322,21 @@ enables you to create reusable functions. Subroutines have many similarities to functions in higher-level languages. Just as a function in C, JavaScript or Python will jump from the function call, to its definition, and then return back to the line of code following the call, -subroutines will also allow us to execute instructions non-sequentially. +subroutines will also allow us to execute instructions non-sequentially. The stack is used to hold the return address used by `RET`, so you **must** implement the stack in step 10, first. Then, add subroutine instructions `CALL` and `RET`. -* For `CALL`, you will likely have to modify your handler call in `cpu_run()`. +- For `CALL`, you will likely have to modify your handler call in `cpu_run()`. The problem is that some instructions want to execute and move to the next instruction like normal, but others, like `CALL` and `JMP` want to go to a specific address. > Note: `CALL` is very similar to the `JMP` instruction. However, there is one - > key difference between them. Can you find it in the specs? + > key difference between them. Can you find it in the specs? - * In **any** case where the instruction handler sets the `PC` directly, you + - In **any** case where the instruction handler sets the `PC` directly, you _don't_ want to advance the PC to the next instruction. So you'll have to set up a special case for those types of instructions. This can be a flag you explicitly set per-instruction... but can also be computed from the From 4d195efb8f3e63e56a9337b31ab230dfddf1d12f Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 1 Nov 2020 17:32:22 -0500 Subject: [PATCH 02/21] Add CPU class constructor --- ls8/cpu.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 9a307496e..6c9522502 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -7,7 +7,19 @@ class CPU: def __init__(self): """Construct a new CPU.""" - pass + # 256-byte RAM, each element is 1 byte (can only store integers 0-255) + self.ram = [0] * 256 + + #RO-R7: 8-bit general purpose registers, R5 = interrupt mask (IM), + # R6 = interrupt status (IS), R7 = stack pointer (SP) + self.reg = [0] * 8 + + # Internal Registers + self.pc = 0 # Program Counter: address of the currently executing instruction + self.ir = 0 # Instruction Register: contains a copy of the currently executing instruction + self.mar = 0 # Memory Address Register: holds the memory address we're reading or writing + self.mdr = 0 # Memory Data Register: holds the value to write or the value just read + self.fl = 0 # Flag Register: holds the current flags status def load(self): """Load a program into memory.""" From 8f5ca9f48699147e08d3b1dec9b88307f34fa84b Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 1 Nov 2020 17:44:56 -0500 Subject: [PATCH 03/21] Add RAM read/write functionality --- ls8/cpu.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ls8/cpu.py b/ls8/cpu.py index 6c9522502..a0e1496f0 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -75,3 +75,16 @@ def trace(self): def run(self): """Run the CPU.""" pass + + def ram_read(self, mar): + if mar >= 0 and mar < len(self.ram): + return self.ram[mar] + else: + print(f"Error: Attempted to read from memory address: {mar}, which is outside of the memory bounds.") + return -1 + + def ram_write(self, mar, mdr): + if mar >= 0 and mar < len(self.ram): + self.ram[mar] = mdr & 0xFF + else: + print(f"Error: Attempted to write to memory address: {mar}, which is outside of the memory bounds.") From 412e0cd1afe534c84b1a875a12f2b77fd9083e4f Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 1 Nov 2020 19:39:27 -0500 Subject: [PATCH 04/21] Add CPU run() functionality --- ls8/cpu.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index a0e1496f0..6fffae6d7 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -74,7 +74,25 @@ def trace(self): def run(self): """Run the CPU.""" - pass + running = True + + while running: + # Fetch the next instruction + self.ir = self.ram_read(self.pc) + operand_a = self.ram_read(self.pc + 1) + operand_b = self.ram_read(self.pc + 2) + + # Decode instruction + binary_ir = bin(self.ir)[2:].zfill(8) + operand_count = int(binary_ir[2], 2) + is_ALU_operation = binary_ir[2] == '1' + instruction_does_set_pc = binary_ir[3] == '1' + instruction_id = int(binary_ir[4:], 2) + + #Increment the program counter + self.pc += (1 + operand_count) + + def ram_read(self, mar): if mar >= 0 and mar < len(self.ram): From db1dc1a84211594e1e205067c52c560e6a24d06e Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 1 Nov 2020 19:54:23 -0500 Subject: [PATCH 05/21] Implement instructions --- ls8/cpu.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 6fffae6d7..5b533ffe4 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -92,7 +92,18 @@ def run(self): #Increment the program counter self.pc += (1 + operand_count) - + # Execute instruction + if self.ir == int('00000001', 2): # HLT + running - False + elif self.ir == int('10000010', 2): # LDI + self.reg[operand_a] = operand_b + elif self.ir == int('01000111', 2): # PRN + print(self.reg[operand_a]) + else: + print(f"Error: Could not execute instruction: {bin(self.ir)[2:].zfill(8)}") + sys.exit(1) + + def ram_read(self, mar): if mar >= 0 and mar < len(self.ram): From e3f4401084a20b3c582ed5f4381a42f6c68e63a5 Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 1 Nov 2020 20:26:26 -0500 Subject: [PATCH 06/21] Reorganize RAM functionality --- ls8/cpu.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 5b533ffe4..0baeb885a 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -21,6 +21,25 @@ def __init__(self): self.mdr = 0 # Memory Data Register: holds the value to write or the value just read self.fl = 0 # Flag Register: holds the current flags status + # Initialize the Stack Pointer + # SP points at the value at the op of the stack (most recently pushed), or at address F4 if the stack is empty + self.reg[7] = 0xF4 # 244 # int('F4', 16) + + + def ram_read(self, mar): + if mar >= 0 and mar < len(self.ram): + return self.ram[mar] + else: + print(f"Error: Attempted to read from memory address: {mar}, which is outside of the memory bounds.") + return -1 + + def ram_write(self, mar, mdr): + if mar >= 0 and mar < len(self.ram): + self.ram[mar] = mdr & 0xFF + else: + print(f"Error: Attempted to write to memory address: {mar}, which is outside of the memory bounds.") + + def load(self): """Load a program into memory.""" @@ -105,15 +124,3 @@ def run(self): - def ram_read(self, mar): - if mar >= 0 and mar < len(self.ram): - return self.ram[mar] - else: - print(f"Error: Attempted to read from memory address: {mar}, which is outside of the memory bounds.") - return -1 - - def ram_write(self, mar, mdr): - if mar >= 0 and mar < len(self.ram): - self.ram[mar] = mdr & 0xFF - else: - print(f"Error: Attempted to write to memory address: {mar}, which is outside of the memory bounds.") From 6da910a22a2ba405b303a24076b514999492846a Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 1 Nov 2020 21:05:39 -0500 Subject: [PATCH 07/21] Add self.halted property --- ls8/cpu.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ls8/cpu.py b/ls8/cpu.py index 0baeb885a..4727813a1 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -20,6 +20,7 @@ def __init__(self): self.mar = 0 # Memory Address Register: holds the memory address we're reading or writing self.mdr = 0 # Memory Data Register: holds the value to write or the value just read self.fl = 0 # Flag Register: holds the current flags status + self.halted = False # Initialize the Stack Pointer # SP points at the value at the op of the stack (most recently pushed), or at address F4 if the stack is empty From e88fc6111a488a89ef07db69fa7ae9888a0aa774 Mon Sep 17 00:00:00 2001 From: Your name Date: Wed, 4 Nov 2020 11:20:22 -0500 Subject: [PATCH 08/21] Refactor code --- ls8/cpu.py | 81 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 23 deletions(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 4727813a1..4bc536207 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -1,6 +1,12 @@ """CPU functionality.""" import sys +import os.path + +HLT = 0b00000001 +LDI = 0b10000010 +PRN = 0b01000111 +MUL = 0b10100010 class CPU: """Main CPU class.""" @@ -26,6 +32,15 @@ def __init__(self): # SP points at the value at the op of the stack (most recently pushed), or at address F4 if the stack is empty self.reg[7] = 0xF4 # 244 # int('F4', 16) + # Property wrapper for stack pointers + @property + def sp(self): + return self.reg[7] + + @sp.setter + def sp(self, a): + self.reg[7] = a & 0xFF + def ram_read(self, mar): if mar >= 0 and mar < len(self.ram): @@ -94,34 +109,54 @@ def trace(self): def run(self): """Run the CPU.""" - running = True + # running = True - while running: - # Fetch the next instruction + # while running: + # # Fetch the next instruction + + while not self.halted self.ir = self.ram_read(self.pc) operand_a = self.ram_read(self.pc + 1) operand_b = self.ram_read(self.pc + 2) - # Decode instruction - binary_ir = bin(self.ir)[2:].zfill(8) - operand_count = int(binary_ir[2], 2) - is_ALU_operation = binary_ir[2] == '1' - instruction_does_set_pc = binary_ir[3] == '1' - instruction_id = int(binary_ir[4:], 2) - - #Increment the program counter - self.pc += (1 + operand_count) - - # Execute instruction - if self.ir == int('00000001', 2): # HLT - running - False - elif self.ir == int('10000010', 2): # LDI - self.reg[operand_a] = operand_b - elif self.ir == int('01000111', 2): # PRN - print(self.reg[operand_a]) - else: - print(f"Error: Could not execute instruction: {bin(self.ir)[2:].zfill(8)}") - sys.exit(1) + # # Decode instruction + # binary_ir = bin(self.ir)[2:].zfill(8) + # operand_count = int(binary_ir[2], 2) + # is_ALU_operation = binary_ir[2] == '1' + # instruction_does_set_pc = binary_ir[3] == '1' + # instruction_id = int(binary_ir[4:], 2) + + # #Increment the program counter + # self.pc += (1 + operand_count) + + # # Execute instruction + # if self.ir == int('00000001', 2): # HLT + # running - False + # elif self.ir == int('10000010', 2): # LDI + # self.reg[operand_a] = operand_b + # elif self.ir == int('01000111', 2): # PRN + # print(self.reg[operand_a]) + # else: + # print(f"Error: Could not execute instruction: {bin(self.ir)[2:].zfill(8)}") + # sys.exit(1) + self.execute_instruction(operand_a, operand_b) + + def execute_instruction(self, operand_a, operand_b): + if self.ir == HLT: + self.halted = True + self.pc += 1 + + elif self.ir == LDI: + self.reg[operand_a] = operand_b + self.pc += 3 + + elif self.ir == PRN: + print(self.eg[operand_a]) + self.pc += 2 + + else: + print(f"Error: Could not execute intsruction: {self.ir}") + sys.exit(1) From d8d76f9ccb06ca73582a1c3e2cab6878f3550407 Mon Sep 17 00:00:00 2001 From: Your name Date: Wed, 4 Nov 2020 20:02:06 -0500 Subject: [PATCH 09/21] Recfactor cpu.py and ls8.py --- ls8/cpu.py | 21 +++++++++++---------- ls8/ls8.py | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 4bc536207..bf724e5d9 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -56,22 +56,23 @@ def ram_write(self, mar, mdr): print(f"Error: Attempted to write to memory address: {mar}, which is outside of the memory bounds.") - def load(self): + # def load(self): + def load(self, program): """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 - ] + # program = [ + # # From print8.ls8 + # 0b10000010, # LDI R0,8 + # 0b00000000, + # 0b00001000, + # 0b01000111, # PRN R0 + # 0b00000000, + # 0b00000001, # HLT + # ] for instruction in program: self.ram[address] = instruction diff --git a/ls8/ls8.py b/ls8/ls8.py index 74128d36b..46abb13f5 100755 --- a/ls8/ls8.py +++ b/ls8/ls8.py @@ -8,4 +8,24 @@ cpu = CPU() cpu.load() +# python3 ls8.py examples/print8.ls8 +if len(sys.argv) != 2: + print(argv_err_msg) + sys.exit(1) + +program = [] +file_name = sys.argv[1] +try: + with open(file_name) as f: + for line in f: + num = line.split("#")[0] + try: + program.append(int(num, 2)) + except: + continue +except: + print(f'Could not find file named: {file_name}') + sys.exit(1) + +cpu.load(program) cpu.run() \ No newline at end of file From 07fbcc621b42e2aa88eb53bbf126a4e072333cf6 Mon Sep 17 00:00:00 2001 From: Your name Date: Wed, 4 Nov 2020 20:06:02 -0500 Subject: [PATCH 10/21] Implement Multiply function --- ls8/cpu.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ls8/cpu.py b/ls8/cpu.py index bf724e5d9..526427146 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -155,6 +155,10 @@ def execute_instruction(self, operand_a, operand_b): print(self.eg[operand_a]) self.pc += 2 + elif self.ir == MUL: + self.reg[operand_a] *= self.reg[operand_b] + self.pc += 3 + else: print(f"Error: Could not execute intsruction: {self.ir}") sys.exit(1) From e45790d4a267184d0649da7a6335221e9fd207e1 Mon Sep 17 00:00:00 2001 From: Your name Date: Wed, 4 Nov 2020 20:23:55 -0500 Subject: [PATCH 11/21] Refactor I/O implementation --- ls8/cpu.py | 24 ++++++++++++++++++++---- ls8/ls8.py | 53 +++++++++++++++++++++++++++++++++++------------------ 2 files changed, 55 insertions(+), 22 deletions(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 526427146..b72c75cfd 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -57,7 +57,7 @@ def ram_write(self, mar, mdr): # def load(self): - def load(self, program): + def load(self, file_name): """Load a program into memory.""" address = 0 @@ -74,9 +74,25 @@ def load(self, program): # 0b00000001, # HLT # ] - for instruction in program: - self.ram[address] = instruction - address += 1 + # for instruction in program: + # self.ram[address] = instruction + # address += 1 + + file_path = os.path.join(os.path.dirname(__file__), file_name) + try: + with open(file_path) as f: + for line in f: + num = line.split("#")[0].strip() #10000010 + try: + instruction = int(num, 2) + self.ram[address] = instructionaddress += 1 + address += 1 + except: + continue + + except: + print(f'Could not find file named: {file_name}') + sys.exit(1) def alu(self, op, reg_a, reg_b): diff --git a/ls8/ls8.py b/ls8/ls8.py index 46abb13f5..81bb68b5c 100755 --- a/ls8/ls8.py +++ b/ls8/ls8.py @@ -2,30 +2,47 @@ """Main.""" +argv_err_msg = """ +Error: You must specify a file Name as a command line argument +This file you specify should contain a binary instruction set to load into the CPU + +Examples: + python3 ls8.py examples/print8.ls8 + python3 ls8.py examples/mult.ls8 + python3 ls8.py examples/stack.ls8 + python3 ls8.py examples/call.ls8 +""" + import sys from cpu import * -cpu = CPU() +project_day_num = 2 + +file_names = [ + "examples/print8.ls8", # 0 - Day 1 + "examples/mult.ls8", # 1 - Day 2 + "examples/stack.ls8", # 2 - Day 3 + "examples/call.ls8", # 3 - Day 4 + "examples/stackoverflow.ls8", # 4 + "examples/interrupts.ls8", # 5 + "examples/keyboard.ls8", # 6 + "examples/printstr.ls8", # 7 + "examples/sctest.ls8", ] # 8 + +file_name = file_names[project_day_num - 1] + -cpu.load() -# python3 ls8.py examples/print8.ls8 if len(sys.argv) != 2: + # print(f'\nRunning file: {file_name}') print(argv_err_msg) sys.exit(1) -program = [] -file_name = sys.argv[1] -try: - with open(file_name) as f: - for line in f: - num = line.split("#")[0] - try: - program.append(int(num, 2)) - except: - continue -except: - print(f'Could not find file named: {file_name}') - sys.exit(1) +else: + file_name = sys.argv[1] + -cpu.load(program) -cpu.run() \ No newline at end of file + +cpu = CPU() +cpu.load(file_name) +cpu.run() +print() \ No newline at end of file From c03eee3654effe7c25d88c52237ae36ce6d12f5b Mon Sep 17 00:00:00 2001 From: Your name Date: Thu, 5 Nov 2020 20:50:07 -0500 Subject: [PATCH 12/21] Implemented system stack // added PUSH and POP functions --- ls8/cpu.py | 117 +++++++++++++++++++++++++---------------------------- ls8/ls8.py | 26 ++++++------ 2 files changed, 67 insertions(+), 76 deletions(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index b72c75cfd..9e784e007 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -7,6 +7,8 @@ LDI = 0b10000010 PRN = 0b01000111 MUL = 0b10100010 +PUSH = 0b01000101 +POP = 0b01000110 class CPU: """Main CPU class.""" @@ -32,15 +34,30 @@ def __init__(self): # SP points at the value at the op of the stack (most recently pushed), or at address F4 if the stack is empty self.reg[7] = 0xF4 # 244 # int('F4', 16) - # Property wrapper for stack pointers - @property - def sp(self): - return self.reg[7] + # Setup Branch Table + self.branchtable = {} + self.branchtable[HLT] = self.execute_HLT + self.branchtable[LDI] = self.execute_LDI + self.branchtable[PRN] = self.execute_PRN + self.branchtable[MUL] = self.execute_MUL + self.branchtable[PUSH] = self.execute_PUSH + self.branchtable[POP] = self.execute_POP - @sp.setter - def sp(self, a): - self.reg[7] = a & 0xFF + # Property wrapper for stack pointers + @property + def sp(self): + return self.reg[7] + + @sp.setter + def sp(self, a): + self.reg[7] = a & 0xFF + + def instruction_size(self): + return ((self.ir >> 6) & 0b11) + 1 + + def instruction_sets_pc(self): + return ((self.ir >> 4) & 0b0001) == 1 def ram_read(self, mar): if mar >= 0 and mar < len(self.ram): @@ -56,28 +73,11 @@ def ram_write(self, mar, mdr): print(f"Error: Attempted to write to memory address: {mar}, which is outside of the memory bounds.") - # def load(self): def load(self, file_name): """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 - file_path = os.path.join(os.path.dirname(__file__), file_name) try: with open(file_path) as f: @@ -123,61 +123,52 @@ def trace(self): print(" %02X" % self.reg[i], end='') print() + + # Run Loop def run(self): """Run the CPU.""" - # running = True - - # while running: - # # Fetch the next instruction while not self.halted self.ir = self.ram_read(self.pc) operand_a = self.ram_read(self.pc + 1) operand_b = self.ram_read(self.pc + 2) - # # Decode instruction - # binary_ir = bin(self.ir)[2:].zfill(8) - # operand_count = int(binary_ir[2], 2) - # is_ALU_operation = binary_ir[2] == '1' - # instruction_does_set_pc = binary_ir[3] == '1' - # instruction_id = int(binary_ir[4:], 2) - - # #Increment the program counter - # self.pc += (1 + operand_count) - - # # Execute instruction - # if self.ir == int('00000001', 2): # HLT - # running - False - # elif self.ir == int('10000010', 2): # LDI - # self.reg[operand_a] = operand_b - # elif self.ir == int('01000111', 2): # PRN - # print(self.reg[operand_a]) - # else: - # print(f"Error: Could not execute instruction: {bin(self.ir)[2:].zfill(8)}") - # sys.exit(1) + if not self.instruction_sets_pc(): + self.pc += self.instruction_size() + self.execute_instruction(operand_a, operand_b) def execute_instruction(self, operand_a, operand_b): - if self.ir == HLT: - self.halted = True - self.pc += 1 + if self.ir in self.branchtable: + self.branchtable[self.ir] (operand_a, operand_b) + else: + print(f"Error: Could not execute intsruction: {self.ir}") + sys.exit(1) - elif self.ir == LDI: - self.reg[operand_a] = operand_b - self.pc += 3 + # Define operations to be loaded in the branch table - elif self.ir == PRN: - print(self.eg[operand_a]) - self.pc += 2 + def execute_HLT(self, operand_a, operand_b): + self.halted = True - elif self.ir == MUL: - self.reg[operand_a] *= self.reg[operand_b] - self.pc += 3 + def execute_LDI(self, operand_a, operand_b): + self.reg[operand_a] = operand_b - else: - print(f"Error: Could not execute intsruction: {self.ir}") - sys.exit(1) + def execute_PRN(self, operand_a, operand_b): + print(self.reg[operand_a]) + + def execute_MUL(self, operand_a, operand_b): + self.reg[operand_a] *= self.reg[operand_b] + + def execute_PUSH(self, operand_a, operand_b): + self.sp -= 1 + value_in_register = self.reg[operand_a] + self.ram[self.sp] = value_in_register + + def execute_POP(self, operand_a, operand_b): + top_most_value_in_stack = self.ram[self.sp] + self.reg[operand_a] = top_most_value_in_stack + self.sp += 1 diff --git a/ls8/ls8.py b/ls8/ls8.py index 81bb68b5c..f81c7c822 100755 --- a/ls8/ls8.py +++ b/ls8/ls8.py @@ -16,20 +16,20 @@ import sys from cpu import * -project_day_num = 2 +# project_day_num = 2 -file_names = [ - "examples/print8.ls8", # 0 - Day 1 - "examples/mult.ls8", # 1 - Day 2 - "examples/stack.ls8", # 2 - Day 3 - "examples/call.ls8", # 3 - Day 4 - "examples/stackoverflow.ls8", # 4 - "examples/interrupts.ls8", # 5 - "examples/keyboard.ls8", # 6 - "examples/printstr.ls8", # 7 - "examples/sctest.ls8", ] # 8 +# file_names = [ +# "examples/print8.ls8", # 0 - Day 1 +# "examples/mult.ls8", # 1 - Day 2 +# "examples/stack.ls8", # 2 - Day 3 +# "examples/call.ls8", # 3 - Day 4 +# "examples/stackoverflow.ls8", # 4 +# "examples/interrupts.ls8", # 5 +# "examples/keyboard.ls8", # 6 +# "examples/printstr.ls8", # 7 +# "examples/sctest.ls8", ] # 8 -file_name = file_names[project_day_num - 1] +# file_name = file_names[project_day_num - 1] if len(sys.argv) != 2: @@ -45,4 +45,4 @@ cpu = CPU() cpu.load(file_name) cpu.run() -print() \ No newline at end of file +# print() \ No newline at end of file From abbaf7991b6d8478285da42aed50fd55c2163104 Mon Sep 17 00:00:00 2001 From: Your name Date: Thu, 5 Nov 2020 21:39:10 -0500 Subject: [PATCH 13/21] Refactor parameter name --- ls8/cpu.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 9e784e007..50730de41 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -148,26 +148,26 @@ def execute_instruction(self, operand_a, operand_b): # Define operations to be loaded in the branch table - def execute_HLT(self, operand_a, operand_b): + def execute_HLT(self, a=None, b=None): self.halted = True - def execute_LDI(self, operand_a, operand_b): - self.reg[operand_a] = operand_b + def execute_LDI(self, reg_num, val): + self.reg[reg_num] = val - def execute_PRN(self, operand_a, operand_b): - print(self.reg[operand_a]) + def execute_PRN(self, reg_num, b=None): + print(self.reg[reg_num]) - def execute_MUL(self, operand_a, operand_b): - self.reg[operand_a] *= self.reg[operand_b] + def execute_MUL(self, reg_num, reg_num2): + self.reg[reg_num] *= self.reg[reg_num2] - def execute_PUSH(self, operand_a, operand_b): + def execute_PUSH(self, reg_num, b=None): self.sp -= 1 - value_in_register = self.reg[operand_a] - self.ram[self.sp] = value_in_register + self.mdr = self.reg[reg_num] + self.ram_write(self.sp, self.mdr) - def execute_POP(self, operand_a, operand_b): - top_most_value_in_stack = self.ram[self.sp] - self.reg[operand_a] = top_most_value_in_stack + def execute_POP(self, dest_reg_num, b=None): + self.mdr = self.ram_read(self.sp) + self.reg[dest_reg_num] = self.mdr self.sp += 1 From 17d6e94712f8afb04725eebb3661d6c52e35cdec Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 8 Nov 2020 11:57:36 -0500 Subject: [PATCH 14/21] Implement CALL, RET, ADD subroutines --- ls8/cpu.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ls8/cpu.py b/ls8/cpu.py index 50730de41..89eccd296 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -9,6 +9,9 @@ MUL = 0b10100010 PUSH = 0b01000101 POP = 0b01000110 +CALL = 0b01010000 +RET = 0b00010001 +ADD = 0b10100000 class CPU: """Main CPU class.""" @@ -42,6 +45,9 @@ def __init__(self): self.branchtable[MUL] = self.execute_MUL self.branchtable[PUSH] = self.execute_PUSH self.branchtable[POP] = self.execute_POP + self.branchtable[CALL] = self.execute_CALL + self.branchtable[RET] = self.execute_RET + self.branchtable[ADD] = self.execute_ADD # Property wrapper for stack pointers @@ -170,5 +176,18 @@ def execute_POP(self, dest_reg_num, b=None): self.reg[dest_reg_num] = self.mdr self.sp += 1 + def execute_CALL(self, dest_reg_num, b=None): + self.sp -= 1 + self.ram_write(self.sp, self.pc + self.instruction_size()) + self.pc = self.reg[dest_reg_num] + + def execute_RET(self, a=None, b=None): + self.mdr = self.ram_read(self.sp) + self.pc = self.mdr + self.sp += 1 + + def execute_ADD(self, reg_num, reg_num2): + self.reg[reg_num] += self.reg[reg_num2] + From 7d599e6787d3e6c4f9bef883dcba1606c8a01df8 Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 8 Nov 2020 13:18:41 -0500 Subject: [PATCH 15/21] Update load() method --- ls8/cpu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 89eccd296..06039dc85 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -91,7 +91,7 @@ def load(self, file_name): num = line.split("#")[0].strip() #10000010 try: instruction = int(num, 2) - self.ram[address] = instructionaddress += 1 + self.ram[address] = instruction address += 1 except: continue From dc0a1323bae75b48fbd4bf10bed48b7ee23754a0 Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 8 Nov 2020 18:20:49 -0500 Subject: [PATCH 16/21] Update cpu.py with updated parameters // Cleaned up code in ls8.py --- ls8/cpu.py | 48 +++++++++++++++++++++++++++++++----------------- ls8/ls8.py | 19 ++----------------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 06039dc85..4ae4cf2eb 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -59,12 +59,26 @@ def sp(self): def sp(self, a): self.reg[7] = a & 0xFF +# Computed Properties + + @property + def operand_a(self): + return self.ram_read(self.pc + 1) + + @property + def operand_b(self): + return self.ram_read(self.pc + 2) + + @property def instruction_size(self): return ((self.ir >> 6) & 0b11) + 1 + @property def instruction_sets_pc(self): return ((self.ir >> 4) & 0b0001) == 1 +# CPU Methods + def ram_read(self, mar): if mar >= 0 and mar < len(self.ram): return self.ram[mar] @@ -88,7 +102,7 @@ def load(self, file_name): try: with open(file_path) as f: for line in f: - num = line.split("#")[0].strip() #10000010 + num = line.split("#")[0].strip() # "10000010" try: instruction = int(num, 2) self.ram[address] = instruction @@ -135,7 +149,7 @@ def trace(self): def run(self): """Run the CPU.""" - while not self.halted + while not self.halted: self.ir = self.ram_read(self.pc) operand_a = self.ram_read(self.pc + 1) operand_b = self.ram_read(self.pc + 2) @@ -157,37 +171,37 @@ def execute_instruction(self, operand_a, operand_b): def execute_HLT(self, a=None, b=None): self.halted = True - def execute_LDI(self, reg_num, val): - self.reg[reg_num] = val + def execute_LDI(self): + self.reg[self.operand_a] = self.operand_b - def execute_PRN(self, reg_num, b=None): - print(self.reg[reg_num]) + def execute_PRN(self): + print(self.reg[self.operand_a]) - def execute_MUL(self, reg_num, reg_num2): - self.reg[reg_num] *= self.reg[reg_num2] + def execute_MUL(self): + self.reg[self.operand_a] *= self.reg[self.operand_b] - def execute_PUSH(self, reg_num, b=None): + def execute_PUSH(self): self.sp -= 1 - self.mdr = self.reg[reg_num] + self.mdr = self.reg[self.operand_a] self.ram_write(self.sp, self.mdr) - def execute_POP(self, dest_reg_num, b=None): + def execute_POP(self): self.mdr = self.ram_read(self.sp) - self.reg[dest_reg_num] = self.mdr + self.reg[self.operand_a] = self.mdr self.sp += 1 - def execute_CALL(self, dest_reg_num, b=None): + def execute_CALL(self): self.sp -= 1 self.ram_write(self.sp, self.pc + self.instruction_size()) - self.pc = self.reg[dest_reg_num] + self.pc = self.reg[self.operand_a] - def execute_RET(self, a=None, b=None): + def execute_RET(self): self.mdr = self.ram_read(self.sp) self.pc = self.mdr self.sp += 1 - def execute_ADD(self, reg_num, reg_num2): - self.reg[reg_num] += self.reg[reg_num2] + def execute_ADD(self): + self.reg[self.operand_a] += self.reg[operand_b] diff --git a/ls8/ls8.py b/ls8/ls8.py index f81c7c822..ed7a1222f 100755 --- a/ls8/ls8.py +++ b/ls8/ls8.py @@ -16,28 +16,14 @@ import sys from cpu import * -# project_day_num = 2 - -# file_names = [ -# "examples/print8.ls8", # 0 - Day 1 -# "examples/mult.ls8", # 1 - Day 2 -# "examples/stack.ls8", # 2 - Day 3 -# "examples/call.ls8", # 3 - Day 4 -# "examples/stackoverflow.ls8", # 4 -# "examples/interrupts.ls8", # 5 -# "examples/keyboard.ls8", # 6 -# "examples/printstr.ls8", # 7 -# "examples/sctest.ls8", ] # 8 - -# file_name = file_names[project_day_num - 1] - if len(sys.argv) != 2: - # print(f'\nRunning file: {file_name}') print(argv_err_msg) sys.exit(1) else: + # cd .. /S7-Computer-Architecture/M1-4/Computer-Architecture/ls8 + # python3 ls8.py print8.ls8 file_name = sys.argv[1] @@ -45,4 +31,3 @@ cpu = CPU() cpu.load(file_name) cpu.run() -# print() \ No newline at end of file From a054b4e576f1112f9b9045458a90803724bf7864 Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 8 Nov 2020 18:44:51 -0500 Subject: [PATCH 17/21] Implement CMP, JMP, JEQ, JNE ops --- ls8/cpu.py | 34 +++++++++++++++++++++++++++++++++- ls8/ls8.py | 4 ++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 4ae4cf2eb..ce23ce12d 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -12,6 +12,10 @@ CALL = 0b01010000 RET = 0b00010001 ADD = 0b10100000 +CMP = 0b10100111 +JMP = 0b01010100 +JEQ = 0b01010101 +JNE = 0b01010110 class CPU: """Main CPU class.""" @@ -48,6 +52,10 @@ def __init__(self): self.branchtable[CALL] = self.execute_CALL self.branchtable[RET] = self.execute_RET self.branchtable[ADD] = self.execute_ADD + self.branchtable[CMP] = self.execute_CMP + self.branchtable[JMP] = self.execute_JMP + self.branchtable[JEQ] = self.execute_JEQ + self.branchtable[JNE] = self.execute_JNE # Property wrapper for stack pointers @@ -201,7 +209,31 @@ def execute_RET(self): self.sp += 1 def execute_ADD(self): - self.reg[self.operand_a] += self.reg[operand_b] + self.reg[self.operand_a] += self.reg[self.operand_b] + + def execute_CMP(self): + if self.reg[self.operand_a] < self.reg[self.operand_b]: + self.fl = 0b00000100 + elif self.reg[self.operand_a] > self.reg[self.operand_b]: + self.fl = 0b00000010 + else: + self.fl = 0b00000001 + + def execute_JMP(self): + self.pc = self.reg[self.operand_a] + + def execute_JEQ(self): + if self.fl == 0b00000001: + self.execute_JMP() + else: + self.pc += self.instruction_size + + def execute_JNE(self): + if self.fl != 0b00000001: + self.execute_JMP() + else: + self.pc += self.instruction_size + diff --git a/ls8/ls8.py b/ls8/ls8.py index ed7a1222f..b09998487 100755 --- a/ls8/ls8.py +++ b/ls8/ls8.py @@ -22,8 +22,8 @@ sys.exit(1) else: - # cd .. /S7-Computer-Architecture/M1-4/Computer-Architecture/ls8 - # python3 ls8.py print8.ls8 + # cd .. /S7-Computer-Architecture/Sprint-Challenge/Sprint-Challenge--Computer-Architecture + # python3 ls8.py sctest.ls8 file_name = sys.argv[1] From e060e4f8f27c40fb485ffb5c1858e135b3df526e Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 8 Nov 2020 19:03:42 -0500 Subject: [PATCH 18/21] Implement AND, OR, XOR, NOT ops --- ls8/cpu.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ls8/cpu.py b/ls8/cpu.py index ce23ce12d..435c412c5 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -16,6 +16,10 @@ JMP = 0b01010100 JEQ = 0b01010101 JNE = 0b01010110 +AND = 0b10101000 +OR = 0b10101010 +XOR = 0b10101011 +NOT = 0b01101001 class CPU: """Main CPU class.""" @@ -56,6 +60,10 @@ def __init__(self): self.branchtable[JMP] = self.execute_JMP self.branchtable[JEQ] = self.execute_JEQ self.branchtable[JNE] = self.execute_JNE + self.branchtable[AND] = self.execute_AND + self.branchtable[OR] = self.execute_OR + self.branchtable[XOR] = self.execute_XOR + self.branchtable[NOT] = self.execute_NOT # Property wrapper for stack pointers @@ -234,6 +242,18 @@ def execute_JNE(self): else: self.pc += self.instruction_size + def execute_AND(self): + self.reg[sel.operand_a] &= self.reg[self.operand_b] + + def execute_OR(self): + self.reg[self.operand_a] |= self.reg[self.operand_b] + + def execute_XOR(self): + self.reg[self.operand_a] ^= self.reg[self.operand_b] + + def execute_NOT(self): + self.reg[self.operand_a] = ~self.reg[self.operand_a] + From 71d5ddc8dfaae7187a19554a28120044fdf67582 Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 8 Nov 2020 19:19:28 -0500 Subject: [PATCH 19/21] Implement SHL, SHR, MOD ops --- ls8/cpu.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ls8/cpu.py b/ls8/cpu.py index 435c412c5..8a6aaa8aa 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -20,6 +20,9 @@ OR = 0b10101010 XOR = 0b10101011 NOT = 0b01101001 +SHL = 0b10101100 +SHR = 0b10101101 +MOD = 0b10100100 class CPU: """Main CPU class.""" @@ -64,6 +67,9 @@ def __init__(self): self.branchtable[OR] = self.execute_OR self.branchtable[XOR] = self.execute_XOR self.branchtable[NOT] = self.execute_NOT + self.branchtable[SHL] = self.execute_SHL + self.branchtable[SHR] = self.execute_SHR + self.branchtable[MOD] = self.execute_MOD # Property wrapper for stack pointers @@ -254,6 +260,14 @@ def execute_XOR(self): def execute_NOT(self): self.reg[self.operand_a] = ~self.reg[self.operand_a] + def execute_SHL(self): + self.reg[self.operand_a] <<= self.reg[self.operand_b] + + def execute_SHR(self): + self.reg[self.operand_a] >>= self.reg[self.operand_b] + + def execute_MOD(self): + self.reg[self.operand_a] %= self.reg[self.operand_b] From 8950d28be76c81f70ac7ecfa4ee8b6382240fd02 Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 8 Nov 2020 19:22:34 -0500 Subject: [PATCH 20/21] Implement ADDI functionality --- ls8/cpu.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ls8/cpu.py b/ls8/cpu.py index 8a6aaa8aa..8087bbc41 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -23,6 +23,7 @@ SHL = 0b10101100 SHR = 0b10101101 MOD = 0b10100100 +ADDI = 0b10101110 class CPU: """Main CPU class.""" @@ -70,6 +71,8 @@ def __init__(self): self.branchtable[SHL] = self.execute_SHL self.branchtable[SHR] = self.execute_SHR self.branchtable[MOD] = self.execute_MOD + self.branchtable[ADDI] = self.execute_ADDI + # Property wrapper for stack pointers @@ -269,5 +272,8 @@ def execute_SHR(self): def execute_MOD(self): self.reg[self.operand_a] %= self.reg[self.operand_b] + def execute_ADDI(self): + self.reg[self.operand_a] += self.operand_b + From 69f2771cac4610d8789fbf065851e8ad09a5d54a Mon Sep 17 00:00:00 2001 From: Your name Date: Sun, 8 Nov 2020 19:55:06 -0500 Subject: [PATCH 21/21] Update run() in cpu.py // Test program using sctest.ls8 --- ls8/cpu.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 8087bbc41..2e37cb4a0 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -175,14 +175,21 @@ def run(self): """Run the CPU.""" while not self.halted: - self.ir = self.ram_read(self.pc) - operand_a = self.ram_read(self.pc + 1) - operand_b = self.ram_read(self.pc + 2) - if not self.instruction_sets_pc(): - self.pc += self.instruction_size() + # Fetch instructions + self.ir = self.ram_read(self.pc) - self.execute_instruction(operand_a, operand_b) + # Execute instructions + if self.ir in self.branchtable: + self.branchtable[self.ir]() + else: + print(f"Error: Could not find instruction: {self.ir} in branch table.") + sys.exit(1) + + # Conditionally increment program counter + if not self.instruction_sets_pc: + self.pc += self.instruction_size + def execute_instruction(self, operand_a, operand_b): if self.ir in self.branchtable: