Bankswitching interrupted by NMI bankswitching

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Bankswitching interrupted by NMI bankswitching
by on (#12856)
Sorry to bug all of you again, but I have alittle problem.

In my project (uses MMC1, last 16k at $C000 is fixed), when I switch banks to obtain data or perform a subroutine, I store the new bank number in RAM, push the old one, and write to MMC 1 reg3, do whats needed, restore old bank number from stack, write old bank number to reg3. I also have to switch banks in my NMI routine to update the sound. I placed the sound update there so I wouldn't have to deal with sound lag.

The problem is, a write to MMC 1 reg3 in the main code can be interrupted by the write during the NMI to update the sound.

I could only see to alternatives; deal with sound lag, or wait for the end of the nmi routine every single time I need to switch banks (which causes alot of unwanted game lag).

Is there a way I can reset the shift register state so the last writes in the main code after interruption can be discarded?

by on (#12860)
You could deal with sound lag by having the NMI increment a sound lag counter and then run the sound code until sound lag becomes 0. I am pretty sure Pokémon Blue for Game Boy does it this way.

by on (#12864)
Ahhh multithreading issues. You could set a flag at the end of NMI which the bankswitch routine checks at the end; if set, it clears it and switches banks again. At the beginning of NMI you would reset the MMC1 shift register by writing $80, but note that this also affects the bank configuration mode. The MMC1 design isn't very friendly to this issue.

by on (#12866)
There is a very simple way to do this. When doing bankswitching, set a flag when writing to the MMC1, and disable it as long as the write ends. Then, in VBlank, if the programm is already writing to the MMC1 (if the flag is set), skip the sound part with the bankswitching. This is very unprobably cause noticeable sound lags, unless you're doing contant bankswitching or something.
I think there is a way to toally avoid lags, but it may be anoying or unreliable.
When doing bankswitching, first save the bank number, and then set the flag, to eventually reset the MMC1 via bit 7 and write normally to it, and when its done clear the flag. In NMI, when dealing with the MMC1, reset it via bit 7 anyways. Then, at the end of the NMI routine, check the flag. If set, you will have to do bankswitching here with the proper bank you already mirrored. If done correctly, the bank should swap normally. Then, when returning, the main code will attempt continue writing to the MMC1. If the NMI happened between the set flag and the first MMC1 write, the bank will be re-switched, and that souldn't cause problems. If the NMI happened between write 1 and write 5, it will partially fill the MMC1 buffer, but not write anything to it since there isn't going to be 5 writes. Since you reset the MMC1 before writing anything to it, that should work. However, that will only work for interupted bank writes, and that won't work for interupted MMC1 status writes, as long as reset it affects it. Well, I'd go with the first "normal" method, personally. The second sounds "risked".

by on (#12876)
Thank you so much everyone.

I decided to skip sound update if in the middle of a mmc reg3 write. If a skip, like tepples suggested, the next frame/nmi call will compensate for it.

I did extensive testing with two different tunes playing (not at the same time) in the background and I could not hear a lag, and of course, did have the original problem.

Thanks again everyone.

by on (#12885)
Blaster Master had an interesting solution. The bankswitch subroutine set a flag, and if an NMI occurred the NMI routine would check that flag first and, if it were set, would turn on another flag and execute RTI immediately. When the bankswitch algorithm finished, it checked to see if the NMI routine set the other flag and, if it were set, would then call the same subroutines the NMI handler would have called had it been allowed to run normally. Essentially, this delays an NMI until the bankswitching is done, still allowing the full NMI process to occur every frame (and only a small fraction of VBlank time is lost due to the delay, so PPU updating can still occur without a problem, at least for that game).

by on (#12935)
Wow, I would never come with that idea. Sunsoft programmers looks quite intelligent.