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
Binary file added .vscode/ipch/78cb2fb1c87129d9/LS8.ipch
Binary file not shown.
Binary file added .vscode/ipch/78cb2fb1c87129d9/mmap_address.bin
Binary file not shown.
Binary file added .vscode/ipch/bca32951ca4ad473/cpu.ipch
Binary file not shown.
Binary file added .vscode/ipch/bca32951ca4ad473/mmap_address.bin
Binary file not shown.
Binary file added .vscode/ipch/cc92be1e265297de/ls8.ipch
Binary file not shown.
Binary file added .vscode/ipch/cc92be1e265297de/mmap_address.bin
Binary file not shown.
Binary file added .vscode/ipch/f6c20eca54484328/CPU.ipch
Binary file not shown.
Binary file added .vscode/ipch/f6c20eca54484328/mmap_address.bin
Binary file not shown.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"C_Cpp.errorSquiggles": "Disabled"
}
26 changes: 13 additions & 13 deletions ls8/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ but you'll have to implement those three above instructions first!
* Read this whole file.
* Skim the spec.

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

This structure holds information about the CPU and associated components.

Expand All @@ -79,14 +79,14 @@ unsigned char x;
(Weirdly in C, if you don't specific `signed` or `unsigned` with a `char`, it's
up to the compiler which it uses.)

## Step 2: Add RAM functions
## Step 2: Add RAM functions

In `cpu.c`, add functions `cpu_ram_read()` and `cpu_ram_write()` that access the
RAM inside the `struct cpu`.

We'll make use of these helper function later.

## Step 3: Implement the core of `cpu_init()`
## Step 3: Implement the core of `cpu_init()`

The `cpu_init()` function takes a pointer to a `struct cpu` and initializes it
as necessary. At first, the PC, registers, and RAM should be cleared to zero.
Expand Down Expand Up @@ -123,7 +123,7 @@ the instruction opcode. See the LS-8 spec for details.
Add the `HLT` instruction to `cpu.h`.

In `cpu_run()` in your switch, exit the loop if a `HLT` instruction is
encountered, regardless of whether or not there are more lines of code in the LS-8 program you loaded.
encountered, regardless of whether or not there are more lines of code in the LS-8 program you loaded.

We can consider `HLT` to be similar to a `return` or `exit()` in that we stop whatever we are doing, wherever we are.

Expand Down Expand Up @@ -178,7 +178,7 @@ so you can look in `argv[1]` for the name of the file to load.
> Bonus: check to make sure the user has put a command line argument where you
> expect, and print an error and exit if they didn't.

In `load_cpu()`, you will now want to use those command line arguments to open a file, read in its contents line by line, and save appropriate data into RAM.
In `load_cpu()`, you will now want to use those command line arguments to open a file, read in its contents line by line, and save appropriate data into RAM.

As you process lines from the file, you should be on the lookout for blank lines
(ignore them), and you should ignore everything after a `#`, since that's a
Expand Down Expand Up @@ -219,7 +219,7 @@ Check the LS-8 spec for what the `MUL` instruction does.
> Note: `MUL` is the responsiblity of the ALU, so it would be
nice if your code eventually called the `alu()` function with appropriate
arguments to get the work done.
>
>


## Step 10: Beautify your `cpu_run()` loop, if needed
Expand Down Expand Up @@ -312,10 +312,10 @@ a high address) and grows _downward_ as things are pushed on. The LS-8 is no
exception to this.

Implement a system stack per the spec. Add `PUSH` and `POP` instructions. Read
the beginning of the spec to see which register is the stack pointer.
* Values themselves should be saved in the ***portion of RAM*** _that is allocated for the stack_.
- Use the stack pointer to modify the correct block of memory.
the beginning of the spec to see which register is the stack pointer.

* Values themselves should be saved in the ***portion of RAM*** _that is allocated for the stack_.
- Use the stack pointer to modify the correct block of memory.
- Make sure you update the stack pointer appropriately as you `PUSH` and `POP` items to and from the stack.

