Should I use NMI even though my stuff works without using it

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
albailey
Posts: 177
Joined: Thu Jul 13, 2006 3:15 pm

Should I use NMI even though my stuff works without using it

Post by albailey »

My game is a simple NROM puzzle game. The only scrolling is when I transition from the title screen, to the puzzle screen. I wait for VBLANK, then scroll a bit, (looping until its all in).
Then I go to my game loop which:
waits for VBLANK
polls for input
does updates
repeat...

Everything I've done so far works, but I am not using NMI.
I'm not "running long" and running out of cycles

I guess my questions are:
Should I be using NMI?
Is it considered bad form to code it this way?
Do some games do what I've done, or do they all sync using NMI?

I'd just like to hear peoples opinions. After all, I plan on doing other games so I'll have to learn other techniques eventally.

Thanks,
Al
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

There is two ways to wait on VBlank :
- Poll $2002
- Use NMIs

If you do the first method, you're sure you'll get a VBlank when your polling loop exits, however you don't know how many VBlanks you missed before, and it IS possible to miss VBlanks.
If you do it the second method, using NMIs, a NMI will trigger every VBlank no matter what (assuming they are enabled), so yeah, that method is better. The other is good only if you don't need smooth timing, so when the screen isn't used anyway. It is NO good for gameplay.
There is at least one Famicom game that does it all with $2002 polls : Portpia Renzoku Satsujin Jiken. However, since it's stricly an investigation game (so it doesn't scroll nor require any precise timing, it just have a blinking cursor that won't blink at constant rate on real hardware, but most user won't see that), and used frame IRQs for sound anyway, then it could rely on that method. That it still not recommanded.
Useless, lumbering half-wits don't scare us.
User avatar
Memblers
Site Admin
Posts: 3901
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers »

You should use NMIs, you need it for all the PPU and sound stuff. My main loop is timed by incrementing a variable in NMI and waiting for it to be non-zero.
User avatar
blargg
Posts: 3717
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

Code: Select all

nmi_count = $10 ; one byte in zero-page, location up to you

nmi:  inc nmi_count
      rti

wait_nmi:
      lda nmi_count
loop: cmp nmi_count
      beq loop
      rts
By not modifying nmi_count outside of NMI, other code can use it for timing, i.e.

Code: Select all

      lda nmi_count
      cmp next_count
      beq one_second_passed
      ...
      
one_second_passed:
      clc               ; update next_count
      adc #60
      sta next_count
      ...
albailey
Posts: 177
Joined: Thu Jul 13, 2006 3:15 pm

Post by albailey »

I know this is an old topic, but I think I probaby didn't do things correctly when I converted over to using NMIs.

All my code works and looks fine, but I might have just gotten lucky. 2 wrongs (or 20) making a right (so to speak).

When I converted my code from polling 2002 to using NMIs I got everything working. But I did it by creating a very large and complicated NMI routine.

Within the NMI routine I was checking my state machine, processing input, moving sprites, invoking sprite DMA, updating backgrounds, etc..

Should the actual "work" be done in the main loop and just the NMI timing or sync-ing (like the counter or flag variables mentioned by blargg) be all I maintain in the NMI?

Al
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

That is a style preference. There's no "right" or "wrong" way. SMB (I think?) and Final Fantasy simply has NMI return right away like blargg's example... while several other games do their work in the actual NMI routine.


The only thing I recommend is do drawing related stuff FIRST. Anything that has to be done in VBlank should be done before you do other stuff. Sprite DMA and any PPU writes should have top priority. Only after all that stuff is done should you start doing other things, like polling joypads, calling the music routine, etc.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

I don't know what you're talking about, but Super Mario Bros. runs the entire game as an NMI handler. After the init code, SMB's main thread is running the following code, which emulators running on handhelds (e.g. PocketNES and PocketNES) will usually automatically detect as a speed hack:

Code: Select all

:
  jmp :-
The easiest way to convert $2002 spinning code to NMI based code uses a variable in RAM:

Code: Select all

.segment "ZEROPAGE"
retraces: .res 1

.segment "CODE"
nmiHandler:
  inc retraces
  rti

; In main loop:
  lda retraces
:
  cmp retraces
  beq :-

dvdmth
Posts: 354
Joined: Wed Mar 22, 2006 8:00 am

Post by dvdmth »

