CORDIC Rotator
Motion & Curves sin, cos and atan computed on a Super Nintendo with
CORDIC — shift-and-add only, no
multiply and no divide. A rotating hand sweeps a ring of CORDIC-computed unit vectors, and a
live atan2 reads its angle straight back. 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 hand rotates automatically.
Click the screen to focus, then watch. Tab away and it pauses.
What it is
CORDIC (COordinate Rotation DIgital Computer) rotates a vector toward a target angle using
nothing but shifts, adds and a small table of pre-computed angles — the classic way to
do trigonometry on hardware with no multiplier. Each iteration rotates by ±atan(2⁻ⁱ),
which is just x ± (y >> i); after 15 unrolled steps the vector has converged
and its coordinates are the cosine and sine of the angle:
x = 1/An ; y = 0 ; z = angle // rotation mode (sin/cos)
for i in 0..15:
if z >= 0: x -= y>>i ; y += x>>i ; z -= atan[i]
else: x += y>>i ; y -= x>>i ; z += atan[i]
// (cos, sin) = (x, y) — no × and no ÷ anywhere
The hand sweeps a full circle by folding the angle into one quadrant (sign-and-swap, still no
multiply), and the bottom HUD runs the vectoring form of the same engine —
atan2 — to recover the hand's angle from its (cos, sin) and prints it
back (ANGLE 137 / ATAN2 137 OK).
Compiler stress-test #12 — the multiply-free one
Every other demo in this gallery leans on the 32-bit math libcalls (__mulsi3,
__udivmodsi4). This one is the deliberate opposite: its gate is inverted —
it asserts those libcalls are absent. That's a genuinely different shape of code for the
compiler to get right.
| Item | What it exercises |
|---|---|
| No __mulsi3 | CORDIC is shift-and-add only — there is NO multiply in the sin/cos/atan core. The disasm gate asserts ZERO __mulsi3 (every other demo asserts its presence) |
| No __divsi3 | No divide either: each microrotation is x ± (y >> i), a constant-shift add. Zero __divsi3 / __udivmodsi4 / variable-shift libcalls in the whole gate object |
| Constant-folded tables | The 15 unrolled rotations have compile-time-constant indices, so each atan(2⁻ⁱ) table entry folds into an immediate adc/sbc operand — no rodata table even appears |
| rep/sep | Native 16-bit accumulator brackets under +mos-a16; 252 occurrences across the unrolled rotation + vectoring sweeps (the values route through the Imag16 zero-page pair as int16) |
| Q2.14 fixed-point | Every angle/value is a raw int16 (value = raw / 16384), kept inside int16 the whole way so host (32-bit int) and target (16-bit int) compute bit-identical results |
The CORDIC core is a portable C header (examples/65816/cordic16.h) linked into the
SNES ROM and a host oracle alike. The gate asserts corpus_result — a 16-bit
rotate-XOR CRC over a full-circle sweep of both the rotation (sin/cos) and vectoring (atan2)
paths — is identical on host and target.
Written in C with the llvm-mos 65816 toolchain. Hit Verify fidelity to
reproduce the build gate's WRAM assert (gate CRC 0x4D41) live in this tab — the
same hash the host oracle and the cycle-accurate bsnes-jg core agree on. The disasm gate proves
the shape: __mulsi3 = __divsi3 = 0, rep/sep = 252,
adc/sbc = 143 — shift-add only, no arithmetic libcalls.