Move the loader onto the 68000, and find 5,920 bytes nobody counted
src/player/load.i expands both codebooks to word-per-pixel form and packs the palette to GGGGGRRRRRBBBBBI out of the RAW container header, byte-exact against tools/bench/dlxload.py on both CPU cores. The palette half is gated on words read back out of the palette registers at $E82000, so "the words reached the hardware" is part of what passes. ROADMAP P1 is done; P2's encoder half (a reserved black entry, 23.4) is not, and is a re-encode rather than an edit. A scene change costs 18.96 ms of 68000 time, 22.8% of one 12 fps frame; boot costs 24.70 ms. The scratch tables describe the CRTC, not the scene, so pal_tables is a separate entry point built once at boot -- 5.29 ms off every scene change. The one that moves something: the scene header is 5,920 B that no rate table in this tree included, because it belongs to no frame record. In FINDINGS 51.3's currency it is divided by the surplus pipe - wire, so it is hypersensitive: 138 ms of extra refill climb at 488 KB/s and 1.099 s at 451.4 KB/s, for the same bytes. tools/analysis/22_scene_load.py prices it across explicit rates. Recorded as open: the two CPU cores agree to <3% on every stage but the table build, where they differ by 16.4%. px68k's C68K charges a flat 50 clocks for MULU/MULS (c68kmacro.h:1869) where the 68000 charges 38+2n, which explains 4,608 of the 8,703 clock gap. 4,095 clocks are unexplained. Nothing else in src/player/ multiplies, so no figure in FINDINGS 24-52 is affected. decode.s and stream.s are untouched; decode.bin is still 1,296 B at the same MD5. check.sh gains a stage that gates byte-exactness on both cores and deliberately does not gate the cycle counts -- MAME's clock is 1/55.46 s and a wall timing would make the green light host-sensitive. Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
+27
-11
@@ -1,6 +1,7 @@
|
||||
# Roadmap — remaining work to a completion target
|
||||
|
||||
Written end of session 19 (2026-08-24), against a tree that is ALL GREEN.
|
||||
Amended end of session 21: P1 done, P2 half done (FINDINGS 53).
|
||||
|
||||
**THE COMPLETION TARGET IS M3, THE VERTICAL SLICE** (USER DECISION): one scene
|
||||
tree — a decision point, two outcomes, a death clip — with audio, streaming from
|
||||
@@ -96,17 +97,32 @@ out of a bounded ring fed by a host file on a paced clock. Neither is a player.
|
||||
**Exit criterion: boots from a real SCSI volume on a stock 2 MB X68000, plays
|
||||
one scene at 12 fps from disc, no host-file pipe, no Lua in the loop. Silent.**
|
||||
|
||||
**P1. Codebook expansion on the 68000.** `dlxload.py:19` expands CB1 to 32 B per
|
||||
entry and CB4 to 8 B, host-side, because at the time it was a load-time cost that
|
||||
would have flattered or damned the inner loop. The player must do it: **8 KB +
|
||||
2 KB per scene**. Note where that lands — *at a scene change, when the ring is
|
||||
empty because of the seek*. It compounds with 51.3 and should be priced against
|
||||
the refill climb, not treated as free setup.
|
||||
~~**P1. Codebook expansion on the 68000.**~~ **DONE, session 21 — FINDINGS 53.**
|
||||
`src/player/load.i` expands both codebooks out of the raw container header,
|
||||
byte-exact against `dlxload.py` on both CPU cores. **9.26 ms**, and it was
|
||||
priced where it lands rather than treated as free setup: the scene header is
|
||||
**5,920 B that no rate table in this tree counted**, and in the currency of
|
||||
51.3 — accumulated slack — those bytes lengthen the refill climb by 138 ms at
|
||||
488 KB/s and by **1.099 s at 451.4 KB/s**, because the surplus they are divided
|
||||
by goes to zero. The whole fixed cost of a scene change is about a third of one
|
||||
frame slot; what makes a branch point expensive is still the seek and the climb.
|
||||
Shipping the codebooks pre-expanded was considered and refused: it trades
|
||||
9.26 ms of CPU for 5,120 more header bytes, which is a wash in milliseconds and
|
||||
not a wash in kind (53.6).
|
||||
|
||||
**P2. Palette packing on the 68000.** The encoder still emits RGB888; the X68000
|
||||
word packing is Lua-side. Whatever writes real palette words must pick `I` per
|
||||
entry by minimum squared error (**1.96 dB**, FINDINGS 23.3) and reserve index 0
|
||||
as black with `I = 0` (23.4).
|
||||
**P2. Palette packing on the 68000. HALF DONE, session 21 — FINDINGS 53.**
|
||||
~~The encoder still emits RGB888; the X68000 word packing is Lua-side.~~ The
|
||||
packing is on the 68000: `pal_pack` writes 256 words straight into `$E82000`
|
||||
with `I` chosen per entry by minimum squared error (**1.96 dB**, 23.3), gated on
|
||||
the words read back **out of the palette registers**. 9.70 ms per scene, plus
|
||||
5.29 ms of scene-independent table build hoisted to boot (53.3).
|
||||
|
||||
**What is left is the other half of the sentence: reserve index 0 as black with
|
||||
`I = 0` (23.4).** That is ENCODER-side, it changes the container, and it moves
|
||||
every constant fitted to the gate container, so it is a re-encode plus a
|
||||
re-measurement rather than an edit. Until then the letterbox gets the palette's
|
||||
closest thing to black (index 255 on the gate container); `load.i` reports
|
||||
whichever index that is and needs no change when it becomes 0.
|
||||
|
||||
**P3. A real frame clock.** `stream.s` has `PACE`/`PACEON` (`$18034`/`$18038`)
|
||||
but the 12 fps tick comes from the Lua producer. Needs MFP timer or VBL. Keep
|
||||
@@ -229,7 +245,7 @@ Listed for completeness; past M3 these are scope, not risk.
|
||||
B1 seek+rate ─┐
|
||||
B3 DTYP ──────┴─> P4 transport ─┐
|
||||
├─> M2 ─> M3 (COMPLETION TARGET) ─> M4
|
||||
P1 P2 P3 P5 P7 ─────────────────┘ ^
|
||||
P1 P2(half) P3 P5 P7 ───────────┘ ^
|
||||
│
|
||||
P6 (bus cost DONE, 52) ──────────────────┤
|
||||
G1 scene graph (fetch, do early) ─────────┘
|
||||
|
||||
Reference in New Issue
Block a user