Probably stupid questions but I'm learning

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
DedGzus
Posts: 11
Joined: Sat Jan 07, 2023 10:11 am

Probably stupid questions but I'm learning

Post by DedGzus »

Just a few questions I'm gonna put out there while I'm learning. I'm just learning NES game coding so give me a break if I sound stupid. Also just getting used to 6502asm.

1. Is ANYTHING ever done in the main loop at the end of the Reset vector or is EVERYTHING always done in the NMI.
2. Is the NMI guaranteed to be called 60 times per second? (Assuming it has finished the previous NMI in time)
I'm sure I'll have more questions but I don't want to ask too many in a single post.
Fiskbit
Posts: 891
Joined: Sat Nov 18, 2017 9:15 pm

Re: Probably stupid questions but I'm learning

Post by Fiskbit »

While there are games that do literally everything inside of NMI, that's probably not the norm and not really recommended. Generally you use the NMI for things that must happen during vblank (PPU-related tasks) and things that must happen at regular intervals (the sound engine, because when lag happens, the experience is much worse if the audio also lags). In this post, I include example code showing the NMI tasks and the main loop tasks. I encourage you to use a model similar to that.

As long as NMI is enabled, it occurs every vblank, regardless of whether the previous NMI has completed. There is an edge case where reading the PPU STATUS ($2002) register at just the wrong time (when the vblank flag is being set) can cause the NMI to be lost, because the PPU outputs an NMI when NMI is enabled and the vblank flag is true, and reading the register in this case clears the flag before this can even happen. There is also an edge case where toggling NMI enable during vblank while the vblank flag is true can cause multiple NMIs.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Probably stupid questions but I'm learning

Post by tepples »

Most NES games point their reset vector at hardware initialization ("init") that jumps to the main game loop. The main loop creates buffers of data to copy to VRAM and OAM and sets a variable to signal to the NMI handler that the buffers are ready. The NMI handler checks whether the buffers are ready, copies the buffers to VRAM and OAM, optionally runs sound-related tasks, optionally reads the controller if requested, optionally handles split-screen scrolling for a top status bar, and notifies the main loop to start a new frame by changing the value of a variable that the main loop reads. A minority of games, largely from Nintendo and Konami, run everything after init in the NMI handler, and code at the reset vector ends at a tight infinite loop. See "NMI thread" and "The frame and NMIs" on the wiki.

If NMI is turned on in $2000, the NMI handler is called 60.1 times per second on NTSC or RGB machines, or 50.0 times per second on PAL NES or PAL famiclones. See "Cycle reference chart" on the wiki. (This is subject to the edge cases described by Fiskbit.)
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Probably stupid questions but I'm learning

Post by tokumaru »

Do note that the "everything in NMI" structure used the popular Nerdy Nights tutorials is not suitable for actual games. In that tutorial, they handle all the game logic (reading controllers, moving objects, etc.) In the NMI *before* doing video updates (OAM DMA, name table updates, etc.), and since video updates can only be done during vblank, this means that the entire program only has the vblank period to run, which is less than 8% of the time available in a complete frame. That may be enough for a simple pong demo, but anything more complex than that will break.

The correct way to put an entire game inside the NMI handler is to do video updates first (based on flags that indicate which updates, if any, are ready to go), and then proceed to the game logic, which will compute the video updates for the next frame. If an NMI fires before the previous one has finished, it will detect this condition (by looking at a flag that indicates whether the game logic is complete) and skip video updates and game logic for that frame, doing only the few tasks that absolutely can't be skipped, such as setting up raster effects (status bars, parallax effects, etc.) and playing audio (if you don't want the music to slow down).
Post Reply