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();
}