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).

loading core…

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.

ItemWhat it exercises
No bitfields anywhere elseEvery 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 extractBuilding 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 unitC 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 memoryThe 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 readoutThe 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.