diff --git a/ls8/README.md b/ls8/README.md index 7bbd29d1..00915b68 100644 --- a/ls8/README.md +++ b/ls8/README.md @@ -61,6 +61,14 @@ but you'll have to implement those three above instructions first! ## Step 0: IMPORTANT: inventory what is here! * Make a list of files here. + + - cpu.c + - declares the functions of the cpu + - cpu.h + - creates the data structures of the cpu + - ls8.c + - runs the cpu + * Write a short 3-10-word description of what each file does. * Note what has been implemented, and what hasn't. * Read this whole file. diff --git a/ls8/cpu.c b/ls8/cpu.c index bdb1f506..904306de 100644 --- a/ls8/cpu.c +++ b/ls8/cpu.c @@ -1,29 +1,51 @@ #include "cpu.h" +#include +#include +#include +#include #define DATA_LEN 6 /** * Load the binary bytes from a .ls8 source file into a RAM array */ -void cpu_load(struct cpu *cpu) +void trace(struct cpu *cpu); + +double getcurtime(void) +{ + struct timeval tv; +​ + gettimeofday(&tv, NULL); +​ + return tv.tv_sec + tv.tv_usec / 1000000.0; +} + +void cpu_load(struct cpu *cpu, char *file_name) { - char data[DATA_LEN] = { - // From print8.ls8 - 0b10000010, // LDI R0,8 - 0b00000000, - 0b00001000, - 0b01000111, // PRN R0 - 0b00000000, - 0b00000001 // HLT - }; + + FILE *fp = fopen(file_name, "r"); + + if (fp == NULL) { + fprintf(stderr, "Error opening file: %s\n", file_name); + return; + } int address = 0; + char line[128]; + + while (fgets(line, sizeof line, fp) != NULL) { + char *endptr; + + unsigned char value = strtoul(line, &endptr, 2); - for (int i = 0; i < DATA_LEN; i++) { - cpu->ram[address++] = data[i]; + if (endptr == line) { + continue; + } + + cpu->ram[address++] = value; } - // TODO: Replace this with something less hard-coded + fclose(fp); } /** @@ -33,10 +55,14 @@ void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB { switch (op) { case ALU_MUL: - // TODO + cpu->registers[regA] *= cpu->registers[regB]; + break; + case ALU_ADD: + cpu->registers[regA] += cpu->registers[regB]; break; // TODO: implement more ALU ops + // ADD, AND, CMP, DEC, DIV, INC, MOD, MUL, NOT, OR, SHL, SHR, SUB, XOR } } @@ -47,14 +73,88 @@ void cpu_run(struct cpu *cpu) { int running = 1; // True until we get a HLT instruction + cpu->registers[7] = 0xF4; + unsigned char *sp; + + sp = &cpu->registers[7]; + + unsigned char operandA, operandB; + 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. + // trace(cpu); + + unsigned char ir = cpu->ram[cpu->pc]; + + operandA = cpu->ram[cpu->pc+1]; + operandB = cpu->ram[cpu->pc+2]; + + switch (ir) { + + case CALL: + + //The address of the instruction directly after CALL is pushed onto the stack. This allows us to return to where we left off when the subroutine finishes executing. + (*sp)--; + cpu->ram[*sp] = cpu->pc+2; + + //The PC is set to the address stored in the given register. We jump to that location in RAM and execute the first instruction in the subroutine. The PC can move forward or backwards from its current location. + cpu->pc = cpu->registers[operandA]; + + break; + + case HLT: + running = 0; + cpu->pc++; + break; + + case MUL: + alu(cpu, ALU_MUL, operandA, operandB); + cpu->pc += 3; + break; + + case ADD: + alu(cpu, ALU_ADD, operandA, operandB); + cpu->pc += 3; + break; + + case LDI: + // Set the value of a register to an integer. + cpu->registers[operandA] = operandB; + cpu->pc += 3; + break; + + case POP: + cpu->registers[operandA] = cpu->ram[*sp]; // Copy the value from the address pointed to by SP to the given register. + (*sp)++; // Increment SP. + cpu->pc += 2; + break; + + case PUSH: + (*sp)--; // Decrement the SP. + cpu->ram[*sp] = cpu->registers[operandA]; // Copy the value in the given register to the address pointed to by SP. + cpu->pc += 2; + break; + + case PRN: + printf("%d\n", cpu->registers[operandA]); + cpu->pc += 2; + break; + + case RET: + // Pop the value from the top of the stack and store it in the PC. + cpu->pc = cpu->ram[*sp]; // Copy the value from the address pointed to by SP to the given register. + (*sp)++; // Increment SP. + break; + + case ST: + cpu->ram[cpu->registers[operandA]] = cpu->registers[operandB]; + cpu->pc += 3; + // Store value in registerB in the address stored in registerA. + break; + + default: + printf("Error: found instruction %x at location %x\n", ir, cpu->pc); + exit(1); + } } } @@ -64,4 +164,50 @@ void cpu_run(struct cpu *cpu) void cpu_init(struct cpu *cpu) { // TODO: Initialize the PC and other special registers + cpu->pc = 0; + memset(cpu->registers, 0, 8); + memset(cpu->ram, 0, 256); +} + +char cpu_ram_read(struct cpu *cpu, int index) +{ + return cpu->ram[index]; +} + +void cpu_ram_write(struct cpu *cpu, unsigned char input) +{ + + int index = 0; + int running = 1; + + while (running) { + + if (cpu->ram[index] == 0b00000001) { + + if (index < 256) { + cpu->ram[index + 1] = 0b00000001; + cpu->ram[index] = input; + } else { + cpu->ram[index - 1] = input; + } + running = 0; + break; + } + } } + +void trace(struct cpu *cpu) +{ + printf("%02X | ", cpu->pc); + + printf("%02X %02X %02X |", + cpu_ram_read(cpu, cpu->pc), + cpu_ram_read(cpu, cpu->pc + 1), + cpu_ram_read(cpu, cpu->pc + 2)); + + for (int i = 0; i < 8; i++) { + printf(" %02X", cpu->registers[i]); + } + + printf("\n"); +} \ No newline at end of file diff --git a/ls8/cpu.h b/ls8/cpu.h index 9862abb1..8c717201 100644 --- a/ls8/cpu.h +++ b/ls8/cpu.h @@ -5,13 +5,16 @@ struct cpu { // TODO // PC + unsigned char pc; // registers (array) + unsigned char registers[8]; // ram (array) + unsigned char ram[256]; }; // ALU operations enum alu_op { - ALU_MUL + ALU_MUL, ALU_ADD // Add more here }; @@ -20,13 +23,22 @@ enum alu_op { // These use binary literals. If these aren't available with your compiler, hex // literals should be used. -#define LDI 0b10000010 -#define PRN 0b01000111 +#define LDI 0b10000010 +#define PRN 0b01000111 +#define HLT 0b00000001 +#define MUL 0b10100010 +#define POP 0b01000110 +#define PUSH 0b01000101 +#define CALL 0b01010000 +#define RET 0b00010001 +#define ADD 0b10100000 +#define ST 0b10000100 + // 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 *file_name); extern void cpu_init(struct cpu *cpu); extern void cpu_run(struct cpu *cpu); diff --git a/ls8/examples/stack.ls8 b/ls8/examples/stack.ls8 index e7c6e668..23511539 100644 --- a/ls8/examples/stack.ls8 +++ b/ls8/examples/stack.ls8 @@ -28,4 +28,4 @@ 00000010 01000111 # PRN R1 00000001 -00000001 # HLT +00000001 # HLT \ No newline at end of file diff --git a/ls8/ls8.c b/ls8/ls8.c index d24578ee..a71c6f14 100644 --- a/ls8/ls8.c +++ b/ls8/ls8.c @@ -4,12 +4,17 @@ /** * Main */ -int main(void) +int main(int argc, char *argv[]) { struct cpu cpu; + if (argc != 2) { + fprintf(stderr, "argc error.\n"); + return 1; + } + cpu_init(&cpu); - cpu_load(&cpu); + cpu_load(&cpu, argv[1]); cpu_run(&cpu); return 0;