Memmove Scroll Slabs

Algorithms

Two counter-scrolling slab bands on a Super Nintendo, driven by overlapping memmove calls. The upper band uses memmove(upper+row1, upper+row0, N) where dst > src — forcing the descending (high-to-low) copy path in llvm-mos's G_MEMMOVE legalizer. The lower band uses the ascending path. Compiler stress-test #79, exercising both sub-paths of the overlapping-memmove legalizer for the first time.

loading core…

Self-running — upper band scrolls down, lower band scrolls up, slabs meet at centre.

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

What it is

The 128×128 canvas is split into an upper 8-row band (scrolling down) and a lower 8-row band (scrolling up). Each frame a fresh row of coloured tiles is injected at the scroll boundary and the rest of the band is shifted by one row via memmove. Because the source and destination windows overlap by 7 rows, the copy must go in the right direction — ascending or descending — or the image smears and the gate CRC diverges.

// Upper 8 rows scroll DOWN (descending memmove: dst > src, overlapping):
memmove(&upper[1][0], &upper[0][0], 7 * 16);   // shift rows 0-6 → 1-7
upper[0][col] = new_colour(seed, col);          // insert fresh top row

// Lower 8 rows scroll UP (ascending memmove: dst < src, overlapping):
memmove(&lower[0][0], &lower[1][0], 7 * 16);   // shift rows 1-7 → 0-6
lower[7][col] = new_colour(seed, col);          // insert fresh bottom row

// Both copies OVERLAP (dst and src share 6 rows).
// Wrong direction on either call smears the image and diverges the CRC.

Compiler stress-test

ItemWhat it exercises
G_MEMMOVE descending pathWhen dst > src and the regions overlap (as in memmove(&upper[1], &upper[0], 7×16)), copying low-to-high would corrupt the source data before reading it. The MOS legalizer detects this via compareOperandLocations (:3145–3152, returns −1) and sets Descending=true, adjusting the copy start offset (:3231/:3247). A wrong direction would copy the first row over itself, leaving all 7 rows unchanged.
G_MEMMOVE ascending pathWhen dst < src (memmove(&lower[0], &lower[1], 7×16)), copying high-to-low is safe. The legalizer takes the Ascending path. Both sub-paths of the same legalizer rule at :422 are exercised — prior demos #23 (lsystem) and #49 (lzdec) only used non-overlapping copies or byte-by-byte loops, never emitting G_MEMMOVE.
SDK memmoveThe underlying call goes to the llvm-mos-sdk's memmove weak implementation at mos-platform/common/c/mem.c:15. On the 65816 it resolves the direction at runtime from the pointer comparison, using indexed copies in the correct order. A bug in the SDK's direction detection would produce mismatched output on the SNES vs the host.
differential gate16 scroll steps × 256 bytes per step × 2 folds. The CRC uses an additive rotate-shift fold (not XOR) because the injected rows have a period-4 byte pattern whose XOR sums to zero — an XOR-based fold would collapse to zero regardless of the data. The additive fold accumulates correctly and diverges on any wrong copy direction.

Written in C with the llvm-mos 65816 toolchain and verified against bsnes-jg and MAME. Hit Verify fidelity to reproduce the build gate's WRAM assert (gate CRC 0x72A7 — 16 steps × 256 bytes, additive fold) live in this tab. No far pointers — host == default == +mos-a16 == +mos-xy16, -verify clean.