This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Use APU Frame IRQ to run sound engine twice as fast ?

Use APU Frame IRQ to run sound engine twice as fast ?
by on (#94086)
Everything is in the title.

My idea is simple, since updating sound engine at only 60Hz or 50Hz is not really a nice thing if you want fast effects. Update at 120Hz or 100Hz however sounds quite a lot better. The first idea that comes to mind is to use the NMI for every other update, as we're used to, but use another IRQ that would come aprox. in the middle of a frame for an additional update.

It's of course possible to use IRQ from a mapper, but this requires a mapper with IRQ, and it can be a problem.
Mappers with a CPU cycle IRQ (like the FME-7 and the FDS) could even get rid of the update during NMI and update the sound engine at any rate you'd like.

Since the APU Frame IRQ runs at a frequency close to the VBlank it would make sense to find a way to have both of them synced so that the frame IRQ happens almost at the middle between two VBlanks.

Of course the thing will eventually get out of sync, so "re-syncing" will be needed once in a while. But I have no idea how this "re-syncing" should be done, which is why I'm discussing it in this thread.

If such a thing is not possible then I guess the only option for updating music more often without using a mapper is to abuse DMC IRQ, which of course will only work for music that don't use DMC.

by on (#94087)
The problem with this is that (at least on the PAL console I tried it on many years back), the time length between frame IRQs is slightly longer than the time lengths between NMIs.

That means that everytime you try to re-sync it in your IRQ handler, it is already too late since the next IRQ will occur slightly later than the previous one, causing the time when frame IRQ occurs to move closer and closer to the NMI point after each time.

To get 120Hz/100Hz, you're probably better off using a sprite #0 hit as the second update point.

by on (#94088)
The problem with sprite #0 hits is that the CPU must poll the $2002 register at the right time, and this can be a huge problem if you want most of your CPU used for gameplay. This might be ok for some sort of demo or for simple games where you can get away with only half of the CPU time used...

I think if you use frame IRQ and waste half of the frame for re-syncing only once in a while (for example every 10 frames) it might be an acceptable lag during gameplay.

by on (#94089)
I wonder if it is possible to resync the IRQ every NMI. This way it would have something like fixed 0.6:0.4 ratio, which is acceptable.

by on (#94111)
Why not just use the DMC IRQ, by itself, to call your music engine? Does it really need to run in the NMI? Plus, you'd have instant compatibility across NTSC and PAL. (Well, tempo-wise, anyway)

by on (#94125)
It needs to not be called right in the middle of vblank. I guess it is quite possible that DMC IRQ could be triggered right before the vblank, and music players usually take time comparable with the vblank itself - so subsequent NMI will simply have no time to work with VRAM.

by on (#94126)
No, no problem would arise, as NMI are allowed to happen during an IRQ routine (so the music engine that was triggered just before the VBlank would get interrupted), and IRQs are not allowed to happen during NMIs by default, as the I flag is set when an NMI happens, therfore, the VRAM updates or whathever there is in the NMI won't get interrupted, unless you place a "cli" instruction which of course you won't.

The only problem with DMC IRQ, is that it'd be hard to predict at which rate exactly the music engine would run, but basically I agree it's fundamentally a great idea to run the engine faster (no matter how much faster) without a mapper.
Also it will only work for music that doesn't use DMC obviously.

by on (#94134)
Bregalad wrote:
The problem with sprite #0 hits is that the CPU must poll the $2002 register at the right time, and this can be a huge problem if you want most of your CPU used for gameplay. This might be ok for some sort of demo or for simple games where you can get away with only half of the CPU time used...

I think if you use frame IRQ and waste half of the frame for re-syncing only once in a while (for example every 10 frames) it might be an acceptable lag during gameplay.

Depending on how your game logic is structured, you could use NMI to handle the normal vblank processing and call the music player once, run part of your game logic (roughly a bit less than half the frame, if it takes that long), use sprite 0 hit and burn a few lines polling $2002, call the music player a second time, then finish your game logic. This assumes, of course, that the first half of the game logic will never overrun the time you have allotted it before the sprite 0 hit. Luckily, the exact point of the second player call is not set in stone, as long as it's somewhat close to halfway between vblanks then it will sound ok.

by on (#94139)
LocalH wrote:
use sprite 0 hit and burn a few lines polling $2002

Sprite 0 hits are terrible for this, not only because breaking your game logic in two requires wasting lots of cycles in idle loops, but also because it severely restricts the kind of scrolling you can use.

Vertical scrolling and sprite 0 hits really don't go well with each other, because the solid line/tile/whatever responsible for causing the hit will keep moving up and down... also, these solid background pixels aren't in a status bar, they'll also move left and right.

So, in order to effectively cause a sprite hit in the middle of the screen you have to manually move a stray solid tile in the background as the screen scrolls. Not very clean and might even cause visual glitches.

Seriously, sprite 0 hits sucks for anything other than status bars at the top of the screen.

by on (#94227)
Shiru wrote:
It needs to not be called right in the middle of vblank. I guess it is quite possible that DMC IRQ could be triggered right before the vblank, and music players usually take time comparable with the vblank itself - so subsequent NMI will simply have no time to work with VRAM.


NMIs take priority over IRQs. Any kind of IRQ will set the I flag, but only regular IRQs are inhibited by the I flag; NMIs will still interrupt the CPU regardless of the I flag (hence the name "non-maskable" interrupt). So, the vblank NMI will interrupt your IRQ routine, but the DMC IRQ will never interrupt your NMI routine.

When the DMC generates an IRQ, it will continually assert the IRQ line of the CPU until you acknowledge it by writing to $4015. So, if a DMC IRQ happens in the middle of your NMI routine, the CPU doesn't actually interrupt until you RTI out of your NMI routine, because only then is the I flag cleared, and only then will the CPU see that the IRQ line is being asserted.

by on (#94229)
Sounds like it's more hassle than it's worth to not make use of a mapper with IRQ capability IMO.

It's cheaper than I'm guessing you think. A (EDIT $2.25) CPLD has enough logic for a MMC3 style IRQ and should have room to spare for simple discrete style mapper. If you want me to test a specific setup let me know.

I know it would be a new mapper and all, and requires skills to make a prototype and such. But knowing how cheap it is to implement I thought it would be worth sharing.

by on (#94260)
infiniteneslives wrote:
Sounds like it's more hassle than it's worth to not make use of a mapper with IRQ capability IMO.

There's no hassle, you literally put your JSR PlayMusic inside your IRQ routine and turn DMC interrupts on. That's all you need to do.

by on (#94284)
Yes, along with some teaking with DMC and $4015 read/writes to keep it working.

The only problem I see again is to know how exacly often your music engine will update, the tempo might be a little random but that's a small price to pay to be able to run it as fast as you want without any mappers.

by on (#94286)
With NMI being able to interrupt IRQ I would worry about NMI that happens right in the middle of registers write, when the APU stays in an undefined state (like, channel frequency is changed but volume is not changed yet) during NMI time. Should be not too bad, but I guess it could be more noticeable than the tempo flow.

by on (#94306)
Well, if the NMI suspends the IRQ until it's over, if your NMI routine is a consistent length, and if you can get the DPCM to trigger its IRQ anywhere in the middle of it (seems like a nice wide target), it'll automatically get synched with the end of the NMI every frame. You should be able to get two updates per frame in very reliably (though one of the updates might be slightly longer; probably this is fine).

Might take a little care to get the first frame's IRQ aligned properly, but once you catch the first DPCM IRQ in your NMI it should be stable afterward.