diff --git a/3rdparty/googletest/CMakeLists.txt b/3rdparty/googletest/CMakeLists.txt index 307ab7c456..ab638629ad 100644 --- a/3rdparty/googletest/CMakeLists.txt +++ b/3rdparty/googletest/CMakeLists.txt @@ -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) - diff --git a/pcsx2/Patch.cpp b/pcsx2/Patch.cpp index 19e2e3a930..96168bb47f 100644 --- a/pcsx2/Patch.cpp +++ b/pcsx2/Patch.cpp @@ -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 s_place_to_string = {{"0", "1", "2", "3"}}; - static constexpr std::array s_cpu_to_string = {{"EE", "IOP"}}; - static constexpr std::array s_type_to_string = { - {"byte", "short", "word", "double", "extended", "beshort", "beword", "bedouble", "bytes"}}; - template static inline std::optional 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(this), 0, sizeof(*this)); } - PatchCommand(const PatchCommand& p) = delete; - PatchCommand(PatchCommand&& p) - { - std::memcpy(static_cast(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(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(placetopatch)], - s_cpu_to_string[static_cast(cpu)], s_type_to_string[static_cast(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* patches, const std::vector& enable_list, const std::vector* enable_immediately_list); template - static void ApplyPatch(const PatchCommand* p, EEMemory& ee, IOPMemory& iop); + requires std::is_base_of_v && + std::is_base_of_v + void ApplyPatch(const PatchCommand* p, EEMemory& ee, IOPMemory& iop); static void ApplyDynaPatch(const DynamicPatch& patch, u32 address); template requires std::is_base_of_v @@ -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& 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& 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 + requires std::is_base_of_v && + std::is_base_of_v void Patch::ApplyPatch(const PatchCommand* p, EEMemory& ee, IOPMemory& iop) { u64 ledata = 0; diff --git a/pcsx2/Patch.h b/pcsx2/Patch.h index 8b8abb5490..bf12994849 100644 --- a/pcsx2/Patch.h +++ b/pcsx2/Patch.h @@ -18,11 +18,15 @@ #include "Config.h" #include "common/MemoryInterface.h" +#include "common/SmallString.h" #include #include #include +class EEMemoryInterface; +class IOPMemoryInterface; + namespace Patch { // "place" is the first number at a pnach line (patch=,...), 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 s_place_to_string = {{"0", "1", "2", "3"}}; + static constexpr std::array s_cpu_to_string = {{"EE", "IOP"}}; + static constexpr std::array 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(this), 0, sizeof(*this)); } + PatchCommand(const PatchCommand& p) = delete; + PatchCommand(PatchCommand&& p) + { + std::memcpy(static_cast(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(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(placetopatch)], + s_cpu_to_string[static_cast(cpu)], s_type_to_string[static_cast(type)], addr, data); + } + }; + static_assert(sizeof(PatchCommand) == 24, "PatchCommand has extra padding"); + + struct DynamicPatchEntry + { + u32 offset; + u32 value; + }; + + struct DynamicPatch + { + std::vector pattern; + std::vector 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 pattern; - std::vector 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& 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& patches, + patch_place_type place, + EEMemoryInterface& ee, + IOPMemoryInterface& iop); + extern void ApplyPatches( + const std::vector& patches, + patch_place_type place, + MemoryInterface& ee, + MemoryInterface& iop); // Get the total counts of the active game patches. extern u32 GetActiveGameDBPatchesCount(); diff --git a/tests/ctest/CMakeLists.txt b/tests/ctest/CMakeLists.txt index 56877e7fc8..2bbbac1c8d 100644 --- a/tests/ctest/CMakeLists.txt +++ b/tests/ctest/CMakeLists.txt @@ -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 diff --git a/tests/ctest/core/CMakeLists.txt b/tests/ctest/core/CMakeLists.txt index 5f01feaf10..1b65e11f96 100644 --- a/tests/ctest/core/CMakeLists.txt +++ b/tests/ctest/core/CMakeLists.txt @@ -1,4 +1,6 @@ add_pcsx2_test(core_test + patch_tests.cpp + MockMemoryInterface.h StubHost.cpp ) diff --git a/tests/ctest/core/MockMemoryInterface.h b/tests/ctest/core/MockMemoryInterface.h new file mode 100644 index 0000000000..885549fd43 --- /dev/null +++ b/tests/ctest/core/MockMemoryInterface.h @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#include "common/MemoryInterface.h" + +#include +#include + +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 + 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 +}; diff --git a/tests/ctest/core/patch_tests.cpp b/tests/ctest/core/patch_tests.cpp new file mode 100644 index 0000000000..36316b11bc --- /dev/null +++ b/tests/ctest/core/patch_tests.cpp @@ -0,0 +1,522 @@ +// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +#include "Patch.h" + +#include "MockMemoryInterface.h" + +#include + +// 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 ee; \ + testing::StrictMock iop; \ + { \ + testing::InSequence seq; \ + patch_test_setup_expected_calls_##name(ee, iop); \ + } \ + Patch::PatchCommand commands[]{__VA_ARGS__}; \ + std::vector 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); +}