#define _DEFAULT_SOURCE #include #include #include #include #include #include #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; }