Question about NMI and triggering.

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Question about NMI and triggering.
by on (#203293)
Hi, I have my CPU for an NES emulator working and it passes nestest.nes. Now I am getting started with writing the PPU, but I am having a hard time actually knowing when an NMI is triggered for a VBLANK.

I am just trying to get the nametable to be written for Donkey Kong -- for something simple first...

If my understanding is correct, the NMI bit is controlled in BIT_7 in $2000. I am turning on this bit when my scanlines hit 241. I set the NMI bit, then the Vblank bit in 2002.

So my cycle is like this (in pseudo-code):

Code:
run() {
    fetchOpcode();
    executeOpcode();
    ppuRun();
    // check NMI here. and execute NMI.
}

ppuRun() {
    incrementPPUCycles();
    if(cycles === 341) ppuScanlines++;
    if(ppuScanlines >= 241 && ppuScanlines <= 260) {
        ppuSetNmi(1);
        ppuSetVblank(1);
    }

    if(ppuScanlines == 261) {
       ppuSetNmi(0);
       ppuSetVblank(0);
    }

    if(ppuScanlines == 262) {
       outputFrame();
       ppuScanlines = 0;
    }
}


Do I have the right idea here? If so, I am having problems actually going into NMI. I actually go into the NMI handler in FFFA/FFFB, but then the instructions are
Code:
PHA
over and over again. My guess is that it's going into the NMI routine in every CPU cycle. Just making sure if i have things conceptually right before going further.
Re: Question about NMI and triggering.
by on (#203294)
/NMI on a 6502 is edge-sensitive. This means if goes from high (unasserted) to low (asserted), the CPU will call the NMI handler. It will not call the NMI handler again until /NMI goes high then low again. This is why you usually* see only one call to the NMI handler per vblank.

/IRQ, by contrast, is level-sensitive. If a CLI or PHP instruction clears the interrupt priority level bit (I, bit 2 of P) while /IRQ remains low, the CPU will call the IRQ handler again.


* A couple games, such as Bases Loaded, clear bit 7 of $2000 and then set it again without reading $2002 to acknowledge the NMI. These games expect this to cause the CPU to call the NMI handler again.
Re: Question about NMI and triggering.
by on (#203295)
So then, it would make sense to do something along the lines of keeping the previous NMI flag and then check --

if the currentNMI is true, and the previousNMI was false, then run NMI handler?
Re: Question about NMI and triggering.
by on (#203296)
Yes, you will need to keep track of the previous state of /NMI. Someone familiar with Visual 6502 could find exactly the node that stores the previous state.
Re: Question about NMI and triggering.
by on (#203299)
Cool! that looks like it fixed it...

Looks like my emu isn't stupidly executing PHA over and over again. :)

As far as the IRQ bit goes, that's just as far as checking if the bit is asserted/deasserted on every opcode execution?
Re: Question about NMI and triggering.
by on (#203323)
Not sure if it's useful, information from test ROMs. "Reading flag" means reading $2002 bit $80 (VBlank flag).
Code:
Reading flag 4 PPU clocks before set shouldn't suppress NMI
Reading flag 3 PPU clocks before set shouldn't suppress NMI
Reading flag 2 PPU clocks before set shouldn't suppress NMI
*Reading flag 1 PPU clock before set should suppress NMI
*Reading flag when it's set should suppress NMI
*Reading flag 1 PPU clock after set should suppress NMI
Reading flag 2 PPU clocks after set shouldn't suppress NMI
Reading flag 3 PPU clocks after set shouldn't suppress NMI
Reading flag 4 PPU clocks after set shouldn't suppress NMI