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.
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
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:
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.
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 (5kB for NTSC, 10kB 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.