Pausing during NMI execution.

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
ludoVIC
Posts: 31
Joined: Fri Jan 08, 2021 8:36 am

Pausing during NMI execution.

Post by ludoVIC » Tue Jan 26, 2021 9:05 am

If my understanding is correct, the code contained in the NMI section is executed 60 times per second.

Does it imply that it must be enough concise to require less than 1/60s to run?
For example, what does it happen if pause inside the NMI, say, for 3 seconds?

I am not having any issue in particular, just asking to improve my theoretical overview.
As always, thanks in advance

EDIT: for example, by "pausing", I mean something like "let's run an empty loop thousands of times".
Last edited by ludoVIC on Tue Jan 26, 2021 10:05 am, edited 1 time in total.

User avatar
nesrocks
Posts: 481
Joined: Thu Aug 13, 2015 4:40 pm
Location: Rio de Janeiro - Brazil
Contact:

Re: Pausing during NMI execution.

Post by nesrocks » Tue Jan 26, 2021 9:47 am

Let me answer before someone who really knows what they are talking about does. Maybe I'll get something right, but I want to try and see! :)

The ppu does not stop for you, in fact, it is always doing its thing in a loop, about 60 times per second, as you mentioned. By "pause" I imagine you mean lock the cpu inside your nmi code for too long. The thing is, "nmi code" and "game code" are just ways to organize the code. The cpu has no idea which code inside your organization it is executing. You'll have to be more specific than that.
Usually in the nmi code we do things that are ppu sensitive. Things that "require" the ppu to be in the v-blank phase. Otherwise visual glitches can happen. So, you have a set amount of cycles to do those things. Just do them first, and then you can "pause" your code for as long as you want. It will be perceived as "lag" by the player. As in, the actual frames that are output to the screen will be repeated, considering your nmi code is in fact the only code that affects the ppu and that you're using buffers and these haven't been updated since the last NMI.

As always, this is a great read: http://wiki.nesdev.com/w/index.php/The_frame_and_NMIs
https://twitter.com/bitinkstudios <- Follow me on twitter! Thanks!

User avatar
za909
Posts: 226
Joined: Fri Jan 24, 2014 9:05 am
Location: Hungary

Re: Pausing during NMI execution.

Post by za909 » Tue Jan 26, 2021 9:54 am

You are on the right track overall in thinking that it is a time-critical part of the program. During the Vertical Blanking period, the PPU is not accessing any of its memory, so this is the time when the CPU is free to change the nametables, palettes, refresh the sprite data by using the $4014 register, etc. and to help with this the PPU generates the NMI interrupt so that the CPU can instantly devote itself to these operations. Usually it's useful to break them down like I just listed, and have a flag and a conditioned jump over all of the operation types so that the rest of the program can request certain updates from the NMI code this way, while others may not be needed all the time.

When the game is paused a "pause handler" will most likely be running, which will be waiting for the player to unpause, and telling the NMI code to do nothing (except refresh the sprites because this is a must every frame, otherwise the sprites will get corrupted).

It is also correct that there is a limited time budget, but it is not 1/60s, as that would be one complete frame. The Vertical Blank is around 7-8% of every frame, just a bit above 2200 CPU cycles (≈ 2273 CPU cycles according to the wiki) . Every instruction takes a certain number of cycles to complete, this is listed in pretty much every 6502 instruction reference that's worth using. So to squeeze the most out of this time, it's common to not use loops but "unroll" them and write them out as many times as needed at the expense of ROM space, as this improves performance slightly (or majorly, depending on the loop and the number of iterations).

User avatar
Quietust
Posts: 1684
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Pausing during NMI execution.

Post by Quietust » Tue Jan 26, 2021 10:37 am

A lot of people seem to confuse NMI and VBLANK, believing them to be the same thing.
While they are related, they are two very different things:

1. VBLANK is a time interval during which the PPU does not attempt to access VRAM or sprites on its own (so it's safe for your program to update them). On NTSC it's exactly 20 scanlines (2273+1/3 CPU cycles) in length, and on PAL it's 70 scanlines (7459+3/8 CPU cycles).
2. In the NES, NMI is a signal which is generated at the very beginning of VBLANK. Once your program receives that signal, it needs to perform all of its PPU updates before VBLANK ends, otherwise bad things (i.e. graphical corruption) will happen.

Furthermore, it is not possible to ignore an NMI (it stands for "Non-Maskable Interrupt"), so if your NMI handler runs for more than 1 frame (29780.5 cycles on NTSC, 33247.5 cycles on PAL) without returning, it'll jump right back to the start for the next one, and if that one returns in time, then the previous one will continue where it left off; if it doesn't return, then it'll just get nested deeper and deeper until the stack overflows (and likely causes the overall program to crash).
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

User avatar
tokumaru
Posts: 11991
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Pausing during NMI execution.

Post by tokumaru » Tue Jan 26, 2021 11:17 am

If you don't return from the NMI handler in time (1/60 of a second), another one will fire and interrupt that one, and if the second one too doesn't return in time, another one will fire and they'll keep piling up until the stack overflows. Actual games will not pause by stalling the CPU, they'll still run one iteration of the game loop (checking input, and doing things that must be done even when the game is paused), but will skip specific gameplay tasks (e.g. game entity updates) to prevent the game from advancing.

Even though the CPU can't ignore NMIs, the PPU can be configured to not to fire them, but turning NMIs on and off all the time has a few drawbacks (unwanted NMIs, scroll glitches, lack of music), so it's generally better to keep them always on and selectively execute NMI tasks based on flags.

unregistered
Posts: 1126
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: Pausing during NMI execution.

Post by unregistered » Tue Jan 26, 2021 11:21 am

tokumaru ninjaed me.


QuietTust is obviously being truthful. :)

