Skip to content

Instantly share code, notes, and snippets.

@flamewing
Last active June 19, 2021 12:37
Show Gist options
  • Save flamewing/ac4b8586f74c74fe24fe30e5e0261fb0 to your computer and use it in GitHub Desktop.
Save flamewing/ac4b8586f74c74fe24fe30e5e0261fb0 to your computer and use it in GitHub Desktop.
Some improved DMA macros, and a couple new DMA macros, for the Mega Drive
dmaSource function addr,((addr>>1)&$7FFFFF)
dmaLength function length,((length>>1)&$7FFF)
dmaCommLength function length,(($9400|((length&$FF00)>>8))<<16)|($9300|(length&$FF))
dmaCommSrcLow function source,(($9600|((source&$FF00)>>8))<<16)|($9500|(source&$FF))
dmaCommSrcHigh function source,$9700|(((source&$FF0000)>>16)&$7F)
; Tells the VDP to copy a region of 68k memory to VRAM or CRAM or VSRAM.
dma68kToVDP macro src,dest,length,type,bswap
if MOMPASS>1
if ((src)&1)<>0
fatal "DMA is transferring from odd source $\{src}! This will transfer the wrong data, because the VDP ignores the low bit of source address. Please align the data to an even address."
endif
if ((dest)&1)<>0
if (type)<>VRAM
fatal "DMA is transferring to odd destination $\{dest}! This is ignored on real hardware for CRAM and VSRAM, and may behave inconsistently on emulators. Please ensure that you transfer to even destinations only."
else
fatal "DMA is transferring to odd destination $\{dest}! This will byte-swap the data copied. If you want to do this, then set the last parameter of the macro to 1 instead."
endif
endif
if ((length)&1)<>0
fatal "DMA is transferring an odd number of bytes $\{length}! DMA can only transfer an even number of bytes."
endif
if (length)==0
fatal "DMA is transferring 0 bytes (becomes a 128kB transfer). If you really mean it, pass 128kB (131072) instead."
endif
if (((src)+(length)-1)>>17)<>((src)>>17)
fatal "DMA crosses a 128kB boundary. You should either split the DMA manually or align the source adequately."
endif
endif
if ~~(bswap)
set .c,0
else
if (type)<>VRAM
fatal "Only VRAM supports byte-swap on DMA."
endif
set .c,1
endif
lea (VDP_control_port).l,a5
move.l #dmaCommLength(dmaLength(length)),(a5)
move.l #dmaCommSrcLow(dmaSource(src)),(a5)
move.l #(dmaCommSrcHigh(dmaSource(src))<<16)|((vdpComm((dest)|.c,type,DMA)>>16)&$FFFF),(a5)
move.w #(vdpComm((dest)|.c,type,DMA)&$FFFF),(DMA_data_thunk).w
move.w (DMA_data_thunk).w,(a5)
endm
; Tells the VDP to fill a region of VRAM with a certain byte.
; VRAM fill works like this: the write to the data port happens as normal; that is:
; * the high byte is written to address^1
; * the low byte is written to address
; Then, the remainder of the fill goes like:
; * the address is incremented by the autoincrement register value
; * high byte of word written to data port is written to address^1
; * repeat until done
; For an even target address, this means:
; * the high byte is written to address+1
; * the low byte is written to address
; * the high byte is written to address (overwrites previous write)
; * the high byte is written to address+3
; * the high byte is written to address+2
; * etc.
; For an odd target address, this means:
; * the high byte is written to address
; * the low byte is written to address+1
; * the high byte is written to address+3
; * the high byte is written to address+2
; * etc.
; This allows reducing the length of the fill by 1.
; It is possible to fill to CRAM and to VSRAM, but it is buggy,
; and not emulated in most emulators.
dmaFillVRAM macro byte,addr,length
if MOMPASS>1
if ((addr)&1)<>0
fatal "DMA is filling an odd destination $\{addr}! This will cause a spurious write to the address immediately before. Please ensure you fill starting at even addresses only."
endif
if (length)==0
fatal "DMA is filling 0 bytes (becomes a 64kB fill). If you really mean it, pass 64kB (65536) instead."
endif
endif
lea (VDP_control_port).l,a5
move.l #dmaCommLength(2*(length-2)),(a5)
move.l #$8F019780,(a5) ; VRAM pointer increment: $0001, VRAM fill
; Forcing the low bit of address to be 1, as described before.
move.l #vdpComm((addr)|1,VRAM,DMA),(a5)
move.w #((byte)<<8)|(byte),(VDP_data_port).l
.loop:
move.w (a5),d1
btst #1,d1
bne.s .loop ; busy loop until the VDP is finished filling...
move.w #$8F02,(a5) ; VRAM pointer increment: $0002
endm
; Tells the VDP to copy from a region of VRAM to another.
dmaCopyVRAM macro src,dest,length
if MOMPASS>1
if (length)==0
fatal "DMA is copying 0 bytes (becomes a 64kB copy). If you really mean it, pass 64kB (65536) instead."
endif
endif
lea (VDP_control_port).l,a5
move.l #dmaCommLength(2*length),(a5)
move.l #dmaCommSrcLow(src),(a5)
move.l #$8F0197C0,(a5) ; VRAM pointer increment: $0001, VRAM copy
move.l #vdpComm(addr,VRAM,DMA),(a5)
.loop:
move.w (a5),d1
btst #1,d1
bne.s .loop ; busy loop until the VDP is finished filling...
move.w #$8F02,(a5) ; VRAM pointer increment: $0002
endm
KosArt_To_VDP macro art,dest,bswap
if MOMPASS>1
if ((dest)&1)<>0
fatal "DMA is transferring to odd destination $\{dest}! This will byte-swap the data copied. If you want to do this, then set the last parameter of the macro to 1 instead."
endif
endif
if ~~(bswap)
set .c,0
else
set .c,1
endif
lea (art).l,a0 ; Decompress source
lea (Chunk_Table).l,a1 ; Decompress destination/transfer source
jsr (KosDec).w
move.l a1,d0 ; Move end address of decompressed art to d0
lsr.w #1,d0 ; d0 is the transfer length in words because (Chunk_Table&$FFFF) == 0
pea ($94009300).l ; Values for setting DMA length registers
movep.w d0,1(sp) ; Longword at a1 is commands for setting transfer length
lea (VDP_control_port).l,a5
move.l (sp)+,(a5) ; Send command to specify DMA transfer length
move.l #dmaCommSrcLow(dmaSource(Chunk_Table)),(a5)
move.l #(dmaCommSrcHigh(dmaSource(Chunk_Table))<<16)|((vdpComm((dest)|.c,VRAM,DMA)>>16)&$FFFF),(a5)
move.w #(vdpComm((dest)|.c,VRAM,DMA)&$FFFF),(DMA_data_thunk).w
move.w (DMA_data_thunk).w,(a5)
endm
@flamewing
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment