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.

loading core…

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

ItemWhat it exercises
Bit-packed gridThe 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 sumsEach 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 shiftsLeft/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 swapA 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.