Patch: Add unit tests

This commit is contained in:
chaoticgd
2026-01-17 15:13:28 +00:00
committed by Ty
parent eef1f837a2
commit 2e122c3255
7 changed files with 723 additions and 131 deletions
+8 -27
View File
@@ -1,38 +1,19 @@
# We need to use a custom CMakeLists for GoogleTest, because we can't compile with exceptions on.
add_library(gtest
googletest/src/gtest.cc
googletest/src/gtest-assertion-result.cc
googletest/src/gtest-death-test.cc
googletest/src/gtest-filepath.cc
googletest/src/gtest-matchers.cc
googletest/src/gtest-port.cc
googletest/src/gtest-printers.cc
googletest/src/gtest-test-part.cc
googletest/src/gtest-typed-test.cc
googletest/src/gtest-all.cc
googlemock/src/gmock-all.cc
googlemock/src/gmock_main.cc
)
target_include_directories(gtest PUBLIC
googletest/include
googlemock/include
)
target_include_directories(gtest PRIVATE
googletest
googletest/include
)
target_include_directories(gtest INTERFACE
googletest/include
googlemock
)
target_link_libraries(gtest Threads::Threads)
set_property(TARGET gtest PROPERTY CXX_STANDARD 17)
set_property(TARGET gtest PROPERTY CXX_STANDARD_REQUIRED ON)
add_library(gtest_main
googletest/src/gtest_main.cc
)
target_include_directories(gtest_main PRIVATE
googletest
googletest/include
)
target_include_directories(gtest_main INTERFACE
googletest/include
)
target_link_libraries(gtest_main Threads::Threads)
set_property(TARGET gtest_main PROPERTY CXX_STANDARD 17)
set_property(TARGET gtest_main PROPERTY CXX_STANDARD_REQUIRED ON)
+32 -80
View File
@@ -7,7 +7,6 @@
#include "common/ByteSwap.h"
#include "common/FileSystem.h"
#include "common/Path.h"
#include "common/SmallString.h"
#include "common/StringUtil.h"
#include "common/ZipHelpers.h"
@@ -33,30 +32,6 @@
namespace Patch
{
enum patch_cpu_type : u8
{
CPU_EE,
CPU_IOP
};
enum patch_data_type : u8
{
BYTE_T,
SHORT_T,
WORD_T,
DOUBLE_T,
EXTENDED_T,
SHORT_BE_T,
WORD_BE_T,
DOUBLE_BE_T,
BYTES_T
};
static constexpr std::array<const char*, 4> s_place_to_string = {{"0", "1", "2", "3"}};
static constexpr std::array<const char*, 2> s_cpu_to_string = {{"EE", "IOP"}};
static constexpr std::array<const char*, 9> s_type_to_string = {
{"byte", "short", "word", "double", "extended", "beshort", "beword", "bedouble", "bytes"}};
template <typename EnumType, class ArrayType>
static inline std::optional<EnumType> LookupEnumName(const std::string_view val, const ArrayType& arr)
{
@@ -68,48 +43,6 @@ namespace Patch
return std::nullopt;
}
struct PatchCommand
{
patch_place_type placetopatch;
patch_cpu_type cpu;
patch_data_type type;
u32 addr;
u64 data;
u8* data_ptr;
// needed because of the pointer
PatchCommand() { std::memset(static_cast<void*>(this), 0, sizeof(*this)); }
PatchCommand(const PatchCommand& p) = delete;
PatchCommand(PatchCommand&& p)
{
std::memcpy(static_cast<void*>(this), &p, sizeof(*this));
p.data_ptr = nullptr;
}
~PatchCommand()
{
if (data_ptr)
std::free(data_ptr);
}
PatchCommand& operator=(const PatchCommand& p) = delete;
PatchCommand& operator=(PatchCommand&& p)
{
std::memcpy(static_cast<void*>(this), &p, sizeof(*this));
p.data_ptr = nullptr;
return *this;
}
bool operator==(const PatchCommand& p) const { return std::memcmp(this, &p, sizeof(*this)) == 0; }
bool operator!=(const PatchCommand& p) const { return std::memcmp(this, &p, sizeof(*this)) != 0; }
SmallString ToString() const
{
return SmallString::from_format("{},{},{},{:08x},{:x}", s_place_to_string[static_cast<u8>(placetopatch)],
s_cpu_to_string[static_cast<u8>(cpu)], s_type_to_string[static_cast<u8>(type)], addr, data);
}
};
static_assert(sizeof(PatchCommand) == 24, "IniPatch has no padding");
struct PatchGroup
{
std::string name;
@@ -157,7 +90,9 @@ namespace Patch
static u32 EnablePatches(const std::vector<PatchGroup>* patches, const std::vector<std::string>& enable_list, const std::vector<std::string>* enable_immediately_list);
template <typename EEMemory, typename IOPMemory>
static void ApplyPatch(const PatchCommand* p, EEMemory& ee, IOPMemory& iop);
requires std::is_base_of_v<MemoryInterface, EEMemory> &&
std::is_base_of_v<MemoryInterface, IOPMemory>
void ApplyPatch(const PatchCommand* p, EEMemory& ee, IOPMemory& iop);
static void ApplyDynaPatch(const DynamicPatch& patch, u32 address);
template <typename Memory>
requires std::is_base_of_v<MemoryInterface, Memory>
@@ -1149,26 +1084,41 @@ void Patch::PatchFunc::dpatch(PatchGroup* group, const std::string_view cmd, con
void Patch::ApplyBootPatches()
{
ApplyLoadedPatches(PPT_ONCE_ON_LOAD);
ApplyLoadedPatches(PPT_COMBINED_0_1);
ApplyLoadedPatches(PPT_ON_LOAD_OR_WHEN_ENABLED);
EEMemoryInterface ee;
IOPMemoryInterface iop;
ApplyPatches(s_active_patches, PPT_ONCE_ON_LOAD, ee, iop);
ApplyPatches(s_active_patches, PPT_COMBINED_0_1, ee, iop);
ApplyPatches(s_active_patches, PPT_ON_LOAD_OR_WHEN_ENABLED, ee, iop);
}
void Patch::ApplyVsyncPatches()
{
ApplyLoadedPatches(PPT_CONTINUOUSLY);
ApplyLoadedPatches(PPT_COMBINED_0_1);
}
void Patch::ApplyLoadedPatches(patch_place_type place)
{
EEMemoryInterface ee;
IOPMemoryInterface iop;
for (const PatchCommand* i : s_active_patches)
{
ApplyPatches(s_active_patches, PPT_CONTINUOUSLY, ee, iop);
ApplyPatches(s_active_patches, PPT_COMBINED_0_1, ee, iop);
}
void Patch::ApplyPatches(
const std::vector<const PatchCommand*>& patches,
patch_place_type place,
EEMemoryInterface& ee,
IOPMemoryInterface& iop)
{
for (const PatchCommand* i : patches)
if (i->placetopatch == place)
ApplyPatch(i, ee, iop);
}
void Patch::ApplyPatches(
const std::vector<const PatchCommand*>& patches,
patch_place_type place,
MemoryInterface& ee,
MemoryInterface& iop)
{
for (const PatchCommand* i : patches)
if (i->placetopatch == place)
ApplyPatch(i, ee, iop);
}
}
u32 Patch::GetActiveGameDBPatchesCount()
@@ -1703,6 +1653,8 @@ void Patch::handle_extended_t(const PatchCommand* p, Memory& memory)
}
template <typename EEMemory, typename IOPMemory>
requires std::is_base_of_v<MemoryInterface, EEMemory> &&
std::is_base_of_v<MemoryInterface, IOPMemory>
void Patch::ApplyPatch(const PatchCommand* p, EEMemory& ee, IOPMemory& iop)
{
u64 ledata = 0;
+98 -23
View File
@@ -18,11 +18,15 @@
#include "Config.h"
#include "common/MemoryInterface.h"
#include "common/SmallString.h"
#include <string>
#include <string_view>
#include <vector>
class EEMemoryInterface;
class IOPMemoryInterface;
namespace Patch
{
// "place" is the first number at a pnach line (patch=<place>,...), e.g.:
@@ -51,6 +55,84 @@ namespace Patch
PPT_END_MARKER
};
enum patch_cpu_type : u8
{
CPU_EE,
CPU_IOP
};
enum patch_data_type : u8
{
BYTE_T,
SHORT_T,
WORD_T,
DOUBLE_T,
EXTENDED_T,
SHORT_BE_T,
WORD_BE_T,
DOUBLE_BE_T,
BYTES_T
};
static constexpr std::array<const char*, 4> s_place_to_string = {{"0", "1", "2", "3"}};
static constexpr std::array<const char*, 2> s_cpu_to_string = {{"EE", "IOP"}};
static constexpr std::array<const char*, 9> s_type_to_string = {
{"byte", "short", "word", "double", "extended", "beshort", "beword", "bedouble", "bytes"}};
struct PatchCommand
{
patch_place_type placetopatch;
patch_cpu_type cpu;
patch_data_type type;
u32 addr;
u64 data;
u8* data_ptr;
// needed because of the pointer
PatchCommand() { std::memset(static_cast<void*>(this), 0, sizeof(*this)); }
PatchCommand(const PatchCommand& p) = delete;
PatchCommand(PatchCommand&& p)
{
std::memcpy(static_cast<void*>(this), &p, sizeof(*this));
p.data_ptr = nullptr;
}
~PatchCommand()
{
if (data_ptr)
std::free(data_ptr);
}
PatchCommand& operator=(const PatchCommand& p) = delete;
PatchCommand& operator=(PatchCommand&& p)
{
std::memcpy(static_cast<void*>(this), &p, sizeof(*this));
p.data_ptr = nullptr;
return *this;
}
bool operator==(const PatchCommand& p) const { return std::memcmp(this, &p, sizeof(*this)) == 0; }
bool operator!=(const PatchCommand& p) const { return std::memcmp(this, &p, sizeof(*this)) != 0; }
SmallString ToString() const
{
return SmallString::from_format("{},{},{},{:08x},{:x}", s_place_to_string[static_cast<u8>(placetopatch)],
s_cpu_to_string[static_cast<u8>(cpu)], s_type_to_string[static_cast<u8>(type)], addr, data);
}
};
static_assert(sizeof(PatchCommand) == 24, "PatchCommand has extra padding");
struct DynamicPatchEntry
{
u32 offset;
u32 value;
};
struct DynamicPatch
{
std::vector<DynamicPatchEntry> pattern;
std::vector<DynamicPatchEntry> replacement;
};
struct PatchInfo
{
std::string name;
@@ -65,18 +147,6 @@ namespace Patch
std::string_view GetNameParentPart() const;
};
struct DynamicPatchEntry
{
u32 offset;
u32 value;
};
struct DynamicPatch
{
std::vector<DynamicPatchEntry> pattern;
std::vector<DynamicPatchEntry> replacement;
};
// Config sections/keys to use to enable patches.
extern const char* PATCHES_CONFIG_SECTION;
extern const char* CHEATS_CONFIG_SECTION;
@@ -96,24 +166,29 @@ namespace Patch
extern bool ReloadPatchAffectingOptions();
extern void UnloadPatches();
// Functions for Dynamic EE patching.
/// Functions for Dynamic EE patching.
extern void LoadDynamicPatches(const std::vector<DynamicPatch>& patches);
extern void ApplyDynamicPatches(u32 pc);
// Apply all loaded patches that should be applied when the entry point is
// being recompiled.
/// Apply all loaded patches that should be applied when the entry point is
/// being recompiled.
extern void ApplyBootPatches();
// Apply all loaded patches that should be applied during vsync.
/// Apply all loaded patches that should be applied during vsync.
extern void ApplyVsyncPatches();
// Patches the emulation memory by applying all the loaded patches with a specific place value.
// Note: unless you know better, there's no need to check whether or not different patch sources
// are enabled (e.g. ws patches, auto game fixes, etc) before calling ApplyLoadedPatches,
// because on boot or on any configuration change --> all the loaded patches are invalidated,
// and then it loads only the ones which are enabled according to the current config
// (this happens at AppCoreThread::ApplySettings(...) )
extern void ApplyLoadedPatches(patch_place_type place);
/// Apply the patches from the provided list which have place values that
/// match the one specified.
extern void ApplyPatches(
const std::vector<const PatchCommand*>& patches,
patch_place_type place,
EEMemoryInterface& ee,
IOPMemoryInterface& iop);
extern void ApplyPatches(
const std::vector<const PatchCommand*>& patches,
patch_place_type place,
MemoryInterface& ee,
MemoryInterface& iop);
// Get the total counts of the active game patches.
extern u32 GetActiveGameDBPatchesCount();
+1 -1
View File
@@ -4,7 +4,7 @@ add_custom_command(TARGET unittests POST_BUILD COMMAND ${CMAKE_CTEST_COMMAND} --
macro(add_pcsx2_test target)
add_executable(${target} EXCLUDE_FROM_ALL ${ARGN})
target_link_libraries(${target} PRIVATE gtest gtest_main)
target_link_libraries(${target} PRIVATE gtest)
if(APPLE)
# For some reason this doesn't get pulled in implicitly...
target_link_libraries(${target} PRIVATE
+2
View File
@@ -1,4 +1,6 @@
add_pcsx2_test(core_test
patch_tests.cpp
MockMemoryInterface.h
StubHost.cpp
)
+60
View File
@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "common/MemoryInterface.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
class MockMemoryInterface : public MemoryInterface
{
public:
MOCK_METHOD(u8, Read8, (u32 address, bool* valid), (override));
MOCK_METHOD(u16, Read16, (u32 address, bool* valid), (override));
MOCK_METHOD(u32, Read32, (u32 address, bool* valid), (override));
MOCK_METHOD(u64, Read64, (u32 address, bool* valid), (override));
MOCK_METHOD(u128, Read128, (u32 address, bool* valid), (override));
MOCK_METHOD(bool, ReadBytes, (u32 address, void* dest, u32 size), (override));
MOCK_METHOD(bool, Write8, (u32 address, u8 value), (override));
MOCK_METHOD(bool, Write16, (u32 address, u16 value), (override));
MOCK_METHOD(bool, Write32, (u32 address, u32 value), (override));
MOCK_METHOD(bool, Write64, (u32 address, u64 value), (override));
MOCK_METHOD(bool, Write128, (u32 address, u128 value), (override));
MOCK_METHOD(bool, WriteBytes, (u32 address, void* src, u32 size), (override));
MOCK_METHOD(bool, CompareBytes, (u32 address, void* src, u32 size), (override));
struct SetValidOutParameterAction
{
template <typename... Args>
void operator()(const Args&... args) const
{
if (std::get<1>(std::tie(args...)))
*std::get<1>(std::tie(args...)) = true;
}
};
#define DEFINE_EXPECT_CALL_FUNCTIONS(size) \
void ExpectRead##size(u32 address, u##size return_value) \
{ \
auto actions = testing::DoAll(SetValidOutParameterAction{}, testing::Return(return_value)); \
EXPECT_CALL(*this, Read##size(address, testing::_)).Times(1).WillOnce(std::move(actions)); \
} \
void ExpectWrite##size(u32 address, u##size expected_value) \
{ \
EXPECT_CALL(*this, Write##size(address, expected_value)).Times(1).WillOnce(testing::Return(true)); \
} \
void ExpectIdempotentWrite##size(u32 address, u##size old_value, u##size new_value) \
{ \
ExpectRead##size(address, old_value); \
if (old_value != new_value) \
ExpectWrite##size(address, new_value); \
}
DEFINE_EXPECT_CALL_FUNCTIONS(8)
DEFINE_EXPECT_CALL_FUNCTIONS(16)
DEFINE_EXPECT_CALL_FUNCTIONS(32)
DEFINE_EXPECT_CALL_FUNCTIONS(64)
DEFINE_EXPECT_CALL_FUNCTIONS(128)
#undef DEFINE_EXPECT_CALL_FUNCTIONS
};
+522
View File
@@ -0,0 +1,522 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "Patch.h"
#include "MockMemoryInterface.h"
#include <gtest/gtest.h>
// Create a test that makes sure applying a given list of patch commands results
// in a certain sequence of memory reads/writes.
#define PATCH_TEST(name, ...) \
static void patch_test_setup_expected_calls_##name(MockMemoryInterface& ee, MockMemoryInterface& iop); \
TEST(Patch, name) \
{ \
testing::StrictMock<MockMemoryInterface> ee; \
testing::StrictMock<MockMemoryInterface> iop; \
{ \
testing::InSequence seq; \
patch_test_setup_expected_calls_##name(ee, iop); \
} \
Patch::PatchCommand commands[]{__VA_ARGS__}; \
std::vector<const Patch::PatchCommand*> pointers; \
pointers.reserve(std::size(commands)); \
for (Patch::PatchCommand& command : commands) \
pointers.push_back(&command); \
Patch::ApplyPatches(pointers, Patch::PPT_ONCE_ON_LOAD, ee, iop); \
Patch::ApplyPatches(pointers, Patch::PPT_CONTINUOUSLY, ee, iop); \
Patch::ApplyPatches(pointers, Patch::PPT_COMBINED_0_1, ee, iop); \
Patch::ApplyPatches(pointers, Patch::PPT_ON_LOAD_OR_WHEN_ENABLED, ee, iop); \
} \
static void patch_test_setup_expected_calls_##name(MockMemoryInterface& ee, MockMemoryInterface& iop)
static Patch::PatchCommand BuildPatchCommand(
Patch::patch_place_type place,
Patch::patch_cpu_type cpu,
u32 address,
Patch::patch_data_type type,
u64 data)
{
Patch::PatchCommand command;
command.placetopatch = place;
command.cpu = cpu;
command.addr = address;
command.type = type;
command.data = data;
return command;
}
// *****************************************************************************
// Writes
// *****************************************************************************
PATCH_TEST(Byte,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00100000, Patch::BYTE_T, 0x12))
{
ee.ExpectIdempotentWrite8(0x00100000, 0, 0x12);
}
PATCH_TEST(Short,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00100000, Patch::SHORT_T, 0x1234))
{
ee.ExpectIdempotentWrite16(0x00100000, 0, 0x1234);
}
PATCH_TEST(Word,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00100000, Patch::WORD_T, 0x12345678))
{
ee.ExpectIdempotentWrite32(0x00100000, 0, 0x12345678);
}
PATCH_TEST(Double,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00100000, Patch::DOUBLE_T, 0x123456789acdef12))
{
ee.ExpectIdempotentWrite64(0x00100000, 0, 0x123456789acdef12);
}
PATCH_TEST(BigEndianShort,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00100000, Patch::SHORT_BE_T, 0x1234))
{
ee.ExpectIdempotentWrite16(0x00100000, 0, 0x3412);
}
PATCH_TEST(BigEndianWord,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00100000, Patch::WORD_BE_T, 0x12345678))
{
ee.ExpectIdempotentWrite32(0x00100000, 0, 0x78563412);
}
PATCH_TEST(BigEndianDouble,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00100000, Patch::DOUBLE_BE_T, 0xabcdef0123456789))
{
ee.ExpectIdempotentWrite64(0x00100000, 0, 0x8967452301efcdab);
}
PATCH_TEST(IOPByte,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_IOP, 0x00100000, Patch::BYTE_T, 0x12))
{
iop.ExpectIdempotentWrite8(0x00100000, 0, 0x12);
}
PATCH_TEST(IOPShort,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_IOP, 0x00100000, Patch::SHORT_T, 0x1234))
{
iop.ExpectIdempotentWrite16(0x00100000, 0, 0x1234);
}
PATCH_TEST(IOPWord,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_IOP, 0x00100000, Patch::WORD_T, 0x12345678))
{
iop.ExpectIdempotentWrite32(0x00100000, 0, 0x12345678);
}
// *****************************************************************************
// Writes (Extended)
// *****************************************************************************
PATCH_TEST(Extended8BitWrite,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00100000, Patch::EXTENDED_T, 0x00000012))
{
ee.ExpectWrite8(0x00100000, 0x12);
}
PATCH_TEST(Extended16BitWrite,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x10100000, Patch::EXTENDED_T, 0x00001234))
{
ee.ExpectWrite16(0x00100000, 0x1234);
}
PATCH_TEST(Extended32BitWrite,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x20100000, Patch::EXTENDED_T, 0x12345678))
{
ee.ExpectWrite32(0x00100000, 0x12345678);
}
// *****************************************************************************
// Increments/Decrements (Extended)
// *****************************************************************************
PATCH_TEST(Extended8BitIncrement,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30000012, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30000012, Patch::EXTENDED_T, 0x00100000))
{
ee.ExpectRead8(0x00100000, 0x00);
ee.ExpectWrite8(0x00100000, 0x12);
ee.ExpectRead8(0x00100000, 0x12);
ee.ExpectWrite8(0x00100000, 0x24);
}
PATCH_TEST(Extended8BitIncrementWrapping,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30000012, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30000012, Patch::EXTENDED_T, 0x00100000))
{
ee.ExpectRead8(0x00100000, 0xee);
ee.ExpectWrite8(0x00100000, 0x00);
ee.ExpectRead8(0x00100000, 0x00);
ee.ExpectWrite8(0x00100000, 0x12);
}
PATCH_TEST(Extended8BitDecrement,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30100012, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30100012, Patch::EXTENDED_T, 0x00100000))
{
ee.ExpectRead8(0x00100000, 0x24);
ee.ExpectWrite8(0x00100000, 0x12);
ee.ExpectRead8(0x00100000, 0x12);
ee.ExpectWrite8(0x00100000, 0x00);
}
PATCH_TEST(Extended8BitDecrementWrapping,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30100012, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30100012, Patch::EXTENDED_T, 0x00100000))
{
ee.ExpectRead8(0x00100000, 0x12);
ee.ExpectWrite8(0x00100000, 0x00);
ee.ExpectRead8(0x00100000, 0x00);
ee.ExpectWrite8(0x00100000, 0xee);
}
PATCH_TEST(Extended16BitIncrement,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30201234, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30201234, Patch::EXTENDED_T, 0x00100000))
{
ee.ExpectRead16(0x00100000, 0x0000);
ee.ExpectWrite16(0x00100000, 0x1234);
ee.ExpectRead16(0x00100000, 0x1234);
ee.ExpectWrite16(0x00100000, 0x2468);
}
PATCH_TEST(Extended16BitIncrementWrapping,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30201234, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30201234, Patch::EXTENDED_T, 0x00100000))
{
ee.ExpectRead16(0x00100000, 0xedcc);
ee.ExpectWrite16(0x00100000, 0x0000);
ee.ExpectRead16(0x00100000, 0x0000);
ee.ExpectWrite16(0x00100000, 0x1234);
}
PATCH_TEST(Extended16BitDecrement,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30301234, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30301234, Patch::EXTENDED_T, 0x00100000))
{
ee.ExpectRead16(0x00100000, 0x2468);
ee.ExpectWrite16(0x00100000, 0x1234);
ee.ExpectRead16(0x00100000, 0x1234);
ee.ExpectWrite16(0x00100000, 0x0000);
}
PATCH_TEST(Extended16BitDecrementWrapping,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30301234, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30301234, Patch::EXTENDED_T, 0x00100000))
{
ee.ExpectRead16(0x00100000, 0x1234);
ee.ExpectWrite16(0x00100000, 0x0000);
ee.ExpectRead16(0x00100000, 0x0000);
ee.ExpectWrite16(0x00100000, 0xedcc);
}
PATCH_TEST(Extended32BitIncrement,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30400000, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x12345678, Patch::EXTENDED_T, 0x00000000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30400000, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x12345678, Patch::EXTENDED_T, 0x00000000))
{
ee.ExpectRead32(0x00100000, 0x00000000);
ee.ExpectWrite32(0x00100000, 0x12345678);
ee.ExpectRead32(0x00100000, 0x12345678);
ee.ExpectWrite32(0x00100000, 0x2468acf0);
}
PATCH_TEST(Extended32BitIncrementWrapping,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30400000, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x12345678, Patch::EXTENDED_T, 0x00000000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30400000, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x12345678, Patch::EXTENDED_T, 0x00000000))
{
ee.ExpectRead32(0x00100000, 0xedcba988);
ee.ExpectWrite32(0x00100000, 0x00000000);
ee.ExpectRead32(0x00100000, 0x00000000);
ee.ExpectWrite32(0x00100000, 0x12345678);
}
PATCH_TEST(Extended32BitDecrement,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30500000, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x12345678, Patch::EXTENDED_T, 0x00000000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30500000, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x12345678, Patch::EXTENDED_T, 0x00000000))
{
ee.ExpectRead32(0x00100000, 0x2468acf0);
ee.ExpectWrite32(0x00100000, 0x12345678);
ee.ExpectRead32(0x00100000, 0x12345678);
ee.ExpectWrite32(0x00100000, 0x00000000);
}
PATCH_TEST(Extended32BitDecrementWrapping,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30500000, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x12345678, Patch::EXTENDED_T, 0x00000000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x30500000, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x12345678, Patch::EXTENDED_T, 0x00000000))
{
ee.ExpectRead32(0x00100000, 0x12345678);
ee.ExpectWrite32(0x00100000, 0x00000000);
ee.ExpectRead32(0x00100000, 0x00000000);
ee.ExpectWrite32(0x00100000, 0xedcba988);
}
// *****************************************************************************
// Serial Write (Extended)
// *****************************************************************************
PATCH_TEST(ExtendedSerialWriteZero,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x40100000, Patch::EXTENDED_T, 0x00000000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00000000, Patch::EXTENDED_T, 0x00000000))
{
}
PATCH_TEST(ExtendedSerialWriteOnce,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x40100000, Patch::EXTENDED_T, 0x00010000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x12345678, Patch::EXTENDED_T, 0x11111111))
{
ee.ExpectWrite32(0x00100000, 0x12345678);
}
PATCH_TEST(ExtendedSerialWriteContiguous,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x40100000, Patch::EXTENDED_T, 0x00020001),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x12345678, Patch::EXTENDED_T, 0x11111111))
{
ee.ExpectWrite32(0x00100000, 0x12345678);
ee.ExpectWrite32(0x00100004, 0x23456789);
}
PATCH_TEST(ExtendedSerialWriteStrided,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x40100000, Patch::EXTENDED_T, 0x00020002),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x12345678, Patch::EXTENDED_T, 0x11111111))
{
ee.ExpectWrite32(0x00100000, 0x12345678);
ee.ExpectWrite32(0x00100008, 0x23456789);
}
// *****************************************************************************
// Copy bytes (Extended)
// *****************************************************************************
PATCH_TEST(ExtendedCopyBytes0,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x50100000, Patch::EXTENDED_T, 0x00000000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00200000, Patch::EXTENDED_T, 0x00000000))
{
}
PATCH_TEST(ExtendedCopyBytes2,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x50100000, Patch::EXTENDED_T, 0x00000002),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00200000, Patch::EXTENDED_T, 0x00000000))
{
ee.ExpectRead8(0x00100000, 0x12);
ee.ExpectWrite8(0x00200000, 0x12);
ee.ExpectRead8(0x00100001, 0x12);
ee.ExpectWrite8(0x00200001, 0x12);
}
// *****************************************************************************
// Pointer write (Extended)
// *****************************************************************************
PATCH_TEST(ExtendedPointerWrite8,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x60100000, Patch::EXTENDED_T, 0x00000012),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00000001, Patch::EXTENDED_T, 0x00000004))
{
ee.ExpectRead32(0x00100000, 0x00200000);
ee.ExpectWrite8(0x00200004, 0x12);
}
PATCH_TEST(ExtendedPointerWrite16,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x60100000, Patch::EXTENDED_T, 0x00001234),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00010001, Patch::EXTENDED_T, 0x00000004))
{
ee.ExpectRead32(0x00100000, 0x00200000);
ee.ExpectWrite16(0x00200004, 0x1234);
}
PATCH_TEST(ExtendedPointerWrite32,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x60100000, Patch::EXTENDED_T, 0x12345678),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00020001, Patch::EXTENDED_T, 0x00000004))
{
ee.ExpectRead32(0x00100000, 0x00200000);
ee.ExpectWrite32(0x00200004, 0x12345678);
}
PATCH_TEST(ExtendedPointerWriteMultiEven,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x60100000, Patch::EXTENDED_T, 0x12345678),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00020002, Patch::EXTENDED_T, 0x00000004),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00000008, Patch::EXTENDED_T, 0x00000000))
{
ee.ExpectRead32(0x00100000, 0x00200000);
ee.ExpectRead32(0x00200004, 0x00300000);
ee.ExpectWrite32(0x00300008, 0x12345678);
}
PATCH_TEST(ExtendedPointerWriteMultiOdd,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x60100000, Patch::EXTENDED_T, 0x12345678),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00020003, Patch::EXTENDED_T, 0x00000004),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00000008, Patch::EXTENDED_T, 0x0000000c))
{
ee.ExpectRead32(0x00100000, 0x00200000);
ee.ExpectRead32(0x00200004, 0x00300000);
ee.ExpectRead32(0x00300008, 0x00400000);
ee.ExpectWrite32(0x0040000c, 0x12345678);
}
PATCH_TEST(ExtendedPointerWriteFirstNull,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x60100000, Patch::EXTENDED_T, 0x12345678),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00020000, Patch::EXTENDED_T, 0x00000004))
{
ee.ExpectRead32(0x00100000, 0x00000000);
}
PATCH_TEST(ExtendedPointerWriteLastNull,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x60100000, Patch::EXTENDED_T, 0x12345678),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00020003, Patch::EXTENDED_T, 0x00000004),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00000008, Patch::EXTENDED_T, 0x0000000c))
{
ee.ExpectRead32(0x00100000, 0x00200000);
ee.ExpectRead32(0x00200004, 0x00300000);
ee.ExpectRead32(0x00300008, 0x00000000);
}
// *****************************************************************************
// Boolean operation (Extended)
// *****************************************************************************
PATCH_TEST(ExtendedBooleanOr8,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x70100000, Patch::EXTENDED_T, 0x00000012))
{
ee.ExpectRead8(0x00100000, 0x78);
ee.ExpectWrite8(0x00100000, 0x7a);
}
PATCH_TEST(ExtendedBooleanOr16,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x70100000, Patch::EXTENDED_T, 0x00101234))
{
ee.ExpectRead16(0x00100000, 0x89ab);
ee.ExpectWrite16(0x00100000, 0x9bbf);
}
PATCH_TEST(ExtendedBooleanAnd8,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x70100000, Patch::EXTENDED_T, 0x00200012))
{
ee.ExpectRead8(0x00100000, 0x34);
ee.ExpectWrite8(0x00100000, 0x10);
}
PATCH_TEST(ExtendedBooleanAnd16,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x70100000, Patch::EXTENDED_T, 0x00301234))
{
ee.ExpectRead16(0x00100000, 0x5678);
ee.ExpectWrite16(0x00100000, 0x1230);
}
PATCH_TEST(ExtendedBooleanXor8,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x70100000, Patch::EXTENDED_T, 0x00400012))
{
ee.ExpectRead8(0x00100000, 0x89);
ee.ExpectWrite8(0x00100000, 0x9b);
}
PATCH_TEST(ExtendedBooleanXor16,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x70100000, Patch::EXTENDED_T, 0x00501234))
{
ee.ExpectRead16(0x00100000, 0x89ab);
ee.ExpectWrite16(0x00100000, 0x9b9f);
}
// *****************************************************************************
// Do multi-lines if conditional (Extended)
// *****************************************************************************
PATCH_TEST(ExtendedConditional8BitEqualTrue,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0xd0100000, Patch::EXTENDED_T, 0x01010012),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00200000, Patch::EXTENDED_T, 0x00000012),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00300000, Patch::EXTENDED_T, 0x00000012))
{
ee.ExpectRead8(0x00100000, 0x12);
ee.ExpectWrite8(0x00200000, 0x12);
ee.ExpectWrite8(0x00300000, 0x12);
}
PATCH_TEST(ExtendedConditional8BitEqualFalse,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0xd0100000, Patch::EXTENDED_T, 0x01010012),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00200000, Patch::EXTENDED_T, 0x00000012),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00300000, Patch::EXTENDED_T, 0x00000012))
{
ee.ExpectRead8(0x00100000, 0x21);
ee.ExpectWrite8(0x00300000, 0x12);
}
PATCH_TEST(ExtendedConditionalEqualTrue,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0xd0100000, Patch::EXTENDED_T, 0x01001234),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00200000, Patch::EXTENDED_T, 0x00000012),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00300000, Patch::EXTENDED_T, 0x00000012))
{
ee.ExpectRead16(0x00100000, 0x1234);
ee.ExpectWrite8(0x00200000, 0x12);
ee.ExpectWrite8(0x00300000, 0x12);
}
PATCH_TEST(ExtendedConditionalEqualFalse,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0xd0100000, Patch::EXTENDED_T, 0x01001234),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00200000, Patch::EXTENDED_T, 0x00000012),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00300000, Patch::EXTENDED_T, 0x00000012))
{
ee.ExpectRead16(0x00100000, 0x4321);
ee.ExpectWrite8(0x00300000, 0x12);
}
PATCH_TEST(ExtendedConditionalNotEqualTrue,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0xd0100000, Patch::EXTENDED_T, 0x01101234),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00200000, Patch::EXTENDED_T, 0x00000012))
{
ee.ExpectRead16(0x00100000, 0x4321);
ee.ExpectWrite8(0x00200000, 0x12);
}
PATCH_TEST(ExtendedConditionalNotEqualFalse,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0xd0100000, Patch::EXTENDED_T, 0x01101234),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00200000, Patch::EXTENDED_T, 0x00000012))
{
ee.ExpectRead16(0x00100000, 0x1234);
}
PATCH_TEST(ExtendedConditionalLessThanTrue,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0xd0100000, Patch::EXTENDED_T, 0x01101234),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00200000, Patch::EXTENDED_T, 0x00000012))
{
ee.ExpectRead16(0x00100000, 0x4321);
ee.ExpectWrite8(0x00200000, 0x12);
}
PATCH_TEST(ExtendedConditionalLessThanFalse,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0xd0100000, Patch::EXTENDED_T, 0x01101234),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00200000, Patch::EXTENDED_T, 0x00000012))
{
ee.ExpectRead16(0x00100000, 0x1234);
}
PATCH_TEST(ExtendedConditionalECodeEqualTrue,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0xe0011234, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00200000, Patch::EXTENDED_T, 0x00000012),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00300000, Patch::EXTENDED_T, 0x00000012))
{
ee.ExpectRead16(0x00100000, 0x1234);
ee.ExpectWrite8(0x00200000, 0x12);
ee.ExpectWrite8(0x00300000, 0x12);
}
PATCH_TEST(ExtendedConditionalECodeEqualFalse,
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0xe0011234, Patch::EXTENDED_T, 0x00100000),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00200000, Patch::EXTENDED_T, 0x00000012),
BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00300000, Patch::EXTENDED_T, 0x00000012))
{
ee.ExpectRead16(0x00100000, 0x4321);
ee.ExpectWrite8(0x00300000, 0x12);
}