Color Grade
Rendering & Graphics
A film-style color grade on a Super Nintendo, taking ten coefficients — three lifts,
three gammas, a gain, a mix and a bias. That's more arguments than fit in registers, so the
extras spill onto the soft stack: the register-overflow calling convention. The
coefficients sweep over time, re-grading the scene through a range of looks. 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 scene re-grades through looks on its own.
Click the screen to focus, then watch. Tab away and it pauses.
What it is
Passing arguments to a function is cheap when they fit in registers. But a colourist's grade needs many knobs at once, and the 65816 has few registers — so the compiler has to put the overflow somewhere. It spills them onto a software stack:
int16 color_grade(v, a,b,c, d,e,f, g, h, i) { // 10 args
acc = v * g; // gain
acc += a + b + c; // three lifts
acc += d - e + f; // three gammas
acc += h * i; // mix * bias
return acc >> 3;
} The callee then fetches those spilled arguments back off its frame. Here the grade is applied to a gradient with the coefficients animating, so the picture drifts through a sequence of looks. It stays bit-exact across every codegen mode.
Compiler stress-test
| Item | What it exercises |
|---|---|
| calling convention | The 65816 passes the first few function arguments in registers/zero-page. This grade takes ten — more than fit — so the extras are pushed onto the soft stack, and the callee reads them back off its frame. That register-overflow spill is the path under test. |
| ten coefficients | A film-style color grade: three lift values, three gammas, a gain, a mix and a bias, applied to a base value. All ten are genuinely used, so none can be optimised away — the spill is real. |
| sweeping | The coefficients animate over time, so the same gradient re-grades through a range of looks — the "live scene re-grade" a colourist would scrub through. |
| bit-exact | int16 arguments, int32 accumulation, explicit widths throughout. The gate folds 100 graded values; host == default == +mos-a16 == +mos-xy16 == bsnes-jg, byte for byte. |
Written in C with the llvm-mos 65816 toolchain and verified against bsnes-jg
(and MAME where the SPC700 IPL is present). Hit Verify fidelity to reproduce the build
gate's WRAM assert (gate hash 0x783F — a fold of 100 graded values) live in this tab.
No far pointers — the same source passes every way: host == default == +mos-a16 ==
+mos-xy16 == bsnes-jg, -verify clean.