Polygon Scanline Fill

Rendering & Graphics

A filled star tumbles and morphs its point count while a scanline rasteriser fills it — running on a Super Nintendo. The catch: the per-scanline edge-crossing table is a C99 variable-length array (int16_t xs[nv]), a runtime-sized stack frame (alloca/VLA). Every other demo here has a fixed-size frame; this one makes the soft-stack target adjust its stack pointer at run time. 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 tumbles and morphs on its own.

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

What it is

To fill a polygon, you walk down the screen one scanline at a time; at each line you find where the polygon's edges cross it, sort those x-positions, and paint the spans between alternating pairs (the "even-odd" rule). The number of crossings depends on how many sides the polygon has — so this demo sizes the crossing table with a variable-length array, allocated on the stack at the run-time width:

// nv is only known at run time (it comes from the star's point count):
uint16_t nv = 2 * npoints;          // 6 .. 16, changes as the star morphs
int16_t xs[nv];                     // ← C99 VLA: a RUNTIME-sized stack frame
for (y = ymin; y <= ymax; y++) {
    nx = scan_crossings(px, py, nv, y, xs);   // edge x-crossings into xs[]
    for (k = 0; k+1 < nx; k += 2)             // even-odd: fill between pairs
        fill_span(xs[k], xs[k+1], y);
}

Because nv isn't known until run time (the star morphs from 3 to 8 points), the compiler can't bake the frame size in — it must emit a runtime stack-pointer adjustment. On a target that synthesizes its stack in software, that's the interesting part. It works, and the filled area stays bit-exact across every codegen mode.

Compiler stress-test

ItemWhat it exercises
int16_t xs[nv]A C99 variable-length array whose size is a run-time value. Every other demo in the battery has a fixed-size stack frame; this one forces the soft-stack target to do a runtime stack-pointer adjustment (the alloca/VLA path).
soft-stack targetllvm-mos synthesizes a software stack in zero page — a runtime frame adjustment is not obviously supported. Idea #36 anticipated "a gap is a finding." It is not a gap: the VLA lowers correctly.
even-odd scanline fillFor each scanline, the x-crossings of the polygon edges are collected into the VLA, sorted, and filled in pairs — the classic polygon rasteriser, with a runtime-sized crossing table.
__divsi3 / __mulsi3Each edge crossing is a signed 32-bit divide; each vertex is placed with a 32-bit multiply off a sine LUT. Divide-and-multiply heavy, on top of the VLA frame.

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 CRC 0x8ED9 — a rotate-XOR fold of the filled area over 16 morphing frames) live in this tab. No far pointers — the same source passes every way: host == default == +mos-a16 == +mos-xy16 == bsnes-jg, -verify clean.