diff --git a/LS8-spec.md b/LS8-spec.md index feb34099..0d1d7ab3 100644 --- a/LS8-spec.md +++ b/LS8-spec.md @@ -73,7 +73,9 @@ Memory map: The SP points at the value at the top of the stack (most recently pushed), or at address `F4` if the stack is empty. - +- R7 is reserved as the stack pointer (SP) +- Stack is empty if R7 points to 0xF4 +- decriment to 0xF3, etc. ## Interrupts diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 00000000..2529cec0 --- /dev/null +++ b/NOTES.md @@ -0,0 +1,305 @@ +# Computer Architecture + +## Outline of Steps to Build CPU +1. Review Main (ls8.c) +2. Define cpu struct (cpu.h) +3. Init / Power on State / LS-8 is booted + - Define PC (cleared to 0) + - Define Registers + R0-R6 (cleared to 0) + R7 (set to 0xF4) + - RAM (cleared to 0 -- using memset) +4. cpu_load() + - hard coded for now +5. cpu_run + - need to define cpu_ram_read(MAR) and cpu_ram_write(MDR) + - Define IR + - Define operand0 + - Define operand1 + - switch IR + case LDI + case PRN + case HLT + default() +6. Adding more ALU (math instructions), e.g.: + add #define MUL 0b1010001 (cpu.h) + add ALU_MUL in enum alu_op (cpu.c) (symbolic name for a number) + add case ALU_MUL: (to switch(op) in alu() in cpu.c) + cpu->reg[regA] = valA * valB; (currying?) + break; + add case MUL: (to switch(IR) in cpu_run() in cpu.c) + alu(cpu, ALU_MUL, operand0, operand1); + cpu->PC +=3; + break; +7. Accounting for whitespace/comments in code + + +*** First 2 bits of instructions state how many operands *** + +## Computer Architecture: Objectives + Deconstruct the components of the CPU + Understand how the CPU components communicate with RAM + Learn the system bus ?? + +## Computer Architecture: Basics +Transistors +Gates (made up of transistors) +Digital Logic (common operations performed by gates) + AND + OR + NOT + NAND + NOR + XOR +Complex Gate Structures + ALU + ADD, MUL, etc. + CPU +RAM + Random Access Memory (RAM) + Fast (compared to hard drives SSDs) + Array of 8-bit numberss accessed by an index + Each element in RAM stored as 1 byte (8-bit number) + Larger, multi-byte values store in sequential addresses in RAM + CPU communicates with RAM via the memory bus + +Example: +Address 0 1 2 3 4 5 6 7 +Value 07 A2 FF 7D 4E F4 A3 8a + +## CPU Terminolgy: +Bytes of data stored in RAM (memory) +Larger 64-bit (8-byte) numbers, stored sequentially in RAM that the CPU can operate on at once are called words +Exact number of bytes per work depends on the architucture: + - 8 bytes for a 64-bit CPU + - 4 bytes for a 32-bit CPU + - 1 byte for an 8-bit CPU + +## CPU Registers +Registers store words that can be accessed at ultra-high-speed + - Similar to RAM, except stored directly on the CPU -- so much faster + - Limited number of registers (8, 16, or 32) + - Fixed names (R0, R1, etc. or EAX, EBX, etc.) + - Many CPUs can only perform math opersons on registers which must be loaded from RAM first (x86 family can perform math on registers quickly or RAM slowly). + + ## CPU Instructions + - Stored on RAM + - Represented by numbers + - Name given to instructions are convenient for devs, not computers + - CPU tracks the address of currently-executing instruction in RAM & performs an action based on the current instruction + - Program Counter (PC): stores the address of the currently-executing instruction + + +## CPU Stack +Stack data is stored in RAM +Stack pointer keeps track of the address of the top of the stack +PUSH and POP to add and remove items from the stack. +Typically, the stack starts at a higher memory address and grows down; the program starts at a low memory address and grows upwards. + +PUSH +*stkptr decriments to a new address and the value is pushed into the stack at that address + +POP +Capture value at the address the *stkptr is pointing to. Insert that value into R0 and *stkptr increments up to the next address. POP next value at R1. +The value still remains at the address (usually it is not reset to zero, instead the next time a value is pushed in it just replaces the older value.) + +When is the stack used? +Temporary storage of variables (ran out of registers for a temp value doing alu calcs) +Return address from a subroutine (subroutines are like functions) +Storage of registers and CPU state while handling an interrupt +Allocation of local variables for a subroutine + +PUSH too many items on to a stack? Stack overload since the stack would have met in the middle with the program value and instructions. +POP from an empty stack? Grab an old value that had not been re-written over with a push? +How to detect if the stack is empty? Look to where the *stkptr is pointing to the top address. +During an interrupt, storage of registers and CPU state (i.e., the PC) must be stored on the stack + +## Subroutines +Subroutines are similar to functions +CALL a subroutine at an address +RET (return) from that subroutine to where we left off +No arguments (just points to an address) +No return values +The return address of a subroutines stored is stored onto the stack +CALL pushes the address of the instruction *after it* onto the stack, then move the PC to the subroutine address. +RET will pop the return address of the stack and store it at the PC. + +Example of a Subroutine + +Parent routine +-------------- +00: LDI R0, 15 (Load 15 (a value) onto register R0) +03: LDI R1, 0B (Load 0B (an address) onto register R1) +06: CALL R1 (Push 08 (an address) onto stack then GOTO R1 (which has 0B)) +08: PRN R0 (Print R0 current value is 25) +0A: HLT (HLT) + +Subroutine +-------------- +0B: ADD R0, 10 (Adds 10 (a value) to R0) +0E: RET (POP return address of stack (which is 08) and stores it at the PC) + +Stack: 08 POP => empty +R0: 15 + 10 => 25 +R1: 0B + +Subroutines keep things dry + +## Interupts +Handle interruption from a peripherals (keyboards, external storage) + +Building Truth Tables from NAND +NAND ~(A AND B) +NOT A NAND A +AND NOT(A NAND B) +OR NAND(NOT A, NOT B) +NOR NOT(OR) +XOR OR(AND(NOT A, B) AND(A NOT B))) + + + +### Half Adder +A and/or B in +Sum and/or Carry out + +Half Adder Truth Table +Inputs Outputs +A B Carry Out Sum +0 0 0 0 +0 1 0 1 +1 0 0 1 +1 1 1 0 + +C = AND(A, B) +S = XOR(A, B) + + +### Full Adder +A and/or B and/or Carry In +Sum and/or Carry out + + +Full Adder Truth Table +Inputs Outputs +A B Carry In Carry Out Sum +0 0 0 0 0 +0 0 1 0 1 +0 1 0 0 1 +0 1 1 1 0 +1 0 0 0 1 +1 0 1 1 0 +1 1 0 1 0 +1 1 1 1 1 + +C = AND(A, B) +S = XOR(A, B) + +CARRY-OUT = A AND B OR Cin(A XOR B) + = A.B + Cin(A ⊕ B) + = A AND B OR Cin AND (A XOR B) + +SUM = (A XOR B) XOR Cin + = (A ⊕ B) ⊕ Cin + = XOR(XOR(a, b), c) + +Karnaugh Map + +Full-Adder +Half-Adder with carry and the sum of the first + + + + + +# Equivalent Equations +The 2nd equation replaces the no carry with a zero + + 1 1 11 100010110 + 010101001 010101001 ++ 010001011 + 010001011 +----------- ----------- + 100110100 100110100 + + + +Bit Shifting +x = 0b10000000 +y = x >> 6 // y is 0b00000010 +1 is shifted to the right 6 times + +NEED TO COMPLETE LATER +CPU +Transistors +Gates +Digital Logic + AND OR NOT + + + +CPU +Interrupt Handler (ISR/IMR) +PC IR + Control + Registers + ALU + + + Cache + RAM + + + +Interrupt Handler + + +Keyboard Disk DMA + +RAM (Random Access Memory) + +1 byte (8 bit number) + +Value 07 A2 FF + +64-bit (8-byte) numbers + +CPU Registers (R0 - R7) +Registers + +CPU Instructions +CPU Clock + Clock Cycle Rate + + + + + + Dec to Binary to + |||| + 13 == 0b1101 == -3 + 14 == 0b1110 == -2 + 15 == 0b1111 == -1 + 16 == 0b10000 + + 2's compliment + IEEE-754 for floating point + + 1’s complement of a binary number is another binary number obtained by toggling all bits in it, i.e., transforming the 0 bit to 1 and the 1 bit to 0. + +Examples: + +1's complement of "0111" is "1000" +1's complement of "1100" is "0011" +2’s complement of a binary number is 1 added to the 1’s complement of the binary number. +Examples: + +2's complement of "0111" is "1001" +2's complement of "1100" is "0100" + +Binary Coded Decimal +26 +0010 0110 BCD==26 +1000 1001 + + + + diff --git a/ls8/cpu.c b/ls8/cpu.c index bdb1f506..d2491b6d 100644 --- a/ls8/cpu.c +++ b/ls8/cpu.c @@ -1,29 +1,56 @@ +#include +#include +#include #include "cpu.h" #define DATA_LEN 6 + +unsigned char cpu_ram_read(struct cpu *cpu, unsigned char MAR) { + return cpu->ram[MAR]; +} + +void cpu_ram_write(struct cpu *cpu, unsigned char MAR, unsigned char MDR) { + cpu->ram[MAR] = MDR; +} + /** * Load the binary bytes from a .ls8 source file into a RAM array */ -void cpu_load(struct cpu *cpu) +void cpu_load(struct cpu *cpu, char *filename) { - char data[DATA_LEN] = { - // From print8.ls8 - 0b10000010, // LDI R0,8 - 0b00000000, - 0b00001000, - 0b01000111, // PRN R0 - 0b00000000, - 0b00000001 // HLT - }; - + // open the file + FILE *fp = fopen(filename, "r"); + + if (fp == NULL) { + fprintf(stderr, "ls8: error opening file %s\n", filename); + exit(2); + } + + char line[8192]; // will break if whitespace or comments > 8192 int address = 0; - for (int i = 0; i < DATA_LEN; i++) { - cpu->ram[address++] = data[i]; + while(fgets(line, sizeof line, fp) != NULL) { + char *endptr; // ignores comments (prefaced with #) and whitespace. + unsigned char val = strtoul(line, &endptr, 2); + + if (endptr == line) { + continue; + } + + cpu_ram_write(cpu, address++, val); } +} + +void cpu_push(struct cpu *cpu, unsigned char val) { + cpu->reg[7]--; + cpu_ram_write(cpu, cpu->reg[7], val); +} - // TODO: Replace this with something less hard-coded +unsigned char cpu_pop(struct cpu *cpu) { + unsigned char val = cpu_ram_read(cpu, cpu->reg[7]); + cpu->reg[7]++; + return val; } /** @@ -31,9 +58,78 @@ void cpu_load(struct cpu *cpu) */ void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB) { + + unsigned char valA = cpu->reg[regA]; + unsigned char valB = cpu->reg[regB]; + switch (op) { case ALU_MUL: - // TODO + cpu->reg[regA] = valA * valB; + break; + + case ALU_ADD: + cpu->reg[regA] = valA + valB; + break; + + case ALU_SUB: + cpu->reg[regA] = valA - valB; + break; + + case ALU_DIV: + if (valB == 0) { + printf("Dividing by zero is undefined."); + exit(1); + } + cpu->reg[regA] = valA / valB; + break; + + case ALU_MOD: + cpu->reg[regA] = valA % valB; + break; + + case ALU_SHR: + cpu->reg[regA] = valA >> valB; + break; + + case ALU_SHL: + cpu->reg[regA] = valA << valB; + break; + + case ALU_AND: + cpu->reg[regA] = valA & valB; + break; + + case ALU_NOT: + cpu->reg[regA] = ~valA; + break; + + case ALU_OR: + cpu->reg[regA] = valA | valB; + break; + + case ALU_XOR: + cpu->reg[regA] = valA ^ valB; + break; + + case ALU_INC: + cpu->reg[regA] = valA + 1; + break; + + case ALU_DEC: + cpu->reg[regA] = valA - 1; + break; + + case ALU_CMP: + if (valA == valB) { + cpu->FL = 1; + } else if (valA > valB) { + cpu->FL = 2; + } else if (valA < valB) { + cpu->FL = 4; + } else { + printf("Comparison Issue"); + exit(1); + } break; // TODO: implement more ALU ops @@ -42,6 +138,11 @@ void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB /** * Run the CPU + * `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 +* `FL`: Flags, see below */ void cpu_run(struct cpu *cpu) { @@ -55,6 +156,162 @@ void cpu_run(struct cpu *cpu) // 4. switch() over it to decide on a course of action. // 5. Do whatever the instruction should do according to the spec. // 6. Move the PC to the next instruction. + unsigned char IR = cpu_ram_read(cpu, cpu->PC); + unsigned char operand0 = cpu_ram_read(cpu, cpu->PC + 1); + unsigned char operand1 = cpu_ram_read(cpu, cpu->PC + 2); + + // printf("TRACE: %02X: %02X %02X %02X\n", cpu->PC, IR, operand0, operand1); + + switch(IR) { + case LDI: + cpu->reg[operand0] = operand1; + cpu->PC += 1 + (IR >> 6); + break; + case LD: + cpu->reg[operand0] = cpu_ram_read(cpu, cpu->reg[operand1]); + // cpu->reg[operand0] = cpu->ram[cpu->reg[operand1]]; + cpu->PC += 1 + (IR >> 6); + break; + case ST: + // cpu_ram_write(cpu, operand0, cpu_ram_read(cpu, cpu->reg[operand1])); + cpu_ram_write(cpu, cpu->reg[operand0], cpu->reg[operand1]); + cpu->PC += 1 + (IR >> 6); + break; + case PRN: + printf("%d\n", cpu->reg[operand0]); + cpu->PC += 1 + (IR >> 6); + break; + case PRA: + printf("%c\n", cpu->reg[operand0]); + cpu->PC += 1 + (IR >> 6); + break; + case SHR: + alu(cpu, ALU_SHR, operand0, operand1); + cpu->PC += 1 + (IR >> 6); + break; + case SHL: + alu(cpu, ALU_SHL, operand0, operand1); + cpu->PC += 1 + (IR >> 6); + break; + case ADD: + alu(cpu, ALU_ADD, operand0, operand1); + cpu->PC += 1 + (IR >> 6); + break; + case SUB: + alu(cpu, ALU_SUB, operand0, operand1); + cpu->PC += 1 + (IR >> 6); + break; + case MUL: + alu(cpu, ALU_MUL, operand0, operand1); + cpu->PC += 1 + (IR >> 6); + break; + case DIV: + alu(cpu, ALU_DIV, operand0, operand1); + cpu->PC += 1 + (IR >> 6); + break; + case MOD: + alu(cpu, ALU_MOD, operand0, operand1); + cpu->PC += 1 + (IR >> 6); + break; + case INC: + alu(cpu, ALU_INC, operand0, 0); + cpu->PC += 1 + (IR >> 6); + break; + case DEC: + alu(cpu, ALU_DEC, operand0, 0); + cpu->PC += 1 + (IR >> 6); + break; + case AND: + alu(cpu, ALU_AND, operand0, operand1); + cpu->PC += 1 + (IR >> 6); + break; + case NOT: + alu(cpu, ALU_NOT, operand0, 0); + cpu->PC += 1 + (IR >> 6); + break; + case OR: + alu(cpu, ALU_OR, operand0, operand1); + cpu->PC += 1 + (IR >> 6); + break; + case XOR: + alu(cpu, ALU_XOR, operand0, operand1); + cpu->PC += 1 + (IR >> 6); + break; + case CMP: + alu(cpu, ALU_CMP, operand0, operand1); + cpu->PC += 1 + (IR >> 6); + break; + case PUSH: + cpu_push(cpu, cpu->reg[operand0]); + cpu->PC += 1 + (IR >> 6); + break; + case POP: + cpu->reg[operand0] = cpu_pop(cpu); + cpu->PC += 1 + (IR >> 6); + break; + case CALL: + cpu_push(cpu, cpu->PC + 2); + cpu->PC = cpu->reg[operand0]; + break; + case JEQ: + if (cpu->FL == 1) { + cpu->PC = cpu->reg[operand0]; + } else { + cpu->PC += 1 + (IR >> 6); + } + break; + case JGE: + if (cpu->FL == 1 || cpu->FL == 2) { + cpu->PC = cpu->reg[operand0]; + } else { + cpu->PC += 1 + (IR >> 6); + } + break; + case JGT: + if (cpu->FL == 2) { + cpu->PC = cpu->reg[operand0]; + } else { + cpu->PC += 1 + (IR >> 6); + } + break; + case JLE: + if (cpu->FL == 1 || cpu->FL == 4) { + cpu->PC = cpu->reg[operand0]; + } else { + cpu->PC += 1 + (IR >> 6); + } + break; + case JLT: + if (cpu->FL == 4) { + cpu->PC = cpu->reg[operand0]; + } else { + cpu->PC += 1 + (IR >> 6); + } + break; + case JNE: + if (cpu->FL != 1) { + cpu->PC = cpu->reg[operand0]; + } else { + cpu->PC += 1 + (IR >> 6); + } + break; + case JMP: + cpu->PC = cpu->reg[operand0]; + break; + case RET: + cpu->PC = cpu_pop(cpu); + break; + case IRET: + cpu_ram_read(cpu, cpu->reg[7]); + cpu->PC = cpu_pop(cpu); + break; + case HLT: + running = 0; + break; + default: + printf("unexpected instruction 0x%02x 0x%02x\n", IR, cpu->PC); + exit(1); + } } } @@ -63,5 +320,14 @@ void cpu_run(struct cpu *cpu) */ void cpu_init(struct cpu *cpu) { - // TODO: Initialize the PC and other special registers + cpu->PC = 0; + cpu->FL = 0; + + for (int i = 0; i < 7; i++) { + cpu->reg[i] = 0; + } + + cpu->reg[7] = 0xF4; + + memset(cpu->ram, 0, sizeof(cpu->ram)); } diff --git a/ls8/cpu.h b/ls8/cpu.h index 46e49c44..9db1e651 100644 --- a/ls8/cpu.h +++ b/ls8/cpu.h @@ -3,15 +3,28 @@ // Holds all information about the CPU struct cpu { - // TODO - // PC - // registers (array) - // ram (array) + unsigned char PC; // program counter + unsigned char FL; // flags + unsigned char reg[8]; // 8 registers + unsigned char ram[256]; // 256 bytes of RAM }; // ALU operations enum alu_op { - ALU_MUL + ALU_MUL, + ALU_ADD, + ALU_SUB, + ALU_DIV, + ALU_MOD, + ALU_SHL, + ALU_SHR, + ALU_AND, + ALU_NOT, + ALU_OR, + ALU_XOR, + ALU_INC, + ALU_DEC, + ALU_CMP, // Add more here }; @@ -20,14 +33,53 @@ enum alu_op { // These use binary literals. If these aren't available with your compiler, hex // literals should be used. -#define LDI 0b10000010 +#define LDI 0b10000010 // 00000rrr iiiiiiii +#define LD 0b10000011 // 00000aaa 00000bbb +#define ST 0b10000100 // 00000aaa 00000bbb #define HLT 0b00000001 #define PRN 0b01000111 +#define PRA 0b01001000 // 00000rrr + +#define PUSH 0b01000101 // 00000rrr +#define POP 0b01000110 // 00000rrr +#define CALL 0b01010000 // 00000rrr +#define RET 0b00010001 + +#define INT 0b01010010 // 00000rrr +#define IRET 0b00010011 + +#define JMP 0b01010100 // 00000rrr +#define JEQ 0b01010101 // 00000rrr +#define JNE 0b01010110 // 00000rrr +#define JGT 0b01010111 // 00000rrr +#define JLT 0b01011000 // 00000rrr +#define JLE 0b01011001 // 00000rrr +#define JGE 0b01011010 // 00000rrr + +// ALU ops +#define ADD 0b10100000 // 00000aaa 00000bbb +#define SUB 0b10100001 // 00000aaa 00000bbb +#define MUL 0b10100010 // 00000aaa 00000bbb +#define DIV 0b10100011 // 00000aaa 00000bbb +#define MOD 0b10100100 // 00000aaa 00000bbb + +#define INC 0b01100101 // 00000rrr +#define DEC 0b01100110 // 00000rrr + +#define CMP 0b10100111 // 00000aaa 00000bbb + +#define AND 0b10101000 // 00000aaa 00000bbb +#define NOT 0b01101001 // 00000rrr +#define OR 0b10101010 // 00000aaa 00000bbb +#define XOR 0b10101011 // 00000aaa 00000bbb +#define SHL 0b10101100 // 00000aaa 00000bbb shift left +#define SHR 0b10101101 // 00000aaa 00000bbb shift right + // TODO: more instructions here. These can be used in cpu_run(). // Function declarations -extern void cpu_load(struct cpu *cpu); +extern void cpu_load(struct cpu *cpu, char *filename); extern void cpu_init(struct cpu *cpu); extern void cpu_run(struct cpu *cpu); diff --git a/ls8/examples/call.ls8 b/ls8/examples/call.ls8 index 71f4dbe7..1d7157ab 100644 --- a/ls8/examples/call.ls8 +++ b/ls8/examples/call.ls8 @@ -1,31 +1,32 @@ -10000010 # LDI R1,MULT2PRINT +10000010 # LDI R1,MULT2PRINT 0 00000001 00011000 -10000010 # LDI R0,10 +10000010 # LDI R0,10 3 00000000 00001010 -01010000 # CALL R1 +01010000 # CALL R1 6 00000001 -10000010 # LDI R0,15 +10000010 # LDI R0,15 8 00000000 00001111 -01010000 # CALL R1 +01010000 # CALL R1 0B 00000001 -10000010 # LDI R0,18 +10000010 # LDI R0,18 0D 00000000 00010010 -01010000 # CALL R1 +01010000 # CALL R1 10 00000001 -10000010 # LDI R0,30 +10000010 # LDI R0,30 12 00000000 00011110 -01010000 # CALL R1 +01010000 # CALL R1 15 00000001 -00000001 # HLT +00000001 # HLT 17 # MULT2PRINT (address 24): -10100000 # ADD R0,R0 +10100000 # ADD R0,R0 18 00000000 00000000 -01000111 # PRN R0 +01000111 # PRN R0 1B +# 01001000 # PRA R0 00000000 -00010001 # RET +00010001 # RET 1D diff --git a/ls8/examples/interrupts.ls8 b/ls8/examples/interrupts.ls8 index 12bd95d8..b8187f4a 100644 --- a/ls8/examples/interrupts.ls8 +++ b/ls8/examples/interrupts.ls8 @@ -1,25 +1,25 @@ -10000010 # LDI R0,0XF8 +10000010 # LDI R0,0XF8 0 RO 0XF8 00000000 11111000 -10000010 # LDI R1,INTHANDLER +10000010 # LDI R1,INTHANDLER 3 R1 INTHANDLER 00000001 00010001 -10000100 # ST R0,R1 +10000100 # ST R0,R1 6 STORE R1 at R0 0XF8 INTHANDLER 00000000 00000001 -10000010 # LDI R5,1 +10000010 # LDI R5,1 9 R5 1 00000101 00000001 -10000010 # LDI R0,LOOP +10000010 # LDI R0,LOOP 0C R0 0XF8 INTHANDLER LOOP 00000000 00001111 # LOOP (address 15): -01010100 # JMP R0 +01010100 # JMP R0 0F 00000000 # INTHANDLER (address 17): -10000010 # LDI R0,65 +10000010 # LDI R0,65 11 00000000 01000001 -01001000 # PRA R0 +01001000 # PRA R0 14 00000000 -00010011 # IRET +00010011 # IRET 16 diff --git a/ls8/examples/printstr.ls8 b/ls8/examples/printstr.ls8 index c69cc778..a8a2776a 100644 --- a/ls8/examples/printstr.ls8 +++ b/ls8/examples/printstr.ls8 @@ -1,44 +1,44 @@ -10000010 # LDI R0,HELLO +10000010 # LDI R0,HELLO 01 R0: Hello 00000000 -00100110 -10000010 # LDI R1,14 +00100110 # (address 38) +10000010 # LDI R1,14 03 R1: 14 00000001 00001110 -10000010 # LDI R2,PRINTSTR +10000010 # LDI R2,PRINTSTR 06 R2: PRINTSTR 00000010 00001100 -01010000 # CALL R2 +01010000 # CALL R2 09 00000010 -00000001 # HLT +00000001 # HLT OB # PRINTSTR (address 12): -10000010 # LDI R2,0 +10000010 # LDI R2,0 0C 00000010 00000000 # PRINTSTRLOOP (address 15): -10100111 # CMP R1,R2 +10100111 # CMP R1,R2 0F 00000001 00000010 -10000010 # LDI R3,PRINTSTREND +10000010 # LDI R3,PRINTSTREND 12 00000011 00100101 -01010101 # JEQ R3 +01010101 # JEQ R3 15 00000011 -10000011 # LD R3,R0 +10000011 # LD R3,R0 17 00000011 00000000 -01001000 # PRA R3 +01001000 # PRA R3 1A 00000011 -01100101 # INC R0 +01100101 # INC R0 1C 00000000 -01100110 # DEC R1 +01100110 # DEC R1 1E 00000001 -10000010 # LDI R3,PRINTSTRLOOP +10000010 # LDI R3,PRINTSTRLOOP 20 00000011 00001111 -01010100 # JMP R3 +01010100 # JMP R3 23 00000011 -# PRINTSTREND (address 37): -00010001 # RET +# PRINTSTREND (address 37): +00010001 # RET 25 # HELLO (address 38): 01001000 # H 01100101 # e diff --git a/ls8/examples/sprint.ls8 b/ls8/examples/sprint.ls8 new file mode 100644 index 00000000..031700d7 --- /dev/null +++ b/ls8/examples/sprint.ls8 @@ -0,0 +1,86 @@ +# Code to test the Sprint Challenge +# +# Expected output: +# 1 +# 4 +# 5 + +10000010 # LDI R0,10 R0 10 +00000000 +00001010 +10000010 # LDI R1,20 R1 20 +00000001 +00010100 +10000010 # LDI R2,TEST1 R2 TEST1 +00000010 +00010011 +10100111 # CMP R0,R1 10 < 20 0B100 +00000000 +00000001 +01010101 # JEQ R2 not true +00000010 +10000010 # LDI R3,1 R3 1 +00000011 +00000001 +01000111 # PRN R3 Print 1 +00000011 +# TEST1 (address 19): +10000010 # LDI R2,TEST2 R2 Test2 +00000010 +00100000 +10100111 # CMP R0,R1 10 < 20 0B100 +00000000 +00000001 +01010110 # JNE R2 not true +00000010 +10000010 # LDI R3,2 +00000011 +00000010 +01000111 # PRN R3 +00000011 +# TEST2 (address 32): +10000010 # LDI R1,10 +00000001 +00001010 +10000010 # LDI R2,TEST3 +00000010 +00110000 +10100111 # CMP R0,R1 +00000000 +00000001 +01010101 # JEQ R2 +00000010 +10000010 # LDI R3,3 +00000011 +00000011 +01000111 # PRN R3 +00000011 +# TEST3 (address 48): +10000010 # LDI R2,TEST4 +00000010 +00111101 +10100111 # CMP R0,R1 +00000000 +00000001 +01010110 # JNE R2 +00000010 +10000010 # LDI R3,4 +00000011 +00000100 +01000111 # PRN R3 +00000011 +# TEST4 (address 61): +10000010 # LDI R3,5 +00000011 +00000101 +01000111 # PRN R3 +00000011 +10000010 # LDI R2,TEST5 +00000010 +01001001 +01010100 # JMP R2 +00000010 +01000111 # PRN R3 +00000011 +# TEST5 (address 73): +00000001 # HLT diff --git a/ls8/examples/stack.ls8 b/ls8/examples/stack.ls8 index e7c6e668..638c5b81 100644 --- a/ls8/examples/stack.ls8 +++ b/ls8/examples/stack.ls8 @@ -13,7 +13,7 @@ 00000011 01000110 # POP R0 00000000 -01000111 # PRN R0 +01000111 # PRN R0 2 00000000 10000010 # LDI R0,4 00000000 @@ -24,8 +24,8 @@ 00000010 01000110 # POP R1 00000001 -01000111 # PRN R2 +01000111 # PRN R2 4 00000010 -01000111 # PRN R1 +01000111 # PRN R1 1 00000001 00000001 # HLT diff --git a/ls8/ls8.c b/ls8/ls8.c index d24578ee..d01aaa95 100644 --- a/ls8/ls8.c +++ b/ls8/ls8.c @@ -4,12 +4,19 @@ /** * Main */ -int main(void) +int main(int argc, char **argv) { struct cpu cpu; + if (argc != 2) { + fprintf(stderr, "usage: ls8 filename\n"); + return 1; + } + + char *filename = argv[1]; + cpu_init(&cpu); - cpu_load(&cpu); + cpu_load(&cpu, filename); cpu_run(&cpu); return 0;