diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..6a0d7102 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "workbench.colorCustomizations": { + "activityBar.background": "#033615", + "titleBar.activeBackground": "#044B1D", + "titleBar.activeForeground": "#F0FEF5" + } +} \ No newline at end of file diff --git a/ls8/cpu.c b/ls8/cpu.c index bdb1f506..5f687e98 100644 --- a/ls8/cpu.c +++ b/ls8/cpu.c @@ -1,29 +1,60 @@ #include "cpu.h" +#include +#include +#include #define DATA_LEN 6 +// read from memory mar = memory address register +unsigned char cpu_ram_read(struct cpu *cpu, unsigned char mar) +{ + return cpu->ram[mar]; +} + +// Write to memory +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 - }; - - int address = 0; - - for (int i = 0; i < DATA_LEN; i++) { - cpu->ram[address++] = data[i]; + //Init the file + FILE *fp = fopen(filename, "r"); + + //Init the line that hold 1024 characters + char line[1024]; + + //Init the Mem Address Register to 0 + unsigned char mar = 0x00; + + //Check if the file exits: + if (fp == NULL) + { + fprintf(stderr, "error opening file %s\n", filename); + exit(2); + } + + //While there's lines in the file... + while (fgets(line, sizeof line, fp) != NULL) + { + char *endptr; + unsigned char machine_code = strtoul(line, &endptr, 2); + + if (endptr == line) + { + //we got no numbers + continue; + } + + //Write it into Memory + cpu_ram_write(cpu, mar++, machine_code); } - // TODO: Replace this with something less hard-coded + fclose(fp); } /** @@ -31,12 +62,29 @@ void cpu_load(struct cpu *cpu) */ void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB) { - switch (op) { - case ALU_MUL: - // TODO - break; + switch (op) + { + case ALU_MUL: + cpu->reg[regA] *= cpu->reg[regB]; + break; - // TODO: implement more ALU ops + case ALU_ADD: + cpu->reg[regA] += cpu->reg[regB]; + break; + case ALU_CMP: + if (cpu->reg[regA] == cpu->reg[regB]) + { + cpu->flag_reg[0] = 1; // E = Equals = 0th bit + } + else if (cpu->reg[regA] > cpu->reg[regB]) + { + cpu->flag_reg[1] = 1; // G = Greater than = 1st bit + } + else + { + cpu->flag_reg[2] = 1; // L = Less than = 2nd bit + } + break; } } @@ -45,16 +93,92 @@ void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB */ void cpu_run(struct cpu *cpu) { - int running = 1; // True until we get a HLT instruction - - while (running) { - // TODO - // 1. Get the value of the current instruction (in address PC). - // 2. Figure out how many operands this next instruction requires - // 3. Get the appropriate value(s) of the operands following this instruction - // 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. + int running = 1; + + unsigned char IR; + + while (running) + { + + IR = cpu_ram_read(cpu, cpu->PC); + unsigned char operandA = cpu_ram_read(cpu, (cpu->PC + 1) & 0xFF); + unsigned char operandB = cpu_ram_read(cpu, (cpu->PC + 2) & 0xFF); + + printf("TRACE: %02X | %02X %02X %02X |", cpu->PC, IR, operandA, operandB); + + printf("\n"); + + int add_to_pc = (IR >> 6) + 1; + // 2. switch() over it to decide on a course of action. + switch (IR) + { + case LDI: + cpu->reg[operandA] = operandB; + + break; + + case PRN: + printf("%d\n", cpu->reg[operandA]); + break; + + case MUL: + alu(cpu, ALU_MUL, operandA, operandB); + break; + + case ADD: + alu(cpu, ALU_ADD, operandA, operandB); + break; + + case PUSH: + cpu_ram_write(cpu, --cpu->reg[7], cpu->reg[operandA]); + break; + + case POP: + cpu->reg[operandA] = cpu_ram_read(cpu, cpu->reg[7]++); + break; + + case CALL: + cpu_ram_write(cpu, --cpu->reg[7], ++cpu->PC); + cpu->PC = cpu->reg[operandA]; + add_to_pc = 0; + break; + + case RET: + cpu->PC = cpu_ram_read(cpu, cpu->reg[7]++); + break; + + case CMP: + alu(cpu, ALU_CMP, operandA, operandB); + break; + + case JMP: + cpu->PC = cpu->reg[operandA]; + add_to_pc = 0; + break; + + case JEQ: + if (cpu->flag_reg[0] == 1) + { + cpu->PC = cpu->reg[operandA]; + add_to_pc = 0; + } + break; + + case JNE: + if (cpu->flag_reg[0] == 0) + { + cpu->PC = cpu->reg[operandA]; + add_to_pc = 0; + } + break; + + case HLT: + running = 0; + break; + } + + // 4. Move the PC to the next instruction. + cpu->PC += add_to_pc; } } @@ -63,5 +187,17 @@ void cpu_run(struct cpu *cpu) */ void cpu_init(struct cpu *cpu) { - // TODO: Initialize the PC and other special registers + // Power On State + // R0-R6 are cleared to 0. + // R7 is set to 0xF4. + + // PC and FL registers are cleared to 0. + // RAM is cleared to 0. + cpu->PC = 0; + + memset(cpu->ram, 0, sizeof(cpu->ram)); + memset(cpu->reg, 0, sizeof(cpu->reg)); + memset(cpu->flag_reg, 0, sizeof(cpu->flag_reg)); + + cpu->reg[7] = 0b11110100; } diff --git a/ls8/cpu.h b/ls8/cpu.h index 46e49c44..13412114 100644 --- a/ls8/cpu.h +++ b/ls8/cpu.h @@ -2,17 +2,34 @@ #define _CPU_H_ // Holds all information about the CPU -struct cpu { - // TODO - // PC - // registers (array) - // ram (array) +struct cpu +{ + unsigned char PC; + unsigned char flag_reg[8]; + unsigned char reg[8]; + unsigned char ram[256]; }; // ALU operations -enum alu_op { - ALU_MUL - // Add more here +enum alu_op +{ + ALU_MUL, + ALU_ADD, + ALU_CMP + + // ALU_ADD = 0b10100000, + // ALU_DEC = 0b01100110, + // ALU_DIV = 0b10100011, + // ALU_INC = 0b01100101, + // ALU_MOD = 0b10100100, + // ALU_SUB = 0b10100001, + // ALU_CMP = 0b10100111, + // ALU_AND = 0b10101000, + // ALU_XOR = 0b10101011, + // ALU_NOT = 0b01101001, + // ALU_OR = 0b10101010, + // ALU_SHL = 0b10101100, + // ALU_SHR = 0b10101101 }; // Instructions @@ -20,15 +37,48 @@ enum alu_op { // These use binary literals. If these aren't available with your compiler, hex // literals should be used. -#define LDI 0b10000010 -#define HLT 0b00000001 -#define PRN 0b01000111 +#define PRN 0b01000111 +#define HLT 0b00000001 +#define LDI 0b10000010 +#define MUL 0b10100010 +#define PUSH 0b01000101 +#define POP 0b01000110 +#define CALL 0b01010000 +#define RET 0b00010001 +#define ADD 0b10100000 +#define CMP 0b10100111 +#define JMP 0b01010100 +#define JEQ 0b01010101 +#define JNE 0b01010110 + +// enum non_alu_op { +// NOP = 0b0000, +// CALL= 0b0000, +// HLT = 0b0001, +// RET = 0b0001, +// LDI = 0b0010, +// INT = 0b0010, +// IRET= 0b0011, +// LD = 0b0011, +// JMP = 0b0100, +// ST = 0b0100 +// JEQ = 0b0101, +// PUSH= 0b0101, +// JNE = 0b0110, +// POP = 0b0110, +// JGT = 0b0111, +// PRN = 0b0111, +// JLT = 0b1000, +// PRA = 0b1000, +// JLE = 0b1001, +// JGE = 0b1010, +// }; + // 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); - -#endif +#endif \ No newline at end of file diff --git a/ls8/examples/sctest.ls8 b/ls8/examples/sctest.ls8 new file mode 100644 index 00000000..137daeba --- /dev/null +++ b/ls8/examples/sctest.ls8 @@ -0,0 +1,85 @@ +# +# Expected output: +# 1 +# 4 +# 5 + +10000010 # LDI R0,10 +00000000 +00001010 +10000010 # LDI R1,20 +00000001 +00010100 +10000010 # LDI R2,TEST1 +00000010 +00010011 +10100111 # CMP R0,R1 +00000000 +00000001 +01010101 # JEQ R2 +00000010 +10000010 # LDI R3,1 +00000011 +00000001 +01000111 # PRN R3 +00000011 +# TEST1 (address 19): +10000010 # LDI R2,TEST2 +00000010 +00100000 +10100111 # CMP R0,R1 +00000000 +00000001 +01010110 # JNE R2 +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 \ No newline at end of file diff --git a/ls8/ls8.c b/ls8/ls8.c index d24578ee..bdf64c8b 100644 --- a/ls8/ls8.c +++ b/ls8/ls8.c @@ -1,15 +1,22 @@ #include #include "cpu.h" +#include /** * Main */ -int main(void) +int main(int argc, char **argv) { struct cpu cpu; + if (argc != 2) + { + fprintf(stderr, "usage: ls8 \n"); + return 1; + } + cpu_init(&cpu); - cpu_load(&cpu); + cpu_load(&cpu, argv[1]); cpu_run(&cpu); return 0;