Some questions about the APU DMC

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
Drakim
Posts: 97
Joined: Mon Apr 04, 2016 3:19 am

Some questions about the APU DMC

Post by Drakim »

There is a lot of spread out information about the APU DMC on the web, and I've been reading up about it. It seems like there is quite a lot of side effects and restrictions to using it, so I figured I'd ask some questions here to clarify.

I'm using the MMC5, setup in PRG mode 3 so that I can switch the PRG bank pages $6000, $8000, $A000, $C000, and $E000.

1. The DMC can only read data from address $C000 (and onwards). At first I figured that means I gotta dedicate the $C000 page for DMC samples, as bankswitching it around for game logic and such would cause corruption in the sound. But then I realized that the last page $E000 where I have my vectors, NMI, IRQ, etc, is actually bank-switchable (but usually best left alone to avoid certain death when the vblank occurs).

But, if I just make sure to clone my vectors and their subroutines at the bottom of the $E000 page, then I can create several ROM banks (at base $E000) with different DMC samples and switch between them freely to play the different samples. The advantage being that I've only locked down one PRG bank page instead of two. Am I correct in this?

2. According to the NESTechFAQ, the DMC freezes your CPU whenever it's using the DMA to fetch out more sample data. Normally this isn't an issue, but if you are doing time critical stuff it can cause trouble. So from my understanding it could potentially eat up some of my vblank time, or it could trigger in my IRQ to mess up my funky scrolling effects. I've been thinking about ways to avoid as much of this problem as possible.

A. Is there some way to temporarily stop the DMC from fetching new samples without stopping playback entirely?
B. If the DMC playback is stopped, is it possible to start it again where it left off?
C. Is the DMC's fetching rate's so deterministic so that you can know roughly where it usually occurs?
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Some questions about the APU DMC

Post by rainwarrior »

Drakim wrote:1. The DMC can only read data from address $C000 (and onwards). At first I figured that means I gotta dedicate the $C000 page for DMC samples, as bankswitching it around for game logic and such would cause corruption in the sound. But then I realized that the last page $E000 where I have my vectors, NMI, IRQ, etc, is actually bank-switchable (but usually best left alone to avoid certain death when the vblank occurs).

