From aa7b13f35344d077b43b3be8a368ce3e998d6fb9 Mon Sep 17 00:00:00 2001 From: Martino Fontana Date: Thu, 19 Jun 2025 15:41:02 +0200 Subject: [PATCH 1/2] PPCDebugInterface: Small refactor to ApplyMemoryPatch Invalidate icache only if target address has a different value. Take separate arguements, instead of a struct, to allow easier usage elsewhere. Overload with u8, u16 and u32 values for the same reason. --- .../Core/Core/Debugger/PPCDebugInterface.cpp | 37 ++++++++++--------- Source/Core/Core/Debugger/PPCDebugInterface.h | 14 ++++++- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/Source/Core/Core/Debugger/PPCDebugInterface.cpp b/Source/Core/Core/Debugger/PPCDebugInterface.cpp index 2c840d0ae5..92a0a26ea8 100644 --- a/Source/Core/Core/Debugger/PPCDebugInterface.cpp +++ b/Source/Core/Core/Debugger/PPCDebugInterface.cpp @@ -29,55 +29,58 @@ #include "Core/PowerPC/PowerPC.h" #include "Core/System.h" -void ApplyMemoryPatch(const Core::CPUThreadGuard& guard, Common::Debug::MemoryPatch& patch, +void ApplyMemoryPatch(const Core::CPUThreadGuard& guard, std::span value, const u32 address, bool store_existing_value) { if (AchievementManager::GetInstance().IsHardcoreModeActive()) return; - if (patch.value.empty()) + if (value.empty()) return; - const u32 address = patch.address; - const std::size_t size = patch.value.size(); + const std::size_t size = value.size(); if (!PowerPC::MMU::HostIsRAMAddress(guard, address)) return; auto& power_pc = guard.GetSystem().GetPowerPC(); + + bool should_invalidate_cache = false; for (u32 offset = 0; offset < size; ++offset) { - if (store_existing_value) + u8 old_value = PowerPC::MMU::HostRead_U8(guard, address + offset); + if (old_value != value[offset]) { - const u8 value = PowerPC::MMU::HostRead_U8(guard, address + offset); - PowerPC::MMU::HostWrite_U8(guard, patch.value[offset], address + offset); - patch.value[offset] = value; - } - else - { - PowerPC::MMU::HostWrite_U8(guard, patch.value[offset], address + offset); + PowerPC::MMU::HostWrite_U8(guard, value[offset], address + offset); + should_invalidate_cache = true; + if (store_existing_value) + value[offset] = old_value; } if (((address + offset) % 4) == 3) - power_pc.ScheduleInvalidateCacheThreadSafe(Common::AlignDown(address + offset, 4)); + { + if (should_invalidate_cache) + power_pc.ScheduleInvalidateCacheThreadSafe(Common::AlignDown(address + offset, 4)); + should_invalidate_cache = false; + } } - if (((address + size) % 4) != 0) + if (should_invalidate_cache) { power_pc.ScheduleInvalidateCacheThreadSafe( - Common::AlignDown(address + static_cast(size), 4)); + Common::AlignDown(address + static_cast(size) - 1, 4)); } } void PPCPatches::ApplyExistingPatch(const Core::CPUThreadGuard& guard, std::size_t index) { auto& patch = m_patches[index]; - ApplyMemoryPatch(guard, patch, false); + ApplyMemoryPatch(guard, patch.value, patch.address, false); } void PPCPatches::Patch(const Core::CPUThreadGuard& guard, std::size_t index) { auto& patch = m_patches[index]; if (patch.type == Common::Debug::MemoryPatch::ApplyType::Once) - ApplyMemoryPatch(guard, patch); + ApplyMemoryPatch(guard, patch.value, patch.address); else PatchEngine::AddMemoryPatch(index); } diff --git a/Source/Core/Core/Debugger/PPCDebugInterface.h b/Source/Core/Core/Debugger/PPCDebugInterface.h index 797f2d5254..c68864583d 100644 --- a/Source/Core/Core/Debugger/PPCDebugInterface.h +++ b/Source/Core/Core/Debugger/PPCDebugInterface.h @@ -3,12 +3,15 @@ #pragma once +#include #include #include +#include #include #include "Common/Debug/MemoryPatches.h" #include "Common/Debug/Watches.h" +#include "Common/Swap.h" #include "Core/Debugger/DebugInterface.h" #include "Core/NetworkCaptureLogger.h" @@ -19,9 +22,18 @@ class System; } // namespace Core class PPCSymbolDB; -void ApplyMemoryPatch(const Core::CPUThreadGuard&, Common::Debug::MemoryPatch& patch, +void ApplyMemoryPatch(const Core::CPUThreadGuard& guard, std::span value, const u32 address, bool store_existing_value = true); +template +void ApplyMemoryPatch(const Core::CPUThreadGuard& guard, T value, const u32 address) +{ + Common::BigEndianValue big_endian{value}; + auto data = + std::span{reinterpret_cast(std::addressof(big_endian)), sizeof(T)}; + ApplyMemoryPatch(guard, data, address); +} + class PPCPatches final : public Common::Debug::MemoryPatches { public: From 5d71ac268f0cada886a101b74af410fd05c3eab3 Mon Sep 17 00:00:00 2001 From: Martino Fontana Date: Thu, 19 Jun 2025 15:47:09 +0200 Subject: [PATCH 2/2] ActionReplay/PatchEngine: Replace HostWrite with ApplyMemoryPatch Compared to the former, the latter invalidates the icache, which is something that is likely desired for patches (especially if they are applied while the game is running). --- Source/Core/Core/ActionReplay.cpp | 39 ++++++++++++++----------------- Source/Core/Core/PatchEngine.cpp | 6 ++--- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/Source/Core/Core/ActionReplay.cpp b/Source/Core/Core/ActionReplay.cpp index 4b309fd5a6..7642de40ac 100644 --- a/Source/Core/Core/ActionReplay.cpp +++ b/Source/Core/Core/ActionReplay.cpp @@ -42,6 +42,7 @@ #include "Core/AchievementManager.h" #include "Core/CheatCodes.h" #include "Core/Config/MainSettings.h" +#include "Core/Debugger/PPCDebugInterface.h" #include "Core/PowerPC/MMU.h" namespace ActionReplay @@ -379,7 +380,7 @@ static bool Subtype_RamWriteAndFill(const Core::CPUThreadGuard& guard, const ARA const u32 repeat = data >> 8; for (u32 i = 0; i <= repeat; ++i) { - PowerPC::MMU::HostWrite_U8(guard, data & 0xFF, new_addr + i); + ApplyMemoryPatch(guard, static_cast(data), new_addr + i); LogInfo("Wrote {:08x} to address {:08x}", data & 0xFF, new_addr + i); } LogInfo("--------"); @@ -393,7 +394,7 @@ static bool Subtype_RamWriteAndFill(const Core::CPUThreadGuard& guard, const ARA const u32 repeat = data >> 16; for (u32 i = 0; i <= repeat; ++i) { - PowerPC::MMU::HostWrite_U16(guard, data & 0xFFFF, new_addr + i * 2); + ApplyMemoryPatch(guard, static_cast(data), new_addr + i * 2); LogInfo("Wrote {:08x} to address {:08x}", data & 0xFFFF, new_addr + i * 2); } LogInfo("--------"); @@ -404,7 +405,7 @@ static bool Subtype_RamWriteAndFill(const Core::CPUThreadGuard& guard, const ARA case DATATYPE_32BIT: // Dword write LogInfo("32-bit Write"); LogInfo("--------"); - PowerPC::MMU::HostWrite_U32(guard, data, new_addr); + ApplyMemoryPatch(guard, data, new_addr); LogInfo("Wrote {:08x} to address {:08x}", data, new_addr); LogInfo("--------"); break; @@ -435,12 +436,12 @@ static bool Subtype_WriteToPointer(const Core::CPUThreadGuard& guard, const ARAd { LogInfo("Write 8-bit to pointer"); LogInfo("--------"); - const u8 thebyte = data & 0xFF; + const u8 thebyte = static_cast(data); const u32 offset = data >> 8; LogInfo("Pointer: {:08x}", ptr); LogInfo("Byte: {:08x}", thebyte); LogInfo("Offset: {:08x}", offset); - PowerPC::MMU::HostWrite_U8(guard, thebyte, ptr + offset); + ApplyMemoryPatch(guard, thebyte, ptr + offset); LogInfo("Wrote {:08x} to address {:08x}", thebyte, ptr + offset); LogInfo("--------"); break; @@ -450,12 +451,12 @@ static bool Subtype_WriteToPointer(const Core::CPUThreadGuard& guard, const ARAd { LogInfo("Write 16-bit to pointer"); LogInfo("--------"); - const u16 theshort = data & 0xFFFF; + const u16 theshort = static_cast(data); const u32 offset = (data >> 16) << 1; LogInfo("Pointer: {:08x}", ptr); LogInfo("Byte: {:08x}", theshort); LogInfo("Offset: {:08x}", offset); - PowerPC::MMU::HostWrite_U16(guard, theshort, ptr + offset); + ApplyMemoryPatch(guard, theshort, ptr + offset); LogInfo("Wrote {:08x} to address {:08x}", theshort, ptr + offset); LogInfo("--------"); break; @@ -465,7 +466,7 @@ static bool Subtype_WriteToPointer(const Core::CPUThreadGuard& guard, const ARAd case DATATYPE_32BIT: LogInfo("Write 32-bit to pointer"); LogInfo("--------"); - PowerPC::MMU::HostWrite_U32(guard, data, ptr); + ApplyMemoryPatch(guard, data, ptr); LogInfo("Wrote {:08x} to address {:08x}", data, ptr); LogInfo("--------"); break; @@ -493,7 +494,7 @@ static bool Subtype_AddCode(const Core::CPUThreadGuard& guard, const ARAddr& add case DATATYPE_8BIT: LogInfo("8-bit Add"); LogInfo("--------"); - PowerPC::MMU::HostWrite_U8(guard, PowerPC::MMU::HostRead_U8(guard, new_addr) + data, new_addr); + ApplyMemoryPatch(guard, PowerPC::MMU::HostRead_U8(guard, new_addr) + data, new_addr); LogInfo("Wrote {:02x} to address {:08x}", PowerPC::MMU::HostRead_U8(guard, new_addr), new_addr); LogInfo("--------"); break; @@ -501,8 +502,7 @@ static bool Subtype_AddCode(const Core::CPUThreadGuard& guard, const ARAddr& add case DATATYPE_16BIT: LogInfo("16-bit Add"); LogInfo("--------"); - PowerPC::MMU::HostWrite_U16(guard, PowerPC::MMU::HostRead_U16(guard, new_addr) + data, - new_addr); + ApplyMemoryPatch(guard, PowerPC::MMU::HostRead_U16(guard, new_addr) + data, new_addr); LogInfo("Wrote {:04x} to address {:08x}", PowerPC::MMU::HostRead_U16(guard, new_addr), new_addr); LogInfo("--------"); @@ -511,8 +511,7 @@ static bool Subtype_AddCode(const Core::CPUThreadGuard& guard, const ARAddr& add case DATATYPE_32BIT: LogInfo("32-bit Add"); LogInfo("--------"); - PowerPC::MMU::HostWrite_U32(guard, PowerPC::MMU::HostRead_U32(guard, new_addr) + data, - new_addr); + ApplyMemoryPatch(guard, PowerPC::MMU::HostRead_U32(guard, new_addr) + data, new_addr); LogInfo("Wrote {:08x} to address {:08x}", PowerPC::MMU::HostRead_U32(guard, new_addr), new_addr); LogInfo("--------"); @@ -528,7 +527,7 @@ static bool Subtype_AddCode(const Core::CPUThreadGuard& guard, const ARAddr& add // data contains an (unsigned?) integer value const float fread = read_float + static_cast(data); const u32 newval = std::bit_cast(fread); - PowerPC::MMU::HostWrite_U32(guard, newval, new_addr); + ApplyMemoryPatch(guard, newval, new_addr); LogInfo("Old Value {:08x}", read); LogInfo("Increment {:08x}", data); LogInfo("New value {:08x}", newval); @@ -586,7 +585,7 @@ static bool ZeroCode_FillAndSlide(const Core::CPUThreadGuard& guard, const u32 v LogInfo("--------"); for (int i = 0; i < write_num; ++i) { - PowerPC::MMU::HostWrite_U8(guard, val & 0xFF, curr_addr); + ApplyMemoryPatch(guard, static_cast(val), curr_addr); curr_addr += addr_incr; val += val_incr; LogInfo("Write {:08x} to address {:08x}", val & 0xFF, curr_addr); @@ -602,7 +601,7 @@ static bool ZeroCode_FillAndSlide(const Core::CPUThreadGuard& guard, const u32 v LogInfo("--------"); for (int i = 0; i < write_num; ++i) { - PowerPC::MMU::HostWrite_U16(guard, val & 0xFFFF, curr_addr); + ApplyMemoryPatch(guard, static_cast(val), curr_addr); LogInfo("Write {:08x} to address {:08x}", val & 0xFFFF, curr_addr); curr_addr += addr_incr * 2; val += val_incr; @@ -617,7 +616,7 @@ static bool ZeroCode_FillAndSlide(const Core::CPUThreadGuard& guard, const u32 v LogInfo("--------"); for (int i = 0; i < write_num; ++i) { - PowerPC::MMU::HostWrite_U32(guard, val, curr_addr); + ApplyMemoryPatch(guard, val, curr_addr); LogInfo("Write {:08x} to address {:08x}", val, curr_addr); curr_addr += addr_incr * 4; val += val_incr; @@ -664,8 +663,7 @@ static bool ZeroCode_MemoryCopy(const Core::CPUThreadGuard& guard, const u32 val LogInfo("Resolved Src Address to: {:08x}", ptr_src); for (int i = 0; i < num_bytes; ++i) { - PowerPC::MMU::HostWrite_U8(guard, PowerPC::MMU::HostRead_U8(guard, ptr_src + i), - ptr_dest + i); + ApplyMemoryPatch(guard, PowerPC::MMU::HostRead_U8(guard, ptr_src + i), ptr_dest + i); LogInfo("Wrote {:08x} to address {:08x}", PowerPC::MMU::HostRead_U8(guard, ptr_src + i), ptr_dest + i); } @@ -677,8 +675,7 @@ static bool ZeroCode_MemoryCopy(const Core::CPUThreadGuard& guard, const u32 val LogInfo("--------"); for (int i = 0; i < num_bytes; ++i) { - PowerPC::MMU::HostWrite_U8(guard, PowerPC::MMU::HostRead_U8(guard, addr_src + i), - addr_dest + i); + ApplyMemoryPatch(guard, PowerPC::MMU::HostRead_U8(guard, addr_src + i), addr_dest + i); LogInfo("Wrote {:08x} to address {:08x}", PowerPC::MMU::HostRead_U8(guard, addr_src + i), addr_dest + i); } diff --git a/Source/Core/Core/PatchEngine.cpp b/Source/Core/Core/PatchEngine.cpp index 65765c44e0..44e5ecedc8 100644 --- a/Source/Core/Core/PatchEngine.cpp +++ b/Source/Core/Core/PatchEngine.cpp @@ -219,19 +219,19 @@ static void ApplyPatches(const Core::CPUThreadGuard& guard, const std::vector(comparand)) { - PowerPC::MMU::HostWrite_U8(guard, static_cast(value), addr); + ApplyMemoryPatch(guard, static_cast(value), addr); } break; case PatchType::Patch16Bit: if (!entry.conditional || PowerPC::MMU::HostRead_U16(guard, addr) == static_cast(comparand)) { - PowerPC::MMU::HostWrite_U16(guard, static_cast(value), addr); + ApplyMemoryPatch(guard, static_cast(value), addr); } break; case PatchType::Patch32Bit: if (!entry.conditional || PowerPC::MMU::HostRead_U32(guard, addr) == comparand) - PowerPC::MMU::HostWrite_U32(guard, value, addr); + ApplyMemoryPatch(guard, value, addr); break; default: // unknown patchtype