diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..98cb24c4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "workbench.colorCustomizations": { + "activityBar.background": "#033615", + "titleBar.activeBackground": "#044B1D", + "titleBar.activeForeground": "#F0FEF5" + }, + "files.associations": { + "*.js": "javascriptreact", + "cpu.h": "c", + "stdlib.h": "c" + } +} \ No newline at end of file diff --git a/ls8/README.md b/ls8/README.md index bb8d4fb4..016c14d1 100644 --- a/ls8/README.md +++ b/ls8/README.md @@ -66,6 +66,27 @@ but you'll have to implement those three above instructions first! * Read this whole file. * Skim the spec. +## File Structure + +* Examples - holds multiple files that gives us CPU word, instructions, register and operand examples + +### Cpu.c + +1. cpu_load - will load the binary bytes that are in .lsa source into an array + +2. alu - will handle the numeric values + +3. cpu_run - will handle processing all the instructions we feed it + +4. cpu_init - creates a cpu struct and starts the PC and all its registers + +### Cpu.h + +1. struct cpu - holds all info about CPU the registers and ram + +2. enum alu_op - all the ALU numeric operations + + ## Step 1: Implement `struct cpu` in `cpu.h` This structure holds information about the CPU and associated components. diff --git a/ls8/cpu.c b/ls8/cpu.c index bdb1f506..5d3ed380 100644 --- a/ls8/cpu.c +++ b/ls8/cpu.c @@ -1,29 +1,52 @@ +#include +#include +#include #include "cpu.h" #define DATA_LEN 6 +/** + * Reads from a CPU struct's RAM array + */ +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 value) +{ + cpu->ram[address] = value; +} + /** * 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 *path) { - char data[DATA_LEN] = { - // From print8.ls8 - 0b10000010, // LDI R0,8 - 0b00000000, - 0b00001000, - 0b01000111, // PRN R0 - 0b00000000, - 0b00000001 // HLT - }; + FILE *fileptr = fopen(path, "r"); + + if (fileptr == NULL) + { + printf("Couldn't open program: %s\n", path); + exit(2); + } int address = 0; + char line[8000]; - for (int i = 0; i < DATA_LEN; i++) { - cpu->ram[address++] = data[i]; + while (fgets(line, sizeof(line), fileptr) != NULL) + { + char *endptr; + // & 0xFF gives you the last 8 bits of the ul (so it will always fit in an unsigned char) + unsigned char value = strtoul(line, &endptr, 2) & 0xFF; + if (endptr == line) + { + continue; + } + cpu_ram_write(cpu, address++, value); } - // TODO: Replace this with something less hard-coded + fclose(fileptr); } /** @@ -31,12 +54,17 @@ 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; + unsigned int a = cpu->registers[regA]; + unsigned int b = cpu->registers[regB]; - // TODO: implement more ALU ops + switch (op) + { + case ALU_MUL: + cpu->registers[regA] = a * b; + break; + case ALU_ADD: + cpu->registers[regA] = a + b; + break; } } @@ -47,14 +75,46 @@ void cpu_run(struct cpu *cpu) { int running = 1; // True until we get a HLT instruction - while (running) { - // TODO + while (running) + { // 1. Get the value of the current instruction (in address PC). + unsigned char ir = cpu_ram_read(cpu, cpu->pc); // 2. Figure out how many operands this next instruction requires + // Not entirely needed if we use a switch statement // 3. Get the appropriate value(s) of the operands following this instruction + // Operations may need up to 2 bytes after PC + unsigned char operandA = cpu_ram_read(cpu, cpu->pc + 1); + unsigned char operandB = cpu_ram_read(cpu, cpu->pc + 2); // 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 (ir) + { + case LDI: + // Set the value of a register to an integer + cpu->registers[operandA] = operandB; + cpu->pc += 3; + break; + case PRN: + // Print numeric value stored in the given register + printf("%d\n", cpu->registers[operandA]); + cpu->pc += 2; + 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 HLT: + // Halt the CPU (and exit the emulator) + running = 0; + break; + default: + break; + } } } @@ -63,5 +123,8 @@ 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->ram, 0, sizeof(cpu->ram)); + memset(cpu->registers, 0, sizeof(cpu->registers)); + cpu->registers[7] = 0xF4; +} \ No newline at end of file diff --git a/ls8/cpu.h b/ls8/cpu.h index 46e49c44..82ee1237 100644 --- a/ls8/cpu.h +++ b/ls8/cpu.h @@ -2,17 +2,21 @@ #define _CPU_H_ // Holds all information about the CPU -struct cpu { - // TODO +struct cpu +{ // PC + unsigned char pc; // registers (array) + unsigned char registers[8]; // ram (array) + unsigned char ram[256]; }; // ALU operations -enum alu_op { - ALU_MUL - // Add more here +enum alu_op +{ + ALU_MUL, + ALU_ADD }; // Instructions @@ -20,15 +24,16 @@ 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 -// TODO: more instructions here. These can be used in cpu_run(). +#define LDI 0b10000010 +#define HLT 0b00000001 +#define PRN 0b01000111 +#define MUL 0b10100010 +#define ADD 0b10100000 // 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); -#endif +#endif \ No newline at end of file diff --git a/ls8/ls8.c b/ls8/ls8.c index d24578ee..3a1778d2 100644 --- a/ls8/ls8.c +++ b/ls8/ls8.c @@ -4,12 +4,18 @@ /** * Main */ -int main(void) +int main(int argc, char **argv) { + if (argc != 2) + { + fprintf(stderr, "usage: must include a single path to a desired program\n"); + return 1; + } + struct cpu cpu; cpu_init(&cpu); - cpu_load(&cpu); + cpu_load(&cpu, argv[1]); cpu_run(&cpu); return 0;