From ec2e886820bed2be55595b1ff2a42e22debd461b Mon Sep 17 00:00:00 2001 From: Toosdai Otte Date: Wed, 2 Dec 2020 23:57:55 -0600 Subject: [PATCH 1/6] inventory --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 20c417457..01033b6ea 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,9 @@ ### Day 1: Get `print8.ls8` running -- [ ] Inventory what is here +- [x] Inventory what is here + - cpu.py: defines CPU class (has commands lsited out) + - ls8.py: uses a CPU instance to run a program - [ ] Implement the `CPU` constructor - [ ] Add RAM functions `ram_read()` and `ram_write()` - [ ] Implement the core of `run()` From 08eeb4c45c918edc0b631d53e3bf946709f7d559 Mon Sep 17 00:00:00 2001 From: Toosdai Otte Date: Wed, 2 Dec 2020 23:58:25 -0600 Subject: [PATCH 2/6] inventory --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01033b6ea..7de5a11ea 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ### Day 1: Get `print8.ls8` running - [x] Inventory what is here - - cpu.py: defines CPU class (has commands lsited out) + - cpu.py: defines CPU class (has commands listed out) - ls8.py: uses a CPU instance to run a program - [ ] Implement the `CPU` constructor - [ ] Add RAM functions `ram_read()` and `ram_write()` From 34c3efd847da2d1e9f0dc7233bc7a2dc2e349cce Mon Sep 17 00:00:00 2001 From: Toosdai Otte Date: Thu, 3 Dec 2020 15:56:13 -0600 Subject: [PATCH 3/6] cpu constructor setup --- README.md | 2 +- ls8/cpu.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7de5a11ea..7c3ca328a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ - [x] Inventory what is here - cpu.py: defines CPU class (has commands listed out) - ls8.py: uses a CPU instance to run a program -- [ ] 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 9a307496e..c88dfdc67 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -7,7 +7,10 @@ class CPU: def __init__(self): """Construct a new CPU.""" - pass + self.mem = [0] * 256 + self.pc = 0 + self.registers = [0] * 8 + self.running = True def load(self): """Load a program into memory.""" From aabe03aa75895332053bdf48b17a2d908dbafbd4 Mon Sep 17 00:00:00 2001 From: Toosdai Otte Date: Thu, 3 Dec 2020 16:00:53 -0600 Subject: [PATCH 4/6] add RAM funtions read and write --- README.md | 2 +- ls8/cpu.py | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7c3ca328a..45c389397 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ - cpu.py: defines CPU class (has commands listed out) - ls8.py: uses a CPU instance to run a program - [x] Implement the `CPU` constructor -- [ ] Add RAM functions `ram_read()` and `ram_write()` +- [x] Add RAM functions `ram_read()` and `ram_write()` - [ ] Implement the core of `run()` - [ ] Implement the `HLT` instruction handler - [ ] Add the `LDI` instruction diff --git a/ls8/cpu.py b/ls8/cpu.py index c88dfdc67..3a2839c5f 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -7,9 +7,17 @@ class CPU: def __init__(self): """Construct a new CPU.""" - self.mem = [0] * 256 + # Set RAM + self.ram = [0] * 256 + # Set program counter to 0 self.pc = 0 + # Create registers self.registers = [0] * 8 + # memory address register + self.mar = 0 + # memory data register + self.mdr = 0 + self.running = True def load(self): @@ -66,3 +74,9 @@ def trace(self): def run(self): """Run the CPU.""" pass + + def ram_read(self, mar): + return self.ram[mar] + + def rem_write(self, mar, mdr): + self.ram[mar] = mdr \ No newline at end of file From 0d60a6c74205fed2e38b37e507f9c13665332ade Mon Sep 17 00:00:00 2001 From: Toosdai Otte Date: Sun, 13 Dec 2020 21:01:31 -0600 Subject: [PATCH 5/6] Day 1 complete --- README.md | 8 ++++---- ls8/cpu.py | 42 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 45c389397..933b868ae 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,10 @@ - ls8.py: uses a CPU instance to run a program - [x] Implement the `CPU` constructor - [x] 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] 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/ls8/cpu.py b/ls8/cpu.py index 3a2839c5f..177924e19 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -2,11 +2,19 @@ import sys +HLT = 0b00000001 +LDI = 0b00000010 +PRN = 0b01000111 + class CPU: """Main CPU class.""" def __init__(self): """Construct a new CPU.""" + + # add a list property for memory/ram + # ditto for registers + # Set RAM self.ram = [0] * 256 # Set program counter to 0 @@ -51,6 +59,13 @@ def alu(self, op, reg_a, reg_b): else: raise Exception("Unsupported ALU operation") + + def ram_read(self, mar): + return self.ram[mar] + + def rem_write(self, mar, mdr): + self.ram[mar] = mdr + def trace(self): """ Handy function to print out the CPU state. You might want to call this @@ -73,10 +88,25 @@ def trace(self): def run(self): """Run the CPU.""" - pass + + while not self.halted: + ir = self.ram_read(self.pc) + operand_a = self.ram_read(self.pc+1) + operand_b = self.ram_read(self.pc+2) + self.execute_instruction(ir, operand_a, operand_b) + + def execute_instruction(self, instruction, operand_a, operand_b): + if instruction == HLT: + self.halted = True + self.pc += 1 + + elif instruction == PRN: + print(self.registers[operand_a]) + self.pc += 2 + + elif instruction == LDI: + self.reigsters[operand_a] = operand_b + self.pc += 3 - def ram_read(self, mar): - return self.ram[mar] - - def rem_write(self, mar, mdr): - self.ram[mar] = mdr \ No newline at end of file + else: + print("Invalid") \ No newline at end of file From 44c3408189d081675a36d47288b28e89bbfcde62 Mon Sep 17 00:00:00 2001 From: Toosdai Otte Date: Tue, 15 Dec 2020 21:18:34 -0600 Subject: [PATCH 6/6] days 2-sprint done, examples files running --- README.md | 12 ++-- ls8/cpu.py | 205 +++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 157 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 933b868ae..357f82d6d 100644 --- a/README.md +++ b/README.md @@ -20,19 +20,19 @@ ### Day 2: Add the ability to load files dynamically, get `mult.ls8` running -- [ ] Un-hardcode the machine code -- [ ] Implement the `load()` function to load an `.ls8` file given the filename +- [x] Un-hardcode the machine code +- [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 177924e19..0767a61dd 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -3,8 +3,19 @@ import sys HLT = 0b00000001 -LDI = 0b00000010 +LDI = 0b10000010 PRN = 0b01000111 +MUL = 0b10100010 +ADD = 0b10100000 +PUSH = 0b01000101 +POP = 0b01000110 +CALL = 0b01010000 +RET = 0b00010001 + +CMP = 0b10100111 +JEQ = 0b01010101 +JNE = 0b01010110 +JMP = 0b01010100 class CPU: """Main CPU class.""" @@ -12,59 +23,70 @@ class CPU: def __init__(self): """Construct a new CPU.""" - # add a list property for memory/ram - # ditto for registers - # Set RAM self.ram = [0] * 256 + # Create registers + self.reg = [0] * 8 + self.reg[7] = 0XF4 + self.reg[6] = 0 # Set program counter to 0 self.pc = 0 - # Create registers - self.registers = [0] * 8 - # memory address register - self.mar = 0 - # memory data register - self.mdr = 0 - - self.running = True + self.running = True + + 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 - + try: + with open(sys.argv[1]) as f: + address = 0 + for line in f: + maybe_binary_number = line[:line.find('#')] + if maybe_binary_number == '': + continue + denary_int = int(maybe_binary_number, 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: self.reg[reg_a] += self.reg[reg_b] - #elif op == "SUB": etc + elif op == MUL: + self.reg[reg_a] *= self.reg[reg_b] + elif op == CMP: # compare + if self.reg[reg_a] < self.reg[reg_b]: + self.reg[6] = 0b00000100 + elif self.reg[reg_a] > self.reg[reg_b]: + self.reg[6] = 0b00000010 + else: + self.reg[6] = 0b00000001 else: raise Exception("Unsupported ALU operation") + def ram_read(self, address): + return self.ram[address] + + def ram_write(self, address, value): + self.ram[address] = value - def ram_read(self, mar): - return self.ram[mar] - - def rem_write(self, mar, mdr): - self.ram[mar] = mdr def trace(self): """ @@ -74,7 +96,7 @@ def trace(self): print(f"TRACE: %02X | %02X %02X %02X |" % ( self.pc, - #self.fl, + # self.fl, #self.ie, self.ram_read(self.pc), self.ram_read(self.pc + 1), @@ -88,25 +110,100 @@ def trace(self): def run(self): """Run the CPU.""" - - while not self.halted: - ir = self.ram_read(self.pc) - operand_a = self.ram_read(self.pc+1) + IR = [] + self.running = True + while self.running: + IR = self.ram[self.pc] + 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) - self.execute_instruction(ir, operand_a, operand_b) + 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: + self.pc += 1 + num_args - def execute_instruction(self, instruction, operand_a, operand_b): - if instruction == HLT: - self.halted = True - self.pc += 1 - - elif instruction == PRN: - print(self.registers[operand_a]) + 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): + + 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: self.pc += 2 - elif instruction == LDI: - self.reigsters[operand_a] = operand_b - self.pc += 3 + def jne(self, operand_a, operand_b): + + flags = self.reg[6] + equal_is_flagged = flags == 1 + if equal_is_flagged: + self.pc += 2 else: - print("Invalid") \ No newline at end of file + # 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): + 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): + SP = self.reg[7] + value = self.ram[SP] + # Copy the value from the address pointed to by SP to the given register. + self.reg[operand_a] = value + self.reg[7] += 1 + + + def call(self, operand_a, operand_b): + # PUSH + return_address = self.pc + 2 + + self.reg[7] -= 1 + + SP = self.reg[7] + self.ram[SP] = return_address + reg_idx = self.ram[self.pc+1] + subroutine_address = self.reg[reg_idx] + + 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 pc + self.pc = return_address + self.reg[7] += 1 \ No newline at end of file