diff --git a/ls8/cpu.c b/ls8/cpu.c index bdb1f506..160a9864 100644 --- a/ls8/cpu.c +++ b/ls8/cpu.c @@ -1,67 +1,213 @@ #include "cpu.h" +#include +#include +#include -#define DATA_LEN 6 +unsigned char cpu_ram_read(struct cpu *cpu, unsigned char address) +{ + return cpu->ram[address]; +} -/** - * Load the binary bytes from a .ls8 source file into a RAM array - */ -void cpu_load(struct cpu *cpu) +void cpu_ram_write(struct cpu *cpu, unsigned char address, unsigned char value) +{ + cpu->ram[address] = value; +} +void cpu_load(struct cpu *cpu, char *file) { - char data[DATA_LEN] = { - // From print8.ls8 - 0b10000010, // LDI R0,8 - 0b00000000, - 0b00001000, - 0b01000111, // PRN R0 - 0b00000000, - 0b00000001 // HLT - }; + FILE *fp; + char line[1024]; + + if ((fp = fopen(file, "r")) == NULL) + { + fprintf(stderr, "File not found.\n"); + exit(1); + } int address = 0; - for (int i = 0; i < DATA_LEN; i++) { - cpu->ram[address++] = data[i]; - } + fp = fopen(file, "r"); + while(fgets(line, sizeof(line), fp) != NULL){ + char *endptr; + unsigned char value; + value = strtoul(line, &endptr, 2); + + if(endptr == line){ + continue; + } - // TODO: Replace this with something less hard-coded + cpu->ram[address++] = value; + } } -/** - * ALU - */ void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB) { switch (op) { + case ALU_ADD: + cpu->registers[regA] += cpu->registers[regB]; + break; + case ALU_AND: + cpu->registers[regA] = cpu->registers[regA] & cpu->registers[regB]; + break; + case ALU_CMP: + if (cpu->registers[regA] == cpu->registers[regB]) + { + cpu->E = 1; + break; + } + else if (cpu->registers[regA] > cpu->registers[regB]) + { + cpu->G = 1; + break; + } + else + { + cpu->L = 1; + break; + } + case ALU_MOD: + cpu->registers[regA] %= cpu->registers[regB]; + break; case ALU_MUL: - // TODO + cpu->registers[regA] *= cpu->registers[regB]; + break; + case ALU_NOT: + cpu->registers[regA] = ~cpu->registers[regA]; + break; + case ALU_OR: + cpu->registers[regA] = cpu->registers[regA] | cpu->registers[regB]; + break; + case ALU_SHL: + cpu->registers[regA] = cpu->registers[regA] << cpu->registers[regB]; + break; + case ALU_SHR: + cpu->registers[regA] = cpu->registers[regA] >> cpu->registers[regB]; + break; + case ALU_XOR: + cpu->registers[regA] = cpu->registers[regA] ^ cpu->registers[regB]; break; - - // TODO: implement more ALU ops } } -/** - * Run the CPU - */ void cpu_run(struct cpu *cpu) { - int running = 1; // True until we get a HLT instruction - + int running = 1; + 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. + unsigned char ir = cpu_ram_read(cpu, cpu->PC); + unsigned char operandA; + unsigned char operandB; + int next_line = 1; + + if (ir & 0x80) + { + operandA = cpu->ram[(cpu->PC + 1) & 0xff]; + operandB = cpu->ram[(cpu->PC + 2) & 0xff]; + next_line = 3; + } + else if (ir & 0x40) + { + operandA = cpu->ram[(cpu->PC + 1) & 0xff]; + next_line = 2; + } + + switch (ir) + { + case ADD: + alu(cpu, ALU_ADD, operandA, operandB); + break; + case AND: + alu(cpu, ALU_AND, operandA, operandB); + break; + case CALL: + cpu->ram[--cpu->registers[7]] = cpu->PC + next_line; + cpu->PC = cpu->registers[operandA]; + continue; + case RET: + cpu->PC = cpu->ram[cpu->registers[7]++]; + continue; + case CMP: + alu(cpu, ALU_CMP, operandA, operandB); + break; + case HLT: + running = 0; + break; + case JEQ: + if (cpu->E == 1) + { + cpu->PC = cpu->registers[operandA]; + continue; + } + break; + case JMP: + cpu->PC = cpu->registers[operandA]; + continue; + case JNE: + if (cpu->E != 1) + { + cpu->PC = cpu->registers[operandA]; + continue; + } + break; + case LDI: + cpu->registers[operandA] = operandB; + break; + case MOD: + if (cpu->registers[operandB] == 0) + { + printf("Dividing by zero is not allowed.\n"); + running = 0; + } + else + { + alu(cpu, ALU_MOD, operandA, operandB); + } + break; + case MUL: + alu(cpu, ALU_MUL, operandA, operandB); + break; + case NOT: + alu(cpu, ALU_AND, operandA, operandB); + break; + case OR: + alu(cpu, ALU_AND, operandA, operandB); + break; + case POP: + cpu->registers[operandA] = cpu_ram_read(cpu, cpu->registers[7]); + cpu->ram[cpu->registers[7]++] = 0x00; + break; + case PRN: + printf("%d\n", cpu->registers[operandA]); + break; + case PUSH: + cpu_ram_write(cpu, --cpu->registers[7], cpu->registers[operandA]); + break; + case SHL: + alu(cpu, ALU_SHL, operandA, operandB); + break; + case SHR: + alu(cpu, ALU_SHR, operandA, operandB); + break; + case ST: + cpu_ram_write(cpu, operandA, cpu->registers[operandB]); + break; + case XOR: + alu(cpu, ALU_AND, operandA, operandB); + break; + default: + break; + } + cpu->PC = cpu->PC + next_line; } } -/** - * Initialize a CPU struct - */ void cpu_init(struct cpu *cpu) { - // TODO: Initialize the PC and other special registers -} + cpu->PC = 0; + cpu->FL = 0; + cpu->E = 0; + cpu->L = 0; + cpu->G = 0; + memset(cpu->ram, 0, 8 * sizeof(unsigned char)); + memset(cpu->registers, 0, 256 * sizeof(unsigned char)); + cpu->registers[7] = 0xF4; +} \ No newline at end of file diff --git a/ls8/cpu.h b/ls8/cpu.h index 46e49c44..770f64ef 100644 --- a/ls8/cpu.h +++ b/ls8/cpu.h @@ -1,34 +1,53 @@ #ifndef _CPU_H_ #define _CPU_H_ -// Holds all information about the CPU struct cpu { - // TODO - // PC - // registers (array) - // ram (array) + unsigned char PC; + unsigned char FL; + unsigned char E; + unsigned char L; + unsigned char G; + unsigned char registers[8]; + unsigned char ram[256]; }; -// ALU operations enum alu_op { - ALU_MUL - // Add more here + ALU_ADD, + ALU_AND, + ALU_CMP, + ALU_MOD, + ALU_MUL, + ALU_NOT, + ALU_OR, + ALU_SHL, + ALU_SHR, + ALU_XOR }; -// Instructions - -// These use binary literals. If these aren't available with your compiler, hex -// literals should be used. - -#define LDI 0b10000010 +#define ADD 0b10100000 +#define AND 0b10101000 +#define CALL 0b01010000 +#define CMP 0b10100111 #define HLT 0b00000001 +#define JEQ 0b01010101 +#define JMP 0b01010100 +#define JNE 0b01010110 +#define LDI 0b10000010 +#define MOD 0b10100100 +#define MUL 0b10100010 +#define NOT 0b01101001 +#define OR 0b10101010 +#define POP 0b01000110 #define PRN 0b01000111 -// TODO: more instructions here. These can be used in cpu_run(). - -// Function declarations - -extern void cpu_load(struct cpu *cpu); +#define PUSH 0b01000101 +#define RET 0b00010001 +#define SHL 0b10101100 +#define SHR 0b10101101 +#define ST 0b10000100 +#define XOR 0b10101011 + +extern void cpu_load(struct cpu *cpu, char *file); 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..8f86bba7 --- /dev/null +++ b/ls8/examples/sctest.ls8 @@ -0,0 +1,86 @@ +# Code to test the Sprint Challenge +# +# 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 diff --git a/ls8/ls8.c b/ls8/ls8.c index d24578ee..ab964617 100644 --- a/ls8/ls8.c +++ b/ls8/ls8.c @@ -1,15 +1,21 @@ #include +#include #include "cpu.h" /** * Main */ -int main(void) +int main(int argc, char *argv[]) { + if (argc < 2) + { + fprintf(stderr, "A file must be given.\n"); + exit(1); + } struct cpu cpu; cpu_init(&cpu); - cpu_load(&cpu); + cpu_load(&cpu, argv[1]); cpu_run(&cpu); return 0;