I've found that the author of "[Z80 Explorer](https://baltazarstudios.com/z80explorer/)", Goran Devic, has written a Z80 module for FPGA, presumably based on the extensive analysis of the die that he made. [This file](https://github.com/gdevic/A-Z80/blob/d70a4f9ab3b068788a72be25352d2ba4a2a9e0d8/cpu/control/interrupts.v) deals with interrupts. I haven't checked much of the rest of files but I think it's safe to assume the following, specifically related to the NMI: - `nreset` is the /RESET line. - `setM1` is a flag that indicates that this is the last cycle in the instruction (as it has to, well, set M1 on the next cycle). - `ctl_no_ints` is a signal that tells whether interrupts are not possible at this moment (interrupts are not possible during CB, DD, ED or FD prefixes, even if M1 will be active the next byte). - `nmi` is the negation of the /NMI line. Now, here's what I gather from it. Ignoring the /RESET line: - `nmi_armed` is clocked by the /NMI line (and by the line that resets it), not by the system clock. It's set with an /NMI falling edge, and reset when the flag that tracks whether the interrupt needs servicing is set. So it seems to be a sort of async-to-sync bridge that holds the fact that NMI was requested until the CPU is ready to read it. - `in_nmi_ALTERA_SYNTHESIZED` seems to be the register that tracks whether the NMI needs servicing. It's clocked by the system clock and copied from `nmi_armed` on the last clock of an instruction, provided interrupts are possible. This means that it will be reset on the last cycle of the next instruction executed, assuming `nmi_armed` has not been set again in between by another /NMI negative edge, of course. Here's a simplified version of the parts of the code that are relevant to NMI, with the nreset signal eliminated for clarity: ``` input wire setM1; input wire ctl_no_ints; input wire nmi; output wire in_nmi; reg nmi_armed; reg in_nmi_ALTERA_SYNTHESIZED; always @(posedge nmi or posedge in_nmi_ALTERA_SYNTHESIZED) begin nmi_armed <= ~in_nmi_ALTERA_SYNTHESIZED; end always @(posedge clk) begin if (setM1 & ~ctl_no_ints) begin in_nmi_ALTERA_SYNTHESIZED <= nmi_armed; end end assign in_nmi = in_nmi_ALTERA_SYNTHESIZED; ```