Put the container on the chip, and find the held bus costs 463 times the seam

ROADMAP P6c, FINDINGS 68. 78,125 B of a DLXP2's audio out of channel 3,
sample-exact, while the video channel fetches records off the same disc.

The two pieces 67.6 said were missing: the lump buffer (pg_afill/pg_afetch,
three slots and the minimum is unmeasured) and 67.2's remainder accumulator
(pg_apay). The capture prices what the accumulator avoided at 1.26 s of
lip-sync over the game, against 67.2's predicted 1.25.

The finding is the third piece, which nothing had named: the MSM6258 has no
starvation state, so the gap between a channel counting out and the next arm
is a held nibble pair driving the predictor. Stealing, the seam is 0.51 ms
over ten seconds because dma.i's new DM_HOOK services the chip from inside
the transfer wait -- 250,000 of 250,240 looks. Held, the 68000 is halted and
gets 369: every one of the ten lump boundaries has a seam, worst 72.8 ms,
2.31% of the audio. Identical bytes, different sound. 64.3 reaching the audio.

Two bugs, and no counter in the player could see either. Clearing DM_BARV does
not unchain a channel -- OCR bits 3-2 are what it obeys -- and the symptom is
POLL TIMEOUT on the lump and every record after it. And the refill ran one lump
ahead of its ring and overwrote the buffer the channel was reading: 11 of 11
armed, 11 fetched, no starve, and the sound wrong from 0.2 s in. Which is why
the gate is a WAV: verify_packed_audio.py walks the stream one delivered byte
at a time, because MAME's okim6258 resets the nibble select on every write and
a byte is two nibbles only 99.994% of the time.

