diff options
author | dweller <dweller@cabin.digital> | 2025-03-26 14:55:29 +0200 |
---|---|---|
committer | dweller <dweller@cabin.digital> | 2025-03-26 14:55:29 +0200 |
commit | 2b50351d523cc18bae321b7f2879a927df5e802d (patch) | |
tree | 885f01cd5b37815ac00cda93292c29ba39e443b0 |
initial commit
-rw-r--r-- | bits.c | 15 | ||||
-rw-r--r-- | fnt.c | 19 | ||||
-rw-r--r-- | main.c | 382 |
3 files changed, 416 insertions, 0 deletions
@@ -0,0 +1,15 @@ +#define iota __COUNTER__ + +#define bit(x) (1 << (x)) + +#define KB bit(10) +#define MB bit(11) +#define GB bit(12) + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long long u64; + +typedef u8 bool; +#define true 1 +#define false 0 @@ -0,0 +1,19 @@ +static const u8 fnt[] = +{ + 0xF0, 0x90, 0x90, 0x90, 0xF0, /* 0 */ + 0x20, 0x60, 0x20, 0x20, 0x70, /* 1 */ + 0xF0, 0x10, 0xF0, 0x80, 0xF0, /* 2 */ + 0xF0, 0x10, 0xF0, 0x10, 0xF0, /* 3 */ + 0x90, 0x90, 0xF0, 0x10, 0x10, /* 4 */ + 0xF0, 0x80, 0xF0, 0x10, 0xF0, /* 5 */ + 0xF0, 0x80, 0xF0, 0x90, 0xF0, /* 6 */ + 0xF0, 0x10, 0x20, 0x40, 0x40, /* 7 */ + 0xF0, 0x90, 0xF0, 0x90, 0xF0, /* 8 */ + 0xF0, 0x90, 0xF0, 0x10, 0xF0, /* 9 */ + 0xF0, 0x90, 0xF0, 0x90, 0x90, /* A */ + 0xE0, 0x90, 0xE0, 0x90, 0xE0, /* B */ + 0xF0, 0x80, 0x80, 0x80, 0xF0, /* C */ + 0xE0, 0x90, 0x90, 0x90, 0xE0, /* D */ + 0xF0, 0x80, 0xF0, 0x80, 0xF0, /* E */ + 0xF0, 0x80, 0xF0, 0x80, 0x80 /* F */ +}; @@ -0,0 +1,382 @@ +#define _DEFAULT_SOURCE +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <time.h> + +#include <endian.h> + +#include "bits.c" +#include "fnt.c" + + +#define C8_RESET_VECTOR 0x200 +#define C8_CYCLES_PER_FRAME 1000 + + +typedef struct +{ + u16 pc; + u8 sp; + + u16 i; + u16 keys; + + u64 cycles; + + /* timers: */ + u8 dt; /* - delay */ + u8 st; /* - sound */ + + bool running; + + union + { + struct + { + u8 v[16]; + u64 disp[32]; + u8 font[sizeof(fnt)]; + u16 stack[16]; + + } sys; + u8 ram[4*KB]; + } mem; + +} chip8; + +#define V mem.sys.v +#define STACK mem.sys.stack +#define DISPLAY mem.sys.disp +#define FONT mem.sys.font +#define RAM mem.ram + + +void c8_reset(chip8* c8) +{ + assert(c8); + assert(sizeof(c8->mem) > sizeof(fnt)); + assert(sizeof(c8->mem) >= 4*KB); + + memcpy(c8->FONT, fnt, sizeof(fnt)); + + c8->pc = C8_RESET_VECTOR; +} + +/* inst. fmts: + * Onnn = 0,1,2,A,B + * 00E0 - CLS + * 00EE - RET + * 0nnn - SYS addr + * 1nnn - JP addr + * 2nnn - CALL addr + * Annn - LD I, addr + * Bnnn - JP V0, addr + * Oxkk = 3,4,6,7,C,E,F + * 3xkk - SE Vx, byte + * 4xkk - SNE Vx, byte + * 6xkk - LD Vx, byte + * 7xkk - ADD Vx, byte + * Cxkk - RND Vx, byte + * Ex9E - SKP Vx + * ExA1 - SKNP Vx + * Fx07 - LD Vx, DT + * Fx0A - LD Vx, K + * Fx15 - LD DT, Vx + * Fx18 - LD ST, Vx + * Fx1E - ADD I, Vx + * Fx29 - LD F, Vx + * Fx33 - LD B, Vx + * Fx55 - LD [I], Vx + * Fx65 - LD Vx, [I] + * Oxyn = 5,8,9,D + * 5xy0 - SE Vx, Vy + * 8xy0 - LD Vx, Vy + * 8xy1 - OR Vx, Vy + * 8xy2 - AND Vx, Vy + * 8xy3 - XOR Vx, Vy + * 8xy4 - ADD Vx, Vy + * 8xy5 - SUB Vx, Vy + * 8xy6 - SHR Vx {, Vy} + * 8xy7 - SUBN Vx, Vy + * 8xyE - SHL Vx {, Vy} + * 9xy0 - SNE Vx, Vy + * Dxyn - DRW Vx, Vy, nibble + */ + +void c8_dump_state(chip8* c8, bool stack) +{ + int i = 0; + + for(i = 0; i < (int)sizeof(c8->V); i += 4) + { + printf(" V%x: %02X (%-3d) ", i+0, c8->V[i+0], c8->V[i+0]); + printf("V%x: %02X (%-3d) ", i+1, c8->V[i+1], c8->V[i+1]); + printf("V%x: %02X (%-3d) ", i+2, c8->V[i+2], c8->V[i+2]); + printf("V%x: %02X (%-3d)\n", i+3, c8->V[i+3], c8->V[i+3]); + } + + printf(" I:%04X SP: %02X DT: %02X ST: %02X\n", c8->i, c8->sp, c8->dt, c8->st); + + if(stack) + { + printf("stack:\n"); + for(i = 0; i < (int)sizeof(c8->STACK); i += 2) + { + printf(" %-2d: %04X ", i+0, c8->STACK[i+0]); + printf("%-2d: %04X\n", i+1, c8->STACK[i+1]); + } + } +} + +void _c8_loop(chip8* c8, bool disasm, u16 from, u16 len) +{ + u16 to = 0; + u16 pc = 0; + + assert(c8); + + if(disasm) + { + pc = c8->pc; + c8->pc = from; + to = from + len*2; + } + + c8->running = true; + while(c8->running) + { + int tmp; + u16 word, instr, nnn; + u8 o, x, y, n, kk; + + if(c8->pc >= sizeof(c8->mem)) + { + printf("PC Overflow!\n"); + c8->running = false; + break; + } + + if(c8->pc & 0x0001) + { + printf("PC is not aligned!\n"); + c8->running = false; + break; + } + + word = *(u16*)(c8->RAM + c8->pc); + instr = be16toh(word); + o = (instr & 0xF000) >> 12; + nnn = (instr & 0x0FFF); + x = (instr & 0x0F00) >> 8; + y = (instr & 0x00F0) >> 4; + n = (instr & 0x000F); + kk = (instr & 0x00FF); + + printf("%04X: ", c8->pc); + switch(o) + { + case 1: + printf(" %04X JP 0x%03X\n", instr, nnn); + if(disasm) break; + c8->pc = nnn - 2; + break; + + case 2: + printf(" %04X CALL 0x%03X\n", instr, nnn); + if(disasm) break; + c8->STACK[c8->sp++] = c8->pc; /* docs says ++sp then stack[sp] = pc ??? */ + c8->pc = nnn - 2; + break; + + case 3: + printf(" %04X SE V%x, 0x%02X\n", instr, x, kk); + if(disasm) break; + if(c8->V[x] == kk) c8->pc += 2; + break; + + case 5: + printf(" %04X SE V%x, V%x\n", instr, x, y); + if(disasm) break; + if(c8->V[x] == c8->V[y]) c8->pc += 2; + break; + + case 6: + printf(" %04X LD V%x, 0x%02X\n", instr, x, kk); + if(disasm) break; + c8->V[x] = kk; + break; + + case 7: + printf(" %04X ADD V%x, 0x%02X\n", instr, x, kk); + if(disasm) break; + tmp = (int)c8->V[x] + (int)kk; + c8->V[0xf] = (tmp > 255); + /* c8->V[x] = (u8)tmp; /* spec says Vf not set */ + break; + + case 8: + { + switch(n) + { + case 4: + printf(" %04X ADD V%x, V%x\n", instr, x, y); + if(disasm) break; + tmp = (int)c8->V[x] + (int)c8->V[y]; + c8->V[0xf] = (tmp > 255); + c8->V[x] = (u8)tmp; + break; + + default: + printf(" %04X Unknown instruction\n", instr); + if(disasm) break; + c8->running = false; + break; + } + } + break; + + case 0xA: + printf(" %04X LD I, 0x%03X\n", instr, nnn); + if(disasm) break; + c8->i = nnn; + break; + + case 0xC: + printf(" %04X RND V%x, 0x%02X\n", instr, x, kk); + if(disasm) break; + c8->V[x] = (rand() % 256) & kk; + break; + + case 0xD: + printf(" %04X DRW V%x, V%x, %d\n", instr, x, y, n); + if(disasm) break; + /* TODO: implement */ + break; + + case 0xE: + if(kk == 0x9E) + { + printf(" %04X SKP V%x\n", instr, x); + if(disasm) break; + if(c8->keys & bit(x)) c8->pc += 2; + } + else if(kk == 0xA1) + { + printf(" %04X SKNP V%x\n", instr, x); + if(disasm) break; + if(!(c8->keys & bit(x))) c8->pc += 2; + } + else + { + printf(" %04X Unknown instruction\n", instr); + if(disasm) break; + c8->running = false; + } + break; + + case 0xF: + if(kk == 0x07) + { + printf(" %04X LD V%x, DT\n", instr, x); + if(disasm) break; + c8->V[x] = c8->dt; + } + else if(kk == 0x15) + { + printf(" %04X LD DT, V%x\n", instr, x); + if(disasm) break; + c8->dt= c8->V[x]; + } + else + { + printf(" %04X Unknown instruction\n", instr); + if(disasm) break; + c8->running = false; + } + break; + + default: + printf(" %04X Unknown instruction\n", instr); + if(disasm) break; + c8->running = false; + break; + } + + if(!disasm) c8_dump_state(c8, false); + + c8->pc += 2; + c8->cycles++; + if((c8->cycles % C8_CYCLES_PER_FRAME) == 0) + { + if(c8->dt) c8->dt--; + if(c8->st) c8->st--; + } + + if(disasm && (c8->pc >= to)) c8->running = false; + } + + if(disasm) c8->pc = pc; +} + +void c8_disam(chip8* c8, u16 from, u16 len) { _c8_loop(c8, true, from, len); } +void c8_loop(chip8* c8) { _c8_loop(c8, false, 0, 0); } + + +int main(int argc, char** argv) +{ + FILE* f = NULL; + chip8 c8 = {0}; + + srand(time(NULL)); + + c8_reset(&c8); + + if(argc > 1) + { + u64 sz, got = 0; + + f = fopen(argv[1], "r"); + if(!f) return 1; + + fseek(f, 0, SEEK_END); + sz = (u64)ftell(f); + rewind(f); + + if(sz >= (sizeof(c8.RAM) - 512)) return 2; + + got = fread(c8.RAM + C8_RESET_VECTOR, 1, sz, f); + if(got != sz) return 3; + + fclose(f); + } + else + { + c8.RAM[C8_RESET_VECTOR + iota] = 0x60; + c8.RAM[C8_RESET_VECTOR + iota] = 0x04; + + c8.RAM[C8_RESET_VECTOR + iota] = 0x61; + c8.RAM[C8_RESET_VECTOR + iota] = 0x03; + + c8.RAM[C8_RESET_VECTOR + iota] = 0x72; + c8.RAM[C8_RESET_VECTOR + iota] = 0x01; + + c8.RAM[C8_RESET_VECTOR + iota] = 0x80; + c8.RAM[C8_RESET_VECTOR + iota] = 0x14; + + c8.RAM[C8_RESET_VECTOR + iota] = 0x30; + c8.RAM[C8_RESET_VECTOR + iota] = 0x10; + + c8.RAM[C8_RESET_VECTOR + iota] = 0x12; + c8.RAM[C8_RESET_VECTOR + iota] = 0x04; + } + + printf("Disasm:\n"); + c8_disam(&c8, C8_RESET_VECTOR, 32); + + printf("\nExec:\n"); + c8_loop(&c8); + + return 0; +} |