SMB and Final Fantasy are on opposite ends of the spectrum. SMB lives and breates in NMI (the only piece of code that doesn't run in NMI is the reset code, which is very short), while Final Fantasy's NMI routine exits immediately and lets the main program resume execution.
"Last version was better," says Floyd. "More bugs. Bugs make game fun."
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

My opinion is that if you already got hte program working by polling $2002, the quickest way to make use of NMI's is to just set a flag in the NMI code, and poll that flag instead of $2002, bacause that will need no modification in the structure of your code, and can be done in, what, 5 minutes? Anyway, this is a very quick modification.

But with more complex games, where computing a frame may take more than a NES frame (if there are too many active enemies, for example), I think it'd be better to do the opposite. Have the main loop process everything and buffer the data to be written to the PPU, and set a flag when that data is ready. The NMI routine would then check that flag, and if a frame is ready for rendering, the data is sent to the PPU, but if it's not, you could still do other timed stuff that does not depend on graphic updates, such as music.

That way, even if the game did slow down because of the complexity of current events, the music would still play normally. If you used the polling method and the processing took more than a frame, you'd miss the Vblank, and everything, including the music, would be delayed by one frame. Even worse, if the game logic finished in the middle of Vblank, you wouldn't be able to detect that (unless you only detect the start of Blank, by first waiting the flag to be clear and then set), and the PPU drawing code could very well spill out of Vblank time, causing very visible glitches.

Polling should just be used with simple programs, where you know the processing time of each frame will fit nicely in a NES frame. Having all the code inside the NMI is already better than that, just do the graphical stuff first, game logic later.
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

Crap

I knew about Final Fantasy, but got mixed up with SMB. My mistake!
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

You can hear the music slow down in Zelda when the game overloads, and you can hear the music slow down AND the status bar flicker in SMB where there is many enemies. That is very bad programming from Nintendo, because they did everything in their NMI and disabled NMIs at all until it's finished (without the disabling, the game would crash).
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Bregalad wrote:That is very bad programming from Nintendo, because they did everything in their NMI and disabled NMIs at all until it's finished
Yeah, this sure causes music slowdown, because each NMI has to wait for the previous one to end, and if that takes longer than the time of a frame, a full frame will be lost.
(without the disabling, the game would crash).
Do you mean if an NMI fired while the previous one was still running? From a hardware point of view, does that even happen? Can an NMI fire while the previous one hasn't finished yet? I once read about IRQ's not firing before the previous IRQ routine finished, but I'm not sure.

If this is possible (NMI's firing before the previous one ended), and you read a flag right at the begining of the routine it could still work well... if a flag indicates that the previous NMI hasn't finished yet, only do the music, then return. If the previous NMI has finished, do all the graphics, sound, and game logic last. It would work just as well as well as if the game logic was outside of the NMI routine.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

For IRQs, they will never fire while one is executing if you do nothing special, because the I flag is automatically set on all interupts, disabling IRQs. The flag is automatically returned when you return from the IRQ, wich is normally clear in all cases, exept if the IRQ was caused by a brk opcode. Nothing prevent the IRQ routine to clear the I flag once the source of the IRQ has been acknownledged, allowing new IRQ to occurs before the first finishes. If the I flag is clear befor the IRQ source is acknownledged, the IRQ won't stop firing as soon as the cli (or possibly plp) opcode is met, and then you'll get a never-ending chain of IRQs, crashing the processor.

For NMIs, it's pretty much the same, the I flag is automatically set, unallowing IRQs to fire until the NMI returns or the flag is cleared by hand. The difference is that NMIs can still be triggered at anytime, regardless of the I flag, so the only way to disable NMIs is to clear $2000.7. If a game is programmed to have the NMI routine not clearing $2000.7, and if the NMI holds longer than a frame, it's unavoidable that the programm will crash, until you do some tricks.
If this is possible (NMI's firing before the previous one ended), and you read a flag right at the begining of the routine it could still work well... if a flag indicates that the previous NMI hasn't finished yet, only do the music, then return.
Yes, this would work well, unless the music code itself is very slow and slow downds things even more (wich is unlikely to be the case). I even think many Konami games does something like that (I haven't checked in details).
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Thanks for the info.
If a game is programmed to have the NMI routine not clearing $2000.7, and if the NMI holds longer than a frame, it's unavoidable that the programm will crash, until you do some tricks.
I guess the NMI routine can take longer than a frame to execute, as long as this doesn't happen too often, or the stack will definitely overflow.
If this is possible (NMI's firing before the previous one ended), and you read a flag right at the begining of the routine it could still work well... if a flag indicates that the previous NMI hasn't finished yet, only do the music, then return.
Yes, this would work well, unless the music code itself is very slow and slow downds things even more (wich is unlikely to be the case).
Yeah, I doubt any regular music code would take a significant ammount of time of the frame, as the rest of the game logic is more complicated and also has to run every frame. Anyway, I think this is a valid option, since the music code can execute quickly, NMI's won't pile up and overflow the stack.

I prefer not to do this for my game, since I'll be using multiple NMI routines at different parts of the game, and I'd rather have the main code control what routines execute at what times, instead of the routines themselves doing that.
User avatar
blargg
Posts: 3717
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

Do you mean if an NMI fired while the previous one was still running?
Yes, that's why it's called the Non-Maskable Interrupt (NMI). The 6502 has no concept of an NMI routine, just that of the NMI being asserted and causing the code to vector to the handler address. Once it's there, the NMI is over in the 6502's eyes. NMI is meant for things which must be reliably responded to, and why it's edge-triggered (once NMI line is asserted, it can be de-asserted and NMI will still occur, unlike IRQ).

The IRQ, on the other hand, has an inhibit flag which serves as a "still executing IRQ handler routine" flag, though there's no requirement that it be left set (inhibited) until the end of the IRQ handler.
Post Reply