Hi, i'm working on writing an NES emulator (like everyone else here) and i'm a bit confused over how to handle interrupts. How should I handle an interrupt that occurs when the status register I flag is set? Should it be ignored, or should I add it to a stack to execute as soon as the current interrupt finishes or CLI is executed? How does the priority of each interrupt type come into this?
Thanks
You can't really ignore an interrupt, from the 6502's point of view an IRQ is coming from external hardware (which could vary widely). As long as the IRQ pin is held low, the 6502 will continually get interrupts. So the way it's handled is that the IRQ-generating hardware will have a way to acknowledge the interrupt, and release the IRQ pin so it goes back to being high.
So yeah, you should do the IRQ as soon as CLI is run or the current interrupt finishes, unless the IRQ source has been acknowledged already.
Thanks for the response. So that explains IRQ interrupts - but what about NMI and reset interrupts? Should they be handled in a similar way? What happens is an NMI interrupt is invoked when an IRQ interrupt is being executed, for instance?
It should be noted that if the 'I' flag is set and the IRQ line is pulled low and returned high again, clearing the 'I' flag will not result in an interrupt being serviced - in order for the 6502 to detect an interrupt, the I flag must be clear during the pulse on the IRQ line.
NMI can never be ignored - after all, it stands for Non-Maskable Interrupt. Also, NMI is edge-triggered, meaning if it stays low for a long time (like 20 scanlines worth of VBLANK), the 6502 will only service it once until it goes high and then low again.
RESET is sort of special, since holding it low causes the CPU to halt completely; only after 'releasing' the reset signal does the CPU execut the interrupt.
If two interrupts happen at the same time, NMI will take priority over IRQ (and RESET obviously takes priority over everything).
Cheers, so one more question: How exactly should i handle the BRK opcode? I know it uses the same vector address as IRQ, but does it actually hold the IRQ line low for a period of time? Should i buffer it until the CPU is available to execute an interrupt?
The BRK opcode is actually somewhat of a side-effect of how interrupts work on the 6502 - when it services an interrupt, it pulls all 8 data lines low on the internal "opcode register" (and thereby executes a BRK), disables PC incrementing (so it doesn't skip the next 2 bytes), and selects an interrupt vector to load from (whether it's NMI, RESET, or IRQ). The only differences between IRQ and BRK are that, with BRK, the PC increments twice and the 'breakpoint' "flag" is set (though it really isn't a flag, but rather a status line indicating that the CPU is servicing an actual interrupt).
BRK executes immediately regardless of the value of the I flag in the status register.