If you run `./ls8 examples/stack.ls8` you should see the output:
Expand All @@ -333,7 +333,7 @@ enable you to jump to any address with the `CALL` instruction, and then return
back to where you called from with the `RET` instruction. This enables you to
create reusable functions.

Subroutines have many similarities to functions in higher-level languages. Just as a function in C, JavaScript or Python will jump from the function call, to its definition, and then return back to the line of code following the call, subroutines will also allow us to execute instructions non-sequentially.
Subroutines have many similarities to functions in higher-level languages. Just as a function in C, JavaScript or Python will jump from the function call, to its definition, and then return back to the line of code following the call, subroutines will also allow us to execute instructions non-sequentially.

The stack is used to hold the return address used by `RET`, so you **must** implement the
stack in step 11, first. Then, add subroutine instructions `CALL` and `RET`.
Expand All @@ -343,8 +343,8 @@ problem is that some instructions want to execute and move to the next
instruction like normal, but others, like `CALL` and `JMP` want to go to a
specific address.

> Note: `CALL` is very similar to the `JMP` instruction. However, there is one key difference between them. Can you find it in the specs?
>
> Note: `CALL` is very similar to the `JMP` instruction. However, there is one key difference between them. Can you find it in the specs?
>

* In **any** case where the instruction handler sets the `PC` directly, you
_don't_ want to advance the PC to the next instruction. So you'll have to set up
Expand Down
169 changes: 132 additions & 37 deletions ls8/cpu.c
Original file line number Diff line number Diff line change
@@ -1,60 +1,142 @@

#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)
unsigned char cpu_ram_read(struct cpu *cpu, unsigned char mar)
{
char data[DATA_LEN] = {
// From print8.ls8
0b10000010, // LDI R0,8
0b00000000,
0b00001000,
0b01000111, // PRN R0
0b00000000,
0b00000001 // HLT
};
// mar = Memory Address registersister, holds the memory address
// we're reading or writing

int address = 0;
// return value from ram specified by mar
return cpu->ram[mar];
}

for (int i = 0; i < DATA_LEN; i++) {
cpu->ram[address++] = data[i];
}
void cpu_ram_write(struct cpu *cpu, unsigned char index, unsigned char mdr)
{
// mdr = Memory Data registersister, holds the value
// to write or the value just read

// write mdr in ram
cpu->ram[index] = mdr;
}

void cpu_push(struct cpu *cpu, unsigned char val)
{
// Decrement SP
cpu->registers[SP]--;
// Copy the value in the given reg to the address pointed to by SP;
cpu_ram_write(cpu, cpu->registers[SP], val);
}

// TODO: Replace this with something less hard-coded
unsigned char cpu_pop(struct cpu *cpu)
{
// Read last value from stack
unsigned char val = cpu_ram_read(cpu, cpu->registers[SP]);
// Increment SP
cpu->registers[SP]++;
return val;
}

