- 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?
Since NMI raises the I flag, the IRQ would not happen until I is cleared again (by CLI/RTI or whatever)
- Yes.
- I don't remember of a test ROM for this behaviour though... does it exist?
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.
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)
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.
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.
APU Frame IRQs I could see -- but can you get that tight of timing on DMC IRQs?
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).
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.
I'm unable to download that file, website seems down..?
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.
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.
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:
; 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
Man, I can't download from that server, for some reason... -_-;;
It seems like the print functions (print_x, print_y etc.) leaves the PPU rendering ON, causing it to go into its +0/-1 odd-frame-cycle mode. Won't this cause a problem for the sync_nmi routine (it says the rendering should be OFF)? If I disable it manually before sync_nmi is executed, the test passes, but otherwise not since the NMI will then occur a few PPU clocks too early (before LDA) on the 2nd run.
I'm having the same trouble as FentonCole. I get the following when I run the demo:
Code:
$23 $00
$23 $00
$21 $00
$21 $00
$20 $00
$20 $00
$20 $00
$20 $00
$20 $00
$20 $00
$20 $00
$24 $20
It's not totally wrong -- it's just shifted down; there's an extra $23 $00 in there and it's as if the first NMI is occuring 1 clock earlier than it should. But when I take out my skip-odd-cycle implimentation, I get a pass.
I'll have to look at this more later
Ugh ugh ugh, all my fault. I ported my dev stuff to ca65 yesterday so others can more easily modify things. I can't test the final code on my NES very easily since it can't run normal ROMs, so I only test using a runtime that prints things over serial rather than to the screen.
New, improved, properly tested on my NES at reset time, ca65 source that can be built on anyone's machine, lots of nifty reusable library routines and scrolling text console, and some description of the code organization and philosophy:
nmi_during_irq2.zip
At this point I'm not sure how to interpret the result (what's actually happening at each clock), but at least you have something you can compare your emulator to a NES with. And if I've still screwed up like before, don't hesitate to call me on it. Testing on a NES using a more normal devcart would be appreciated.
Can't you upload into another server? I'm unable to downl... oh, I already said that, right?
EDIT: ...got it.
Code:
http://www.proxyhustle.com/
Thanks for all the excellent work blargg! It's definately appreciated.
I'm having some trouble though. I get the right sequence of numbers... but it still says I failed.
Code:
23 00
21 00
21 00
20 00
20 00
20 00
20 00
20 00
20 00
20 00
24 20
24 20
30BFCFCA
FAILED: 2
Similar problem with the new BRK test. I get the same results as before (except there's an extra 27 36 00 line in front) but it's saying I'm failing, whereas I pass the old test.
Did the correct output change? Is my checksum wrong? If so, what problem does that indicate?
Thanks again for your much valuable tests. It seems like the NMI is delayed for another instruction if it's past the point of being able to steal the interrupt from BRK/IRQ, i.e. somewhere around ~5/6 CPU cycles into the ISR. Going with that, I now pass the tests.
Disch, the last two lines of the nmi_during_irq test should have 25 for the NMI flag, so carry is set, indicating that for these two the NMI is occurring after the SEC at the beginning of the IRQ handler, not before as is apparently happening for yours. What really seems to be going on (to me) internally is that NMI will replace the IRQ if it is asserted during the first five clocks of the IRQ handling; if it occurs during the last two, it will occur after the first instruction of the IRQ handler (fitting in with the usual behavior of NMI). This would mean that the first two "20 00" lines are where the NMI is being asserted during the first and second clocks of the CLC, not the IRQ handling itself.
blargg wrote:
What really seems to be going on (to me) internally is that NMI will replace the IRQ if it is asserted during the first five clocks of the IRQ handling; if it occurs during the last two, it will occur after the first instruction of the IRQ handler (fitting in with the usual behavior of NMI).
Rather than trying to understand why this is so (it doesn't really make any logical sense to me), I'll just accept that that's how it works.
Anyway I have both working now, and since correct numbers for the new tests have not yet been posted, here they are:
Code:
nmi_during_brk:
27 30 00
26 36 00
26 36 00
36 00 00
36 00 00
36 00 00
36 00 00
36 00 00
27 36 00
27 36 00
nmi_during_irq:
23 00
21 00
21 00
20 00
20 00
20 00
20 00
20 00
20 00
20 00
25 20
25 20
And a recap of behavior for good measure:
-) if an NMI occurs during the first 5 cycles of BRK or IRQ, status is pushed to stack as if it were a BRK/IRQ, but the NMI vector is jumped to instead. IRQ/BRK is effectively supressed. Though an IRQ would still be pending and will occur after RTI unless acknowledged -- but the BRK is completely skipped over.
-) if an NMI occurs during the last 2 cycles of BRK or IRQ, BRK/IRQ proceeds as normal, and NMI occurs after the first instruction following the BRK/IRQ.
I have no clues what's happening here, I got:
Code:
23 00
21 00
21 00
25 21
25 21
25 21
...
- Could you help me?
The first column is the flags on entry to the NMI, the second IRQ, or 00 if that handler wasn't executed. The time the IRQ (would) occur is fixed, while the time for the NMI is one clock later on each successive row. This is the code section where the interrupts occur:
Code:
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 should occur here, but yours seems to be occurring before the clc
nop
The first result row you got was 23 00, so the NMI is firing when the C and Z flags are set, and it's before the LDA #1. Then the next two lines, it's firing when just the C flag is set, so you can see that it must be just after the LDA #1 which clears the Z flag. Then you have 25 21, so the NMI is firing when the I and C flags are set, and the IRQ is firing with just the C flag set. So apparently your IRQ is firing before the CLC, and before the NMI as well (since the NMI occurs when the I flag is set, as it would be inside the IRQ handler). The source code includes similar commentary on the correct sequence for comparison.
Thanks for the test roms blargg.
(small typo by Disch 3 posts above me, nmi_during_brk 1st line "27 30 00" should of course be "27 36 00")
@blargg
- You're using frameIRQs. My emulator passes in all APU (and NMI) test ROMs, with no hacks. During the sequence #4 (line 5, or "20 00"), the IRQs start firing (making a request to trigger)
before the CLC instruction. This way, a 7-cycles IRQ is started, but a NMI is requested during the 1st of those 7 cycles.
- Is my IRQ timing off even if it passes the APU test ROMs???
Code:
23 00
21 00
21 00
20 00
25 21 <- here
25 20
25 21
25 20
25 21
25 20
25 21
25 20
EDIT: I fixed my code and it passes. There's an exception though: I cannot figure why a NMI is being requested during a LDX #$01 instruction (2nd byte fetch to be exact), making to trigger right in the end of the instruction, instead of triggering in the next one. I added an hack to acknowledge the NMI request during such instruction. This issue happens on nmi_timing test ROM.