Hi Zoltan, nmi_pending captures the edge transition, while pending_level == 7 reflects the current level. Level 7 interrupts are edge-triggered — only the transition from a level less than 7 to level 7 initiates an NMI. If we just tested pending_level == 7, then it would act as level-triggered (the SR.I < pending_level check passes on every re-entry from rte as long as Level 7 stays asserted), which violates the spec.
Concretely, nmi_pending: - Is set true only on the rising edge: level == 7 && pending_level != 7 - Is cleared after the NMI is taken: nmi_pending = false - Is not re-set if Level 7 remains continuously asserted This matters on the Sun-3 where the PROM routes the Intersil 7170 clock to Level 7 (intreg=0x81). The IRQC re-evaluates on every clock tick, keeping pending_level == 7. Without the separate edge flag, the CPU would take a spurious NMI on every interrupt check instead of once per tick. Thanks, Dan. On Tue, Aug 18, 2026 at 3:56 AM BALATON Zoltan <[email protected]> wrote: > On Mon, 17 Aug 2026, 54weasels wrote: > > High level description: > > Level 7 interrupts are Non-Maskable Interrupts (NMI) on the M68k > architecture. The hardware asserts an NMI only on the rising edge of the > level 7 signal. The current QEMU implementation treats level 7 like a > standard level interrupt. This patch ensures proper NMI edge-triggered > semantics for level 7, which is strictly required by the Sun-3 > keyboard/mouse NMI routing logic. > > > > Impact on existing functionality: > > Corrects NMI edge-triggering for all M68k boards, adhering closely to > the Motorola specifications. Existing boards will now correctly require an > edge transition to trigger consecutive NMIs. > > > > Context: This patch was originally submitted as part of the monolithic > Sun-3 Machine Emulation series ( > https://patchew.org/QEMU/[email protected]/) and > has been split into atomic components. > > --- > > target/m68k/cpu.h | 1 + > > target/m68k/helper.c | 6 ++++++ > > target/m68k/op_helper.c | 14 ++++++-------- > > 3 files changed, 13 insertions(+), 8 deletions(-) > > > > diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h > > index 7cf3791108..058777b891 100644 > > --- a/target/m68k/cpu.h > > +++ b/target/m68k/cpu.h > > @@ -148,6 +148,7 @@ typedef struct CPUArchState { > > > > int pending_vector; > > int pending_level; > > + bool nmi_pending; > > What's the difference between nmi_pending and pending_level == 7? If > nothing do we need a new variable for it or could just test pending_level? > > Regards, > BALATON Zoltan > > > /* Fields up to this point are cleared by a CPU reset */ > > struct {} end_reset_fields; > > diff --git a/target/m68k/helper.c b/target/m68k/helper.c > > index 68f523ea84..93739ccda7 100644 > > --- a/target/m68k/helper.c > > +++ b/target/m68k/helper.c > > @@ -949,6 +949,12 @@ void m68k_set_irq_level(M68kCPU *cpu, int level, > uint8_t vector) > > CPUState *cs = CPU(cpu); > > CPUM68KState *env = &cpu->env; > > > > + if (level == 7 && env->pending_level != 7) { > > + env->nmi_pending = true; > > + } else if (level != 7) { > > + env->nmi_pending = false; > > + } > > + > > env->pending_level = level; > > env->pending_vector = vector; > > if (level) { > > diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c > > index 38f7a68981..30af4a2631 100644 > > --- a/target/m68k/op_helper.c > > +++ b/target/m68k/op_helper.c > > @@ -522,14 +522,12 @@ bool m68k_cpu_exec_interrupt(CPUState *cs, int > interrupt_request) > > { > > CPUM68KState *env = cpu_env(cs); > > > > - if (interrupt_request & CPU_INTERRUPT_HARD > > - && ((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) { > > - /* > > - * Real hardware gets the interrupt vector via an IACK cycle > > - * at this point. Current emulated hardware doesn't rely on > > - * this, so we provide/save the vector when the interrupt is > > - * first signalled. > > - */ > > + if (env->nmi_pending) { > > + env->nmi_pending = false; > > + cs->exception_index = env->pending_vector; > > + do_interrupt_m68k_hardirq(env); > > + return true; > > + } else if (((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) { > > cs->exception_index = env->pending_vector; > > do_interrupt_m68k_hardirq(env); > > return true; > > >
