ROADMAP P4a. src/player/dma.i programs HD63450 channel 1 and takes the SCSI DATA IN phase off the CPU; src/player/dmagate.s reads the same 2,048 B at LBA 1000 three ways -- PIO, the channel with the bus held, the channel stealing cycles -- and all three are byte-exact against the host's copy of the volume. The evidence never reads $EA0015, because 57.3 established that it cannot: with the DMAC's OWN asserted MAME cannot tell a CPU-driven byte there from a DMAC-driven one. The discriminator is the CPU's own progress. MTC is sampled by the INSTRUCTION AFTER the one that starts the channel, and held it reads 0 of 2,048 -- the whole transfer happened between two instructions, because the 68000 did not execute in between -- against the full count and 426 loop trips for the stealing configuration. Put the stealing registers in the held slot and every byte still arrives and tools/bench/dma_run.sh goes RED, which is what says the counter can come out different; 58.3's vacuous "UNDERRUNS: 0/120" is the trap being avoided. tools/analysis/27_dmac_config.py decodes the four register bytes out of the player's own source, with the MC68450 field tables now in one copy (tools/analysis/mc68450.py) shared with 21_iplrom_dmac.py, so the player's configuration and the IPL ROM's 16..19 clk/B one are the same decoding. Three bounds on the apparatus, read out of MAME 0.277 rather than inferred: the CZ-6BS1 has NO request line to the DMAC (its flow control is DTACK), so external request cannot be run; single address cannot be run either, because only channel 0 has device callbacks; and only burst is modelled as held. Of the four rows of the W ladder exactly one -- dual address held -- has a code path here, and it is the one demonstrated. W did not move by one clock, for the third session running. What outlives the emulator is the currency. Every W in this project is clocks per DELIVERED byte, which presumes the device asks; an auto-requested channel spends its share of the bus whether or not a byte is there, so a record costs what it costs to ARRIVE -- halve the delivery rate and the CPU cost of the same record doubles. tools/analysis/28_autorequest_cost.py prices it from MC68450 3.8 and 5.2.3.3.2, gating its formulas against Table 5-3's sixteen rows first. At 37,405 B and an explicit 460 KB/s: max rate costs the whole 95.3% of a frame the record takes to land, and of the GCR's four bus shares only BR=00, 50%, carries the rate -- 10.61 clk/B, 47.6% of a frame, against 40.4% for the W=9 row and 391.8% measured for PIO. The GCR is a design lever nothing in this tree had named. 59.4 changes what is left. sc_in_data now REFUSES a windowed read when the data phase is the channel's (SCE_WINDOW), because a channel writes a contiguous run and cannot drop the 300 B in front of a record. 117 of 120 records need one, so sector-aligned records have gone from a preference in ROADMAP's re-encode bundle to the precondition the transport enforces -- and that bundle is now the only thing between this tree and M2. One collision, recorded because the procedure is the finding: DM_USE first sat at $18300, which is ring.i's XF_SLOT mailbox, and the P4b stage -- untouched by this work -- went red on a run that never reached its snapshot. check.sh was ALL GREEN before any of this, which is what made that red unambiguous. ALL GREEN after too, with one new stage. decode.bin is unchanged at 1,296 B and the same MD5. Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
86 lines
4.6 KiB
Bash
Executable File
86 lines
4.6 KiB
Bash
Executable File
#!/bin/bash
|
|
# One HD63450 data-phase run: does the DMAC drive the SCSI data phase, and does
|
|
# it HOLD THE BUS? (ROADMAP P4a, the last item before M2.)
|
|
#
|
|
# tools/bench/dma_run.sh [container.dlx]
|
|
#
|
|
# The apparatus is tools/bench/scsi_run.sh's -- `x68000 -exp1 cz6bs1` and a
|
|
# zero-filled scsiexrom.bin on a private rompath -- and the volume is
|
|
# tools/bench/mkvol.sh's, the same bytes the host-file ring rig reads.
|
|
#
|
|
# WHAT A GREEN RUN MEANS: the same 2,048 B came off the disc three ways -- PIO,
|
|
# the channel with the bus held, the channel stealing cycles -- all three
|
|
# byte-exact against the host's copy; and in the held configuration THE WHOLE
|
|
# TRANSFER HAPPENED BETWEEN TWO INSTRUCTIONS, which is what holding the bus
|
|
# means and is not a claim about $EA0015 (57.3).
|
|
#
|
|
# WHAT IT DOES NOT MEAN: anything about `W`. MAME's DMAC runs on wall-clock
|
|
# attotimes (42.5) and models a held bus by HALTING the CPU rather than by
|
|
# charging it cycles per operand. This settles which configuration works.
|
|
set -e
|
|
cd "$(dirname "$0")/../.."
|
|
DLX=${1:-tmp/rc_fr_singe_scsi_span.dlx}
|
|
|
|
bash tools/bench/mkvol.sh "$DLX"
|
|
|
|
tools/vasm/vasmm68k_mot -Fbin -o tmp/dmagate.bin src/player/dmagate.s > /dev/null
|
|
|
|
# What the player will program, decoded out of the same constants it programs.
|
|
python3 tools/analysis/27_dmac_config.py
|
|
|
|
# stdbuf -oL: without it a long MAME run is unobservable until it exits, and a
|
|
# run that is merely finishing looks exactly like one that is wedged (34.1).
|
|
( cd tmp && SDL_VIDEODRIVER=dummy stdbuf -oL timeout -k 5 300 \
|
|
mame x68000 -bios ipl10 -exp1 cz6bs1 \
|
|
-rompath "$HOME/mame/roms;./p4roms" -hard dlxdisk.chd \
|
|
-ramsize 2M -video soft -window -sound none -nothrottle -plugins \
|
|
-autoboot_script ../tools/bench/dma.lua \
|
|
-seconds_to_run 90 > dma_run.log 2>&1 )
|
|
grep -aq "^\[DMA\] done" tmp/dma_run.log || {
|
|
echo "FAIL: the DMA gate did not finish -- no completion marker."
|
|
tail -8 tmp/dma_run.log; exit 1; }
|
|
grep -a "^\[DMA\]" tmp/dma_run.log | sed 's/^\[DMA\] / /'
|
|
|
|
# THE ASSERTIONS. Printing a result and gating on it are different things.
|
|
fail() { echo "FAIL: $1"; exit 1; }
|
|
grep -aq "BYTES OK: 2048 B from LBA 1000 .*\[pio\]" tmp/dma_run.log || \
|
|
fail "the PIO reference read did not match -- nothing below is about the DMAC."
|
|
grep -aq "BYTES OK: 2048 B from LBA 1000 .*\[held\]" tmp/dma_run.log || \
|
|
fail "the bus-held DMA read did not deliver the disc's bytes."
|
|
grep -aq "BYTES OK: 2048 B from LBA 1000 .*\[steal\]" tmp/dma_run.log || \
|
|
fail "the cycle-stealing DMA read did not deliver the disc's bytes."
|
|
grep -aq "MTC one instruction after START: 0 of 2048 .*NEVER EXECUTED .*\[held\]" \
|
|
tmp/dma_run.log || \
|
|
fail "the bus was NOT held: the CPU executed while the channel ran, so this is
|
|
not the configuration ROADMAP P4a asks for. That MTC is the whole of the
|
|
evidence that does not come from watching \$EA0015 (57.3)."
|
|
grep -aq "CPU trips round the wait loop: 1 \[held\]" tmp/dma_run.log || \
|
|
fail "the held configuration's CPU went round its wait loop more than once --
|
|
it was running, so the bus was not held for the whole transfer."
|
|
# A NEGATIVE ASSERTION IS WRITTEN AS AN `if`, not as `grep ... && fail`: under
|
|
# `set -e` a failing grep in an AND-list takes the whole script's exit status
|
|
# with it, so the run would report the failure it was looking for as a pass.
|
|
SPIN=$(sed -n 's/.*CPU trips round the wait loop: \([0-9]*\) \[steal\].*/\1/p' \
|
|
tmp/dma_run.log)
|
|
[ -n "$SPIN" ] && [ "$SPIN" -ge 100 ] || \
|
|
fail "the cycle-stealing configuration did not leave the CPU running (spin
|
|
= ${SPIN:-none}) -- the two configurations are meant to DIFFER in exactly
|
|
that, and a contrast of one against one is not a contrast."
|
|
if grep -aq "MTC one instruction after START: 0 of 2048 .*\[steal\]" tmp/dma_run.log
|
|
then
|
|
fail "the cycle-stealing configuration also finished between two instructions,
|
|
so the comparison has no contrast in it and the discriminator is measuring
|
|
something other than bus ownership."
|
|
fi
|
|
grep -aq "COC .*CER=\$00 MTC=0 .*(+2048) \[held\]" tmp/dma_run.log || \
|
|
fail "the held channel did not report a clean completion of every byte."
|
|
grep -aq "COC .*CER=\$00 MTC=0 .*(+2048) \[steal\]" tmp/dma_run.log || \
|
|
fail "the stealing channel did not report a clean completion of every byte."
|
|
grep -aq "WINDOWED DMA READ REFUSED" tmp/dma_run.log || \
|
|
fail "a WINDOWED read through the channel was not refused. 117 of 120 records
|
|
start part way into a sector (58.3), and a channel cannot drop the bytes
|
|
in front of one -- so it would write the neighbouring records into the
|
|
ring, over data the decoder has not finished with, with no bounds check
|
|
to catch it (49.2)."
|
|
exit 0
|