summaryrefslogtreecommitdiff
path: root/sources
diff options
context:
space:
mode:
Diffstat (limited to 'sources')
-rw-r--r--sources/bits.c15
-rw-r--r--sources/fnt.c19
-rw-r--r--sources/instr.c102
-rw-r--r--sources/main.c350
-rw-r--r--sources/meta/disasm.c41
-rw-r--r--sources/meta/undef.c36
6 files changed, 563 insertions, 0 deletions
diff --git a/sources/bits.c b/sources/bits.c
new file mode 100644
index 0000000..a6b7887
--- /dev/null
+++ b/sources/bits.c
@@ -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
diff --git a/sources/fnt.c b/sources/fnt.c
new file mode 100644
index 0000000..ca45865
--- /dev/null
+++ b/sources/fnt.c
@@ -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 */
+};
diff --git a/sources/instr.c b/sources/instr.c
new file mode 100644
index 0000000..8c0b132
--- /dev/null
+++ b/sources/instr.c
@@ -0,0 +1,102 @@
+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;
+
+
+#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); \
+ \
+ 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; \
+}
+
+#include "meta/disasm.c"
diff --git a/sources/main.c b/sources/main.c
new file mode 100644
index 0000000..56630df
--- /dev/null
+++ b/sources/main.c
@@ -0,0 +1,350 @@
+#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;
+
+#include "instr.c"
+
+#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;
+}
+
+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;
+}
+
+
+int main(int argc, char** argv)
+{
+ u64 sz = 0;
+ FILE* f = NULL;
+ chip8 c8 = {0};
+
+ srand(time(NULL));
+
+ c8_reset(&c8);
+
+ if(argc > 1)
+ {
+ u64 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;
+
+ sz = iota;
+ }
+
+ printf("Disasm:\n");
+ {
+ u64 i;
+ char buf[512] = {0};
+
+ for(i = 0; i < sz; i += 2)
+ {
+ u16 pc = C8_RESET_VECTOR + i;
+ c8_disasm(c8.RAM, pc, buf);
+ printf("%04X: %s\n", pc, buf);
+ }
+ }
+
+ return 0;
+}
diff --git a/sources/meta/disasm.c b/sources/meta/disasm.c
new file mode 100644
index 0000000..2a33aed
--- /dev/null
+++ b/sources/meta/disasm.c
@@ -0,0 +1,41 @@
+#define X_C8_ILL sprintf(usrdat, "ILL unknown")
+#define X_C8_CLS sprintf(usrdat, "CLS")
+#define X_C8_RET sprintf(usrdat, "RET")
+#define X_C8_SYS sprintf(usrdat, "SYS 0x%03X", nnn)
+#define X_C8_JP sprintf(usrdat, "JP 0x%03X", nnn)
+#define X_C8_CALL sprintf(usrdat, "CALL 0x%03X", nnn)
+#define X_C8_LDI sprintf(usrdat, "LD I, 0x%03X", nnn)
+#define X_C8_JPV sprintf(usrdat, "JP V0, 0x%03X", nnn)
+#define X_C8_SEB sprintf(usrdat, "SE V%x, %d", x, kk)
+#define X_C8_SNEB sprintf(usrdat, "SNE V%x, %d", x, kk)
+#define X_C8_LD sprintf(usrdat, "LD V%x, %d", x, kk)
+#define X_C8_ADDB sprintf(usrdat, "ADD V%x, %d", x, kk)
+#define X_C8_RND sprintf(usrdat, "RND V%x, %d", x, kk)
+#define X_C8_SKP sprintf(usrdat, "SKP V%x", x)
+#define X_C8_SKNP sprintf(usrdat, "SKNP V%x", x)
+#define X_C8_MVDT sprintf(usrdat, "LD V%x, DT", x)
+#define X_C8_LDK sprintf(usrdat, "LD V%x, K", x)
+#define X_C8_LDDT sprintf(usrdat, "LD DT, V%x", x)
+#define X_C8_LDST sprintf(usrdat, "LD ST, V%x", x)
+#define X_C8_ADDI sprintf(usrdat, "ADD I, V%x", x)
+#define X_C8_HEX sprintf(usrdat, "LD F, V%x", x)
+#define X_C8_BCD sprintf(usrdat, "LD B, V%x", x)
+#define X_C8_SAVE sprintf(usrdat, "LD [I], V%x", x)
+#define X_C8_RESTORE sprintf(usrdat, "LD V%x, [I]", x)
+#define X_C8_SE sprintf(usrdat, "SE V%x, V%x", x, y)
+#define X_C8_MOVE sprintf(usrdat, "LD V%x, V%x", x, y)
+#define X_C8_OR sprintf(usrdat, "OR V%x, V%x", x, y)
+#define X_C8_AND sprintf(usrdat, "AND V%x, V%x", x, y)
+#define X_C8_XOR sprintf(usrdat, "XOR V%x, V%x", x, y)
+#define X_C8_ADD sprintf(usrdat, "ADD V%x, V%x", x, y)
+#define X_C8_SUB sprintf(usrdat, "SUB V%x, V%x", x, y)
+#define X_C8_SHR sprintf(usrdat, "SHR V%x, V%x", x, y)
+#define X_C8_SUBN sprintf(usrdat, "SUBN V%x, V%x", x, y)
+#define X_C8_SHL sprintf(usrdat, "SHL V%x, V%x", x, y)
+#define X_C8_SNE sprintf(usrdat, "SNE V%x, V%x", x, y)
+#define X_C8_DRW sprintf(usrdat, "DRW V%x, V%x, %d", x, y, n)
+
+c8_decode_generate(disasm)
+
+#include "undef.c"
+
diff --git a/sources/meta/undef.c b/sources/meta/undef.c
new file mode 100644
index 0000000..1863e9b
--- /dev/null
+++ b/sources/meta/undef.c
@@ -0,0 +1,36 @@
+#undef X_C8_ILL
+#undef X_C8_CLS
+#undef X_C8_RET
+#undef X_C8_SYS
+#undef X_C8_JP
+#undef X_C8_CALL
+#undef X_C8_LDI
+#undef X_C8_JPV
+#undef X_C8_SEB
+#undef X_C8_SNEB
+#undef X_C8_LD
+#undef X_C8_ADDB
+#undef X_C8_RND
+#undef X_C8_SKP
+#undef X_C8_SKNP
+#undef X_C8_MVDT
+#undef X_C8_LDK
+#undef X_C8_LDDT
+#undef X_C8_LDST
+#undef X_C8_ADDI
+#undef X_C8_HEX
+#undef X_C8_BCD
+#undef X_C8_SAVE
+#undef X_C8_RESTORE
+#undef X_C8_SE
+#undef X_C8_MOVE
+#undef X_C8_OR
+#undef X_C8_AND
+#undef X_C8_XOR
+#undef X_C8_ADD
+#undef X_C8_SUB
+#undef X_C8_SHR
+#undef X_C8_SUBN
+#undef X_C8_SHL
+#undef X_C8_SNE
+#undef X_C8_DRW