ROADMAP K1, the packed player's one open structural item. A frame is a picture
AND a palette, and no run in this tree had pointed a DMA channel at the palette
registers. dmagate.s runs 7-9, gated by dma_run.sh and check.sh:
7. 512 B off the disc into $E82000, bus held -- byte-exact in 256 register
words, read back OUT OF the registers by the 68000;
8. the SAME transfer aimed at RAM -- byte-exact at $2C000, and 256 of 256
palette words still read the poison the CPU wrote, which is what attributes
run 7 to the channel's MAR rather than to the readback path;
9. ONE array-chained start across two kinds of destination -- the palette and
six picture rows at the 1,024 B line stride, 2,048 B byte-exact.
So a packed frame is one channel start: a 193-entry array, palette first, CPU
halted from the first byte to the last. The array is scene-constant, because
the packed layout spends both 256-colour pages and there is no page to flip.
What is left on the CPU per frame in the video path is the channel start and the
READ(10) -- no per-frame PAINT, which is not the same claim as no per-frame CPU.
The destination is POISONED first (62.1). Runs 4-6 wrote into RAM that was zero
and GVRAM that was stale against a record that is mostly pad; "it matches the
disc" was weaker than it read as. The host counts whether the poison actually
discriminates instead of assuming it: 511 of 512, and the gate refuses under 500.
And it opened a hardware item (62.4, ROADMAP B4). MAME maps the palette to
palette_device over memory_array, whose write16 is a plain COMBINE_DATA -- RAM
that honours mem_mask, with no handler that could refuse a byte write. Unlike
GVRAM's 256-colour arm there is nothing here to be wrong about, so the run
bounds the model and not the board. What a real palette register does with a
byte write is unmeasured. A negative costs 0.28% of a frame and nothing else.
29_packed_player.py now also prints the two rows with the per-frame palette
charged -- 55.7% of a frame on the chain, 582 KB/s -- alongside the picture-only
figures the codec comparison is quoted against.
check.sh ALL GREEN before (tmp/check_s30_start.log) and after
(tmp/check_s30_end.log).
Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
155 lines
8.9 KiB
Bash
Executable File
155 lines
8.9 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."
|
|
# ---- the GVRAM run and its control (47.6.2). A channel that writes GVRAM in
|
|
# buffer mode is the decoder-free packed player's entire per-frame path, and a
|
|
# run with no control is 58.3's vacuous "UNDERRUNS: 0/120" again -- the IPL
|
|
# leaves R20 = $0B16, bit 11 ALREADY SET, so the first cut of this test could
|
|
# not have failed.
|
|
grep -aq "BYTES OK: 2048 B from LBA 1000 .*\[gvram\]" tmp/dma_run.log || \
|
|
fail "the channel did not fill GVRAM in buffer mode -- a device->GVRAM
|
|
transfer is the whole of the decoder-free packed player's frame."
|
|
grep -aq "R20 during the run = \$0916 (bit 11 SET)" tmp/dma_run.log || \
|
|
fail "the GVRAM run did not run in buffer mode with a KNOWN R20."
|
|
grep -aq "R20 during the run = \$0116 (bit 11 CLEAR)" tmp/dma_run.log || \
|
|
fail "the negative control did not run with bit 11 clear."
|
|
if grep -aq "BYTES OK: 2048 B from LBA 1000 .*\[masked\]" tmp/dma_run.log
|
|
then
|
|
fail "the MASKED control delivered every byte, so the run above is not a
|
|
measurement of R20 bit 11 -- it is a measurement of nothing."
|
|
fi
|
|
grep -aq "EXACTLY THE MECHANISM" tmp/dma_run.log || \
|
|
fail "the masked control lost bytes at ODD offsets too, or lost none at all.
|
|
The claim is not a COUNT -- stale GVRAM matches the disc by coincidence
|
|
wherever the record is pad -- it is a PLACE: gvram_w's 256-colour arm
|
|
drops what the channel wrote to EVEN addresses and stores what it wrote
|
|
to odd ones. Damage anywhere else is a different mechanism."
|
|
|
|
grep -aq "BYTES OK: 2048 B from LBA 1000 .*\[chain\]" tmp/dma_run.log || \
|
|
fail "the array-chained run did not put the bytes at the row bases its array
|
|
named. A picture row is 256 B of a 1024 B line stride, so a frame is 192
|
|
destinations; if the channel cannot walk them the CPU has to restart it
|
|
per row and the decoder-free path costs a per-row front end."
|
|
grep -aq "THE CHANNEL WALKED THE ARRAY ITSELF" tmp/dma_run.log || \
|
|
fail "the chained run did not report walking its own array."
|
|
|
|
# ---- THE PALETTE (ROADMAP K1, FINDINGS 61.9). If the registers at $E82000 take
|
|
# a byte-wide DMA the way GVRAM does in buffer mode, a per-frame palette is a
|
|
# 193rd array-chain entry and ONE channel start paints a whole frame; if they do
|
|
# not, the CPU writes 256 words a frame and the architecture still stands. The
|
|
# run is poisoned first and controlled twice -- once by aiming the same transfer
|
|
# elsewhere, once by counting how many of the 512 positions the poison and the
|
|
# disc actually differ in.
|
|
grep -aq "BYTES OK: 512 B from LBA 1000 .*\[pal\]" tmp/dma_run.log || \
|
|
fail "the channel did not write the palette registers at \$E82000 -- so a
|
|
per-frame palette costs the CPU 256 word writes and cannot ride the
|
|
frame's array chain (61.9). That is a RESULT, not a broken run: check the
|
|
PALETTE WRONG line above for whether the bytes were dropped or misplaced."
|
|
DIFF=$(sed -n 's/.*PALETTE POISON IS A DISCRIMINATOR: \([0-9]*\) of 512.*/\1/p' \
|
|
tmp/dma_run.log)
|
|
[ -n "$DIFF" ] && [ "$DIFF" -ge 500 ] || \
|
|
fail "the poison and the disc's bytes agree in ${DIFF:-?} of 512 positions, so
|
|
the palette run could have passed without a channel writing anything --
|
|
this is run 4's could-not-fail trap in a new place. Change DGPOIS."
|
|
grep -aq "BYTES OK: 512 B from LBA 1000 .*\[palctl\]" tmp/dma_run.log || \
|
|
fail "the ATTRIBUTION control's read did not land in RAM, so its palette claim
|
|
is about a transfer that did not happen."
|
|
grep -aq "PALETTE UNTOUCHED BY THE CONTROL: 256 of 256 words" tmp/dma_run.log || \
|
|
fail "the palette changed during a transfer aimed 20 KB away from it. Then
|
|
what reached \$E82000 in the run above was not decided by the channel's
|
|
MAR, and that run measured something else."
|
|
if grep -aq "CONTROL DID NOT FAIL \[palctl\]" tmp/dma_run.log
|
|
then
|
|
fail "the control reported its own failure -- see the line above it."
|
|
fi
|
|
grep -aq "BYTES OK: 2048 B from LBA 1000 .*\[palchain\]" tmp/dma_run.log || \
|
|
fail "ONE array-chained start could not cross from the palette registers into
|
|
GVRAM. A frame is one palette entry and 192 row entries; if the two kinds
|
|
of destination cannot share a chain, the CPU is back in the video path
|
|
once a frame to start the second half of it."
|
|
grep -aq "ONE START PAINTED THE PALETTE AND 6 ROWS" tmp/dma_run.log || \
|
|
fail "the palette+rows run did not report the crossing it exists to show."
|
|
|
|
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
|