Soft-Float Mandelbrot
FractalsThe 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, then play. (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.