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
142 changes: 124 additions & 18 deletions ls8/cpu.c
Original file line number Diff line number Diff line change
@@ -1,58 +1,156 @@
#include "cpu.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/**
* Load the binary bytes from a .ls8 source file into a RAM array
*/
void cpu_load(struct cpu *cpu)

#define DATA_LEN 6
#define SP 7

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




void cpu_ram_write(struct cpu *cpu, unsigned char mar, unsigned char mdr)
{
cpu->ram[mar] = mdr;
}

void push(struct cpu *cpu, unsigned char value)
{
const int DATA_LEN = 6;
char data[DATA_LEN] = {
// From print8.ls8
0b10000010, // LDI R0,8
0b00000000,
0b00001000,
0b01000111, // PRN R0
0b00000000,
0b00000001 // HLT
};
cpu->registers[SP]--;
cpu_ram_write(cpu, cpu->registers[SP], value);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good. Well done with using cpu_ram_write() instead of just assigning the value.

}

unsigned char pop(struct cpu *cpu)
{
unsigned char value = cpu_ram_read(cpu, cpu->registers[SP]);

cpu->registers[SP]++;
return value;
}


void cpu_load(struct cpu *cpu, char *filename)
{


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

if(fp == NULL) {
fprintf(stderr, "ls8: error opening file: %s\n", filename);
exit(1);
}

char buff[8192];
int address = 0;

for (int i = 0; i < DATA_LEN; i++) {
cpu->ram[address++] = data[i];
while (fgets(buff, sizeof buff, fp) != NULL) {
unsigned char val = strtoul(buff, NULL, 2);
cpu_ram_write(cpu, address++, val);
}


fclose(fp);

}



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


/**
* ALU
*/
void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB)
{

unsigned char num_A = cpu->registers[regA];
unsigned char num_B = cpu->registers[regB];

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

// TODO: implement more ALU ops
}
}



/**
* Run the CPU
*/
void cpu_run(struct cpu *cpu)
{
int running = 1; // True until we get a HLT instruction
unsigned char IR, operandA, operandB;


while (running) {
// TODO
// 1. Get the value of the current instruction (in address PC).
IR = cpu_ram_read(cpu, cpu->PC);
operandA = cpu_ram_read(cpu, cpu->PC+1);
operandB = cpu_ram_read(cpu, cpu->PC+2);


// 2. switch() over it to decide on a course of action.
switch (IR) {
case LDI:
cpu->registers[operandA] = operandB;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

cpu->PC += 3;
break;
case MUL:
alu(cpu, ALU_MUL, operandA, operandB);
cpu->PC += 3;
break;
case ADD:
alu(cpu, ALU_ADD, operandA, operandB);
cpu->PC += 3;
case POP:
cpu->registers[operandA] = pop(cpu);
cpu->PC += 2;
break;
case PRN:
printf("%d\n", cpu->registers[operandA]);
cpu->PC += 2;
break;
case PUSH:
push(cpu, cpu->registers[operandA]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just out of curiosity, why did you make the functionality of push and pop in helper functions instead of just putting that in this switch?

cpu->PC += 2;
break;
case CALL:
push(cpu, (cpu->PC + 2));
cpu->PC = cpu->registers[operandA];
break;
case RET:
cpu->PC = pop(cpu);
break;
case HTL:
running = 0;
cpu->PC += 1;
break;
default:
printf("Unexpected instruction 0x%02X at 0x%02X\n", IR, cpu->PC);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great tool for debugging.

exit(1);
}
}


// 3. Do whatever the instruction should do according to the spec.
// 4. Move the PC to the next instruction.
}
}

/**
Expand All @@ -61,6 +159,14 @@ void cpu_run(struct cpu *cpu)
void cpu_init(struct cpu *cpu)
{
// TODO: Initialize the PC and other special registers

for (int i = 0; i < 6; i++)
{
cpu->registers[i] = 0;
}
cpu->PC = 0;
cpu->FL = 0;
cpu->registers[7] = 0xF4;
// TODO: Zero registers and RAM
memset(cpu->ram, 0, sizeof(cpu->ram));

}
20 changes: 19 additions & 1 deletion ls8/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,18 @@
struct cpu {
// TODO
// PC
unsigned char PC;
unsigned char FL;
// registers (array)
unsigned char registers[8];
// ram (array)
unsigned char ram[256];
};

enum alu_op {
ALU_MUL,
ALU_ADD

};

// Instructions
Expand All @@ -15,11 +25,19 @@ struct cpu {
// literals should be used.

#define LDI 0b10000010
#define PRN 0b01000111
#define HTL 0b00000001
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be HLT for "halt". But as long as you have it the same in both places, it'll still work.

#define MUL 0b10100010
#define PUSH 0b01000101
#define POP 0b01000110
#define ADD 0b10100000


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

Expand Down
12 changes: 10 additions & 2 deletions ls8/ls8.c
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
#include <stdio.h>
#include "cpu.h"


/**
* Main
*/
int main(void)
int main(int argc, char **argv)
{
struct cpu cpu;

if (argc !=2 ) {
fprintf(stderr, "usage: ls8 filename\n");
return 1;
}

char *filename = argv[1];

cpu_init(&cpu);
cpu_load(&cpu);
cpu_load(&cpu, filename);
cpu_run(&cpu);

return 0;
Expand Down