But, if I just make sure to clone my vectors and their subroutines at the bottom of the $E000 page, then I can create several ROM banks (at base $E000) with different DMC samples and switch between them freely to play the different samples. The advantage being that I've only locked down one PRG bank page instead of two. Am I correct in this?
I don't know what you mean about "instead of two"? Yes you can duplicate your vectors table if you want to bank $E000 but I don't understand why you think this is an advantage over banking $C000. Either way seems okay to me. Games that I've seen with DPCM bankswitching used the $C000 bank (but probably had a fixed $E000 anyway, e.g. MMC3/FME7).
Drakim wrote:2. According to the NESTechFAQ, the DMC freezes your CPU whenever it's using the DMA to fetch out more sample data. Normally this isn't an issue, but if you are doing time critical stuff it can cause trouble. So from my understanding it could potentially eat up some of my vblank time, or it could trigger in my IRQ to mess up my funky scrolling effects. I've been thinking about ways to avoid as much of this problem as possible.
It will happen during vblank, yes, but at worst this is only like 10 cycles. (At the fastest rate it's 432 cycles between fetches.)

I don't think it can trigger an IRQ on you. Like you can use the DMC to trigger its own IRQ at the end of a sample, but it shouldn't otherwise interfere with an MMC5 IRQ except by delaying it (or something in the IRQ handler) by 2 cycles randomly.
Drakim wrote:A. Is there some way to temporarily stop the DMC from fetching new samples without stopping playback entirely?
No.
Drakim wrote:B. If the DMC playback is stopped, is it possible to start it again where it left off?
It's not possible to read back the play position. You could estimate it, if you knew how long it had been since the sample started.
Drakim wrote:C. Is the DMC's fetching rate's so deterministic so that you can know roughly where it usually occurs?
I forget if there's some trickiness to setting it up to start on a specific cycle, but the spacing between reads is entirely regular and predictable. It's also also always on even cycles only, and you can use the OAM DMA to get aligned to that (helpful in making DPCM safe controller reading routine).
Drakim
Posts: 97
Joined: Mon Apr 04, 2016 3:19 am

Re: Some questions about the APU DMC

Post by Drakim »

Thanks for answering!
rainwarrior wrote:I don't know what you mean about "instead of two"? Yes you can duplicate your vectors table if you want to bank $E000 but I don't understand why you think this is an advantage over banking $C000. Either way seems okay to me. Games that I've seen with DPCM bankswitching used the $C000 bank (but probably had a fixed $E000 anyway, e.g. MMC3/FME7).
I guess somewhat a matter of preference, but I'll explain.

What I mean is, we got two things that always needs to be accessible, the vectors table and the DCM samples. The most straightforward approach would be to always have $E000 on one bank with the vectors table, and always have $C000 on one bank with the DCM samples (or if they all can't fit, switch between different banks of DCM samples).

This means we have $8000 and $A000 left for PRG/RAM, and $6000 for RAM.

But if we combine the two, by jamming both the vector tables and the DCM samples on $E000, then we have freed up $C000 to do other things, as it can now be switched around without messing up the vector table or DCM samples.

Sometimes I find myself in a situation where having more separate PRG banks to use helps simplify things and minimize how often I have to switch banks back and forth. So having another one is great.
rainwarrior wrote:It will happen during vblank, yes, but at worst this is only like 10 cycles. (At the fastest rate it's 432 cycles between fetches.)
Oh, that's not so bad after all. The OAM DMA takes 513 cycles so I thought I was dealing with something similar for this. Great to hear :beer:
rainwarrior wrote:I don't think it can trigger an IRQ on you. Like you can use the DMC to trigger its own IRQ at the end of a sample, but it shouldn't otherwise interfere with an MMC5 IRQ except by delaying it (or something in the IRQ handler) by 2 cycles randomly.
So it can't trigger in the middle of my MMC5 IRQ, it either takes place before or after? I was thinking about all the havok it might cause if it triggered JUST as I was switching the nametables around and such in the middle of my IRQ routine.
User avatar
Bregalad
Posts: 8055
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Re: Some questions about the APU DMC

Post by Bregalad »

Drakim wrote:But then I realized that the last page $E000 where I have my vectors, NMI, IRQ, etc, is actually bank-switchable (but usually best left alone to avoid certain death when the vblank occurs).
You can absolutely bankswitch ROM at $E000 and there won't be "certain death" if you handle things properly. Typically you'd want the NMI, IRQ and Reset routines to be present in all banks which are bankswitchable here, typically by having a pseudo-fixed region somewhere, for example let's say $f800-$ffff. Another approach would be to have trampoline code switching back the bank where the handler you want to use is located, at the cost of some delay. (For NMI this might be problematic, but for Reset it'll be typical.)
Drakim
Posts: 97
Joined: Mon Apr 04, 2016 3:19 am

Re: Some questions about the APU DMC

Post by Drakim »

Bregalad wrote:You can absolutely bankswitch ROM at $E000 and there won't be "certain death" if you handle things properly. Typically you'd want the NMI, IRQ and Reset routines to be present in all banks which are bankswitchable here, typically by having a pseudo-fixed region somewhere, for example let's say $f800-$ffff. Another approach would be to have trampoline code switching back the bank where the handler you want to use is located, at the cost of some delay. (For NMI this might be problematic, but for Reset it'll be typical.)
Thanks. I kinda knew it was possible, but it's still something that's nice to have confirmed. The trampoline trick is neat, but I'd want to avoid that for the scanline IRQ when doing raster effects as it would add an overhead each time it triggers. Sometimes you might want to trigger it often (wavey water effect?) so eliminating overhead is essential.

So I've considered this from some angles now, and as I see it, I have two alternatives for organizing my banks:

ALTERNATIVE 1
$8000 = Free for game code/ram
$A000 = Free for game code/ram
$C000 = Stuck on currently playing DMC sample
$E000 = Partially for game code, bottom always has a clone of the NMI, IRQ, and Reset routines.

ALTERNATIVE 2
$8000 = Free for game code/ram
$A000 = Free for game code/ram
$C000 = Free for game code/ram
$E000 = Stuck on currently playing DMC sample, bottom always has a clone of the NMI, IRQ, and Reset routines.

And frankly alternative 2 simply seems superior. Not only do I as a programmer get 3 full banks to utilize for my entire game without having to compromise, but it just feels right to get all of the non-gameplay engine stuff all bunched together in it's own bank. While not being able to fit some extra subroutines into a bank can complicate your design as you have to split it across two banks and makes sure it all works together, the DMC samples don't have the same issue. The only restriction I can see is that you don't have a full bank free for a really really really long DMC sample, but I seriously doubt that will be an issue, lol.

Time to play around with GGSound :D
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Some questions about the APU DMC

Post by tepples »

Drakim wrote: ALTERNATIVE 1
$8000 = Free for game code/ram
$A000 = Free for game code/ram
$C000 = Stuck on currently playing DMC sample
$E000 = Partially for game code, bottom always has a clone of the NMI, IRQ, and Reset routines.
The pattern with MMC3 games is to decide whether each piece of code needs to access more than about 7K of data in ROM. Code that does can go in a fixed bank; code that doesn't can go in $A000-$BFFF along with its own data. This gives you three full 8K banks to use, two fixed and one switchable.
  • $8000-$9FFF: Code that needs to access large data, such as background map decoding
  • $A000-$BFFF: Data used by $8000 and $E000 code; code that accesses 7K of data or less along with its data
  • $C000-$DFFF: Currently playing sample
  • $E000-$FFFF: Vectors and handlers for reset, NMI, and IRQ; other game code that didn't fit in $8000-$9FFF
Besides, the longest sample is 4K. If you want longer, you'll have to break it up into multiple samples, which you can place two to a bank.
Bananmos
Posts: 552
Joined: Wed Mar 09, 2005 9:08 am
Contact:

Re: Some questions about the APU DMC

Post by Bananmos »

Nice to know someone else is thinking about the DPCM + scanline effects implication - most people just ignore this caveat.

For doing the usual screen-split, you needn't worry too much if you are using MMC5 IRQs. Only 4 CPU cycles will be "stolen" and it happens on a granularity of appr. 4 scanlines or less. So just make sure your window for register writes in hblank is as tight as possible (pre-write $2006, load values into all registers etc and write them in one go... the usual stuff)

For doing wavy effects you might find the IRQ entrance/exit overhead to be a bit of a problem though, and might need a raster loop which keeps writing every hblank, and in this case the skew introduced by DPCM DMA could be an issue. In this case you might want to try the technique I used a long time ago in our "Years Behind" music demo.

Basically:
1) Make sure your samples are never stopped by the DPCM hardware - instead pad them with silence and make your NMI music routine stop them manually
2) In your IRQ raster loop, make sure you have a cycle counter that makes your loop take an extra cycle (for NTSC you'd add #85 and do a BCC)
3) Have a 16-entry table that, for each possible DPCM sample rate, stores how much to adjust the cycle counter addition with (#85 for no playback)

I'd show you the code itself, but I can't seem to find my old source code on this computer. I did post the source on the forum years ago:
viewtopic.php?f=5&t=5526&start=30

...but the link is now defunct. But looks like Bregalad downloaded it and might possibly have a second copy lying around somewhere? :)

But if not, it's not too tricky conceptually and you'd need to change the exact values to NTSC anyway. I created my DPCM-adjustment table by watching timed writes on a real NES (as it was kind of exciting to watch it get more stable in-action), but creating it from the awesome nesdev wiki we have in these days is probably easier and less error-prone...
User avatar
Bregalad
Posts: 8055
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Re: Some questions about the APU DMC

Post by Bregalad »

Indeed I still have the folder.
Attachments
YearsBehind.7z
(254.87 KiB) Downloaded 130 times
Bananmos
Posts: 552
Joined: Wed Mar 09, 2005 9:08 am
Contact:

Re: Some questions about the APU DMC

Post by Bananmos »

Thanks Bregalad! Hope to see some more people doing dpcm+scrolling tricks :)
Post Reply