Game of Life
Cellular Automata Conway's Game of Life
running live on a Super Nintendo. A 128×112 grid of cells evolves by the
B3/S23 rule — a cell is born with exactly 3 live neighbours and survives with 2 or 3.
A Gosper glider gun in the top-left fires a glider every 30 generations down into a
random soup that settles into still-lifes and blinkers. The cells are bit-packed (8 per
byte) and their neighbour counts are computed bit-parallel — no multiplies, no divides.
Written in C and compiled with the
llvm-mos-based 65816 toolchain
(+mos-a16, 16-bit accumulator). Runs automatically.
Self-running demo — the gun fires automatically; watch gliders cross the field.
Click the screen to focus, then watch. Tab away and it pauses.
What it is
John Conway's Game of Life (1970) is the most famous cellular automaton: a grid of cells that live or die each generation by counting their eight neighbours. From those two rules emerge gliders, oscillators, and — in Bill Gosper's glider gun — patterns that grow without bound, firing a stream of gliders forever. The whole step is one pass over the grid:
for each cell:
n = count of live cells among the 8 neighbours
next = (n == 3) or (alive and n == 2)
The trick on an 8-bit CPU is to do all eight cells of a byte at once. Each of the eight
neighbour directions is a packed byte (after a cross-byte shift to align it); the eight bytes
are summed bit-parallel into a 4-bit-per-cell count held across four bitplanes
(c0=1s … c3=8s) with a ripple of half-adders. The rule then becomes a
handful of boolean selects: count==3 is
~c3 & ~c2 & c1 & c0, count==2 is
~c3 & ~c2 & c1 & ~c0.
Why it stresses the compiler
Most demos in this battery lean on 32-bit multiply/divide libcalls. Life is the opposite — it
is deliberately multiply- and divide-free, so it exercises the parts of the backend the
arithmetic demos don't: dense boolean logic (and/eor/
ora across the SWAR full-adder), constant-amount shifts
(asl/lsr for the cross-byte neighbour alignment), indexed array
traffic over three row pointers, and the two-buffer ping-pong swap. It is the
2-D companion to the 1-D Rule 90/110 automaton.
Compiler stress-test
| Item | What it exercises |
|---|---|
| Bit-packed grid | The 128×112 field stores 8 cells per byte — a packed byte is also a 2bpp bitplane byte, so the renderer copies grid bytes straight into tile memory with no conversion |
| SWAR neighbour sums | Each byte’s eight neighbour bit-vectors are added bit-parallel into a 4-bit-per-cell count with a ripple of half-adders (carry = a & b; sum = a ^ b) — pure and/eor/ora, no per-cell branch |
| Cross-byte shifts | Left/right neighbours come from (m >> 1) | (Lbit << 7) and (m << 1) | Rbit, pulling the boundary bit from the adjacent byte — constant-1 asl/lsr + ora |
| Two-buffer swap | A classic double-buffer ping-pong (use_a ^= 1) reads one grid and writes the other, then swaps — no per-generation copy |
A separate 64×48 gate grid seeded with the glider gun runs 32 generations and folds every
output row into a CRC-16 after each one. The gate asserts corpus_result
(0xDDF1) is identical on host, default 8-bit SNES, +mos-a16, and
+mos-xy16. The Verify fidelity button above reproduces that gate live in
this tab via the same cycle-accurate bsnes-jg core used in CI.
Written in C with the llvm-mos 65816 toolchain. Hit Verify fidelity
to reproduce the build gate's WRAM assert (corpus_result = 0xDDF1) live in
this tab. No far pointers — the same logic checked four ways: host == default ==
+mos-a16 == +mos-xy16, with bsnes-jg as the in-browser
confirmation.