Truchet · Packed Bitfields
Ciphers & Bit Tricks
The classic Truchet /
10 PRINT diagonal maze, computed live on a Super
Nintendo — but every cell is a 16-bit C bitfield
struct: orient:1, style:1, hue:3, phase:2, mark:1, energy:4 packed into one word.
A colour wave ripples through the labyrinth, and every redraw reads the per-cell fields back out —
so the compiler’s bitfield insert/extract (mask, and, ora,
shift — pure ALU, no libcalls) runs thousands of times a frame. The packing is
bit-for-bit identical host vs console. Runs automatically; no joypad needed. Written in C
and compiled with the llvm-mos-based 65816
toolchain (+mos-a16).
Self-running demo — a colour wave pulses through the diagonal maze. (Boot holds a few seconds while the bitfield gate folds.)
Click the screen to focus, then watch. Tab away and it pauses.
What it is
A grid of diagonal segments — each cell a \ or / — forms an interlocking
labyrinth (the one-liner 10 PRINT CHR$(205.5+RND(1)); : GOTO 10 made on a C64). Here
each cell’s state lives in a packed bitfield, and a decaying wave field drives the colour:
// struct Cell — six fields packed into ONE 16-bit word: uint16_t orient:1, style:1, hue:3, phase:2, mark:1, energy:4; c.energy = ne; // INSERT: word = (word & ~MASK) | (ne << SHIFT) e = c.energy; // EXTRACT: e = (word >> SHIFT) & MASK // ^ on the 65816: lda / and / ora / asl … no libcall
Declaring the fields uint16_t : n (not unsigned : n) is load-bearing:
unsigned is 32-bit on the host but 16-bit on the 65816, so it would pack into a
different-width unit. The 16-bit type makes the layout identical (sizeof(Cell) == 2),
and the differential gate folds the extracted field values — so any disagreement is a real
insert/extract miscompile, never a legal layout quirk.
Compiler stress-test #29b — packed bitfields
A Round 2 demo — each targets a codegen corner the first twenty never execute. Here it is bitfield insert/extract: the mask/shift/merge the compiler emits to poke a few bits inside a word, including the read-modify-write that must preserve the neighbouring fields.
| Item | What it exercises |
|---|---|
| No bitfields anywhere else | Every prior demo uses whole int/uint variables; this one packs SIX fields into one 16-bit struct (orient:1, style:1, hue:3, phase:2, mark:1, energy:4). Reading or writing a field is the compiler’s mask-and-merge — load the word, AND off the field, OR in the new bits, shift. Verified: and=13, ora=8, shift=32, and ZERO libcalls (bitfields are pure ALU) |
| Insert vs extract | Building a cell INSERTS six fields from a PRNG word; the wave step EXTRACTS each cell and its four neighbours, then inserts updated energy/mark/phase — a read-modify-write that must NOT clobber the adjacent orient/hue/style fields. That cross-field clobber is the classic bitfield miscompile |
| 16-bit storage unit | C packs bitfields into the DECLARED type, and `unsigned` is 32-bit on the host but 16-bit on the 65816 — different unit, different layout. Declaring the fields `uint16_t : n` gives a 16-bit unit on both (sizeof Cell == 2), so the struct bytes and the field values agree across platforms |
| The differential folds VALUES, not memory | The gate folds the EXTRACTED field values into a CRC, not the raw struct bytes — so a divergence means a real insert/extract miscompile, never a legal packing difference. host == default-8bit == +mos-a16 == +mos-xy16 == gate CRC 0xB3E6 |
| The maze is the readout | The classic 10-PRINT / Truchet labyrinth: each cell’s `orient` bit picks a / or diagonal and its `energy` field picks the colour. A pulse wave ripples through, so every redraw re-reads thousands of packed bitfields |
The kernel is a portable C header (examples/65816/truchet.h) linked into the SNES ROM,
a host oracle, and the corpus differential slice alike. The gate seeds a 16×14 grid, runs 24 wave
steps, and folds every cell’s extracted fields into corpus_result — identical on
host and on the console across every target codegen mode.
Written in C with the llvm-mos 65816 toolchain. Hit Verify fidelity to
reproduce the build gate’s WRAM assert (gate CRC 0xB3E6) live in this tab. The
disasm gate proves the shape: and = 13, ora = 8, shift = 32,
libcalls = 0 — bitfield insert/extract in native-16 mode.