Soft-Float Mandelbrot
Fractals
The Mandelbrot set — z² + c
iterated to escape — computed live on a Super Nintendo in IEEE-754 single-precision
float. The 65816 has no floating-point unit, so every multiply, add,
subtract, divide and compare is a software-float libcall — hundreds of instructions per
pixel. That is the whole point: the chunky image grinds in row by row because each fat pixel
really is a flurry of __mulsf3/__addsf3. And it is
bit-for-bit identical to host x86 single precision. 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 set grinds in and Mode 7 zooms it automatically. (Boot takes several seconds: the gate and the first frame are software floating point.)
Click the screen to focus, then watch. Tab away and it pauses.
What it is
The Mandelbrot set is the escape-time picture of iterating z ← z² + c from
z₀ = 0, with c the pixel coordinate, colouring each point by how fast
it flies to infinity. Every other demo in this battery does its arithmetic in fixed-point
integer; this one does it in IEEE-754 single-precision float — so on
the FPU-less 65816 the inner loop is nothing but soft-float libcalls:
z = (zr, zi) = 0.0f // IEEE-754 single precision ; c = pixel coordinate
for n in 0..maxiter:
zr2 = zr*zr ; zi2 = zi*zi // two __mulsf3
if zr2 + zi2 > 4.0f: break // __addsf3 + __gtsf2 — escaped
zi = (zr*zi) + (zr*zi) + ci // __mulsf3 + __addsf3
zr = zr2 - zi2 + cr // __subsf3 + __addsf3
// escape count n = pixel colour ; EVERY op above is a soft-float call
Each pixel grinds that loop and is far-stored into a high-WRAM buffer at
$7E2000 — memory only reachable through 24-bit addressing, the
#320 far-pointer path
(sta [dp]) — then far-loaded back into
Mode 7
character VRAM. Because a full-resolution soft-float recompute would take minutes, a
chunky 16×14 grid is ground a row at a time and painted in progressively, then the affine matrix
spins and zoom-breathes it: the grind is the proof, the hardware is the motion.
Compiler stress-test #21 — IEEE-754 soft-float
This is the first Round 2 demo — each Round-2 entry targets a codegen corner the first twenty never execute. Here it is the entire soft-float library: with no FPU, every floating-point operation lowers to a compiler-rt libcall, a code path no fixed-point demo ever runs.
| Item | What it exercises |
|---|---|
| No FPU — every op a libcall | The 65816 has no floating-point hardware, so each +, −, ×, ÷ and compare in the loop is a compiler-rt soft-float call: __mulsf3 (z·z, zr·zi), __addsf3/__subsf3 (the recurrence), __divsf3 (the window step), __gtsf2 (the |z|²>4 escape test), __floatsisf (pixel index → float). That whole library is otherwise untouched by the demo battery |
| Bit-exact differential | IEEE-754 single precision is fully specified — +, −, ×, ÷ and the comparisons are all correctly rounded — so the 65816 soft-float must equal host x86 single precision BIT FOR BIT. Verified: host == default-8bit == +mos-a16 == +mos-xy16 == gate CRC 0x4169 |
| FMA forbidden by construction | The one thing that could diverge host from target is fused multiply-add contraction (a·b+c fused on the host but two separate libcalls on the target). Every arithmetic op is its own statement, so no expression ever holds an a·b±c pattern to fuse — and baseline cc -O2 has no FMA instruction without -march anyway |
| Far high-WRAM framebuffer | The 64×56 escape image lives at $7E2000 — high WRAM reachable only by 24-bit addressing, the #320 far-store/load path (sta [dp] / lda [dp]). a16-only. A 16×14 coarse grid is 4×-upscaled into it; each fat pixel is hundreds of soft-float instructions |
| Grind is the proof | A full-resolution soft-float recompute would take minutes, so the set is ground a coarse row at a time and painted in progressively — the visible top-to-bottom crawl IS the demonstration that real software floating point is running on the console; Mode 7 then spins and zoom-breathes it |
The soft-float kernel is a portable C header (examples/65816/mandel-float.h) linked
into the SNES ROM, a host oracle, and the corpus differential slice alike. The gate folds two
zoom windows plus a bit-exact orbit witness into corpus_result. Because single
precision is fully specified, that 16-bit hash must be identical on host x86 and on the console —
and it is, across every target codegen mode (default 8-bit, +mos-a16,
+mos-xy16) — which holds only because the source forbids FMA contraction by giving
every operation its own statement.
Written in C with the llvm-mos 65816 toolchain. Hit Verify fidelity to
reproduce the build gate's WRAM assert (gate CRC 0x4169) live in this tab — the
same hash the host oracle (x86 single precision) and the cycle-accurate bsnes-jg core agree on,
bit for bit. The disasm gate proves the shape: __mulsf3 = 8,
__add/subsf3 = 12, rep/sep = 35 — IEEE-754 soft-float in native-16
mode.