64-Bit Avalanche
Big Numbers
A hash avalanche computed live on a
Super Nintendo in 64-bit integers. The 65816 is a 16-bit machine with no 64-bit
ALU, so every hash is a pile of
multi-limb libcalls — __muldi3, __lshrdi3,
__udivdi3 — over four 16-bit words. Each cell (i, j) is output bit
j of splitmix64(seed ^ (1<<i)): flip any one input bit and ~half the
output bits flip, so a correct 64-bit implementation paints a ~50%-dense field with
no structure — this shimmering rainbow. The picture is the proof. And it is
bit-for-bit identical to host x86. Runs automatically; no joypad needed. Written in C
and compiled with the llvm-mos-based 65816
toolchain (+mos-a16).
Self-running demo — the seed crawls and the matrix re-mixes; the colours cycle down the output-bit axis. (Boot takes a moment to grind the first 64-bit hashes.)
Click the screen to focus, then watch. Tab away and it pauses.
What it is
Each column i of the image is a 64-bit splitmix64 hash of the seed with input
bit i flipped; row j shows output bit j of that hash. So the whole
64×56 grid is an avalanche matrix — the canonical way to see whether a mixer is any
good. Every Round-1 demo on this site does its arithmetic in 32-bit (or smaller); this one is all
uint64_t, which on a 16-bit CPU means software multi-limb routines:
h64_mix(x): // splitmix64 + a high-fold x += 0x9E3779B97F4A7C15 // __adddi3 x = (x ^ (x>>30)) * 0xBF58476D1CE4E5B9 // __muldi3 x = (x ^ (x>>27)) * 0x94D049BB133111EB // __muldi3 x ^= x>>31 ; x ^= x>>32 // __lshrdi3 (within-limb + whole-limb) cell(i,j) = bit j of h64_mix(seed ^ (1<<i)) // 1<<i : variable-count __ashldi3
The matrix lives in bank-0 WRAM (no far pointers) and is blitted into Mode 7 character VRAM, framed 4× to fill the screen; the hardware affine matrix drifts it while the palette cycles. The seed crawls by the golden-ratio constant each pass, so the field keeps re-mixing — every frame is a fresh batch of 64-bit multiplies and shifts.
Compiler stress-test #22 — 64-bit integers
A Round 2 demo — each Round-2 entry targets a codegen corner the first twenty never
execute. Here it is the 64-bit integer libcall family: __muldi3,
__lshrdi3/__ashldi3 (including variable counts and the whole-limb
>>32), __udivdi3, __adddi3 — none of which a
32-bit demo ever calls.
| Item | What it exercises |
|---|---|
| No 64-bit ALU — every op a libcall | The 65816 is a 16-bit machine, so each 64-bit operation is a multi-limb libcall over four 16-bit words: __muldi3 (64×64 multiply), __lshrdi3/__ashldi3 (64-bit shifts), __udivdi3 (64-bit divide), __adddi3 and 64-bit xor. The integer-width family no Round-1 demo touches |
| Bit-exact differential | 64-bit integer ops are EXACT (no rounding), so a conforming 4-limb implementation must equal host x86 (native uint64_t) bit-for-bit. Verified host == default-8bit == +mos-a16 == +mos-xy16 == gate CRC 0x27EA — any limb-carry / shift / divide miscompile diverges the hash immediately |
| Both shift regimes + variable counts | splitmix64 shifts by 30/27/31 (within a 32-bit limb) plus a >>32 (whole-limb), and the input-bit flip is 1ULL<<i with a RUNTIME count 0..63 — the variable-count __ashldi3 path. The three distinct wide-shift codegen paths a bug tends to hide in |
| The picture IS the proof | cell (i,j) = output bit j of hash64(seed ^ (1<<i)). A good mixer has the avalanche property — flipping ANY one input bit flips ~half the output bits (35 of 64 measured), so the matrix is ~50% dense with NO structure, a shimmering rainbow field. Visible structure would mean a broken 64-bit op |
| __udivdi3 — the slow one | The gate divides the 64-bit accumulator by a runtime divisor (acc / (s|1)) so the compiler must emit the full 64-bit divide routine rather than a constant-folded reciprocal — the least-exercised wide-integer libcall |
The hash kernel is a portable C header (examples/65816/avalanche.h) linked into the
SNES ROM, a host oracle, and the corpus differential slice alike. The gate chains 256 splitmix64
steps with a runtime 64-bit divide and folds the accumulator into corpus_result.
Because 64-bit integer ops are exact, that 16-bit hash must be identical on host x86 (native
uint64_t) and on the console (four 16-bit limbs) — and it is, across every
target codegen mode (default 8-bit, +mos-a16, +mos-xy16).
Written in C with the llvm-mos 65816 toolchain. Hit Verify fidelity to
reproduce the build gate's WRAM assert (gate CRC 0x27EA) live in this tab — the same
hash the host oracle and the cycle-accurate bsnes-jg core agree on, bit for bit. The disasm gate
proves the shape: __muldi3 = 2, a 64-bit shift, __udivdi3 = 1,
rep/sep = 19 — 64-bit integers in native-16 mode.