diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index e5f6843f7..2f9d65aa8 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #include "cpu_core.h" @@ -224,6 +224,7 @@ void CPU::Reset() g_state.downcount = 0; g_state.pending_ticks = 0; g_state.gte_completion_tick = 0; + g_state.muldiv_completion_tick = 0; } bool CPU::DoState(StateWrapper& sw) @@ -231,6 +232,7 @@ bool CPU::DoState(StateWrapper& sw) sw.Do(&g_state.pending_ticks); sw.Do(&g_state.downcount); sw.DoEx(&g_state.gte_completion_tick, 78, static_cast(0)); + sw.DoEx(&g_state.muldiv_completion_tick, 80, static_cast(0)); sw.DoArray(g_state.regs.r, static_cast(Reg::count)); sw.Do(&g_state.pc); sw.Do(&g_state.npc); @@ -301,6 +303,7 @@ bool CPU::DoState(StateWrapper& sw) ((g_settings.cpu_execution_mode == CPUExecutionMode::Interpreter) ? CPUExecutionMode::CachedInterpreter : g_settings.cpu_execution_mode); g_state.gte_completion_tick = 0; + g_state.muldiv_completion_tick = 0; UpdateMemoryPointers(); UpdateDebugDispatcherFlag(); } @@ -1100,6 +1103,8 @@ restart_instruction: const u32 value = g_state.regs.hi; WriteReg(inst.r.rd, value); + StallUntilMulDivComplete(); + if constexpr (pgxp_mode >= PGXPMode::CPU) PGXP::CPU_MOVE(static_cast(inst.r.rd.GetValue()), static_cast(Reg::hi), value); } @@ -1110,6 +1115,8 @@ restart_instruction: const u32 value = ReadReg(inst.r.rs); g_state.regs.hi = value; + StallUntilMulDivComplete(); + if constexpr (pgxp_mode >= PGXPMode::CPU) PGXP::CPU_MOVE(static_cast(Reg::hi), static_cast(inst.r.rs.GetValue()), value); } @@ -1120,6 +1127,8 @@ restart_instruction: const u32 value = g_state.regs.lo; WriteReg(inst.r.rd, value); + StallUntilMulDivComplete(); + if constexpr (pgxp_mode >= PGXPMode::CPU) PGXP::CPU_MOVE(static_cast(inst.r.rd.GetValue()), static_cast(Reg::lo), value); } @@ -1130,6 +1139,8 @@ restart_instruction: const u32 value = ReadReg(inst.r.rs); g_state.regs.lo = value; + StallUntilMulDivComplete(); + if constexpr (pgxp_mode == PGXPMode::CPU) PGXP::CPU_MOVE(static_cast(Reg::lo), static_cast(inst.r.rs.GetValue()), value); } @@ -1145,6 +1156,9 @@ restart_instruction: g_state.regs.hi = Truncate32(result >> 32); g_state.regs.lo = Truncate32(result); + StallUntilMulDivComplete(); + AddMulDivTicks(GetMultTicks(static_cast(lhs))); + if constexpr (pgxp_mode >= PGXPMode::CPU) PGXP::CPU_MULT(inst, lhs, rhs); } @@ -1159,6 +1173,9 @@ restart_instruction: g_state.regs.hi = Truncate32(result >> 32); g_state.regs.lo = Truncate32(result); + StallUntilMulDivComplete(); + AddMulDivTicks(GetMultTicks(lhs)); + if constexpr (pgxp_mode >= PGXPMode::CPU) PGXP::CPU_MULTU(inst, lhs, rhs); } @@ -1187,6 +1204,9 @@ restart_instruction: g_state.regs.hi = static_cast(num % denom); } + StallUntilMulDivComplete(); + AddMulDivTicks(GetDivTicks()); + if constexpr (pgxp_mode >= PGXPMode::CPU) PGXP::CPU_DIV(inst, num, denom); } @@ -1209,6 +1229,9 @@ restart_instruction: g_state.regs.hi = num % denom; } + StallUntilMulDivComplete(); + AddMulDivTicks(GetDivTicks()); + if constexpr (pgxp_mode >= PGXPMode::CPU) PGXP::CPU_DIVU(inst, num, denom); } diff --git a/src/core/cpu_core.h b/src/core/cpu_core.h index ed98a2286..8078c6b93 100644 --- a/src/core/cpu_core.h +++ b/src/core/cpu_core.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #pragma once @@ -76,6 +76,7 @@ struct ALIGN_TO_CACHE_LINE State u32 downcount = 0; u32 pending_ticks = 0; u32 gte_completion_tick = 0; + u32 muldiv_completion_tick = 0; Registers regs = {}; Cop0Registers cop0_regs = {}; @@ -155,6 +156,8 @@ ALWAYS_INLINE static void ResetPendingTicks() { g_state.gte_completion_tick = (g_state.pending_ticks < g_state.gte_completion_tick) ? (g_state.gte_completion_tick - g_state.pending_ticks) : 0; + g_state.muldiv_completion_tick = + (g_state.pending_ticks < g_state.muldiv_completion_tick) ? (g_state.muldiv_completion_tick - g_state.pending_ticks) : 0; g_state.pending_ticks = 0; } ALWAYS_INLINE static void AddPendingTicks(TickCount ticks) diff --git a/src/core/cpu_core_private.h b/src/core/cpu_core_private.h index 479c2f58f..00ab5849a 100644 --- a/src/core/cpu_core_private.h +++ b/src/core/cpu_core_private.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #pragma once @@ -132,6 +132,36 @@ ALWAYS_INLINE static void StallUntilGTEComplete() (g_state.gte_completion_tick > g_state.pending_ticks) ? g_state.gte_completion_tick : g_state.pending_ticks; } +ALWAYS_INLINE static void AddMulDivTicks(TickCount ticks) +{ + g_state.muldiv_completion_tick = g_state.pending_ticks + ticks; +} + +ALWAYS_INLINE static void StallUntilMulDivComplete() +{ + g_state.pending_ticks = + (g_state.muldiv_completion_tick > g_state.pending_ticks) ? g_state.muldiv_completion_tick : g_state.pending_ticks; +} + +ALWAYS_INLINE static constexpr TickCount GetMultTicks(s32 rs) +{ + // Subtract one because of the instruction cycle. + if (rs < 0) + return (rs >= -2048) ? (6 - 1) : ((rs >= -1048576) ? (9 - 1) : (13 - 1)); + else + return (rs < 0x800) ? (6 - 1) : ((rs < 0x100000) ? (9 - 1) : (13 - 1)); +} + +ALWAYS_INLINE static constexpr TickCount GetMultTicks(u32 rs) +{ + return (rs < 0x800) ? (6 - 1) : ((rs < 0x100000) ? (9 - 1) : (13 - 1)); +} + +ALWAYS_INLINE static constexpr TickCount GetDivTicks() +{ + return (36 - 1); +} + // kernel call interception void HandleA0Syscall(); void HandleB0Syscall(); diff --git a/src/core/save_state_version.h b/src/core/save_state_version.h index e21c74a15..17a2299ec 100644 --- a/src/core/save_state_version.h +++ b/src/core/save_state_version.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #pragma once @@ -6,7 +6,7 @@ #include "common/types.h" static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; -static constexpr u32 SAVE_STATE_VERSION = 79; +static constexpr u32 SAVE_STATE_VERSION = 80; static constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42; static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION);