Fourier Epicycles

Motion & Curves

A chain of rotating vectors — epicycles — whose tip traces a shape. Feed in the Fourier coefficients of a 5-pointed star and the chain draws the star, one stroke per frame, over the dim circle each vector sweeps. Runs automatically; no joypad needed. Written in C and compiled with the llvm-mos-based 65816 toolchain (+mos-a16, 16-bit accumulator mode).

loading core…

Self-running demo — the star draws itself, then redraws.

Click the screen to focus, then watch. Tab away and it pauses.

What it is

Any closed outline can be written as a sum of rotating vectors — its Fourier series. Each vector k has a fixed integer frequency f_k and a complex amplitude c_k; at time t the tip of the chain is:

P(t) = Σ_k  c_k · e^(i·2π·f_k·t)          // a sum of rotating vectors

per vector:  rx += re·cos(θ) − im·sin(θ)      // four 16×16→32 multiplies
             ry += re·sin(θ) + im·cos(θ)      //   (the complex multiply)
tip = (rx, ry) >> 14                          // accumulate, then scale to pixels

The coefficients here are the Discrete Fourier Transform of a 5-pointed star, computed at build time. On screen the dominant vector's circle is drawn dim as a scaffold, then the bright outline blooms as the chain sweeps one full period — the bottom HUD counts the points plotted.

Compiler stress-test #10 — the many-multiply one

Where the CORDIC demo is deliberately multiply-free, this one is multiply-dense: a sin/cos-LUT lookup and four 32-bit multiplies for every harmonic, every traced point, with no divide anywhere. A different shape of arithmetic for the compiler to get right.

ItemWhat it exercises
Four __mulsi3 per harmonicEach rotating vector is a complex multiply c·e^{iθ} = (re·cos − im·sin, re·sin + im·cos) — four 16×16→32 multiplies. The disasm gate asserts __mulsi3 is present (4 distinct products)
No divideUnlike the spirograph (gear-ratio divide) or n-body (1/r²), this is divide-free: the hot loop is multiply + accumulate only. Zero __udivsi3 / __udivmodsi4 in the gate object
Baked DFT coefficientsThe 8 vectors are the Discrete Fourier Transform of a 5-pointed star outline, generated at build time (tools/gen-epicycles-tables.py) and ordered largest-first, so a prefix is the best low-order approximation
Off-axis on purposeThe star is rotated ~23° so every coefficient has a non-zero real AND imaginary part — a vertically-symmetric star gives purely-imaginary c_k, which would fold the real-part multiplies to ×0 and halve the stress
rep/sepNative 16-bit accumulator brackets under +mos-a16; 28 occurrences across the per-harmonic sin/cos-LUT sweep and the 32-bit accumulation (the values route through the Imag16 zero-page pair)

The epicycle math is a portable C header (examples/65816/epicycles.h) linked into the SNES ROM and a host oracle alike. The gate asserts corpus_result — a 16-bit rotate-XOR CRC over the traced tip points spanning one full period — 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 0x4F6C) live in this tab — the same hash the host oracle and the cycle-accurate bsnes-jg core agree on, across the default 8-bit, the +mos-a16, and the +mos-xy16 builds.