But, just want to note that it IS possible to disable NMIs by writing a positive value, bit7 being clear, to PPU register $2000. So if you need to eliminate vblank code from repeating every frame for a while, that’s NOT impossible. :)


Since bit7 of $2000 enables NMIs only when it’s set; disables NMIs otherwise.

User avatar
tokumaru
Posts: 11991
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Pausing during NMI execution.

Post by tokumaru » Tue Jan 26, 2021 12:28 pm

Just to be clear, the drawbacks of constantly enabling/disabling NMIs I mentioned above are:

1- Unwanted NMIs: if you happen to enable NMIs while the vblank flag is set, an NMI will fire immediately, not aligned to the beginning of vblank, which can throw off the timing of your NMI handler, causing visual glitches. You can get around this one by reading $2002 to clear the vblank flag before enabling NMIs.

2- Scroll glitches: if $2000 is written while the PPU is resetting the horizontal scroll between scanlines, it can cause the wrong name table to be displayed for the entire scanlines (this happens quite often in SMB, and I always wondered what the heck these glitches were when I was a kid). I don't know of any simple strategies to get around this one, besides simply not enabling/disabling NMIs mid-screen.

3- Lack of music: if NMIs are not firing, you can't call the music playback routine at a steady 60Hz, so you'll either get audio hiccups or no sound at all.

For these reasons, I advocate that it's better to keep NMIs always on and use variables/flags to control which specific tasks the NMI handler is supposed to do.

Fiskbit
Posts: 249
Joined: Sat Nov 18, 2017 9:15 pm

Re: Pausing during NMI execution.

Post by Fiskbit » Thu Jan 28, 2021 5:11 am

tokumaru wrote:
Tue Jan 26, 2021 12:28 pm
2- Scroll glitches: if $2000 is written while the PPU is resetting the horizontal scroll between scanlines, it can cause the wrong name table to be displayed for the entire scanlines (this happens quite often in SMB, and I always wondered what the heck these glitches were when I was a kid). I don't know of any simple strategies to get around this one, besides simply not enabling/disabling NMIs mid-screen.
The nametable for the glitched scanline is selected based on the high byte of the PPU register's address. Bit 0 of #$20 is 0, so nametable 0 is selected. To prevent the glitch, you can choose a register mirror (eg $2000 vs $2100) that matches the current nametable.

User avatar
tokumaru
Posts: 11991
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Pausing during NMI execution.

Post by tokumaru » Thu Jan 28, 2021 6:05 am

Fiskbit wrote:
Thu Jan 28, 2021 5:11 am
The nametable for the glitched scanline is selected based on the high byte of the PPU register's address. Bit 0 of #$20 is 0, so nametable 0 is selected. To prevent the glitch, you can choose a register mirror (eg $2000 vs $2100) that matches the current nametable.
Thanks for the clarification, I wasn't aware that there was a workaround for this. Still, I'd much rather not have to do this.

ludoVIC
Posts: 31
Joined: Fri Jan 08, 2021 8:36 am

Re: Pausing during NMI execution.

Post by ludoVIC » Thu Jan 28, 2021 10:01 am

Thanks to everybody for the comments!!!
Being a beginner, I didn't manage to understand all your explanations in full details, but I am sure I'll do as soon I'll improve my knowledge.
That said, I think I might have got some key points:

It's better to have NMI always enabled, and handling "game pause" by just varying the actions performed during it.
Concretely, we can set a variable PAUSE to be read at every NMI, and e.g. if 1 just update the sprites and check if start is pressed,
if 0 accept the general game controls.
Finally, it is always good to keep in mind that NMI operations should be written "efficiently" to avoid potential problems of lag/stack overflow.

May I ask if my "sum up" is correct?

Pokun
Posts: 1746
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Pausing during NMI execution.

Post by Pokun » Sat Jan 30, 2021 10:30 am

It sounds correct to me. Though you can have different NMI handlers selected depending on a state machine, I personally don't do that. I have game logic in the main thread (the "forever" loop) and PPU and APU updates in the NMI handler. I have a state machine (like Nerdy Nights teaches you) and a jump/vector table in main that jumps to the code that matches the current machine state (states might be TITLE, GAME, PAUSE, GAMEOVER, HIGHSCORE etc) every loop. The NMI doesn't care what state it is. It just keeps updating whatever PPU components it's instructed to update (OAM-DMA is done every NMI, but palette and nametable updates are conditional based on a flag). Even the pause screen might have some animations, a blinking cursor or something, and there is basically never a reason to pause the NMI. You can just have it do nothing if nothing is changing (OAM-DMA is the sole exception and should probably be done every NMI due to a DRAM refreshing problem).

Also even the pause state doesn't wait in a loop (that would make the game basically hang). It's just a machine state (PAUSE) that checks if the START button is pressed and unpauses (changes the machine state back to GAME) if it is, and possibly handles some other things if there is a pause menu or something, then it jumps back to the end of the main thread and ends the logic frame like normal and waits for the next NMI.

If your machine states are so different that they actually require different NMI handlers, then of course you can have a jump/vector table in the NMI as well which jumps to the NMI handler that matches the current state.



I'm going to add one important thing to the explanation of NMI and vblank. The NMI is a feature of the 6502 CPU (and many other CPU types). If you buy a 6502 you will see that there is an input pin called NMI, and if you wire up a pushbutton to it, the code will jump to the NMI handler whenever that button is pushed. The concept of vblank doesn't exist for the CPU, the NMI is just an interrupt which can be used for many different things. The PPU has an output pin that it outputs a signal on whenever it's in vblank. Nintendo designed the NES to use the NMI pin for this so they connected the PPU's vblank pin with the CPU's NMI pin so that NMI happens when the PPU tells it, so that there is an easy way for the programmer to know when vblank happens and can update the PPU. It's up to you as the programmer exactly how you want to use this.

The vblank time period is not necessarily as long as your NMI routine takes (which lasts until the PC reaches the RTI instruction), and if you stall the NMI like that you will soon have several nested NMIs (NMI that happens while the PC is still in the NMI handler) which is probably something you don't want. Having too much code in the NMI can also cause nested NMIs. Rainwarrior made a beginner example ROM that you can look some time when you feel comfortable enough. He prevents nested NMIs using a flag (called NMI lock).

The other type of interrupt the 6502 features is the IRQ. Although it means Interrupt ReQuest, you can think of it as "maskable interrupts". The main difference between NMI and IRQ is that the latter can be ignored (AKA masked) by the CPU by setting the I flag (using the SEI instruction or CLI for clearing it). The IRQ pin is connected to various things including the APU and the cartridge port and some cartridges uses it. You can also use it manually with the BRK instruction. This is called a "software interrupt" because you trigger it using code.

Other systems like the Game Boy, several Sega systems and the PC-Engine actually uses the IRQ instead of the NMI to signal a vblank. It's just an engineering design decision.

User avatar
tokumaru
Posts: 11991
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Pausing during NMI execution.

Post by tokumaru » Sat Jan 30, 2021 11:09 am

Pokun wrote:
Sat Jan 30, 2021 10:30 am
(OAM-DMA is the sole exception and should probably be done every NMI due to a DRAM refreshing problem).
Except when your game is susceptible to lag frames... If the NMI hits before the OAM buffer is fully updated, you definitely don't want to DMA a partially modified OAM. Unless you have two OAM buffers (a big waste of RAM if all you have is 2K, but I believe Kirby's Adventure, which has extra RAM, has two OAM buffers), then you can DMA the finished one while you're updating the other one.

AFAIK, the the DRAM is refreshed automatically during rendering, and the short time of vblank is not enough for it to decay. IIRC, even on the PAL NES, where the vblank period is much longer, OAM can only be updated during the first 20 or so scanlines of it, supposedly because the refresh process starts after that.

Fiskbit
Posts: 249
Joined: Sat Nov 18, 2017 9:15 pm

Re: Pausing during NMI execution.

Post by Fiskbit » Sat Jan 30, 2021 8:10 pm

Indeed, OAM DMA should be avoided on lag frames because of visual glitches that will occur from the data being in a partially-updated state, such as flickering or corrupt tiles. This is actually a common mistake even in commercial games, such as SMB2 and the Japanese cartridge version of Zelda 1. OAM won't decay in the short vblank period unless something's wrong with the hardware (eg signal reflection, which affects some retro and modern cartridge hardware such as the FDS and Everdrive N8 original), in which case even doing OAM DMA every vblank won't necessarily save you.

Pokun
Posts: 1746
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Pausing during NMI execution.

Post by Pokun » Sun Jan 31, 2021 9:29 am

I have a flag that skips all PPU updates (but not APU updates) in the NMI if the logic frame isn't done yet, so that should prevent OAM-DMA during lag frames. Oh I see, that means I'm not doing OAM-DMA every NMI after all. My bad.

Post Reply