Skip to main content
Back to articles
2026 / 01
| 2 min read

Emulation Milestones and TypeScript Migration

Intel 8088 demos, Zig-based 6502 experiments, and migrating the frontend to TypeScript for better type safety.

emulator 8088 6502 typescript c64

I spent most of today watching the TypeScript compiler catch message shape mismatches I would have missed for weeks. The UART bus worker now has typed contracts with the frontend, and when I changed the status field from a string to an enum the compiler found every place that needed updating. The win isn’t prettier code; it’s that these failures now happen at compile time instead of silently at runtime.

The migration took the workers first — uart-bus.worker.ts, the interpreter worker, the language backend adapters — because those are the boundaries where things go wrong. The frontend posts a message, the worker parses it, and if either side changes the shape without telling the other you get undefined behaviour that surfaces while you’re demoing to someone. Now the compiler enforces the contract.

On the core side, two threads ran in parallel. The Intel 8088 now runs C programs compiled with gcc-ia16, including a Fibonacci generator that prints the first 30 numbers. Getting the toolchain working meant Docker containers, libc stubs, and replacing a MOVZX instruction the original 8088 doesn’t have with an XOR. The payoff is that test programs can be written in C instead of hand-assembled.

Meanwhile the Zig MOS 6502 core in cores/mos6502-zig/ is taking shape. Today I implemented the memory module, the UART, and the LDA instruction with all its addressing modes. The Zig core mirrors the Rust core’s UART model at $D000-$D001, so the two can be validated side by side against the same tests.

The Rust MOS 6502 core models C64-era memory behaviour and exposes a clean switch when I need a flat address space for tests:

pub(crate) raw_memory_mode: bool,

This flag disables C64 banking and leaves the UART active so the Klaus Dormann tests can run without video or ROM overlays. The core still runs at 1 MHz; only the memory model collapses when correctness is the priority.

The C64 video system merged today. The text adapter maps PETSCII to Unicode box drawing so a VIC-II-era screen can survive in a browser:

const TL_CORNER = 0xe0; // ┌
const TR_CORNER = 0xf2; // ┐
const BL_CORNER = 0xee; // └
const BR_CORNER = 0xfd; // ┘
const HORIZ = 0xc0;     // ─
const VERT = 0xdd;      // │

It isn’t a video chip. It’s the smallest thing that gets a C64 screen into a terminal, and that’s all I need it to be today.

Tomorrow: finish the Zig 6502 instruction set and start the Forth REPL polish. The dungeon crawler also needs its combat system wired up.

Next: dungeon features, Forth polish, and core work

Previous: 2026-01-23