* [PATCH v4 0/6] Implement missing functionality and hooks in mk68k to support upcoming sun3 impl
@ 2026-08-18 2:22 54weasels
2026-08-18 2:22 ` [PATCH v4 1/6] target/m68k: Add dummy CAAR register 54weasels
` (5 more replies)
0 siblings, 6 replies; 12+ messages in thread
From: 54weasels @ 2026-08-18 2:22 UTC (permalink / raw)
To: qemu-devel; +Cc: laurent, 54weasels
Extends the 68020 implementation to support the requirements of a sun3 machine.
This patch was originally submitted as part of the monolithic Sun-3 Machine Emulation series (https://patchew.org/QEMU/20260503015756.99176-1-54weasels@gmail.com/) and has been split into smaller parts.
54weasels (6):
target/m68k: Add dummy CAAR register
target/m68k: Fix fsave/frestore for 68881 FPU
target/m68k: Fix NMI pending for Level 7 interrupts
target/m68k: Extract Function Codes during TLB fills
target/m68k: Implement custom MMU intercept hook
target/m68k: Implement Physical Bus Error and Stack
target/m68k/cpu.c | 4 +-
target/m68k/cpu.h | 18 ++++-
target/m68k/helper.c | 130 ++++++++++++++++++++++++++++-
target/m68k/op_helper.c | 175 ++++++++++++++++++++++++++--------------
target/m68k/translate.c | 30 +++++--
5 files changed, 280 insertions(+), 77 deletions(-)
--
2.50.1 (Apple Git-155)
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v4 1/6] target/m68k: Add dummy CAAR register
2026-08-18 2:22 [PATCH v4 0/6] Implement missing functionality and hooks in mk68k to support upcoming sun3 impl 54weasels
@ 2026-08-18 2:22 ` 54weasels
2026-08-18 2:22 ` [PATCH v4 2/6] target/m68k: Fix fsave/frestore for 68881 FPU 54weasels
` (4 subsequent siblings)
5 siblings, 0 replies; 12+ messages in thread
From: 54weasels @ 2026-08-18 2:22 UTC (permalink / raw)
To: qemu-devel; +Cc: laurent, 54weasels
High level description:
The Sun-3 ROM expects the Cache Control Register (CAAR) to exist on the 68020. Reading/writing this register currently aborts QEMU. This patch provides a dummy implementation that simply ignores writes and returns 0 on reads, allowing the ROM to continue booting.
Impact on existing functionality:
No impact on existing targets. ColdFire and Mac targets do not access CAAR in a way that currently aborts, and providing a safe dummy implementation ensures robustness across M68k boards.
Context: This patch was originally submitted as part of the monolithic Sun-3 Machine Emulation series (https://patchew.org/QEMU/20260503015756.99176-1-54weasels@gmail.com/) and has been split into atomic components.
---
target/m68k/helper.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index 5f91d206f5..68f523ea84 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -281,8 +281,10 @@ void HELPER(m68k_movec_to)(CPUM68KState *env, uint32_t reg, uint32_t val)
return;
}
break;
- /* Unimplemented Registers */
+ /* Dummy implementation for CAAR */
case M68K_CR_CAAR:
+ return;
+ /* Unimplemented Registers */
case M68K_CR_PCR:
case M68K_CR_BUSCR:
cpu_abort(env_cpu(env),
@@ -385,8 +387,10 @@ uint32_t HELPER(m68k_movec_from)(CPUM68KState *env, uint32_t reg)
return env->mmu.ttr[M68K_DTTR1];
}
break;
- /* Unimplemented Registers */
+ /* Dummy implementation for CAAR */
case M68K_CR_CAAR:
+ return 0;
+ /* Unimplemented Registers */
case M68K_CR_PCR:
case M68K_CR_BUSCR:
cpu_abort(env_cpu(env), "Unimplemented control register read 0x%x\n",
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v4 2/6] target/m68k: Fix fsave/frestore for 68881 FPU
2026-08-18 2:22 [PATCH v4 0/6] Implement missing functionality and hooks in mk68k to support upcoming sun3 impl 54weasels
2026-08-18 2:22 ` [PATCH v4 1/6] target/m68k: Add dummy CAAR register 54weasels
@ 2026-08-18 2:22 ` 54weasels
2026-08-18 10:52 ` BALATON Zoltan
2026-08-18 2:22 ` [PATCH v4 3/6] target/m68k: Fix NMI pending for Level 7 interrupts 54weasels
` (3 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: 54weasels @ 2026-08-18 2:22 UTC (permalink / raw)
To: qemu-devel; +Cc: laurent, 54weasels
High level description:
The 68881/68882 FPUs require fsave/frestore instructions to read/write state frames. QEMU only fully implemented this for the 68040. The Sun-3 uses a 68881 FPU, and the boot ROM executes `fsave` to probe FPU presence. This patch adds basic 68881 FPU state frame handling (NULL frame) to accurately simulate an idle FPU state.
Impact on existing functionality:
Fixes FPU detection for 68881/68882 across all M68k boards without affecting 68040 specific state logic.
Context: This patch was originally submitted as part of the monolithic Sun-3 Machine Emulation series (https://patchew.org/QEMU/20260503015756.99176-1-54weasels@gmail.com/) and has been split into atomic components.
---
target/m68k/translate.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 138c89d3e5..bdf619883c 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -5364,11 +5364,19 @@ DISAS_INSN(frestore)
gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
return;
}
- if (m68k_feature(s->env, M68K_FEATURE_M68040)) {
+ if (m68k_feature(s->env, M68K_FEATURE_M68040) ||
+ m68k_feature(s->env, M68K_FEATURE_FPU)) {
SRC_EA(env, addr, OS_LONG, 0, NULL);
- /* FIXME: check the state frame */
+ if (m68k_feature(s->env, M68K_FEATURE_M68040)) {
+ /* FIXME: check the state frame */
+ } else {
+ /*
+ * 68881/68882 FRESTORE: read the state frame
+ * (NULL frame is 4 bytes)
+ */
+ }
} else {
- disas_undef(env, s, insn);
+ disas_undef_fpu(env, s, insn);
}
}
@@ -5383,8 +5391,12 @@ DISAS_INSN(fsave)
/* always write IDLE */
TCGv idle = tcg_constant_i32(0x41000000);
DEST_EA(env, insn, OS_LONG, idle, NULL);
+ } else if (m68k_feature(s->env, M68K_FEATURE_FPU)) {
+ /* 68881/68882 FSAVE: always write NULL frame */
+ TCGv null_frame = tcg_constant_i32(0x00000000);
+ DEST_EA(env, insn, OS_LONG, null_frame, NULL);
} else {
- disas_undef(env, s, insn);
+ disas_undef_fpu(env, s, insn);
}
}
#endif
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v4 3/6] target/m68k: Fix NMI pending for Level 7 interrupts
2026-08-18 2:22 [PATCH v4 0/6] Implement missing functionality and hooks in mk68k to support upcoming sun3 impl 54weasels
2026-08-18 2:22 ` [PATCH v4 1/6] target/m68k: Add dummy CAAR register 54weasels
2026-08-18 2:22 ` [PATCH v4 2/6] target/m68k: Fix fsave/frestore for 68881 FPU 54weasels
@ 2026-08-18 2:22 ` 54weasels
2026-08-18 10:56 ` BALATON Zoltan
2026-08-18 2:22 ` [PATCH v4 4/6] target/m68k: Extract Function Codes during TLB fills 54weasels
` (2 subsequent siblings)
5 siblings, 1 reply; 12+ messages in thread
From: 54weasels @ 2026-08-18 2:22 UTC (permalink / raw)
To: qemu-devel; +Cc: laurent, 54weasels
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/20260503015756.99176-1-54weasels@gmail.com/) 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;
/* 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;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v4 4/6] target/m68k: Extract Function Codes during TLB fills
2026-08-18 2:22 [PATCH v4 0/6] Implement missing functionality and hooks in mk68k to support upcoming sun3 impl 54weasels
` (2 preceding siblings ...)
2026-08-18 2:22 ` [PATCH v4 3/6] target/m68k: Fix NMI pending for Level 7 interrupts 54weasels
@ 2026-08-18 2:22 ` 54weasels
2026-08-18 2:22 ` [PATCH v4 5/6] target/m68k: Implement custom MMU intercept hook 54weasels
2026-08-18 2:22 ` [PATCH v4 6/6] target/m68k: Implement Physical Bus Error and Stack 54weasels
5 siblings, 0 replies; 12+ messages in thread
From: 54weasels @ 2026-08-18 2:22 UTC (permalink / raw)
To: qemu-devel; +Cc: laurent, 54weasels
High level description:
The Motorola 68020 architecture utilizes 3-bit Function Codes (FC0-FC2) to distinguish between User/Supervisor Data and Program memory spaces during MMU translation. Previously, QEMU's TLB fills did not explicitly extract or pass these codes down to the translation hooks. This patch extracts the explicit Function Codes (either natively or via the `moves` instruction) and packs them into the `access_type` field. This is required for the upcoming Sun-3 emulation, whose external MMU hardware maps entirely different physical address spaces depending on the Function Code asserted on the bus.
Impact on existing functionality:
This change introduces no functional impact to existing ColdFire or Mac/Quadra m68k emulation targets. The extraction logic gracefully defaults to standard mapping modes for platforms that do not implement a custom MMU intercept hook, preserving existing TLB behavior.
Context: This patch was originally submitted as part of the monolithic Sun-3 Machine Emulation series (https://patchew.org/QEMU/20260503015756.99176-1-54weasels@gmail.com/) and has been split into atomic components.
---
target/m68k/cpu.c | 4 ++--
target/m68k/cpu.h | 10 ++++++----
target/m68k/helper.c | 26 ++++++++++++++++++++++++++
target/m68k/translate.c | 10 ++++++----
4 files changed, 40 insertions(+), 10 deletions(-)
diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c
index ce2707dee5..f97f20e999 100644
--- a/target/m68k/cpu.c
+++ b/target/m68k/cpu.c
@@ -52,8 +52,8 @@ static TCGTBCPUState m68k_get_tb_cpu_state(CPUState *cs)
flags = (env->macsr >> 4) & TB_FLAGS_MACSR;
if (env->sr & SR_S) {
flags |= TB_FLAGS_MSR_S;
- flags |= (env->sfc << (TB_FLAGS_SFC_S_BIT - 2)) & TB_FLAGS_SFC_S;
- flags |= (env->dfc << (TB_FLAGS_DFC_S_BIT - 2)) & TB_FLAGS_DFC_S;
+ flags |= (env->sfc << TB_FLAGS_SFC_S_BIT) & TB_FLAGS_SFC_S;
+ flags |= (env->dfc << TB_FLAGS_DFC_S_BIT) & TB_FLAGS_DFC_S;
}
if (M68K_SR_TRACE(env->sr) == M68K_SR_TRACE_ANY_INS) {
flags |= TB_FLAGS_TRACE;
diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
index 058777b891..42d34502fc 100644
--- a/target/m68k/cpu.h
+++ b/target/m68k/cpu.h
@@ -601,12 +601,14 @@ void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
#define TB_FLAGS_MSR_S_BIT 13
#define TB_FLAGS_MSR_S (1 << TB_FLAGS_MSR_S_BIT)
#define TB_FLAGS_SFC_S_BIT 14
-#define TB_FLAGS_SFC_S (1 << TB_FLAGS_SFC_S_BIT)
-#define TB_FLAGS_DFC_S_BIT 15
-#define TB_FLAGS_DFC_S (1 << TB_FLAGS_DFC_S_BIT)
-#define TB_FLAGS_TRACE 16
+#define TB_FLAGS_SFC_S (7 << TB_FLAGS_SFC_S_BIT) /* 3 Bits reserved */
+#define TB_FLAGS_DFC_S_BIT 17
+#define TB_FLAGS_DFC_S (7 << TB_FLAGS_DFC_S_BIT) /* 3 Bits reserved */
+#define TB_FLAGS_TRACE 20
#define TB_FLAGS_TRACE_BIT (1 << TB_FLAGS_TRACE)
+#define MMU_MOVES_FC_BASE 2 /* mmu_idx 2-9 correspond to FC 0-7 */
+
void dump_mmu(CPUM68KState *env);
#endif
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index 93739ccda7..f3ee95441a 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -975,6 +975,32 @@ bool m68k_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
int ret;
target_ulong page_size;
+ if (qemu_access_type == MMU_INST_FETCH) {
+ access_type = ACCESS_CODE;
+ } else {
+ access_type = ACCESS_DATA;
+ if (qemu_access_type == MMU_DATA_STORE) {
+ access_type |= ACCESS_STORE;
+ }
+ }
+
+ /* Decode explicit Function Codes from moves instructions */
+ if (mmu_idx >= MMU_MOVES_FC_BASE) {
+ uint8_t fc = mmu_idx - MMU_MOVES_FC_BASE;
+ access_type |= (fc << 8); /* Pack explicit FC into access type */
+ if (fc != 1 && fc != 2) {
+ access_type |= ACCESS_SUPER;
+ }
+ } else {
+ /* Standard memory accesses map logically to normal M68K FCs */
+ if (mmu_idx == MMU_KERNEL_IDX) {
+ access_type |= ACCESS_SUPER;
+ access_type |= ((qemu_access_type == MMU_INST_FETCH ? 6 : 5) << 8);
+ } else {
+ access_type |= ((qemu_access_type == MMU_INST_FETCH ? 2 : 1) << 8);
+ }
+ }
+
if ((env->mmu.tcr & M68K_TCR_ENABLED) == 0) {
/* MMU disabled */
tlb_set_page(cs, address & TARGET_PAGE_MASK,
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index bdf619883c..6aa4c5fafd 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -163,10 +163,12 @@ static void do_writebacks(DisasContext *s)
#define IS_USER(s) 1
#else
#define IS_USER(s) (!(s->base.tb->flags & TB_FLAGS_MSR_S))
-#define SFC_INDEX(s) ((s->base.tb->flags & TB_FLAGS_SFC_S) ? \
- MMU_KERNEL_IDX : MMU_USER_IDX)
-#define DFC_INDEX(s) ((s->base.tb->flags & TB_FLAGS_DFC_S) ? \
- MMU_KERNEL_IDX : MMU_USER_IDX)
+#define SFC_INDEX(s) (MMU_MOVES_FC_BASE + \
+ (((s)->base.tb->flags & TB_FLAGS_SFC_S) >> \
+ TB_FLAGS_SFC_S_BIT))
+#define DFC_INDEX(s) (MMU_MOVES_FC_BASE + \
+ (((s)->base.tb->flags & TB_FLAGS_DFC_S) >> \
+ TB_FLAGS_DFC_S_BIT))
#endif
typedef void (*disas_proc)(CPUM68KState *env, DisasContext *s, uint16_t insn);
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v4 5/6] target/m68k: Implement custom MMU intercept hook
2026-08-18 2:22 [PATCH v4 0/6] Implement missing functionality and hooks in mk68k to support upcoming sun3 impl 54weasels
` (3 preceding siblings ...)
2026-08-18 2:22 ` [PATCH v4 4/6] target/m68k: Extract Function Codes during TLB fills 54weasels
@ 2026-08-18 2:22 ` 54weasels
2026-08-18 2:22 ` [PATCH v4 6/6] target/m68k: Implement Physical Bus Error and Stack 54weasels
5 siblings, 0 replies; 12+ messages in thread
From: 54weasels @ 2026-08-18 2:22 UTC (permalink / raw)
To: qemu-devel; +Cc: laurent, 54weasels
High level description:
Boards like the Sun-3 use an external custom MMU logic array rather than the standard 68851/68030 embedded MMU. This patch introduces a `custom_mmu_get_physical_address` opaque hook, allowing board initialization code to intercept TLB fills and delegate translation directly to the machine's external MMU implementation.
Impact on existing functionality:
Zero impact on existing boards. If the hook is not explicitly registered by the board initialization code, the standard 68k MMU translation pipeline executes normally.
Context: This patch was originally submitted as part of the monolithic Sun-3 Machine Emulation series (https://patchew.org/QEMU/20260503015756.99176-1-54weasels@gmail.com/) and has been split into atomic components.
---
target/m68k/cpu.h | 7 ++++
target/m68k/helper.c | 89 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 96 insertions(+)
diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
index 42d34502fc..d4ecb1c3ef 100644
--- a/target/m68k/cpu.h
+++ b/target/m68k/cpu.h
@@ -153,6 +153,13 @@ typedef struct CPUArchState {
/* Fields up to this point are cleared by a CPU reset */
struct {} end_reset_fields;
+ /* Custom MMU intercept logic, if any (e.g. for Sun-3) */
+ void *custom_mmu_opaque;
+ int (*custom_mmu_get_physical_address)(void *env, hwaddr *physical,
+ int *prot, vaddr address,
+ int access_type,
+ hwaddr *page_size);
+
/* Fields from here on are preserved across CPU reset. */
uint64_t features;
} CPUM68KState;
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index f3ee95441a..1eef6dcaaf 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -920,6 +920,21 @@ hwaddr m68k_cpu_get_phys_addr_debug(CPUState *cs, vaddr addr)
int access_type;
target_ulong page_size;
+ access_type = ACCESS_DATA | ACCESS_DEBUG;
+ if (env->sr & SR_S) {
+ access_type |= ACCESS_SUPER;
+ }
+
+ if (env->custom_mmu_get_physical_address) {
+ hwaddr custom_page_size;
+ if (env->custom_mmu_get_physical_address(env, &phys_addr, &prot, addr,
+ access_type,
+ &custom_page_size) == 0) {
+ return phys_addr;
+ }
+ return -1;
+ }
+
if ((env->mmu.tcr & M68K_TCR_ENABLED) == 0) {
/* MMU disabled */
return addr;
@@ -1001,6 +1016,80 @@ bool m68k_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
}
}
+ if (env->custom_mmu_get_physical_address) {
+ hwaddr custom_page_size;
+
+ /* Delegate translation to external board-specific MMU if registered */
+ ret = env->custom_mmu_get_physical_address(env, &physical, &prot,
+ address, access_type,
+ &custom_page_size);
+
+ if (likely(ret == 0)) {
+ tlb_set_page(cs, address & TARGET_PAGE_MASK,
+ physical & TARGET_PAGE_MASK, prot,
+ mmu_idx, custom_page_size);
+ return true;
+ }
+
+ if (probe) {
+ return false;
+ }
+
+ /* page fault */
+ cs->exception_index = EXCP_ACCESS;
+ env->mmu.ar = address;
+
+ if (m68k_feature(env, M68K_FEATURE_M68040)) {
+ env->mmu.ssw = M68K_ATC_040;
+ switch (size) {
+ case 1:
+ env->mmu.ssw |= M68K_BA_SIZE_BYTE;
+ break;
+ case 2:
+ env->mmu.ssw |= M68K_BA_SIZE_WORD;
+ break;
+ case 4:
+ env->mmu.ssw |= M68K_BA_SIZE_LONG;
+ break;
+ }
+ env->mmu.ssw |= M68K_TM_040_DATA;
+ } else {
+ /* M68020/030 Special Status Word (SSW) */
+ uint16_t ssw = 0x0100; /* DF - Data Fault */
+ switch (size) {
+ case 1:
+ ssw |= 0x0010;
+ break;
+ case 2:
+ ssw |= 0x0020;
+ break;
+ case 3:
+ ssw |= 0x0030;
+ break;
+ case 4:
+ ssw |= 0x0000;
+ break;
+ }
+ if (qemu_access_type != MMU_DATA_STORE) {
+ ssw |= 0x0040; /* RW - Read */
+ }
+ /* Function Code */
+ uint8_t fc;
+ if (mmu_idx >= MMU_MOVES_FC_BASE) {
+ fc = mmu_idx - MMU_MOVES_FC_BASE;
+ } else {
+ if (mmu_idx == MMU_KERNEL_IDX) {
+ fc = (qemu_access_type == MMU_INST_FETCH) ? 6 : 5;
+ } else {
+ fc = (qemu_access_type == MMU_INST_FETCH) ? 2 : 1;
+ }
+ }
+ ssw |= (fc & 7);
+ env->mmu.ssw = ssw;
+ }
+ cpu_loop_exit_restore(cs, retaddr);
+ }
+
if ((env->mmu.tcr & M68K_TCR_ENABLED) == 0) {
/* MMU disabled */
tlb_set_page(cs, address & TARGET_PAGE_MASK,
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v4 6/6] target/m68k: Implement Physical Bus Error and Stack
2026-08-18 2:22 [PATCH v4 0/6] Implement missing functionality and hooks in mk68k to support upcoming sun3 impl 54weasels
` (4 preceding siblings ...)
2026-08-18 2:22 ` [PATCH v4 5/6] target/m68k: Implement custom MMU intercept hook 54weasels
@ 2026-08-18 2:22 ` 54weasels
5 siblings, 0 replies; 12+ messages in thread
From: 54weasels @ 2026-08-18 2:22 UTC (permalink / raw)
To: qemu-devel; +Cc: laurent, 54weasels
High level description:
Physical memory timeouts (e.g., probing unpopulated memory locations) must generate a Bus Error exception, resulting in Format 0xA or 0xB stack frames on the 68020. The Sun-3 PROM probes memory sizes by intentionally causing bus errors and analyzing the Special Status Word (SSW) on the stack frame. This patch implements hardware-injected EXCP_ACCESS cycle generation and the corresponding 84-byte long stack frame teardown.
Impact on existing functionality:
Accurately populates the 68020 84-byte SSW stack frames. Older architectures (like 68000) safely ignore the hook and gracefully exit without generating the advanced frames.
Context: This patch was originally submitted as part of the monolithic Sun-3 Machine Emulation series (https://patchew.org/QEMU/20260503015756.99176-1-54weasels@gmail.com/) and has been split into atomic components.
---
target/m68k/helper.c | 1 +
target/m68k/op_helper.c | 161 +++++++++++++++++++++++++++-------------
2 files changed, 109 insertions(+), 53 deletions(-)
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index 1eef6dcaaf..abfbcead69 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -29,6 +29,7 @@
#include "system/memory.h"
#include "gdbstub/helpers.h"
#include "fpu/softfloat.h"
+#include "qemu/log.h"
#include "qemu/qemu-print.h"
#define SIGNBIT (1u << 31)
diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c
index 30af4a2631..1d4b244416 100644
--- a/target/m68k/op_helper.c
+++ b/target/m68k/op_helper.c
@@ -26,6 +26,7 @@
#include "qemu/plugin.h"
#if !defined(CONFIG_USER_ONLY)
+#include "system/runstate.h"
static void cf_rte(CPUM68KState *env)
{
@@ -74,6 +75,12 @@ throwaway:
case 7:
sp += 52;
break;
+ case 0xa: /* Short Bus Cycle Fault (Format 0xA) */
+ sp += 32 - 8; /* 32 bytes total - 8 bytes header = 24 bytes */
+ break;
+ case 0xb: /* Long Bus Cycle Fault (Format 0xB) */
+ sp += 92 - 8; /* 92 bytes total - 8 bytes header = 84 bytes */
+ break;
}
}
env->aregs[7] = sp;
@@ -343,56 +350,80 @@ static void m68k_interrupt_all(CPUM68KState *env, int is_hw)
switch (cs->exception_index) {
case EXCP_ACCESS:
if (env->mmu.fault) {
- cpu_abort(cs, "DOUBLE MMU FAULT\n");
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "M68K: Double MMU Fault. Halting CPU and requesting reset.\n");
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ cs->halted = 1;
+ cs->exception_index = EXCP_HLT;
+ cpu_loop_exit(cs);
}
env->mmu.fault = true;
- /* push data 3 */
- sp -= 4;
- cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
- /* push data 2 */
- sp -= 4;
- cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
- /* push data 1 */
- sp -= 4;
- cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
- /* write back 1 / push data 0 */
- sp -= 4;
- cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
- /* write back 1 address */
- sp -= 4;
- cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
- /* write back 2 data */
- sp -= 4;
- cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
- /* write back 2 address */
- sp -= 4;
- cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
- /* write back 3 data */
- sp -= 4;
- cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
- /* write back 3 address */
- sp -= 4;
- cpu_stl_be_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0);
- /* fault address */
- sp -= 4;
- cpu_stl_be_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0);
- /* write back 1 status */
- sp -= 2;
- cpu_stw_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
- /* write back 2 status */
- sp -= 2;
- cpu_stw_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
- /* write back 3 status */
- sp -= 2;
- cpu_stw_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
- /* special status word */
- sp -= 2;
- cpu_stw_be_mmuidx_ra(env, sp, env->mmu.ssw, MMU_KERNEL_IDX, 0);
- /* effective address */
- sp -= 4;
- cpu_stl_be_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0);
-
- do_stack_frame(env, &sp, 7, oldsr, 0, env->pc);
+
+ if (m68k_feature(env, M68K_FEATURE_M68040)) {
+ /* push data 3 */
+ sp -= 4;
+ cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
+ /* push data 2 */
+ sp -= 4;
+ cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
+ /* push data 1 */
+ sp -= 4;
+ cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
+ /* write back 1 / push data 0 */
+ sp -= 4;
+ cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
+ /* write back 1 address */
+ sp -= 4;
+ cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
+ /* write back 2 data */
+ sp -= 4;
+ cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
+ /* write back 2 address */
+ sp -= 4;
+ cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
+ /* write back 3 data */
+ sp -= 4;
+ cpu_stl_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
+ /* write back 3 address */
+ sp -= 4;
+ cpu_stl_be_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0);
+ /* fault address */
+ sp -= 4;
+ cpu_stl_be_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0);
+ /* write back 1 status */
+ sp -= 2;
+ cpu_stw_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
+ /* write back 2 status */
+ sp -= 2;
+ cpu_stw_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
+ /* write back 3 status */
+ sp -= 2;
+ cpu_stw_be_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0);
+ /* special status word */
+ sp -= 2;
+ cpu_stw_be_mmuidx_ra(env, sp, env->mmu.ssw, MMU_KERNEL_IDX, 0);
+ /* effective address */
+ sp -= 4;
+ cpu_stl_be_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0);
+
+ do_stack_frame(env, &sp, 7, oldsr, 0, env->pc);
+ } else {
+ /* M68020 Long Bus Cycle Fault (Format 0xB) */
+ /*
+ * 84 bytes of internal state are pushed before the generic
+ * 8-byte header
+ */
+ sp -= 84;
+ for (int i = 0; i < 84; i += 4) {
+ cpu_stl_be_mmuidx_ra(env, sp + i, 0, MMU_KERNEL_IDX, 0);
+ }
+ /* Offset 0x02 from internal frame: SSW */
+ cpu_stw_be_mmuidx_ra(env, sp + 2, env->mmu.ssw, MMU_KERNEL_IDX, 0);
+ /* Offset 0x08 from internal frame: Fault Address */
+ cpu_stl_be_mmuidx_ra(env, sp + 8, env->mmu.ar, MMU_KERNEL_IDX, 0);
+
+ do_stack_frame(env, &sp, 0xb, oldsr, 0, env->pc);
+ }
env->mmu.fault = false;
if (qemu_loglevel_mask(CPU_LOG_INT)) {
qemu_log(" "
@@ -438,7 +469,9 @@ static void m68k_interrupt_all(CPUM68KState *env, int is_hw)
env->aregs[7] = sp;
/* Jump to vector. */
+ env->mmu.fault = true;
env->pc = cpu_ldl_be_mmuidx_ra(env, env->vbr + vector, MMU_KERNEL_IDX, 0);
+ env->mmu.fault = false;
do_plugin_vcpu_interrupt_cb(cs, last_pc);
}
@@ -510,12 +543,34 @@ void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
if (access_type != MMU_DATA_STORE) {
env->mmu.ssw |= M68K_RW_040;
}
-
- env->mmu.ar = addr;
-
- cs->exception_index = EXCP_ACCESS;
- cpu_loop_exit(cs);
+ } else if (m68k_feature(env, M68K_FEATURE_M68020)) {
+ /*
+ * M68020 Long Bus Cycle Fault (Format 0xB).
+ * The Motorola 68020 hardware intrinsically generates a physical
+ * Bus Error exception whenever the system bus flags a transaction
+ * timeout or failure (e.g., attempting to read an unpopulated bus
+ * address). This natively injects the EXCP_ACCESS cycle to build
+ * the generic 84-byte exception stack frame.
+ */
+ env->mmu.ssw = 0;
+ if (access_type == MMU_INST_FETCH) {
+ env->mmu.ssw |= 0x1000;
+ } else if (access_type == MMU_DATA_STORE) {
+ env->mmu.ssw |= 0x0040;
+ } else {
+ env->mmu.ssw |= 0x0080;
+ }
+ } else {
+ /*
+ * Older architectures (e.g. 68000) do not currently support
+ * hardware-injected transaction failures in QEMU.
+ */
+ return;
}
+
+ env->mmu.ar = addr;
+ cs->exception_index = EXCP_ACCESS;
+ cpu_loop_exit(cs);
}
bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v4 2/6] target/m68k: Fix fsave/frestore for 68881 FPU
2026-08-18 2:22 ` [PATCH v4 2/6] target/m68k: Fix fsave/frestore for 68881 FPU 54weasels
@ 2026-08-18 10:52 ` BALATON Zoltan
2026-08-20 4:37 ` Purr Box
0 siblings, 1 reply; 12+ messages in thread
From: BALATON Zoltan @ 2026-08-18 10:52 UTC (permalink / raw)
To: 54weasels; +Cc: qemu-devel, laurent
On Mon, 17 Aug 2026, 54weasels wrote:
> High level description:
> The 68881/68882 FPUs require fsave/frestore instructions to read/write state frames. QEMU only fully implemented this for the 68040. The Sun-3 uses a 68881 FPU, and the boot ROM executes `fsave` to probe FPU presence. This patch adds basic 68881 FPU state frame handling (NULL frame) to accurately simulate an idle FPU state.
>
> Impact on existing functionality:
> Fixes FPU detection for 68881/68882 across all M68k boards without affecting 68040 specific state logic.
Comments such as the next Context: should go below the --- as everything
before that is part of the commit message. In QEMU a Context: tag is not
commonly used so I think you want this as a comment not part of the
commit message.
> Context: This patch was originally submitted as part of the monolithic Sun-3 Machine Emulation series (https://patchew.org/QEMU/20260503015756.99176-1-54weasels@gmail.com/) and has been split into atomic components.
> ---
> target/m68k/translate.c | 20 ++++++++++++++++----
> 1 file changed, 16 insertions(+), 4 deletions(-)
>
> diff --git a/target/m68k/translate.c b/target/m68k/translate.c
> index 138c89d3e5..bdf619883c 100644
> --- a/target/m68k/translate.c
> +++ b/target/m68k/translate.c
> @@ -5364,11 +5364,19 @@ DISAS_INSN(frestore)
> gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
> return;
> }
> - if (m68k_feature(s->env, M68K_FEATURE_M68040)) {
> + if (m68k_feature(s->env, M68K_FEATURE_M68040) ||
> + m68k_feature(s->env, M68K_FEATURE_FPU)) {
M68020+ (including M68040) has FPU set so no need to keep that test and
can be replaced with M68K_FEATURE_FPU.
> SRC_EA(env, addr, OS_LONG, 0, NULL);
> - /* FIXME: check the state frame */
> + if (m68k_feature(s->env, M68K_FEATURE_M68040)) {
> + /* FIXME: check the state frame */
> + } else {
What about M68060 or other CPUs with built-in FPU? As none of these do
anything maybe the if/else is not needed and can just keep the FIXME
comment?
> + /*
> + * 68881/68882 FRESTORE: read the state frame
> + * (NULL frame is 4 bytes)
> + */
> + }
> } else {
> - disas_undef(env, s, insn);
> + disas_undef_fpu(env, s, insn);
> }
> }
>
> @@ -5383,8 +5391,12 @@ DISAS_INSN(fsave)
> /* always write IDLE */
> TCGv idle = tcg_constant_i32(0x41000000);
> DEST_EA(env, insn, OS_LONG, idle, NULL);
> + } else if (m68k_feature(s->env, M68K_FEATURE_FPU)) {
> + /* 68881/68882 FSAVE: always write NULL frame */
> + TCGv null_frame = tcg_constant_i32(0x00000000);
> + DEST_EA(env, insn, OS_LONG, null_frame, NULL);
Same comment as above about CPUs with FPU. Maybe there should be only one
block for M68K_FEATURE_FPU replacing M68K_FEATURE_M68040 and a switch for
setting the frame value?
Regards,
BALATON Zoltan
> } else {
> - disas_undef(env, s, insn);
> + disas_undef_fpu(env, s, insn);
> }
> }
> #endif
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v4 3/6] target/m68k: Fix NMI pending for Level 7 interrupts
2026-08-18 2:22 ` [PATCH v4 3/6] target/m68k: Fix NMI pending for Level 7 interrupts 54weasels
@ 2026-08-18 10:56 ` BALATON Zoltan
2026-08-20 7:37 ` Purr Box
0 siblings, 1 reply; 12+ messages in thread
From: BALATON Zoltan @ 2026-08-18 10:56 UTC (permalink / raw)
To: 54weasels; +Cc: qemu-devel, laurent
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/20260503015756.99176-1-54weasels@gmail.com/) 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;
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v4 2/6] target/m68k: Fix fsave/frestore for 68881 FPU
2026-08-18 10:52 ` BALATON Zoltan
@ 2026-08-20 4:37 ` Purr Box
2026-08-20 9:55 ` BALATON Zoltan
0 siblings, 1 reply; 12+ messages in thread
From: Purr Box @ 2026-08-20 4:37 UTC (permalink / raw)
To: BALATON Zoltan; +Cc: qemu-devel, laurent
[-- Attachment #1: Type: text/plain, Size: 4032 bytes --]
Hi Zoltan,
Thanks for the review!
I've refactored the logic for v5. I removed the redundant M68040 checks in
frestore (keeping just the FIXME), and streamlined fsave so that it
uniformly checks for M68K_FEATURE_FPU, writing an IDLE frame (0x41000000)
for the 68040 and a NULL frame (0x00000000) for the 68881/68882.
Also, thanks for the tip on the Context: tag. I've updated my script to
inject it below the --- separator in the .patch files so it stays off the
permanent git commit log while remaining visible to reviewers.
I'll wait a bit longer to see if there are comments on the rest of the
series before spinning new patches.
Dan.
On Tue, Aug 18, 2026 at 3:52 AM BALATON Zoltan <balaton@eik.bme.hu> wrote:
> On Mon, 17 Aug 2026, 54weasels wrote:
> > High level description:
> > The 68881/68882 FPUs require fsave/frestore instructions to read/write
> state frames. QEMU only fully implemented this for the 68040. The Sun-3
> uses a 68881 FPU, and the boot ROM executes `fsave` to probe FPU presence.
> This patch adds basic 68881 FPU state frame handling (NULL frame) to
> accurately simulate an idle FPU state.
> >
> > Impact on existing functionality:
> > Fixes FPU detection for 68881/68882 across all M68k boards without
> affecting 68040 specific state logic.
>
> Comments such as the next Context: should go below the --- as everything
> before that is part of the commit message. In QEMU a Context: tag is not
> commonly used so I think you want this as a comment not part of the
> commit message.
>
> > Context: This patch was originally submitted as part of the monolithic
> Sun-3 Machine Emulation series (
> https://patchew.org/QEMU/20260503015756.99176-1-54weasels@gmail.com/) and
> has been split into atomic components.
> > ---
> > target/m68k/translate.c | 20 ++++++++++++++++----
> > 1 file changed, 16 insertions(+), 4 deletions(-)
> >
> > diff --git a/target/m68k/translate.c b/target/m68k/translate.c
> > index 138c89d3e5..bdf619883c 100644
> > --- a/target/m68k/translate.c
> > +++ b/target/m68k/translate.c
> > @@ -5364,11 +5364,19 @@ DISAS_INSN(frestore)
> > gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
> > return;
> > }
> > - if (m68k_feature(s->env, M68K_FEATURE_M68040)) {
> > + if (m68k_feature(s->env, M68K_FEATURE_M68040) ||
> > + m68k_feature(s->env, M68K_FEATURE_FPU)) {
>
> M68020+ (including M68040) has FPU set so no need to keep that test and
> can be replaced with M68K_FEATURE_FPU.
>
> > SRC_EA(env, addr, OS_LONG, 0, NULL);
> > - /* FIXME: check the state frame */
> > + if (m68k_feature(s->env, M68K_FEATURE_M68040)) {
> > + /* FIXME: check the state frame */
> > + } else {
>
> What about M68060 or other CPUs with built-in FPU? As none of these do
> anything maybe the if/else is not needed and can just keep the FIXME
> comment?
>
> > + /*
> > + * 68881/68882 FRESTORE: read the state frame
> > + * (NULL frame is 4 bytes)
> > + */
> > + }
> > } else {
> > - disas_undef(env, s, insn);
> > + disas_undef_fpu(env, s, insn);
> > }
> > }
> >
> > @@ -5383,8 +5391,12 @@ DISAS_INSN(fsave)
> > /* always write IDLE */
> > TCGv idle = tcg_constant_i32(0x41000000);
> > DEST_EA(env, insn, OS_LONG, idle, NULL);
> > + } else if (m68k_feature(s->env, M68K_FEATURE_FPU)) {
> > + /* 68881/68882 FSAVE: always write NULL frame */
> > + TCGv null_frame = tcg_constant_i32(0x00000000);
> > + DEST_EA(env, insn, OS_LONG, null_frame, NULL);
>
> Same comment as above about CPUs with FPU. Maybe there should be only one
> block for M68K_FEATURE_FPU replacing M68K_FEATURE_M68040 and a switch for
> setting the frame value?
>
> Regards,
> BALATON Zoltan
>
> > } else {
> > - disas_undef(env, s, insn);
> > + disas_undef_fpu(env, s, insn);
> > }
> > }
> > #endif
> >
>
[-- Attachment #2: Type: text/html, Size: 4920 bytes --]
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v4 3/6] target/m68k: Fix NMI pending for Level 7 interrupts
2026-08-18 10:56 ` BALATON Zoltan
@ 2026-08-20 7:37 ` Purr Box
0 siblings, 0 replies; 12+ messages in thread
From: Purr Box @ 2026-08-20 7:37 UTC (permalink / raw)
To: BALATON Zoltan; +Cc: qemu-devel, laurent
[-- Attachment #1: Type: text/plain, Size: 4566 bytes --]
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 <balaton@eik.bme.hu> 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/20260503015756.99176-1-54weasels@gmail.com/) 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;
> >
>
[-- Attachment #2: Type: text/html, Size: 5556 bytes --]
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v4 2/6] target/m68k: Fix fsave/frestore for 68881 FPU
2026-08-20 4:37 ` Purr Box
@ 2026-08-20 9:55 ` BALATON Zoltan
0 siblings, 0 replies; 12+ messages in thread
From: BALATON Zoltan @ 2026-08-20 9:55 UTC (permalink / raw)
To: Purr Box; +Cc: qemu-devel, laurent
[-- Attachment #1: Type: text/plain, Size: 4298 bytes --]
Hello,
On Wed, 19 Aug 2026, Purr Box wrote:
> Hi Zoltan,
>
> Thanks for the review!
> I've refactored the logic for v5. I removed the redundant M68040 checks in
> frestore (keeping just the FIXME), and streamlined fsave so that it
> uniformly checks for M68K_FEATURE_FPU, writing an IDLE frame (0x41000000)
> for the 68040 and a NULL frame (0x00000000) for the 68881/68882.
> Also, thanks for the tip on the Context: tag. I've updated my script to
> inject it below the --- separator in the .patch files so it stays off the
> permanent git commit log while remaining visible to reviewers.
Update your script to also run qemu/scripts/checkpatch.pl or do that
before submission. See
https://www.qemu.org/docs/master/devel/submitting-a-patch.html
That should catch things like missing Signed-off-by that I've just
noticed.
Regards,
BALATON Zoltan
> I'll wait a bit longer to see if there are comments on the rest of the
> series before spinning new patches.
>
> Dan.
>
> On Tue, Aug 18, 2026 at 3:52 AM BALATON Zoltan <balaton@eik.bme.hu> wrote:
>
>> On Mon, 17 Aug 2026, 54weasels wrote:
>>> High level description:
>>> The 68881/68882 FPUs require fsave/frestore instructions to read/write
>> state frames. QEMU only fully implemented this for the 68040. The Sun-3
>> uses a 68881 FPU, and the boot ROM executes `fsave` to probe FPU presence.
>> This patch adds basic 68881 FPU state frame handling (NULL frame) to
>> accurately simulate an idle FPU state.
>>>
>>> Impact on existing functionality:
>>> Fixes FPU detection for 68881/68882 across all M68k boards without
>> affecting 68040 specific state logic.
>>
>> Comments such as the next Context: should go below the --- as everything
>> before that is part of the commit message. In QEMU a Context: tag is not
>> commonly used so I think you want this as a comment not part of the
>> commit message.
>>
>>> Context: This patch was originally submitted as part of the monolithic
>> Sun-3 Machine Emulation series (
>> https://patchew.org/QEMU/20260503015756.99176-1-54weasels@gmail.com/) and
>> has been split into atomic components.
>>> ---
>>> target/m68k/translate.c | 20 ++++++++++++++++----
>>> 1 file changed, 16 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/target/m68k/translate.c b/target/m68k/translate.c
>>> index 138c89d3e5..bdf619883c 100644
>>> --- a/target/m68k/translate.c
>>> +++ b/target/m68k/translate.c
>>> @@ -5364,11 +5364,19 @@ DISAS_INSN(frestore)
>>> gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE);
>>> return;
>>> }
>>> - if (m68k_feature(s->env, M68K_FEATURE_M68040)) {
>>> + if (m68k_feature(s->env, M68K_FEATURE_M68040) ||
>>> + m68k_feature(s->env, M68K_FEATURE_FPU)) {
>>
>> M68020+ (including M68040) has FPU set so no need to keep that test and
>> can be replaced with M68K_FEATURE_FPU.
>>
>>> SRC_EA(env, addr, OS_LONG, 0, NULL);
>>> - /* FIXME: check the state frame */
>>> + if (m68k_feature(s->env, M68K_FEATURE_M68040)) {
>>> + /* FIXME: check the state frame */
>>> + } else {
>>
>> What about M68060 or other CPUs with built-in FPU? As none of these do
>> anything maybe the if/else is not needed and can just keep the FIXME
>> comment?
>>
>>> + /*
>>> + * 68881/68882 FRESTORE: read the state frame
>>> + * (NULL frame is 4 bytes)
>>> + */
>>> + }
>>> } else {
>>> - disas_undef(env, s, insn);
>>> + disas_undef_fpu(env, s, insn);
>>> }
>>> }
>>>
>>> @@ -5383,8 +5391,12 @@ DISAS_INSN(fsave)
>>> /* always write IDLE */
>>> TCGv idle = tcg_constant_i32(0x41000000);
>>> DEST_EA(env, insn, OS_LONG, idle, NULL);
>>> + } else if (m68k_feature(s->env, M68K_FEATURE_FPU)) {
>>> + /* 68881/68882 FSAVE: always write NULL frame */
>>> + TCGv null_frame = tcg_constant_i32(0x00000000);
>>> + DEST_EA(env, insn, OS_LONG, null_frame, NULL);
>>
>> Same comment as above about CPUs with FPU. Maybe there should be only one
>> block for M68K_FEATURE_FPU replacing M68K_FEATURE_M68040 and a switch for
>> setting the frame value?
>>
>> Regards,
>> BALATON Zoltan
>>
>>> } else {
>>> - disas_undef(env, s, insn);
>>> + disas_undef_fpu(env, s, insn);
>>> }
>>> }
>>> #endif
>>>
>>
>
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-08-20 9:56 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-18 2:22 [PATCH v4 0/6] Implement missing functionality and hooks in mk68k to support upcoming sun3 impl 54weasels
2026-08-18 2:22 ` [PATCH v4 1/6] target/m68k: Add dummy CAAR register 54weasels
2026-08-18 2:22 ` [PATCH v4 2/6] target/m68k: Fix fsave/frestore for 68881 FPU 54weasels
2026-08-18 10:52 ` BALATON Zoltan
2026-08-20 4:37 ` Purr Box
2026-08-20 9:55 ` BALATON Zoltan
2026-08-18 2:22 ` [PATCH v4 3/6] target/m68k: Fix NMI pending for Level 7 interrupts 54weasels
2026-08-18 10:56 ` BALATON Zoltan
2026-08-20 7:37 ` Purr Box
2026-08-18 2:22 ` [PATCH v4 4/6] target/m68k: Extract Function Codes during TLB fills 54weasels
2026-08-18 2:22 ` [PATCH v4 5/6] target/m68k: Implement custom MMU intercept hook 54weasels
2026-08-18 2:22 ` [PATCH v4 6/6] target/m68k: Implement Physical Bus Error and Stack 54weasels
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.