From dc45f3b638f95e8bc7181cc55eb79c574ef6adb4 Mon Sep 17 00:00:00 2001 From: Ash827 Date: Wed, 2 Dec 2020 23:25:54 -0500 Subject: [PATCH 1/5] Day 1 done --- LS8-spec.md | 2 +- README.md | 14 ++-- instructions.py | 0 inventory.md | 43 ++++++++++ ls8/cpu.py | 86 +++++++++++++++++++- numbers.txt | 203 ++++++++++++++++++++++++++++++++++++++++++++++ simple_machine.py | 78 ++++++++++++++++++ 7 files changed, 416 insertions(+), 10 deletions(-) create mode 100644 instructions.py create mode 100644 inventory.md create mode 100644 numbers.txt create mode 100644 simple_machine.py diff --git a/LS8-spec.md b/LS8-spec.md index fa24efe44..8ddb81ab5 100644 --- a/LS8-spec.md +++ b/LS8-spec.md @@ -15,7 +15,7 @@ ## Internal Registers -* `PC`: Program Counter, address of the currently executing instruction +* `PC`: Program Counter, address of the currently executing instruction * `IR`: Instruction Register, contains a copy of the currently executing instruction * `MAR`: Memory Address Register, holds the memory address we're reading or writing * `MDR`: Memory Data Register, holds the value to write or the value just read diff --git a/README.md b/README.md index 20c417457..b1fb7c5c8 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,13 @@ ### 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 +- [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 diff --git a/instructions.py b/instructions.py new file mode 100644 index 000000000..e69de29bb diff --git a/inventory.md b/inventory.md new file mode 100644 index 000000000..a6749ff49 --- /dev/null +++ b/inventory.md @@ -0,0 +1,43 @@ +Inventory Step + +CS Computer Architecture Repo asm --- ls8 --- examples folder (call, interrupts, keyboard, mult, print8, printstr, sctest, stack, stackoverflow) --- +README: LS8 emulator project details --- cpu.py: CPU class (needs to be developed) --- ls8.py: where file will be run FAQ LS8-cheatsheet.md +LS8-spec.md: Registers, Internal Registers, Flags, Stack, Interrupts, README.md - objectives for modules 1-4 + +ADD regA regB: add reg A and reg B, store in reg A +AND regA regB: bitwise-AND values in reg A and B, store in reg A +CALL register: call register +CMP regA regB: compare reg A and reg B, for equal, less than, greater than +DEC register: decrement the value in the register +DIV regA regB: reg A/reg B, store in reg A, if 0 print error and halt +HLT: halts CPU and exits emulator +INC register: increment the value in the given register +INT register: issue interrupt number stored in given register +IRET: return from interrupt handler +JEQ register: if equal flag is set to true, jump to address stored in given register +JGE register: if greater than flag or equal flag is set to true, jump to address stored in given register +JLE register: if less than or equal flag is set to true, jump to address stored in given register +JLT register: if less than flag is set to true, jump to address stored in given register +JMP register: jump to address stored in given register +JNE register: if equal flag is clear (false, 0) jump to address stored in given register +LD regA regB: loads regA with value at memory address stored in regB +LDI reg immediate: set value of a register to an interger +MOD regA regB (this instruction is handled by the ALU): divide value in the first register by the value in the second, storing the REMAINDER of the result in regA +MUL regA regB (this instruction is handled by the ALU): multiply the values in two registers together and store result in regA +NOP: no operation, do nothing for this instruction +NOT register (this instruction is handled by the ALU): perform bitwise not on value in register +OR regA regB (this is an instruction handled by the ALU): perform a bitwise-OR between values in regA and regB, storing result in regA +POP register: pop value at top of stack into given register + 1. copy value from address (pointed to by SP) to the given register + 2. increment SP +PRA register (pseudo-instruction): print alpha character value stored in the given register. print to the colsole the ASCII character corresponding to the value in the register +PRN register (pseudo-instruction): print numeric value store in the given register. print to the console the decimal integer value that is stored in the given register +PUSH register: push the value in the given register on the stack + 1. decrement the SP + 2. copy the value in the given register to the address pointed to by SP +RET: return from subroutine. pop value from top of stack and store in PC +SHL (this is an instruction handled by the ALU): shift the value in regA left by the number of bits specified in regB, filling the low bits with 0 +SHR (this is an instruction handled by the ALU): shift the value in regA right by the number of bits specified in regB, filling the highest bits with 0 +ST regA regB: store value in regB in the address stored in regA +SUB regA regB (this is an instruction handled by the ALU): subtract the value in the second reg from the first, storing result in regA +XOR regA regB: perform a bitwise-XOR between the values in regA and regB, storing result in regA \ No newline at end of file diff --git a/ls8/cpu.py b/ls8/cpu.py index 9a307496e..1db60e3ef 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -2,12 +2,50 @@ import sys +HLT = 0b00000001 +LDI = 0b10000010 +PRN = 0b01000111 +MUL = 0b10100010 +ADD = 0b10100000 +PUSH = 0b01000101 +POP = 0b01000110 +CMP = 0b10100111 +CALL = 0b01010000 +RET = 0b00010001 +JEQ = 0b01010101 +JNE = 0b01010110 +JMP = 0b01010100 +SHL = 0b10101100 +SHR = 0b10101101 +MOD = 0b10100100 +NOT = 0b01101001 +OR = 0b10101010 +AND = 0b10101000 +XOR = 0b10101011 + class CPU: """Main CPU class.""" def __init__(self): """Construct a new CPU.""" - pass + self.ram = [0] * 256 + self.reg = [0] * 8 + self.reg[7] = 0xF4 + self.reg[6] = 0 # flags reg + self.pc = 0 + 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.""" @@ -40,6 +78,15 @@ def alu(self, op, reg_a, reg_b): else: raise Exception("Unsupported ALU operation") + + def ram_read(self, address): + return self.ram[address] + + + def ram_write(self, address, data): + self.ram[address] = data + + def trace(self): """ Handy function to print out the CPU state. You might want to call this @@ -60,6 +107,41 @@ def trace(self): print() + def run(self): """Run the CPU.""" - pass + IR = [] + self.running = True + while self.running: + IR = self.ram[self.pc] # instruction register + 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) + 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: + # increment pc here + self.pc += 1 + num_args + + + 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]) + +if __name__ == '__main__': + cpu = CPU() + cpu.load() + cpu.run() \ No newline at end of file diff --git a/numbers.txt b/numbers.txt new file mode 100644 index 000000000..23c0226ab --- /dev/null +++ b/numbers.txt @@ -0,0 +1,203 @@ + + + +Why different number bases? +- +Commonly used bases? +- binary: base 2 +- hexadecimal (aka hex): base 16 +- decimal: base 10 +- octal: base 8 + +BINARY +Alan Turing +John von Neumann + +At the hardware level, it's easier to store numbers as a series of 'off' and 'on' data-points, +which lends itself to binary. + +Because it's easier to represent voltage in base 2 + +binary is useful for: +yes /no +true/false +on/off + +George Boole. Boolean logic. ((true and true) or (false and false)) --> True + +Why not base 3? +Base 3. Later a prototype. zero voltage is 0, medium voltage is 1, high voltage is 2. + +HEX +Hex is easy to convert to from binary +Hex is compact (used to print hashes, often) + +computer memory works in multiples of 8 + +Base 10 +- easy for humans +- because humans have 10 fingers?? +- Arithmetic for Billy Goats + + 999 +1000 + +Place-Based Number Systems +Hindu-Arabic number system vs Roman numerals + +VII * CXLCX = ???? + +0-9 +0 +9 +10 + +BINARY +1000 + +0 0 +1 1 +2 10 +3 11 +4 100 +5 101 +6 110 +7 111 +8 1000 +9 1001 +10 1010 +11 1011 +12 1100 +13 1101 +14 1110 +15 1111 + ++------ 8's place +|+----- 4's place +||+---- 2's place +|||+--- 1's place +|||| +1010 + ++------ 8's place +|+----- 4's place +||+---- 2's place +|||+--- 1's place +|||| +1000 + + +8 bits = byte +4 bits = 1/2 byte, a nibble + + +HEXADECIMAL +6 + 10, we have 10 symbols to work with +0-9, A-F + +0 0 +1 1 +2 2 +3 3 +4 4 +5 5 +6 6 +7 7 +8 8 +9 9 +10 A +11 B +12 C +13 D +14 E +15 F + +16 10 + +17 11 +18 12 +19 13 +20 14 +21 15 +22 16 +23 17 +24 18 +25 19 +26 1A +27 1B +28 1C +29 1D +30 1E +31 1F +32 20 + +0b100101011 +0d999 +0x1F == 0d31 + +54 --> hex?? 0x36 + +3 * 16 == 48 + +Decimal to hex: divide by 16, then add back the remainder + +Hex to decimal: multiply the symbol in the 16's place by 16, then add the remaining + +0xE3 to decimal: (E * 16) + 3 = (14 * 16) + 3 = 224 + 3 = 227 + +16 +14 +--- + 64 + 16 + +224 + +0b00011010 --> decimal --> 26 +0d10 + 0d16 == 0d26 + +43 --> binary? + +0b 00101011 + +binary hex +1111 --> F + + F F +1111 1111 + + 5 5 +0101 0101 --> decimal +0x55 ---> 85 +(5 * 16) + 5 + + E 6 +1110 0110 --> decimal + +0xE6 --> decimal --> 230 + +(14 * 16) + 6 + + + +0d73 --> binary + +73 --> 0x49 --> 0b01001001 + +4 * 16 = 64 + +0x49 + 4 9 +0100 1001 + +#ff ff ff + R G B + +#00 00 00 + + + +- leading 0s are just to pad out the byte, 0b00000001 +- don't change the number +- number bases don't change underlying +- computer represents everything in binary, down deep \ No newline at end of file diff --git a/simple_machine.py b/simple_machine.py new file mode 100644 index 000000000..2613bd8c5 --- /dev/null +++ b/simple_machine.py @@ -0,0 +1,78 @@ + +PRINT_TIM = 0b00000001 +HALT = 0b00000010 +PRINT_NUM = 0b00000011 # a 2-byte command, takes 1 argument +SAVE = 0b00000100 # a 3-byte command, takes 2 arguments +PRINT_REG = 0b00000101 +PRINT_SUM = 0b00000110 + + +# a data-driven machine +# function call +# a "variable" == registers for our programs to save things into + + +# RAM +memory = [ + PRINT_TIM, + PRINT_TIM, + PRINT_NUM, # print 99 or some other number + 42, + SAVE, # save 99 into register 2 # <-- PC + 99, # the number to save + 2, # the register to put it into + PRINT_REG, + 2, + PRINT_SUM, # R1 + R2 + HALT, +] + + +# registers, R0-R7 +registers = [0] * 8 + +running = True + +# program counter +pc = 0 + +while running: + command = memory[pc] + + + if command == PRINT_TIM: + print('tim!') + + elif command == PRINT_NUM: + num_to_print = memory[pc + 1] # we already incremented PC! + print(num_to_print) + + pc += 1 # but increment again + + + elif command == SAVE: + num_to_save = memory[pc + 1] + register_address = memory[pc + 2] + + registers[register_address] = num_to_save + + # shorter: + # registers[memory + 2] = memory[pc + 1] + + pc += 2 + + elif command == PRINT_REG: + reg_address = memory[pc + 1] + + saved_number = registers[reg_address] + + print(saved_number) + + # print(registers[memory[pc + 1]]) + + + elif command == HALT: + running = False + + + pc += 1 # so we don't get sucked into an infinite loop! \ No newline at end of file From 88021774f353e88862187e267d771ca052ac9d19 Mon Sep 17 00:00:00 2001 From: Ash827 Date: Sun, 6 Dec 2020 22:11:42 -0500 Subject: [PATCH 2/5] Day 2 complete --- ls8/cpu.py | 71 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 1db60e3ef..d1a8ae5ca 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -50,31 +50,62 @@ def __init__(self): 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 + if (len(sys.argv)) != 2: + print('remember to pass the second file name') + print('usage: python fileio.py ') + sys.exit() + try: + with open(sys.argv[1]) as f: + address = 0 + for line in f: + possible_binary = line[:line.find('#')] + # if no comment on line + if possible_binary == '': + continue # passes rest of loop + denary_int = int(possible_binary, 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: # add self.reg[reg_a] += self.reg[reg_b] - #elif op == "SUB": etc + elif op == MUL: # multiply + self.reg[reg_a] *= self.reg[reg_b] + elif op == CMP: # compare + # less than + if self.reg[reg_a] < self.reg[reg_b]: + self.reg[6] = 0b00000100 + # greater than + elif self.reg[reg_a] > self.reg[reg_b]: + self.reg[6] = 0b00000010 + # equal to + else: + self.reg[6] = 0b00000001 + elif op == SHL: # shift left + decimal = self.reg[reg_a] << self.reg[reg_b] + self.reg[reg_a] = f"{decimal:#010b}" + elif op == SHR: # shift right + decimal = self.reg[reg_a] >> self.reg[reg_b] + self.reg[reg_a] = f"{decimal:#010b}" + elif op == MOD: # modulo, get remainder + # cannot divide by 0 + if self.reg[reg_b] == 0: + # halt + self.hlt(reg_a, reg_b) + self.reg[reg_a] = self.reg[reg_a] % self.reg[reg_b] + elif op == NOT: # store bitwise not + self.reg[reg_a] = ~self.reg[reg_a] + elif op == OR: # store bitwise or + self.reg[reg_a] = self.reg[reg_a] | self.reg[reg_b] + elif op == AND: # store bitwise and + self.reg[reg_a] = self.reg[reg_a] & self.reg[reg_b] + elif op == XOR: # store bitwise xor + self.reg[reg_a] = self.reg[reg_a] ^ self.reg[reg_b] else: raise Exception("Unsupported ALU operation") From 1f3ebdd43581a2f776d9ee76d2400950f1e4b897 Mon Sep 17 00:00:00 2001 From: Ash827 Date: Tue, 8 Dec 2020 23:03:23 -0500 Subject: [PATCH 3/5] Day 3 --- ls8/cpu.py | 62 ++++++++++++++- our_program01.ls8 | 30 +++++++ simple_machine02.py | 148 +++++++++++++++++++++++++++++++++++ stack_and_more_notes.txt | 165 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 402 insertions(+), 3 deletions(-) create mode 100644 our_program01.ls8 create mode 100644 simple_machine02.py create mode 100644 stack_and_more_notes.txt diff --git a/ls8/cpu.py b/ls8/cpu.py index d1a8ae5ca..119758fe0 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -41,9 +41,9 @@ def __init__(self): POP: self.pop, CALL: self.call, RET: self.ret, - JEQ: self.jeq, - JNE: self.jne, - JMP: self.jmp + #JEQ: self.jeq, + #JNE: self.jne, + #JMP: self.jmp } @@ -171,6 +171,62 @@ def ldi(self, 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 push(self, operand_a, operand_b): + # decrement the SP. + 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): + # get SP + SP = self.reg[7] + # get address pointed to by SP + value = self.ram[SP] + # Copy the value from the address pointed to by SP to the given register. + self.reg[operand_a] = value + # increment SP + self.reg[7] += 1 + + def call(self, operand_a, operand_b): + # PUSH + return_address = self.pc + 2 + # decrement the SP, stored in R7 + self.reg[7] -= 1 + # store the value at the SP address + SP = self.reg[7] + # the return address is 8 as we can see by looking ahead by 2 + self.ram[SP] = return_address + # ram (command) at 7 contains 1 + reg_idx = self.ram[self.pc+1] + # reg 1 contains 24 + subroutine_address = self.reg[reg_idx] + # pc is at 24 + 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 the PC + self.pc = return_address + # increment stack + self.reg[7] += 1 + if __name__ == '__main__': cpu = CPU() diff --git a/our_program01.ls8 b/our_program01.ls8 new file mode 100644 index 000000000..234a54424 --- /dev/null +++ b/our_program01.ls8 @@ -0,0 +1,30 @@ +00000001 # print tim +00000001 # print tim +01000011 # print num +00101010 # the number 42 + +10000100 # save +01100011 # 99 +00000010 # into R2 + +10000100 # save +00000001 # the number 1 +00000011 # into R3 + +10100110 # ADD (R2 += R3) +00000010 # R2 +00000011 # R3 + +01000101 # print reg +00000010 # R2 again - should be 100 now! + +01000111 # PUSH +00000010 # from R2 + +01001000 # POP <-- PC +00000100 # into R4 + +01000101 # print reg +00000100 # Register 4 - should now be the number 99 + +00000010 # but this time it's the command halt \ No newline at end of file diff --git a/simple_machine02.py b/simple_machine02.py new file mode 100644 index 000000000..a83f7144e --- /dev/null +++ b/simple_machine02.py @@ -0,0 +1,148 @@ +import sys + +PRINT_TIM = 0b00000001 +HALT = 0b00000010 +PRINT_NUM = 0b01000011 # a 2-byte command, takes 1 argument +SAVE = 0b10000100 # a 3-byte command, takes 2 arguments +PRINT_REG = 0b01000101 +ADD = 0b10100110 +PUSH = 0b01000111 +POP = 0b01001000 + + +# a data-driven machine +# function call +# a "variable" == registers for our programs to save things into + + +# RAM +memory = [0] * 256 + +# registers, R0-R7 +registers = [0] * 8 + +# set the stack pointer +registers[7] = 0xF4 + +running = True + +# program counter +pc = 0 + +def load_ram(): + try: + if len(sys.argv) < 2: + print(f'Error from {sys.argv[0]}: missing filename argument') + print(f'Usage: python3 {sys.argv[0]} ') + sys.exit(1) + + + # add a counter that adds to memory at that index + ram_index = 0 + + with open(sys.argv[1]) as f: + for line in f: + split_line = line.split("#")[0] + stripped_split_line = split_line.strip() + + if stripped_split_line != "": + command = int(stripped_split_line, 2) + + # load command into memory + memory[ram_index] = command + + ram_index += 1 + + except FileNotFoundError: + print(f'Error from {sys.argv[0]}: {sys.argv[1]} not found') + print("(Did you double check the file name?)") + +load_ram() + +while running: + command = memory[pc] + + + if command == PRINT_TIM: + print('tim!') + + elif command == PRINT_NUM: + num_to_print = memory[pc + 1] # we already incremented PC! + print(num_to_print) + + # pc += 1 # but increment again + + + elif command == SAVE: + num_to_save = memory[pc + 1] + register_address = memory[pc + 2] + + registers[register_address] = num_to_save + + # shorter: + # registers[memory + 2] = memory[pc + 1] + + # pc += 2 + + elif command == PRINT_REG: + reg_address = memory[pc + 1] + + saved_number = registers[reg_address] + + print(saved_number) + + # print(registers[memory[pc + 1]]) + + elif command == ADD: + reg1_address = memory[pc + 1] + reg2_address = memory[pc + 2] + + registers[reg1_address] += registers[reg2_address] + + elif command == PUSH: + # decrement the SP + ## at start, the SP points to address F4 + ### can just do arithmetic on hex, like F4 - 1 + ## R7 is our SP, currently points to (aka holds) F4 + registers[7] -= 1 + + # copy value from given register into address pointed to by SP + ## value from register? + register_address = memory[pc + 1] + value = registers[register_address] + + ## copy into SP address + ### now let's copy this value into our memory + ### but where in memory??? + + # memory[SP] = value + # memory[register[7]] = value + + SP = registers[7] + memory[SP] = value + + + elif command == POP: + # Copy the value from the address pointed to by `SP` to the given register. + ## get the SP + SP = registers[7] + ## copy the value from memory at that SP address + value = memory[SP] + + ## get the target register address + ### aka, where should we put this value from RAM? + register_address = memory[pc + 1] + + ## Put the value in that register + registers[register_address] = value + + # Increment the SP (move it back up) + registers[7] += 1 + + + elif command == HALT: + running = False + + number_of_operands = command >> 6 + pc += (1 + number_of_operands) + # pc += 1 # so we don't get sucked into an infinite loop! \ No newline at end of file diff --git a/stack_and_more_notes.txt b/stack_and_more_notes.txt new file mode 100644 index 000000000..148df47f9 --- /dev/null +++ b/stack_and_more_notes.txt @@ -0,0 +1,165 @@ + +Computer arch topics we have implemented in our project +- RAM, self.ram +- registers! +- CPU, self.run +- ALU + +Why LS8? +- 8 bits +- not the registers, as you might think +- Also, 8-bit CPU and ALU architectures are those that are based on registers, +address buses, or data buses of that size + CPU + 00000000 + 00000000 + 00000000 + 00000000 + RAM + +- with 8-bit address bus, the CPU can address 256 bytes of RAM + + +General purpose calculating machine vs specialized calculating machines + "computer" "calculator" + + Can do anything hardwired for specific calculations + Broadly applicable Faster (often) + + + +More memory? Stack! +- more variables +- function calls and nested function calls + +To make a stack? +- Push +- Pop +- Memory space +-- RAM +-- 0-255 (256 bytes) + +- a pointer to track where the top of the stack +-- variable that is a memory address + + +how to push +how to Pop +Handling 'leftovers' from push +Stack underflow +Why doesn't the CPU prevent underflow, or prevent wrapping around in memory? +- don't want the CPU to spend time/energy checking +- used to be dev's job, now it's the compiler's job + +Stack overflow +- software's job to prevent this + +Stacks and nested function calls + +self.ram = [0] * 256 + +registers[7] = F3 + +SP = F3 + +memory[SP] + +FF: 00 +FE: 00 +FD: 00 +FC: 00 +FB: 00 +FA: 00 +F9: 00 +F8: 00 +F7: 00 +F6: 00 +F5: 00 +F4: 00 +F3: 42 <--- SP +F2: 42 +F1: 42 +F0: 00 +EF: 00 +EE: 00 +ED: 00 +EC: 00 +. +. +. +2B: 42 +2A: 42 +. +. +. +10: 42 +9: 42 +8: 42 +7: JUMP +6: R3 +5: PUSH +4: R3 +3: 42 +2: SAVE +1: PRINT_TIM +0: PRINT_TIM + + +R0: 99 +R1: 42 +R3: 42 + +PUSH R0: +- decrement the SP +- copy the value from the given register + +PUSH R1: +- decrement the SP +- copy the value from the given register + +POP R3: +- copy into the register +- increment SP + +PUSH R0: +- decrement the SP +- copy the value from the given register + +POP R3: +- copy into the register +- increment SP + + + + + + +Stack +700: 4 +699: 2 +698: 3 +697: 6 +696: 6 +695: 7 <-- SP +694: 3 +693: 6 + +registers[4] = 6 + +a = 4 + +def mult(x, y): + z = x * y + + return z + +def main(): + a = 2 + + b = 3 + + c = a * b + + d = mult(a, b) + + e = 7 \ No newline at end of file From 1c12dd92e3b893a0e8340bdde79fe7d2134c0aed Mon Sep 17 00:00:00 2001 From: Ash827 Date: Sun, 13 Dec 2020 21:36:00 -0500 Subject: [PATCH 4/5] Sprint Challenge complete --- ls8/cpu.py | 87 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 13 deletions(-) diff --git a/ls8/cpu.py b/ls8/cpu.py index 119758fe0..0a3f8d16b 100644 --- a/ls8/cpu.py +++ b/ls8/cpu.py @@ -1,6 +1,7 @@ """CPU functionality.""" import sys +import time HLT = 0b00000001 LDI = 0b10000010 @@ -9,12 +10,16 @@ ADD = 0b10100000 PUSH = 0b01000101 POP = 0b01000110 -CMP = 0b10100111 CALL = 0b01010000 RET = 0b00010001 + +# sprint challenge for MVP +CMP = 0b10100111 JEQ = 0b01010101 JNE = 0b01010110 JMP = 0b01010100 + +# stretch opcodes SHL = 0b10101100 SHR = 0b10101101 MOD = 0b10100100 @@ -23,6 +28,7 @@ AND = 0b10101000 XOR = 0b10101011 + class CPU: """Main CPU class.""" @@ -31,8 +37,12 @@ def __init__(self): self.ram = [0] * 256 self.reg = [0] * 8 self.reg[7] = 0xF4 - self.reg[6] = 0 # flags reg + # flags reg + self.reg[6] = 0 self.pc = 0 + self.running = True + # all flags set to false on initialization + self.fl = 0b00000000 self.branchtable = { HLT: self.hlt, LDI: self.ldi, @@ -41,15 +51,14 @@ def __init__(self): POP: self.pop, CALL: self.call, RET: self.ret, - #JEQ: self.jeq, - #JNE: self.jne, - #JMP: self.jmp + JEQ: self.jeq, + JNE: self.jne, + JMP: self.jmp, } def load(self): """Load a program into memory.""" - if (len(sys.argv)) != 2: print('remember to pass the second file name') print('usage: python fileio.py ') @@ -123,19 +132,15 @@ 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.fl, 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() @@ -174,11 +179,37 @@ def prn(self, operand_a, operand_b): def jmp(self, operand_a, operand_b): - reg_idx = self.reg[operand_a] - self.pc = reg_idx + + def jeq(self, operand_a, operand_b): + # If equal flag is set (true), jump to the + # address stored in the given register + 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: + # just increment + self.pc += 2 + + + def jne(self, operand_a, operand_b): + # If E flag is clear (false, 0), jump to + # the address stored in the given register + flags = self.reg[6] + equal_is_flagged = flags == 1 + if equal_is_flagged: + # just increment + self.pc += 2 + else: + # 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): # decrement the SP. @@ -200,6 +231,7 @@ def pop(self, operand_a, operand_b): # increment SP self.reg[7] += 1 + def call(self, operand_a, operand_b): # PUSH return_address = self.pc + 2 @@ -228,6 +260,35 @@ def ret(self, operand_a, operand_b): self.reg[7] += 1 + def check_inter(self): + interrupts = self.reg[self.im] & self.reg[self.isr] + for interrupt in range(8): + bit = 1 << interrupt + #if an interrupt is triggered + if interrupts & bit: + # save the old interrupt state + self.old_im = self.reg[self.im] + # disable interrupts + self.reg[self.im] = 0 + # clear the interrupt + self.reg[self.isr] &= (255 ^ bit) + # decrement the stack pointer + self.reg[self.sp] -= 1 + # push the pc to the stack + self.ram_write(self.reg[self.sp], self.pc) + #decrement the stack pointer + self.reg[self.sp] -= 1 + # push the flags to the stack + self.ram_write(self.reg[self.sp], self.fl) + # push the registers to the stack R0-R6 + for i in range(7): + self.reg[self.sp] -= 1 + self.ram_write(self.reg[self.sp], self.reg[i]) + self.pc = self.ram[0xF8 + interrupt] + # break out and stop checking interrupts + break + + if __name__ == '__main__': cpu = CPU() cpu.load() From bfc4ca132679572a48e7a10a5801597b8e66c6dc Mon Sep 17 00:00:00 2001 From: Ashley Gaskins Date: Sun, 13 Dec 2020 21:37:10 -0500 Subject: [PATCH 5/5] Add files via upload --- sprint complete.pdf | Bin 0 -> 72866 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 sprint complete.pdf diff --git a/sprint complete.pdf b/sprint complete.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4d7565762f39f1516c3a9299f2d8585ab3b34844 GIT binary patch literal 72866 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

eI9X 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 literal 0 HcmV?d00001