diff --git a/README.md b/README.md index e81f54cf..ec4fa468 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Computer Architecture +# Computer Architecture - ## Project @@ -7,22 +7,24 @@ ## Task List: add this to the first comment of your Pull Request ### Day 1: Get `print8.ls8` running -- [ ] Inventory what is here -- [ ] Implement `struct cpu` in `cpu.h` -- [ ] Add RAM functions `cpu_ram_read` and `cpu_ram_write` -- [ ] Implement `cpu_init()` -- [ ] Implement the core of `cpu_run()` -- [ ] Implement the `HLT` instruction handler -- [ ] Add the `LDI` instruction -- [ ] Add the `PRN` instruction +- [x] Inventory what is here +- [x] Implement `struct cpu` in `cpu.h` +- [x] Add RAM functions `cpu_ram_read` and `cpu_ram_write` +- [x] Implement `cpu_init()` +- [x] Implement the core of `cpu_run()` +- [x] Implement the `HLT` instruction handler +- [x] Add the `LDI` instruction +- [x] Add the `PRN` instruction ### Day 2: Add the ability to load files dynamically, get `mult.ls8` and `stack.ls8` running -- [ ] Un-hardcode the machine code -- [ ] Implement the `cpu_load` function to load an `.ls8` file given the filename passed in as an argument -- [ ] Implement a Multiply instruction and Print the result (run `mult8.ls8`) +- [x] Un-hardcode the machine code +- [x] Implement the `cpu_load` function to load an `.ls8` file given the filename passed in as an argument +- [x] Implement a Multiply instruction and Print the result (run `mult8.ls8`) + +### Day 3: - [ ] Implement the System Stack and be able to run the `stack.ls8` program -### Day 3: Get `call.ls8` running +### Day 4: Get `call.ls8` running - [ ] Implement the CALL and RET instructions - [ ] Implement Subroutine Calls and be able to run the `call.ls8` program diff --git a/ls8/README.1.md b/ls8/README.1.md new file mode 100644 index 00000000..c0e12237 --- /dev/null +++ b/ls8/README.1.md @@ -0,0 +1,174 @@ +# Sprint Challenge: Computer Architecture - Conditional Jumps + +This challenge allows you to practice the concepts and techniques learned over +the past week and apply them in a concrete project. This Sprint explored TOPIC. +During this Sprint, you studied CPU components, number bases, bitwise +operations, CPU stack, interrupts, and subroutines. + +In your challenge this week, you will demonstrate proficiency by adding _conditional jumps_ to your LS-8 emulator as well as answering a few questions during a one-on-one session with a PM. + +## Instructions + +**Read these instructions carefully. Understand exactly what is expected +_before_ starting this Sprint Challenge.** + +This is an individual assessment. All work must be your own. Your challenge +score is a measure of your ability to work independently using the material +covered through this sprint. You need to demonstrate proficiency in the concepts +and objectives introduced and practiced in preceding days. + +You are not allowed to collaborate during the Sprint Challenge. However, you are +encouraged to follow the twenty-minute rule and seek support from your PM and +Instructor in your cohort help channel on Slack. Your work reflects your +proficiency with Computer Architecture and your command of the related concepts +and techniques. + +You have three hours to complete this challenge. Plan your time accordingly. + +## Commits + +Commit your code regularly and meaningfully. This helps both you (in case you +ever need to return to old code for any number of reasons and your project manager. + +## Description + +In this code challenge, you will add _conditional jumps_ (AKA _conditional branching_) to your LS-8 emulator. + +## Project Set Up + +Options for submission, whichever is easier for you: + +* Copy your source into this repo, or... + +* Submit a PR for the Sprint Challenge from the `Computer-Architecture` repo + you've been using all along (in addition to the PR for `ANSWERS.md`) + +## Minimum Viable Product + +Your finished project must include all of the following requirements: + +- [ ] Add the `CMP` instruction and `equal` flag to your LS-8. + +- [ ] Add the `JMP` instruction. + +- [ ] Add the `JEQ` and `JNE` instructions. + +[See the LS-8 spec for details](https://github.com/LambdaSchool/Computer-Architecture/blob/master/LS8-spec.md) + +In your solution, it is essential that you follow best practices and produce +clean and professional results. Schedule time to review, refine, and assess your +work and perform basic professional polishing including spell-checking and +grammar-checking on your work. It is better to submit a challenge that meets MVP +than one that attempts too much and does not. + +Validate your work through testing and ensure that your code operates as designed. + +[Here is some code](sctest.ls8) that exercises the above instructions. It should +print: + +``` +1 +4 +5 +``` + +``` +# 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 +``` + +## Stretch Problems + +After finishing your required elements, you can push your work further. These +goals may or may not be things you have learned in this module but they build on +the material you just studied. Time allowing, stretch your limits and see if you +can deliver on the following optional goals: + +- [ ] Add the ALU operations: `AND` `OR` `XOR` `NOT` `SHL` `SHR` `MOD` +- [ ] Add an `ADDI` extension instruction to add an immediate value to a register +- [ ] Add timer interrupts +- [ ] Add keyboard interrupts diff --git a/ls8/cpu.c b/ls8/cpu.c index bdb1f506..76398eb7 100644 --- a/ls8/cpu.c +++ b/ls8/cpu.c @@ -1,29 +1,52 @@ #include "cpu.h" +#include "string.h" +#include "stdio.h" +#include -#define DATA_LEN 6 +#define DATA_LEN 12 /** * 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 *program) { - 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]; + + FILE *fp; + + char line[1024]; + + fp = fopen(program, "r"); + + if( fp == NULL) { + fprintf(stderr, "Error opening file.\n"); + exit(1); } - // TODO: Replace this with something less hard-coded + int i = 0x00; + while( fgets(line, sizeof(line), fp) != NULL ) { + char *endPtr; + unsigned char byte = strtol(line, &endPtr, 2); + + if(endPtr == line) { + continue; + } + + cpu_ram_write(cpu, i++, byte); + + } + + fclose(fp); + +} + +unsigned char cpu_ram_read(struct cpu *cpu, unsigned char address) +{ + return cpu->ram[address]; +} + +void cpu_ram_write(struct cpu *cpu, int address, unsigned char value) +{ + cpu->ram[address] = value; } /** @@ -47,14 +70,119 @@ void cpu_run(struct cpu *cpu) { int running = 1; // True until we get a HLT instruction + //int stack[256]; + //int sp = cpu->registers[7]; + + //int PC = cpu->PC; + //unsigned char value; + 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 // 3. Get the appropriate value(s) of the operands following this instruction + 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(instruction) { + + case HLT: + running = 0; + break; + + case LDI: + cpu->registers[operandA] = operandB; + cpu->PC += 3; + break; + + case PRN: + printf("%d\n", cpu->registers[operandA]); + cpu->PC += 2; + break; + + case MUL: + cpu->registers[operandA] = (cpu->registers[operandA] * cpu->registers[operandB]); + //test that MUL completed successfully + printf("%d\n", cpu->registers[operandA]); + cpu->PC += 2; + break; +/* + case PUSH: + sp--; + //cpu->registers[sp] = cpu->registers[operandA]; + cpu_ram_write(cpu, sp, cpu->registers[operandA]); + cpu->PC += 2; + break; + + case POP: + cpu->registers[operandA] = cpu_ram_read(cpu, sp); + sp++; + cpu->PC += 2; + break; + + case CALL: + sp--; + cpu->registers[sp] = cpu->registers[operandA]; + cpu_ram_write(cpu, cpu->PC, operandA); + cpu->PC = cpu->registers[sp]; + break; + + case RET: + cpu->registers[operandA] = cpu_ram_read(cpu, sp); + cpu->PC = cpu->registers[operandA]; + sp++; + break; + */ + + case JMP: + //printf("jumping to instruction\n"); + cpu->PC = cpu->registers[operandA]; + break; + + case CMP: + if(cpu->registers[operandA] == cpu->registers[operandB]) { + //printf("setting FL to 1\n"); + cpu->FL = 1; + cpu->PC += 3; + break; + + } else { + //printf("setting FL to 0\n"); + cpu->FL = 0; + cpu->PC += 3; + break; + } + //break; + + case JEQ: + if(cpu->FL == 1) { + cpu->PC = cpu->registers[operandA]; + break; + } else { + cpu->PC += 2; + break; + } + //break; + + case JNE: + if(cpu->FL == 0) { + cpu->PC = cpu->registers[operandA]; + break; + } else { + cpu->PC += 2; + break; + } + //break; + + default: + fprintf(stderr, "PC %02x: unknown instruction %02x\n", cpu->PC, instruction); + exit(3); + + } } } @@ -63,5 +191,13 @@ void cpu_run(struct cpu *cpu) */ void cpu_init(struct cpu *cpu) { - // TODO: Initialize the PC and other special registers + cpu->PC = 0; + cpu->FL = 0; + + memset(cpu->registers, 0, 8*sizeof(unsigned char)); + + memset(cpu->ram, 0, 8*sizeof(unsigned char)); + + //cpu->registers[7] = 0xF4; + } diff --git a/ls8/cpu.h b/ls8/cpu.h index 46e49c44..5365b92d 100644 --- a/ls8/cpu.h +++ b/ls8/cpu.h @@ -5,8 +5,12 @@ struct cpu { // TODO // PC + int PC; + int FL; // registers (array) + unsigned char registers[8]; // ram (array) + unsigned char ram[8]; }; // ALU operations @@ -23,11 +27,22 @@ enum alu_op { #define LDI 0b10000010 #define HLT 0b00000001 #define PRN 0b01000111 +#define MUL 0b10100010 +#define PUSH 0b01000101 +#define POP 0b01000110 +#define CALL 0b01010000 +#define RET 0b00010001 + +#define CMP 0b10100111 +#define JMP 0b01010100 +#define JEQ 0b01010101 +#define JNE 0b01010110 + // 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 *program); extern void cpu_init(struct cpu *cpu); extern void cpu_run(struct cpu *cpu); 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..32c98a19 100644 --- a/ls8/ls8.c +++ b/ls8/ls8.c @@ -1,16 +1,28 @@ #include #include "cpu.h" +#include /** * Main */ -int main(void) +int main(int argc, char **argv) { struct cpu cpu; + char *program; - cpu_init(&cpu); - cpu_load(&cpu); - cpu_run(&cpu); + if (argc < 2) { + fprintf(stderr, "Please specify which program the LS8 should run.\nUsage: ls8 program\n"); + exit(1); + + } else { + program = argv[1]; + + printf("%s\n", program); + + cpu_init(&cpu); + cpu_load(&cpu, program); + cpu_run(&cpu); + } return 0; } \ No newline at end of file