Gap-Buffer Rope Editor

Algorithms & Data

A text gap buffer — the data structure inside many real editors — whose cursor moves memmove the text across the gap, both directions and more than 256 bytes at a time. This is compiler stress-test #96, the final Cluster-A demo: it runs the exact scenario a real llvm-mos bug once corrupted — a 16-bit-indexed copy under +mos-xy16 — at scale, as a working editor primitive rather than a synthetic loop.

loading core…

Self-running — text blocks slide across the gap as the cursor jumps and edits.

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

What it is

In a gap buffer the characters live on either side of a movable empty region at the cursor. Typing and deleting only touch the gap’s edge and are cheap. The one costly operation is a long cursor jump: all the text between the old and new position must be copied across the gap with memmove, which handles the overlapping ranges safely.

// a gap buffer: text lives on either side of a movable "gap"
//   text = buf[0, gap_start)  ++  buf[gap_end, N)
uint8_t buf[576]; uint16_t gap_start, gap_end;

insert(ch): buf[gap_start++] = ch;      // typing fills the gap
delete():   gap_start--;                // backspace grows the gap

move(pos):  // move the cursor — memmove text across the gap, EITHER way
  if (pos < gap_start) {                 // cursor left  -> shift text right
    n = gap_start - pos;
    memmove(&buf[gap_end - n], &buf[pos], n);   // n can exceed 256 bytes
  } else {                               // cursor right -> shift text left
    n = pos - gap_start;
    memmove(&buf[gap_start], &buf[gap_end], n);
  }

The buffer holds 576 bytes with ~480 characters typed in, so a cursor jump copies more than 256 bytes — a 16-bit-indexed move. A scripted edit stream drives jumps in both directions and folds all 576 bytes into a rolling CRC each step. The picture is the proof: a wrong index would smear the text and change the CRC.

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

ItemWhat it exercises
the gap bufferA gap buffer is how many real text editors store a line: the characters sit on either side of an empty “gap” at the cursor. Typing writes into the gap; backspace enlarges it. The only expensive operation is moving the cursor a long way — that memmoves all the text between the old and new cursor across the gap.
memmove, both directions, at scaleMoving the cursor left shifts text toward the high end; moving it right shifts text toward the low end — so both memmove copy directions fire. With a 576-byte buffer holding ~480 characters, a cursor jump moves more than 256 bytes, which forces the copy onto the 65816’s 16-bit-index path.
the 256-byte boundaryThe 65816 addresses memory with 8-bit or 16-bit index registers. A copy of ≤256 bytes fits an 8-bit index; more than that needs a 16-bit index. The buffer is sized past 256 so every cursor-move memmove is driven by a 16-bit index — the exact path a real compiler bug once corrupted.
the bug this guards (#23, patch 0002)Under 16-bit-index mode (+mos-xy16), an earlier compiler could slip a “switch index to 8-bit” instruction (sep #$10) between writing a 16-bit index and reading it, zeroing the high byte and mis-addressing the copy. The fix reloads the index after each width switch. This is the fourth and final Cluster-A demo guarding it — after a synthetic memmove, an indirect-pointer reversal, and an indexed store, this one exercises memmove as a real editor primitive, at scale, in both directions.
result: bit-exact in every modeA gap buffer is a pure byte shuffle — identical on the host and on the SNES. A scripted edit stream (type / delete / jump) runs, and the gate folds all 576 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 smear the text and change the CRC. It does not — the fix holds, now permanently regression-guarded.
the visualThe 576-byte buffer is drawn as a 16×16 window of colour cells (colour by byte class). As the cursor jumps around and text is typed and deleted, the coloured text blocks slide across the gap — the memmoves made visible.

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