diff mbox series

[v5,3/6] target/m68k: Fix NMI pending for Level 7 interrupts

Message ID 20260903044626.24453-4-54weasels@gmail.com
State New
Headers show
Series Implement missing functionality and hooks in m68k to support upcoming Sun-3 implementation | expand

Commit Message

54weasels Sept. 3, 2026, 4:45 a.m. UTC
Why this change is made & How it will be used:
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.

Signed-off-by: 54weasels <54weasels@gmail.com>
---
 target/m68k/cpu.h       |  4 ++++
 target/m68k/helper.c    |  6 ++++++
 target/m68k/op_helper.c | 14 ++++++--------
 3 files changed, 16 insertions(+), 8 deletions(-)
diff mbox series

Patch

diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
index 7cf3791108..8f4dec4a40 100644
--- a/target/m68k/cpu.h
+++ b/target/m68k/cpu.h
@@ -25,6 +25,9 @@ 
 #include "exec/cpu-interrupt.h"
 #include "qemu/cpu-float.h"
 #include "cpu-qom.h"
+#include "exec/cpu-defs.h"
+
+#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_1
 
 #define OS_BYTE     0
 #define OS_WORD     1
@@ -148,6 +151,7 @@  typedef struct CPUArchState {
 
     int pending_vector;
     int pending_level;
+    bool nmi_pending;
 
     /* 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;