Put the data phase on the DMAC, and find auto-request is charged by time
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
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
; The HD63450 driving the SCSI data phase. ROADMAP P4a.
|
||||
;
|
||||
; WHAT P4a HAS TO SHOW, and why it needed a new kind of evidence. FINDINGS 58
|
||||
; measured the CPU moving every byte itself at 87.28 clocks per delivered byte
|
||||
; -- 391.8% of a 12 fps frame, against 22.4% for the cheapest DMA row of the
|
||||
; ladder and 85.3% for the dearest. So the whole of what is left before M2 is a
|
||||
; DMAC configuration that HOLDS THE BUS. 57.3 is why it could not simply be
|
||||
; watched into existence: x68k_scsiext.cpp glues $EA0015 so that with the DMAC's
|
||||
; OWN asserted -- which it is at idle on this machine -- MAME CANNOT DISTINGUISH
|
||||
; a CPU-driven byte at that address from a DMAC-driven one. Watching the data
|
||||
; register cannot answer the question it looks like it answers.
|
||||
;
|
||||
; THE DISCRIMINATOR USED HERE IS THE CPU'S OWN PROGRESS, and it never reads
|
||||
; $EA0015 at all. A DMAC that holds the bus is one the CPU is not running
|
||||
; against; so the witness is a single instruction:
|
||||
;
|
||||
; move.b #CCR_START,DM_CCR ; the channel is told to go
|
||||
; move.w DM_MTC,d0 ; <- sampled by the VERY NEXT instruction
|
||||
;
|
||||
; If the bus was held for the transfer, the whole transfer happened between
|
||||
; those two instructions and d0 reads ZERO. If it was not, d0 reads very nearly
|
||||
; the full count and the CPU goes on to spin thousands of times while the
|
||||
; channel trickles. Both configurations deliver the same bytes; what separates
|
||||
; them is whether the 68000 got to execute anything meanwhile, which is exactly
|
||||
; what "holds the bus" means and is not a fact about $EA0015.
|
||||
;
|
||||
; WHAT MAME CAN AND CANNOT BE ASKED, stated here because it bounds the claim and
|
||||
; it is not obvious from the outside:
|
||||
;
|
||||
; * THE CARD HAS NO EXREQ PATH. x68k_scsiext.cpp's drq_w only stores a flag;
|
||||
; the expansion slot has no request line to the DMAC at all (x68k.cpp wires
|
||||
; drq0 from the FDC and drq3 from ADPCM, and nothing else). The card's flow
|
||||
; control is DTACK: on a DMAC cycle with DRQ low the card NEGATES DTACK and
|
||||
; the HD63450 discards that operand and retries. So every configuration
|
||||
; below is AUTO-REQUEST; REQG=10, external request -- the mode the ladder's
|
||||
; W=5 and W=12 rows assume -- has no wiring in this model and cannot be run.
|
||||
; * SINGLE ADDRESS CANNOT BE RUN EITHER. hd63450.cpp only takes the implicit
|
||||
; path when a channel has a dma_read/dma_write callback, and on this machine
|
||||
; only channel 0 (the FDC) has one. DTYP=10/11 on channels 1..3 falls
|
||||
; through to the dual-address code.
|
||||
; * ONLY BURST IS MODELLED AS HELD. The device tests `(dcr & 0xc0) == 0`, so
|
||||
; XRM=10 (cycle steal without hold) and XRM=11 (cycle steal WITH hold) are
|
||||
; one code path. The bus is held, and the CPU halted, only for XRM=00 burst
|
||||
; with REQG=01 max rate.
|
||||
;
|
||||
; So of the four rows of the per-byte ladder, exactly ONE -- dual address, bus
|
||||
; held, 9 clk/B -- has a code path in this model, and it is the one demonstrated
|
||||
; below. That is a bound on the apparatus and not a result about the board.
|
||||
;
|
||||
; AND IT IS STILL NOT A RATE. MAME's DMAC is configured in wall-clock attotimes
|
||||
; (42.5), not per-operand cycles: set_burst_clocks gives channel 1 450 ns an
|
||||
; operand no matter what the 68000 is doing. `W` is untouched by every line in
|
||||
; this file and still wants a board (ROADMAP B1/B3).
|
||||
|
||||
; ---- the channel. 1, not 0: channel 0 is the FDC's and is the one channel
|
||||
; with device callbacks, which would silently take the implicit-address path.
|
||||
; Channel 1 is also the channel the IPL ROM points at the SASI data register
|
||||
; (52.5), so this is the machine's own disk channel programmed differently.
|
||||
DMA_CH = 1
|
||||
DMACB = DMAC+DMA_CH*DMAC_CH ; $E84040
|
||||
DM_CSR = DMACB+$00 ; channel status (write 1s to clear)
|
||||
DM_CER = DMACB+$01 ; channel error (read only)
|
||||
DM_DCR = DMACB+$04 ; device control
|
||||
DM_OCR = DMACB+$05 ; operation control
|
||||
DM_SCR = DMACB+$06 ; sequence control
|
||||
DM_CCR = DMACB+$07 ; channel control
|
||||
DM_MTC = DMACB+$0A ; memory transfer count, WORD
|
||||
DM_MAR = DMACB+$0C ; memory address, LONG
|
||||
DM_DAR = DMACB+$14 ; device address, LONG
|
||||
DM_MFC = DMACB+$29
|
||||
DM_CPR = DMACB+$2D
|
||||
DM_DFC = DMACB+$31
|
||||
|
||||
; CSR bits
|
||||
CSR_COC = $80 ; channel operation complete
|
||||
CSR_BTC = $40
|
||||
CSR_NDT = $20 ; normal device termination
|
||||
CSR_ERR = $10 ; channel error -- CER says which
|
||||
CSR_ACT = $08 ; channel active
|
||||
CCR_START = $80
|
||||
|
||||
; ---- the two configurations, as (DCR, OCR) pairs. Both are decoded by
|
||||
; tools/analysis/27_dmac_config.py out of THESE bytes, using the same MC68450
|
||||
; field tables 21_iplrom_dmac.py reads the IPL ROM's channels with -- so what
|
||||
; the run claims it programmed and what it programmed cannot drift apart.
|
||||
;
|
||||
; HELD : DCR $00 = XRM 00 burst, DTYP 00 dual address, DPS 0 8-bit port
|
||||
; OCR $81 = DIR device->memory, SIZE byte, no chain, REQG 01 max rate
|
||||
; STEAL: DCR $80 = XRM 10 cycle steal WITHOUT hold, otherwise identical
|
||||
; OCR $80 = REQG 00 auto-request at limited rate
|
||||
DM_HELD_DCR = $00
|
||||
DM_HELD_OCR = $81
|
||||
DM_STEAL_DCR = $80
|
||||
DM_STEAL_OCR = $80
|
||||
|
||||
; ---- what the run reports. Every one of these is a DMAC register or a count
|
||||
; of the CPU's own instructions; none of them is a read of $EA0015.
|
||||
; $18500 AND NOT $18300, WHICH IS WHERE THIS FIRST WENT. scsi.i's trace ends at
|
||||
; $182FF and the next 160 bytes are the RING's: $18300 is ring.i's XF_SLOT
|
||||
; mailbox, and tools/bench/stream.lua reads the same addresses from outside.
|
||||
; dma.i is included by stream.s as well as by the gate, so DM_USE landed on the
|
||||
; transfer request slot and the ring rig's first record request read as "use the
|
||||
; DMAC" -- P4b's stage went red on a run that never reached its snapshot. The
|
||||
; symptom was in a stage this session did not touch, which is the whole argument
|
||||
; for check.sh being run before and after rather than only after.
|
||||
DM_USE = $18500 ; u32 0 = PIO data phase, 1 = this file
|
||||
DM_DCRV = $18504 ; u32 the DCR byte to program
|
||||
DM_OCRV = $18508 ; u32 the OCR byte to program
|
||||
DM_MTC0 = $1850C ; u32 MTC one instruction after START
|
||||
DM_SPIN = $18510 ; u32 times the CPU went round the wait
|
||||
DM_CSRF = $18514 ; u32 CSR when the channel finished
|
||||
DM_CERF = $18518 ; u32 CER with it
|
||||
DM_MTCF = $1851C ; u32 MTC with it
|
||||
DM_MARF = $18520 ; u32 MAR with it -- where it stopped
|
||||
DM_LEN = $18524 ; u32 bytes the channel was asked for
|
||||
DM_PATIENCE = 4000000 ; the wait is bounded like every other
|
||||
|
||||
; ---------------------------------------------------------------- sc_in_dma
|
||||
; Receive d1 bytes into (a1) in phase d2, WITHOUT the CPU touching one of them.
|
||||
; Entered from sc_in_data when DM_USE is set; same registers, same contract.
|
||||
;
|
||||
; ORDER MATTERS AND IT IS NOT THE OBVIOUS ONE. The SPC is put into DMA transfer
|
||||
; BEFORE the channel is started, because in the held configuration the 68000
|
||||
; stops executing at the CCR write and does not run again until the transfer is
|
||||
; over -- so anything the SPC needs to be told has to have been told already.
|
||||
sc_in_dma:
|
||||
movem.l d3-d5,-(sp)
|
||||
move.l d1,d5 ; keep the length for the report
|
||||
move.l d5,DM_LEN.l
|
||||
move.b d2,SC_PCTL
|
||||
move.l d1,d0
|
||||
bsr sc_settc ; the SPC counts the same bytes down
|
||||
|
||||
; ---- the channel, quiet first: CSR is write-one-to-clear and a stale
|
||||
; COC from a previous record would pass the wait loop instantly.
|
||||
move.b #$FF,DM_CSR
|
||||
move.l DM_DCRV.l,d0
|
||||
move.b d0,DM_DCR
|
||||
move.l DM_OCRV.l,d0
|
||||
move.b d0,DM_OCR
|
||||
move.b #$04,DM_SCR ; MAC 01 memory increment, DAC 00 none:
|
||||
; the device address is a REGISTER and
|
||||
; must not walk off it.
|
||||
move.b #$05,DM_MFC ; the function codes the IPL ROM uses
|
||||
move.b #$05,DM_DFC
|
||||
move.b #$01,DM_CPR
|
||||
move.w d5,DM_MTC
|
||||
move.l a1,DM_MAR
|
||||
move.l #SC_DREG,DM_DAR ; $EA0015 -- the DMAC's door, and now
|
||||
; the DMAC is the one going through it
|
||||
move.b #SCMD_XFER,SC_SCMD ; no PROGRAM bit: the SPC raises DRQ
|
||||
move.l #11,SC_TAG.l ; 11 = channel armed, SPC in DMA mode
|
||||
bsr sc_snap
|
||||
|
||||
; ---- START, and the witness immediately after it
|
||||
move.b #CCR_START,DM_CCR
|
||||
move.w DM_MTC,d0 ; THE DISCRIMINATOR. Held: zero.
|
||||
andi.l #$FFFF,d0
|
||||
move.l d0,DM_MTC0.l
|
||||
|
||||
; ---- wait for the channel, counting the CPU's own trips round the loop.
|
||||
; In the held configuration this is one trip, because the CPU did not
|
||||
; get to run until the transfer was over. In the stealing one it is
|
||||
; thousands, and every one of them is a 68000 instruction that executed
|
||||
; while the disc was delivering -- which is the whole point of P4a.
|
||||
clr.l DM_SPIN.l
|
||||
move.l #DM_PATIENCE,d3
|
||||
.wait: addq.l #1,DM_SPIN.l
|
||||
move.b DM_CSR,d4
|
||||
btst #4,d4 ; ERR
|
||||
bne.s .err
|
||||
btst #7,d4 ; COC
|
||||
bne.s .fin
|
||||
subq.l #1,d3
|
||||
bne.s .wait
|
||||
bsr .report
|
||||
movem.l (sp)+,d3-d5
|
||||
move.l #SCE_TIMEOUT,SC_ERR.l
|
||||
moveq #-1,d0
|
||||
rts
|
||||
.err: bsr .report
|
||||
movem.l (sp)+,d3-d5
|
||||
move.l #SCE_TIMEOUT,SC_ERR.l ; a channel error is a dead transport
|
||||
moveq #-1,d0
|
||||
rts
|
||||
.fin: bsr .report
|
||||
move.b #$FF,DM_CSR ; leave the channel as we found it
|
||||
move.l #12,SC_TAG.l ; 12 = channel reported COC
|
||||
bsr sc_snap
|
||||
movem.l (sp)+,d3-d5
|
||||
bsr sc_xferend ; the SPC's own transfer, not the DMAC's
|
||||
move.l d0,-(sp)
|
||||
move.l #9,SC_TAG.l
|
||||
bsr sc_snap
|
||||
move.l (sp)+,d0
|
||||
rts
|
||||
|
||||
; ---- the channel's own account of what it did, read out of its registers
|
||||
.report:
|
||||
moveq #0,d0
|
||||
move.b DM_CSR,d0
|
||||
move.l d0,DM_CSRF.l
|
||||
moveq #0,d0
|
||||
move.b DM_CER,d0
|
||||
move.l d0,DM_CERF.l
|
||||
move.w DM_MTC,d0
|
||||
andi.l #$FFFF,d0
|
||||
move.l d0,DM_MTCF.l
|
||||
move.l DM_MAR,d0
|
||||
move.l d0,DM_MARF.l
|
||||
rts
|
||||
@@ -0,0 +1,132 @@
|
||||
; Front-end for the HD63450 DATA PHASE (ROADMAP P4a), for the rig.
|
||||
;
|
||||
; THE QUESTION. FINDINGS 58 put the transport on the 68000 and priced it: the
|
||||
; CPU moving every byte itself costs 87.28 clocks per delivered byte, 391.8% of
|
||||
; a 12 fps frame. Against that, the cheapest DMA row of the ladder is 22.4% and
|
||||
; the dearest is 85.3%, so everything left before M2 turns on getting the DMAC
|
||||
; to drive the data phase with the bus HELD. 57.3 is why it cannot be shown by
|
||||
; watching the data register: with the DMAC's OWN asserted, which it is at idle
|
||||
; here, MAME cannot tell a CPU-driven byte at $EA0015 from a DMAC-driven one.
|
||||
;
|
||||
; THE EVIDENCE THIS GATE PRODUCES, and none of it is a read of $EA0015:
|
||||
;
|
||||
; 1. THE SAME BYTES. The same sectors are read three times -- once by the PIO
|
||||
; path FINDINGS 58 measured, once by the channel with the bus held, once by
|
||||
; the channel stealing cycles -- and the HOST compares all three against its
|
||||
; own copy of the image. A transport that returns the wrong bytes without
|
||||
; saying so is the failure a checksum-free ring cannot survive (49.2).
|
||||
; 2. THE CPU'S OWN PROGRESS. MTC is sampled by the INSTRUCTION AFTER the one
|
||||
; that starts the channel. Held, it reads zero: the entire transfer
|
||||
; happened between two instructions, because the 68000 did not execute in
|
||||
; between. Stealing, it reads nearly the full count and the CPU then goes
|
||||
; round its wait loop thousands of times while the bytes arrive. That
|
||||
; difference IS "the DMAC held the bus", and it is a fact about the CPU.
|
||||
; 3. THE CHANNEL'S OWN ACCOUNT. CSR, CER, the final MTC and the final MAR:
|
||||
; the channel says it completed without error, moved every byte, and left
|
||||
; its memory pointer exactly one transfer-length past where it started.
|
||||
; 4. THE WINDOW IS REFUSED. A windowed read (58.3: 117 of 120 records start
|
||||
; part way into a sector) is rejected by the transport rather than silently
|
||||
; delivering the neighbouring records' bytes into the ring. P4a's
|
||||
; precondition is stated by the code that has it, not by a comment.
|
||||
;
|
||||
; WHAT IT DOES NOT SHOW. Not `W`. Not one clock of it. MAME's DMAC runs on
|
||||
; wall-clock attotimes (42.5) and its burst mode halts the CPU outright rather
|
||||
; than costing it cycles per operand, so this gate settles WHICH CONFIGURATION
|
||||
; WORKS and not what one costs. See src/player/dma.i for the three ways this
|
||||
; model bounds the question -- no EXREQ wiring, no single-address path, and only
|
||||
; burst modelled as held.
|
||||
|
||||
DGFLAG = $18600 ; 0 idle / 1 done
|
||||
DGREC = $18610 ; 3 x 32 B: rc, err, mtc0, spin, csr, cer, mtc, mar
|
||||
DGREC_SZ = 32
|
||||
DGWIN = $18680 ; u32 return of the WINDOWED dma read (want -1)
|
||||
DGWERR = $18684 ; u32 SC_ERR after it (want SCE_WINDOW)
|
||||
DGLBA = 1000 ; a NON-ZERO LBA throughout: a driver that emits
|
||||
; a malformed LBA field still passes LBA 0
|
||||
DGBLK = 4 ; 4 x 512 = 2,048 B
|
||||
DGDST0 = $20000 ; PIO
|
||||
DGDST1 = $24000 ; DMA, bus held
|
||||
DGDST2 = $28000 ; DMA, cycle stealing
|
||||
|
||||
org $10000
|
||||
start:
|
||||
clr.l DGFLAG.l
|
||||
|
||||
; ---- 1. the PIO path, unchanged, as the reference the other two are measured
|
||||
; against. It is here so that a DMA failure cannot be confused with a SCSI
|
||||
; protocol failure: if this one is wrong, nothing below is about the DMAC.
|
||||
bsr scsi_init
|
||||
move.l #DGLBA,d3
|
||||
moveq #DGBLK,d4
|
||||
lea DGDST0,a1
|
||||
bsr scsi_read
|
||||
lea DGREC,a0
|
||||
bsr dg_save
|
||||
|
||||
; ---- 2. the channel, WITH THE BUS HELD
|
||||
bsr scsi_init
|
||||
move.l #DM_HELD_DCR,DM_DCRV.l
|
||||
move.l #DM_HELD_OCR,DM_OCRV.l
|
||||
move.l #1,DM_USE.l ; after scsi_init, which clears it
|
||||
move.l #DGLBA,d3
|
||||
moveq #DGBLK,d4
|
||||
lea DGDST1,a1
|
||||
bsr scsi_read
|
||||
lea DGREC+DGREC_SZ,a0
|
||||
bsr dg_save
|
||||
|
||||
; ---- 3. the channel, STEALING CYCLES. Same bytes, same code, two register
|
||||
; values different -- which is what makes the comparison a comparison.
|
||||
bsr scsi_init
|
||||
move.l #DM_STEAL_DCR,DM_DCRV.l
|
||||
move.l #DM_STEAL_OCR,DM_OCRV.l
|
||||
move.l #1,DM_USE.l
|
||||
move.l #DGLBA,d3
|
||||
moveq #DGBLK,d4
|
||||
lea DGDST2,a1
|
||||
bsr scsi_read
|
||||
lea DGREC+2*DGREC_SZ,a0
|
||||
bsr dg_save
|
||||
|
||||
; ---- 4. and a WINDOWED read through the channel, which must be REFUSED. This
|
||||
; is the one test here that is expected to fail, and it has to fail LOUDLY: the
|
||||
; alternative is a channel writing a whole sector into a ring that has room for
|
||||
; a record, over the top of records the decoder has not finished with.
|
||||
bsr scsi_init
|
||||
move.l #DM_HELD_DCR,DM_DCRV.l
|
||||
move.l #DM_HELD_OCR,DM_OCRV.l
|
||||
move.l #1,DM_USE.l
|
||||
move.l #300,SC_WSKIP.l ; a record that starts 300 B into a sector
|
||||
move.l #1024,SC_WKEEP.l
|
||||
move.l #DGLBA,d3
|
||||
moveq #DGBLK,d4
|
||||
lea DGDST2,a1
|
||||
bsr scsi_read_win
|
||||
move.l d0,DGWIN.l
|
||||
move.l SC_ERR.l,DGWERR.l
|
||||
|
||||
move.l #1,DGFLAG.l
|
||||
hold: bra.s hold
|
||||
|
||||
; ---- one config's result, copied out of the shared reporting words before the
|
||||
; next run overwrites them. d0 = scsi_read's return; a0 = where it goes.
|
||||
dg_save:
|
||||
move.l d0,(a0)+
|
||||
move.l SC_ERR.l,(a0)+
|
||||
move.l DM_MTC0.l,(a0)+
|
||||
move.l DM_SPIN.l,(a0)+
|
||||
move.l DM_CSRF.l,(a0)+
|
||||
move.l DM_CERF.l,(a0)+
|
||||
move.l DM_MTCF.l,(a0)+
|
||||
move.l DM_MARF.l,(a0)+
|
||||
; and clear them, so a config that never reached the channel reports
|
||||
; zeros of its own rather than the previous config's numbers.
|
||||
clr.l DM_MTC0.l
|
||||
clr.l DM_SPIN.l
|
||||
clr.l DM_CSRF.l
|
||||
clr.l DM_CERF.l
|
||||
clr.l DM_MTCF.l
|
||||
clr.l DM_MARF.l
|
||||
rts
|
||||
|
||||
include "src/player/scsi.i"
|
||||
@@ -125,6 +125,8 @@ SCE_SELTMO = 1 ; the target never answered selection
|
||||
SCE_PHASE = 2 ; the bus went somewhere unexpected
|
||||
SCE_TIMEOUT = 3 ; a poll loop ran out of patience
|
||||
SCE_STATUS = 4 ; the target returned non-zero status
|
||||
SCE_WINDOW = 5 ; a WINDOWED read was asked of the DMAC,
|
||||
; which cannot drop bytes (58.3/P4a)
|
||||
|
||||
SC_ERR = $18200 ; u32 last error
|
||||
SC_STAT = $18204 ; u32 SCSI status byte from the last cmd
|
||||
@@ -237,6 +239,8 @@ sci1: nop
|
||||
move.b #$FF,SC_INTS ; INTS is cleared by writing its bits
|
||||
move.b #0,SC_PCTL
|
||||
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 SC_TRN.l
|
||||
move.l #0,SC_TAG.l
|
||||
bsr sc_snap
|
||||
@@ -439,6 +443,26 @@ sip3: move.b SC_DREG,(a1)+
|
||||
; the same limitation sc_in_pio's caller already carries -- and the fix is the
|
||||
; same one: d5 has to become what each phase actually delivered.
|
||||
sc_in_data:
|
||||
; ---- P4a: the DATA IN phase can be handed to the HD63450 instead, and
|
||||
; when it is, the CPU touches none of these bytes. src/player/dma.i.
|
||||
; The window is REFUSED rather than ignored: a channel writes a
|
||||
; contiguous run and cannot be told to drop the first 300 bytes, so a
|
||||
; windowed DMA read would deliver the neighbours' bytes into the ring
|
||||
; and the block loop has no bounds check to catch it (49.2, 58.3).
|
||||
; Refusing it here is what makes "sector-aligned records" a PRECONDITION
|
||||
; the transport states rather than an assumption it carries.
|
||||
tst.l DM_USE.l
|
||||
beq.s .pio
|
||||
tst.l SC_WSKIP.l
|
||||
bne.s .nowin
|
||||
move.l SC_WKEEP.l,d0
|
||||
cmp.l d1,d0
|
||||
bne.s .nowin
|
||||
bra sc_in_dma
|
||||
.nowin: move.l #SCE_WINDOW,SC_ERR.l
|
||||
moveq #-1,d0
|
||||
rts
|
||||
.pio:
|
||||
movem.l d6-d7,-(sp)
|
||||
move.b d2,SC_PCTL
|
||||
move.l d1,d0
|
||||
@@ -657,3 +681,9 @@ scr_phase:
|
||||
move.l #SCE_PHASE,SC_ERR.l
|
||||
moveq #-1,d0
|
||||
bra.s scr_out
|
||||
|
||||
; ---- the DMAC side of the data phase, ROADMAP P4a. Included unconditionally
|
||||
; so that there is ONE transport file: sc_in_data dispatches on DM_USE, which
|
||||
; scsi_init clears, so a front-end that never sets it assembles the same PIO
|
||||
; path FINDINGS 58 measured and executes not one instruction of the following.
|
||||
include "src/player/dma.i"
|
||||
|
||||
Reference in New Issue
Block a user