diff --git a/rpcs3/Emu/Cell/Modules/cellGcmSys.cpp b/rpcs3/Emu/Cell/Modules/cellGcmSys.cpp index 01c32f38f3..1acb353418 100644 --- a/rpcs3/Emu/Cell/Modules/cellGcmSys.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGcmSys.cpp @@ -13,7 +13,6 @@ logs::channel cellGcmSys("cellGcmSys", logs::level::notice); extern s32 cellGcmCallback(ppu_thread& ppu, vm::ptr context, u32 count); -extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr); const u32 tiled_pitches[] = { 0x00000000, 0x00000200, 0x00000300, 0x00000400, @@ -379,13 +378,7 @@ s32 _cellGcmInitBody(vm::pptr context, u32 cmdSize, u32 ioSi current_context.begin.set(g_defaultCommandBufferBegin + 4096); // 4 kb reserved at the beginning current_context.end.set(g_defaultCommandBufferBegin + 32 * 1024 - 4); // 4b at the end for jump current_context.current = current_context.begin; - current_context.callback.set(gcm_info.context_addr + 0x40); - - vm::write32(gcm_info.context_addr + 0x40, gcm_info.context_addr + 0x48); - vm::write32(gcm_info.context_addr + 0x44, 0xabadcafe); - vm::write32(gcm_info.context_addr + 0x48, ppu_instructions::HACK(FIND_FUNC(cellGcmCallback))); - vm::write32(gcm_info.context_addr + 0x4c, ppu_instructions::BLR()); - ppu_register_function_at(gcm_info.context_addr + 0x48, 8, BIND_FUNC(cellGcmCallback)); + current_context.callback.set(ppu_function_manager::addr + 8 * FIND_FUNC(cellGcmCallback)); vm::_ref(gcm_info.context_addr) = current_context; context->set(gcm_info.context_addr); diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index 8956ee21e3..000cdb25d6 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -689,7 +689,6 @@ std::vector ppu_analyse(const std::vector>& se } if (ptr + 8 <= fend && - (ptr[0] == STD(r2, r1, 0x28) && (ptr[1] & 0xfc000000) == HACK(0) && ptr[2] == BLR() || (ptr[0] & 0xffff0000) == LI(r12, 0) && (ptr[1] & 0xffff0000) == ORIS(r12, r12, 0) && (ptr[2] & 0xffff0000) == LWZ(r12, r12, 0) && @@ -697,7 +696,7 @@ std::vector ppu_analyse(const std::vector>& se ptr[4] == LWZ(r0, r12, 0) && ptr[5] == LWZ(r2, r12, 4) && ptr[6] == MTCTR(r0) && - ptr[7] == BCTR())) + ptr[7] == BCTR()) { // The most used simple import stub func.size = 0x20; @@ -709,7 +708,6 @@ std::vector ppu_analyse(const std::vector>& se auto p2 = ptr + 8; while (p2 + 8 <= fend && - (p2[0] == STD(r2, r1, 0x28) && (p2[1] & 0xfc000000) == HACK(0) && p2[2] == BLR() || (p2[0] & 0xffff0000) == LI(r12, 0) && (p2[1] & 0xffff0000) == ORIS(r12, r12, 0) && (p2[2] & 0xffff0000) == LWZ(r12, r12, 0) && @@ -717,7 +715,7 @@ std::vector ppu_analyse(const std::vector>& se p2[4] == LWZ(r0, r12, 0) && p2[5] == LWZ(r2, r12, 4) && p2[6] == MTCTR(r0) && - p2[7] == BCTR())) + p2[7] == BCTR()) { auto& next = add_func(p2.addr(), 0, func.addr); next.size = 0x20; diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index c5fb3a3bee..0098559fe4 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -274,7 +274,6 @@ struct ppu_itype ADDI, ADDIS, BC, - HACK, SC, B, MCRF, @@ -691,7 +690,6 @@ struct ppu_iflag ADDI = read_ra, ADDIS = read_ra, BC = 0, - HACK = 0, SC = 0, B = 0, MCRF = 0, @@ -1093,7 +1091,6 @@ struct ppu_iname NAME(ADDI) NAME(ADDIS) NAME(BC) - NAME(HACK) NAME(SC) NAME(B) NAME(MCRF) diff --git a/rpcs3/Emu/Cell/PPUDisAsm.cpp b/rpcs3/Emu/Cell/PPUDisAsm.cpp index a52e76672e..19740123bb 100644 --- a/rpcs3/Emu/Cell/PPUDisAsm.cpp +++ b/rpcs3/Emu/Cell/PPUDisAsm.cpp @@ -892,11 +892,6 @@ void PPUDisAsm::BC(ppu_opcode_t op) Write(fmt::format("bc [%x:%x:%x:%x:%x], cr%d[%x], 0x%x, %d, %d", bo0, bo1, bo2, bo3, bo4, bi / 4, bi % 4, bd, aa, lk)); } -void PPUDisAsm::HACK(ppu_opcode_t op) -{ - Write(fmt::format("hack %d", op.opcode & 0x3ffffff)); -} - void PPUDisAsm::SC(ppu_opcode_t op) { if (op.opcode != ppu_instructions::SC(0)) diff --git a/rpcs3/Emu/Cell/PPUDisAsm.h b/rpcs3/Emu/Cell/PPUDisAsm.h index 134738d65d..3604c98130 100644 --- a/rpcs3/Emu/Cell/PPUDisAsm.h +++ b/rpcs3/Emu/Cell/PPUDisAsm.h @@ -397,7 +397,6 @@ public: void ADDI(ppu_opcode_t op); void ADDIS(ppu_opcode_t op); void BC(ppu_opcode_t op); - void HACK(ppu_opcode_t op); void SC(ppu_opcode_t op); void B(ppu_opcode_t op); void MCRF(ppu_opcode_t op); diff --git a/rpcs3/Emu/Cell/PPUFunction.cpp b/rpcs3/Emu/Cell/PPUFunction.cpp index 87cfe11ec5..a443b59349 100644 --- a/rpcs3/Emu/Cell/PPUFunction.cpp +++ b/rpcs3/Emu/Cell/PPUFunction.cpp @@ -2381,8 +2381,17 @@ std::vector& ppu_function_manager::access() { static std::vector list { - nullptr, - [](ppu_thread& ppu) { ppu.state += cpu_flag::ret; }, + [](ppu_thread& ppu) -> bool + { + LOG_ERROR(PPU, "Unregistered function called (LR=0x%x)", ppu.lr); + ppu.gpr[3] = 0; + return true; + }, + [](ppu_thread& ppu) -> bool + { + ppu.state += cpu_flag::ret; + return true; + }, }; return list; @@ -2396,3 +2405,5 @@ u32 ppu_function_manager::add_function(ppu_function_t function) return ::size32(list) - 1; } + +DECLARE(ppu_function_manager::addr); diff --git a/rpcs3/Emu/Cell/PPUFunction.h b/rpcs3/Emu/Cell/PPUFunction.h index 193c812081..80405d92b8 100644 --- a/rpcs3/Emu/Cell/PPUFunction.h +++ b/rpcs3/Emu/Cell/PPUFunction.h @@ -2,15 +2,16 @@ #include "PPUThread.h" -using ppu_function_t = void(*)(ppu_thread&); +using ppu_function_t = bool(*)(ppu_thread&); // BIND_FUNC macro "converts" any appropriate HLE function to ppu_function_t, binding it to PPU thread context. -#define BIND_FUNC(func) (static_cast([](ppu_thread& ppu) {\ +#define BIND_FUNC(func) (static_cast([](ppu_thread& ppu) -> bool {\ const auto old_f = ppu.last_function;\ ppu.last_function = #func;\ ppu_func_detail::do_call(ppu, func);\ ppu.test_state();\ ppu.last_function = old_f;\ + return true;\ })) struct ppu_va_args_t @@ -274,6 +275,9 @@ public: { return access(); } + + // Allocation address + static u32 addr; }; template diff --git a/rpcs3/Emu/Cell/PPUInterpreter.cpp b/rpcs3/Emu/Cell/PPUInterpreter.cpp index dfca7074fa..19a6115d95 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.cpp +++ b/rpcs3/Emu/Cell/PPUInterpreter.cpp @@ -1936,12 +1936,6 @@ bool ppu_interpreter::BC(ppu_thread& ppu, ppu_opcode_t op) } } -bool ppu_interpreter::HACK(ppu_thread& ppu, ppu_opcode_t op) -{ - ppu_execute_function(ppu, op.opcode & 0x3ffffff); - return true; -} - bool ppu_interpreter::SC(ppu_thread& ppu, ppu_opcode_t op) { if (op.opcode != ppu_instructions::SC(0)) diff --git a/rpcs3/Emu/Cell/PPUInterpreter.h b/rpcs3/Emu/Cell/PPUInterpreter.h index 2478f497cb..a07970e2b0 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.h +++ b/rpcs3/Emu/Cell/PPUInterpreter.h @@ -160,7 +160,6 @@ struct ppu_interpreter static bool ADDI(ppu_thread&, ppu_opcode_t); static bool ADDIS(ppu_thread&, ppu_opcode_t); static bool BC(ppu_thread&, ppu_opcode_t); - static bool HACK(ppu_thread&, ppu_opcode_t); static bool SC(ppu_thread&, ppu_opcode_t); static bool B(ppu_thread&, ppu_opcode_t); static bool MCRF(ppu_thread&, ppu_opcode_t); diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index cb9fc9db30..9065c47a4e 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -13,7 +13,8 @@ #include "Emu/Cell/lv2/sys_prx.h" -#include +#include +#include #include namespace vm { using namespace ps3; } @@ -118,6 +119,7 @@ cfg::set_entry g_cfg_load_libs(cfg::root.core, "Load libraries"); extern std::string ppu_get_function_name(const std::string& module, u32 fnid); extern std::string ppu_get_variable_name(const std::string& module, u32 vnid); extern void ppu_register_range(u32 addr, u32 size); +extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr); extern void ppu_initialize(const ppu_module& info); extern void ppu_initialize(); @@ -125,53 +127,6 @@ extern void sys_initialize_tls(ppu_thread&, u64, u32, u32, u32); extern u32 g_ps3_sdk_version; -// Function lookup table. Not supposed to grow after emulation start. -std::vector g_ppu_function_cache; - -// Function name cache in format %s.%s (module name, function name) -std::vector g_ppu_function_names; - -// Function NID cache for autopause. Autopause tool should probably be rewritten. -std::vector g_ppu_fnid_cache; - -extern std::string ppu_get_module_function_name(u32 index) -{ - if (index < g_ppu_function_names.size()) - { - return g_ppu_function_names[index]; - } - - return fmt::format(".%u", index); -} - -extern void ppu_execute_function(ppu_thread& ppu, u32 index) -{ - if (index < g_ppu_function_cache.size()) - { - // If autopause occures, check_status() will hold the thread until unpaused - if (debug::autopause::pause_function(g_ppu_fnid_cache[index])) ppu.test_state(); - - if (const auto func = g_ppu_function_cache[index]) - { - func(ppu); - LOG_TRACE(HLE, "'%s' finished, r3=0x%llx", ppu_get_module_function_name(index), ppu.gpr[3]); - return; - } - } - - fmt::throw_exception("Function not registered (index %u)" HERE, index); -} - -extern ppu_function_t ppu_get_function(u32 index) -{ - if (index < g_ppu_function_cache.size()) - { - return g_ppu_function_cache[index]; - } - - return nullptr; -} - extern u32 ppu_generate_id(const char* name) { // Symbol name suffix @@ -224,6 +179,26 @@ const ppu_static_module* ppu_module_manager::get_module(const std::string& name) return found != map.end() ? found->second : nullptr; } +// Global linkage information +struct ppu_linkage_info +{ + struct module + { + struct info + { + u32 export_addr = 0; + std::set imports; + }; + + // FNID -> (export; [imports...]) + std::map functions; + std::map variables; + }; + + // Module map + std::unordered_map modules; +}; + // Initialize static modules. static void ppu_initialize_modules() { @@ -323,13 +298,6 @@ static void ppu_initialize_modules() &ppu_module_manager::sys_lv2dbg, }; - // Reinitialize function cache - g_ppu_function_cache = ppu_function_manager::get(); - g_ppu_function_names.clear(); - g_ppu_function_names.resize(g_ppu_function_cache.size()); - g_ppu_fnid_cache.clear(); - g_ppu_fnid_cache.resize(g_ppu_function_cache.size()); - // "Use" all the modules for correct linkage for (auto& module : registered) { @@ -338,8 +306,6 @@ static void ppu_initialize_modules() for (auto& function : module->functions) { LOG_TRACE(LOADER, "** 0x%08X: %s", function.first, function.second.name); - g_ppu_function_names.at(function.second.index) = fmt::format("%s.%s", module->name, function.second.name); - g_ppu_fnid_cache.at(function.second.index) = function.first; } for (auto& variable : module->variables) @@ -348,152 +314,31 @@ static void ppu_initialize_modules() variable.second.var->set(0); } } + + // Initialize double-purpose fake OPD array for HLE functions + const auto& hle_funcs = ppu_function_manager::get(); + + // Allocate memory for the array (must be called after fixed allocations) + ppu_function_manager::addr = vm::alloc(::size32(hle_funcs) * 8, vm::main); + + // Initialize as PPU executable code + ppu_register_range(ppu_function_manager::addr, ::size32(hle_funcs) * 8); + + // Fill the array (visible data: self address and function index) + for (u32 addr = ppu_function_manager::addr, index = 0; index < hle_funcs.size(); addr += 8, index++) + { + // Function address = current address, RTOC = BLR instruction for the interpreter + vm::ps3::write32(addr + 0, addr); + vm::ps3::write32(addr + 4, ppu_instructions::BLR()); + + // Register the HLE function directly + ppu_register_function_at(addr + 0, 4, hle_funcs[index]); + } + + // Set memory protection to read-only + vm::page_protect(ppu_function_manager::addr, ::align(::size32(hle_funcs) * 8, 0x1000), 0, 0, vm::page_writable); } -// Detect import stub at specified address and inject HACK instruction with index immediate. -static bool ppu_patch_import_stub(u32 addr, u32 index) -{ - const auto data = vm::_ptr(addr); - - using namespace ppu_instructions; - - // Check various patterns: - - if (vm::check_addr(addr, 32) && - (data[0] & 0xffff0000) == LI(r12, 0) && - (data[1] & 0xffff0000) == ORIS(r12, r12, 0) && - (data[2] & 0xffff0000) == LWZ(r12, r12, 0) && - data[3] == STD(r2, r1, 0x28) && - data[4] == LWZ(r0, r12, 0) && - data[5] == LWZ(r2, r12, 4) && - data[6] == MTCTR(r0) && - data[7] == BCTR()) - { - data[0] = STD(r2, r1, 0x28); // Save RTOC - data[1] = HACK(index); - data[2] = BLR(); - std::fill(data + 3, data + 8, NOP()); - return true; - } - - if (vm::check_addr(addr, 12) && - (data[0] & 0xffff0000) == LI(r0, 0) && - (data[1] & 0xffff0000) == ORIS(r0, r0, 0) && - (data[2] & 0xfc000003) == B(0, 0, 0)) - { - const auto sub = vm::cptr::make(addr + 8 + ((s32)data[2] << 6 >> 8 << 2)); - - if (vm::check_addr(sub.addr(), 60) && - sub[0x0] == STDU(r1, r1, -0x80) && - sub[0x1] == STD(r2, r1, 0x70) && - sub[0x2] == MR(r2, r0) && - sub[0x3] == MFLR(r0) && - sub[0x4] == STD(r0, r1, 0x90) && - sub[0x5] == LWZ(r2, r2, 0) && - sub[0x6] == LWZ(r0, r2, 0) && - sub[0x7] == LWZ(r2, r2, 4) && - sub[0x8] == MTCTR(r0) && - sub[0x9] == BCTRL() && - sub[0xa] == LD(r2, r1, 0x70) && - sub[0xb] == ADDI(r1, r1, 0x80) && - sub[0xc] == LD(r0, r1, 0x10) && - sub[0xd] == MTLR(r0) && - sub[0xe] == BLR()) - { - data[0] = HACK(index); - data[1] = BLR(); - data[2] = NOP(); - return true; - } - } - - if (vm::check_addr(addr, 64) && - data[0x0] == MFLR(r0) && - data[0x1] == STD(r0, r1, 0x10) && - data[0x2] == STDU(r1, r1, -0x80) && - data[0x3] == STD(r2, r1, 0x70) && - (data[0x4] & 0xffff0000) == LI(r2, 0) && - (data[0x5] & 0xffff0000) == ORIS(r2, r2, 0) && - data[0x6] == LWZ(r2, r2, 0) && - data[0x7] == LWZ(r0, r2, 0) && - data[0x8] == LWZ(r2, r2, 4) && - data[0x9] == MTCTR(r0) && - data[0xa] == BCTRL() && - data[0xb] == LD(r2, r1, 0x70) && - data[0xc] == ADDI(r1, r1, 0x80) && - data[0xd] == LD(r0, r1, 0x10) && - data[0xe] == MTLR(r0) && - data[0xf] == BLR()) - { - data[0] = HACK(index); - data[1] = BLR(); - std::fill(data + 2, data + 16, NOP()); - return true; - } - - if (vm::check_addr(addr, 60) && - data[0x0] == MFLR(r0) && - data[0x1] == STD(r0, r1, 0x10) && - data[0x2] == STDU(r1, r1, -0x80) && - data[0x3] == STD(r2, r1, 0x70) && - (data[0x4] & 0xffff0000) == LIS(r12, 0) && - (data[0x5] & 0xffff0000) == LWZ(r12, r12, 0) && - data[0x6] == LWZ(r0, r12, 0) && - data[0x7] == LWZ(r2, r12, 4) && - data[0x8] == MTCTR(r0) && - data[0x9] == BCTRL() && - data[0xa] == LD(r2, r1, 0x70) && - data[0xb] == ADDI(r1, r1, 0x80) && - data[0xc] == LD(r0, r1, 0x10) && - data[0xd] == MTLR(r0) && - data[0xe] == BLR()) - { - data[0] = HACK(index); - data[1] = BLR(); - std::fill(data + 2, data + 15, NOP()); - return true; - } - - if (vm::check_addr(addr, 56) && - (data[0x0] & 0xffff0000) == LI(r12, 0) && - (data[0x1] & 0xffff0000) == ORIS(r12, r12, 0) && - (data[0x2] & 0xffff0000) == LWZ(r12, r12, 0) && - data[0x3] == STD(r2, r1, 0x28) && - data[0x4] == MFLR(r0) && - data[0x5] == STD(r0, r1, 0x20) && - data[0x6] == LWZ(r0, r12, 0) && - data[0x7] == LWZ(r2, r12, 4) && - data[0x8] == MTCTR(r0) && - data[0x9] == BCTRL() && - data[0xa] == LD(r0, r1, 0x20) && - data[0xb] == MTLR(r0) && - data[0xc] == LD(r2, r1, 0x28) && - data[0xd] == BLR()) - { - data[0] = HACK(index); - data[1] = BLR(); - std::fill(data + 2, data + 14, NOP()); - return true; - } - - return false; -} - -// Global linkage information -struct ppu_linkage_info -{ - struct module - { - using info_t = std::unordered_map>>; - - info_t functions; - info_t variables; - }; - - // Module -> (NID -> (export; [imports...])) - std::unordered_map modules; -}; - // Link variable static void ppu_patch_variable_refs(u32 vref, u32 vaddr) { @@ -605,7 +450,7 @@ static auto ppu_load_exports(const std::shared_ptr& link, u32 // Function linkage info auto& flink = link->modules[module_name].functions[fnid]; - if (flink.first) + if (flink.export_addr) { LOG_FATAL(LOADER, "Already linked function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name); } @@ -616,18 +461,32 @@ static auto ppu_load_exports(const std::shared_ptr& link, u32 if (_sf && (_sf->flags & MFF_FORCED_HLE)) { - // Inject HACK instruction (TODO: guess function size and analyse B instruction, or reimplement BLR flag for HACK instruction) - const auto code = vm::ptr::make(vm::read32(faddr)); - code[0] = ppu_instructions::HACK(_sf->index); - code[1] = ppu_instructions::BLR(); + // Inject a branch to the HLE implementation + const u32 _entry = vm::read32(faddr); + const u32 target = ppu_function_manager::addr + 8 * _sf->index; + + if ((target <= _entry && _entry - target <= 0x2000000) || (target > _entry && target - _entry < 0x2000000)) + { + // Use relative branch + vm::write32(_entry, ppu_instructions::B(target - _entry)); + } + else if (target < 0x2000000) + { + // Use absolute branch if possible + vm::write32(_entry, ppu_instructions::B(target, true)); + } + else + { + LOG_FATAL(LOADER, "Failed to patch function at 0x%x (0x%x)", _entry, target); + } } else { // Set exported function - flink.first = faddr; + flink.export_addr = faddr; // Fix imports - for (const auto addr : flink.second) + for (const u32 addr : flink.imports) { vm::write32(addr, faddr); //LOG_WARNING(LOADER, "Exported function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name); @@ -649,17 +508,17 @@ static auto ppu_load_exports(const std::shared_ptr& link, u32 // Variable linkage info auto& vlink = link->modules[module_name].variables[vnid]; - if (vlink.first) + if (vlink.export_addr) { LOG_FATAL(LOADER, "Already linked variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name); } else { // Set exported variable - vlink.first = vaddr; + vlink.export_addr = vaddr; // Fix imports - for (const auto vref : vlink.second) + for (const auto vref : vlink.imports) { ppu_patch_variable_refs(vref, vaddr); //LOG_WARNING(LOADER, "Exported variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name); @@ -705,10 +564,13 @@ static void ppu_load_imports(const std::shared_ptr& link, u32 auto& flink = link->modules[module_name].functions[fnid]; // Add new import - flink.second.emplace(faddr); + flink.imports.emplace(faddr); // Link if available - if (flink.first) vm::write32(faddr, flink.first); + if (flink.export_addr) + { + vm::write32(faddr, flink.export_addr); + } //LOG_WARNING(LOADER, "Imported function '%s' in module '%s' (0x%x)", ppu_get_function_name(module_name, fnid), module_name, faddr); } @@ -726,10 +588,13 @@ static void ppu_load_imports(const std::shared_ptr& link, u32 auto& vlink = link->modules[module_name].variables[vnid]; // Add new import - vlink.second.emplace(vref); + vlink.imports.emplace(vref); // Link if available - if (vlink.first) ppu_patch_variable_refs(vref, vlink.first); + if (vlink.export_addr) + { + ppu_patch_variable_refs(vref, vlink.export_addr); + } //LOG_WARNING(LOADER, "Imported variable '%s' in module '%s' (0x%x)", ppu_get_variable_name(module_name, vnid), module_name, vlink.first); } @@ -940,8 +805,6 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::stri void ppu_load_exec(const ppu_exec_object& elf) { - ppu_initialize_modules(); - if (g_cfg_hook_ppu_funcs) { LOG_TODO(LOADER, "'Hook static functions' option deactivated"); @@ -996,6 +859,7 @@ void ppu_load_exec(const ppu_exec_object& elf) } } + // Load section list, used by the analyser for (const auto& s : elf.shdrs) { LOG_NOTICE(LOADER, "** Section: sh_type=0x%x, addr=0x%llx, size=0x%llx, flags=0x%x", s.sh_type, s.sh_addr, s.sh_size, s.sh_flags); @@ -1009,6 +873,9 @@ void ppu_load_exec(const ppu_exec_object& elf) } } + // Initialize HLE modules + ppu_initialize_modules(); + // Load other programs for (auto& prog : elf.progs) { @@ -1296,55 +1163,31 @@ void ppu_load_exec(const ppu_exec_object& elf) for (auto& entry : module.second.functions) { const u32 fnid = entry.first; - const u32 faddr = entry.second.first; + const u32 faddr = entry.second.export_addr; if (faddr == 0) { + const std::string fname = ppu_get_function_name(module.first, fnid); + + // Link HLE implementation if available if (const auto _sf = _sm && _sm->functions.count(fnid) ? &_sm->functions.at(fnid) : nullptr) { - // Static function - for (auto& import : entry.second.second) - { - const u32 stub = vm::read32(import); + LOG_NOTICE(LOADER, "Linking HLE function '%s' in module '%s' (index %u)", fname, module.first, _sf->index); - if (!ppu_patch_import_stub(stub, _sf->index)) - { - LOG_ERROR(LOADER, "Failed to inject code for function '%s' in module '%s' (0x%x)", _sf->name, module.first, stub); - } - else - { - LOG_NOTICE(LOADER, "Injected hack for function '%s' in module '%s' (*0x%x)", _sf->name, module.first, stub); - } + for (const u32 import : entry.second.imports) + { + LOG_TRACE(LOADER, "** Linked at *0x%x (0x%x)", import, vm::read32(import)); + vm::write32(import, ppu_function_manager::addr + 8 * _sf->index); } } else { - // TODO - const u32 index = ::size32(g_ppu_function_cache); - const std::string& fname = ppu_get_function_name(module.first, fnid); - g_ppu_function_cache.emplace_back(); - g_ppu_function_names.emplace_back(fmt::format("%s.%s", module.first, fname)); - g_ppu_fnid_cache.emplace_back(fnid); + LOG_ERROR(LOADER, "Unknown function '%s' in module '%s'", fname, module.first); - LOG_ERROR(LOADER, "Unknown function '%s' in module '%s' (index %u)", fname, module.first, index); - - for (auto& import : entry.second.second) + for (const u32 import : entry.second.imports) { - if (_sm) - { - const u32 stub = vm::read32(import); - - if (!ppu_patch_import_stub(stub, index)) - { - LOG_ERROR(LOADER, "Failed to inject code for function '%s' in module '%s' (0x%x)", fname, module.first, stub); - } - else - { - LOG_NOTICE(LOADER, "Injected hack for function '%s' in module '%s' (*0x%x)", fname, module.first, stub); - } - } - - LOG_WARNING(LOADER, "** Not linked at *0x%x", import); + LOG_WARNING(LOADER, "** Not linked at *0x%x (0x%x)", import, vm::read32(import)); + vm::write32(import, ppu_function_manager::addr); } } } @@ -1353,16 +1196,18 @@ void ppu_load_exec(const ppu_exec_object& elf) for (auto& entry : module.second.variables) { const u32 vnid = entry.first; - const u32 vaddr = entry.second.first; + const u32 vaddr = entry.second.export_addr; if (vaddr == 0) { - // Static variable + const std::string vname = ppu_get_variable_name(module.first, vnid); + + // Link HLE variable if available if (const auto _sv = _sm && _sm->variables.count(vnid) ? &_sm->variables.at(vnid) : nullptr) { - LOG_NOTICE(LOADER, "Linking HLE variable '%s' in module '%s' (*0x%x):", ppu_get_variable_name(module.first, vnid), module.first, _sv->var->addr()); + LOG_NOTICE(LOADER, "Linking HLE variable '%s' in module '%s' (*0x%x):", vname, module.first, _sv->var->addr()); - for (auto& ref : entry.second.second) + for (const u32 ref : entry.second.imports) { ppu_patch_variable_refs(ref, _sv->var->addr()); LOG_NOTICE(LOADER, "** Linked at ref=*0x%x", ref); @@ -1370,9 +1215,9 @@ void ppu_load_exec(const ppu_exec_object& elf) } else { - LOG_ERROR(LOADER, "Unknown variable '%s' in module '%s'", ppu_get_variable_name(module.first, vnid), module.first); + LOG_ERROR(LOADER, "Unknown variable '%s' in module '%s'", vname, module.first); - for (auto& ref : entry.second.second) + for (const u32 ref : entry.second.imports) { LOG_WARNING(LOADER, "** Not linked at ref=*0x%x", ref); } diff --git a/rpcs3/Emu/Cell/PPUOpcodes.h b/rpcs3/Emu/Cell/PPUOpcodes.h index 66a66b8bd0..23fa3dd911 100644 --- a/rpcs3/Emu/Cell/PPUOpcodes.h +++ b/rpcs3/Emu/Cell/PPUOpcodes.h @@ -121,7 +121,6 @@ public: // Main opcodes (field 0..5) fill_table(0x00, 6, -1, { - { 0x01, &D::HACK }, { 0x02, &D::TDI }, { 0x03, &D::TWI }, { 0x07, &D::MULLI }, @@ -609,7 +608,6 @@ namespace ppu_instructions namespace implicts { - inline u32 HACK(u32 index) { return 0x01 << 26 | index; } inline u32 NOP() { return ORI(r0, r0, 0); } inline u32 MR(u32 rt, u32 ra) { return OR(rt, ra, ra, false); } inline u32 LI(u32 rt, u32 imm) { return ADDI(rt, r0, imm); } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 8e8ffc2da0..09b8559795 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -102,7 +102,6 @@ const ppu_decoder s_ppu_interpreter_fast; extern void ppu_initialize(); extern void ppu_initialize(const ppu_module& info); extern void ppu_execute_syscall(ppu_thread& ppu, u64 code); -extern void ppu_execute_function(ppu_thread& ppu, u32 index); const auto s_ppu_compiled = static_cast(utils::memory_reserve(0x100000000)); @@ -157,24 +156,34 @@ extern void ppu_register_range(u32 addr, u32 size) extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr) { + // Initialize specific function + if (ptr) + { + s_ppu_compiled[addr / 4] = ::narrow(reinterpret_cast(ptr)); + return; + } + if (!size) { LOG_ERROR(PPU, "ppu_register_function_at(0x%x): empty range", addr); return; } - ppu_register_range(addr, size); - if (g_cfg_ppu_decoder.get() == ppu_decoder_type::llvm) { - s_ppu_compiled[addr / 4] = ::narrow(reinterpret_cast(ptr)); return; } // Initialize interpreter cache + const u32 fallback = ::narrow(reinterpret_cast(ppu_fallback)); + while (size) { - s_ppu_compiled[addr / 4] = ppu_cache(addr); + if (s_ppu_compiled[addr / 4] == fallback) + { + s_ppu_compiled[addr / 4] = ppu_cache(addr); + } + addr += 4; size -= 4; } @@ -354,7 +363,7 @@ void ppu_thread::cpu_task() } case ppu_cmd::hle_call: { - cmd_pop(), ppu_execute_function(*this, arg); + cmd_pop(), ppu_function_manager::get().at(arg)(*this); break; } case ppu_cmd::initialize: @@ -379,7 +388,8 @@ void ppu_thread::exec_task() { if (g_cfg_ppu_decoder.get() == ppu_decoder_type::llvm) { - return reinterpret_cast((std::uintptr_t)s_ppu_compiled[cia / 4])(*this); + reinterpret_cast(static_cast(s_ppu_compiled[cia / 4]))(*this); + return; } const auto base = vm::_ptr(0); @@ -558,7 +568,7 @@ void ppu_thread::fast_call(u32 addr, u32 rtoc) cia = addr; gpr[2] = rtoc; - lr = Emu.GetCPUThreadStop(); + lr = ppu_function_manager::addr + 8; // HLE stop address last_function = nullptr; g_tls_log_prefix = [] @@ -662,8 +672,6 @@ const ppu_decoder s_ppu_itype; extern u64 get_timebased_time(); extern ppu_function_t ppu_get_syscall(u64 code); extern std::string ppu_get_syscall_name(u64 code); -extern ppu_function_t ppu_get_function(u32 index); -extern std::string ppu_get_module_function_name(u32 index); extern __m128 sse_exp2_ps(__m128 A); extern __m128 sse_log2_ps(__m128 A); @@ -782,25 +790,6 @@ extern void ppu_initialize() return; } - if (g_cfg_ppu_decoder.get() != ppu_decoder_type::llvm || _funcs->empty()) - { - if (!Emu.GetCPUThreadStop()) - { - auto ppu_thr_stop_data = vm::ptr::make(vm::alloc(2 * 4, vm::main)); - Emu.SetCPUThreadStop(ppu_thr_stop_data.addr()); - ppu_thr_stop_data[0] = ppu_instructions::HACK(1); - ppu_thr_stop_data[1] = ppu_instructions::BLR(); - ppu_register_function_at(ppu_thr_stop_data.addr(), 8, nullptr); - } - - for (const auto& func : *_funcs) - { - ppu_register_function_at(func.addr, func.size, nullptr); - } - - return; - } - std::size_t fpos = 0; while (fpos < _funcs->size()) @@ -891,7 +880,6 @@ extern void ppu_initialize(const ppu_module& info) { "__end", (u64)&ppu_unreachable }, { "__check", (u64)&ppu_check }, { "__trace", (u64)&ppu_trace }, - { "__hlecall", (u64)&ppu_execute_function }, { "__syscall", (u64)&ppu_execute_syscall }, { "__get_tb", (u64)&get_timebased_time }, { "__lwarx", (u64)&ppu_lwarx }, @@ -918,18 +906,6 @@ extern void ppu_initialize(const ppu_module& info) } } - for (u64 index = 1; ; index++) - { - if (auto func = ppu_get_function(index)) - { - link_table.emplace(ppu_get_module_function_name(index), (u64)func); - } - else - { - break; - } - } - const auto jit = fxm::make(std::move(link_table), g_cfg_llvm_cpu.get()); LOG_SUCCESS(PPU, "LLVM: JIT initialized (%s)", jit->cpu()); @@ -1058,7 +1034,6 @@ extern void ppu_initialize(const ppu_module& info) pm.run(*func); const auto _syscall = module->getFunction("__syscall"); - const auto _hlecall = module->getFunction("__hlecall"); for (auto i = inst_begin(*func), end = inst_end(*func); i != end;) { @@ -1084,20 +1059,6 @@ extern void ppu_initialize(const ppu_module& info) } } - if (cif == _hlecall && op1 && isa(op1)) - { - const u32 index = static_cast(cast(op1)->getZExtValue()); - - if (const auto ptr = ppu_get_function(index)) - { - const auto n = ppu_get_module_function_name(index); - const auto f = cast(module->getOrInsertFunction(n, _func)); - - // Call the function directly - ReplaceInstWithInst(ci, CallInst::Create(f, {ci->getArgOperand(0)})); - } - } - continue; } diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index 4f63fb7191..7bef9eb0b0 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -1775,12 +1775,6 @@ void PPUTranslator::BC(ppu_opcode_t op) CallFunction(target, !op.lk); } -void PPUTranslator::HACK(ppu_opcode_t op) -{ - Call(GetType(), "__hlecall", m_thread, m_ir->getInt32(op.opcode & 0x3ffffff)); - UndefineVolatileRegisters(); -} - void PPUTranslator::SC(ppu_opcode_t op) { if (op.opcode != ppu_instructions::SC(0) && op.opcode != ppu_instructions::SC(1)) diff --git a/rpcs3/Emu/Cell/PPUTranslator.h b/rpcs3/Emu/Cell/PPUTranslator.h index adf4582e16..19af8b6fb9 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.h +++ b/rpcs3/Emu/Cell/PPUTranslator.h @@ -599,7 +599,6 @@ public: void ADDI(ppu_opcode_t op); void ADDIS(ppu_opcode_t op); void BC(ppu_opcode_t op); - void HACK(ppu_opcode_t op); void SC(ppu_opcode_t op); void B(ppu_opcode_t op); void MCRF(ppu_opcode_t op); diff --git a/rpcs3/Emu/PSP2/ARMv7Function.cpp b/rpcs3/Emu/PSP2/ARMv7Function.cpp index 8c4ecb48e1..1f25c085e5 100644 --- a/rpcs3/Emu/PSP2/ARMv7Function.cpp +++ b/rpcs3/Emu/PSP2/ARMv7Function.cpp @@ -54,3 +54,5 @@ u32 arm_function_manager::add_function(arm_function_t function) return ::size32(list) - 1; } + +DECLARE(arm_function_manager::addr); diff --git a/rpcs3/Emu/PSP2/ARMv7Function.h b/rpcs3/Emu/PSP2/ARMv7Function.h index ca7348f204..e395ea0dcb 100644 --- a/rpcs3/Emu/PSP2/ARMv7Function.h +++ b/rpcs3/Emu/PSP2/ARMv7Function.h @@ -468,6 +468,9 @@ public: { return access(); } + + // Allocation address + static u32 addr; }; template diff --git a/rpcs3/Emu/PSP2/ARMv7Module.cpp b/rpcs3/Emu/PSP2/ARMv7Module.cpp index c0d0def891..e4aa5997ad 100644 --- a/rpcs3/Emu/PSP2/ARMv7Module.cpp +++ b/rpcs3/Emu/PSP2/ARMv7Module.cpp @@ -630,7 +630,7 @@ void arm_load_exec(const arm_exec_object& elf) const auto stop_code = vm::ptr::make(vm::alloc(3 * 4, vm::main)); stop_code[0] = 0xf870; // HACK instruction (Thumb) stop_code[1] = 1; // Predefined function index (HLE return) - Emu.SetCPUThreadStop(stop_code.addr()); + arm_function_manager::addr = stop_code.addr(); const std::string& thread_name = proc_param->sceUserMainThreadName ? proc_param->sceUserMainThreadName.get_ptr() : "main_thread"; const u32 stack_size = proc_param->sceUserMainThreadStackSize ? proc_param->sceUserMainThreadStackSize->value() : 256 * 1024; diff --git a/rpcs3/Emu/PSP2/ARMv7Thread.cpp b/rpcs3/Emu/PSP2/ARMv7Thread.cpp index e8d3451c7b..82b978381d 100644 --- a/rpcs3/Emu/PSP2/ARMv7Thread.cpp +++ b/rpcs3/Emu/PSP2/ARMv7Thread.cpp @@ -6,6 +6,7 @@ #include "ARMv7Thread.h" #include "ARMv7Opcodes.h" #include "ARMv7Interpreter.h" +#include "ARMv7Function.h" #include "Utilities/GSL.h" @@ -122,7 +123,7 @@ void ARMv7Thread::fast_call(u32 addr) const auto old_func = last_function; PC = addr; - LR = Emu.GetCPUThreadStop(); + LR = arm_function_manager::addr; // TODO last_function = nullptr; auto at_ret = gsl::finally([&]() diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 6f6a982543..4d0031625e 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -62,12 +62,6 @@ namespace rpcs3 event& on_resume() { static event on_resume; return on_resume; } } -Emulator::Emulator() - : m_status(Stopped) - , m_cpu_thr_stop(0) -{ -} - void Emulator::Init() { if (!g_tty) @@ -106,8 +100,6 @@ void Emulator::Init() fs::create_dir(dev_hdd1 + "game/"); fs::create_path(dev_hdd1); fs::create_path(dev_usb); - - SetCPUThreadStop(0); } void Emulator::SetPath(const std::string& path, const std::string& elf_path) diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 0bacd1487c..b3531a3cbe 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -39,15 +39,13 @@ enum Status : u32 class Emulator final { - atomic_t m_status; + atomic_t m_status{Stopped}; EmuCallbacks m_cb; atomic_t m_pause_start_time; // set when paused atomic_t m_pause_amend_time; // increased when resumed - u32 m_cpu_thr_stop; - std::string m_path; std::string m_elf_path; std::string m_cache_path; @@ -55,7 +53,7 @@ class Emulator final std::string m_title; public: - Emulator(); + Emulator() = default; void SetCallbacks(EmuCallbacks&& cb) { @@ -109,13 +107,6 @@ public: return m_pause_amend_time; } - void SetCPUThreadStop(u32 addr) - { - m_cpu_thr_stop = addr; - } - - u32 GetCPUThreadStop() const { return m_cpu_thr_stop; } - bool BootGame(const std::string& path, bool direct = false); static std::string GetGameDir();