/* * Copyright (C) 2025 dwlr * * BSD 3-Clause License (BSD-3-Clause) * See LICENSE for details */ #define C8_RESET_VECTOR 0x200 #define C8_CYCLES_PER_FRAME 20 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 */ }; typedef struct { u16 pc; u8 sp; u16 i; u16 prevkeys; 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 typedef enum { C8_ILL, /* Onnn = 0,1,2,A,B */ /* 00E0 - CLS */ C8_CLS, /* 00EE - RET */ C8_RET, /* 0nnn - SYS addr */ C8_SYS, /* 1nnn - JP addr */ C8_JP, /* 2nnn - CALL addr */ C8_CALL, /* Annn - LD I, addr */ C8_LDI, /* Bnnn - JP V0, addr */ C8_JPV, /* Oxkk = 3,4,6,7,C,E,F */ /* 3xkk - SE Vx, byte */ C8_SEB, /* 4xkk - SNE Vx, byte */ C8_SNEB, /* 6xkk - LD Vx, byte */ C8_LD, /* 7xkk - ADD Vx, byte */ C8_ADDB, /* Cxkk - RND Vx, byte */ C8_RND, /* Ex9E - SKP Vx */ C8_SKP, /* ExA1 - SKNP Vx */ C8_SKNP, /* Fx07 - LD Vx, DT */ C8_MVDT, /* Fx0A - LD Vx, K */ C8_LDK, /* Fx15 - LD DT, Vx */ C8_LDDT, /* Fx18 - LD ST, Vx */ C8_LDST, /* Fx1E - ADD I, Vx */ C8_ADDI, /* Fx29 - LD F, Vx */ C8_HEX, /* Fx33 - LD B, Vx */ C8_BCD, /* Fx55 - LD [I], Vx */ C8_SAVE, /* Fx65 - LD Vx, [I] */ C8_RESTORE, /* Oxyn = 5,8,9,D */ /* 5xy0 - SE Vx, Vy */ C8_SE, /* 8xy0 - LD Vx, Vy */ C8_MOVE, /* 8xy1 - OR Vx, Vy */ C8_OR, /* 8xy2 - AND Vx, Vy */ C8_AND, /* 8xy3 - XOR Vx, Vy */ C8_XOR, /* 8xy4 - ADD Vx, Vy */ C8_ADD, /* 8xy5 - SUB Vx, Vy */ C8_SUB, /* 8xy6 - SHR Vx {, Vy} */ C8_SHR, /* 8xy7 - SUBN Vx, Vy */ C8_SUBN, /* 8xyE - SHL Vx {, Vy} */ C8_SHL, /* 9xy0 - SNE Vx, Vy */ C8_SNE, /* Dxyn - DRW Vx, Vy, nibble */ C8_DRW, C8_OP_CNT } chip8_op; 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; c8->running = true; } 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 CYCLES: %llu, KEYS: %X\n", c8->i, c8->sp, c8->dt, c8->st, c8->cycles, c8->keys); 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]); } } } #define c8_decode_generate(name) \ void c8_##name(u8* code, u16 offset, void* usrdat) \ { \ u16 word, instr, nnn; \ u8 o, x, y, n, kk; \ \ (void)usrdat; \ \ word = *(u16*)(code + offset); \ instr = be16toh(word); \ o = (instr & 0xF000) >> 12; \ nnn = (instr & 0x0FFF); \ x = (instr & 0x0F00) >> 8; \ y = (instr & 0x00F0) >> 4; \ n = (instr & 0x000F); \ kk = (instr & 0x00FF); \ \ (void)o; \ (void)nnn; \ (void)x; \ (void)y; \ (void)n; \ (void)kk; \ \ X_C8_PRELUDE; \ \ if(o == 0 && nnn == 0x0E0) X_C8_CLS; \ else if(o == 0 && nnn == 0x0EE) X_C8_RET; \ else if(o == 0) X_C8_SYS; \ else if(o == 1) X_C8_JP; \ else if(o == 2) X_C8_CALL; \ else if(o == 0xA) X_C8_LDI; \ else if(o == 0xB) X_C8_JPV; \ else if(o == 3) X_C8_SEB; \ else if(o == 4) X_C8_SNEB; \ else if(o == 6) X_C8_LD; \ else if(o == 7) X_C8_ADDB; \ else if(o == 0xC) X_C8_RND; \ else if(o == 0xE && kk == 0x9E) X_C8_SKP; \ else if(o == 0xE && kk == 0xA1) X_C8_SKNP; \ else if(o == 0xF && kk == 0x07) X_C8_MVDT; \ else if(o == 0xF && kk == 0x0A) X_C8_LDK; \ else if(o == 0xF && kk == 0x15) X_C8_LDDT; \ else if(o == 0xF && kk == 0x18) X_C8_LDST; \ else if(o == 0xF && kk == 0x1E) X_C8_ADDI; \ else if(o == 0xF && kk == 0x29) X_C8_HEX; \ else if(o == 0xF && kk == 0x33) X_C8_BCD; \ else if(o == 0xF && kk == 0x55) X_C8_SAVE; \ else if(o == 0xF && kk == 0x65) X_C8_RESTORE; \ else if(o == 5 && n == 0) X_C8_SE; \ else if(o == 8 && n == 0) X_C8_MOVE; \ else if(o == 8 && n == 1) X_C8_OR; \ else if(o == 8 && n == 2) X_C8_AND; \ else if(o == 8 && n == 3) X_C8_XOR; \ else if(o == 8 && n == 4) X_C8_ADD; \ else if(o == 8 && n == 5) X_C8_SUB; \ else if(o == 8 && n == 6) X_C8_SHR; \ else if(o == 8 && n == 7) X_C8_SUBN; \ else if(o == 8 && n == 0xE) X_C8_SHL; \ else if(o == 9 && n == 0) X_C8_SNE; \ else if(o == 0xD) X_C8_DRW; \ else X_C8_ILL; \ \ X_C8_EPILOGUE; \ }