Skip to content
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

## Project

* [Implement the LS-8 Emulator](ls8/)
- [Implement the LS-8 Emulator](ls8/)

## 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`
Expand All @@ -17,15 +18,18 @@
- [ ] 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`)
- [ ] Implement the System Stack and be able to run the `stack.ls8` program

### Day 3: Get `call.ls8` running

- [ ] Implement the CALL and RET instructions
- [ ] Implement Subroutine Calls and be able to run the `call.ls8` program

### Stretch

- [ ] Add the timer interrupt to the LS-8 emulator
- [ ] Add the keyboard interrupt to the LS-8 emulator
118 changes: 99 additions & 19 deletions ls8/cpu.c
Original file line number Diff line number Diff line change
@@ -1,40 +1,88 @@
#include "cpu.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define DATA_LEN 6

/**
* 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 *src;
int lines = 0;
src = fopen(path, "r");

if (src == NULL)
{
printf("File %s could not be opened.\n", path);
exit(1);
}

for (char c = getc(src); c != EOF; c = getc(src))
{
if (c == '\n')
{
lines += 1;
}
}

fseek(src, 0L, SEEK_SET);

char data[lines + 1];
char line[255];
char *cut;
int count = 0;

while (fgets(line, sizeof(line), src) != NULL)
{
if (line[0] == '0' || line[0] == '1')
{
data[count] = strtol(line, &cut, 2);
count += 1;
}
else
{
continue;
}
}

fclose(src);

int address = 0;

for (int i = 0; i < DATA_LEN; i++) {
for (int i = 0; i < count + 1; i++)
{
cpu->ram[address++] = data[i];
}

// TODO: Replace this with something less hard-coded
}

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;
}

/**
* ALU
*/
void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB)
{
switch (op) {
case ALU_MUL:
// TODO
break;
(void)cpu;
(void)regA;
(void)regB;

switch (op)
{
case ALU_MUL:
// TODO
break;

// TODO: implement more ALU ops
}
Expand All @@ -47,14 +95,44 @@ void cpu_run(struct cpu *cpu)
{
int running = 1; // True until we get a HLT instruction

while (running) {
while (running)
{
// TODO
// 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
unsigned char ops = (IR & 0xC0) >> 6;

// 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);

// 4. switch() over it to decide on a course of action.
// 5. Do whatever the instruction should do according to the spec.
switch (IR)
{

// 5. Do whatever the instruction should do according to the spec.
case LDI:
cpu->registers[op0] = op1;
break;

case PRN:
printf("%d\n", cpu->registers[op0]);
break;

case HLT:
running = 0;
break;

default:
printf("Unkown Instruction: 0x%02x\n", IR);
running = 0;
break;
}

// 6. Move the PC to the next instruction.
cpu->PC += (ops + 1);
}
}

Expand All @@ -63,5 +141,7 @@ 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);
}
23 changes: 15 additions & 8 deletions ls8/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,39 @@
#define _CPU_H_

// Holds all information about the CPU
struct cpu {
struct cpu
{
// TODO
// PC
unsigned char PC;
unsigned char FL;
// 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
// Add more here
};

// Instructions

// 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 LDI 0b10000010
#define HLT 0b00000001
#define PRN 0b01000111
// 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);

Expand Down
8 changes: 6 additions & 2 deletions ls8/ls8.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
/**
* Main
*/
int main(void)
int main(int argc, char *argv[])
{

(void)argc;
(void)argv;

struct cpu cpu;

cpu_init(&cpu);
cpu_load(&cpu);
cpu_load(&cpu, argv[1]);
cpu_run(&cpu);

return 0;
Expand Down