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
12 changes: 12 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"workbench.colorCustomizations": {
"activityBar.background": "#033615",
"titleBar.activeBackground": "#044B1D",
"titleBar.activeForeground": "#F0FEF5"
},
"files.associations": {
"*.js": "javascriptreact",
"cpu.h": "c",
"stdlib.h": "c"
}
}
21 changes: 21 additions & 0 deletions ls8/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,27 @@ but you'll have to implement those three above instructions first!
* Read this whole file.
* Skim the spec.

## File Structure

* Examples - holds multiple files that gives us CPU word, instructions, register and operand examples

### Cpu.c

1. cpu_load - will load the binary bytes that are in .lsa source into an array

2. alu - will handle the numeric values

3. cpu_run - will handle processing all the instructions we feed it

4. cpu_init - creates a cpu struct and starts the PC and all its registers

### Cpu.h

1. struct cpu - holds all info about CPU the registers and ram

2. enum alu_op - all the ALU numeric operations


## Step 1: Implement `struct cpu` in `cpu.h`

This structure holds information about the CPU and associated components.
Expand Down
107 changes: 85 additions & 22 deletions ls8/cpu.c
Original file line number Diff line number Diff line change
@@ -1,42 +1,70 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cpu.h"

#define DATA_LEN 6

/**
* Reads from a CPU struct's RAM array
*/
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;
}

/**
* 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 *fileptr = fopen(path, "r");

if (fileptr == NULL)
{
printf("Couldn't open program: %s\n", path);
exit(2);
}

int address = 0;
char line[8000];

for (int i = 0; i < DATA_LEN; i++) {
cpu->ram[address++] = data[i];
while (fgets(line, sizeof(line), fileptr) != NULL)
{
char *endptr;
// & 0xFF gives you the last 8 bits of the ul (so it will always fit in an unsigned char)
unsigned char value = strtoul(line, &endptr, 2) & 0xFF;
if (endptr == line)
{
continue;
}
cpu_ram_write(cpu, address++, value);
}

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

/**
* ALU
*/
void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB)
{
switch (op) {
case ALU_MUL:
// TODO
break;
unsigned int a = cpu->registers[regA];
unsigned int b = cpu->registers[regB];

// TODO: implement more ALU ops
switch (op)
{
case ALU_MUL:
cpu->registers[regA] = a * b;
break;
case ALU_ADD:
cpu->registers[regA] = a + b;
break;
}
}

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

while (running) {
// TODO
while (running)
{
// 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
// Not entirely needed if we use a switch statement
// 3. Get the appropriate value(s) of the operands following this instruction
// Operations may need up to 2 bytes after PC
unsigned char operandA = cpu_ram_read(cpu, cpu->pc + 1);
unsigned char operandB = 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.
// 6. Move the PC to the next instruction.
switch (ir)
{
case LDI:
// Set the value of a register to an integer
cpu->registers[operandA] = operandB;
cpu->pc += 3;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looking good!

break;
case PRN:
// Print numeric value stored in the given register
printf("%d\n", cpu->registers[operandA]);
cpu->pc += 2;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Eventually you might come across a way to increase the PC using the instruction number itself (hint: this uses bit shifting).

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 HLT:
// Halt the CPU (and exit the emulator)
running = 0;
break;
default:
break;
}
}
}

Expand All @@ -63,5 +123,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->ram, 0, sizeof(cpu->ram));
memset(cpu->registers, 0, sizeof(cpu->registers));
cpu->registers[7] = 0xF4;
}
27 changes: 16 additions & 11 deletions ls8/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,38 @@
#define _CPU_H_

// Holds all information about the CPU
struct cpu {
// TODO
struct cpu
{
// 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,
ALU_ADD
};

// 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
// TODO: more instructions here. These can be used in cpu_run().
#define LDI 0b10000010
#define HLT 0b00000001
#define PRN 0b01000111
#define MUL 0b10100010
#define ADD 0b10100000

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

#endif
#endif
10 changes: 8 additions & 2 deletions ls8/ls8.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@
/**
* Main
*/
int main(void)
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "usage: must include a single path to a desired program\n");
return 1;
}

struct cpu cpu;

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

return 0;
Expand Down