Brainfuck Threaded-Code VM
Games & Classics
A Brainfuck interpreter running live on a
Super Nintendo, dispatching each of its eight opcodes with a computed goto
— goto *handlers[op], the "threaded code" loop that lowers to a single indirect
jump. Watch the program pointer scrub the source, the tape cells mutate, and the output marquee
spell out HELLO WORLD! one byte at a time. Runs automatically; no joypad needed.
Written in C and compiled with the llvm-mos-based
65816 toolchain (+mos-a16, 16-bit accumulator mode).
Self-running demo — the VM interprets Hello World on a loop.
Click the screen to focus, then watch. Tab away and it pauses.
What it is
Brainfuck is a minimal language with just
eight instructions — > < + - . , [ ] — operating on a byte tape and a data
pointer. This demo compiles the canonical "Hello World!" program to an opcode stream and runs it
through a threaded-code interpreter: instead of a switch, each opcode is an
index into a table of label addresses, and dispatch is a direct computed jump:
static const void *handlers[] = { &&h_incp, &&h_inc, ... };
#define NEXT() goto *handlers[prog[pc++]] // ← the computed goto
h_incp: dp++; NEXT(); // '>'
h_inc: tape[dp]++; NEXT(); // '+'
h_out: emit(tape[dp]); NEXT(); // '.'
h_jz: if(!tape[dp]) pc = match+1; NEXT(); // '[' On screen you see the compiled program with the current instruction (the "tape head") highlighted in cyan, the 64-cell tape as an ASCII heat grid with the data pointer marked, the output marquee filling in, and live step / pointer counters. Every operator except input runs, driving thousands of computed-goto dispatches per pass.
Compiler stress-test
| Item | What it exercises |
|---|---|
| goto *handlers[op] | GNU C labels-as-values dispatch — the opcode indexes a table of &&label addresses and control jumps straight there. This is the "threaded code" interpreter, the fastest dispatch shape. |
| indirect jmp | On the 65816 the computed goto lowers to a real indexed-indirect jump — jmp ($addr,x) (opcode $7C) — not a switch/branch chain. That indirect jump is the whole point of the demo. |
| vs. a switch VM | A dense switch (like the Turtle VM #29a) lowers to a contiguous jump table; a computed goto threads per-op through an address table. Different codegen path, different bugs hide there. |
| rep/sep | Mode-switch brackets mark 16-bit accumulator sections under +mos-a16 (the pc/dp pointer arithmetic). Pure control flow otherwise — no 32-bit multiply or divide anywhere. |
This is the corner a switch-based VM does not open. The whole reason the
demo exists is to exercise the backend's lowering of LLVM indirectbr /
blockaddress on the 65816 — and it does so correctly: the computed goto compiles to
a genuine indexed-indirect jump across the default 8-bit, +mos-a16 and
+mos-xy16 backends, all -verify-machineinstrs clean.
Written in C with the llvm-mos 65816 toolchain and verified against bsnes-jg
(and MAME where the SPC700 IPL is present). Hit Verify fidelity to reproduce the build
gate's WRAM assert (gate CRC 0x9954 — a rotate-XOR fold of the program's output +
final tape) live in this tab. No far pointers — the same source passes every way: host ==
default == +mos-a16 == +mos-xy16 == bsnes-jg.