From 6b45c98fd9dc15eab7c06177a73f68659f6b56f0 Mon Sep 17 00:00:00 2001 From: Ty Lamontagne Date: Thu, 26 Oct 2023 18:26:16 -0400 Subject: [PATCH] Debugger: IOP Function Tree Co-authored-by: Ziemas --- pcsx2-qt/Debugger/CpuWidget.cpp | 216 +++++++++++++++++++++++++++----- pcsx2-qt/Debugger/CpuWidget.h | 5 +- pcsx2-qt/Debugger/CpuWidget.ui | 29 +++++ pcsx2/DebugTools/SymbolMap.cpp | 92 +++++++++++++- pcsx2/DebugTools/SymbolMap.h | 40 +++++- pcsx2/IopBios.cpp | 111 +++++++++++++--- pcsx2/IopBios.h | 1 + pcsx2/R3000AInterpreter.cpp | 5 + pcsx2/x86/iR3000A.cpp | 5 + 9 files changed, 449 insertions(+), 55 deletions(-) diff --git a/pcsx2-qt/Debugger/CpuWidget.cpp b/pcsx2-qt/Debugger/CpuWidget.cpp index 920ef095678e38..0d3f9c83c791b6 100644 --- a/pcsx2-qt/Debugger/CpuWidget.cpp +++ b/pcsx2-qt/Debugger/CpuWidget.cpp @@ -94,6 +94,8 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu) connect(m_ui.tabWidgetRegFunc, &QTabWidget::currentChanged, [this](int i) {if(i == 1){updateFunctionList(true);} }); connect(m_ui.listFunctions, &QListWidget::customContextMenuRequested, this, &CpuWidget::onFuncListContextMenu); connect(m_ui.listFunctions, &QListWidget::itemDoubleClicked, this, &CpuWidget::onFuncListDoubleClick); + connect(m_ui.treeModules, &QTreeWidget::customContextMenuRequested, this, &CpuWidget::onModuleTreeContextMenu); + connect(m_ui.treeModules, &QTreeWidget::itemDoubleClicked, this, &CpuWidget::onModuleTreeDoubleClick); connect(m_ui.btnRefreshFunctions, &QPushButton::clicked, [this] { updateFunctionList(); }); connect(m_ui.txtFuncSearch, &QLineEdit::textChanged, [this] { updateFunctionList(); }); @@ -109,6 +111,15 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu) m_ui.registerWidget->SetCpu(&cpu); m_ui.memoryviewWidget->SetCpu(&cpu); + if (cpu.getCpuType() == BREAKPOINT_EE) + { + m_ui.treeModules->setVisible(false); + } + else + { + m_ui.treeModules->header()->setSectionResizeMode(0, QHeaderView::ResizeMode::ResizeToContents); + m_ui.listFunctions->setVisible(false); + } this->repaint(); } @@ -467,36 +478,82 @@ void CpuWidget::updateFunctionList(bool whenEmpty) if (!m_cpu.isAlive()) return; - if (whenEmpty && m_ui.listFunctions->count()) - return; + if (m_cpu.getCpuType() == BREAKPOINT_EE || !m_moduleView) + { + if (whenEmpty && m_ui.listFunctions->count()) + return; - m_ui.listFunctions->clear(); + m_ui.listFunctions->clear(); - const auto demangler = demangler::CDemangler::createGcc(); - const QString filter = m_ui.txtFuncSearch->text().toLower(); - for (const auto& symbol : m_cpu.GetSymbolMap().GetAllSymbols(SymbolType::ST_FUNCTION)) - { - QString symbolName = symbol.name.c_str(); - if (m_demangleFunctions) + const auto demangler = demangler::CDemangler::createGcc(); + const QString filter = m_ui.txtFuncSearch->text().toLower(); + for (const auto& symbol : m_cpu.GetSymbolMap().GetAllSymbols(SymbolType::ST_FUNCTION)) { - symbolName = QString(demangler->demangleToString(symbol.name).c_str()); + QString symbolName = symbol.name.c_str(); + if (m_demangleFunctions) + { + symbolName = QString(demangler->demangleToString(symbol.name).c_str()); - // If the name isn't mangled, or it doesn't understand, it'll return an empty string - // Fall back to the original name if this is the case - if (symbolName.isEmpty()) - symbolName = symbol.name.c_str(); - } + // If the name isn't mangled, or it doesn't understand, it'll return an empty string + // Fall back to the original name if this is the case + if (symbolName.isEmpty()) + symbolName = symbol.name.c_str(); + } - if (filter.size() && !symbolName.toLower().contains(filter)) - continue; + if (filter.size() && !symbolName.toLower().contains(filter)) + continue; - QListWidgetItem* item = new QListWidgetItem(); + QListWidgetItem* item = new QListWidgetItem(); - item->setText(QString("%0 %1").arg(FilledQStringFromValue(symbol.address, 16)).arg(symbolName)); + item->setText(QString("%0 %1").arg(FilledQStringFromValue(symbol.address, 16)).arg(symbolName)); - item->setData(256, symbol.address); + item->setData(256, symbol.address); - m_ui.listFunctions->addItem(item); + m_ui.listFunctions->addItem(item); + } + } + else + { + const auto demangler = demangler::CDemangler::createGcc(); + const QString filter = m_ui.txtFuncSearch->text().toLower(); + + m_ui.treeModules->clear(); + for (const auto& module : m_cpu.GetSymbolMap().GetModules()) + { + QTreeWidgetItem* moduleItem = new QTreeWidgetItem(m_ui.treeModules, QStringList({QString(module.name.c_str()), QString("%0.%1").arg(module.version.major).arg(module.version.minor)})); + QList functions; + for (const auto& sym : module.exports) + { + if (!QString(sym.name.c_str()).toLower().contains(filter)) + continue; + + QString symbolName = QString(sym.name.c_str()); + if (m_demangleFunctions) + { + QString demangledName = QString(demangler->demangleToString(sym.name).c_str()); + if (!demangledName.isEmpty()) + symbolName = demangledName; + } + QTreeWidgetItem* functionItem = new QTreeWidgetItem(moduleItem, QStringList(QString("%0 %1").arg(FilledQStringFromValue(sym.address, 16)).arg(symbolName))); + functionItem->setData(0, 256, sym.address); + functions.append(functionItem); + } + moduleItem->addChildren(functions); + + if (!filter.isEmpty() && functions.size()) + { + moduleItem->setExpanded(true); + m_ui.treeModules->insertTopLevelItem(0, moduleItem); + } + else if (filter.isEmpty()) + { + m_ui.treeModules->insertTopLevelItem(0, moduleItem); + } + else + { + delete moduleItem; + } + } } } @@ -555,19 +612,6 @@ void CpuWidget::onFuncListContextMenu(QPoint pos) else m_funclistContextMenu->clear(); - //: "Demangling" is the opposite of "Name mangling", which is a process where a compiler takes function names and combines them with other characteristics of the function (e.g. what types of data it accepts) to ensure they stay unique even when multiple functions exist with the same name (but different inputs / const-ness). See here: https://en.wikipedia.org/wiki/Name_mangling#C++ - QAction* demangleAction = new QAction(tr("Demangle Symbols"), m_ui.listFunctions); - demangleAction->setCheckable(true); - demangleAction->setChecked(m_demangleFunctions); - - connect(demangleAction, &QAction::triggered, [this] { - m_demangleFunctions = !m_demangleFunctions; - m_ui.disassemblyWidget->setDemangle(m_demangleFunctions); - updateFunctionList(); - }); - - m_funclistContextMenu->addAction(demangleAction); - QAction* copyName = new QAction(tr("Copy Function Name"), m_ui.listFunctions); connect(copyName, &QAction::triggered, [this] { // We only store the address in the widget item @@ -601,9 +645,38 @@ void CpuWidget::onFuncListContextMenu(QPoint pos) m_ui.memoryviewWidget->gotoAddress(m_ui.listFunctions->selectedItems().first()->data(256).toUInt()); }); + m_funclistContextMenu->addSeparator(); + m_funclistContextMenu->addAction(gotoMemory); + //: "Demangling" is the opposite of "Name mangling", which is a process where a compiler takes function names and combines them with other characteristics of the function (e.g. what types of data it accepts) to ensure they stay unique even when multiple functions exist with the same name (but different inputs / const-ness). See here: https://en.wikipedia.org/wiki/Name_mangling#C++ + QAction* demangleAction = new QAction(tr("Demangle Symbols"), m_ui.listFunctions); + demangleAction->setCheckable(true); + demangleAction->setChecked(m_demangleFunctions); + connect(demangleAction, &QAction::triggered, [this] { + m_demangleFunctions = !m_demangleFunctions; + m_ui.disassemblyWidget->setDemangle(m_demangleFunctions); + updateFunctionList(); + }); + + m_funclistContextMenu->addAction(demangleAction); + + if (m_cpu.getCpuType() == BREAKPOINT_IOP) + { + QAction* moduleViewAction = new QAction(tr("Module Tree"), m_ui.listFunctions); + moduleViewAction->setCheckable(true); + moduleViewAction->setChecked(m_moduleView); + + connect(moduleViewAction, &QAction::triggered, [this] { + m_moduleView = !m_moduleView; + m_ui.treeModules->setVisible(m_moduleView); + m_ui.listFunctions->setVisible(!m_moduleView); + updateFunctionList(); + }); + + m_funclistContextMenu->addAction(moduleViewAction); + } m_funclistContextMenu->popup(m_ui.listFunctions->viewport()->mapToGlobal(pos)); } @@ -612,6 +685,81 @@ void CpuWidget::onFuncListDoubleClick(QListWidgetItem* item) m_ui.disassemblyWidget->gotoAddress(item->data(256).toUInt()); } +void CpuWidget::onModuleTreeContextMenu(QPoint pos) +{ + if (!m_moduleTreeContextMenu) + m_moduleTreeContextMenu = new QMenu(m_ui.treeModules); + else + m_moduleTreeContextMenu->clear(); + + if (m_ui.treeModules->selectedItems().count() && m_ui.treeModules->selectedItems().first()->data(0, 256).isValid()) + { + QAction* copyName = new QAction(tr("Copy Function Name"), m_ui.treeModules); + connect(copyName, &QAction::triggered, [this] { + QApplication::clipboard()->setText(m_cpu.GetSymbolMap().GetLabelName(m_ui.treeModules->selectedItems().first()->data(0, 256).toUInt()).c_str()); + }); + m_moduleTreeContextMenu->addAction(copyName); + + QAction* copyAddress = new QAction(tr("Copy Function Address"), m_ui.treeModules); + connect(copyAddress, &QAction::triggered, [this] { + const QString addressString = FilledQStringFromValue(m_ui.treeModules->selectedItems().first()->data(0, 256).toUInt(), 16); + QApplication::clipboard()->setText(addressString); + }); + m_moduleTreeContextMenu->addAction(copyAddress); + + m_moduleTreeContextMenu->addSeparator(); + + QAction* gotoDisasm = new QAction(tr("Go to in Disassembly"), m_ui.treeModules); + connect(gotoDisasm, &QAction::triggered, [this] { + m_ui.disassemblyWidget->gotoAddress(m_ui.treeModules->selectedItems().first()->data(0, 256).toUInt()); + }); + m_moduleTreeContextMenu->addAction(gotoDisasm); + + QAction* gotoMemory = new QAction(tr("Go to in Memory View"), m_ui.treeModules); + connect(gotoMemory, &QAction::triggered, [this] { + m_ui.memoryviewWidget->gotoAddress(m_ui.treeModules->selectedItems().first()->data(0, 256).toUInt()); + }); + m_moduleTreeContextMenu->addAction(gotoMemory); + } + + //: "Demangling" is the opposite of "Name mangling", which is a process where a compiler takes function names and combines them with other characteristics of the function (e.g. what types of data it accepts) to ensure they stay unique even when multiple functions exist with the same name (but different inputs / const-ness). See here: https://en.wikipedia.org/wiki/Name_mangling#C++ + QAction* demangleAction = new QAction(tr("Demangle Symbols"), m_ui.treeModules); + demangleAction->setCheckable(true); + demangleAction->setChecked(m_demangleFunctions); + + connect(demangleAction, &QAction::triggered, [this] { + m_demangleFunctions = !m_demangleFunctions; + m_ui.disassemblyWidget->setDemangle(m_demangleFunctions); + updateFunctionList(); + }); + + m_moduleTreeContextMenu->addSeparator(); + + m_moduleTreeContextMenu->addAction(demangleAction); + + QAction* moduleViewAction = new QAction(tr("Module Tree"), m_ui.treeModules); + moduleViewAction->setCheckable(true); + moduleViewAction->setChecked(m_moduleView); + + connect(moduleViewAction, &QAction::triggered, [this] { + m_moduleView = !m_moduleView; + m_ui.treeModules->setVisible(m_moduleView); + m_ui.listFunctions->setVisible(!m_moduleView); + updateFunctionList(); + }); + + m_moduleTreeContextMenu->addAction(moduleViewAction); + + m_moduleTreeContextMenu->popup(m_ui.treeModules->viewport()->mapToGlobal(pos)); +} + +void CpuWidget::onModuleTreeDoubleClick(QTreeWidgetItem* item) +{ + if (item->data(0, 256).isValid()) + { + m_ui.disassemblyWidget->gotoAddress(item->data(0, 256).toUInt()); + } +} void CpuWidget::updateStackFrames() { m_stackModel.refreshData(); diff --git a/pcsx2-qt/Debugger/CpuWidget.h b/pcsx2-qt/Debugger/CpuWidget.h index 771c97544b8647..22d54ad6bb0ca1 100644 --- a/pcsx2-qt/Debugger/CpuWidget.h +++ b/pcsx2-qt/Debugger/CpuWidget.h @@ -74,7 +74,8 @@ public slots: void onFuncListContextMenu(QPoint pos); void onFuncListDoubleClick(QListWidgetItem* item); bool getDemangleFunctions() const { return m_demangleFunctions; } - + void onModuleTreeContextMenu(QPoint pos); + void onModuleTreeDoubleClick(QTreeWidgetItem* item); void reloadCPUWidgets() { if (!QtHost::IsOnUIThread()) @@ -99,6 +100,7 @@ public slots: QMenu* m_stacklistContextMenu = 0; QMenu* m_funclistContextMenu = 0; + QMenu* m_moduleTreeContextMenu = 0; Ui::CpuWidget m_ui; @@ -110,4 +112,5 @@ public slots: StackModel m_stackModel; bool m_demangleFunctions = true; + bool m_moduleView = true; }; diff --git a/pcsx2-qt/Debugger/CpuWidget.ui b/pcsx2-qt/Debugger/CpuWidget.ui index a2b35f1e010ef5..16a81cb2ad2a6e 100644 --- a/pcsx2-qt/Debugger/CpuWidget.ui +++ b/pcsx2-qt/Debugger/CpuWidget.ui @@ -115,6 +115,35 @@ 0 + + + + true + + + Qt::CustomContextMenu + + + 8 + + + true + + + 2 + + + + Module + + + + + Version + + + + diff --git a/pcsx2/DebugTools/SymbolMap.cpp b/pcsx2/DebugTools/SymbolMap.cpp index 92f41052775d0e..116b803bf50f5b 100644 --- a/pcsx2/DebugTools/SymbolMap.cpp +++ b/pcsx2/DebugTools/SymbolMap.cpp @@ -40,6 +40,7 @@ void SymbolMap::Clear() functions.clear(); labels.clear(); data.clear(); + modules.clear(); } @@ -222,7 +223,7 @@ std::string SymbolMap::GetDescription(unsigned int address) const return descriptionTemp; } -std::vector SymbolMap::GetAllSymbols(SymbolType symmask) +std::vector SymbolMap::GetAllSymbols(SymbolType symmask) const { std::vector result; @@ -273,6 +274,7 @@ void SymbolMap::AddFunction(std::string name, u32 address, u32 size) func.start = address; func.size = size; func.index = (int)functions.size(); + func.name = name; functions[address] = func; functions.insert(std::make_pair(address, func)); @@ -509,3 +511,91 @@ DataType SymbolMap::GetDataType(u32 startAddress) const return DATATYPE_NONE; return it->second.type; } + +bool SymbolMap::AddModule(const std::string& name, ModuleVersion version) +{ + std::lock_guard guard(m_lock); + auto it = modules.find(name); + if (it != modules.end()) + { + for (auto [itr, end] = modules.equal_range(name); itr != end; ++itr) + { + // Different major versions, we treat this one as a different module + if (itr->second.version.major != version.major) + continue; + + // RegisterLibraryEntries will fail if the new minor ver is <= the old minor ver + // and the major version is the same + if (itr->second.version.minor >= version.minor) + return false; + + // Remove the old module and its export table + RemoveModule(name, itr->second.version); + break; + } + } + + modules.insert(std::make_pair(name, ModuleEntry{name, version, {}})); + return true; +} + +void SymbolMap::AddModuleExport(const std::string& module, ModuleVersion version, const std::string& name, u32 address, u32 size) +{ + std::lock_guard guard(m_lock); + for (auto [itr, end] = modules.equal_range(module); itr != end; ++itr) + { + if (itr->second.version != version) + continue; + + AddFunction(name, address, size); + AddLabel(name, address); + itr->second.exports.push_back({address, size, 0, name}); + } +} + +std::vector SymbolMap::GetModules() const +{ + std::lock_guard guard(m_lock); + std::vector result; + for (auto& module : modules) + { + std::vector exports; + for (auto& fun : module.second.exports) + { + exports.push_back({fun.name, fun.start, fun.size}); + } + result.push_back({module.second.name, module.second.version, exports}); + } + return result; +} + +void SymbolMap::RemoveModule(const std::string& name, ModuleVersion version) +{ + std::lock_guard guard(m_lock); + for (auto [itr, end] = modules.equal_range(name); itr != end; ++itr) + { + if (itr->second.version != version) + continue; + + for (auto& exportEntry : itr->second.exports) + { + RemoveFunction(exportEntry.start); + } + + modules.erase(itr); + break; + } +} + +void SymbolMap::ClearModules() +{ + std::lock_guard guard(m_lock); + for (auto& module : modules) + { + for (auto& exportEntry : module.second.exports) + { + RemoveFunction(exportEntry.start); + } + } + modules.clear(); +} diff --git a/pcsx2/DebugTools/SymbolMap.h b/pcsx2/DebugTools/SymbolMap.h index efbec566084ca6..c8126a523f29e5 100644 --- a/pcsx2/DebugTools/SymbolMap.h +++ b/pcsx2/DebugTools/SymbolMap.h @@ -45,12 +45,19 @@ struct SymbolEntry u32 size; }; -struct LoadedModuleInfo +struct ModuleVersion +{ + u8 major; + u8 minor; + + friend auto operator<=>(const ModuleVersion&, const ModuleVersion&) = default; +}; + +struct ModuleInfo { std::string name; - u32 address; - u32 size; - bool active; + ModuleVersion version; + std::vector exports; }; enum DataType @@ -75,7 +82,7 @@ class SymbolMap bool GetSymbolInfo(SymbolInfo* info, u32 address, SymbolType symmask = ST_FUNCTION) const; u32 GetNextSymbolAddress(u32 address, SymbolType symmask); std::string GetDescription(unsigned int address) const; - std::vector GetAllSymbols(SymbolType symmask); + std::vector GetAllSymbols(SymbolType symmask) const; void AddFunction(std::string name, u32 address, u32 size); u32 GetFunctionStart(u32 address) const; @@ -94,6 +101,17 @@ class SymbolMap u32 GetDataSize(u32 startAddress) const; DataType GetDataType(u32 startAddress) const; + // Module functions for IOP symbols + + bool AddModule(const std::string& name, ModuleVersion version); + void AddModuleExport(const std::string& module, ModuleVersion version, const std::string& name, u32 address, u32 size); + std::vector GetModules() const; + void RemoveModule(const std::string& name, ModuleVersion version); + // Clears any modules and their associated exports + // Prefer this over Clear() so we don't clear user defined functions + // In the future we should mark functions as user defined + void ClearModules(); + static const u32 INVALID_ADDRESS = (u32)-1; bool IsEmpty() const { return functions.empty() && labels.empty() && data.empty(); }; @@ -106,6 +124,7 @@ class SymbolMap u32 start; u32 size; int index; + std::string name; }; struct LabelEntry @@ -121,9 +140,20 @@ class SymbolMap u32 size; }; + struct ModuleEntry + { + std::string name; + ModuleVersion version; + // This is duplicated data from the function map + // The issue is that multiple exports can point to the same address + // The address we use as a key... We should use a multimap in the future + std::vector exports; + }; + std::map functions; std::map labels; std::map data; + std::multimap modules; mutable std::recursive_mutex m_lock; }; diff --git a/pcsx2/IopBios.cpp b/pcsx2/IopBios.cpp index ed849275195fef..9a045fe3f6efe4 100644 --- a/pcsx2/IopBios.cpp +++ b/pcsx2/IopBios.cpp @@ -25,6 +25,10 @@ #include "x86/iR3000A.h" #include "VMManager.h" +#include +#include +#include +#include #include "common/FileSystem.h" #include "common/Path.h" @@ -42,10 +46,10 @@ #endif #if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) -#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif #if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) -#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif #ifndef O_BINARY @@ -1017,6 +1021,31 @@ namespace R3000A namespace loadcore { + u32 GetModList(u32 a0reg) + { + u32 lcptr = iopMemRead32(0x3f0); + u32 lcstring = irxFindLoadcore(lcptr); + u32 list = 0; + + if (lcstring == 0) + { + list = lcptr - 0x20; + } + else + { + list = lcstring + 0x18; + } + + u32 mod = iopMemRead32(list); + + while (mod != 0) + { + mod = iopMemRead32(mod); + } + + return list; + } + // Gets the thread list ptr from thbase u32 GetThreadList(u32 a0reg, u32 version) { @@ -1035,8 +1064,49 @@ namespace R3000A return list; } - int RegisterLibraryEntries_HLE() + void LoadFuncs(u32 a0reg) { + const std::string modname = iopMemReadString(a0reg + 12); + ModuleVersion version = {iopMemRead8(a0 + 9), iopMemRead8(a0 + 8)}; + DevCon.WriteLn(Color_Gray, "RegisterLibraryEntries: %8.8s version %x.%02x", modname.data(), version.major, version.minor); + + if (R3000SymbolMap.AddModule(modname, version)) + { + u32 func = a0reg + 20; + u32 funcptr = iopMemRead32(func); + u32 index = 0; + while (funcptr != 0) + { + const std::string funcname = std::string(irxImportFuncname(modname, index)); + if (!funcname.empty()) + { + R3000SymbolMap.AddModuleExport(modname, version, fmt::format("{}::{}", modname, funcname).c_str(), funcptr, 0); + } + else + { + R3000SymbolMap.AddModuleExport(modname, version, fmt::format("{}::unkn_{:02}", modname, index).c_str(), funcptr, 0); + } + index++; + func += 4; + funcptr = iopMemRead32(func); + } + } + } + + void ReleaseFuncs(u32 a0reg) + { + const std::string modname = iopMemReadString(a0reg + 12); + ModuleVersion version = {iopMemRead8(a0 + 9), iopMemRead8(a0 + 8)}; + + DevCon.WriteLn(Color_Gray, "ReleaseLibraryEntries: %8.8s version %x.%02x", modname.data(), version.major, version.minor); + + R3000SymbolMap.RemoveModule(modname, version); + } + + void RegisterLibraryEntries_DEBUG() + { + LoadFuncs(a0); + const std::string modname = iopMemReadString(a0 + 12); if (modname == "thbase") { @@ -1044,13 +1114,12 @@ namespace R3000A CurrentBiosInformation.iopThreadListAddr = GetThreadList(a0, version); } - return 0; + CurrentBiosInformation.iopModListAddr = GetModList(a0); } - void RegisterLibraryEntries_DEBUG() + void ReleaseLibraryEntries_DEBUG() { - const std::string modname = iopMemReadString(a0 + 12); - DevCon.WriteLn(Color_Gray, "RegisterLibraryEntries: %8.8s version %x.%02x", modname.data(), (unsigned)iopMemRead8(a0 + 9), (unsigned)iopMemRead8(a0 + 8)); + ReleaseFuncs(a0); } } // namespace loadcore @@ -1099,6 +1168,24 @@ namespace R3000A } } // namespace sifcmd + u32 irxFindLoadcore(u32 entrypc) + { + u32 i; + + i = entrypc; + while (entrypc - i < 0x50) + { + // find loadcore string + if (iopMemRead32(i) == 0x49497350 && iopMemRead32(i + 4) == 0x64616F6C) + { + return i; + } + i -= 4; + } + + return 0; + } + u32 irxImportTableAddr(u32 entrypc) { u32 i; @@ -1128,10 +1215,10 @@ namespace R3000A // case 3: ??? } - return 0; + return ""; } -// clang-format off + // clang-format off #define MODULE(n) \ if (#n == libname) \ { \ @@ -1157,11 +1244,6 @@ namespace R3000A EXPORT_H( 14, Kprintf) END_MODULE - // For grabbing the thread list from thbase - MODULE(loadcore) - EXPORT_H( 6, RegisterLibraryEntries) - END_MODULE - // Special case with ioman and iomanX // They are mostly compatible excluding stat structures if(libname == "ioman" || libname == "iomanx") @@ -1201,6 +1283,7 @@ namespace R3000A // clang-format off MODULE(loadcore) EXPORT_D( 6, RegisterLibraryEntries) + EXPORT_D( 7, ReleaseLibraryEntries); END_MODULE MODULE(intrman) EXPORT_D( 4, RegisterIntrHandler) diff --git a/pcsx2/IopBios.h b/pcsx2/IopBios.h index e433e8f4ecefcf..92110cf217b1fc 100644 --- a/pcsx2/IopBios.h +++ b/pcsx2/IopBios.h @@ -71,6 +71,7 @@ typedef void (*irxDEBUG)(); namespace R3000A { + u32 irxFindLoadcore(u32 entrypc); u32 irxImportTableAddr(u32 entrypc); const char* irxImportFuncname(const std::string& libname, u16 index); irxHLE irxImportHLE(const std::string& libnam, u16 index); diff --git a/pcsx2/R3000AInterpreter.cpp b/pcsx2/R3000AInterpreter.cpp index 5c6af6d27dce9d..93677002b96b69 100644 --- a/pcsx2/R3000AInterpreter.cpp +++ b/pcsx2/R3000AInterpreter.cpp @@ -249,6 +249,11 @@ static void doBranch(s32 tar) { if (tar == 0x0) DevCon.Warning("[R3000 Interpreter] Warning: Branch to 0x0!"); + if(static_cast(tar) == 0xBFC4A000) + { + R3000SymbolMap.ClearModules(); + } + branch2 = iopIsDelaySlot = true; branchPC = tar; execI(); diff --git a/pcsx2/x86/iR3000A.cpp b/pcsx2/x86/iR3000A.cpp index 766262f5149dd9..caf749d5870c2e 100644 --- a/pcsx2/x86/iR3000A.cpp +++ b/pcsx2/x86/iR3000A.cpp @@ -1537,6 +1537,11 @@ static void iopRecRecompile(const u32 startpc) u32 i; u32 willbranch3 = 0; + if(startpc == 0xBFC4A000) + { + R3000SymbolMap.ClearModules(); + } + // Inject IRX hack if (startpc == 0x1630 && EmuConfig.CurrentIRX.length() > 3) {