/**
* ALU
* Load the binary bytes from a .ls8 source file into a RAM array
*/
void alu(struct cpu *cpu, enum alu_op op, unsigned char regA, unsigned char regB)
void cpu_load(struct cpu *cpu, char *filename)
{
switch (op) {
case ALU_MUL:
// TODO
break;
FILE *fp = fopen(filename, "r");

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

char line[8192]; // to hold individual lines in the file
int address = 0;

while (fgets(line, sizeof(line), fp) != NULL)
{
char *endptr; // to keep track of non-numbers in the file
// converts str to number
unsigned char byte = strtoul(line, &endptr, 2);

// prevents unnecessary lines being stored on ram
if (endptr == line)
{
continue;
}

// TODO: implement more ALU ops
cpu->ram[address++] = byte;
}

fclose(fp);
}

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

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.
// True until we get a HLT instruction
int running = 1;
unsigned char command, operand1, operand2;

while (running)
{
command = cpu_ram_read(cpu, cpu->PC);

operand1 = cpu_ram_read(cpu, cpu->PC + 1);
operand2 = cpu_ram_read(cpu, cpu->PC + 2);

switch (command)
{
case LDI:
// sets value of operand1 to the registers[operand1]
cpu->registers[operand1] = operand2;
// Move the PC to the next instruction.
cpu->PC += 3;
break;
case PRN:
printf("%d\n", cpu->registers[operand1]);
// Move the PC to the next instruction.
cpu->PC += 2;
break;
case MUL:
cpu->registers[operand1] *= operand2;
cpu->PC += 3;
break;
case ADD:
cpu->registers[operand1] += operand2;
cpu->PC += 3;
break;

case PUSH:
cpu_push(cpu, cpu->registers[operand1]);
cpu->PC += 2;
break;

case POP:
// Copy the value from the address pointed to by SP to the given registers
cpu->registers[operand1] = cpu_pop(cpu);
cpu->PC += 2;
break;

case HLT:
// Set running to false to stop program
running = 0;
// Move the PC to the next instruction.
cpu->PC += 1;
break;

default:
printf("KIT: I can't do that Michael\n");
exit(1);
break;
}
}
}

Expand All @@ -63,5 +145,18 @@ void cpu_run(struct cpu *cpu)
*/
void cpu_init(struct cpu *cpu)
{
// TODO: Initialize the PC and other special registers
}
// R0-R6 are cleared to 0
for (int i = 0; i < 6; i++)
{
cpu->registers[i] = 0;
}

// R7 is set to 0xF4
cpu->registers[7] = 0xF4;

// PC is cleared to 0
cpu->PC = 0;

// RAM is cleared to 0
memset(cpu->ram, 0, sizeof(cpu->ram));
}
54 changes: 40 additions & 14 deletions ls8/cpu.h
Original file line number Diff line number Diff line change
@@ -1,33 +1,59 @@
#ifndef _CPU_H_
#define _CPU_H_

// Holds all information about the CPU
struct cpu {
// TODO
// PC
// registers (array)
// ram (array)
// DONE✔: Holds all information about the CPU
struct cpu
{
// DONE✔: PC
unsigned int PC;
/* DONE✔: registers (array) // R0 - R7
R5 is reserved for the interrupt mask (IM)
R6 is reserved for the interrupt status (IS)
R7 is reserved for the stack pointer (SP)
*/
unsigned int IM;
unsigned int IS;
unsigned int SP;
unsigned char registers[8];
// DONE✔: ram (array) // 8 bit address
// that can hold up to 256 bytes of RAM total
unsigned char ram[256];
};

// ALU operations
enum alu_op {
ALU_MUL
// Add more here
enum alu_op
{
ALU_MUL, // what is this???
ALU_ADD, // Add more here
// not done yet, just added
// ALU_NOP,
// ALU_NOT,
// ALU_POP,
// ALU_PRA,
// ALU_PRN,
// ALU_PUSH,
// ALU_RET,
};

// 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 PRN 0b01000111
#define HLT 0b00000001
// TODO: more instructions here. These can be used in cpu_run().
#define MUL 0b10100010
#define PUSH 0b01000101
//POP register - pop the value at the top of the stack into the given reg.
#define POP 0b01000110
#define CALL 0b01010000
#define RET 0b00010001
#define ADD 0b10100000

// 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
16 changes: 13 additions & 3 deletions ls8/ls8.c
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
#include <stdio.h>
#include <stdlib.h>
#include "cpu.h"

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

// check to make sure the user has put a command line argument where you
// expect, and print an error and exit if they didn't
if (argc != 2)
{
fprintf(stderr, "usage: ls8 filename\n");
exit(1);
}

char *filename = argv[1];

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

return 0;
Expand Down
File renamed without changes.
File renamed without changes.