From 76d89eb9973671726dc7fffaf22b06a7a77be550 Mon Sep 17 00:00:00 2001
From: dweller <dweller@cabin.digital>
Date: Wed, 26 Mar 2025 17:31:51 +0200
Subject: build ''system''; decode metaprog; disasm

---
 .gitignore            |   1 +
 bits.c                |  15 ---
 build/x11             |  14 ++
 clean                 |   2 +
 fnt.c                 |  19 ---
 instr.c               | 149 ---------------------
 main.c                | 343 -------------------------------------------------
 sources/bits.c        |  15 +++
 sources/fnt.c         |  19 +++
 sources/instr.c       | 102 +++++++++++++++
 sources/main.c        | 350 ++++++++++++++++++++++++++++++++++++++++++++++++++
 sources/meta/disasm.c |  41 ++++++
 sources/meta/undef.c  |  36 ++++++
 13 files changed, 580 insertions(+), 526 deletions(-)
 create mode 100644 .gitignore
 delete mode 100644 bits.c
 create mode 100755 build/x11
 create mode 100755 clean
 delete mode 100644 fnt.c
 delete mode 100644 instr.c
 delete mode 100644 main.c
 create mode 100644 sources/bits.c
 create mode 100644 sources/fnt.c
 create mode 100644 sources/instr.c
 create mode 100644 sources/main.c
 create mode 100644 sources/meta/disasm.c
 create mode 100644 sources/meta/undef.c

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d4f588e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+artifacts/
diff --git a/bits.c b/bits.c
deleted file mode 100644
index a6b7887..0000000
--- a/bits.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#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/build/x11 b/build/x11
new file mode 100755
index 0000000..a383438
--- /dev/null
+++ b/build/x11
@@ -0,0 +1,14 @@
+#!/bin/sh -e
+
+bench()
+{
+    if [ x = "x$(which time)" ]; then
+        $@
+        echo "time not installed"
+    else
+        time $@
+    fi
+}
+
+>&2 bench cc -std=c89 -Wall -Wextra sources/main.c -static -o artifacts/xip-8
+>&2 echo "------------------------------------------------\n"
diff --git a/clean b/clean
new file mode 100755
index 0000000..1dd2ea1
--- /dev/null
+++ b/clean
@@ -0,0 +1,2 @@
+#!/bin/sh -e
+rm -rf artifacts/*
diff --git a/fnt.c b/fnt.c
deleted file mode 100644
index ca45865..0000000
--- a/fnt.c
+++ /dev/null
@@ -1,19 +0,0 @@
-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/instr.c b/instr.c
deleted file mode 100644
index bef859d..0000000
--- a/instr.c
+++ /dev/null
@@ -1,149 +0,0 @@
-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;
-
-typedef void (*instr_func)(chip8* c8, chip8_op op, u16 nnn, u8 x, u8 y, u8 n, u8 kk);
-
-
-const char* chip8_disasm_fmts[C8_OP_CNT] =
-{
-    "ILL  unknown",
-    "CLS",
-    "RET",
-    "SYS  %03X",
-    "JP   %03X",
-    "CALL %03X",
-    "LD   I,  %03X",
-    "JP   V0, %03X",
-    "SE   V%-2x, %-2d",
-    "SNE  V%-2x, %-2d",
-    "LD   V%-2x, %-2d",
-    "ADD  V%-2x, %-2d",
-    "RND  V%-2x, %-2d",
-    "SKP  V%-2x",
-    "SKNP V%-2x",
-    "LD   V%-2x, DT",
-    "LD   V%-2x, K",
-    "LD   DT, V%-2x",
-    "LD   ST, V%-2x",
-    "ADD  I, V%-2x",
-    "LD   F, V%-2x",
-    "LD   B, V%-2x",
-    "LD   [I], V%-2x",
-    "LD   V%-2x, [I]",
-    "SE   V%-2x, V%-2x",
-    "LD   V%-2x, V%-2x",
-    "OR   V%-2x, V%-2x",
-    "AND  V%-2x, V%-2x",
-    "XOR  V%-2x, V%-2x",
-    "ADD  V%-2x, V%-2x",
-    "SUB  V%-2x, V%-2x",
-    "SHR  V%-2x, V%-2x",
-    "SUBN V%-2x, V%-2x",
-    "SHL  V%-2x, V%-2x",
-    "SNE  V%-2x, V%-2x",
-    "DRW  V%-2x, V%-2x, %-1d"
-};
-
-void c8_decode(u8* code, u16 offset, instr_func func, chip8* c8);
-void c8_disasm(chip8* c8, chip8_op op, u16 nnn, u8 x, u8 y, u8 n, u8 kk);
-void c8_do(chip8* c8, chip8_op op, u16 nnn, u8 x, u8 y, u8 n, u8 kk);
-
-void c8_decode(u8* code, u16 offset, instr_func func, chip8* c8)
-{
-    chip8_op op = C8_ILL;
-    u16 word, instr, nnn;
-    u8  o, x, y, n, kk;
-
-    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)      op = C8_CLS;
-    else if(o == 0 && nnn == 0x0EE) op = C8_RET;
-    else if(o == 0)                 op = C8_SYS;
-    else if(o == 1)                 op = C8_JP;
-    else if(o == 2)                 op = C8_CALL;
-    else if(o == 0xA)               op = C8_LDI;
-    else if(o == 0xB)               op = C8_JPV;
-    else if(o == 3)                 op = C8_SEB;
-    else if(o == 4)                 op = C8_SNEB;
-    else if(o == 6)                 op = C8_LD;
-    else if(o == 7)                 op = C8_ADDB;
-    else if(o == 0xC)               op = C8_RND;
-    else if(o == 0xE && kk == 0x9E) op = C8_SKP;
-    else if(o == 0xE && kk == 0xA1) op = C8_SKNP;
-    else if(o == 0xF && kk == 0x07) op = C8_MVDT;
-    else if(o == 0xF && kk == 0x0A) op = C8_LDK;
-    else if(o == 0xF && kk == 0x15) op = C8_LDDT;
-    else if(o == 0xF && kk == 0x18) op = C8_LDST;
-    else if(o == 0xF && kk == 0x1E) op = C8_ADDI;
-    else if(o == 0xF && kk == 0x29) op = C8_HEX;
-    else if(o == 0xF && kk == 0x33) op = C8_BCD;
-    else if(o == 0xF && kk == 0x55) op = C8_SAVE;
-    else if(o == 0xF && kk == 0x65) op = C8_RESTORE;
-    else if(o == 5 && n == 0)       op = C8_SE;
-    else if(o == 8 && n == 0)       op = C8_MOVE;
-    else if(o == 8 && n == 1)       op = C8_OR;
-    else if(o == 8 && n == 2)       op = C8_AND;
-    else if(o == 8 && n == 3)       op = C8_XOR;
-    else if(o == 8 && n == 4)       op = C8_ADD;
-    else if(o == 8 && n == 5)       op = C8_SUB;
-    else if(o == 8 && n == 6)       op = C8_SHR;
-    else if(o == 8 && n == 7)       op = C8_SUBN;
-    else if(o == 8 && n == 0xE)     op = C8_SHL;
-    else if(o == 9 && n == 0)       op = C8_SNE;
-    else if(o == 0xD)               op = C8_DRW;
-
-    func(c8, op, nnn, x, y, n, kk);
-}
-
-void c8_disasm(chip8* c8, chip8_op op, u16 nnn, u8 x, u8 y, u8 kk, u8 n);
-void c8_do(chip8* c8, chip8_op op, u16 nnn, u8 x, u8 y, u8 kk, u8 n);
-
diff --git a/main.c b/main.c
deleted file mode 100644
index c082914..0000000
--- a/main.c
+++ /dev/null
@@ -1,343 +0,0 @@
-#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;
-}
-
-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;
-}
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
-- 
cgit v1.2.3