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
404 lines
20 KiB
ArmAsm
404 lines
20 KiB
ArmAsm
; 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 = $18800 ; 9 x 32 B: rc, err, mtc0, spin, csr, cer, mtc, mar
|
|
; $18800 AND NOT $18610, WHERE THIS LIVED: nine
|
|
; records of 32 B run to $188FF, and from $18610
|
|
; they would have run over DGWIN at $18700 --
|
|
; the window run's own result, which run 10 then
|
|
; writes back. A silent overlap between two
|
|
; runs' evidence is the kind of thing that makes
|
|
; a gate report the wrong run's numbers.
|
|
DGREC_SZ = 32
|
|
DGWIN = $18700 ; u32 return of the WINDOWED dma read (want -1)
|
|
DGWERR = $18704 ; u32 SC_ERR after it (want SCE_WINDOW)
|
|
DGR20 = $18708 ; u32 R20 as it stood during run 4
|
|
DGR20N = $1870C ; u32 R20 as it stood during run 5 (the control)
|
|
DGR20C = $18710 ; u32 R20 as it stood during run 6 (chained)
|
|
DGR20P = $18714 ; u32 R20 as it stood during run 9 (palette+rows)
|
|
R20_BUF = $0916 ; 256-colour, 31.5 kHz, G-VRAM SET TO BUFFER
|
|
R20_MSK = $0116 ; the SAME, bit 11 CLEAR -- the negative control
|
|
DGDST4 = $C0C000 ; where the control writes
|
|
DGCHA = $19000 ; the array-chain table: 8 x {u32 MAR, u16 MTC}
|
|
DGCHN = 8 ; entries
|
|
DGCROW = 256 ; bytes an entry carries -- one packed picture row
|
|
DGDST5 = $C10000 ; and the 8 row bases, at the 1024 B line stride
|
|
CRTC20 = $E80028 ; CRTC R20
|
|
DGDST3 = $C08000 ; DMA straight into GVRAM, BUFFER MODE (47.6.2)
|
|
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
|
|
|
|
; ---- runs 7-9: THE PALETTE (ROADMAP K1, FINDINGS 61.9's first open item).
|
|
DGPAL = $E82000 ; the GRAPHIC palette: 256 words, GGGGGRRRRRBBBBBI
|
|
DGPALN = 256 ; words in it -- and 512 B is exactly ONE sector
|
|
DGPBLK = 1 ; so the whole palette is one block off the disc
|
|
DGPOIS = $A500 ; the poison: word i = DGPOIS|i. A palette that
|
|
; still reads this was not written by anything.
|
|
DGDST6 = $2C000 ; run 8's destination: RAM, so the palette is
|
|
; left alone and must still read poison
|
|
DGDST7 = $C14000 ; run 9's six GVRAM rows, at the 1024 B stride
|
|
DGCHA2 = $19100 ; run 9's array: 7 x {u32 MAR, u16 MTC}
|
|
DGCHN2 = 7 ; palette 512 B + 6 rows x 256 B = 2,048 B
|
|
DGCROW2 = 256
|
|
DGPS7 = $1A000 ; the palette as it stood after run 7 ...
|
|
DGPS8 = $1A200 ; ... after run 8 (the control: poison) ...
|
|
DGPS9 = $1A400 ; ... and after run 9 (chained). SNAPSHOTS, not
|
|
; a late read: each run overwrites the previous
|
|
; run's palette, so a host that looks once at
|
|
; the end sees only the last of the three.
|
|
|
|
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. THE CHANNEL WRITING GVRAM, IN BUFFER MODE. 47.6.2: "the DMAC has not
|
|
; been near this" -- 44.7 costed a device->GVRAM transfer and 47 built the packed
|
|
; layout, and no run in this tree has ever pointed a channel at $C00000. Two
|
|
; separate things are being asked at once and both are write-path questions:
|
|
;
|
|
; a. can a channel write GVRAM AT ALL? Nothing says it cannot -- the DMAC
|
|
; writes through the same program address space the CPU does -- but a
|
|
; transport that silently drops its writes into a device handler is exactly
|
|
; the failure this gate exists to catch, and the SPC's own bytes make a
|
|
; better witness than a fill pattern.
|
|
; b. does a BYTE-wide channel fill the PACKED layout? 47.1 measured the write
|
|
; path with word writes from the CPU. A dual-address channel with an 8-bit
|
|
; device port writes BYTES, and MAME's gvram_w in buffer mode passes
|
|
; mem_mask straight through -- so an even byte should land in the HIGH half
|
|
; of its word (page 1) and an odd byte in the LOW half (page 0). If it
|
|
; does, a linear DMA of a stream interleaved (right<<8)|left -- which is
|
|
; exactly show_frame256_packed.lua's layout -- fills the screen with no CPU
|
|
; in the loop at all.
|
|
;
|
|
; R20 bit 11 is left SET across the readback on purpose: gvram_r returns the raw
|
|
; word in buffer mode and the masked byte outside it, so clearing it first would
|
|
; hide half of what is being measured.
|
|
;
|
|
; R20 IS WRITTEN OUTRIGHT AND NOT OR-ED INTO. The first cut of this run set bit
|
|
; 11 on top of whatever the IPL left, and the IPL leaves $0B16 (22.1) -- bit 11
|
|
; ALREADY SET, and COL = %11, the 65,536-colour setup, which writes whole words
|
|
; with or without the bit. The run passed and proved nothing: it was a test that
|
|
; could not fail. Run 5 below is the control that makes this one mean something,
|
|
; and the two differ in EXACTLY BIT 11.
|
|
bsr scsi_init
|
|
move.w #R20_BUF,CRTC20.l
|
|
move.l #R20_BUF,DGR20.l
|
|
move.l #DM_HELD_DCR,DM_DCRV.l
|
|
move.l #DM_HELD_OCR,DM_OCRV.l
|
|
move.l #1,DM_USE.l
|
|
move.l #DGLBA,d3
|
|
moveq #DGBLK,d4
|
|
lea DGDST3,a1
|
|
bsr scsi_read
|
|
lea DGREC+3*DGREC_SZ,a0
|
|
bsr dg_save
|
|
|
|
; ---- 5. THE NEGATIVE CONTROL: the same transfer with bit 11 CLEAR. In masked
|
|
; 256-colour mode gvram_w takes `data & 0x00ff` and IGNORES mem_mask, so a byte
|
|
; written to an EVEN address -- where the 68000 puts the MS byte, and where every
|
|
; other disc byte lands -- contributes nothing and cannot be read back. Half the
|
|
; transfer must be lost, and if it is not, run 4 was not measuring the bit.
|
|
bsr scsi_init
|
|
move.w #R20_MSK,CRTC20.l
|
|
move.l #R20_MSK,DGR20N.l
|
|
move.l #DM_HELD_DCR,DM_DCRV.l
|
|
move.l #DM_HELD_OCR,DM_OCRV.l
|
|
move.l #1,DM_USE.l
|
|
move.l #DGLBA,d3
|
|
moveq #DGBLK,d4
|
|
lea DGDST4,a1
|
|
bsr scsi_read
|
|
lea DGREC+4*DGREC_SZ,a0
|
|
bsr dg_save
|
|
move.w #R20_BUF,CRTC20.l ; back to buffer mode, so the host reads
|
|
; RAW WORDS out of both destinations
|
|
|
|
; ---- 6. THE ROW STRIDE, WHICH IS THE REAL SHAPE OF THE TRANSFER. Runs 4 and 5
|
|
; wrote 2,048 contiguous bytes, and no picture is contiguous: a packed row is
|
|
; 256 B of a 1024 B line stride, so a frame is 192 destinations and not one.
|
|
; 46.6 said "no stride for a DMAC to skip" about the bytes WITHIN a row and left
|
|
; the rows themselves unexamined; a channel cannot skip 768 B any more than it
|
|
; could skip the 300 B in front of a record (run 10 below).
|
|
;
|
|
; The MC68450 answers this with SEQUENTIAL ARRAY CHAINING -- an array of 6-byte
|
|
; {u32 MAR, u16 MTC} entries it walks by itself -- and MAME implements it. So
|
|
; the question is not whether the CPU can restart the channel 192 times a frame;
|
|
; it is whether it has to at all. Eight rows here, out of the same LBA and the
|
|
; same 2,048 B, so the ONLY difference from run 4 is where the bytes land.
|
|
bsr scsi_init
|
|
move.w #R20_BUF,CRTC20.l
|
|
move.l #R20_BUF,DGR20C.l
|
|
lea DGCHA,a0
|
|
lea DGDST5,a1
|
|
moveq #DGCHN-1,d5
|
|
dg_mkch:
|
|
move.l a1,(a0)+ ; MAR: this row's base
|
|
move.w #DGCROW,(a0)+ ; MTC: 256 bytes of it
|
|
lea 1024(a1),a1 ; the next row is a line stride away
|
|
dbra d5,dg_mkch
|
|
move.l #DM_HELD_DCR,DM_DCRV.l
|
|
move.l #DM_HELD_OCR|$08,DM_OCRV.l ; OCR CHAIN = %10, array chain
|
|
move.l #DGCHA,DM_BARV.l
|
|
move.l #DGCHN,DM_BTCV.l
|
|
move.l #1,DM_USE.l
|
|
move.l #DGLBA,d3
|
|
moveq #DGBLK,d4
|
|
lea DGDST5,a1 ; ignored under chaining; passed so the
|
|
; call site reads the same as the others
|
|
bsr scsi_read
|
|
lea DGREC+5*DGREC_SZ,a0
|
|
bsr dg_save
|
|
clr.l DM_BARV.l ; and OFF again, so run 7 is unchained
|
|
|
|
; ---- 7. THE PALETTE. Can a channel write $E82000? (FINDINGS 61.9, ROADMAP
|
|
; K1.) Runs 4-6 put the PICTURE on the channel; a packed frame is a picture AND
|
|
; a palette, and if the palette registers take a byte-wide DMA the way GVRAM
|
|
; does in buffer mode then the palette is a 193rd array-chain entry and ONE
|
|
; channel start paints a whole frame -- no per-frame CPU work in the video path
|
|
; at all. If they do not, the CPU writes 256 words a frame (61.9 derives that
|
|
; at ~2,370 clocks, 0.28% of a frame) and the architecture still stands; this is
|
|
; the difference between cheap and free, and it is worth one run to know which.
|
|
;
|
|
; 512 B IS THE WHOLE PALETTE AND EXACTLY ONE SECTOR, which is why this run reads
|
|
; one block where the others read four: a transfer that ran long would write
|
|
; $E82200 (the TEXT palette) and then $E82400 (the video controller's own
|
|
; registers, priority included), and a probe that reconfigures the video
|
|
; controller as a side effect is not a probe.
|
|
;
|
|
; THE PALETTE IS POISONED FIRST, and that is what stops this being run 4's trap
|
|
; a second time. A destination that already holds the right bytes cannot tell a
|
|
; channel that wrote them from a channel that did nothing; RAM at $20000 was
|
|
; zero and the record is mostly pad, so "it matches" has been a weak claim all
|
|
; session. Word i is set to $A500|i, which no 512 B of container matches by
|
|
; accident, and the host reports how many of the 512 positions the poison and
|
|
; the disc actually differ in rather than assuming all of them.
|
|
bsr scsi_init
|
|
bsr dg_poison
|
|
move.l #DM_HELD_DCR,DM_DCRV.l
|
|
move.l #DM_HELD_OCR,DM_OCRV.l
|
|
move.l #1,DM_USE.l
|
|
move.l #DGLBA,d3
|
|
moveq #DGPBLK,d4
|
|
lea DGPAL,a1
|
|
bsr scsi_read
|
|
lea DGPS7,a1
|
|
bsr dg_palsnap ; before run 8 overwrites it
|
|
lea DGREC+6*DGREC_SZ,a0
|
|
bsr dg_save
|
|
|
|
; ---- 8. THE NEGATIVE CONTROL, and it is an ATTRIBUTION control rather than a
|
|
; mechanism one. Run 5's control could point at a mode bit; there is no mode
|
|
; bit here, so what has to be excluded is that run 7's palette held the disc's
|
|
; bytes for some reason OTHER than the channel having written them there --
|
|
; a readback that aliases somewhere else, the SPC's own path touching the
|
|
; registers, the poison never having landed.
|
|
;
|
|
; Same transfer, same channel, same bytes; ONE thing different, the destination
|
|
; address. The disc's bytes must appear at $2C000, and the palette must still
|
|
; read poison in all 256 words. Two claims from one run, and the second is the
|
|
; one that makes run 7 mean anything: it also proves the CPU's poison writes
|
|
; reach the registers the host reads back, which is the positive half.
|
|
bsr scsi_init
|
|
bsr dg_poison
|
|
move.l #DM_HELD_DCR,DM_DCRV.l
|
|
move.l #DM_HELD_OCR,DM_OCRV.l
|
|
move.l #1,DM_USE.l
|
|
move.l #DGLBA,d3
|
|
moveq #DGPBLK,d4
|
|
lea DGDST6,a1
|
|
bsr scsi_read
|
|
lea DGPS8,a1
|
|
bsr dg_palsnap ; must be poison, word for word
|
|
lea DGREC+7*DGREC_SZ,a0
|
|
bsr dg_save
|
|
|
|
; ---- 9. THE 193rd ENTRY: one start, the palette AND the picture rows. This is
|
|
; the run K1 exists for. Runs 7 and 8 only show that a channel can write the
|
|
; palette registers; what the architecture needs is that ONE array chain can
|
|
; cross from a hardware register area into GVRAM without the CPU between them,
|
|
; because a frame is a palette entry followed by 192 row entries and the whole
|
|
; claim is that the CPU starts the channel once.
|
|
;
|
|
; Seven entries, 2,048 B, out of the same LBA as everything else: 512 B into the
|
|
; palette and then six rows of 256 B at the 1024 B line stride. The destination
|
|
; regions are of two different KINDS -- device registers and video RAM in buffer
|
|
; mode -- which is exactly the crossing that has never been run.
|
|
bsr scsi_init
|
|
bsr dg_poison
|
|
move.w #R20_BUF,CRTC20.l
|
|
move.l #R20_BUF,DGR20P.l
|
|
lea DGCHA2,a0
|
|
move.l #DGPAL,(a0)+ ; entry 0: the palette, a whole sector
|
|
move.w #DGPALN*2,(a0)+
|
|
lea DGDST7,a1
|
|
moveq #DGCHN2-2,d5 ; the remaining six are picture rows
|
|
dg_mkch2:
|
|
move.l a1,(a0)+
|
|
move.w #DGCROW2,(a0)+
|
|
lea 1024(a1),a1
|
|
dbra d5,dg_mkch2
|
|
move.l #DM_HELD_DCR,DM_DCRV.l
|
|
move.l #DM_HELD_OCR|$08,DM_OCRV.l ; OCR CHAIN = %10, array chain
|
|
move.l #DGCHA2,DM_BARV.l
|
|
move.l #DGCHN2,DM_BTCV.l
|
|
move.l #1,DM_USE.l
|
|
move.l #DGLBA,d3
|
|
moveq #DGBLK,d4
|
|
lea DGDST7,a1 ; ignored under chaining, as in run 6
|
|
bsr scsi_read
|
|
lea DGPS9,a1
|
|
bsr dg_palsnap
|
|
lea DGREC+8*DGREC_SZ,a0
|
|
bsr dg_save
|
|
clr.l DM_BARV.l ; and OFF again, so run 10 is unchained
|
|
|
|
; ---- 10. 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
|
|
|
|
; ---- the poison. Word i of the graphic palette <- $A500|i, written by the
|
|
; 68000 itself. Two jobs: a destination that cannot hold the right answer by
|
|
; accident, and a known pattern the control run reads back out of the registers
|
|
; to show the readback path shows what is actually in them.
|
|
dg_poison:
|
|
movem.l d0-d2/a0,-(sp)
|
|
lea DGPAL,a0
|
|
moveq #0,d1 ; i
|
|
move.w #DGPALN-1,d0
|
|
.p: move.w d1,d2
|
|
ori.w #DGPOIS,d2 ; $A500|i, and i never exceeds 255
|
|
move.w d2,(a0)+
|
|
addq.w #1,d1
|
|
dbra d0,.p
|
|
movem.l (sp)+,d0-d2/a0
|
|
rts
|
|
|
|
; ---- the palette as it stands, copied to (a1) by the 68000 READING THE
|
|
; REGISTERS. A snapshot and not a late host read: each palette run overwrites
|
|
; the last one's result, so all three have to be kept while they are true. It
|
|
; is also the same shape of evidence 53.3 used for pal_pack -- the words come
|
|
; back out of $E82000 rather than out of the buffer they were built in.
|
|
dg_palsnap:
|
|
movem.l d0/a0-a1,-(sp)
|
|
lea DGPAL,a0
|
|
move.w #DGPALN-1,d0
|
|
.s: move.w (a0)+,(a1)+
|
|
dbra d0,.s
|
|
movem.l (sp)+,d0/a0-a1
|
|
rts
|
|
|
|
include "src/player/scsi.i"
|