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, then play. (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.