Skip to content

Instantly share code, notes, and snippets.

@eparadis
Last active October 22, 2021 01:04
Show Gist options
  • Save eparadis/0e5fe0aac7bfe005cab65df5d9e4209d to your computer and use it in GitHub Desktop.
Save eparadis/0e5fe0aac7bfe005cab65df5d9e4209d to your computer and use it in GitHub Desktop.
sketch of a 8 bit cpu simulator/emulator
#include <stdint.h>
#include <stdio.h>
typedef uint8_t FLAG;
typedef uint8_t INSTR;
typedef uint8_t REGISTER;
typedef uint16_t IP;
const FLAG FLAG_HALT = 0x80;
const INSTR INSTR_LOAD = 0x08;
const INSTR INSTR_STORE = 0x09;
const INSTR INSTR_ADDI = 0x0A;
const INSTR INSTR_NOP = 0x00;
typedef REGISTER (*loader_t)(IP);
typedef void (*storer_t)(REGISTER, IP);
typedef struct cpu_state {
FLAG flags;
IP ip;
REGISTER rA;
} cpu_state;
struct cpu_state cpu(
cpu_state *curr_state,
loader_t load,
storer_t store,
loader_t io_read,
storer_t io_write
) {
cpu_state new_state;
INSTR instruction = load( curr_state->ip);
switch( instruction) {
case INSTR_LOAD: {
REGISTER upper_addr = load( curr_state->ip + 1);
REGISTER lower_addr = load( curr_state->ip + 2);
new_state.rA = load( upper_addr << 8 | lower_addr);
new_state.ip = curr_state->ip + 3;
break;
}
case INSTR_STORE: {
REGISTER upper_addr = load( curr_state->ip + 1);
REGISTER lower_addr = load( curr_state->ip + 2);
store( curr_state->rA, upper_addr << 8 | lower_addr);
new_state.ip = curr_state->ip + 3;
break;
}
case INSTR_ADDI: {
REGISTER addend = load( curr_state->ip + 1);
new_state.rA = curr_state->rA + addend;
new_state.ip = curr_state->ip + 2;
break;
}
case INSTR_NOP: {
new_state.ip = curr_state->ip + 1;
break;
}
default: {
new_state.flags |= FLAG_HALT;
break;
}
}
return new_state;
}
uint8_t ram[128];
REGISTER load( IP addr) {
printf("DEBUG[LOAD] addr:%.4x\n", addr);
return ram[addr % 128];
}
void store( REGISTER value, IP addr) {
printf("DEBUG[STORE] addr:%.4x value:%.4x\n", addr, value);
ram[addr % 128] = value;
}
void debug_state( cpu_state state) {
printf("DEBUG[CPU] flags:%.2x ip:%.4x rA:%.2x\n", state.flags, state.ip, state.rA);
}
void debug_dump(uint8_t *block, uint16_t start, uint16_t end) {
for( uint16_t i = start; i <= end; i += 1) {
printf("%.2x ", block[i]);
if( i % 16 == 15) {
printf("\n");
}
}
}
void zero_addresses(uint8_t *block, uint16_t start, uint16_t end) {
for( uint16_t i = start; i <= end; i += 1) {
block[i] = 0;
}
}
int main( ) {
zero_addresses(ram, 0, 127);
ram[0] = INSTR_ADDI;
ram[1] = 0x03;
ram[2] = INSTR_STORE;
ram[3] = 0x00;
ram[4] = 0x1F;
ram[5] = INSTR_LOAD;
ram[6] = 0x00;
ram[7] = 0x1E;
ram[8] = 0xAA; // illegal instr to cause halt
struct cpu_state state;
struct cpu_state next_state;
state.ip = 0;
state.flags = 0;
state.rA = 0;
while( (state.flags & FLAG_HALT) == 0) {
debug_state(state);
next_state = cpu( &state, &load, &store, &load, &store);
state = next_state;
}
debug_state(state);
debug_dump(ram, 0, 0x0F);
debug_dump(ram, 0x1E, 0x1F);
}
@eparadis
Copy link
Author

Can you tell I don’t actually write C very often? Yeah.

I wrote this on a machine without a compiler. It definitely won’t build as written.

I wrote this when I woke up in the middle of the night and couldn’t get back to sleep, so no guarantee it makes any sense at all.

@eparadis
Copy link
Author

I got it a little closer to real C. Still doesn’t compile.

@eparadis
Copy link
Author

I pulled this into repl.it to hammer out all the syntax (and conceptual) errors it had. I'll probably open a proper repo for it now that it compiles and shows some actual promise.

@eparadis
Copy link
Author

OK now i really need to do something with this because I obviously can't let it go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment