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:
+117
@@ -1,3 +1,120 @@
|
||||
# Status & next-session handoff — end of session 26 (2026-08-24)
|
||||
|
||||
## Session 26: the player runs off the disc, and PIO costs 87 clocks a byte
|
||||
|
||||
**Green light first and last: `./tools/bench/check.sh` was ALL GREEN before any
|
||||
of this and ALL GREEN after**, plus two new stages.
|
||||
|
||||
**ROADMAP P4b is DONE. P4a is the only item left before M2. FINDINGS 58.**
|
||||
**Emulated** — MAME 0.277, `x68000 -exp1 cz6bs1 -ramsize 2M`. No real hardware
|
||||
ran.
|
||||
|
||||
**1. THE SEAM IS CLOSED.** `src/player/xfer.i` sits behind `src/player/ring.i`'s
|
||||
`XF_*` mailbox in place of `tools/bench/stream.lua`'s modelled transport. `XF_GO`
|
||||
is answered by a real READ(10) to a real MB89352; `XF_ACK` is a word the 68000
|
||||
bumps when the bytes have landed, not one a host synthesises from emulated time.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 120 records fetched by the 68000 | one READ(10) each |
|
||||
| 4,488,588 B into a 256 KB ring | **pixel-exact** over all 120 frames |
|
||||
| a real mid-stream seek, pass 2 | **pixel-exact** after it too |
|
||||
| 18 wraps, 14.7 KB mean hole | the SAME tiling as 49.4 and 55.4 |
|
||||
|
||||
The 18 wraps are the load-bearing assertion. A host producer made them in
|
||||
session 18, `ring.i` made them against a modelled transport in session 23, and a
|
||||
real SPC makes them now — **`ring.i` cannot tell which side of the mailbox
|
||||
answered it**, which is what makes this a test of the transport rather than of a
|
||||
new rig. 58.1.
|
||||
|
||||
**The change above the seam is two `bsr`s**, and the one in `ring_seek`'s
|
||||
quiet-wait is not optional: with the transport *inside* the machine, that loop
|
||||
is the only thing that can retire an outstanding request, so without it a seek
|
||||
issued with a request in flight spins forever. A host transport retired it on
|
||||
its own time. That is exactly the kind of difference the seam exists to hide,
|
||||
and it is the one it could not.
|
||||
|
||||
**2. WHAT IT COSTS, AND THE NUMBER LEAVES THE EMULATOR.** `tools/bench/
|
||||
xfer_cost.sh` runs the same 120 frames twice — same ring, same `stream.s`, same
|
||||
`ring.i` placing every record — and changes only which side answers:
|
||||
|
||||
| | emulated | per frame |
|
||||
|---|---|---|
|
||||
| decode + `ring_poll` alone | 6.7737 s | 67.7% of a 12 fps frame |
|
||||
| ...with the real transport | 45.9516 s | **459.5%** |
|
||||
| **the transport alone** | **391,779,000 clk** | **391.8%** |
|
||||
|
||||
**87.28 clocks per delivered byte.** The keep loop in `scsi.i`, priced against
|
||||
the **68000's own cycle table**, is 86 clocks, and the FIFO also carries the
|
||||
dropped window bytes of 58.3 — **87.15 predicted, 87.28 measured, 0.2% apart.**
|
||||
So this is the instruction stream and **not** MAME's SPC model: **the first
|
||||
number this rig has produced that a real board would also pay.** The machine's
|
||||
own V-DISP clock agrees from the other end — 120 frames in 560 slots of a 12 fps
|
||||
clock is **2.57 fps**. 58.2.
|
||||
|
||||
**Against the ladder** (clocks charged to the CPU per delivered byte):
|
||||
`W`=5 held is 22.4% of a frame, `W`=9 is 40.4%, `W`=19 — the IPL ROM's own disk
|
||||
channel, 52.5 — is 85.3%, and **PIO is 391.8%**. **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 one clock.
|
||||
|
||||
**3. "UNDERRUNS: 0/120" IS VACUOUS HERE, and the rig now says so in place.** A
|
||||
synchronous transport cannot underrun by construction — a frame cannot start
|
||||
before its record has landed, because the decoder *is* the transport. The
|
||||
counter that means something is `NO IDLE`: **119 of 120 frames found their slot
|
||||
already open, worst overrun 441 whole ticks.** Same class of error as 49.7.2's
|
||||
free-running ring passing at 48 KB: a rig configuration in which the failure
|
||||
being tested for cannot occur. **The binding resource flipped too** — against a
|
||||
modelled 488 KB/s pipe this container was rate-bound and the ring never filled;
|
||||
here the ring **fills, 90 refusals for space**, while the decoder starves for
|
||||
*time*.
|
||||
|
||||
**4. A RECORD IS NOT A SECTOR, and the cheapest fix is a re-encode.** 117 of 120
|
||||
records start part way into a sector. Reading whole sectors into the ring
|
||||
**corrupts the neighbouring records** — the block loop has no bounds check
|
||||
(49.2) — so `scsi.i` reads the covering sectors and stores only the window.
|
||||
That is free in PIO because the CPU is already touching every byte, **and it
|
||||
stops being free the moment P4a succeeds**: a channel writes a contiguous run
|
||||
and cannot drop bytes. `tools/analysis/26_sector_align.py` prices the three ways
|
||||
out: windowed PIO **+1.34% wire, no DMA**; bounce buffer **+1.34% wire and
|
||||
+5 clk/B of copy** (22.4% of the frame — the cost `aligned` was chosen over
|
||||
`split` to avoid, arriving by a different door); **sector-aligned records
|
||||
+0.43% wire and zero clocks.** The last wins on both axes and is a container
|
||||
change. **ROADMAP now carries a four-item re-encode bundle** (index-0 black,
|
||||
`--spans all`, joint `lam`, sector alignment) and **P4a should be attempted
|
||||
against a sector-aligned container, not against this one.** 58.3.
|
||||
|
||||
**5. The rig refuses to be asked for a rate.** `DLX_XFER=scsi` rejects a
|
||||
non-zero `--kbps` outright rather than ignoring it, and `stream.lua` suppresses
|
||||
`CHANNEL IDLE`, `DEADLINE` and `REQUIRED PREFILL` rather than printing them as
|
||||
zeros — a zero there reads as "the channel never stopped", which would be a
|
||||
claim about a medium this tree has never timed.
|
||||
|
||||
**One rig bug found and fixed in the same sitting:** the host's `nsent` is now
|
||||
rebased at every seek. `XS_NXFER` is cumulative, so on a second pass it was
|
||||
already >= `nframes` and the slack sampling never fired — the second pass
|
||||
reported a ceiling of 0 frames and a build time of **-1 ticks**, which is an
|
||||
empty series printing as a result.
|
||||
|
||||
**New in the tree:** `src/player/xfer.i`, `tools/bench/mkvol.sh` (one copy of
|
||||
the volume layout, shared by both SCSI rigs), `tools/bench/xfer_cost.sh`,
|
||||
`tools/analysis/26_sector_align.py`, `DLX_XFER=scsi` and `DLX_SECONDS` in
|
||||
`pace_run.sh`, and two `check.sh` stages. `src/player/scsi.i` gained
|
||||
`sc_in_data` (the three-loop windowed receive) and `scsi_read_win`; `ring.i`
|
||||
gained the two transport hooks; `stream.s` includes `scsi.i` and `xfer.i`.
|
||||
|
||||
**No decoder code changed.** `decode.bin` is still 1,296 B at the same MD5.
|
||||
|
||||
**Next: P4a, and it is now the whole of what is left before M2.** A DMAC
|
||||
configuration that HOLDS THE BUS, shown by evidence that does not come from
|
||||
watching `$EA0015` (57.3: with `exown` asserted, MAME cannot tell a CPU-driven
|
||||
byte there from a DMAC-driven one). Do it against a sector-aligned container if
|
||||
the re-encode bundle has been run by then, because 58.3 is otherwise a bounce
|
||||
copy at +5 clk/B. **P2's remaining half** is unchanged and is now item 1 of
|
||||
that bundle.
|
||||
|
||||
---
|
||||
|
||||
# Status & next-session handoff — end of session 25 (2026-08-24)
|
||||
|
||||
## Session 25: the 68000 reads the disc itself, and P4 was never blocked
|
||||
|
||||
Reference in New Issue
Block a user