diff --git a/README.md b/README.md index 20c417457..b381380be 100644 --- a/README.md +++ b/README.md @@ -8,28 +8,31 @@ ### 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 + - cpu.py - Defines a CPU class this is where the program is ran and commands listed out + - ls8.py - Uses a CPU instance to run a program + - ls8-spec.md - A list of the program specs +- [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 -- [ ] 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 +- [x] Implement the CALL and RET instructions - [ ] Implement Subroutine Calls and be able to run the `call.ls8` program ### Stretch diff --git a/ls8/cpu.py b/ls8/cpu.py index 9a307496e..d9db56bab 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -2,40 +2,95 @@ import sys +HLT = 0b00000001 +LDI = 0b10000010 +PRN = 0b01000111 +MUL = 0b10100010 +PUSH = 0b01000101 +POP = 0b01000110 + +SP = 7 + class CPU: """Main CPU class.""" def __init__(self): """Construct a new CPU.""" - pass - - def load(self): + #Add list properties to the `CPU` class to hold 256 bytes of memory + self.ram = [0] * 256 + # and 8 general-purpose registers. + self.reg = [0] * 8 + self.reg[7] = 0xF4 + #R0 + #R1 + #R2 + #R3 + #R4 + #R5 reserved as the interrupt mask (IM) + #R6 reserved as the interrupt status (IS) + #R7 reserved as the stack pointer (SP) + + #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 + self.ir = 0 + #MAR: Memory Address Register, holds the memory address we're reading or writing + 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 + # Running + self.running = True + + #`ram_read()` should accept the address to read and return the value stored there. + 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, address, val): + self.ram[address] = val + + 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 - - + # 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 + + # open the file + with open(filename) as my_file: + # go through each line to parse and get + # the instruction + for line in my_file: + # try and get the instruction/operand in the line + comment_split = line.split("#") + maybe_binary_number = comment_split[0] + try: + x = int(maybe_binary_number, 2) + #print("{:08b}: {:d}".format(x, x)) + self.ram_write(x, address) + address += 1 + except: + continue + def alu(self, op, reg_a, reg_b): """ALU operations.""" - if op == "ADD": - self.reg[reg_a] += self.reg[reg_b] + if op == MUL: + self.reg[reg_a] *= self.reg[reg_b] #elif op == "SUB": etc else: raise Exception("Unsupported ALU operation") @@ -62,4 +117,39 @@ def trace(self): def run(self): """Run the CPU.""" - pass + 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): + print(instruction) + 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 + elif instruction == MUL: + self.alu(instruction, operation_1, operation_2) + self.pc += 3 + elif instruction == PUSH: + # decrement the stack pointer + self.reg[SP] -= 1 + # store the operaind in the stack + self.ram_write(self.reg[operation_1], self.reg[SP]) + self.pc += 2 + elif instruction == POP: + self.reg[operation_1] = self.ram_read(self.reg[SP]) + self.reg[SP] += 1 + self.pc += 2 + else: + print("I don't know what to do.") + sys.exit(1) + pass + diff --git a/ls8/ls8.py b/ls8/ls8.py index 74128d36b..cbdff7a30 100755 --- a/ls8/ls8.py +++ b/ls8/ls8.py @@ -5,7 +5,9 @@ import sys from cpu import * -cpu = CPU() - -cpu.load() -cpu.run() \ No newline at end of file +if len(sys.argv) != 2: + print("Wrong number of arguments passed in") +else: + cpu = CPU() + cpu.load(sys.argv[1]) + cpu.run()