From 7741e50ea66bfbc981fe619a73bbe72ddb22e16a Mon Sep 17 00:00:00 2001 From: AbreuOliver Date: Sat, 12 Dec 2020 13:18:48 -0500 Subject: [PATCH 01/13] Initializes commit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 20c417457..11006bff5 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ### Day 1: Get `print8.ls8` running -- [ ] Inventory what is here +- [x] Inventory what is here - [ ] Implement the `CPU` constructor - [ ] Add RAM functions `ram_read()` and `ram_write()` - [ ] Implement the core of `run()` From 3f828e6d1adb5d9f4f83fca807a27a49409cbb6a Mon Sep 17 00:00:00 2001 From: AbreuOliver Date: Sat, 12 Dec 2020 13:25:14 -0500 Subject: [PATCH 02/13] feat(cpu.py): Adds 256 bytes of RAM to CPU --- ls8/cpu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 9a307496e..e082ae375 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -7,7 +7,7 @@ class CPU: def __init__(self): """Construct a new CPU.""" - pass + self.ram = [0] * 256 def load(self): """Load a program into memory.""" From 5c1a6f9d6b1e73c1b83e865868ef92f130d18199 Mon Sep 17 00:00:00 2001 From: AbreuOliver Date: Sat, 12 Dec 2020 13:27:15 -0500 Subject: [PATCH 03/13] feat(cpu.py): Creates 8 general-purpose registers --- ls8/cpu.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ls8/cpu.py b/ls8/cpu.py index e082ae375..6ba91e656 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -8,6 +8,7 @@ class CPU: def __init__(self): """Construct a new CPU.""" self.ram = [0] * 256 + self.reg = [0] * 8 def load(self): """Load a program into memory.""" From cd58eff4d9eeb5e6ddf22a469353e309433cac1d Mon Sep 17 00:00:00 2001 From: AbreuOliver Date: Sat, 12 Dec 2020 13:45:11 -0500 Subject: [PATCH 04/13] feat(cpu.py): Explains general-purpose registers, creates internal registers --- ls8/cpu.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ls8/cpu.py b/ls8/cpu.py index 6ba91e656..dd043231b 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -8,7 +8,28 @@ class CPU: def __init__(self): """Construct a new CPU.""" self.ram = [0] * 256 + # GENERAL-PURPSE REGISTERS: self.reg = [0] * 8 + # --> R0 + # --> R1 + # --> R2 + # --> R3 + # --> R4 + # --> R5: RESERVED FOR INTERRUPT MASK (IM) + # --> R6: RESERVED FOR INTERRUPT STATUS (IS) + # --> R7: RESERVED FOR STACK POINTER (SP) + + # INTERNAL REGISERS: + # --> PROGRAM COUNTER (PC): ADDRESS OF THE CURRENTLY EXECUTING INSTRUCTION + self.pc = 0 + # --> IR: INSTRUCTION REGISTER - CONTAINS A COPY OF THE CURRENTLY EXECUTING INSTRUCTION + self.ir = 0 + # --> MAR: MEMORY ADDRESS REGISTER - HOLDS THE MEMORY ADDRESS CURRENTLY BEING READ OR WRITTEN + self.mar = 0 + # --> MDR: MEMORY DATA REGISTER - HOLDS THE VALUE TO WRITE OR THE VALUE JUST READ + self.mdr = 0 + # --> FL: FLAGS - see below + self.fl = 0 def load(self): """Load a program into memory.""" From 3bedca48115aa61ea41f07b4498e166a9abb5d26 Mon Sep 17 00:00:00 2001 From: AbreuOliver Date: Sat, 12 Dec 2020 13:56:27 -0500 Subject: [PATCH 05/13] feat(cpu.py): Adds RAM functions --- README.md | 2 +- ls8/cpu.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 11006bff5..9486938ee 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ### Day 1: Get `print8.ls8` running - [x] Inventory what is here -- [ ] Implement the `CPU` constructor +- [x] Implement the `CPU` constructor - [ ] Add RAM functions `ram_read()` and `ram_write()` - [ ] Implement the core of `run()` - [ ] Implement the `HLT` instruction handler diff --git a/ls8/cpu.py b/ls8/cpu.py index dd043231b..088153319 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -31,6 +31,14 @@ def __init__(self): # --> FL: FLAGS - see below self.fl = 0 + # `RAM_READ()` - SHOULD ACCEPT THE ADDRESS TO READ AND RETURN THE VALUE STORED + def ram_read(self, address): + return self.ram[address] + + # `RAM_WRITE()` - SHOULD ACCEPT A VALUE TO WRITE AND THE ADDRESS TO WRITE IT TO + def ram_write(self, value, address): + self.ram[address] = value + def load(self): """Load a program into memory.""" From 66469aceda616f6aa9720b540f47372f1cea9792 Mon Sep 17 00:00:00 2001 From: AbreuOliver Date: Sat, 12 Dec 2020 14:13:19 -0500 Subject: [PATCH 06/13] feat(cpu.py): Implements core of 'run()' function --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9486938ee..7837731d0 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ - [x] Inventory what is here - [x] Implement the `CPU` constructor -- [ ] Add RAM functions `ram_read()` and `ram_write()` -- [ ] Implement the core of `run()` +- [x] Add RAM functions `ram_read()` and `ram_write()` +- [x] Implement the core of `run()` - [ ] Implement the `HLT` instruction handler - [ ] Add the `LDI` instruction - [ ] Add the `PRN` instruction From 7e970c7136e00ad373d2eff47447bf1be42c16b0 Mon Sep 17 00:00:00 2001 From: AbreuOliver Date: Sat, 12 Dec 2020 14:14:28 -0500 Subject: [PATCH 07/13] feat(cpu.py): Re-attempts to save implementation of core of 'run()' function --- ls8/cpu.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 088153319..d61328440 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -92,4 +92,19 @@ def trace(self): def run(self): """Run the CPU.""" - pass + while self.running: + command_to_execute = self.ram_read(self.pc) + + if command_to_execute == 0b10000010: # LDI + operation_1 = self.ram_read(self.pc + 1) + operation_2 = self.ram_read(self.pc + 2) + + self.reg[operation_1] = operation_2 + self.pc += 3 + if command_to_execute == 0b01000111: # PRN + operation_1 = self.ram_read(self.pc + 1) + print(self.reg[operation_1]) + self.pc += 2 + if command_to_execute == 0b00000001: # HLT + self.running = False + self.pc += 1 From 2e791f205040e570019ba8d44dd17fa074344d2d Mon Sep 17 00:00:00 2001 From: AbreuOliver Date: Sat, 12 Dec 2020 14:17:53 -0500 Subject: [PATCH 08/13] feat(cpu.py): Implements HLT, LDI & PRN --- README.md | 6 +++--- ls8/cpu.py | 12 +++++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7837731d0..b1fb7c5c8 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ - [x] Implement the `CPU` constructor - [x] Add RAM functions `ram_read()` and `ram_write()` - [x] Implement the core of `run()` -- [ ] Implement the `HLT` instruction handler -- [ ] Add the `LDI` instruction -- [ ] Add the `PRN` instruction +- [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/ls8/cpu.py b/ls8/cpu.py index d61328440..237a9a853 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -2,6 +2,10 @@ import sys +HLT = 0b00000001 +LDI = 0b10000010 +PRN = 0b01000111 + class CPU: """Main CPU class.""" @@ -31,6 +35,8 @@ def __init__(self): # --> FL: FLAGS - see below self.fl = 0 + self.running = True + # `RAM_READ()` - SHOULD ACCEPT THE ADDRESS TO READ AND RETURN THE VALUE STORED def ram_read(self, address): return self.ram[address] @@ -95,16 +101,16 @@ def run(self): while self.running: command_to_execute = self.ram_read(self.pc) - if command_to_execute == 0b10000010: # LDI + if command_to_execute == LDI: operation_1 = self.ram_read(self.pc + 1) operation_2 = self.ram_read(self.pc + 2) self.reg[operation_1] = operation_2 self.pc += 3 - if command_to_execute == 0b01000111: # PRN + if command_to_execute == PRN: operation_1 = self.ram_read(self.pc + 1) print(self.reg[operation_1]) self.pc += 2 - if command_to_execute == 0b00000001: # HLT + if command_to_execute == HLT: self.running = False self.pc += 1 From 2b28d7734d19e736a438974446517c10eb554ced Mon Sep 17 00:00:00 2001 From: AbreuOliver Date: Sat, 12 Dec 2020 14:48:02 -0500 Subject: [PATCH 09/13] feat(cpu.py): Un-hardcodes the machine code --- README.md | 2 +- ls8/README.md | 23 +++++++-------------- ls8/cpu.py | 56 +++++++++++++++++++++++++-------------------------- 3 files changed, 36 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index b1fb7c5c8..1e2a28f0a 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ### Day 2: Add the ability to load files dynamically, get `mult.ls8` running -- [ ] Un-hardcode the machine code +- [x] Un-hardcode the machine code - [ ] Implement the `load()` function to load an `.ls8` file given the filename passed in as an argument - [ ] Implement a Multiply instruction (run `mult.ls8`) diff --git a/ls8/README.md b/ls8/README.md index d9170d968..811d6031c 100644 --- a/ls8/README.md +++ b/ls8/README.md @@ -5,17 +5,12 @@ _Objective_: to gain a deeper understanding of how a CPU functions at a low level. -We're going to write an emulator for the world-famous LambdaSchool-8 computer, -otherwise known as LS-8! This is an 8-bit computer with 8-bit memory addressing, -which is about as simple as it gets. +We're going to write an emulator for the world-famous LambdaSchool-8 computer, otherwise known as LS-8! This is an 8-bit computer with 8-bit memory addressing, which is about as simple as it gets. -An 8 bit CPU is one that only has 8 wires available for addresses (specifying -where something is in memory), computations, and instructions. With 8 bits, our -CPU has a total of 256 bytes of memory and can only compute values up to 255. +An 8 bit CPU is one that only has 8 wires available for addresses (specifying where something is in memory), computations, and instructions. With 8 bits, our CPU has a total of 256 bytes of memory and can only compute values up to 255. The CPU could support 256 instructions, as well, but we won't need them. -For starters, we'll execute code that stores the value 8 in a register, -then prints it out: +For starters, we'll execute code that stores the value 8 in a register, then prints it out: ``` # print8.ls8: Print the number 8 on the screen @@ -48,8 +43,7 @@ This code above requires the implementation of three instructions: See [the LS-8 spec](../LS8-spec.md) for more details. -The above program is already hardcoded into the source file `cpu.py`. To run it, -you will eventually: +The above program is already hardcoded into the source file `cpu.py`. To run it, you will eventually: ``` python3 ls8.py @@ -67,8 +61,7 @@ but you'll have to implement those three above instructions first! ## Step 1: Add the constructor to `cpu.py` -Add list properties to the `CPU` class to hold 256 bytes of memory and 8 -general-purpose registers. +Add list properties to the `CPU` class to hold 256 bytes of memory and 8 general-purpose registers. > Hint: you can make a list of a certain number of zeros with this syntax: > @@ -78,13 +71,11 @@ general-purpose registers. Also add properties for any internal registers you need, e.g. `PC`. -Later on, you might do further initialization here, e.g. setting the initial -value of the stack pointer. +Later on, you might do further initialization here, e.g. setting the initial value of the stack pointer. ## Step 2: Add RAM functions -In `CPU`, add method `ram_read()` and `ram_write()` that access the RAM inside -the `CPU` object. +In `CPU`, add method `ram_read()` and `ram_write()` that access the RAM inside the `CPU` object. `ram_read()` should accept the address to read and return the value stored there. diff --git a/ls8/cpu.py b/ls8/cpu.py index 237a9a853..2c420e979 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -45,26 +45,22 @@ def ram_read(self, address): def ram_write(self, value, address): self.ram[address] = value - def load(self): + def load(self, filename): """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 + with open (filename) as file: + for line in file: + comment_split = line.split("#") + num = comment_split[0].strip() + if num == "": + continue + value = int(num, 2) + self.ram_write(address, value) + address += 1 def alu(self, op, reg_a, reg_b): @@ -100,17 +96,21 @@ def run(self): """Run the CPU.""" while self.running: command_to_execute = self.ram_read(self.pc) - - if command_to_execute == LDI: - operation_1 = self.ram_read(self.pc + 1) - operation_2 = self.ram_read(self.pc + 2) - - self.reg[operation_1] = operation_2 - self.pc += 3 - if command_to_execute == PRN: - operation_1 = self.ram_read(self.pc + 1) - print(self.reg[operation_1]) - self.pc += 2 - if command_to_execute == HLT: - self.running = False - self.pc += 1 + operation_1 = self.ram_read(self.pc + 1) + operation_2 = self.ram_read(self.pc + 2) + + self.execute_instruction(command_to_execute, operation_1, operation_2) + + def execute_instruction(self, instruction, operation_1, operation_2): + if instruction == LDI: + self.reg[operation_1] = operation_2 + self.pc += 3 + elif instruction == PRN: + print(self.reg[operation_1]) + self.pc += 2 + elif instruction == HLT: + self.running = False + self.pc += 1 + else: + print("INVALID INSTRUCTION") + pass From a96f21b686b6a09c1a68029c7c759218f555fd97 Mon Sep 17 00:00:00 2001 From: AbreuOliver Date: Sat, 12 Dec 2020 15:13:21 -0500 Subject: [PATCH 10/13] feat(ls8.py): Implements 'load' function --- ls8/ls8.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ls8/ls8.py b/ls8/ls8.py index 74128d36b..f4531aaa5 100755 --- a/ls8/ls8.py +++ b/ls8/ls8.py @@ -7,5 +7,12 @@ cpu = CPU() -cpu.load() -cpu.run() \ No newline at end of file +try: + filename = sys.argv[1] + with open(filename) as f: + cpu.load(filename) + cpu.run() +except IndexError: + print("Please provide an input") +except FileNotFoundError: + print(f"Error from {sys.argv[0]}: {sys.argv[1]} not found") \ No newline at end of file From 840e2cbe063cde1812962b54e4ee8f9910e58ef7 Mon Sep 17 00:00:00 2001 From: Oliver Abreu Date: Mon, 14 Dec 2020 19:40:15 -0500 Subject: [PATCH 11/13] feat(cpu.py): Hardcodes more instructions --- ls8/cpu.py | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 2c420e979..f212f1825 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -2,9 +2,16 @@ import sys -HLT = 0b00000001 +HLT = 0b00000001 LDI = 0b10000010 PRN = 0b01000111 +MUL = 0b10100010 +PUSH = 0b01000101 +POP = 0b01000110 +CALL = 0b01010000 +RET = 0b00010001 +ADD = 0b10100000 + class CPU: """Main CPU class.""" @@ -12,7 +19,7 @@ class CPU: def __init__(self): """Construct a new CPU.""" self.ram = [0] * 256 - # GENERAL-PURPSE REGISTERS: + # GENERAL-PURPOSE REGISTERS: self.reg = [0] * 8 # --> R0 # --> R1 @@ -23,20 +30,23 @@ def __init__(self): # --> R6: RESERVED FOR INTERRUPT STATUS (IS) # --> R7: RESERVED FOR STACK POINTER (SP) - # INTERNAL REGISERS: - # --> PROGRAM COUNTER (PC): ADDRESS OF THE CURRENTLY EXECUTING INSTRUCTION + # INTERNAL REGISTERS: + # --> (PC) PROGRAM COUNTER --------- ADDRESS OF THE CURRENTLY EXECUTING INSTRUCTION self.pc = 0 - # --> IR: INSTRUCTION REGISTER - CONTAINS A COPY OF THE CURRENTLY EXECUTING INSTRUCTION + # --> (IR) INSTRUCTION REGISTER ---- CONTAINS A COPY OF THE CURRENTLY EXECUTING INSTRUCTION self.ir = 0 - # --> MAR: MEMORY ADDRESS REGISTER - HOLDS THE MEMORY ADDRESS CURRENTLY BEING READ OR WRITTEN + # --> (MAR) MEMORY ADDRESS REGISTER - HOLDS THE MEMORY ADDRESS CURRENTLY BEING READ OR WRITTEN self.mar = 0 - # --> MDR: MEMORY DATA REGISTER - HOLDS THE VALUE TO WRITE OR THE VALUE JUST READ + # --> (MDR) MEMORY DATA REGISTER ---- HOLDS THE VALUE TO WRITE OR THE VALUE JUST READ self.mdr = 0 - # --> FL: FLAGS - see below + # --> (FL) FLAG REGISTER ----------- HOLDS THE CURRENT FLAG STATUS self.fl = 0 self.running = True + # INITIALIZE THE STACKPOINTER + self.reg[7] = 0xF4 + # `RAM_READ()` - SHOULD ACCEPT THE ADDRESS TO READ AND RETURN THE VALUE STORED def ram_read(self, address): return self.ram[address] @@ -52,7 +62,7 @@ def load(self, filename): # For now, we've just hardcoded a program: - with open (filename) as file: + with open(filename) as file: for line in file: comment_split = line.split("#") num = comment_split[0].strip() @@ -62,13 +72,12 @@ def load(self, filename): self.ram_write(address, value) 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 == "SUB": etc + # elif op == "SUB": etc else: raise Exception("Unsupported ALU operation") @@ -80,8 +89,8 @@ def trace(self): print(f"TRACE: %02X | %02X %02X %02X |" % ( self.pc, - #self.fl, - #self.ie, + # self.fl, + # self.ie, self.ram_read(self.pc), self.ram_read(self.pc + 1), self.ram_read(self.pc + 2) @@ -99,9 +108,10 @@ def run(self): operation_1 = self.ram_read(self.pc + 1) operation_2 = self.ram_read(self.pc + 2) - self.execute_instruction(command_to_execute, operation_1, operation_2) + self.execute_instruction( + command_to_execute, operation_1, operation_2) - def execute_instruction(self, instruction, operation_1, operation_2): + def execute_instruction(self, instruction, operation_1, operation_2): if instruction == LDI: self.reg[operation_1] = operation_2 self.pc += 3 From 1e9adb332efd512c8d5c1254a20b5918d887fb54 Mon Sep 17 00:00:00 2001 From: Oliver Abreu Date: Mon, 14 Dec 2020 22:16:12 -0500 Subject: [PATCH 12/13] feat(cpu.py): Imports os.path and codes more instructions --- ls8/cpu.py | 141 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 106 insertions(+), 35 deletions(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index f212f1825..0dbfd0db3 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -1,6 +1,7 @@ """CPU functionality.""" import sys +import os.path HLT = 0b00000001 LDI = 0b10000010 @@ -47,30 +48,77 @@ def __init__(self): # INITIALIZE THE STACKPOINTER self.reg[7] = 0xF4 + 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 + self.branchtable[CALL] = self.execute_CALL + self.branchtable[RET] = self.execute_RET + self.branchtable[ADD] = self.execute_ADD + + @property + def sp(self): + return self.reg[7] + + @sp.setter + 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 + # `RAM_READ()` - SHOULD ACCEPT THE ADDRESS TO READ AND RETURN THE VALUE STORED - def ram_read(self, address): - return self.ram[address] + def ram_read(self, mar): + if mar >= 0 and mar < len(self.ram): + return self.ram[mar] + else: + print( + f"Error: No memory at address '{mar}' ") + return -1 # `RAM_WRITE()` - SHOULD ACCEPT A VALUE TO WRITE AND THE ADDRESS TO WRITE IT TO def ram_write(self, value, address): - self.ram[address] = value + if mar >= 0 and mar < len(self.ram): + self.ram[mar] = mdr & 0xFF + else: + print(f"Error: Unable to write to memory at address '{mar}' ") def load(self, filename): """Load a program into memory.""" - address = 0 - # For now, we've just hardcoded a program: - - with open(filename) as file: - for line in file: - comment_split = line.split("#") - num = comment_split[0].strip() - if num == "": - continue - value = int(num, 2) - self.ram_write(address, value) - address += 1 + file_path = os.path.join(os.path.dirname(__file__), file_name) + try: + with open(file_path) as file: + for line in file: + num = line.split("#")[0].strip() # "10000010" + try: + instruction = int(num, 2) + self.ram[address] = instruction + address += 1 + except: + continue + except: + print(f"Could not find file with name '{file_name}' ") + sys.exit(1) def alu(self, op, reg_a, reg_b): """ALU operations.""" @@ -104,23 +152,46 @@ def trace(self): def run(self): """Run the CPU.""" while self.running: - command_to_execute = self.ram_read(self.pc) - operation_1 = self.ram_read(self.pc + 1) - operation_2 = self.ram_read(self.pc + 2) - - self.execute_instruction( - command_to_execute, operation_1, operation_2) - - def execute_instruction(self, instruction, operation_1, operation_2): - if instruction == LDI: - self.reg[operation_1] = operation_2 - self.pc += 3 - elif instruction == PRN: - print(self.reg[operation_1]) - self.pc += 2 - elif instruction == HLT: - self.running = False - self.pc += 1 - else: - print("INVALID INSTRUCTION") - pass + + self.ir = self.ram_read(self.pc) + + if self.ir in self.branchtable: + self.branchtable[self.ir]() + else: + print(f"Operation '{self.ir}' could be found") + sys.exit(1) + + + def execute_HLT(self): + self.halted = True + + def execute_LDI(self): + self.reg[self.operand_a] = self.operand_b + + def execute_PRN(self): + print(self.reg[self.operand_a]) + + def execute_MUL(self): + self.reg[self.operand_a] *= self.reg[self.operand_b] + + def execute_PUSH(self): + self.sp -= 1 + self.mdr = self.reg[self.operand_a] + self.ram_write(self.sp, self.mdr) + + def execute_POP(self): + self.mdr = self.ram_read(self.sp) + self.reg[self.operand_a] = self.mdr + self.sp += 1 + + def execute_CALL(self): + self.sp -= 1 + self.ram_write(self.sp, self.pc + self.instruction_size) + self.pc = self.reg[self.operand_a] + + def execute_RET(self): + self.pc = self.ram_read(self.sp) + self.sp += 1 + + def execute_ADD(self): + self.reg[self.operand_a] += self.reg[self.operand_b] From 1cbd809015707c62b93676fb9623d6f6a4b0445d Mon Sep 17 00:00:00 2001 From: Oliver Abreu Date: Mon, 14 Dec 2020 22:46:38 -0500 Subject: [PATCH 13/13] fix(cpu.py): Corrects typos in code to produce working file --- README.md | 12 ++++---- ls8/cpu.py | 85 +++++++++++++++++++++++++++++++++++++----------------- ls8/ls8.py | 17 +++++------ 3 files changed, 73 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 1e2a28f0a..64dd7b2b3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Project -* [Implement the LS-8 Emulator](ls8/) +- [Implement the LS-8 Emulator](ls8/) ## Task List: add this to the first comment of your Pull Request @@ -19,18 +19,18 @@ ### Day 2: Add the ability to load files dynamically, get `mult.ls8` running - [x] Un-hardcode the machine code -- [ ] Implement the `load()` function to load an `.ls8` file given the filename +- [x] Implement the `load()` function to load an `.ls8` file given the filename passed in as an argument -- [ ] Implement a Multiply instruction (run `mult.ls8`) +- [x] Implement a Multiply instruction (run `mult.ls8`) ### Day 3: Stack -- [ ] Implement the System Stack and be able to run the `stack.ls8` program +- [x] Implement the System Stack and be able to run the `stack.ls8` program ### Day 4: Get `call.ls8` running -- [ ] Implement the CALL and RET instructions -- [ ] Implement Subroutine Calls and be able to run the `call.ls8` program +- [x] Implement the CALL and RET instructions +- [x] Implement Subroutine Calls and be able to run the `call.ls8` program ### Stretch diff --git a/ls8/cpu.py b/ls8/cpu.py index 0dbfd0db3..56406f230 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: @@ -58,34 +62,37 @@ 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 - def sp(self): - return self.reg[7] + @property + def sp(self): + return self.reg[7] - @sp.setter - def sp(self, a): - self.reg[7] = a & 0xFF + @sp.setter + 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_a(self): - return self.ram_read(self.pc + 1) + @property + def operand_b(self): + return self.ram_read(self.pc + 2) - @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_size(self): - return ((self.ir >> 6) & 0b11) + 1 - - @property - def instruction_sets_pc(self): - return ((self.ir >> 4) & 0b0001) == 1 + @property + def instruction_sets_pc(self): + return ((self.ir >> 4) & 0b0001) == 1 # `RAM_READ()` - SHOULD ACCEPT THE ADDRESS TO READ AND RETURN THE VALUE STORED + def ram_read(self, mar): if mar >= 0 and mar < len(self.ram): return self.ram[mar] @@ -95,7 +102,7 @@ def ram_read(self, mar): return -1 # `RAM_WRITE()` - SHOULD ACCEPT A VALUE TO WRITE AND THE ADDRESS TO WRITE IT TO - def ram_write(self, value, address): + def ram_write(self, mar, mdr): if mar >= 0 and mar < len(self.ram): self.ram[mar] = mdr & 0xFF else: @@ -105,11 +112,11 @@ def load(self, filename): """Load a program into memory.""" address = 0 - file_path = os.path.join(os.path.dirname(__file__), file_name) + file_path = os.path.join(os.path.dirname(__file__), filename) try: with open(file_path) as file: for line in file: - num = line.split("#")[0].strip() # "10000010" + num = line.split("#")[0].strip() try: instruction = int(num, 2) self.ram[address] = instruction @@ -117,7 +124,7 @@ def load(self, filename): except: continue except: - print(f"Could not find file with name '{file_name}' ") + print(f"Could not find file with name '{filename}' ") sys.exit(1) def alu(self, op, reg_a, reg_b): @@ -152,7 +159,7 @@ def trace(self): def run(self): """Run the CPU.""" while self.running: - + self.ir = self.ram_read(self.pc) if self.ir in self.branchtable: @@ -161,6 +168,9 @@ def run(self): print(f"Operation '{self.ir}' could be found") sys.exit(1) + # ENSURE THAT THE PROGRAM COUNTER IS INCREMENTED + if not self.instruction_sets_pc: + self.pc += self.instruction_size def execute_HLT(self): self.halted = True @@ -194,4 +204,27 @@ def execute_RET(self): self.sp += 1 def execute_ADD(self): - self.reg[self.operand_a] += self.reg[self.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 f4531aaa5..559671379 100755 --- a/ls8/ls8.py +++ b/ls8/ls8.py @@ -7,12 +7,11 @@ cpu = CPU() -try: - filename = sys.argv[1] - with open(filename) as f: - cpu.load(filename) - cpu.run() -except IndexError: - print("Please provide an input") -except FileNotFoundError: - print(f"Error from {sys.argv[0]}: {sys.argv[1]} not found") \ No newline at end of file +if len(sys.argv) != 2: + print(f"Error from {sys.argv[0]}: {sys.argv[1]} not found") + sys.exit(1) +else: + file_name = sys.argv[1] + +cpu.load(file_name) +cpu.run()