CPU/Interpreter: Emulate lo/hi read stall after mult/div

Still need to do the recompiler.
This commit is contained in:
Stenzek 2025-03-22 01:56:12 +10:00
parent 74064af730
commit 115ba4433c
No known key found for this signature in database
4 changed files with 61 additions and 5 deletions

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> // SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0 // SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "cpu_core.h" #include "cpu_core.h"
@ -224,6 +224,7 @@ void CPU::Reset()
g_state.downcount = 0; g_state.downcount = 0;
g_state.pending_ticks = 0; g_state.pending_ticks = 0;
g_state.gte_completion_tick = 0; g_state.gte_completion_tick = 0;
g_state.muldiv_completion_tick = 0;
} }
bool CPU::DoState(StateWrapper& sw) bool CPU::DoState(StateWrapper& sw)
@ -231,6 +232,7 @@ bool CPU::DoState(StateWrapper& sw)
sw.Do(&g_state.pending_ticks); sw.Do(&g_state.pending_ticks);
sw.Do(&g_state.downcount); sw.Do(&g_state.downcount);
sw.DoEx(&g_state.gte_completion_tick, 78, static_cast<u32>(0)); sw.DoEx(&g_state.gte_completion_tick, 78, static_cast<u32>(0));
sw.DoEx(&g_state.muldiv_completion_tick, 80, static_cast<u32>(0));
sw.DoArray(g_state.regs.r, static_cast<u32>(Reg::count)); sw.DoArray(g_state.regs.r, static_cast<u32>(Reg::count));
sw.Do(&g_state.pc); sw.Do(&g_state.pc);
sw.Do(&g_state.npc); 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 == CPUExecutionMode::Interpreter) ? CPUExecutionMode::CachedInterpreter :
g_settings.cpu_execution_mode); g_settings.cpu_execution_mode);
g_state.gte_completion_tick = 0; g_state.gte_completion_tick = 0;
g_state.muldiv_completion_tick = 0;
UpdateMemoryPointers(); UpdateMemoryPointers();
UpdateDebugDispatcherFlag(); UpdateDebugDispatcherFlag();
} }
@ -1100,6 +1103,8 @@ restart_instruction:
const u32 value = g_state.regs.hi; const u32 value = g_state.regs.hi;
WriteReg(inst.r.rd, value); WriteReg(inst.r.rd, value);
StallUntilMulDivComplete();
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_MOVE(static_cast<u32>(inst.r.rd.GetValue()), static_cast<u32>(Reg::hi), value); PGXP::CPU_MOVE(static_cast<u32>(inst.r.rd.GetValue()), static_cast<u32>(Reg::hi), value);
} }
@ -1110,6 +1115,8 @@ restart_instruction:
const u32 value = ReadReg(inst.r.rs); const u32 value = ReadReg(inst.r.rs);
g_state.regs.hi = value; g_state.regs.hi = value;
StallUntilMulDivComplete();
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_MOVE(static_cast<u32>(Reg::hi), static_cast<u32>(inst.r.rs.GetValue()), value); PGXP::CPU_MOVE(static_cast<u32>(Reg::hi), static_cast<u32>(inst.r.rs.GetValue()), value);
} }
@ -1120,6 +1127,8 @@ restart_instruction:
const u32 value = g_state.regs.lo; const u32 value = g_state.regs.lo;
WriteReg(inst.r.rd, value); WriteReg(inst.r.rd, value);
StallUntilMulDivComplete();
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_MOVE(static_cast<u32>(inst.r.rd.GetValue()), static_cast<u32>(Reg::lo), value); PGXP::CPU_MOVE(static_cast<u32>(inst.r.rd.GetValue()), static_cast<u32>(Reg::lo), value);
} }
@ -1130,6 +1139,8 @@ restart_instruction:
const u32 value = ReadReg(inst.r.rs); const u32 value = ReadReg(inst.r.rs);
g_state.regs.lo = value; g_state.regs.lo = value;
StallUntilMulDivComplete();
if constexpr (pgxp_mode == PGXPMode::CPU) if constexpr (pgxp_mode == PGXPMode::CPU)
PGXP::CPU_MOVE(static_cast<u32>(Reg::lo), static_cast<u32>(inst.r.rs.GetValue()), value); PGXP::CPU_MOVE(static_cast<u32>(Reg::lo), static_cast<u32>(inst.r.rs.GetValue()), value);
} }
@ -1145,6 +1156,9 @@ restart_instruction:
g_state.regs.hi = Truncate32(result >> 32); g_state.regs.hi = Truncate32(result >> 32);
g_state.regs.lo = Truncate32(result); g_state.regs.lo = Truncate32(result);
StallUntilMulDivComplete();
AddMulDivTicks(GetMultTicks(static_cast<s32>(lhs)));
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_MULT(inst, lhs, rhs); PGXP::CPU_MULT(inst, lhs, rhs);
} }
@ -1159,6 +1173,9 @@ restart_instruction:
g_state.regs.hi = Truncate32(result >> 32); g_state.regs.hi = Truncate32(result >> 32);
g_state.regs.lo = Truncate32(result); g_state.regs.lo = Truncate32(result);
StallUntilMulDivComplete();
AddMulDivTicks(GetMultTicks(lhs));
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_MULTU(inst, lhs, rhs); PGXP::CPU_MULTU(inst, lhs, rhs);
} }
@ -1187,6 +1204,9 @@ restart_instruction:
g_state.regs.hi = static_cast<u32>(num % denom); g_state.regs.hi = static_cast<u32>(num % denom);
} }
StallUntilMulDivComplete();
AddMulDivTicks(GetDivTicks());
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_DIV(inst, num, denom); PGXP::CPU_DIV(inst, num, denom);
} }
@ -1209,6 +1229,9 @@ restart_instruction:
g_state.regs.hi = num % denom; g_state.regs.hi = num % denom;
} }
StallUntilMulDivComplete();
AddMulDivTicks(GetDivTicks());
if constexpr (pgxp_mode >= PGXPMode::CPU) if constexpr (pgxp_mode >= PGXPMode::CPU)
PGXP::CPU_DIVU(inst, num, denom); PGXP::CPU_DIVU(inst, num, denom);
} }

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> // SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0 // SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once #pragma once
@ -76,6 +76,7 @@ struct ALIGN_TO_CACHE_LINE State
u32 downcount = 0; u32 downcount = 0;
u32 pending_ticks = 0; u32 pending_ticks = 0;
u32 gte_completion_tick = 0; u32 gte_completion_tick = 0;
u32 muldiv_completion_tick = 0;
Registers regs = {}; Registers regs = {};
Cop0Registers cop0_regs = {}; Cop0Registers cop0_regs = {};
@ -155,6 +156,8 @@ ALWAYS_INLINE static void ResetPendingTicks()
{ {
g_state.gte_completion_tick = 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.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; g_state.pending_ticks = 0;
} }
ALWAYS_INLINE static void AddPendingTicks(TickCount ticks) ALWAYS_INLINE static void AddPendingTicks(TickCount ticks)

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> // SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0 // SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once #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; (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 // kernel call interception
void HandleA0Syscall(); void HandleA0Syscall();
void HandleB0Syscall(); void HandleB0Syscall();

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> // SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0 // SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once #pragma once
@ -6,7 +6,7 @@
#include "common/types.h" #include "common/types.h"
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; 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 constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42;
static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION); static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION);