It is currently Sun Sep 15, 2019 7:22 pm

 All times are UTC - 7 hours

### Forum rules

• For making cartridges of your Super NES games, see Reproduction.

 Page 1 of 1 [ 6 posts ]
 Print view Previous topic | Next topic
Author Message
 Post subject: Abstracting DMA codePosted: Tue Sep 04, 2018 11:54 am

Joined: Wed May 19, 2010 6:12 pm
Posts: 2892
Something that has always bothered me is how many registers you need to write to in order to get DMA to working.

Not only do you have to touch \$4300-\$4306 and \$420b, but if you need to touch other registers depending on what you're trying to do:

\$2115 to \$2117 for VRAM
\$2102 and \$2103 for OAM
\$2121 for CGRAM
\$2181 to \$2183 for WRAM

I remember somebody saying "but you only need to make a routine that writes to these registers ONCE" but how do I make a routine that writes to a bunch of registers without having to pass a ton of variables, which would require as much code as I would need to write to said registers in the first place?

I know there are some commonly used arrangements such as:

-Writing bytes to \$2104
-Writing bytes to \$2122
-Writing bytes to \$2180
-Writing words to \$2118, incrementing by 1 when \$2119 is written
-Writing bytes to \$2118, incrementing by 1 when \$2118 is written
-Writing bytes to \$2119, incrementing by 1 when \$2119 is written
-Writing words to \$2118, incrementing by 32 when \$2119 is written

Actually there are a TON of commonly used arrangements, and even more potentially useful arrangements, but I know for fact that most combinations don't make any sense, so there must be a way of pre-baking in patterns that make sense.

There's also a dilema of immediate parameters vs variable parameters. Immediate parameters you can store in an array, but variable parameters you have to either hardcode or use some kind of parameter passing method.

Top

 Post subject: Re: Abstracting DMA codePosted: Tue Sep 04, 2018 12:07 pm

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 4208
Most people made macros to do this. There wasn't (and isn't) a fear of "passing a ton of {variables,arguments}". It's inherent to programming.

In commercial games I've seen source code for, most use a combination of macros for some DMA routines, while others are actually jsl-able based on what's passed on the stack, or referenced directly by a small set of variables in DP/RAM that are dedicated solely for this purpose. SNES has a tolerably large amount of RAM, so this isn't very wasteful.

The advice "but you only need to make a routine that writes to these registers ONCE" is almost certainly referring to the fact that you can write a general-purpose routine that handles \$4300-4306 + \$420b for you, while the rest is up to you.

Some games I believe have some DMA that's done inside of NMI, where some of the "source" values come from DP/RAM variables, while others are hard-coded (e.g. your graphics transfer routine uses a static length of data so you don't need to tweak the DMA length register every time; palette update DMA, etc.) based on a global variable that controls whether or not the DMA should be done (so that it isn't being done every single frame). But that's more about "saving time" (cycle-wise) than "how many registers I have to write to".

if the concern is truly how many registers you need to write to (read: the quantity of), then I don't think anything can be done about that. You're going to have to write to said registers to accomplish DMA in the fashion you want regardless. It's how the system was designed. How can this be fought aside from inventing a time machine and going back in time to Nintendo Co. Ltd and demanding they design the system differently? Hmm, feeling of deja vu...

Top

 Post subject: Re: Abstracting DMA codePosted: Tue Sep 04, 2018 12:20 pm

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 7582
Macros are indeed a great way to eliminate subroutine argument boilerplate. In ca65:

Code:
.macro FUNC a0, a1, a2, a3
lda #a0
sta arg0
ldy #a1
ldx #a2
lda #a3
jsr func
.endmacro

; calling becomes one line in your code
FUNC 1, 2, 3, 4

And if you later decide that you're calling this function so much that it would be good to optimize the calls for size instead of speed you can modify the function and macro without having to change any of the other calling code:

Code:
.macro FUNC a0, a1, a2, a3
jsr func
.byte a0, a1, a2, a3
.endmacro

In this case, the start of func should now add +4 to the return value on the stack, and load the 4 variables from where the original return address pointed. Adds some cycles of overhead but it reduces the argument part of each call to just the direct bytes.

One thing that's hard to deal with in macros is immediate values vs. arguments that should come from memory locations, current registers, etc. So you can't treat it like a C function where any result goes into an argument, but you can make variations of the macro to take different argument types if you need that.

Top

 Post subject: Re: Abstracting DMA codePosted: Tue Sep 04, 2018 12:55 pm

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21591
Location: NE Indiana, USA (NTSC)
Fortunately, the 65816 has just enough registers to pass all arguments of a DMA copy. From my first attempt at programming the Super NES:
Code:
;;
; Copies data to the S-PPU using DMA channel 0.
; @param DBR source bank
; @param Y number of bytes to copy
; @param A 15-8: destination PPU register; 7-0: DMA mode
.proc ppu_copy
php
setaxy16
sta \$804300
stx \$804302
sty \$804305
seta8
phb
pla
sta \$804304
lda #%00000001
sta \$80420B
plp
rtl
.endproc

The use of the mirror in \$800000 forces the write to go to a bank with MMIO even if DBR is in \$40-\$7F or \$C0-\$FF.

_________________
Pin Eight | Twitter | GitHub | Patreon

Top

 Post subject: Re: Abstracting DMA codePosted: Tue Sep 04, 2018 1:12 pm

Joined: Wed May 19, 2010 6:12 pm
Posts: 2892
That gives me an idea:

Y = length
DP = source bank

Code:
stx \$2116
ldx #\$1801
stx \$4300
sta \$4302
sep #\$20
tdc
sta \$4304
sty \$4305
lda #\$80
sta \$2115
lda #\$01
sta \$420b

Top

 Post subject: Re: Abstracting DMA codePosted: Tue Sep 04, 2018 1:57 pm

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 4208
Yup, seen games that do it that way too. Just make sure that if it's a subroutine you push/pop 16-bit A/X/Y and P if appropriate.

Top

 Display posts from previous: All posts1 day7 days2 weeks1 month3 months6 months1 year Sort by AuthorPost timeSubject AscendingDescending
 Page 1 of 1 [ 6 posts ]

 All times are UTC - 7 hours

#### Who is online

Users browsing this forum: No registered users and 6 guests

 You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot post attachments in this forum

Search for:
 Jump to:  Select a forum ------------------ NES / Famicom    NESdev    NESemdev    NES Graphics    NES Music    Homebrew Projects       2019 NESdev Competition       2018 NESdev Competition       2017 NESdev Competition       2016 NESdev Competition       2014 NESdev Competition       2011 NESdev Competition    Newbie Help Center    NES Hardware and Flash Equipment       Reproduction    NESdev International       FCdev       NESdev China       NESdev Middle East Other    General Stuff    Membler Industries    Other Retro Dev       SNESdev       GBDev    Test Forum Site Issues    phpBB Issues    Web Issues    nesdevWiki