From 7b2ef307886983d0507fa9a74303781b15da2a42 Mon Sep 17 00:00:00 2001 From: Mihai Parparita Date: Fri, 3 Jul 2026 10:48:49 -0700 Subject: [PATCH] ppcexec: Respect CPU-specific POW behavior Followup to #186 - select `MSR[POW]` handling mode once during CPU initialization instead of treating every processor as `HID0`-gated. We match documented (and QEMU) behavior: - 603/7xx CPUs require `HID0` doze, nap, or sleep bits before we enter the emulator sleep loop - 604-family CPUs use just for `POW` directly - 601 does not support `POW` at all --- cpu/ppc/ppcemu.h | 9 +++++++++ cpu/ppc/ppcexec.cpp | 30 ++++++++++++++++++++++++++++++ cpu/ppc/ppcopcodes.cpp | 31 ++++++++++++++++++++----------- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/cpu/ppc/ppcemu.h b/cpu/ppc/ppcemu.h index 7256f6c7a6..297b0f75f0 100644 --- a/cpu/ppc/ppcemu.h +++ b/cpu/ppc/ppcemu.h @@ -375,6 +375,15 @@ extern bool include_601; // For non-PowerPC 601 emulation with 601 extras extern bool is_altivec; // For Altivec Emulation extern bool is_64bit; // For PowerPC G5 Emulation +enum class PPCPowMode : uint8_t { + None, + Unconditional, + HID0, +}; + +extern PPCPowMode ppc_pow_mode; +extern uint32_t ppc_pow_hid0_mask; + // Make execution deterministic (ignore external input, used a fixed date, etc.) extern bool is_deterministic; diff --git a/cpu/ppc/ppcexec.cpp b/cpu/ppc/ppcexec.cpp index 45fa5c656a..92b6e5de9e 100644 --- a/cpu/ppc/ppcexec.cpp +++ b/cpu/ppc/ppcexec.cpp @@ -58,6 +58,15 @@ MemCtrlBase* mem_ctrl_instance = 0; bool is_601 = false; bool include_601 = false; +PPCPowMode ppc_pow_mode = PPCPowMode::None; +uint32_t ppc_pow_hid0_mask = 0; + +// 603/7xx HID0 power-saving mode bits. They are selected in HID0, +// then entered by setting MSR[POW] with mtmsr. +static constexpr uint32_t HID0_DOZE = 0x00800000; +static constexpr uint32_t HID0_NAP = 0x00400000; +static constexpr uint32_t HID0_SLEEP = 0x00200000; +static constexpr uint32_t HID0_POWER_SAVE_MASK = HID0_DOZE | HID0_NAP | HID0_SLEEP; bool is_deterministic = false; @@ -917,6 +926,27 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, bool do_include_6 ppc_state.spr[SPR::PVR] = cpu_version; is_601 = (cpu_version >> 16) == 1; include_601 = !is_601 & do_include_601; + ppc_pow_mode = PPCPowMode::None; + ppc_pow_hid0_mask = 0; + + switch (cpu_version) { + case PPC_VER::MPC603: + case PPC_VER::MPC603E: + case PPC_VER::MPC603EV: + case PPC_VER::MPC750: + // 603/7xx enter power-saving modes through HID0 doze/nap/sleep + // bits selected before MSR[POW] is set. + ppc_pow_mode = PPCPowMode::HID0; + ppc_pow_hid0_mask = HID0_POWER_SAVE_MASK; + break; + case PPC_VER::MPC604: + case PPC_VER::MPC604E: + // 604-family processors use MSR[POW] directly. + ppc_pow_mode = PPCPowMode::Unconditional; + break; + default: + break; + } initialize_ppc_opcode_table(); diff --git a/cpu/ppc/ppcopcodes.cpp b/cpu/ppc/ppcopcodes.cpp index 8480c7b134..cec08290e2 100644 --- a/cpu/ppc/ppcopcodes.cpp +++ b/cpu/ppc/ppcopcodes.cpp @@ -30,13 +30,6 @@ along with this program. If not, see . #include #include -// MPC750 HID0 power-saving mode bits. They are selected in HID0, then -// entered by setting MSR[POW] with mtmsr. -static constexpr uint32_t HID0_DOZE = 0x00800000; -static constexpr uint32_t HID0_NAP = 0x00400000; -static constexpr uint32_t HID0_SLEEP = 0x00200000; -static constexpr uint32_t HID0_POWER_SAVE_MASK = HID0_DOZE | HID0_NAP | HID0_SLEEP; - //Extract the registers desired and the values of the registers. // Affects CR Field 0 - For integer operations @@ -820,10 +813,26 @@ void dppc_interpreter::ppc_mtmsr(uint32_t opcode) { dec_exception_pending = false; //LOG_F(WARNING, "MTMSR: decrementer exception triggered"); ppc_exception_handler(Except_Type::EXC_DECR, 0); - } else if ((ppc_state.msr & MSR::POW) && - (ppc_state.spr[SPR::HID0] & HID0_POWER_SAVE_MASK)) { - exec_flags |= EXEF_SLEEP; - ppc_next_instruction_address = (ppc_state.pc & 0xFFFFFFFCUL) + 4; + } else if (ppc_state.msr & MSR::POW) [[unlikely]] { + bool enter_sleep = false; + + switch (ppc_pow_mode) { + case PPCPowMode::Unconditional: + enter_sleep = true; + break; + case PPCPowMode::HID0: + enter_sleep = (ppc_state.spr[SPR::HID0] & ppc_pow_hid0_mask) != 0; + break; + case PPCPowMode::None: + break; + } + + if (enter_sleep) { + exec_flags |= EXEF_SLEEP; + ppc_next_instruction_address = (ppc_state.pc & 0xFFFFFFFCUL) + 4; + } else { + mmu_change_mode(); + } } else { mmu_change_mode(); }