Akumajou Densetsu's weird sound initialization?

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Akumajou Densetsu's weird sound initialization?
by on (#229865)
I was looking through Akumajou Densetsu's code and the reset code has a subroutine with sound initialization (most likely) at the end of it. What is weird about it is that after each register write it jumps to a subroutine that consists of nothing but NOP. Does anyone have some sort of explanation for this and does it have any functional purpose?

Code:
...
 LDA #$30
 STA P1_VOLUME
 JSR NoOperation

 STA P2_VOLUME
 JSR NoOperation

 STA NOI_VOLUME
 JSR NoOperation

 LDA #$7F
 STA P1_SWEEP
 JSR NoOperation

 STA P2_SWEEP
 JSR NoOperation

 LDA #$00
 STA VRC6_P1_VOLUME
 JSR NoOperation

 STA VRC6_P2_VOLUME
 JSR NoOperation

 STA VRC6_SAW_VOLUME
 JSR NoOperation

 LDA #$0F
 STA APU_STATUS
 JSR NoOperation
 RTS

NoOperation:
 NOP
 NOP
 NOP
 NOP
 NOP
 NOP
 NOP
 NOP
 NOP
 NOP
 RTS


EDIT:
This is somewhat off topic from the original question, but I'm going to add this here anyway.

After the reset routine the game seems to go to the following loop:

Code:
Loop:
 INC $0020
 CLC
 LDA $0020
 ADC $001B
 STA $0020
 JMP Loop


As far as I can tell, there is no "break condition" meaning it'll be stuck doing that. NMI of course is a temporary break, but it'll return there after NMI is done. Is this game doing all game logic in NMI and using the non NMI time to wait for new NMI to happen? Or is there some other purpose to this? I haven't seen the game do anything else outside NMI so far.
Re: Akumajou Densetsu's weird sound initialization?
by on (#229868)
I can't answer the first part of your question, except pointing out that it's weird they use so many NOPs when they could have instead used a DEX/BNE loop or something like that and waste less ROM. Anyway, it's very clear that for some reason they thought they should waste time between various writes to memory-mapper-registers.

For the second part of your question, pretty much all Konami games does that. They run the entirety of game logic within NMI, espousing an "all-in-NMI" strategy. The loop you're seeing just generate pseudo-random numbers. You can replace it with a simple "here: JMP here" kind of loop and the game will work the same, except luck-based elements which will be altered.
Re: Akumajou Densetsu's weird sound initialization?
by on (#229869)
Bregalad wrote:
For the second part of your question, pretty much all Konami games does that. They run the entirety of game logic within NMI, espousing an "all-in-NMI" strategy. The loop you're seeing just generate pseudo-random numbers. You can replace it with a simple "here: JMP here" kind of loop and the game will work the same, except luck-based elements which will be altered.


Does this strathegy have adventages or disadventages over handling game logic outside NMI?
Re: Akumajou Densetsu's weird sound initialization?
by on (#229874)
Previous thread where I asked a very similar question:
https://forums.nesdev.com/viewtopic.php?f=9&t=15989

The VRC7 does need a delay between writes to its audio registers, which is normal for the Yamaha FM chips. My theory is that they initially thought VRC6 was going to require a similar delay, but by the time it was finished it didn't need it, and this delay code is vestigial. (The later two VRC6 games don't delay between writes like this.)
Re: Akumajou Densetsu's weird sound initialization?
by on (#229875)
SusiKette wrote:
Does this strathegy have adventages or disadventages over handling game logic outside NMI?

If the "all in NMI" implementation in question doesn't allow reentrant NMIs (i.e. handling an NMI even if the previous handler didn't finish yet), the game will miss vblanks when lag frames occur, causing the music to slow down and raster effects (e.g. status bars) to glitch. If it does allow reentrant NMIs, it can detect lag frames and still perform crucial tasks like audio playback and raster effects.

So yeah, depending on how you implement it, "all in NMI" can be as versatile as "main + NMI". I personally prefer to do "main + NMI", keeping the two threads clearly separated.
Re: Akumajou Densetsu's weird sound initialization?
by on (#229892)
SusiKette wrote:
As far as I can tell, there is no "break condition" meaning it'll be stuck doing that. NMI of course is a temporary break, but it'll return there after NMI is done. Is this game doing all game logic in NMI and using the non NMI time to wait for new NMI to happen? Or is there some other purpose to this? I haven't seen the game do anything else outside NMI so far.


That is odd, I have no idea why they would do that. NMI or IRQ has to be the escape from the loop, the return address is stored on the stack, and you can use the TSX instruction to index it. It's also OK (but.. weird) if you just never return from the interrupt, the interrupt source will never notice.
Re: Akumajou Densetsu's weird sound initialization?
by on (#229912)
tokumaru wrote:
If the "all in NMI" implementation in question doesn't allow reentrant NMIs (i.e. handling an NMI even if the previous handler didn't finish yet), the game will miss vblanks when lag frames occur, causing the music to slow down and raster effects (e.g. status bars) to glitch. If it does allow reentrant NMIs, it can detect lag frames and still perform crucial tasks like audio playback and raster effects.

So yeah, depending on how you implement it, "all in NMI" can be as versatile as "main + NMI". I personally prefer to do "main + NMI", keeping the two threads clearly separated.

Konami games does "all in NMI" and handle lagging and re-entrant NMIs properly - by software (rather than disable further NMIs by $2000.7, it probably sets some flag or something so that further NMIs only do music and minimal work before returning to unfinished game logic buisness).
Re: Akumajou Densetsu's weird sound initialization?
by on (#229933)
That's definitely the good way to do it. I believe that they're are games that disable NMIs though, and those have poor handling of lag frames.
Re: Akumajou Densetsu's weird sound initialization?
by on (#230221)
Bregalad wrote:
Konami games does "all in NMI" and handle lagging and re-entrant NMIs properly - by software (rather than disable further NMIs by $2000.7, it probably sets some flag or something so that further NMIs only do music and minimal work before returning to unfinished game logic buisness).


tokumaru wrote:
That's definitely the good way to do it. I believe that they're are games that disable NMIs though, and those have poor handling of lag frames.


What would one typically do to handle lag frames? Detect lag, flag it and avoid doing non critical stuff?
Re: Akumajou Densetsu's weird sound initialization?
by on (#230222)
pwnskar wrote:
What would one typically do to handle lag frames? Detect lag, flag it and avoid doing non critical stuff?


I would simply have a variable that tells that the last frame was completed. It's set at the end of NMI and tested at the beginning of NMI. After the test it's cleared and things go on normally. If a new NMI starts before the flag gets set again the code should return to where it left off (maybe with RTI). You can of course update some stuff before it if you want. VRAM updates are something that has to be skipped, but not sure about the rest.
I have never used the all in NMI style so someone has to confirm if this is correct or not.

(Since we are talking about the all in NMI style, when I say NMI it also includes the main loop of the game)
Re: Akumajou Densetsu's weird sound initialization?
by on (#230228)
SusiKette wrote:
pwnskar wrote:
What would one typically do to handle lag frames? Detect lag, flag it and avoid doing non critical stuff?


I would simply have a variable that tells that the last frame was completed. It's set at the end of NMI and tested at the beginning of NMI. After the test it's cleared and things go on normally. If a new NMI starts before the flag gets set again the code should return to where it left off (maybe with RTI). You can of course update some stuff before it if you want. VRAM updates are something that has to be skipped, but not sure about the rest.
I have never used the all in NMI style so someone has to confirm if this is correct or not.

(Since we are talking about the all in NMI style, when I say NMI it also includes the main loop of the game)


So it's basically just avoid doing your NMI stuff before all of your last NMI stuff has been finished?

About the all-in-NMI stuff though. Having your game logic at the end of your NMI, how is that different from having a regular main loop outside of it? Most of the time you only let your game logic run once after each NMI anyway so you would end up with the same behavior as an all-in-NMI approach either way?
Re: Akumajou Densetsu's weird sound initialization?
by on (#230229)
Well, by keeping your main logic outside of NMI you at least don't have to worry too much about lag frames.

Personally, I think it also makes my code a lot more manageable and modular (for example I can have multiple different main loops that all rely on the same the same NMI for timing and PPU updates)
Re: Akumajou Densetsu's weird sound initialization?
by on (#230230)
pwnskar wrote:
About the all-in-NMI stuff though. Having your game logic at the end of your NMI, how is that different from having a regular main loop outside of it? Most of the time you only let your game logic run once after each NMI anyway so you would end up with the same behavior as an all-in-NMI approach either way?


It's simply preference. I'm pretty sure most people here keep NMI and main loop separate just for clarity. I think lag frames are easier to manage if they are separate anyway. You can just jump at the end of your NMI if lag frame occurs and maybe run only sound engine or something and then return with RTI. You could do return immediately though if you don't mind music slowing down during lag.
Re: Akumajou Densetsu's weird sound initialization?
by on (#230251)
pwnskar wrote:
Having your game logic at the end of your NMI, how is that different from having a regular main loop outside of it? Most of the time you only let your game logic run once after each NMI anyway so you would end up with the same behavior as an all-in-NMI approach either way?

It's just a different style, but in the end it doesn't change things much. Doing it all-in-NMI, the start of the NMI is as usual, but then at some points it tests if the previous frame is finished, and if it's not it exits. In normal cases, it probably restores the state of the last frame by means of complex state machines and possibly pointers. This is a bit harder to manage than simply calling routines in a given order within code, but in the end it's exact same thing.
Re: Akumajou Densetsu's weird sound initialization?
by on (#230455)
Bregalad wrote:
In normal cases, it probably restores the state of the last frame by means of complex state machines and possibly pointers. This is a bit harder to manage than simply calling routines in a given order within code, but in the end it's exact same thing.

You don't really have to do all that, do you? As far as I know, the difference between the two is like this (in pseudo-code):

Separate main loop and NMI:
Code:
reset:
    initialize everything...
main:
    do game logic...
    ready = 1
    wait until ready == 0
    jmp main
   
nmi:
    save registers...
    do essentials...
    update music...
    if ready == 1 {
        ready = 0
        do sprite DMA...
        update nametables...
        read controllers...
    }
    restore registers...
    rti


All in NMI:
Code:
reset:
    initialize everything...
    ready = 1
main:
    jmp main

nmi:
    save registers...
    do essentials...
    update music...
    if ready == 1 {
        ready = 0
        do sprite DMA...
        update nametables...
        read controllers...
        do game logic...
        ready = 1
    }
    restore registers...
    rti
Re: Akumajou Densetsu's weird sound initialization?
by on (#230456)
It's best practice to do the sprite DMA and nametable updates before the music updates because unlike the music updates, the sprite DMA and nametable updates need to finish before the PPU starts on the next frame.
Re: Akumajou Densetsu's weird sound initialization?
by on (#230458)
Yes, but it'd also be better to do it before the game logic, and I didn't want to complicate things by breaking up the if ready == 1 block into two pieces while still keeping the NMI routine reentrant, or working around that by adding an else clause with update music... in both paths, etc. It's just a simple example.