From f572e29a1346caa6f06c7f01f3f9a55f11e91ffe Mon Sep 17 00:00:00 2001 From: Eladash Date: Sat, 19 Nov 2022 13:50:31 +0200 Subject: [PATCH] PPU: Add new patch function for SONIC 06 --- Utilities/bin_patch.cpp | 23 +++++++--- rpcs3/Emu/CMakeLists.txt | 1 + rpcs3/Emu/Cell/Modules/HLE_PATCHES.cpp | 58 ++++++++++++++++++++++++++ rpcs3/Emu/Cell/Modules/StaticHLE.cpp | 2 +- rpcs3/Emu/Cell/PPUFunction.h | 4 +- rpcs3/Emu/Cell/PPUModule.cpp | 19 ++++----- rpcs3/Emu/Cell/PPUModule.h | 3 +- rpcs3/Emu/Cell/PPUThread.cpp | 6 +-- rpcs3/emucore.vcxproj | 1 + rpcs3/emucore.vcxproj.filters | 2 + 10 files changed, 94 insertions(+), 25 deletions(-) create mode 100644 rpcs3/Emu/Cell/Modules/HLE_PATCHES.cpp diff --git a/Utilities/bin_patch.cpp b/Utilities/bin_patch.cpp index 51daa7f6e4..b2aae1f0ad 100644 --- a/Utilities/bin_patch.cpp +++ b/Utilities/bin_patch.cpp @@ -718,7 +718,7 @@ static usz apply_modification(std::basic_string& applied, const patch_engin // Always executable u64 flags = vm::alloc_executable | vm::alloc_unwritable; - switch (p.offset % patch_engine::mem_protection::mask) + switch (p.offset & patch_engine::mem_protection::mask) { case patch_engine::mem_protection::rw: case patch_engine::mem_protection::wx: @@ -754,15 +754,26 @@ static usz apply_modification(std::basic_string& applied, const patch_engin // Register code ppu_register_range(addr, alloc_size); - // Write branch to code - ppu_form_branch_to_code(out_branch, addr); resval = out_branch & -4; + // Write branch to return to code + if (!ppu_form_branch_to_code(addr + static_cast(p.value.long_value) * 4, resval + 4)) + { + patch_log.error("Failed to write return jump at 0x%x", addr + static_cast(p.value.long_value) * 4); + ensure(alloc_map->dealloc(addr)); + continue; + } + + // Write branch to code + if (!ppu_form_branch_to_code(out_branch, addr)) + { + patch_log.error("Failed to jump to code cave at 0x%x", out_branch); + ensure(alloc_map->dealloc(addr)); + continue; + } + // Write address of the allocated memory to the code entry *vm::get_super_ptr(resval) = addr; - - // Write branch to return to code - ppu_form_branch_to_code(addr + static_cast(p.value.long_value) * 4, resval + 4); relocate_instructions_at = addr; break; } diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 367ca1a18c..0669bed610 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -322,6 +322,7 @@ target_sources(rpcs3_emu PRIVATE Cell/Modules/cellVoice.cpp Cell/Modules/cellVpost.cpp Cell/Modules/cellWebBrowser.cpp + Cell/Modules/HLE_PATCHES.cpp Cell/Modules/libad_async.cpp Cell/Modules/libad_core.cpp Cell/Modules/libmedi.cpp diff --git a/rpcs3/Emu/Cell/Modules/HLE_PATCHES.cpp b/rpcs3/Emu/Cell/Modules/HLE_PATCHES.cpp new file mode 100644 index 0000000000..c34c9b1c3c --- /dev/null +++ b/rpcs3/Emu/Cell/Modules/HLE_PATCHES.cpp @@ -0,0 +1,58 @@ +#include "stdafx.h" +#include "Emu/IdManager.h" +#include "Emu/Cell/PPUModule.h" +#include "Utilities/Thread.h" + +#include "Emu/Cell/lv2/sys_spu.h" +#include "Emu/Cell/lv2/sys_sync.h" + +#include + +// SONIC THE HEDGEDOG: a fix for a race condition between SPUs and PPUs causing missing graphics (SNR is overriden when non-empty) +void WaitForSPUsToEmptySNRs(ppu_thread& ppu, u32 spu_id, u32 snr_mask) +{ + ppu.state += cpu_flag::wait; + + auto [spu, group] = lv2_spu_group::get_thread(spu_id); + + if ((!spu && spu_id != umax) || snr_mask % 4 == 0) + { + return; + } + + // Wait until specified SNRs are reported empty at least once + for (bool has_busy = true; has_busy && !ppu.is_stopped(); std::this_thread::yield()) + { + has_busy = false; + + auto for_one = [&](u32, spu_thread& spu) + { + if ((snr_mask & 1) && spu.ch_snr1.get_count()) + { + has_busy = true; + return; + } + + if ((snr_mask & 2) && spu.ch_snr2.get_count()) + { + has_busy = true; + } + }; + + if (spu) + { + // Wait for a single SPU + for_one(spu->id, *spu); + } + else + { + // Wait for all SPUs + idm::select>(for_one); + } + } +} + +DECLARE(ppu_module_manager::hle_patches)("RPCS3_HLE_LIBRARY", []() +{ + REG_FUNC(RPCS3_HLE_LIBRARY, WaitForSPUsToEmptySNRs); +}); diff --git a/rpcs3/Emu/Cell/Modules/StaticHLE.cpp b/rpcs3/Emu/Cell/Modules/StaticHLE.cpp index 4a8ae582a3..bce536d22a 100644 --- a/rpcs3/Emu/Cell/Modules/StaticHLE.cpp +++ b/rpcs3/Emu/Cell/Modules/StaticHLE.cpp @@ -163,7 +163,7 @@ bool statichle_handler::check_against_patterns(vm::cptr& data, u32 size, u32 } const auto sfunc = &::at32(smodule->functions, pat.fnid); - const u32 target = g_fxo->get().func_addr(sfunc->index) + 4; + const u32 target = g_fxo->get().func_addr(sfunc->index, true); // write stub vm::write32(addr, ppu_instructions::LIS(0, (target&0xFFFF0000)>>16)); diff --git a/rpcs3/Emu/Cell/PPUFunction.h b/rpcs3/Emu/Cell/PPUFunction.h index 6f8829b353..f3232e9d35 100644 --- a/rpcs3/Emu/Cell/PPUFunction.h +++ b/rpcs3/Emu/Cell/PPUFunction.h @@ -290,14 +290,14 @@ public: return access(llvm); } - u32 func_addr(u32 index) const + u32 func_addr(u32 index, bool is_code_addr = false) const { if (index >= access().size() || !addr) { return 0; } - return addr + index * 8; + return addr + index * 8 + (is_code_addr ? 4 : 0); } // Allocation address diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 5b1fb69ed8..dfdadb2a41 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -153,7 +153,7 @@ struct ppu_linkage_info // Module map std::map modules{}; - std::map>, std::less<>> lib_lock; + std::map, std::less<>> lib_lock; shared_mutex mutex; }; @@ -278,6 +278,7 @@ static void ppu_initialize_modules(ppu_linkage_info* link, utils::serial* ar = n &ppu_module_manager::sys_libc, &ppu_module_manager::sys_lv2dbg, &ppu_module_manager::static_hle, + &ppu_module_manager::hle_patches, }; // Initialize double-purpose fake OPD array for HLE functions @@ -640,9 +641,9 @@ extern bool ppu_register_library_lock(std::string_view libname, bool lock_lib) reader_lock lock(link->mutex); - if (auto it = std::as_const(link->lib_lock).find(libname); it != link->lib_lock.cend() && it->second) + if (auto it = link->lib_lock.find(libname); it != link->lib_lock.cend()) { - return lock_lib ? !it->second->test_and_set() : it->second->test_and_reset(); + return lock_lib ? !it->second.test_and_set() : it->second.test_and_reset(); } if (!lock_lib) @@ -653,15 +654,9 @@ extern bool ppu_register_library_lock(std::string_view libname, bool lock_lib) lock.upgrade(); - auto& lib_lock = link->lib_lock.emplace(std::string{libname}, nullptr).first->second; + auto& lib_lock = link->lib_lock.emplace(std::string{libname}, false).first->second; - if (!lib_lock) - { - lib_lock = std::make_shared>(true); - return true; - } - - return !lib_lock->test_and_set(); + return !lib_lock.test_and_set(); } // Load and register exports; return special exports found (nameless module) @@ -750,7 +745,7 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo if (_sf && (_sf->flags & MFF_FORCED_HLE)) { // Inject a branch to the HLE implementation - const u32 target = g_fxo->get().func_addr(_sf->index) + 4; + const u32 target = g_fxo->get().func_addr(_sf->index, true); // Set exported function flink.export_addr = target - 4; diff --git a/rpcs3/Emu/Cell/PPUModule.h b/rpcs3/Emu/Cell/PPUModule.h index 1928b9ae80..efeef707b6 100644 --- a/rpcs3/Emu/Cell/PPUModule.h +++ b/rpcs3/Emu/Cell/PPUModule.h @@ -80,9 +80,9 @@ public: std::unordered_map> functions{}; std::unordered_map> variables{}; -public: ppu_static_module(const char* name); +public: ppu_static_module(const char* name, void(*init)()) : ppu_static_module(name) { @@ -278,6 +278,7 @@ public: static const ppu_static_module sys_libc; static const ppu_static_module sys_lv2dbg; static const ppu_static_module static_hle; + static const ppu_static_module hle_patches; private: inline static std::unordered_map s_module_map; diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 1efe0f148a..44ebbbd49c 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -667,7 +667,7 @@ struct ppu_far_jumps_t // NOTE: In order to clean up this information all calls must return in order auto& saved_info = calls_info.emplace_back(); saved_info.cia = pc; - saved_info.saved_lr = std::exchange(ppu->lr, FIND_FUNC(ppu_return_from_far_jump)); + saved_info.saved_lr = std::exchange(ppu->lr, g_fxo->get().func_addr(FIND_FUNC(ppu_return_from_far_jump), true)); saved_info.saved_r2 = std::exchange(ppu->gpr[2], opd.rtoc); } @@ -1243,7 +1243,7 @@ std::vector> ppu_thread::dump_callstack_list() const } // Ignore HLE stop address - return addr == g_fxo->get().func_addr(1) + 4; + return addr == g_fxo->get().func_addr(1, true); }; if (is_invalid(addr)) @@ -1921,7 +1921,7 @@ void ppu_thread::fast_call(u32 addr, u64 rtoc) interrupt_thread_executing = true; cia = addr; gpr[2] = rtoc; - lr = g_fxo->get().func_addr(1) + 4; // HLE stop address + lr = g_fxo->get().func_addr(1, true); // HLE stop address current_function = nullptr; if (std::exchange(loaded_from_savestate, false)) diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 81bf3473c3..a644dc7c82 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -66,6 +66,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 760b59afc2..daef86ecdd 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1101,6 +1101,8 @@ Emu\Io + + Emu\Cell\Modules