So you're making a Super Nintendo game, (or demo) and you run into a problem. You want to animate sprites and BG tiles, but you ran out of v-blank time with all the heavy DMAing you're doing. What this tutorial covers is how to avoid the dreaded v-blank limit, without blowing your brains out with frustration.

Making a table for DMA updates

You might at first take the "hardwired" approach, where you individually program everything that needs to be DMAed in the v-blank routine. This results in an overcomplicated v-blank routine, with very rigid animation patterns. You probably don't want this.

A more flexible method is to use a table for DMA updates that is calculated during the previous frame. Using a DMA table is almost like DMAing in the middle of the frame.

Use the following routine to DMA during v-blank, using a DMA table.


dma:
php
phd
rep #$20
sep #$10
ldx #$80
stx $2115
ldx #$00
ldy #$01
lda #$4300
tcd				;;move direct page to dma registers
lda #$1801
sta $00				;;dma to $2118, 16-bit dma writes

dma_loop:
lda !dma_address,x
sta $02
lda !dma_bank,x
sta $04
lda !dma_legnth,x
beq end_dma			;;end dma if legnth is 0
sta $05
lda !dma_destination,x
sta $2116
sty $420b
inx #2
bra dma_loop

end_dma:
stz !dma_table_pointer
stz !total_dma_legnth
pld
plp
rts

Take advantage of the animation framerate

You're probably not going to animate objects and backgrounds at 60fps. Not even Disney animates their characters at such a high frequency. You're most likely going to animate characters at 30fps or less. Therefore you have duplicate animation frames. Updating a character at 60fps when he is animated at 30fps is a waste of DMA.

In order to take advantage of low animation framerates, you need 2 registers to define your animation frame, for each animated object:

  1. currently loaded frame register - this register holds the frame number of whatever frame is currently loaded in vram.
  2. requested frame register - this register holds the frame number of the frame that should appear next.

Compare the requested frame with the currently loaded frame. If they match, then there is no need to request a DMA update. If they don't match, request a DMA update.

Maximum DMA limit

Most importantly, you need the game to keep track of how much DMA time is being spent within a frame. Before the game makes a DMA request, it has to check how much DMA time it has left. If it exceeds the limit (6,14KB for NTSC, 14,65KB for PAL) the DMA request should be ignored. Remember the "currently loaded frame register" I mentioned in last section? It should only be updated if a DMA request has been sent. This way, if there isn't enough time to send a DMA request one frame, it will try again the next.