; 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_BTC = DMACB+$1A ; base transfer count, WORD (array chain) DM_BAR = DMACB+$1C ; base address, LONG (array chain) 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 ; ---- SEQUENTIAL ARRAY CHAINING, and it is OFF unless a caller asks for it. ; A device->GVRAM transfer cannot be one contiguous run: a picture row is 256 B ; of a 1024 B line stride, so 192 rows want 192 destinations. The MC68450 walks ; an array of 6-byte {u32 MAR, u16 MTC} entries for exactly this, and MAME's ; hd63450 implements it (`(ocr & 0x0c) == 0x08`, dma_transfer_start and ; dma_transfer_continue). DM_BARV = 0 means no chaining and NOTHING below ; changes, which is what stream.s gets: this file is included by the player as ; well as by the gate, and a mailbox that defaults to a new behaviour is how ; 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 ; 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.l DM_BARV.l,d0 bne.s .chain move.w d5,DM_MTC move.l a1,DM_MAR bra.s .darset .chain: ; MAR and MTC are NOT written: the channel loads both from the array's ; first entry when it starts, and reloads them from the next entry at ; every count-out. Writing them here would be writing registers the ; hardware is about to overwrite, which reads like a contract and is not. move.l d0,DM_BAR move.l DM_BTCV.l,d0 move.w d0,DM_BTC .darset: 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.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 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