Put the transport on the 68000, and find PIO costs 87 clocks a byte

ROADMAP P4b. src/player/xfer.i answers src/player/ring.i's XF_* mailbox with a
real READ(10) to a real MB89352 in place of tools/bench/stream.lua's modelled
transport: 120 records, 4,488,588 B, pixel-exact out of a 256 KB ring, with a
real mid-stream seek in a second pass. The tiling is the SAME 18 wraps and
14.7 KB mean hole that 49.4's host producer and 55.4's modelled transport
produced -- a third transport, same placement, which is the assertion that
ring.i could not tell which side of the seam answered it.

What it costs is the finding. tools/bench/xfer_cost.sh subtracts the same 120
frames run twice and gets 87.28 clocks per delivered byte, against the 68000's
own cycle table for the loop, which says 87.15 -- 0.2% apart, so the cost is the
instruction stream and not MAME's device model, and it is the first number this
rig has produced that survives leaving the emulator. That is 391.8% of a 12 fps
frame; the machine's own V-DISP clock agrees from the other end at 2.57 fps.
Against the ladder, W=5 held is 22.4% of a frame and W=19 is 85.3%, so P4a is
worth 4.6x the worst DMA configuration in this tree and 17.5x the best -- where
before this session it was worth 9 against 19. W itself did not move by a clock.

"UNDERRUNS: 0/120" is vacuous with a synchronous transport, and stream.lua now
prints that argument next to the zero: a frame cannot start before its record
has landed because the decoder IS the transport. The counter that means
something is NO IDLE, 119/120 with a worst overrun of 441 whole ticks. Same
class of error as 49.7.2's free-running ring passing at 48 KB.

58.3: a record is not a sector -- 117 of 120 start part way into one, and
reading whole sectors into the ring corrupts the neighbours rather than wasting
bytes (49.2, no bounds check). scsi.i reads the covering sectors and stores only
the window, which is free in PIO and stops being free the moment P4a succeeds.
tools/analysis/26_sector_align.py prices the three ways out and sector-aligned
records win on both axes: +0.43% wire and zero clocks, against +1.34% and a
bounce copy at +5 clk/B. ROADMAP now carries a four-item re-encode bundle and
P4a should be attempted against a sector-aligned container.

check.sh gains two stages and was ALL GREEN before and after. decode.bin is
unchanged at 1,296 B and the same MD5.

Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
prosolis
2026-08-24 23:15:53 -07:00
parent e935d8661c
commit 5921fab118
15 changed files with 1221 additions and 48 deletions
+28
View File
@@ -126,6 +126,34 @@ at 488 KB/s a one-deep request queue gives away **6.8% of the pipe and underruns
59 of 120 frames**, a two-deep one gives away 3.4% and underruns none — on a
container whose whole surplus over the wire is 8.7% (FINDINGS 55).
**The player runs off a real disc now, and PIO costs 87 clocks a byte.**
`src/player/xfer.i` answers the ring's request mailbox with a real READ(10) to a
real MB89352 instead of a host moving bytes at a modelled rate: 120 records,
4,488,588 B, **pixel-exact out of a 256 KB ring**, with a real mid-stream seek in
a second pass, and the **same 18 wraps** three different transports have now
produced. What it costs is the finding. Subtracting the same 120 frames run
twice gives **87.28 clocks per delivered byte**, and the 68000's own cycle table
for that loop says **87.15** — 0.2% apart, so the cost is the instruction stream
rather than the emulator's device model, and it is the first number this rig has
produced that a real board would also pay. At this container's mean record that
is **391.8% of a 12 fps frame**; the machine's own V-DISP clock agrees from the
other end at **2.57 fps**. Against the W ladder — 22.4% of a frame at 5 clocks a
byte, 85.3% at 19 — **the CPU doing the work itself is 4.6x the worst DMA
configuration this project has found and 17.5x the best.** Getting the DMAC to
hold the bus is no longer worth 9 against 19; it is worth 87 against either, and
it is the only thing left before a player (FINDINGS 58).
**A record is not a sector, and the cheapest fix is a re-encode.** 117 of 120
records start part way into a 512 B block, and reading whole blocks into the
ring corrupts the neighbouring records rather than merely wasting bytes — the
block loop reads with no bounds check. PIO absorbs this for free by simply not
storing the bytes outside the window, which is a property that disappears the
moment a DMA channel takes over. Priced three ways: windowed PIO is +1.34% on
the wire and cannot be done by a channel at all; a bounce buffer is +1.34% and
**+5 clocks on every delivered byte**, 22.4% of a frame; sector-aligning records
in the container is **+0.43% and zero clocks**. The last wins on both axes and
joins the re-encode bundle (FINDINGS 58.3).
**The scene graph is in, and the worst gap between two decision points is
zero.** `tools/import/scenegraph.py` imports the arcade scene graph — 40 scenes,
516 sequences, 906 input windows — and 5.4% of the game's 612 branch transitions