Skip to main content
Big update
Source Link
  • nreset is the /RESET line.
  • nmi is the negation of the /NMI 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 disabled for this opcode. Interrupts are disabled for CB, DD, ED or FD prefixes, even if M1 will be active in the next byte - EDIT: according to the code, they're also disabled for DI and EI, even in the case of NMI.(*)
  • 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 executed11-cycle NMI processing, assuming nmi_armed has not been setwhich sets setM1 again in between by another /NMI negative edge, of course.

(*) According to the code, DI and EI also set ctl_no_ints, which would mean they also prevent an NMI from executing in the next instruction. I don't think this is right - in the die simulation, EI seems to disable IFF1 until the falling edge of M1T2 of the next instruction, in which it is set again, and that seems to be the way it prevents a maskable interrupt after EI, yet NMI can happen right after either DI or EI.

Update:

In the "Z80 Remix" die-level simulation, nmi_armed is node #57 while in_nmi appears to be node #78 or #1070, not sure which as I haven't seen them differ and I can't read dies. #57 is about halfway between the _nmi and the _halt pins (easy to find by ticking and unticking "NMI" without stepping the clock, and checking what's changed), while #78 and #1070 are both immediately below the _halt pin.

There's an inconsistency between the die version and the Verilog version, in that in the die version, nmi_armed is cleared 2 half-clocks after node #78 goes on, while in the Verilog version it should be cleared pretty much immediately. I don't know the reason for this discrepancy, but it doesn't affect the outcome much.

As mentioned above, there's another inconsistency in that in the die simulation, an NMI can occur right after DI or EI, while in the Verilog version can't.

Yet another inconsistency is that in the Verilog version, there's a synchronous int_armed register that holds the negation of the /INT state until the CPU acknowledges it, while in the die simulation, activating /INT at any clock other than the last will skip the interrupt.

I don't know the reason for these discrepancies, but it would appear that the Verilog version is not too precise in these details. These inconsistencies diminish the confidence in the Verilog version and thus in the accuracy of this answer, although the die simulation seems to support the general findings.

  • nreset is the /RESET line.
  • nmi is the negation of the /NMI 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 disabled for this opcode. Interrupts are disabled for CB, DD, ED or FD prefixes, even if M1 will be active in the next byte - EDIT: according to the code, they're also disabled for DI and EI, even in the case of NMI.
  • 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.
  • nreset is the /RESET line.
  • nmi is the negation of the /NMI 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 disabled for this opcode. Interrupts are disabled for CB, DD, ED or FD prefixes, even if M1 will be active in the next byte (*)
  • 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 11-cycle NMI processing, which sets setM1 again.

(*) According to the code, DI and EI also set ctl_no_ints, which would mean they also prevent an NMI from executing in the next instruction. I don't think this is right - in the die simulation, EI seems to disable IFF1 until the falling edge of M1T2 of the next instruction, in which it is set again, and that seems to be the way it prevents a maskable interrupt after EI, yet NMI can happen right after either DI or EI.

Update:

In the "Z80 Remix" die-level simulation, nmi_armed is node #57 while in_nmi appears to be node #78 or #1070, not sure which as I haven't seen them differ and I can't read dies. #57 is about halfway between the _nmi and the _halt pins (easy to find by ticking and unticking "NMI" without stepping the clock, and checking what's changed), while #78 and #1070 are both immediately below the _halt pin.

There's an inconsistency between the die version and the Verilog version, in that in the die version, nmi_armed is cleared 2 half-clocks after node #78 goes on, while in the Verilog version it should be cleared pretty much immediately. I don't know the reason for this discrepancy, but it doesn't affect the outcome much.

As mentioned above, there's another inconsistency in that in the die simulation, an NMI can occur right after DI or EI, while in the Verilog version can't.

Yet another inconsistency is that in the Verilog version, there's a synchronous int_armed register that holds the negation of the /INT state until the CPU acknowledges it, while in the die simulation, activating /INT at any clock other than the last will skip the interrupt.

I don't know the reason for these discrepancies, but it would appear that the Verilog version is not too precise in these details. These inconsistencies diminish the confidence in the Verilog version and thus in the accuracy of this answer, although the die simulation seems to support the general findings.

after looking at the code, DI and EI also set ctl_no_ints
Source Link

I've found that the author of "Z80 Explorer", Goran Devic, has written a Z80 module for FPGA, presumably based on the extensive analysis of the die that he made. This file 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.
  • nmi is the negation of the /NMI 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 atdisabled for this moment (interruptsopcode. Interrupts are not possible duringdisabled for CB, DD, ED or FD prefixes, even if M1 will be active thein the next byte).
  • - EDIT: according to the code, they're also disabled for nmiDI isand EI, even in the negationcase of the /NMI lineNMI.

Now, here's what I gather from it. Ignoring the /RESET line, which just resets both registers:

  • 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 ofHere are the parts of the code that are relevant to NMI, with the nreset signal eliminated for clarity, simplified to get rid of the confusingly named wires:

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;

I've found that the author of "Z80 Explorer", Goran Devic, has written a Z80 module for FPGA, presumably based on the extensive analysis of the die that he made. This file 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;

I've found that the author of "Z80 Explorer", Goran Devic, has written a Z80 module for FPGA, presumably based on the extensive analysis of the die that he made. This file 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.
  • nmi is the negation of the /NMI 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 disabled for this opcode. Interrupts are disabled for CB, DD, ED or FD prefixes, even if M1 will be active in the next byte - EDIT: according to the code, they're also disabled for DI and EI, even in the case of NMI.

Now, here's what I gather from it. Ignoring the /RESET line, which just resets both registers:

  • 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 are the parts of the code that are relevant to NMI, with the nreset signal eliminated for clarity, simplified to get rid of the confusingly named wires:

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;
Add code exceprt, clarify a part
Source Link

I've found that the author of "Z80 Explorer", Goran Devic, has written a Z80 module for FPGA, presumably based on the extensive analysis of the die that he made. This file 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 to the one where it wasexecuted, 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;

I've found that the author of "Z80 Explorer", Goran Devic, has written a Z80 module for FPGA, presumably based on the extensive analysis of the die that he made. This file 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 to the one where it was set.

I've found that the author of "Z80 Explorer", Goran Devic, has written a Z80 module for FPGA, presumably based on the extensive analysis of the die that he made. This file 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;
Source Link
Loading