43110 wrote:
Overnight I considered adding cli to NMI, but that has a worst case of executing the IRQ twice (not good at all)!
I believe that the NMI fires no matter the state of the I flag, since the NMI is non-maskable by definition. If your NMI code takes longer than a frame to execute another NMI will interrupt it unless you disable NMIs through PPU register $2000. That shouldn't be a problem in your case though, because you're not running the game logic inside the NMI handler, so it will never take that long.
A CLI will allow you to respond to the IRQ even if you're in the middle of you PPU updates, and you could set up the next IRQ immediately. You could set a flag indicating that an IRQ fired in the middle of VBlank, so that after the updates you could call the game logic if this flag was set. This would result in a small delay in handling the current IRQ, but the next one will stil fire on time. The only problem is that this will steal you some VBlank time (as will the samples being fetched for playback), but if you take that lost time into account when designing your PPU update code
Quote:
I'm going to try to see if having NMI simply increment a counter will work, and do vblank updates in a main loop
Do you even have a main loop? If I understand correctly, your game logic runs in the IRQ handler, which is not a loop. If you really do have a main loop (that probably doesn't have anything to do), polling a VBlank flag won't work, because the IRQ still has precedence over the main loop. Say that you're waiting for the flag to change, and then the NMI fires and changes the flag, but then the IRQ also fires, meaning that the IRQ handler will run before the CPU resumes your main loop, so the entire game logic will run during VBlank,
stealing all the time you'd have for PPU updates. Then, when control is finally returned to the main loop, VBlank will probably have already ended and you'll not be able to update the graphics (if you try you'll get a corrupted screen). Something just as bad would happen if the loop successfully detected the start of VBlank but the PPU updates were interrupted by the IRQ that does the game logic.
Quote:
or after IRQ or but not both.
Now it seems like you're just guessing! This is not something you guess, it's something you plan carefully so that you don't have to redesign the whole thing later on. The NES is very strict about PPU updates: they can only happen when the PPU is not rendering. This means you can't risk losing a lot of VBlank time, or you'll end up with glitched screens more often than not.
The only way I see your idea working is this:
1- The game logic runs entirely in the IRQ handler. If the logic takes too long and another IRQ fires, set up the next IRQ and set a flag indicating that the logic has to catch up. At the end of the IRQ handler, if this flag is set, run the IRQ handler again and try to catch up (this has to stop at some point, if you're not able to catch up eventually you'll have to stop trying and accept the slowdown).
2- The NMI handler always updates the PPU using the set of buffers that's marked as consistent (you'll definitely need 2 sets of buffers). If the NMI is interrupted by an IRQ, set up the next IRQ and set a flag indicating that the logic has to catch up. At the end of the NMI handler, if this flag is set, run the IRQ handler.
3- The main loop would do nothing more than decide whether to stay in the current game mode or switch to another one. This will be indicated by the game logic through specific variables that will be constantly checked in the main loop.