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

#define DATA_LEN 6
#define SP 7

/**
* 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 *dir)
{
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 *fptr;

if ((fptr = fopen(dir,"r"))== NULL)
{
fprintf(stderr, "File does not exist: %s\n",dir);
exit(1);
}
//
char temp[256];
char *instruction;

unsigned int counter = 0;

while(fgets(temp,sizeof(temp),fptr) != NULL){
instruction = strndup(temp,8);

if (instruction[0] == '0' || instruction[0] == '1'){
cpu->ram[counter] = strtoul(instruction,NULL,2);
counter++;
}
}

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

}

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

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

// Stack

void cpu_push(struct cpu *cpu, unsigned char value)
{
cpu->registers[SP]--;
cpu_ram_write(cpu,value, cpu->registers[SP]);
printf("PUSH :%d @ %d\n VALUE: %d\n",cpu_ram_read(cpu,cpu->registers[SP]), cpu->registers[SP], value );
}

unsigned char cpu_pop(struct cpu *cpu){
if(cpu->registers[SP] == 0xF4){
fprintf(stderr, "stack is empty");
exit(1);
}

unsigned char value = cpu_ram_read(cpu,cpu->registers[SP]);
printf("POP :%d @ %d\n",value, cpu->registers[SP] );
cpu_ram_write(cpu,cpu->registers[SP],0);
cpu->registers[SP]++;

return value;

}

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

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

cpu->registers[regA] *= cpu->registers[regB];
// TODO: implement more ALU ops

}
}

Expand All @@ -46,15 +90,59 @@ 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
// Remember Zero is interpreted as false and anything non-zero is interpreted as true.

unsigned char IR;
unsigned char opA;
unsigned char opB;

while (running) {
// TODO
// 1. Get the value of the current instruction (in address PC).
IR = cpu_ram_read(cpu, cpu->PC);
// 2. Figure out how many operands this next instruction requires
int operands = IR >> 6;
// 3. Get the appropriate value(s) of the operands following this instruction
// 4. switch() over it to decide on a course of action.
switch (operands){
case 1:
opA = cpu_ram_read(cpu,cpu->PC+1);
case 2:
opA = cpu_ram_read(cpu,cpu->PC+1);
opB = cpu_ram_read(cpu,cpu->PC+2);
}

// DEBUG
printf("TRACE: %02X: %02X %02X %02X %02X\n", cpu->PC, IR, opA, opB, cpu->registers[0]);

// 4. switch() over it (THE INSTRUCTION) to decide on a course of action.
switch(IR){
// 5. Do whatever the instruction should do according to the spec.
case LDI:
// cpu_ram_write(cpu, opA, opB);
// cpu_ram_write(cpu, opB, opA);
cpu->registers[opA] = opB;
break;
case PRN:
printf("%d\n", cpu->registers[opA]);
break;
case MUL:
alu(cpu,ALU_MUL,opA,opB);
break;
case HLT:
running = 0;
break;
case PUSH:
cpu_push(cpu,cpu->registers[opA]);
break;
case POP:
cpu->registers[opA] = cpu_pop(cpu);
break;
default:
fprintf(stderr, "PC %02x: unknown instruction %02x\n", cpu->PC, IR);
exit(3);
}
// 6. Move the PC to the next instruction.
cpu->PC = cpu->PC + operands + 1;
}
}

Expand All @@ -63,5 +151,13 @@ void cpu_run(struct cpu *cpu)
*/
void cpu_init(struct cpu *cpu)
{
// TODO: Initialize the PC and other special registers
// Initialize the PC and other special registers

cpu->PC = 0;
cpu->FL = 0;
memset(cpu->registers,0,8);
memset(cpu->ram,0,256);

}


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

// ALU operations
Expand All @@ -23,11 +28,14 @@ enum alu_op {
#define LDI 0b10000010
#define HLT 0b00000001
#define PRN 0b01000111
#define MUL 0b10100010
#define PUSH 0b01000101
#define POP 0b01000110
// 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 *dir);
extern void cpu_init(struct cpu *cpu);
extern void cpu_run(struct cpu *cpu);

Expand Down
Binary file added ls8/examples/ls8
Binary file not shown.
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[])
{
if (argc != 2){
fprintf(stderr, "Invalid number of arguments. Add arg to ls8 file.\n");
return 1;
}

struct cpu cpu;

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

return 0;
Expand Down