Bignum Factorial
Big Numbers n! computed live on a Super Nintendo using schoolbook carry-propagation multiply
across a 700-element base-10000 bignum array. Each step multiplies every cell by n,
propagates the carry, and renders the growing decimal number — up to 2800 digits — filling
27 rows of BG3 tilemap. Runs automatically; no joypad needed. Written in C and compiled with
the llvm-mos-based 65816 toolchain
(+mos-a16, 16-bit accumulator mode).
Self-running demo — the factorial number grows automatically.
Click the screen to focus, then watch. Tab away and it pauses.
What it is
The demo computes n! incrementally: at each step it multiplies the bignum by
n using the schoolbook carry-propagation algorithm, then re-renders the full decimal
expansion on screen. The number is stored in a 700-element little-endian base-10000 array —
each uint16 cell holds 4 decimal digits:
for i in 0..nelem:
prod = d[i] × n + carry // __mulsi3 (u16×u16→u32)
d[i] = prod mod 10000 //
carry = prod div 10000 // __udivmodsi4 (both quot+rem in same expr)
while carry:
d[nelem++] = carry mod 10000
carry = carry div 10000 1000! has 2568 decimal digits — 642 base-10000 elements — and fills roughly 27 rows of the 256×224 SNES screen. The HUD row at the bottom shows the current n and digit count.
Compiler stress-test
| Item | What it exercises |
|---|---|
| Algorithm | Schoolbook carry-propagation multiply across a 700-element uint16 array in base 10000 — one multiply + combined divide+mod per element per factorial step |
| Representation | Little-endian base-10000 bignum: each uint16 cell holds 4 decimal digits 0–9999; 700 cells cover up to 2800 decimal digits (1000! ≈ 2568 digits) |
| __mulsi3 | 32×32→32 multiply called once per inner-loop iteration (d[i] × n); emitted because both operands widen to uint32 |
| __udivmodsi4 | Combined 32÷32 divide+mod; clang emits this single libcall (not separate udivsi3/umodsi3) when both quotient and remainder of the same division are live in the same expression |
| rep/sep | Mode-switch brackets mark 16-bit accumulator sections under +mos-a16; 14 occurrences in the inner loop |
The algorithm is a portable C header (examples/65816/factorial.h) linked into
the SNES ROM and separately into a host oracle. The gate asserts corpus_result
— a 16-bit rotate-XOR CRC of the 50! digit array — is identical on host, default 8-bit SNES,
+mos-a16, and +mos-xy16: a five-way differential.
Written in C with the llvm-mos 65816 toolchain and verified against two
emulators (MAME + bsnes-jg). Hit Verify fidelity to reproduce the build gate's WRAM
assert (gate CRC 0x772F, 50!) live in this tab. No far pointers — the same
binary passes five ways: host == default == +mos-a16 == +mos-xy16
== bsnes-jg.