diff --git a/ls8/cpu.py b/ls8/cpu.py index 9a307496e..fb69e1870 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -2,41 +2,138 @@ import sys +LDI = 0b10000010 +PRN = 0b01000111 +HLT = 0b00000001 +MUL = 0b10100010 +POP = 0b01000110 +PUSH = 0b01000101 +CALL = 0b01010000 +RET = 0b00010001 +ADD = 0b10100000 +JNE = 0b01010110 +JEQ = 0b01010101 +JMP = 0b01010100 +CMP = 0b10100111 + + class CPU: """Main CPU class.""" def __init__(self): """Construct a new CPU.""" - pass - - 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 + self.ram = [0] * 256 + self.reg = [0] * 8 + self.pc = 0 + self.sp = 0xF4 + self.flags = 0b00000000 + + self.branchtable = {} + self.branchtable[LDI] = self.ldi + self.branchtable[PRN] = self.prn + self.branchtable[HLT] = self.hlt + self.branchtable[MUL] = self.mul + self.branchtable[POP] = self.pop + self.branchtable[PUSH] = self.push + self.branchtable[CALL] = self.call + self.branchtable[RET] = self.ret + self.branchtable[ADD] = self.add + self.branchtable[JNE] = self.jne + self.branchtable[JEQ] = self.jeq + self.branchtable[JMP] = self.jmp + self.branchtable[CMP] = self.cmp + + """ START ALU function calls""" + + def ldi(self, operand_a, operand_b): + self.reg[operand_a] = operand_b + self.pc += 3 + + def prn(self, operand_a, operand_b): + print(self.reg[operand_a]) + self.pc += 2 + + def hlt(self, operand_a, operand_b): + sys.exit(1) + + def mul(self, operand_a, operand_b): + self.alu("MUL", operand_a, operand_b) + self.pc += 3 + + def add(self, operand_a, operand_b): + self.alu("ADD", operand_a, operand_b) + self.pc += 3 + + def cmp(self, operand_a, operand_b): + self.alu("CMP", operand_a, operand_b) + self.pc += 3 + + def pop(self, operand_a, operand_b): + value = self.ram[self.sp] + self.reg[operand_a] = value + self.pc += 2 + + def push(self, operand_a, operand_b): + self.sp -= 1 + value = self.reg[operand_a] + self.ram_write(self.sp, value) + self.pc += 2 + + def call(self, operand_a, operand_b): + self.sp -= 1 + value = self.pc + 2 + self.ram_write(self.sp, value) + self.pc = self.reg[operand_a] + + def ret(self, operand_a, operand_b): + value = self.ram[self.sp] + self.pc = value + + def jmp(self, operand_a, operand_b): + self.pc = self.reg[operand_a] + + def jeq(self, operand_a, operand_b): + if self.flags == 0b00000001: + self.pc = self.reg[operand_a] + else: + self.pc += 2 + def jne(self, operand_a, operand_b): + if self.flags != 0b00000001: + self.pc = self.reg[operand_a] + else: + self.pc += 2 + + def load(self, filename): + try: + address = 0 + with open(filename) as f: + for line in f: + comment_split = line.split("#") + num = comment_split[0].strip() + if len(num) == 0: + continue + value = int(num, 2) + self.ram[address] = value + address += 1 + except FileNotFoundError: + print(f"{sys.argv[0]}: {sys.argv[1]} not found") + sys.exit(2) 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 == "MUL": + self.reg[reg_a] *= self.reg[reg_b] + elif op == "CMP": + if self.reg[reg_a] > self.reg[reg_b]: + self.flags = 0b00000010 + elif self.reg[reg_a] < self.reg[reg_b]: + self.flags = 0b00000100 + else: + self.flags = 0b00000001 else: raise Exception("Unsupported ALU operation") @@ -48,8 +145,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) @@ -60,6 +157,21 @@ def trace(self): print() + def ram_read(self, address): + return self.ram[address] + + def ram_write(self, address, value): + self.ram[address] = value + def run(self): """Run the CPU.""" - pass + + while True: + IR = self.ram_read(self.pc) + operand_a = self.ram_read(self.pc + 1) + operand_b = self.ram_read(self.pc + 2) + + if IR not in self.branchtable: + sys.exit(1) + else: + self.branchtable[IR](operand_a, operand_b) diff --git a/ls8/cpu2.py b/ls8/cpu2.py new file mode 100644 index 000000000..24a5b0a04 --- /dev/null +++ b/ls8/cpu2.py @@ -0,0 +1,134 @@ +"""CPU functionality.""" + +import sys + + +LDI = 0b10000010 +PRN = 0b01000111 +HLT = 0b00000001 +MUL = 0b10100010 +POP = 0b01000110 +PUSH = 0b01000101 +ADD = 0b10100000 + + +class CPU: + """Main CPU class.""" + + def __init__(self): + """Construct a new CPU.""" + self.ram = [0] * 255 + self.reg = [0] * 16 + self.pc = 0 + self.sp = 6 + + self.branchtable = {} + self.branchtable[LDI] = self.ldi + self.branchtable[PRN] = self.prn + self.branchtable[HLT] = self.hlt + self.branchtable[MUL] = self.mul + self.branchtable[POP] = self.pop + self.branchtable[PUSH] = self.push + + def alu(self, op, reg_a, reg_b): + """ALU operations.""" + + if op == "ADD": + self.reg[reg_a] += self.reg[reg_b] + elif op == "MUL": + self.reg[reg_a] *= self.reg[reg_b] + else: + raise Exception("Unsupported ALU operation") + + def ldi(self, op_a, op_b): + self.reg[op_a] = op_b + self.pc += (self.ram[self.pc] >> 6) + 1 + print(':', self.pc) + + def prn(self, val): + print(self.reg[val]) + self.pc += (self.ram[self.pc] >> 6) + 1 + print(':', self.pc) + + def hlt(self): + sys.exit(2) + + def mul(self, op_a, op_b): + self.alu('MUL', op_a, op_b) + # print(op_a, op_b) + self.pc += (self.ram[self.pc] >> 6) + 1 + print(':', self.pc) + + def pop(self, val): + stack_value = self.ram[self.sp] + self.reg[val] = stack_value + self.sp += 1 + self.pc += (self.ram[self.pc] >> 6) + 1 + print(':', self.pc) + + def push(self, val): + self.sp -= 1 + value = self.reg[val] + self.ram_write(self.sp, value) + self.pc += (self.ram[self.pc] >> 6) + 1 + print(':', self.pc) + + 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.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.""" + IR = self.ram[self.pc] + running = True + while running: + IR = self.ram[self.pc] + operand_a = self.ram_read(self.pc+1) + operand_b = self.ram_read(self.pc+2) + + self.branchtable[LDI](operand_a, operand_b) + self.branchtable[PUSH](operand_a) + self.branchtable[POP](operand_a) + self.branchtable[MUL](operand_a, operand_b) + self.branchtable[PRN](operand_a) + self.branchtable[HLT]() + + # self.pc += (IR >> 6) + 1 + + def ram_read(self, address): + return self.ram[address] + + def ram_write(self, address, value): + self.ram[address] = value + + def load(self, filename): + try: + address = 0 + with open(filename) as f: + for line in f: + comment_split = line.split("#") + num = comment_split[0].strip() + if len(num) == 0: + continue + value = int(num, 2) + self.ram[address] = value + address += 1 + except FileNotFoundError: + print(f"{sys.argv[0]}: {sys.argv[1]} not found") + sys.exit(2) diff --git a/ls8/examples/stack.ls8 b/ls8/examples/stack.ls8 index e7c6e6687..a5a7c8bae 100644 --- a/ls8/examples/stack.ls8 +++ b/ls8/examples/stack.ls8 @@ -29,3 +29,4 @@ 01000111 # PRN R1 00000001 00000001 # HLT + diff --git a/ls8/ls8.py b/ls8/ls8.py index 74128d36b..955c573af 100755 --- a/ls8/ls8.py +++ b/ls8/ls8.py @@ -7,5 +7,10 @@ cpu = CPU() -cpu.load() -cpu.run() \ No newline at end of file +# cpu.load() +cpu.load(sys.argv[1]) +cpu.run() +# for i in cpu.ram: +# if i != 0: +# print(i) +