From b882e6aa2b23ee7998573fdd728998f4292c5884 Mon Sep 17 00:00:00 2001 From: ThisIsJorgeLima Date: Mon, 15 Jun 2020 12:27:29 -0400 Subject: [PATCH 1/5] initial commit --- notebooks/day01.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 notebooks/day01.py diff --git a/notebooks/day01.py b/notebooks/day01.py new file mode 100644 index 000000000..e69de29bb From b5e51a924c57d14735a1433d54a7ae57ef399f84 Mon Sep 17 00:00:00 2001 From: ThisIsJorgeLima Date: Tue, 16 Jun 2020 09:49:56 -0400 Subject: [PATCH 2/5] MVP complete --- ls8/cpu.py | 79 ++++++++++++++++++++++++++++++++++++++----- ls8/ls8.py | 2 +- notebooks/comp.py | 76 +++++++++++++++++++++++++++++++++++++++++ notebooks/comp00.py | 79 +++++++++++++++++++++++++++++++++++++++++++ notebooks/day01.py | 0 notebooks/practice.py | 31 +++++++++++++++++ 6 files changed, 257 insertions(+), 10 deletions(-) create mode 100644 notebooks/comp.py create mode 100644 notebooks/comp00.py delete mode 100644 notebooks/day01.py create mode 100644 notebooks/practice.py diff --git a/ls8/cpu.py b/ls8/cpu.py index 9a307496e..9c1aa1d3a 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -2,12 +2,36 @@ import sys + +# Step 1: Add the constructor to cpu.py +# x = [0] * 25 # x is a list of 25 zeroes +# 256 bytes in total + + class CPU: """Main CPU class.""" def __init__(self): """Construct a new CPU.""" - pass + # RAM + self.ram = [0] * 256 + # Our registers + self.registers = [0] * 8 # 8 general-purpose registers, like variables, R0, R1, R2 .. R7 + # internal registers + self.pc = 0 # Program Counter, index of the current instruction + + +# Step 2: Add RAM functions +# ram_read() should accept the address to read and return the value stored there. + +# ram_write() should accept a value to write, and the address to write it to. + + + def ram_read(self, address): + return self.ram[address] + + def ram_write(self, value, address): + self.ram[address] = value def load(self): """Load a program into memory.""" @@ -18,25 +42,24 @@ def load(self): program = [ # From print8.ls8 - 0b10000010, # LDI R0,8 + 0b10000010, # LDI R0,8 0b00000000, 0b00001000, - 0b01000111, # PRN R0 + 0b01000111, # PRN R0 0b00000000, - 0b00000001, # HLT + 0b00000001, # HLT ] for instruction in program: self.ram[address] = instruction 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") @@ -48,8 +71,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) @@ -62,4 +85,42 @@ def trace(self): def run(self): """Run the CPU.""" - pass + + +# Step 3: Implement the core of CPU's run() method +# Step 4: Implement the HLT instruction handler +# Step 5: Add the LDI instruction +# Step 6: Add the PRN instruction +# Step 7: Un-hardcode the machine code +# Step 8: Implement a Multiply and Print the Result +# Step 9: Beautify your run() loop +# Step 10: Implement System Stack +# Step 11: Implement Subroutine Calls + + HLT = 0b00000001 # Instruction handler + LDI = 0b10000010 # instruction + PRN = 0b01000111 # PRN instruction + running = True + + self.ir = self.ram_read(self.pc) + reg_num1 = self.ram_read(self.pc + 1) + reg_num2 = self.ram_read(self.pc + 2) + + while running: + # Lets set our value of our register + if self.ir == LDI: + self.reg[reg_num1] = reg_num2 + self.pc += 3 + # print + elif self.ir == PRN: + print(self.reg[reg_num1]) # print our stored value + self.pc += 2 + # Halt our CPU and exit + elif self.ir == HLT: + running = False + self.pc += 1 + + else: + print(f"Unknown instruction {ir} at address {pc}") + + sys.exit(1) diff --git a/ls8/ls8.py b/ls8/ls8.py index 74128d36b..b9bffd194 100755 --- a/ls8/ls8.py +++ b/ls8/ls8.py @@ -8,4 +8,4 @@ cpu = CPU() cpu.load() -cpu.run() \ No newline at end of file +cpu.run() diff --git a/notebooks/comp.py b/notebooks/comp.py new file mode 100644 index 000000000..e33e6a7ca --- /dev/null +++ b/notebooks/comp.py @@ -0,0 +1,76 @@ +""" +Beej Computer Emulator + +Software that pretends to be hardware + +Turing Complete--it can solve any problem for which there is an algorithm. +""" + +""" +Memory--like a big array + +"Index into the memory array" == "address" == "pointer" +""" + + +# memory = [0] * 256 # RAM + +import sys +PRINT_BEEJ = 1 +HALT = 2 +SAVE_REG = 3 # SAVE_REG R1,37 register[1] = 37 +PRINT_REG = 4 # PRINT_REG R1 print(register[1]) +ADD = 5 + +memory = [ + SAVE_REG, # SAVE_REG R1,37 + 1, # <-- index into the register array + 99, # <-- value that we want to store there + SAVE_REG, + 2, # <-- index into the register array + 11, # <-- value that we want to store there + ADD, # ADD R1, R2 register[1] += register[2] + 1, + 2, + PRINT_REG, + 1, + PRINT_BEEJ, + HALT, +] + +register = [0] * 8 # 8 general-purpose registers, like variables, R0, R1, R2 .. R7 + +pc = 0 # Program Counter, index of the current instruction +running = True + +while running: + ir = memory[pc] # Instruction register + + if ir == PRINT_BEEJ: + print("Beej!") + pc += 1 + + elif ir == SAVE_REG: + reg_num = memory[pc + 1] + value = memory[pc + 2] + register[reg_num] = value + pc += 3 + + elif ir == PRINT_REG: + reg_num = memory[pc + 1] + print(register[reg_num]) + pc += 2 + + elif ir == ADD: + reg_num1 = memory[pc + 1] + reg_num2 = memory[pc4 + 2] + register[reg_num1] += register[reg_num2] + pc += 3 + + elif ir == HALT: + running = False + pc += 1 + + else: + print(f'Unknown instruction {ir} at address {pc}') + sys.exit(1) diff --git a/notebooks/comp00.py b/notebooks/comp00.py new file mode 100644 index 000000000..66fafba13 --- /dev/null +++ b/notebooks/comp00.py @@ -0,0 +1,79 @@ +""" +Computer emulator + +Software that pretends to be hardware + +Turing Complete -- it can solve any problem for which there is an algorithm. + +256bytes in total + +Write a program that can run any program. +- add this to your resume. +""" + +# Memory = an array +# Index in the array and value that is stored there +""" +Memory --like a big array +Index into the memory array" =="address" == "pointer" +""" + +# a = [1, 2, 3] +# +# print(a[0]) +# a[0] = 99 + +# memory = [0] * 256 #RAM + +import sys +PRINT_JORGE = 1 +HALT = 2 +SAVE_REG = 3 # SAVE_REG R1, 37 +PRINT_REG = 4 # PRINT_REG R1 + +memory = [ + PRINT_JORGE, # < ---- PC + # PRINT_JORGE, + # PRINT_JORGE, + # SAVE_REG R1, 37 + 1, + # 37, + 99, + SAVE_REG, + 2, + 11, + ADD, # ADD R1, R2 register[1] += register[2] + 1, + 2, + PRINT_REG, + 1, + PRINT_JORGE, + HALT, +] + +register = [0] * 8 # 8 registers, like variables. We have 8 here, RO, R1, R2 .. R7 +pc = 0 # Program Counter, index of the current instruction +running = True + +# for v in memory: +while running: + ir = memory[pc] # Instruction register = ir + # if v == PRINT_JORGE: + if ir == PRINT_JORGE: + print("Jorge !") + pc += 1 + # elif v == HALT: + # elif ir == HALT: + elif ir == SAVE_REG: + reg_num = memory[pc + 1] # grab it out of memory + reg_num = memory[pc + 2] + register[reg_num] = value + # break + running = False + pc += 3 + elif ir == HALT + running = False + pc += 1 + else: + print(f"Unkown instruction {ir} at address {pc}") + sys.exit diff --git a/notebooks/day01.py b/notebooks/day01.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/notebooks/practice.py b/notebooks/practice.py new file mode 100644 index 000000000..4bb028a7b --- /dev/null +++ b/notebooks/practice.py @@ -0,0 +1,31 @@ +""" + + +Print out all of the strings in the following array in alphabetical order, each on a separate line. +['Waltz', 'Tango', 'Viennese Waltz', 'Foxtrot', 'Cha Cha', 'Samba', 'Rumba', 'Paso Doble', 'Jive'] +The expected output is: +'Cha Cha' +'Foxtrot' +'Jive' +'Paso Doble' +'Rumba' +'Samba' +'Tango' +'Viennese Waltz' +'Waltz' +You may use whatever programming language you'd like. +Verbalize your thought process as much as possible before writing any code. Run through the UPER problem solving framework while going through your thought process. + +""" + + +line = ['Waltz', 'Tango', 'Viennese Waltz', 'Foxtrot', + 'Cha Cha', 'Samba', 'Rumba', 'Paso Doble', 'Jive'] + +line.sort() # (n log(n)) +# Python timsort +# use the built in function +# always (n log(n)) +# Bubble Sort +for i in line: + print(i) From 7a0b13e200fcede762d8c93a2a74860b149a5aa2 Mon Sep 17 00:00:00 2001 From: ThisIsJorgeLima Date: Wed, 17 Jun 2020 10:59:29 -0400 Subject: [PATCH 3/5] Incomplete --- ls8/cpu.py | 80 ++++++++++++++++++++++------ ls8/ls8.py | 5 +- notebooks/+bf.py | 12 +++++ notebooks/+bf00.py | 52 ++++++++++++++++++ notebooks/bitwise notes | 108 ++++++++++++++++++++++++++++++++++++++ notebooks/branch_table.py | 48 +++++++++++++++++ notebooks/comp.py | 2 +- notebooks/comp00.py | 1 + notebooks/comp01.py | 87 ++++++++++++++++++++++++++++++ notebooks/comp02.py | 79 ++++++++++++++++++++++++++++ notebooks/practice.py | 6 +-- notebooks/prog00 | 0 notebooks/prog1.txt | 13 +++++ notebooks/prog2 | 13 +++++ 14 files changed, 485 insertions(+), 21 deletions(-) create mode 100644 notebooks/+bf.py create mode 100644 notebooks/+bf00.py create mode 100644 notebooks/bitwise notes create mode 100644 notebooks/branch_table.py create mode 100644 notebooks/comp01.py create mode 100644 notebooks/comp02.py create mode 100644 notebooks/prog00 create mode 100644 notebooks/prog1.txt create mode 100644 notebooks/prog2 diff --git a/ls8/cpu.py b/ls8/cpu.py index 9c1aa1d3a..c51fe3945 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -19,6 +19,17 @@ def __init__(self): self.registers = [0] * 8 # 8 general-purpose registers, like variables, R0, R1, R2 .. R7 # internal registers self.pc = 0 # Program Counter, index of the current instruction + self.halted = False + self.ir = 0 + # self.branch_table = { + self.ldi = 0b10000010, + self.prn = 0b01000111, + # ADD: self.alu, + # DIV: self.alu, + # SUB: self.alu, + # AND: self.alu, + # OR: self.alu + # } # Step 2: Add RAM functions @@ -33,30 +44,58 @@ 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 + # path = 'examples/' + filename + # address = 0 + # -- Load program --- + with open(filename, 'r') as f: + # for address, line in enumerate(f): + for line in f: + line = line.split('#') + line = line[0].strip() + # except ValueError: + continue + + self.ram[address] = int(line, 2) + address += 1 # 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 - address += 1 + # for address, instruction in enumerate(program): + # self.ram[address] = instruction + # 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: + self.reg[reg_a] -= self.reg[reg_b] + elif op == MUL: + self.reg[reg_a] *= self.reg[reg_b] + elif op == DIV: + self.reg[reg_a] //= self.reg[reg_b] + elif op == AND: + self.reg[reg_a] &= self.reg[reg_b] + elif op == OR: + self.reg[reg_a] |= self.reg[reg_b] + + else: + raise Exception("Unsupported ALU operation") + if op == "ADD": self.reg[reg_a] += self.reg[reg_b] # elif op == "SUB": etc @@ -100,11 +139,20 @@ def run(self): HLT = 0b00000001 # Instruction handler LDI = 0b10000010 # instruction PRN = 0b01000111 # PRN instruction + + ADD = 0b10100000 + SUB = 0b10100001 + MUL = 0b10100010 + DIV = 0b10100011 + + AND = 0b10101000 + OR = 0b10101010 + running = True self.ir = self.ram_read(self.pc) - reg_num1 = self.ram_read(self.pc + 1) - reg_num2 = self.ram_read(self.pc + 2) + reg_num1 = self.ram_read(self.pc + 1) # register + reg_num2 = self.ram_read(self.pc + 2) # value while running: # Lets set our value of our register diff --git a/ls8/ls8.py b/ls8/ls8.py index b9bffd194..41ceaaf55 100755 --- a/ls8/ls8.py +++ b/ls8/ls8.py @@ -7,5 +7,8 @@ cpu = CPU() -cpu.load() +if len(sys.argv) == 2: + filename = sys.argv[1] + +cpu.load(filename) cpu.run() diff --git a/notebooks/+bf.py b/notebooks/+bf.py new file mode 100644 index 000000000..4f9e73773 --- /dev/null +++ b/notebooks/+bf.py @@ -0,0 +1,12 @@ +def foo(): + print("foo") + + +bar = foo +baz = foo +frotz = bar + +foo() +bar() +baz() +frotz() diff --git a/notebooks/+bf00.py b/notebooks/+bf00.py new file mode 100644 index 000000000..7af2e7491 --- /dev/null +++ b/notebooks/+bf00.py @@ -0,0 +1,52 @@ +# Branch Tables +# (AKA) Dispatch Tables) + +# def function1(): + + +def function1(x, y): + # print("function1") + print(f"function1 {x} {y}") + + +def function2(x, y): + # print("function2") + print(f"function2 {x} {y}") + + +def function3(x, y): + # print("function3") + print(f"function3 {x} {y}") + + +def function4(x, y): + # print("function4") + print(f"function4 {x} {y}") + + +def call_function(n, x=None, y=None): + branch_table = { + 1: function1, + 2: function2, + 3: function3, + 4: function4, + # writen as a lambda function: + 5: lambda x, y: print(f"lambda {x} {y}") + # if n == 1: + # function1() + # elif n == 2: + # function2() + # elif n == 3: + # function3() + # elif n == 4: + # function4() + + } + # f = branch_table[n] + # f(x, y) + branch_table[n](x, y) + + +call_function(2, 99, 100) +call_function(3, 2, 3) +call_function(5, 33, 44) diff --git a/notebooks/bitwise notes b/notebooks/bitwise notes new file mode 100644 index 000000000..cbea22d28 --- /dev/null +++ b/notebooks/bitwise notes @@ -0,0 +1,108 @@ +Bitwise Operations +------------------ + +Review: Boolean + +if x < 10 or x > 2: + print("foo") + +x = 0 + +if False: + print("foo") + +A B A and B +---------------- +F F F +T F F +F T F +T T T + +A B A or B +---------------- +F F F +T F T +F T T +T T T + +Bitwise: + +A B A & B Bitwise AND +---------------- +0 0 0 +1 0 0 +0 1 0 +1 1 1 + +A B A | B Bitwise OR +---------------- +0 0 0 +1 0 1 +0 1 1 +1 1 1 + + 11001100 +& 11110000 AND Mask +---------- + 11000000 + +In general: + +* You can use AND to clear (set to 0) bits +* You can use OR to set (set to 1) bits + +Shifting +-------- + +Moving all bits in a number left or right. + + 111001 + 011100 + 001110 + 000111 + 000011 + +Base 10, Decimal analogy: + + vv + 12345678 + 23456780 + 34567800 + 03456780 + 00345678 + 00034567 + 00003456 + 00000345 + 00000034 + ^^ + + vv + 12345678 + d& 00990000 First AND + ----------- + 00340000 Then shift + 00034000 + 00003400 + 00000340 + 00000034 + + + vv +ir = 0b10000010 + + vv + 10000010 + & 11000000 + ---------- + 10000000 + ^^ + 00000010 + ^^ + +num_operands = (ir & 0b11000000) >> 6 + +how_far_to_move_pc = num_operands + 1 + +pc += how_far_to_move_pc + +LDI R1,R2 diff --git a/notebooks/branch_table.py b/notebooks/branch_table.py new file mode 100644 index 000000000..fe69c82eb --- /dev/null +++ b/notebooks/branch_table.py @@ -0,0 +1,48 @@ +# Branch Tables +# (AKA Dispatch Tables) + + +def fun1(x, y): + print(f"fun1 {x} {y}") + + +def fun2(x, y): + print(f"fun2 {x} {y}") + + +def fun3(x, y): + print(f"fun3 {x} {y}") + + +def fun4(x, y): + print(f"fun4 {x} {y}") + + +def call_fun(n, x=None, y=None): + """ + if n == 1: + fun1(x, y) + elif n == 2: + fun2(x, y) + elif n == 3: + fun3(x, y) + elif n == 4: + fun4(x, y) + """ + + branch_table = { + 1: fun1, + 2: fun2, + 3: fun3, + 4: fun4, + 5: lambda x, y: print(f"lambda {x} {y}") + } + + #f = branch_table[n] + #f(x, y) + branch_table[n](x, y) + + +call_fun(2, 99, 100) +call_fun(3, 2, 3) +call_fun(5, 33, 44) diff --git a/notebooks/comp.py b/notebooks/comp.py index e33e6a7ca..436de6eaa 100644 --- a/notebooks/comp.py +++ b/notebooks/comp.py @@ -63,7 +63,7 @@ elif ir == ADD: reg_num1 = memory[pc + 1] - reg_num2 = memory[pc4 + 2] + reg_num2 = memory[pc + 2] register[reg_num1] += register[reg_num2] pc += 3 diff --git a/notebooks/comp00.py b/notebooks/comp00.py index 66fafba13..0cdff05b6 100644 --- a/notebooks/comp00.py +++ b/notebooks/comp00.py @@ -31,6 +31,7 @@ SAVE_REG = 3 # SAVE_REG R1, 37 PRINT_REG = 4 # PRINT_REG R1 +# running instructions memory = [ PRINT_JORGE, # < ---- PC # PRINT_JORGE, diff --git a/notebooks/comp01.py b/notebooks/comp01.py new file mode 100644 index 000000000..1988802c2 --- /dev/null +++ b/notebooks/comp01.py @@ -0,0 +1,87 @@ +""" +Beej Computer Emulator + +Software that pretends to be hardware + +Turing Complete--it can solve any problem for which there is an algorithm. +""" + +""" +Memory--like a big array + +"Index into the memory array" == "address" == "pointer" +""" + + +# memory = [0] * 256 # RAM + +import sys +PRINT_BEEJ = 1 +HALT = 2 +SAVE_REG = 3 # SAVE_REG R1,37 register[1] = 37 +PRINT_REG = 4 # PRINT_REG R1 print(register[1]) +ADD = 5 + +memory = [0] * 256 +register = [0] * 8 # 8 general-purpose registers, like variables, R0, R1, R2 .. R7 + +# -- Load program -- +filename = sys.argv[1] +# hardcoded +with open(filename) as f: + # with open("prog1") as f: + # itteration looking at one line at a time + for address, line in enumerate(f): + # for line in f: + # print(line) + line = line.split("#") + + try: + v = int(line[0]) + except ValueError: + continue + # print(v) + + memory[address] = v + address += 1 + print(memory[:15]) + print(sys.argv) # embedded into our sys + +sys.exit(0) + +# -- Run loop -- + +pc = 0 # Program Counter, index of the current instruction +running = True + +while running: + ir = memory[pc] # Instruction register + + if ir == PRINT_BEEJ: + print("Beej!") + pc += 1 + + elif ir == SAVE_REG: + reg_num = memory[pc + 1] + value = memory[pc + 2] + register[reg_num] = value + pc += 3 + + elif ir == PRINT_REG: + reg_num = memory[pc + 1] + print(register[reg_num]) + pc += 2 + + elif ir == ADD: + reg_num1 = memory[pc + 1] + reg_num2 = memory[pc4 + 2] + register[reg_num1] += register[reg_num2] + pc += 3 + + elif ir == HALT: + running = False + pc += 1 + + else: + print(f'Unknown instruction {ir} at address {pc}') + sys.exit(1) diff --git a/notebooks/comp02.py b/notebooks/comp02.py new file mode 100644 index 000000000..4f9c85aee --- /dev/null +++ b/notebooks/comp02.py @@ -0,0 +1,79 @@ +""" +Beej Computer Emulator + +Software that pretends to be hardware + +Turing Complete--it can solve any problem for which there is an algorithm. +""" + +""" +Memory--like a big array + +"Index into the memory array" == "address" == "pointer" +""" + + +import sys +PRINT_BEEJ = 1 +HALT = 2 +SAVE_REG = 3 # SAVE_REG R1,37 register[1] = 37 +PRINT_REG = 4 # PRINT_REG R1 print(register[1]) +ADD = 5 + +memory = [0] * 256 + +register = [0] * 8 # 8 general-purpose registers, like variables, R0, R1, R2 .. R7 + +# -- Load program -- +filename = sys.argv[1] + +# TODO: error checking on sys.argv + +with open(filename) as f: + for address, line in enumerate(f): + + line = line.split("#") + + try: + v = int(line[0], 10) + except ValueError: + continue + + memory[address] = v + +# -- Run loop -- + +pc = 0 # Program Counter, index of the current instruction +running = True + +while running: + ir = memory[pc] # Instruction register + + if ir == PRINT_BEEJ: + print("Beej!") + pc += 1 + + elif ir == SAVE_REG: + reg_num = memory[pc + 1] + value = memory[pc + 2] + register[reg_num] = value + pc += 3 + + elif ir == PRINT_REG: + reg_num = memory[pc + 1] + print(register[reg_num]) + pc += 2 + + elif ir == ADD: + reg_num1 = memory[pc + 1] + reg_num2 = memory[pc + 2] + register[reg_num1] += register[reg_num2] + pc += 3 + + elif ir == HALT: + running = False + pc += 1 + + else: + print(f'Unknown instruction {ir} at address {pc}') + sys.exit(1) diff --git a/notebooks/practice.py b/notebooks/practice.py index 4bb028a7b..411146977 100644 --- a/notebooks/practice.py +++ b/notebooks/practice.py @@ -1,6 +1,4 @@ """ - - Print out all of the strings in the following array in alphabetical order, each on a separate line. ['Waltz', 'Tango', 'Viennese Waltz', 'Foxtrot', 'Cha Cha', 'Samba', 'Rumba', 'Paso Doble', 'Jive'] The expected output is: @@ -26,6 +24,8 @@ # Python timsort # use the built in function # always (n log(n)) -# Bubble Sort +# Bubble Sort 0(n^2) +# Mergesort +# 2-3 challenges. for i in line: print(i) diff --git a/notebooks/prog00 b/notebooks/prog00 new file mode 100644 index 000000000..e69de29bb diff --git a/notebooks/prog1.txt b/notebooks/prog1.txt new file mode 100644 index 000000000..6f6a10b29 --- /dev/null +++ b/notebooks/prog1.txt @@ -0,0 +1,13 @@ +3 # SAVE_REG R1,99 +1 +99 +3 # SAVE_REG R2,11 +2 +11 +5 # ADD R1,R2 +1 +2 +4 # PRINT_REG +1 +1 # PRINT_BEEJ +2 # HALT diff --git a/notebooks/prog2 b/notebooks/prog2 new file mode 100644 index 000000000..6f6a10b29 --- /dev/null +++ b/notebooks/prog2 @@ -0,0 +1,13 @@ +3 # SAVE_REG R1,99 +1 +99 +3 # SAVE_REG R2,11 +2 +11 +5 # ADD R1,R2 +1 +2 +4 # PRINT_REG +1 +1 # PRINT_BEEJ +2 # HALT From f846f79adf92c2a6e9c33f5156c09355e0c3d81f Mon Sep 17 00:00:00 2001 From: ThisIsJorgeLima Date: Wed, 17 Jun 2020 21:52:44 -0400 Subject: [PATCH 4/5] MVP complete --- ls8/cpu.py | 269 ++++++++++++++++++++++++++++++-------------- ls8/ls8.py | 5 +- notebooks/comp03.py | 92 +++++++++++++++ notebooks/comp04.py | 110 ++++++++++++++++++ 4 files changed, 387 insertions(+), 89 deletions(-) create mode 100644 notebooks/comp03.py create mode 100644 notebooks/comp04.py diff --git a/ls8/cpu.py b/ls8/cpu.py index c51fe3945..c220ebaf8 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -3,27 +3,27 @@ import sys -# Step 1: Add the constructor to cpu.py -# x = [0] * 25 # x is a list of 25 zeroes -# 256 bytes in total - - class CPU: """Main CPU class.""" +# Step 1: Add the constructor to cpu.py def __init__(self): """Construct a new CPU.""" # RAM + # create 256 bytes of RAM self.ram = [0] * 256 - # Our registers - self.registers = [0] * 8 # 8 general-purpose registers, like variables, R0, R1, R2 .. R7 + # create 8 registrars + self.reg = [0] * 8 # 8 general-purpose registers, like variables, R0, R1, R2 .. R7 # internal registers + # set the program counter to 0 self.pc = 0 # Program Counter, index of the current instruction self.halted = False self.ir = 0 + self.reg[7] = 0xF4 + self.sp = self.reg[7] # self.branch_table = { - self.ldi = 0b10000010, - self.prn = 0b01000111, + # self.ldi = 0b10000010, + # self.prn = 0b01000111, # ADD: self.alu, # DIV: self.alu, # SUB: self.alu, @@ -31,76 +31,78 @@ def __init__(self): # OR: self.alu # } - # Step 2: Add RAM functions -# ram_read() should accept the address to read and return the value stored there. - -# ram_write() should accept a value to write, and the address to write it to. - - def ram_read(self, address): + """ + This reads a value at its designated address of RAM + """ return self.ram[address] def ram_write(self, value, address): + """ + This writes a value to RAM at its designated address + """ self.ram[address] = value + # For now, we've just hardcoded a program: + +# Step 7: Un-hardcode the machine code + # program = [ + # # From print8.ls8 + # 0b10000010, # LDI R0,8 + # 0b00000000, #registrar 0 + # 0b00001000, # value 8 + # 0b01000111, # PRN R0 + # 0b00000000, #print value in first registrar + # 0b00000001, # HLT + # ] + # for instruction in program: + # self.ram[address] = instruction + # address += 1 def load(self, filename): """Load a program into memory.""" - # path = 'examples/' + filename - # address = 0 + address = 0 # -- Load program --- - with open(filename, 'r') as f: - # for address, line in enumerate(f): + with open(filename, ) as f: for line in f: - line = line.split('#') + # print(line) + line = line.split("#") line = line[0].strip() # except ValueError: - continue - + # Our command needs to be casted and set to base 2 + if line == '': + continue + # print('value is: ' + str(value)) + # will assign are converted value into the next entry in our RAM self.ram[address] = int(line, 2) + # after it is entered into RAM, it will increment the address value by 1 address += 1 - # 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 address, instruction in enumerate(program): - # self.ram[address] = instruction - # address += 1 - 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: - self.reg[reg_a] -= self.reg[reg_b] - elif op == MUL: + # elif op == "SUB": + # self.reg[reg_a] -= self.reg[reg_b] + elif op == "MUL": self.reg[reg_a] *= self.reg[reg_b] - elif op == DIV: - self.reg[reg_a] //= self.reg[reg_b] - elif op == AND: - self.reg[reg_a] &= self.reg[reg_b] - elif op == OR: - self.reg[reg_a] |= self.reg[reg_b] + # elif op == " DIV": + # self.reg[reg_a] //= self.reg[reg_b] + # elif op == "AND": + # self.reg[reg_a] &= self.reg[reg_b] + # elif op == "OR": + # self.reg[reg_a] |= self.reg[reg_b] else: raise Exception("Unsupported ALU operation") - if op == "ADD": - self.reg[reg_a] += self.reg[reg_b] - # elif op == "SUB": etc - else: - raise Exception("Unsupported ALU operation") + # if op == "ADD": + # self.reg[reg_a] += self.reg[reg_b] + # # elif op == "SUB": etc + # else: + # raise Exception("Unsupported ALU operation") def trace(self): """ @@ -123,21 +125,15 @@ def trace(self): print() def run(self): - """Run the CPU.""" - - -# Step 3: Implement the core of CPU's run() method -# Step 4: Implement the HLT instruction handler -# Step 5: Add the LDI instruction -# Step 6: Add the PRN instruction -# Step 7: Un-hardcode the machine code -# Step 8: Implement a Multiply and Print the Result -# Step 9: Beautify your run() loop -# Step 10: Implement System Stack -# Step 11: Implement Subroutine Calls - + """ + Run the CPU: + The core running of our built CPU + """ + # Step 4: Implement the HLT instruction handler HLT = 0b00000001 # Instruction handler + # Step 5: Add the LDI instruction LDI = 0b10000010 # instruction + # Step 6: Add the PRN instruction PRN = 0b01000111 # PRN instruction ADD = 0b10100000 @@ -147,28 +143,131 @@ def run(self): AND = 0b10101000 OR = 0b10101010 - + PUSH = 0b01000101 + POP = 0b01000110 + CALL = 0b01010000 + RET = 0b00010001 + # kept in a while loop to continue working through passed in commands. running = True - - self.ir = self.ram_read(self.pc) - reg_num1 = self.ram_read(self.pc + 1) # register - reg_num2 = self.ram_read(self.pc + 2) # value - - while running: - # Lets set our value of our register - if self.ir == LDI: - self.reg[reg_num1] = reg_num2 - self.pc += 3 - # print - elif self.ir == PRN: - print(self.reg[reg_num1]) # print our stored value - self.pc += 2 - # Halt our CPU and exit - elif self.ir == HLT: + # Step 9: Beautify your run() loop + while running == True: + # print(f"PC:{self.pc}") + """ + Lets load our function now. This will write our pre-written commands in the program variable to our RAM. + + The instructions will loaded and cycle through our RAM. + """ + instruction = self.ram[self.pc] + + # Halt our CPU and exit + if instruction == HLT: running = False self.pc += 1 + # checking if instruction is to set registrar to integer: + # From Spec: + # `LDI register immediate` + # Will set the value of a register to an integer. + # The first value sets which registrar will assign the integer + # The second value determines what that value is + # The PC (program counter) goes one forward to see the value + elif instruction == LDI: + # one RAM entries out determines which registrar is loaded: + reg_slot = self.ram_read(self.pc + 1) + # two RAM entries out determines what value is loaded + int_value = self.ram_read(self.pc + 2) + + # The registrar at slot reg_slot is being assigned the int_value both + # determined from RAM above + self.reg[reg_slot] = int_value + # the program counter is set 3 entries ahead + # one for the current instruction + # one for the integer value + # one for the registrar slot + # 3 bit operation + self.pc += 3 + # Writing logic for if the Print function is called + elif instruction == PRN: + # import pdb; pdb.set_trace() + # loading which registrar slot should be preinted + # print(self.reg[reg_num1]) # print our stored value + reg_slot = self.ram_read(self.pc + 1) + # prints the value at that registrar slot + print(self.reg[reg_slot]) + # print("RAM: ", self.ram) + # print("REG: ", self.reg) + # print("Value", value) + # inriments by two + # one for the current instruction + # one for the reg_slot + self.pc += 2 + elif instruction == MUL: + # if MUL is called, it will grab the next two values in our program counter + # Step 8: Implement a Multiply and Print the Result + # to find out what values in the registrar are going to be multiplied: + reg_num1 = self.ram[self.pc+1] # register + reg_num2 = self.ram[self.pc+2] # value + self.alu('MUL', reg_num1, reg_num2) + # 3 bit operation + self.pc += 3 + # if ADD is called: + elif instruction == ADD: + # This will grab the next values in our program counter. + # and will know which values in our registrar will be added + reg_num1 = self.ram[self.pc+1] # register + reg_num2 = self.ram[self.pc+2] # value + self.alu('ADD', reg_num1, reg_num2) + # 3 bit operation + self.pc += 3 + # Step 10: Implement System Stack + # PUSH + elif instruction == PUSH: + # This will determine which registrar is pushing their value to the stack: + reg_slot = self.ram[self.pc+1] + val = self.reg[reg_slot] + # decriment the stack pointer + self.sp -= 1 + # now we will push the value pulled from our registar to the stack: + self.ram[self.sp] = val + # 2 bit operation + self.pc += 2 + # POP + elif instruction == POP: + # This will determine which registrar will get popped from the stack: + reg_slot = self.ram[self.pc+1] + # now the value of the registrar is updated + # at the reg_slot with its assigned value: + value = self.ram[self.sp] + self.reg[reg_slot] = value + # now will incriment the stack pointer: + self.sp += 1 + # 2 bit operation + self.pc += 2 + # CALL + elif instruction == CALL: + # Since the CALL spec has the address after the CALL instruction. + # We will designate the + return_address = self.pc + 2 + # now will decriment the stack pointer by 1 + self.sp -= 1 + # will assign the new top of the stack value of our returned address: + self.ram[self.sp] = return_address + # with the return addres now stored. + # We need to determine which registar slot is now used for our called function + reg_slot = self.ram[self.pc+1] + # Step 11: Implement Subroutine Calls + # The subroutine location is the value at that designated reg_slot: + subroutine_location = self.reg[reg_slot] + # Our program counter is now set to the location of that subroutine + self.pc = subroutine_location + elif instruction == RET: + return_address = self.ram[self.sp] + # now will incriment the stack pointer: + self.sp += 1 + # return our address: + self.pc = return_address else: - print(f"Unknown instruction {ir} at address {pc}") - + print(" print(f'unknown instruction {instruction} at address {IR}") + print(f"Our program counter value is: {self.pc}") + print(f"The command issued was: {self.ram_read(self.pc)}") sys.exit(1) diff --git a/ls8/ls8.py b/ls8/ls8.py index 41ceaaf55..0bddf9dfe 100755 --- a/ls8/ls8.py +++ b/ls8/ls8.py @@ -7,8 +7,5 @@ cpu = CPU() -if len(sys.argv) == 2: - filename = sys.argv[1] - -cpu.load(filename) +cpu.load(sys.argv[1]) cpu.run() diff --git a/notebooks/comp03.py b/notebooks/comp03.py new file mode 100644 index 000000000..ba321f687 --- /dev/null +++ b/notebooks/comp03.py @@ -0,0 +1,92 @@ +""" +Beej Computer Emulator + +Software that pretends to be hardware + +Turing Complete--it can solve any problem for which there is an algorithm. +""" + +""" +Memory--like a big array + +"Index into the memory array" == "address" == "pointer" +""" + + +import sys +PRINT_BEEJ = 1 +HALT = 2 +SAVE_REG = 3 # SAVE_REG R1,37 register[1] = 37 +PRINT_REG = 4 # PRINT_REG R1 print(register[1]) +ADD = 5 + +memory = [0] * 256 + +register = [0] * 8 # 8 general-purpose registers, like variables, R0, R1, R2 .. R7 + +# -- Load program -- + +# TODO: error checking on sys.argv + +with open(filename) as f: + for address, line in enumerate(f): + + line = line.split("#") + + try: + v = int(line[0], 10) + except ValueError: + continue + + memory[address] = v + +# -- Run loop -- + +pc = 0 # Program Counter, index of the current instruction +SP = 7 # Register 7 +register[7] = 0xf4 # our stack pointer +running = True + +while running: + ir = memory[pc] # Instruction register + + if ir == PRINT_BEEJ: + print("Beej!") + pc += 1 + + elif ir == SAVE_REG: + reg_num = memory[pc + 1] + value = memory[pc + 2] + register[reg_num] = value + pc += 3 + + elif ir == PRINT_REG: + reg_num = memory[pc + 1] + print(register[reg_num]) + pc += 2 + + elif ir == ADD: + reg_num1 = memory[pc + 1] + reg_num2 = memory[pc + 2] + register[reg_num1] += register[reg_num2] + pc += 3 + + elif ir == PUSH: # pc push # Push register 2 + # decrement stack pointer + register[SP] -= 1 + # Get the value we want to store from the register + register_num = memory[pc + 1] + value = register[register_num] + # Figure out where to store it + top_of_stack_address = register[SP] + # Store it + memory[top_of_stack_address] = value + pc += 2 + + elif ir == HALT: + running = False + pc += 1 + + else: + print(f'Unknown instruction {ir} at address {pc}') + sys.exit(1) diff --git a/notebooks/comp04.py b/notebooks/comp04.py new file mode 100644 index 000000000..ca8e0cc0e --- /dev/null +++ b/notebooks/comp04.py @@ -0,0 +1,110 @@ +""" +Beej Computer Emulator + +Software that pretends to be hardware + +Turing Complete--it can solve any problem for which there is an algorithm. +""" + +""" +Memory--like a big array + +"Index into the memory array" == "address" == "pointer" +""" + +import sys + +PRINT_BEEJ = 1 +HALT = 2 +SAVE_REG = 3 # SAVE_REG R1,37 register[1] = 37 +PRINT_REG = 4 # PRINT_REG R1 print(register[1]) +ADD = 5 +PUSH = 6 +POP = 7 + +memory = [0] * 256 + +register = [0] * 8 # 8 general-purpose registers, like variables, R0, R1, R2 .. R7 + +# -- Load program -- +filename = sys.argv[1] + +# TODO: error checking on sys.argv + +with open(filename) as f: + address = 0 + + for line in f: + + line = line.split("#") + + try: + v = int(line[0], 10) + except ValueError: + continue + + memory[address] = v + + address += 1 + +# -- Run loop -- + +SP = 7 + +pc = 0 # Program Counter, index of the current instruction +register[SP] = 0xf4 + +running = True + +while running: + ir = memory[pc] # Instruction register + + if ir == PRINT_BEEJ: + print("Beej!") + pc += 1 + + elif ir == SAVE_REG: + reg_num = memory[pc + 1] + value = memory[pc + 2] + register[reg_num] = value + pc += 3 + + elif ir == PRINT_REG: + reg_num = memory[pc + 1] + print(register[reg_num]) + pc += 2 + + elif ir == ADD: + reg_num1 = memory[pc + 1] + reg_num2 = memory[pc + 2] + register[reg_num1] += register[reg_num2] + pc += 3 + + elif ir == PUSH: + # decrement SP + register[SP] -= 1 + + # Get the value we want to store from the register + reg_num = memory[pc + 1] + value = register[reg_num] # <-- this is the value that we want to push + + # Figure out where to store it + top_of_stack_addr = register[SP] + + # Store it + memory[top_of_stack_addr] = value + + pc += 2 + + elif ir == POP: + # TODO + pass + + elif ir == HALT: + running = False + pc += 1 + + else: + print(f'Unknown instruction {ir} at address {pc}') + sys.exit(1) + From 9674bc93883c9d9de3182073d4226a265f4c5a50 Mon Sep 17 00:00:00 2001 From: "Jorge A. Lima" <53373957+ThisIsJorgeLima@users.noreply.github.com> Date: Wed, 17 Jun 2020 22:00:04 -0400 Subject: [PATCH 5/5] Update cpu.py --- ls8/cpu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index c220ebaf8..8aac2589b 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -267,7 +267,7 @@ def run(self): # return our address: self.pc = return_address else: - print(" print(f'unknown instruction {instruction} at address {IR}") + print(f"unknown instruction {instruction} at address {IR}") print(f"Our program counter value is: {self.pc}") print(f"The command issued was: {self.ram_read(self.pc)}") sys.exit(1)