Files
PS2Recomp/ps2xIOP/src/iop_subsystem.cpp
2026-09-19 21:17:58 -03:00

278 lines
8.8 KiB
C++

#include "ps2x/iop/iop_subsystem.h"
#include "iop_service.h"
#include "iop_module_manager.h"
#include "emulator/iop_emulator.h"
#include "module_factories.h"
#include "ps2x/iop/ps2_path.h"
#include <sstream>
#include <unordered_map>
#include <unordered_set>
#include <utility>
namespace ps2x::iop
{
class IopSubsystem::Impl
{
public:
explicit Impl(IopHost &hostRef)
: host(hostRef),
emulator(hostRef)
{
coreServices.emplace_back(detail::createMcservService(host));
coreServices.emplace_back(detail::createDbcmanService(host));
coreServices.emplace_back(detail::createLibSdService(host));
refreshServiceModuleKeys();
rebuildRoutes();
}
bool serviceActive(const detail::IopService &service) const
{
return moduleManager.isLoaded(service.moduleAliases());
}
void refreshServiceModuleKeys()
{
std::vector<std::string> keys;
for (const auto &service : coreServices)
{
for (std::string_view alias : service->moduleAliases())
keys.emplace_back(alias);
}
moduleManager.setServiceModuleKeys(std::move(keys));
}
void rebuildRoutes()
{
routes.clear();
lastError.clear();
for (const auto &service : coreServices)
{
if (!serviceActive(*service))
continue;
for (const uint32_t sid : service->sids())
{
if (!routes.emplace(sid, service.get()).second)
{
std::ostringstream out;
out << "duplicate IOP SID 0x" << std::hex << sid << " in core services";
lastError = out.str();
routes.clear();
return;
}
}
}
}
void recordLoadOutcome(std::string_view path, bool hle)
{
constexpr size_t maxOutcomes = 32u;
if (loadOutcomes.size() >= maxOutcomes || !loggedLoadPaths.emplace(path).second)
return;
std::string message = hle ? "[IOP:HLE] fallback module='" : "[IOP:load-failed] module='";
message.append(path);
message += hle ? "' physical IRX unavailable; using registered HLE provider"
: "' no HLE provider accepted the module; physical IRX was not loaded";
loadOutcomes.push_back(message);
host.log(hle ? LogLevel::Info : LogLevel::Warning, message);
}
IopHost &host;
detail::ServiceList coreServices;
std::unordered_map<uint32_t, detail::IopService *> routes;
std::vector<std::string> loadOutcomes;
std::unordered_set<std::string> loggedLoadPaths;
std::string lastError;
detail::IopModuleManager moduleManager;
detail::IopEmulator emulator;
};
IopSubsystem::IopSubsystem(IopHost &host)
: m_impl(std::make_unique<Impl>(host))
{
}
IopSubsystem::~IopSubsystem() = default;
IopSubsystem::IopSubsystem(IopSubsystem &&) noexcept = default;
IopSubsystem &IopSubsystem::operator=(IopSubsystem &&) noexcept = default;
void IopSubsystem::reset()
{
m_impl->moduleManager.reset();
m_impl->loadOutcomes.clear();
m_impl->loggedLoadPaths.clear();
for (auto &service : m_impl->coreServices)
{
if (service)
{
service->reset();
}
}
m_impl->emulator.reset();
m_impl->refreshServiceModuleKeys();
m_impl->rebuildRoutes();
}
ModuleLoadResult IopSubsystem::loadModule(std::string_view path, const void *arguments, uint32_t argumentSize)
{
const ParsedPs2Path parsed = parsePs2Path(path);
if (!parsed)
return {true, -1, -1};
if (parsed.device != Ps2PathDevice::Rom0)
{
ModuleLoadResult physical = m_impl->emulator.loadModule(path, arguments, argumentSize);
if (physical.moduleId > 0)
{
m_impl->moduleManager.observePhysicalLoad(physical.moduleId, path);
m_impl->rebuildRoutes();
return physical;
}
}
ModuleLoadResult hle = m_impl->moduleManager.loadHle(path);
if (hle.moduleId > 0)
{
m_impl->rebuildRoutes();
if (parsed.device != Ps2PathDevice::Rom0)
m_impl->recordLoadOutcome(path, true);
}
else
{
m_impl->recordLoadOutcome(path, false);
}
return hle;
}
ModuleLoadResult IopSubsystem::loadModuleBuffer(uint32_t guestAddress, const void *arguments, uint32_t argumentSize)
{
return m_impl->emulator.loadModuleBuffer(guestAddress, arguments, argumentSize);
}
bool IopSubsystem::stopModule(int32_t moduleId, int32_t *result)
{
if (m_impl->moduleManager.stopHle(moduleId, result))
{
m_impl->rebuildRoutes();
return true;
}
if (!m_impl->emulator.stopModule(moduleId, result))
return false;
m_impl->moduleManager.observePhysicalStop(moduleId);
m_impl->rebuildRoutes();
return true;
}
void IopSubsystem::runEeCycles(uint64_t eeCycles) noexcept
{
m_impl->emulator.runEeCycles(eeCycles);
}
RpcAbi IopSubsystem::selectRpcAbi(const RpcAbiRequest &request) const
{
for (const auto &service : m_impl->coreServices)
{
if (service && m_impl->serviceActive(*service))
{
const RpcAbi selected = service->selectRpcAbi(request);
if (selected != RpcAbi::RuntimeDefault)
{
return selected;
}
}
}
return RpcAbi::RuntimeDefault;
}
bool IopSubsystem::canBindRpc(uint32_t sid) const noexcept
{
if (m_impl->routes.find(sid) != m_impl->routes.end())
{
return true;
}
return m_impl->emulator.hasRpcServer(sid);
}
RpcResult IopSubsystem::handleRpc(const RpcRequest &request)
{
const auto route = m_impl->routes.find(request.sid);
detail::IopService *hle = route != m_impl->routes.end() ? route->second : nullptr;
RpcResult emulated = m_impl->emulator.handleRpc(request);
if (emulated.handled || !hle)
{
return emulated;
}
return hle->handleRpc(request);
}
void IopSubsystem::onSifTransfer(const SifTransfer &transfer)
{
for (auto &service : m_impl->coreServices)
{
if (service && m_impl->serviceActive(*service))
{
service->onSifTransfer(transfer);
}
}
m_impl->emulator.onSifTransfer(transfer);
}
uint32_t IopSubsystem::allocateMemory(uint32_t size, uint32_t alignment)
{
return m_impl->emulator.allocateMemory(size, alignment);
}
bool IopSubsystem::freeMemory(uint32_t address)
{
return m_impl->emulator.freeMemory(address);
}
bool IopSubsystem::readMemory(uint32_t address, void *destination, size_t size) const
{
return m_impl->emulator.readMemory(address, destination, size);
}
bool IopSubsystem::writeMemory(uint32_t address, const void *source, size_t size)
{
return m_impl->emulator.writeMemory(address, source, size);
}
bool IopSubsystem::zeroMemory(uint32_t address, size_t size)
{
return m_impl->emulator.zeroMemory(address, size);
}
bool IopSubsystem::isMemoryRange(uint32_t address, size_t size) const
{
return m_impl->emulator.isMemoryRange(address, size);
}
DebugSnapshot IopSubsystem::debugSnapshot() const
{
DebugSnapshot snapshot;
snapshot.emulatorCycles = m_impl->emulator.cycles();
snapshot.emulatorInstructions = m_impl->emulator.instructions();
snapshot.emulatorLoadedModules = m_impl->emulator.loadedModuleCount();
snapshot.emulatorThreads = m_impl->emulator.threadCount();
snapshot.emulatorRpcServers = m_impl->emulator.rpcServerCount();
snapshot.diagnostics = m_impl->loadOutcomes;
if (!m_impl->lastError.empty())
{
snapshot.diagnostics.push_back(m_impl->lastError);
}
for (const auto &service : m_impl->coreServices)
{
DebugService row;
row.name = service->name();
row.sids.assign(service->sids().begin(), service->sids().end());
row.active = m_impl->serviceActive(*service);
service->appendDebugMetrics(row.metrics);
snapshot.services.push_back(std::move(row));
}
return snapshot;
}
}