diff --git a/ls8/cpu.c b/ls8/cpu.c index bdb1f506..bf601268 100644 --- a/ls8/cpu.c +++ b/ls8/cpu.c @@ -1,45 +1,99 @@ +#include "stdio.h" +#include "stdlib.h" +#include "string.h" #include "cpu.h" #define DATA_LEN 6 +unsigned char cpu_ram_read(struct cpu *cpu, unsigned char address){ + return cpu->ram[address]; +}; + +void cpu_ram_write(struct cpu *cpu, unsigned char address, unsigned char val){ + cpu->ram[address] = val; +}; + /** * Load the binary bytes from a .ls8 source file into a RAM array */ -void cpu_load(struct cpu *cpu) -{ - char data[DATA_LEN] = { - // From print8.ls8 - 0b10000010, // LDI R0,8 - 0b00000000, - 0b00001000, - 0b01000111, // PRN R0 - 0b00000000, - 0b00000001 // HLT - }; - int address = 0; +void cpu_load(struct cpu *cpu, char *path) +{ + FILE *filepointer = fopen(path, "r"); - for (int i = 0; i < DATA_LEN; i++) { - cpu->ram[address++] = data[i]; + if (filepointer == NULL) + { + printf("Couldn't open the program you entered: %s\n", path); + exit(2); } - // TODO: Replace this with something less hard-coded + int address = 0; + char line[1024]; + while (fgets(line, sizeof(line), filepointer) != NULL) + { + char *endpoint; + unsigned char val = strtoul(line, &endpoint, 2) & 0xFF; + if (endpoint == line) + { + continue; + } + cpu_ram_write(cpu, address++, val); + } + fclose(filepointer); } - /** * ALU */ void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB) { - switch (op) { - case ALU_MUL: - // TODO - break; + unsigned char *reg = cpu->regs; + unsigned char valA = reg[regA]; + unsigned char valB = reg[regB]; + switch (op) + { + case ALU_MUL: + // TODO + reg[regA] *= valB; + break; + case ALU_ADD: + reg[regA] += valB; + break; // TODO: implement more ALU ops } } +void push(struct cpu *cpu, unsigned char value) +{ + cpu->regs[7]--; + cpu_ram_write(cpu, cpu->regs[7], cpu->regs[value]); + cpu->PC += 2; +} + +void pop(struct cpu *cpu, unsigned char value) +{ + cpu->regs[7]++; + cpu->regs[value] = cpu_ram_read(cpu, cpu->regs[7]); + cpu->PC += 2; +} + + +void call(struct cpu *cpu, unsigned char registerOfAddress) +{ + char address = cpu->regs[registerOfAddress]; + char nextInstruction = cpu->PC + 2; + + cpu->regs[7]--; + cpu_ram_write(cpu, cpu->regs[7], nextInstruction); + cpu->PC = address; +} + +void ret(struct cpu *cpu) +{ + unsigned char address = cpu_ram_read(cpu, cpu->regs[7]); + cpu->regs[7]++; + cpu->PC = address; +} /** * Run the CPU */ @@ -50,11 +104,52 @@ void cpu_run(struct cpu *cpu) while (running) { // TODO // 1. Get the value of the current instruction (in address PC). + unsigned char instruction = cpu_ram_read(cpu, cpu->PC); // 2. Figure out how many operands this next instruction requires + // Not needed if we use a switch statement // 3. Get the appropriate value(s) of the operands following this instruction + unsigned char op0 = cpu_ram_read(cpu, cpu->PC + 1); + unsigned char op1 = cpu_ram_read(cpu, cpu->PC + 2); + printf("trace: %02X: %02X %02X %02X\n", cpu->PC, instruction, op0, op1); // 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. + switch (instruction){ + case LDI: + cpu->regs[op0] = op1; + cpu->PC += 3; + break; + case PRN: + printf("%d\n", cpu->regs[op0]); + cpu->PC += 2; + break; + case HLT: //HLT the cpu and exit the emulator + running = 0; + break; + case MUL: + alu(cpu, ALU_MUL, op0, op1); + cpu->PC += 3; + break; + case ADD: + alu(cpu, ALU_ADD, op0, op1); + cpu->PC += 3; + break; + case PUSH: + push(cpu, op0); + break; + case POP: + pop(cpu, op1); + break; + case CALL: + call(cpu, op0); + break; + case RET: + ret(cpu); + break; + default: + printf("unexpected instruction 0x%02X at 0x%02X\n", instruction, cpu->PC); + exit(1); + } } } @@ -64,4 +159,12 @@ void cpu_run(struct cpu *cpu) void cpu_init(struct cpu *cpu) { // TODO: Initialize the PC and other special registers -} + for(int i = 0; i < 7; i++){ + cpu->regs[i] = 0; + } + cpu->regs[7] = 0xF4; + + cpu->PC = 0; + cpu->FL = 0; + memset(cpu->ram, 0, sizeof cpu->ram); +} \ No newline at end of file diff --git a/ls8/cpu.h b/ls8/cpu.h index 46e49c44..a8157882 100644 --- a/ls8/cpu.h +++ b/ls8/cpu.h @@ -5,29 +5,43 @@ struct cpu { // TODO // PC + unsigned char PC; // registers (array) + unsigned char regs[8]; // ram (array) + unsigned char ram[256]; + //flag + unsigned char FL; }; // ALU operations enum alu_op { - ALU_MUL + ALU_MUL, + ALU_ADD // Add more here }; // Instructions - +#define ADDR_PROGRAM_ENTRY 0x00 +#define ADDR_EMPTY_STACK 0xF4 // 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 MUL 0b10100010 +#define ADD 0b10100000 +#define PUSH 0b01000101 +#define POP 0b01000110 +#define CALL 0b01010000 +#define RET 0b00010001 + // 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 *path); extern void cpu_init(struct cpu *cpu); extern void cpu_run(struct cpu *cpu); diff --git a/ls8/ls8.c b/ls8/ls8.c index d24578ee..b532d85d 100644 --- a/ls8/ls8.c +++ b/ls8/ls8.c @@ -1,16 +1,27 @@ #include +#include #include "cpu.h" /** * Main */ -int main(void) +int main(int argc, char **argv) { + // if(argc != 2){ + // fprintf(stderr, "incorrect number of arguments"); + // exit(1); + // } + if(argc != 2){ + fprintf(stderr, "incorrect number of arguments"); + return 1; + } + struct cpu cpu; cpu_init(&cpu); - cpu_load(&cpu); + cpu_load(&cpu, argv[1]); cpu_run(&cpu); return 0; -} \ No newline at end of file +} +