5

According to multiple documentation sources, non-maskable interrupts (NMIs) can be nested (or reentrant) in a Z80 microprocessor.

This is, upon a /NMI signal pulse, the CPU will interrupt the current program by resetting IFF1 to disable maskable interrupts, pushing the program counter in the stack and jumping to address 0x0066 to execute the interrupt service routine there.

The point is that a new /NMI signal pulse can interrupt the ongoing ISR before it ends with a RETN instruction. This is documented in The Undocumented Z80 Documented and confirmed in other StackExchange questions. When this happens, there is an odd case that, if permitted, would derive in an abnormal situation.

If maskable interrupts were enabled before the NMI was accepted, the first invocation of the ISR will execute with IFF1 reset and IFF2 set. This is the well-known mechanism to restore IFF1 from IFF2 by RETN instruction when ISR terminates, so the user-defined interruption config is honored.

When the second /NMI pulse is detected before RETN takes place, a new NMI is accepted by the CPU. It will perform exactly the same procedure as the outer ISR: reset IFF1, push PC in the stack and jump to 0x0066.

Now let's say the inner ISR ends with a RETN instruction without any further NMI reentrance. This will copy IFF2 into IFF1, pop the PC from the stack, and continue with the execution of the ISR of the outer NMI. As maskable interrupts were enabled since the very beginning, this will effectively enable those interrupts. And the ISR of the outer NMI will execute such that it can be interrupted again by a maskable interrupt.

In other words, nested or reentrant NMIs can derive in situations where maskable interrupts are enabled, and a /INT signal can interrupt the execution of a NMI. This is equivalent to putting a EI instruction in the code of the NMI service routine.

After this, I am not sure if there is something I am not considering, or just that this is a flaw of the original design of the CPU. I checked the source code of a few Z80 emulators (MAME included), and all them operate as I am assuming. If that's how the things work, is /NMI something that have to be used very carefully? Do designers have to consider this and ensure /NMI cannot have multiple pulses? Or perhaps it is totally OK to have maskable interrupts interrupting NMIs? Am I exaggerating the situation then?

4
  • 1
    Before going into any details, I assume you are aware that an NMI is only intended as a last resort to allow interruption in unusual/critical situations? They are kind of a 'hack' to still allow interruption when incidents past regular operation happen. They essentially offer this bypassing all regular mechanics - and as usual when bypassing safety measures, the risks are all to be accounted for by the implementor.
    – Raffzahn
    Jan 16, 2022 at 17:49
  • That was my impression. In my mind, the perfect example for NMI is the parity check of a memory system. If parity detects an error, it could trigger a NMI to stop the world and inform the user that some RAM chip have to be replaced. However, I read recently that Sega Master System used the NMI to handle the pause button. Which is far from that last resort interruption. Jan 16, 2022 at 17:55
  • It all depends on the use case of a system. An NMI is reserved for well defined situations - either because they are dead end anyway (like a memory fault or a power outage) or because the system is defined to work that way. A general purpose computer will be of the first kind, while an embedded system might be of the later. Game consoles are as well embedded systems. Every interaction within is well adjusted to cooperate in a non harmful way. In such an environment everything can be used as one likes, as all components are adjusted to work with it.
    – Raffzahn
    Jan 16, 2022 at 18:06
  • AlvaroPolo, a good and well-presented question. NMIs are an essential and well-structured part of a microprocessor like a Z80, 6502, 6809 etc. Any running program has the capability to disable maskable interrupts with one inappropriate or crash-executed execution of a DI-type instruction so the system designer must be provided with a mechanism that cannot be blocked in this way. NMIs and INTs/IRQs are well-balanced options that system designers must select between and use with care, like many things. Same as maximum interrupt repetition, which you've uncovered an interesting Z80 aspect of.
    – TonyM
    Jan 16, 2022 at 18:33

1 Answer 1

3

It would cost a single flip-flop to prevent re-entering NMI handler, or at least the greatest part of it. The flip-flop output pin is routed to the /NMI pin, flip-flop reset is to actual NMI pulse source. The set pin would be then strobed programmatically (for example, doing out to a dedicated IO-port), as the last instruction before RETN.

This way, there is only a small chance of re-entering the NMI handler before RETN executes and in most cases re-entering would not do any harm.

More sophisticated measures could be counting /M1 cycles after triggering the abovementioned dedicated port -- so that actual NMI de-assertion would be done exactly as RETN has fully executed. This kind of NMI handling was actually done by me in "ZX-Evolution" ZX Spectrum clone.

The real problem of NMI in Z80 is the practical inability for an external observer to determine when Z80 actually starts to execute NMI ('start' here is when Z80 begins pushing return address). Given such an indication (in a way as it exists for maskable interrupt, which begins with /IORQ+/M1 strobation), one can enter NMI handler fully preserving CPU and memory state (by routing return address push to the dedicated registers instead of memory).

My current way of thinking as how to achieve that includes looking for every Z80 M1 and RD/WR cycle it executes and keeping a state of whether Z80 finishes executing each instruction, so that an unexpected double push after /NMI is toggled would be the thing.

9
  • In most cases, rather than using a dedicated flip flop to prevent re-entrant NMI, one would design whatever device would trigger an NMI in such a way as to ensure that it will only trigger once.
    – supercat
    Jan 17, 2022 at 15:38
  • Only triggering NMI once a lifetime of device (meaning lifetime since Z80 and devices were reset) does not seem like an universal approach. Once multiple NMI events each from independent device are wanted, the re-entry problem returns.
    – lvd
    Jan 17, 2022 at 17:24
  • 2
    My point was that there's nothing wrong with using NMI in situations where a system is operating normally, and is expected to continue to do so, with the proviso that external hardware must only assert NMI at times when a program is prepared for that to happen. How external hardware would know that the program is prepared for an NMI would depend upon the application.
    – supercat
    Jan 17, 2022 at 20:31
  • 1
    Oh, I think it was clear from that question that I was interested in whether or not designers have to take this inconsistency in consideration. But not how, which is what your response explains. But please, do not take this as a blame. I appreciate your response. It is just I was mainly interested in finding out if this is how the Z80 actually works and what is the intention behind it. Jan 18, 2022 at 14:19
  • 1
    It is obvious for me that the designers made exactly what they did. All your assumptions and deductions about NMI are actually right, and the only thing one can do here is to live with it :) Either by working around in software or in hardware (or both).
    – lvd
    Jan 18, 2022 at 16:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .