Pool Fountain

Games & Classics

A sparking fountain on a Super Nintendo, drawn by a hand-rolled free-list pool allocator — no malloc. Forty-eight fixed particle slots are recycled through a singly-linked LIFO free list threaded through the slots themselves: a birth pops the free head, a death pushes the slot back. Continuous alloc/free churn every frame. Runs automatically; no joypad needed. Written in C and compiled with the llvm-mos-based 65816 toolchain (+mos-a16, 16-bit accumulator mode).

loading core…

Self-running demo — sparks are born and die as the pool recycles slots.

Click the screen to focus, then watch. Tab away and it pauses.

What it is

A pool allocator hands out slots from a fixed array. To know which slots are free without scanning, it keeps a free list: a linked stack whose links live inside the free slots. Each free slot stores, in its own next field, the index of the next free one; a single head points at the top of the stack.

alloc():  i = head;  head = slot[head].next;   // pop the free head
free(i):  slot[i].next = head;  head = i;      // push it back (LIFO)

// spawn a particle → alloc();   particle dies → free(i)

Allocation pops the head; freeing pushes the slot back on top — two pointer writes, no search, no heap. Here the pool feeds a fountain: three sparks are born per frame, gravity brings each down, and death recycles the slot for the next birth. The whole birth/death cycle stays bit-exact across every codegen mode.

Compiler stress-test

ItemWhat it exercises
free listA singly-linked stack of unused slots threaded through the slots themselves — each free slot stores the index of the next free slot in its own next field. No separate bookkeeping array, no malloc: recycling 48 fixed slots is two pointer writes.
pop / pushSpawning a particle pops the head off the free list (alloc); a particle dying pushes its slot back (free). Distinct from a bump/arena pool that only ever grows then resets — here individual slots come and go every frame.
indexed slot chaseOn the 65816 the link walk head = slot[head].next becomes an indexed struct-array load (ldy-indexed). The differential proves that recycling — across default-8-bit, +mos-a16 and +mos-xy16 — is bit-exact, with zero arithmetic libcalls (pure pointer/index math).
fountainThe pool drives a particle fountain: three births per frame from the nozzle, gravity pulling each spark down, death (life expiry or off-screen) recycling the slot. When births outrun the 48 slots the pool saturates and drops births — the exhaustion path is exercised too.

Written in C with the llvm-mos 65816 toolchain and verified against bsnes-jg (and MAME where the SPC700 IPL is present). Hit Verify fidelity to reproduce the build gate's WRAM assert (gate hash 0x2B9B — a fold of the full pool state over 100 simulated frames) live in this tab. No far pointers — the same source passes every way: host == default == +mos-a16 == +mos-xy16 == bsnes-jg, -verify clean.