In the past, I would always run my sound engine after nmi. In my current game I've introduced mmc3 splits. In some parts of the game, there are some splits near the top of the screen. In others, they are near the bottom. Having the sound engine always run after nmi caused some jitters (with the splits) in some cases. So, I moved it to running after the last split irq. However, in this case I ran into a subtle intermittent chr bankswitching bug where the next nmi would interrupt the currently running irq, due to the sound engine running slightly long on occasion.
My solution has been to configure the split system I have to run the sound engine after a specific split where I know there's more than enough time before the next irq.
I was kinda curious if anybody else has done this out there, either for homebrew or if anyone has seen it done in commercial games.
Managing mmc3 splits and sound engine
Moderator: Moderators
- GradualGames
- Posts: 1106
- Joined: Sun Nov 09, 2008 9:18 pm
- Location: Pennsylvania, USA
- Contact:
Re: Managing mmc3 splits and sound engine
Here's what The Curse of Possum Hollow (MMC3) does:
All bank switching outside interrupt handlers is done by writing to two variables in RAM and then calling a subroutine that copies the values in RAM to MMC3 window registers 6 and 7. Though the NMI and IRQ handlers modify CHR-related window registers (0-5), they always finish by restoring these values to registers 6 and 7. So even if the subroutine that restores these values is interrupted, the result will still be correct. I took pains to ensure the correctness of this reentrancy.
Neither the NMI handler nor the IRQ handler interacts with audio at all. Audio runs on the main thread, where the loop for each game state periodically calls a subroutine that does this:
All bank switching outside interrupt handlers is done by writing to two variables in RAM and then calling a subroutine that copies the values in RAM to MMC3 window registers 6 and 7. Though the NMI and IRQ handlers modify CHR-related window registers (0-5), they always finish by restoring these values to registers 6 and 7. So even if the subroutine that restores these values is interrupted, the result will still be correct. I took pains to ensure the correctness of this reentrancy.
Neither the NMI handler nor the IRQ handler interacts with audio at all. Audio runs on the main thread, where the loop for each game state periodically calls a subroutine that does this:
- If the master NMI counter (incremented by NMI) and music NMI counter are equal: Return.
- Switch $8000 to the audio engine's bank and $A000 to the bank containing the current song's music sequence data (of which there are three).
- If a music change was requested, start it.
- If a sound effect was requested, switch in the audio engine and start it. This queue is organized as a bitfield, with one bit per sound effect. For simplicity, only one sound effect can start in each frame, and lower-numbered sound effects get priority.
- Until the master NMI counter and music NMI counter are equal: Call the audio engine's update routine once and increment the music NMI counter.
- GradualGames
- Posts: 1106
- Joined: Sun Nov 09, 2008 9:18 pm
- Location: Pennsylvania, USA
- Contact:
Re: Managing mmc3 splits and sound engine
I do have code in place to ensure that if PRG bankswitching is done during nmi, nmi will return and the bank select register will be what it was supposed to be before nmi. I do all chr bankswitching during nmi, and never in the main thread so I do not protect it there.tepples wrote:So even if the subroutine that restores these values is interrupted, the result will still be correct. I took pains to ensure the correctness of this reentrancy.
I believe I was running into issues where the last mmc3 irq I configured was itself being interrupted by nmi before returning (due to the sound engine).
I am not sure I understand exactly what is happening when this scenario occurs; it seems it is probably better to ensure that it CAN'T occur, would you agree?
...I was considering doing music updates during the main thread, myself, but this approach of choosing a split after which to run the sound engine appears to be working well.
- rainwarrior
- Posts: 8731
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Managing mmc3 splits and sound engine
That seems pretty sensible to me.GradualGames wrote:My solution has been to configure the split system I have to run the sound engine after a specific split where I know there's more than enough time before the next irq.
- TmEE
- Posts: 960
- Joined: Wed Feb 13, 2008 9:10 am
- Location: Norway (50 and 60Hz compatible :P)
- Contact:
Re: Managing mmc3 splits and sound engine
I would make the sound engine just create list of IO writes allowing the engine to be called any time in the frame. Then in NMI you'll send that list to sound chip(s), it will surely take whole lot less cycles to do than running the entire sound engine there and you can even make the list writes take fixed amount of time allowing some additional possibilites.
Re: Managing mmc3 splits and sound engine
Use a sound engine that runs in a fixed number of cycles every frame
- GradualGames
- Posts: 1106
- Joined: Sun Nov 09, 2008 9:18 pm
- Location: Pennsylvania, USA
- Contact:
Re: Managing mmc3 splits and sound engine
I was actually thinking about your engine while solving this problem, but it's well in hand now.pubby wrote:Use a sound engine that runs in a fixed number of cycles every frame
One of my goals is to keep the sound engine running at a consistent fps so even if the game slows down the sound/music does not.TmEE wrote:I would make the sound engine just create list of IO writes allowing the engine to be called any time in the frame. Then in NMI you'll send that list to sound chip(s), it will surely take whole lot less cycles to do than running the entire sound engine there and you can even make the list writes take fixed amount of time allowing some additional possibilites.
- TmEE
- Posts: 960
- Joined: Wed Feb 13, 2008 9:10 am
- Location: Norway (50 and 60Hz compatible :P)
- Contact:
Re: Managing mmc3 splits and sound engine
You can always have one extra MMC3 IRQ for the sound engine at some non critical area of the frame or perhaps call the engine right after the code of last split.