check.sh ALL GREEN before (tmp/check_s36_start.log) and after
(tmp/check_s36_end.log), with the new stage.

Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
prosolis
2026-08-25 13:11:37 -07:00
parent e3778f62b0
commit 191f2b47bb
13 changed files with 1413 additions and 20 deletions
+37
View File
@@ -126,6 +126,36 @@ DM_LEN = $18524 ; u32 bytes the channel was asked for
; DM_USE landed on ring.i's slot and turned a stage red (above).
DM_BARV = $18528 ; u32 array base address, 0 = no chain
DM_BTCV = $1852C ; u32 array entry count (BTC)
; ---- THE SERVICE HOOK, and it is the whole of what a SECOND consumer needs
; from this file. ROADMAP P6c. A player that feeds ADPCM has to look at the
; audio channel more often than once a frame: the MSM6258 has no FIFO, so the
; instant its channel counts out the chip goes on replaying whatever byte its
; data register still holds -- and at 12 fps a once-a-frame re-arm makes that
; replay 90 ms long, which is not a gap, it is a buzz.
;
; THE ONLY PLACE A 68000 HAS TO SPARE IS INSIDE THIS WAIT. In the STEALING
; configuration the CPU goes round the loop below thousands of times per record
; (measured: 1,100,520 trips over 120 frames) and every one of them is time the
; disc is delivering and the CPU is not. So the hook is called from there, and
; the second consumer costs the video path nothing it was using.
;
; AND IN THE HELD CONFIGURATION THE HOOK CANNOT RUN AT ALL, which is not a bug
; in it: a burst channel HALTS the 68000 (dma_transfer_start asserts
; INPUT_LINE_HALT) and the CPU does not execute the loop, or anything else,
; until the record has landed. That asymmetry is the measurement -- FINDINGS
; 64.3 showed a held channel costs the frame CLOCK half its ticks, and this is
; the same fact reaching the audio.
;
; ZERO BY DEFAULT and every other front-end in this tree leaves it zero, so the
; cost to them is a `move.l` and a `beq` per trip. A mailbox that defaulted to
; a new behaviour is how DM_USE landed on ring.i's slot (above).
DM_HOOK = $18530 ; u32 0 = none, else a routine to call
; on every trip round the transfer wait.
; d0 is dead here and a0 is saved round
; the call, so the hook may trash both;
; it must preserve EVERYTHING else,
; because sc_in_dma's own d3/d4/d5 and
; scsi_read's a1 are live across it.
DM_PATIENCE = 4000000 ; the wait is bounded like every other
; ---------------------------------------------------------------- sc_in_dma
@@ -191,6 +221,13 @@ sc_in_dma:
clr.l DM_SPIN.l
move.l #DM_PATIENCE,d3
.wait: addq.l #1,DM_SPIN.l
move.l DM_HOOK.l,d0 ; the second consumer's slot -- see above
beq.s .nohook
move.l a0,-(sp)
movea.l d0,a0
jsr (a0)
movea.l (sp)+,a0
.nohook:
move.b DM_CSR,d4
btst #4,d4 ; ERR
bne.s .err
+462 -1
View File
@@ -94,6 +94,28 @@ PG_CADF = $18924 ; audio cadence: frames between one lump and the
; screen, which is a picture, and a gate that
; only looked for errors would pass it.
PG_CADA = $18928 ; ...and the sectors in a lump
PG_AUDON = $1892C ; 1 = PLAY the container's audio (ROADMAP P6c).
; Separate from PG_CADF because the two are
; different facts: the cadence has to be obeyed
; by the LBA arithmetic whether or not a chip is
; being fed, and a run that skips the lumps
; without playing them is the control this one
; is measured against.
PG_AFPS = $18960 ; the CONTAINER's fps, which is NOT PG_FPS. The
; pace is a rig knob (DLX_PK_FPS) and the audio
; was CUT at the container's rate; computing the
; payload from the pace would make a 6 fps gate
; run hand the chip half as many bytes a lump
; and still look self-consistent.
PG_AHZ = $18964 ; the chip's sample rate out of the header
PG_ALBA0 = $18968 ; LBA of lump 0 (off_aud/512 -- sector 1)
PG_NLUMP = $1896C ; lumps in the scene
PG_ABYTES = $18970 ; the stream's total payload, so the LAST lump
; is short rather than padded
PG_APRE = $18974 ; lumps fetched before frame 0. A NUMBER and
; not a constant: what the audio needs in hand
; is set by how far the transport runs behind
; real time, which is a property of the medium.
; ---- outputs
PG_SHOWN = $18930 ; frames displayed. Bumped AFTER bit 11 is
@@ -117,6 +139,69 @@ PG_LOSTV = $18958 ; V-DISP edges the machine did not see, summed
PG_ARRN = $1895C ; entries the array was built with (an assertion
; the rig reads back rather than a comment)
; ---- the audio path's own account. ROADMAP P6c.
PG_AARM = $18980 ; lumps ARMED on channel 3
PG_AFET = $18984 ; lumps FETCHED off the disc
PG_ABYT = $18988 ; payload bytes handed to the chip, padding NOT
; included -- the number FINDINGS 67.2 is about
PG_ADRY = $1898C ; times the service found the channel counted out
; and NO lump fetched to follow it: a STARVE, and
; the chip replays its last byte until it is fed
PG_ASEAM = $18990 ; service calls that found the channel counted
; out. Held, one of these is a whole frame of
; replayed byte; stealing, it is one trip round
; the transfer wait.
PG_ASRV = $18994 ; times pg_aserv was ENTERED at all -- the
; denominator PG_ASEAM is meaningless without,
; and the direct measure of what holding the bus
; costs a second consumer
PG_ACSR = $18998 ; channel 3's CSR at the end of the scene
PG_ACER = $1899C ; ...and CER with it
PG_ALATE = $189A0 ; re-arms issued with MTC still non-zero. By
; construction this is 0 -- the service only
; arms on COC -- and a non-zero reading means
; bytes were dropped rather than played.
PG_AMTC0 = $189A4 ; MTC one instruction after the FIRST start, the
; same witness dma.i uses: proof a byte moved
PG_AFERR = $189A8 ; SC_ERR of the first FAILED lump fetch. Its own
; slot and not PG_ERR's: a lump fetch that fails
; leaves the picture perfect and the sound gone,
; and a shared error word would report it as a
; video failure on whatever frame came next.
PG_AFERA = $189AC ; ...and the lump it failed on
; ---- and the state it keeps. In RAM and not in registers, because pg_aserv
; is called from inside dma.i's transfer wait as well as from the frame loop.
PG_AK = $189B0 ; next lump to ARM
PG_AKF = $189B4 ; next lump to FETCH
PG_AACC = $189B8 ; the remainder accumulator (FINDINGS 67.2)
PG_ABUSY = $189BC ; 1 = a lump fetch is in flight; the hook must
; not re-enter the transport it is running under
PG_ASLOTA = $189C0 ; slot the next ARM reads from
PG_ASLOTF = $189C4 ; slot the next FETCH writes to
; ---- the lump buffers. 65.3 charged 14,336 B for double-buffering the cadence
; and FINDINGS 67.6 recorded that no player held it; this is where it is held.
;
; THREE SLOTS, AND THE MINIMUM IS NOT KNOWN. Three is what was run and what
; FINDINGS 68 measures; two was not tried. The reason to suspect the answer is
; not one is that the two clocks are independent -- the chip consumes a lump in
; exactly 11/12 s and the video path decides when the next one is fetched, and
; under this apparatus the video path is 8.9% slow (a record lands in 90.7 ms
; against a 12 fps slot of 83.3, packed_free_steal). Whether that costs a third
; slot or only a deeper prefill is an experiment nobody has run, so PG_APRE is a
; mailbox rather than a constant and this number is an upper bound rather than a
; requirement.
;
; THE SLOT IS 16,384 B FOR A*512 = 7,168 B OF LUMP, which is 9,216 B a slot of
; address space nothing uses. It buys a shift instead of a multiply on the slot
; address and it is only address space -- nothing is written to it -- but it is
; not free on a machine with a filesystem in it and a shipping player should
; charge A*512 and pay the mulu.
PG_ABUF = $20000
PG_ASTRIDE = $4000 ; 16,384 B a slot -- A*512 = 7,168 with room, and
; a power of two so the slot address is a shift
PG_ANBUF = 3
; ---- the array chain. 193 x 6 B = 1,158 B, built once at scene setup.
; $1B000 and not $19000: dmagate.s's two arrays live at $19000/$19100 and
; ring.i's disc-offset table at $19400, and a front-end that shares an address
@@ -183,6 +268,24 @@ start:
clr.l PG_TSPIN.l
clr.l PG_GSPIN.l
clr.l PG_LOSTV.l
clr.l PG_AARM.l
clr.l PG_AFET.l
clr.l PG_ABYT.l
clr.l PG_ADRY.l
clr.l PG_ASEAM.l
clr.l PG_ASRV.l
clr.l PG_ACSR.l
clr.l PG_ACER.l
clr.l PG_ALATE.l
clr.l PG_AMTC0.l
clr.l PG_AFERR.l
move.l #-1,PG_AFERA.l
clr.l PG_AK.l
clr.l PG_AKF.l
clr.l PG_AACC.l
clr.l PG_ABUSY.l
clr.l PG_ASLOTA.l
clr.l PG_ASLOTF.l
; ---- 1. the display. THE PLAYER OWNS IT, and that is a change from every
; other rig in this tree: tools/bench/stream.lua and decode.lua call
@@ -224,7 +327,14 @@ start:
.noclk:
move.l CLK_VDISP.l,PG_VD0.l
; ---- 6. the scene.
; ---- 6. THE CHIP, and it is last for the same reason the clock was: ad_setup
; touches the YM2151 and the PPI, and the ~100 ms it then has to settle for
; (FINDINGS 66.4) is 100 ms this player would otherwise spend with a transport
; half brought up. It also has to be after scsi_init, because the prefill is a
; READ(10) like any other.
bsr pg_ainit
; ---- 7. the scene.
move.l PG_ITER.l,d6
tst.l d6
bne.s .it
@@ -234,7 +344,20 @@ pg_pass:
moveq #0,d7 ; d7 = frame index within the pass
pg_loop:
bsr pg_gate ; wait for tick d7
; THE AUDIO IS SERVICED THREE TIMES A FRAME AND THAT IS NOT BELT AND BRACES.
; Once here, once after the record has landed, and -- in the STEALING
; configuration only -- on every trip round dma.i's transfer wait, through
; DM_HOOK. The chip has no FIFO: the instant channel 3 counts out it goes on
; replaying whatever byte its data register still holds, so the interval between
; a count-out and the next arm is not silence, it is a held nibble pair driving
; the predictor. Held, the CPU is halted for the whole transfer and the two
; calls below are the ONLY two it gets; that difference is what PG_ASRV counts.
bsr pg_aserv
bsr pg_frame ; and paint it
move.l d0,-(sp)
bsr pg_aserv
bsr pg_afill ; ...and fetch at most ONE lump, so the
move.l (sp)+,d0 ; audio's share of the frame is bounded
tst.l d0
bmi.s pg_failed
addq.l #1,d7
@@ -251,11 +374,13 @@ pg_loop:
move.l CLK_VDISP.l,PG_VDISP.l
bsr clk_stop
bsr pg_areport
move.l #$FF,PG_FLAG.l
bra.s pg_hold
pg_failed:
move.l CLK_VDISP.l,PG_VDISP.l
bsr clk_stop
bsr pg_areport
move.l #$E2,PG_FLAG.l
pg_hold:
bra.s pg_hold
@@ -528,7 +653,343 @@ pg_static:
movem.l (sp)+,d0-d3/a0-a1
rts
; ===========================================================================
; THE AUDIO PATH. ROADMAP P6c -- the container's own bytes, out of a channel,
; beside the video channel that holds the bus.
;
; WHAT IS NEW HERE AND WHAT IS NOT. The transport is not new: it is adpcm.i's
; channel-3 configuration, every byte of which tools/analysis/21_iplrom_dmac.py
; decoded out of the IPL ROM, and it worked first time (FINDINGS 66.1). The
; container is not new either: DLXP2 has carried the lumps since FINDINGS 67.
; What no layer of this project has ever done is JOIN THEM -- and the two things
; that had to be built to do it are the two 67.6 named as missing:
;
; THE LUMP BUFFER, which is below and is 3 x 16,384 B rather than the 14,336 B
; 65.3 charged. Two slots is right when the transport keeps up with real
; time. This apparatus does not: a record lands in 90.7 ms against a 12 fps
; slot of 83.3 ms, so the video path runs 8.9% slow and the chip does not
; run slow with it -- 15,625 Hz is a crystal, not a pace. The third slot is
; the prefill that covers the difference and PG_APRE is how much of it is
; filled before frame 0.
;
; THE REMAINDER ACCUMULATOR, which is pg_apay and is the whole of FINDINGS
; 67.2. A lump is A*512 = 7,168 B of SPACE; eleven frames of audio is
; 11*15625/24 = 7,161.4583... B. Handing the chip the lump instead of the
; payload is 0.09% too much audio, and 0.09% is not waste, it is DRIFT:
; 0.84 ms a group, 1.25 s of lip-sync over the game's 22.8 minutes. It is
; the same arithmetic clock.i carries one dimension over, and it is the
; third time this tree has met it (54, 65.3, 67.2).
;
; AND THE FAILURE MODE IS THE FORMAT'S OWN (67.4): nothing here parses anything.
; A lump read one sector out is not an error, it is 7,168 B of noise; a payload
; length one byte out is not an error either, it is a rate. Both are why
; tools/bench/verify_packed_audio.py reads the CAPTURE rather than the counters.
; ---------------------------------------------------------------- pg_ainit
; Bring the chip up, prefill PG_APRE lumps, arm the first and start playing.
; ORDER IS THE GATE'S, unchanged from adpcmgate.s and for its reason: the chip
; resets its accumulator, its step index AND its nibble select when it is told
; to PLAY and begins consuming immediately out of whatever its data register
; holds -- so the CHANNEL goes first and the CPU waits for MTC to move, which is
; proof a byte was actually taken, before the PLAY.
pg_ainit:
tst.l PG_AUDON.l
beq .out
movem.l d0-d4/a1,-(sp)
bsr ad_setup
; ---- SETTLE, and FINDINGS 66.4 is why. The 8 MHz ADPCM clock is CT1 in the
; YM2151's port register $1B -- a different device from the divider -- and this
; machine delivers that write on the sound system's own schedule rather than at
; the store. A transfer started in the same breath plays its first ~17 ms at
; the PREVIOUS clock, which reads exactly like a broken probe. A player sets
; its clock once at boot and can afford 100 ms; this is that once.
move.l #60000,d0
.settle:subq.l #1,d0
bne.s .settle
; ---- the prefill. At least one lump, or there is nothing to arm.
move.l PG_APRE.l,d4
bne.s .pre
moveq #1,d4
.pre:
cmp.l PG_NLUMP.l,d4
bls.s .pl
move.l PG_NLUMP.l,d4
.pl: tst.l d4
beq.s .done
.pf: bsr pg_afetch
subq.l #1,d4
bne.s .pf
; ---- arm lump 0, then wait for the channel to actually take byte 0 before
; PLAY. Not a delay loop: the condition is MTC having moved, which is the
; channel's own account of a byte having left RAM.
bsr pg_aarm
move.l #4000000,d3
move.w A3_MTC,d0
andi.l #$FFFF,d0
move.l d0,d2 ; d2 = MTC as armed
.first: move.w A3_MTC,d0
andi.l #$FFFF,d0
cmp.l d2,d0
bne.s .go
subq.l #1,d3
bne.s .first
.go: move.l d0,PG_AMTC0.l
bsr ad_play
; ---- and NOW the hook, which is the second consumer's whole claim on the video
; path (dma.i). After the play, so that a trip round a transfer wait cannot
; find a channel that has not been started yet.
move.l #pg_aserv,DM_HOOK.l
.done:
movem.l (sp)+,d0-d4/a1
.out: rts
; ---------------------------------------------------------------- pg_aserv
; ONE LOOK AT THE AUDIO CHANNEL. Called from the frame loop twice a frame and,
; in the stealing configuration, from inside dma.i's transfer wait -- so it must
; preserve every register and must be cheap on the path where nothing is due.
;
; IT ARMS ONLY ON COC. The alternative -- re-arm at the group boundary whatever
; the channel is doing -- drops MTC bytes on the floor, and dropped bytes are a
; rate error in the same direction 67.2's padding was. PG_ALATE is the
; assertion that it never happens and is 0 on a correct run.
pg_aserv:
movem.l d0-d2/a1,-(sp)
addq.l #1,PG_ASRV.l
tst.l PG_AUDON.l
beq.s .out
tst.l PG_ABUSY.l ; a lump fetch is in flight and IT is
bne.s .out ; the transfer this hook is running
; under -- see pg_afetch
move.l PG_AK.l,d0
cmp.l PG_NLUMP.l,d0
bcc.s .out ; the scene's audio is all armed
moveq #0,d1
move.b A3_CSR,d1
btst #7,d1 ; COC: the channel counted out and the
beq.s .out ; chip is now replaying its last byte
addq.l #1,PG_ASEAM.l
move.l PG_AKF.l,d1
cmp.l d1,d0
bcs.s .arm
addq.l #1,PG_ADRY.l ; STARVE: nothing fetched to follow it.
bra.s .out ; Counted, not absorbed -- the chip is
; audibly stuck on one byte pair.
.arm: bsr pg_aarm
.out: movem.l (sp)+,d0-d2/a1
rts
; ---------------------------------------------------------------- pg_aarm
; Arm channel 3 on the lump in slot PG_ASLOTA for its PAYLOAD length, and
; advance. Trashes d0-d2/a1, which pg_aserv and pg_ainit have saved.
pg_aarm:
tst.l PG_AARM.l
beq.s .clean ; the FIRST arm has no previous lump and
; MTC out of reset is not ours to read
move.w A3_MTC,d0
andi.l #$FFFF,d0
beq.s .clean
addq.l #1,PG_ALATE.l ; bytes the previous lump never played
.clean:
bsr pg_apay ; d0 = this lump's payload
tst.l d0
beq.s .skip
move.l d0,d1
add.l d0,PG_ABYT.l
move.l PG_ASLOTA.l,d0
lsl.l #8,d0
lsl.l #6,d0 ; slot * $4000
add.l #PG_ABUF,d0
movea.l d0,a1
bsr ad_arm ; d1 = MTC, a1 = MAR
addq.l #1,PG_AARM.l
.skip:
addq.l #1,PG_AK.l
move.l PG_ASLOTA.l,d0
addq.l #1,d0
cmp.l #PG_ANBUF,d0
bcs.s .st
moveq #0,d0
.st: move.l d0,PG_ASLOTA.l
rts
; ---------------------------------------------------------------- pg_apay
; d0 = the PAYLOAD of the next lump, in bytes. FINDINGS 67.2, and it is three
; lines of arithmetic because the alternative is 1.25 s of lip-sync:
;
; acc += F*hz ; n = acc / (2*fps) ; acc %= (2*fps)
;
; THE fps IS THE CONTAINER'S (PG_AFPS) AND NOT THE PACE. The stream was cut at
; the rate it was cut at; a run paced slower is a run whose PICTURES are slow,
; and computing the payload from the pace would hand the chip half a lump and
; still balance.
;
; divu is a WORD divide and the quotient has to fit 16 bits: at F=11, 15,625 Hz
; and 12 fps it is 7,161 or 7,162, and the accumulator it is taken from never
; exceeds F*hz + (2*fps-1) = 171,898.
pg_apay:
movem.l d1-d2,-(sp)
move.l PG_CADF.l,d0
move.l PG_AHZ.l,d1
mulu d1,d0 ; F*hz = 171,875
add.l PG_AACC.l,d0
move.l PG_AFPS.l,d1
add.l d1,d1 ; 2*fps = 24, two samples to a byte
divu d1,d0
move.l d0,d2
clr.w d2
swap d2 ; divu leaves the REMAINDER in the high
move.l d2,PG_AACC.l ; half, and it is 11 at F=11 -- which is
andi.l #$FFFF,d0 ; the whole finding
; ---- and the LAST lump is short rather than padded: the stream ends where it
; ends and the geometry would otherwise hand the chip whatever followed it.
move.l PG_ABYTES.l,d1
sub.l PG_ABYT.l,d1
bcc.s .lim
moveq #0,d1
.lim: cmp.l d1,d0
bls.s .out
move.l d1,d0
.out: movem.l (sp)+,d1-d2
rts
; ---------------------------------------------------------------- pg_afill
; At most ONE lump a frame, and only if a slot is free. One, because a lump is
; A*512 = 7,168 B off the same disc the record came off and a frame that fetched
; two would spend 26 ms of its slot on audio.
;
; AND "FREE" COUNTS THE LUMP THAT IS STILL PLAYING, which is the bug this
; session shipped and the capture caught. PG_AK is the NEXT lump to arm, so the
; one channel 3 is reading right now is PG_AK-1 and its slot is not free -- the
; resident set is PG_AKF-PG_AK+1 lumps, not PG_AKF-PG_AK. Off by one, the
; fetcher runs three lumps ahead instead of two and lump 3 lands on top of lump
; 0 WHILE THE CHANNEL IS READING IT.
;
; NOTHING IN THE PLAYER CAN SEE THAT HAPPEN. Every counter stays right: 11 of
; 11 lumps armed, 11 fetched, 78,125 B of payload, no starve, no late re-arm --
; and the sound is wrong from 0.2 s in, because the channel goes on delivering
; bytes at exactly the right rate out of a buffer somebody else is writing.
; That is FINDINGS 67.4's failure mode arriving on the audio side: nothing
; parses a packed container, so a wrong byte is not an error, it is a sound.
; It is why the gate reads the CAPTURE (tools/bench/verify_packed_audio.py).
pg_afill:
movem.l d0-d1,-(sp)
tst.l PG_AUDON.l
beq.s .out
move.l PG_AKF.l,d0
cmp.l PG_NLUMP.l,d0
bcc.s .out ; the whole stream is fetched
sub.l PG_AK.l,d0
addq.l #1,d0 ; ...+1 for the lump being PLAYED out of
; its slot right now (see above)
cmp.l #PG_ANBUF,d0
bcc.s .out ; every slot is spoken for
bsr pg_afetch
.out: movem.l (sp)+,d0-d1
rts
; ---------------------------------------------------------------- pg_afetch
; READ(10) lump PG_AKF into slot PG_ASLOTF. Trashes d0-d4/a1.
;
; lump k is at LBA PG_ALBA0 + k*(F*PG_RECS + PG_CADA)
;
; ARITHMETIC, like the record's, and for the same reason: a group is F records
; and one lump of A sectors, both fixed, so the format still has no index and
; still needs none (FINDINGS 67.1). That was the packed branch's whole claim
; and a second stream at an unrelated rate was the thing that could have ended
; it; it did not.
;
; THREE THINGS ARE TURNED OFF ROUND THE READ AND ALL THREE MATTER.
;
; DM_BARV, because the video path leaves the channel pointed at an array of
; 193 GVRAM destinations and a lump fetched through it would be painted on
; the screen instead of played.
;
; THE OCR's CHAIN FIELD, AND CLEARING DM_BARV IS NOT ENOUGH -- this cost the
; session its first wrong answer. dma.i reads DM_BARV to decide whether to
; write MAR and MTC, but the register the CHANNEL obeys is OCR bits 3-2, and
; pg_dmacfg has set them to %10 for the whole scene. So the "unchained"
; read wrote MAR and MTC and the channel then ignored both and loaded them
; from whatever BAR happened to hold (hd63450.cpp dma_transfer_start). It
; does not fail as a bad address: the SPC's phase never completes and the
; read comes back POLL TIMEOUT, on the LUMP and then on every record after
; it -- a wedged transport that reads like a disc problem.
;
; DM_HOOK's reentry, through PG_ABUSY: pg_aserv is called from inside the very
; transfer wait this read is running under, and an arm issued from there
; would point channel 3 at a buffer being written into.
pg_afetch:
movem.l d0-d5/a1,-(sp)
move.l #1,PG_ABUSY.l
move.l DM_BARV.l,-(sp)
move.l DM_OCRV.l,-(sp)
clr.l DM_BARV.l
move.l DM_OCRV.l,d0
andi.l #$FFFFFFF3,d0 ; CHAIN = %00, no chaining
move.l d0,DM_OCRV.l
move.l PG_CADF.l,d3
move.l PG_RECS.l,d0
mulu d0,d3 ; F records...
move.l PG_CADA.l,d0
add.l d0,d3 ; ...and the lump in front of them
move.l PG_AKF.l,d0
mulu d3,d0 ; k * the group's sectors
add.l PG_ALBA0.l,d0
move.l d0,d3
move.l PG_CADA.l,d4
move.l PG_ASLOTF.l,d0
lsl.l #8,d0
lsl.l #6,d0
add.l #PG_ABUF,d0
movea.l d0,a1
bsr scsi_read
tst.l d0
bmi.s .err
addq.l #1,PG_AFET.l
addq.l #1,PG_AKF.l
move.l PG_ASLOTF.l,d0
addq.l #1,d0
cmp.l #PG_ANBUF,d0
bcs.s .st
moveq #0,d0
.st: move.l d0,PG_ASLOTF.l
bra.s .done
.err: tst.l PG_AFERR.l
bne.s .done
move.l SC_ERR.l,PG_AFERR.l
move.l PG_AKF.l,PG_AFERA.l
.done:
move.l (sp)+,DM_OCRV.l
move.l (sp)+,DM_BARV.l
clr.l PG_ABUSY.l
movem.l (sp)+,d0-d5/a1
rts
; ---------------------------------------------------------------- pg_areport
; The channel's own account, and the chip stopped. ad_halt matters to the
; CAPTURE and not to the machine: left playing the chip goes on replaying its
; last byte forever, and tools/bench/verify_packed_audio.py would then have to
; find the end of the stream in a tail that never ends.
pg_areport:
tst.l PG_AUDON.l
beq.s .out
movem.l d0,-(sp)
clr.l DM_HOOK.l
moveq #0,d0
move.b A3_CSR,d0
move.l d0,PG_ACSR.l
moveq #0,d0
move.b A3_CER,d0
move.l d0,PG_ACER.l
bsr ad_halt
movem.l (sp)+,d0
.out: rts
include "src/player/clock.i"
include "src/player/adpcm.i"
; scsi.i includes src/player/dma.i itself, at its foot -- so it is NOT included
; here. The DMAC constants and sc_in_dma come in with the transport that uses
; them, which is also why dmagate.s includes only scsi.i.
+6
View File
@@ -241,6 +241,12 @@ sci1: nop
clr.l SC_ERR.l
clr.l DM_USE.l ; PIO unless a caller asks otherwise,
; AFTER this call (src/player/dma.i)
clr.l DM_HOOK.l ; ...and no second consumer is being
; serviced until one installs itself,
; for the same reason and in the same
; direction: a mailbox that defaults to
; a behaviour is how DM_USE landed on
; ring.i's slot.
clr.l SC_TRN.l
move.l #0,SC_TAG.l
bsr sc_snap