First 68000 instructions in this project to draw a pixel. Everything before
this was GVRAM filled from Lua, which costs zero 68000 cycles, so the blit
figure the whole CPU budget rests on had never been validated.
Four variants of a full-frame 256x192 paint, timed in MAME and each also
hand-derived from the MC68000 timing tables beforehand; the two agree to
0.006-0.43%, which is what makes the result trustworthy after this project's
history of false-good measurements.
V1 movem.l blit from a word-expanded RAM frame 446,286 cyc 53.6%
V2 naive move.b/move.w per pixel 1,284,174 cyc 154.1%
V3 write-only floor, no source read 225,789 cyc 27.1%
V4 same writes in 4x4 block order 637,971 cyc 76.6%
Scope: MAME's gvram_w/gvram_r carry no timing at all, so these are instruction
cycles against zero-wait-state memory -- a floor, not a hardware prediction.
V1's output snapshots pixel-exact through verify_frame256.py, closing
FINDINGS 23.5. The V1/V3 gap shows reading the source frame is exactly half
the cost, which makes the architecture question live: decode-direct-to-GVRAM
needs no RAM reference frame and scales with the non-SKIP block fraction,
crossing compose-then-blit at 70% of blocks changed. That fraction is now the
top priority and is already a by-product of vq_hybrid.py's mode decision.
Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6