The payload assembles KIND_DICT_CHUNK frames into a 32 KB dictionary at
POOL_PTR + 2 * SLOT_BYTES and learns the tile kinds:
* frame_parse.h gains FP_KIND_TILES_FULL (2), FP_KIND_TILES_DELTA (3) and
FP_KIND_DICT_CHUNK (4), exactly frame.py's KINDS table, plus the marker
cells DICT_READY/DICT_BYTES/DICT_BAD and TILES_DROP. A host test parses
the header and compares it with KINDS so the two cannot drift apart.
* dict_chunk() enforces an ascending, contiguous offset (no gap, overlap,
duplicate or reorder; a mid-entry offset only as a continuation), a chunk
that does not overrun the last entry, and no chunk after DICT_READY. A
refused chunk leaves the prefix alone. Readiness is exactly 32768 bytes
(256 whole entries), so a short upload never becomes ready.
* The dispatcher routes STRIP to the blit, DICT_CHUNK to the assembler, and
refuses TILES_FULL/TILES_DELTA without painting, counting them in
TILES_DROP (Task 4 adds the tile blit).
* A compile-time assertion refuses a POOL_BYTES that cannot hold the
dictionary; the 1987 preprocessor does not fold macro arithmetic in #if,
so it is a defined array whose negative length fails the build.
* VT1200_MODE=dict injects both tile kinds before the dictionary, 23
contiguous chunks with a duplicate offset, a gap and an overrunning tail
mixed in, and reads the stored 32 KB back byte for byte.
One PIXBLT B,XY per source row paints the whole screen: DYDX names
`stretch` rows and SPTCH = 0 makes each of them read the same packed
source row, so the vertical stretch costs no extra CPU work.
vram_b.asm carries the primitive (PSIZE=1, CONVSP/CONVDP=0x14,
PMASK=0, OFFSET=0x04000000, DPTCH=0x800, raster op 0) plus DINT/EINT
thunks; vidstream.c masks interrupts for the burst (spec 8.9), refuses
geometry the screen cannot hold, and counts an OS repaint through a
sentinel word in the unused right edge of the last screen row
(OS_TOUCH, 0x01810220).
MAME mode draw injects a 128-row frame (stretch 8 = 1024 screen rows,
ruling R3) and dumps the screen; vidstream_check.py compares all 1024
rows against the bytes actually delivered. RED: 0/1024 rows matched.
GREEN: 1024/1024, RX_OK=1, OS_TOUCH=0.
Fixes a latent harness bug found on the way: `bpclear <address>` takes
a breakpoint INDEX, so the idle-loop breakpoint was never cleared; once
a burst masked interrupts long enough, its stale action fired again and
re-entered main (a second malloc, counters back to 0).