News: 11 March 2016 - Forum Rules

Author Topic: SNES DMA Question  (Read 6699 times)

RedComet

  • Hero Member
  • *****
  • Posts: 3171
    • View Profile
    • Twilight Translations
SNES DMA Question
« on: March 30, 2011, 10:25:02 pm »
The below code transfers two 2bpp 8x8 tiles from RAM at $7e6000 + (!ram_index << 5) to VRAM at $2000 + (!ram_index << 4). (I'm shifting by 4 due to the way VRAM is addressed.) I've stepped through this code and I don't see any problems with it that are glaringly obvious. The data is in RAM and all the addresses are being calculated correctly. However, once the lda #$80 : sta $420b is executed, none of the data is transfered to VRAM. I confirmed this using bsnes. So any suggestions?

Code: [Select]
;===============================;
;dma_char ;
;===============================;
;This routine DMA's the last ;
;8x16 character drawn in the ram;
;buffer to VRAM. ;
;===============================;

print "dma_char @ ",pc

dma_char:
php
rep #$20

lda !ram_index ;determine the location of the 8x16 to DMA
asl #4
pha

clc ;the location in  vram to put the 8x16
adc #$2000
sta $2116

lda #$1801
sta $4370

lda #$0020 ;two 8x8 2bpp tiles = $20 bytes
sta $4375

pla ;the location of the 8x16 in ram
asl
clc
adc #$6000
sta $4372

sep #$20

lda #$7e
sta $4374

wait_for_vblank:
lda $4212 ;check the vblank flag
and #$80
beq wait_for_vblank

lda #$80
sta $420b

plp
rts
Twilight Translations - More than just Dragonball Z. :P

KingMike

  • Forum Moderator
  • Hero Member
  • *****
  • Posts: 7260
  • *sigh* A changed avatar. Big deal.
    • View Profile
Re: SNES DMA Question
« Reply #1 on: March 30, 2011, 11:32:00 pm »
It was byuu's recommendation to check the vblank flag like this:
Code: [Select]
CHECK_MINUS:
 LDA $4212 : BMI CHECK_MINUS
CHECK_PLUS:
 LDA $4212 : BPL CHECK_PLUS
I think he said it was due to timing.

Also, it doesn't look like you set $2115, which controls the auto-increment of the VRAM address ($2116).
Set the highest bit of $2115 to 0 to increment after $2118 (low byte) is written or 1 to increment after $2119 (high byte).
I think you'd typically want the latter. The low 4 bits of $2115 control the increment amount, but I think for transferring tile data, I guess you'd want to leave them at 0000, which increments $2116 by 1 each time.
End result: you're most likely wanting to put lda #$80: sta $2115 somewhere before writing to $420B.

I'm just assuming the NMI routine isn't using channel 7 for DMA/HDMA and mucking with your writes. :)
« Last Edit: March 30, 2011, 11:43:05 pm by KingMike »
"My watch says 30 chickens" Google, 2018

RedComet

  • Hero Member
  • *****
  • Posts: 3171
    • View Profile
    • Twilight Translations
Re: SNES DMA Question
« Reply #2 on: March 31, 2011, 01:51:53 am »
After playing with this some more I discovered the problem. While waiting for vblank, an interrupt that altered the vram address was called. So I set the address after it's in vblank and just prior to initiating the transfer:

Code: [Select]
dma_char:
php
rep #$20

lda !ram_index ;determine the location of the 8x16 to DMA
asl #4
pha

clc ;the location in  vram to put the 8x16
adc #$2000
sta $2116

sta !vram_addy ;vram address will be set again prior to
;transfer in case an interrupt that accesses
;the ppu is called while waiting for vblank

lda #$1801
sta $4370

lda #$0020 ;two 8x8 2bpp tiles = $20 bytes
sta $4375

pla ;the location of the 8x16 in ram
asl
clc
adc #$6000
sta $4372

sep #$20

lda #$7e
sta $4374

wait_for_vblank:
lda $4212 ;check the vblank flag
bpl wait_for_vblank

php

rep #$20

lda !vram_addy
sta $2116

plp

lda #$80
sta $420b

plp
rts

Thanks for the help KingMike. :)
Twilight Translations - More than just Dragonball Z. :P

Nightcrawler

  • Hero Member
  • *****
  • Posts: 5792
    • View Profile
    • Nightcrawler's Translation Corporation
Re: SNES DMA Question
« Reply #3 on: March 31, 2011, 08:40:35 am »
I've fallen for that one before. It's easy to forget about the NMI routines effect on your code. Second biggest trip up point is when a game has a very long NMI routine. Your transfer can fail because there is simply not enough time left in the vblank period because the game already hogged it up with their NMI routine. A number of games typically use a transfer queue that you could take advantage of and eliminate having to manage this yourself, or compete with NMI.
TransCorp - Over 20 years of community dedication.
Dual Orb 2, Wozz, Emerald Dragon, Tenshi No Uta, Glory of Heracles IV SFC/SNES Translations

MathOnNapkins

  • Forum Moderator
  • Hero Member
  • *****
  • Posts: 647
  • Who ya gonna call
    • View Profile
    • Arc-Nova - Rohmackin' and Chiptunin'
Re: SNES DMA Question
« Reply #4 on: March 31, 2011, 10:10:18 am »
I typically get around this by adding my own VRAM handler code to the end of the NMI routine. Of course, you could still run out of V-Blank time if NMI is doing a lot of work on a particular frame. There are quasi-reliable ways of checking if you have enough time, though.

tomaitheous

  • Hero Member
  • *****
  • Posts: 543
    • View Profile
    • PC Engine Dev
Re: SNES DMA Question
« Reply #5 on: April 13, 2011, 12:02:03 am »
Is the cpu halted when the DMA channels are in progress during vblank?  If not, would it be best to write a hook at the beginning of the NMI to do the transfer while the NMI code does it's thing? Or (assuming the cpu is not halted) can the cpu simultaneously write to the vram port while DMA is happening?

Nightcrawler

  • Hero Member
  • *****
  • Posts: 5792
    • View Profile
    • Nightcrawler's Translation Corporation
Re: SNES DMA Question
« Reply #6 on: April 13, 2011, 08:57:15 am »
On the SNES, the CPU is halted when DMA takes place. A few cycles after you write to $420b, DMA begins. The CPU is halted until all DMAs finish. HDMA will pause DMA if it is set to occur during this time. Conflicting DMA on channels used for HDMA are terminated.
TransCorp - Over 20 years of community dedication.
Dual Orb 2, Wozz, Emerald Dragon, Tenshi No Uta, Glory of Heracles IV SFC/SNES Translations