NMI & IRQ

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

User avatar
Zepper
Formerly Fx3
Posts: 3190
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

NMI & IRQ

Post by Zepper » Fri Dec 28, 2007 8:23 am

- If both are pending to trigger, I'm allowing the NMI to trigger first, then the IRQ at the next instruction / byte fetch. Is this correct?

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

Post by Disch » Fri Dec 28, 2007 9:04 am

Since NMI raises the I flag, the IRQ would not happen until I is cleared again (by CLI/RTI or whatever)

User avatar
Zepper
Formerly Fx3
Posts: 3190
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Post by Zepper » Fri Dec 28, 2007 9:17 am

- Yes.
- I don't remember of a test ROM for this behaviour though... does it exist?

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

Post by dvdmth » Fri Dec 28, 2007 10:57 am

Some situations get a little tricky. For example, suppose an IRQ is pending, then during the first four cycles of the 7-cycle interrupt sequence an NMI occurs. The CPU will then continue the seven-cycle sequence (no new sequence begins), but it will jump to ($FFFA) as if the first interrupt were an NMI instead of an IRQ. If the NMI occurs during the fifth or sixth cycle of the seven-cycle sequence, a new seven-cycle sequence will begin immediately after the conclusion of the current sequence (with the value of PC pushed to the stack being the same asthe ($FFFE) vector). Finally, if the NMI comes during the last cycle, one instruction of the IRQ handler will execute before the NMI is processed.

A similar phenomenon exists if BRK is interrupted by NMI. If the NMI comes during the first four cycles of BRK, the CPU will treat the BRK as if it were the NMI and jump to ($FFFA). However, the B flag will still be set in the processor flags on the stack, and the return address on the stack will still point to the instruction after BRK (instead of pointing to the BRK). Thus, if a program used BRK and if there was a possibility of an NMI occurring during the BRK, that program would have to test for the B flag in both the NMI and IRQ handlers, since either of them could have the flag set.
"Last version was better," says Floyd. "More bugs. Bugs make game fun."

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Fri Dec 28, 2007 2:16 pm

Test ROM: nmi_during_brk.zip

Result when run on NES:

NMI BRK X
$26 $36 $00
$26 $36 $00
$36 $00 $00
$36 $00 $00
$36 $00 $00
$36 $00 $00
$36 $00 $00
$26 $36 $00
$26 $36 $00

PASSED

Test has NMI occur one clock later on each successive row, such that it eventually suppresses the BRK (apparently there's a 5-clock window for this). First column is the processor status on the stack when the NMI occurs, second when BRK occurs (zero if it didn't occur), third should always be zero.

As you can see, when the BRK is suppressed, the B flag (bit 4) is set in the status during NMI.

(If I think of a way to test NMI during IRQ this cleanly, I'll code and post)

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

Post by Disch » Fri Dec 28, 2007 3:03 pm

blargg wrote:(If I think of a way to test NMI during IRQ this cleanly, I'll code and post)
Could you use CPU triggered MMC3 IRQs? I thought you did something like that for some of your MMC3 IRQ test ROMs.

Also -- thanks! More test ROMs are always excellent.

tepples
Posts: 21750
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples » Fri Dec 28, 2007 3:09 pm

Or you could do it once for MMC3 IRQs, once for DMC IRQs, and once for APU Frame IRQs, to catch emulator developers in the act of getting hacky with one IRQ source and not the others.

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

Post by Disch » Fri Dec 28, 2007 3:12 pm

APU Frame IRQs I could see -- but can you get that tight of timing on DMC IRQs?

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Fri Dec 28, 2007 3:28 pm

The main trick is getting an IRQ to occur at an arbitrary time, since the NMI timing can't be meaningfully changed (or maybe it could, if the enabling-mid-VBL-causes-immediate-NMI behavior were used).

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

Post by Disch » Fri Dec 28, 2007 3:31 pm

that's why I thought MMC3 would work. You can trigger the IRQ any time with a $2007 read/write under the right conditions (which you can set up beforehand)

Using that would require minimal code changes from what you have now. Just replace the BRK with like a $2007 read, put some $2006 writes just prior to it and maybe adjust your wait time to accomidate.

User avatar
Zepper
Formerly Fx3
Posts: 3190
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Post by Zepper » Fri Dec 28, 2007 6:12 pm

I'm unable to download that file, website seems down..?

FentonCole
Posts: 4
Joined: Sun Dec 30, 2007 4:42 am

Post by FentonCole » Sun Dec 30, 2007 4:48 am

Inside the sync_nmi routine, I noticed that the beq instruction branches directly to cmp and not lda. Was this intentional? I seem to get stuck inside this loop on the very last test since the nmi flag hasn't been set at that point yet. Nice work nevertheless.

User avatar
Zepper
Formerly Fx3
Posts: 3190
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Post by Zepper » Sun Dec 30, 2007 10:43 am

FentonCole wrote:Inside the sync_nmi routine, I noticed that the beq instruction branches directly to cmp and not lda. Was this intentional? I seem to get stuck inside this loop on the very last test since the nmi flag hasn't been set at that point yet. Nice work nevertheless.
Yes, same here... ^_^;; I'm doing a deep examination... but supposely, it's waiting for a NMI or IRQ to occur.

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Sun Dec 30, 2007 5:45 pm

I finally got an NMI interrupting an IRQ at varying times: nmi_during_irq.zip

Sorry for terseness, I just don't have the energy to do more. I can answer questions.

Code: Select all

; Critical test code
lda #0
clv
sec
; Z and C set, others clear

; NMI occurs here first,
lda   #1    ; clear Z flag
; then here for two clocks,
clc         ; clear C flag
; then here.

; IRQ always occurs here.
sec
...

; IRQ handler is the same except it saves into irq_flag
nmi:
    ...
    pla           ; save status byte from stack
    sta nmi_flag
    pha
    bit $4015     ; clear APU IRQ flag
    ...
    rti

Each row of the following table is for NMI occurring one clock later than the previous.

NMI IRQ
-----------------------------------------------------------
$23 $00  NMI occurs before LDA #1
$21 $00  NMI occurs after LDA #1 (Z flag clear)
$21 $00 
$20 $00  NMI occurs after CLC, interrupting IRQ
$20 $00 
$20 $00 
$20 $00 
$20 $00 
$20 $00 
$20 $00  Same result for 7 clocks before IRQ is vectored
$24 $20  IRQ occurs, then NMI occurs immediately after
$24 $20 

User avatar
Zepper
Formerly Fx3
Posts: 3190
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Post by Zepper » Sun Dec 30, 2007 6:18 pm

Man, I can't download from that server, for some reason... -_-;;

Post Reply