Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ls8/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ but you'll have to implement those three above instructions first!
## Step 0: IMPORTANT: inventory what is here!

* Make a list of files here.

- cpu.c
- declares the functions of the cpu
- cpu.h
- creates the data structures of the cpu
- ls8.c
- runs the cpu

* Write a short 3-10-word description of what each file does.
* Note what has been implemented, and what hasn't.
* Read this whole file.
Expand Down
188 changes: 167 additions & 21 deletions ls8/cpu.c
Original file line number Diff line number Diff line change
@@ -1,29 +1,51 @@
#include "cpu.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.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 trace(struct cpu *cpu);

double getcurtime(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec / 1000000.0;
}

void cpu_load(struct cpu *cpu, char *file_name)
{
char data[DATA_LEN] = {
// From print8.ls8
0b10000010, // LDI R0,8
0b00000000,
0b00001000,
0b01000111, // PRN R0
0b00000000,
0b00000001 // HLT
};

FILE *fp = fopen(file_name, "r");

if (fp == NULL) {
fprintf(stderr, "Error opening file: %s\n", file_name);
return;
}

int address = 0;
char line[128];

while (fgets(line, sizeof line, fp) != NULL) {
char *endptr;

unsigned char value = strtoul(line, &endptr, 2);

for (int i = 0; i < DATA_LEN; i++) {
cpu->ram[address++] = data[i];
if (endptr == line) {
continue;
}

cpu->ram[address++] = value;
}

// TODO: Replace this with something less hard-coded
fclose(fp);
}

/**
Expand All @@ -33,10 +55,14 @@ void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB
{
switch (op) {
case ALU_MUL:
// TODO
cpu->registers[regA] *= cpu->registers[regB];
break;
case ALU_ADD:
cpu->registers[regA] += cpu->registers[regB];
break;

// TODO: implement more ALU ops
// ADD, AND, CMP, DEC, DIV, INC, MOD, MUL, NOT, OR, SHL, SHR, SUB, XOR
}
}

Expand All @@ -47,14 +73,88 @@ void cpu_run(struct cpu *cpu)
{
int running = 1; // True until we get a HLT instruction

cpu->registers[7] = 0xF4;
unsigned char *sp;

sp = &cpu->registers[7];

unsigned char operandA, operandB;

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.
// trace(cpu);

unsigned char ir = cpu->ram[cpu->pc];

operandA = cpu->ram[cpu->pc+1];
operandB = cpu->ram[cpu->pc+2];

switch (ir) {

case CALL:

//The address of the instruction directly after CALL is pushed onto the stack. This allows us to return to where we left off when the subroutine finishes executing.
(*sp)--;
cpu->ram[*sp] = cpu->pc+2;

//The PC is set to the address stored in the given register. We jump to that location in RAM and execute the first instruction in the subroutine. The PC can move forward or backwards from its current location.
cpu->pc = cpu->registers[operandA];

break;

case HLT:
running = 0;
cpu->pc++;
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 LDI:
// Set the value of a register to an integer.
cpu->registers[operandA] = operandB;
cpu->pc += 3;
break;

case POP:
cpu->registers[operandA] = cpu->ram[*sp]; // Copy the value from the address pointed to by SP to the given register.
(*sp)++; // Increment SP.
cpu->pc += 2;
break;

case PUSH:
(*sp)--; // Decrement the SP.
cpu->ram[*sp] = cpu->registers[operandA]; // Copy the value in the given register to the address pointed to by SP.
cpu->pc += 2;
break;

case PRN:
printf("%d\n", cpu->registers[operandA]);
cpu->pc += 2;
break;

case RET:
// Pop the value from the top of the stack and store it in the PC.
cpu->pc = cpu->ram[*sp]; // Copy the value from the address pointed to by SP to the given register.
(*sp)++; // Increment SP.
break;

case ST:
cpu->ram[cpu->registers[operandA]] = cpu->registers[operandB];
cpu->pc += 3;
// Store value in registerB in the address stored in registerA.
break;

default:
printf("Error: found instruction %x at location %x\n", ir, cpu->pc);
exit(1);
}
}
}

Expand All @@ -64,4 +164,50 @@ 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);
}

char cpu_ram_read(struct cpu *cpu, int index)
{
return cpu->ram[index];
}

void cpu_ram_write(struct cpu *cpu, unsigned char input)
{

int index = 0;
int running = 1;

while (running) {

if (cpu->ram[index] == 0b00000001) {

if (index < 256) {
cpu->ram[index + 1] = 0b00000001;
cpu->ram[index] = input;
} else {
cpu->ram[index - 1] = input;
}
running = 0;
break;
}
}
}

void trace(struct cpu *cpu)
{
printf("%02X | ", cpu->pc);

printf("%02X %02X %02X |",
cpu_ram_read(cpu, cpu->pc),
cpu_ram_read(cpu, cpu->pc + 1),
cpu_ram_read(cpu, cpu->pc + 2));

for (int i = 0; i < 8; i++) {
printf(" %02X", cpu->registers[i]);
}

printf("\n");
}
20 changes: 16 additions & 4 deletions ls8/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
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
ALU_MUL, ALU_ADD
// Add more here
};

Expand All @@ -20,13 +23,22 @@ enum alu_op {
// These use binary literals. If these aren't available with your compiler, hex
// literals should be used.

#define LDI 0b10000010
#define PRN 0b01000111
#define LDI 0b10000010
#define PRN 0b01000111
#define HLT 0b00000001
#define MUL 0b10100010
#define POP 0b01000110
#define PUSH 0b01000101
#define CALL 0b01010000
#define RET 0b00010001
#define ADD 0b10100000
#define ST 0b10000100

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

Expand Down
2 changes: 1 addition & 1 deletion ls8/examples/stack.ls8
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@
00000010
01000111 # PRN R1
00000001
00000001 # HLT
00000001 # HLT
9 changes: 7 additions & 2 deletions ls8/ls8.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
/**
* Main
*/
int main(void)
int main(int argc, char *argv[])
{
struct cpu cpu;

if (argc != 2) {
fprintf(stderr, "argc error.\n");
return 1;
}

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

return 0;
Expand Down