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
120 changes: 100 additions & 20 deletions ls8/cpu.c
Original file line number Diff line number Diff line change
@@ -1,40 +1,87 @@
#include "cpu.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define DATA_LEN 6
// ================== RAM functions ===================

/** Read from the CPU's ram at the specifc location
*/
unsigned char cpu_ram_read(struct cpu *cpu, unsigned char MAR)
{
return cpu->ram[MAR];
}

/** Writes at index specified for the CPU's ram
*/
void cpu_ram_write(struct cpu *cpu, unsigned char MAR, unsigned char num)
{
cpu->ram[MAR] = num;
}

// ================== Instrcution Handlers ===================

// LDI function: Set the value of a register to an integer
void handle_LDI(struct cpu *cpu)
{
unsigned char regA = cpu_ram_read(cpu, (cpu->pc + 1));
unsigned char value = cpu_ram_read(cpu, (cpu->pc + 2));
cpu->registers[regA] = value;
};

// PRN function: Print numeric value stored in the given register
void handle_PRN(struct cpu *cpu)
{
unsigned char regA = cpu_ram_read(cpu, (cpu->pc + 1));
printf("%d\n", cpu->registers[regA]);
}

// HLT function: Halt the CPU (and exit the emulator)
void handle_HLT()
{
exit(0);
}

// ================== CPU functions ===================

/**
* 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 *argv)
{
char data[DATA_LEN] = {
// From print8.ls8
0b10000010, // LDI R0,8
0b00000000,
0b00001000,
0b01000111, // PRN R0
0b00000000,
0b00000001 // HLT
};
FILE *ptr = fopen(argv, "r");
char c[100];
long ret;

int address = 0;

for (int i = 0; i < DATA_LEN; i++) {
cpu->ram[address++] = data[i];
while (fgets(c, 100, ptr) != NULL)
{
if (c[0] == '1' || c[0] == '0')
{
ret = strtoul(c, NULL, 2);
cpu_ram_write(cpu, address++, ret);
}
}

fclose(ptr);

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

/**
* ALU
*/
void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB)
void alu(struct cpu *cpu, enum alu_op op)
{
switch (op) {
case ALU_MUL:
// TODO
break;
unsigned char regA = cpu_ram_read(cpu, (cpu->pc + 1));
unsigned char regB = cpu_ram_read(cpu, (cpu->pc + 2));

switch (op)
{
case ALU_MUL:
// TODO
cpu->registers[regA] = cpu->registers[regA] * cpu->registers[regB];
break;

// TODO: implement more ALU ops
}
Expand All @@ -45,16 +92,45 @@ void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB
*/
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).
// 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.
unsigned char IR = cpu_ram_read(cpu, (cpu->pc));

switch (IR)
{
case LDI:
handle_LDI(cpu);
break;

case PRN:
handle_PRN(cpu);
break;

case HLT:
handle_HLT();
break;

case MUL:
alu(cpu, ALU_MUL);
break;

default:
printf("unexpected instruction 0x%02X at 0x%02X\n", IR, cpu->pc);
exit(1);
}

int opNum = (11000000 & IR) >> 6;
cpu->pc += (opNum + 1);
}
}

Expand All @@ -64,4 +140,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->registers, 0, sizeof(cpu->registers));
cpu->registers[7] = 0xF4;
memset(cpu->ram, 0, sizeof(cpu->ram));
}
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;
// 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
#define MUL 0b10100010

// 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 *argv);
extern void cpu_init(struct cpu *cpu);
extern void cpu_run(struct cpu *cpu);

Expand Down
20 changes: 17 additions & 3 deletions ls8/ls8.c
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include "cpu.h"

/**
* Main
*/
int main(void)
int main(int argc, char *argv[])
{
if (argc < 2)
{
fprintf(stderr, "Not enough arguments.\nUsage: ./ls8 {file path from current directory}\n");
exit(1);
}

FILE *fptr;
if ((fptr = fopen(argv[1], "r")) == NULL)
{
printf("No such file.\n");
exit(1);
}
fclose(fptr);
struct cpu cpu;

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

return 0;
}
}