Overlap-Move Mosaic

Algorithms & Data

A 16×24 = 384-byte mosaic scrolled in place by four overlapping memmove calls every step — two descending (dst>src), two ascending (dst<src), each over more than 256 bytes so the copy must use a 16-bit index register. This is compiler stress-test #93: it re-runs the exact scenario a real llvm-mos bug once corrupted — an in-place memmove under 16-bit-index mode — larger and in both directions, to confirm the fix holds.

loading core…

Self-running — the mosaic shears down-right, then up-left, as the memmoves alternate.

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

What it is

memmove copies bytes safely even when the source and destination ranges overlap — the case plain memcpy is not allowed to handle. The compiler proves the direction: if the destination is above the source it copies high-to-low, otherwise low-to-high. This demo exercises both directions, on a buffer deliberately sized past 256 bytes so each copy is driven by a 16-bit index register.

// a 16×24 = 384-byte mosaic, scrolled IN PLACE (dst and src overlap)
uint8_t *flat = &cell[0][0];

// descending overlap (dst > src): scroll down / right
memmove(&cell[1][0], &cell[0][0], 23*16);   // 368 bytes
memmove(flat + 1,    flat,        384 - 1);  // 383 bytes

// ascending overlap (dst < src): scroll up / left
memmove(&cell[0][0], &cell[1][0], 23*16);   // 368 bytes
memmove(flat,        flat + 2,    384 - 2);  // 382 bytes

// every count > 256, so the copy must index with a 16-bit register.

Each step alternates a descending pair (scroll down + right) and an ascending pair (scroll up + left), then folds all 384 bytes into a rolling CRC. The picture is the proof: any corruption of the 16-bit index would both streak the mosaic and change the CRC.

Compiler stress-test #93 — Round 6: hardening the fixes

ItemWhat it exercises
in-place overlapping memmovememmove (unlike memcpy) is defined even when source and destination overlap — it must pick a copy direction so it never clobbers bytes it has not read yet. dst>src copies high-to-low (descending); dst<src copies low-to-high (ascending). This demo drives both directions, four memmoves per step, over the same 384-byte buffer.
the 256-byte boundary (why >256 matters)The 65816 addresses memory with 8-bit or 16-bit index registers. A copy of ≤256 bytes fits an 8-bit index; a copy of more than 256 bytes needs a 16-bit index. This buffer is 384 bytes precisely so every memmove is forced onto the 16-bit-index path — the exact path a real compiler bug once corrupted.
the bug this guards (#23, patch 0002)When the accumulator/index registers run 16-bit (the +mos-xy16 mode), an earlier version of the compiler could place a "switch index back to 8-bit" instruction (sep #$10) between writing a 16-bit index and reading it — zeroing the index’s high byte and corrupting an in-place memmove. The fix reloads the index after switching width. This demo re-runs that exact scenario, larger and in both directions, and confirms the fix holds.
result: bit-exact in every modeA memmove of a byte array is a pure data shuffle — identical on the host and on the SNES. The gate folds all 384 bytes each step into a CRC; host == default == +mos-a16 == +mos-xy16 on both MAME and bsnes-jg, -verify-machineinstrs clean. A dropped index high byte would streak the mosaic and change the CRC. It does not — the fix is correct and now permanently regression-guarded.
the visualA 16×16 window of the mosaic is drawn as solid colour tiles. On descending steps the pattern shears down and right; on ascending steps it shears up and left. Fresh coloured rows/bytes are injected at the exposed edges, so the diagonal bands keep flowing.

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 0xA990 — a fold of 16 steps × 384 bytes) live in this tab. No far pointers — host == default == +mos-a16 == +mos-xy16, -verify clean.