mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-30 10:14:24 -04:00
feat: IOP emulator
refactor: codegen to catch callbacks on mips code feat: added a lot of entries or IOP emulator
This commit is contained in:
@@ -391,6 +391,7 @@ void EeScheduler::accountCycles(uint32_t cycles) noexcept
|
||||
const uint64_t elapsed = std::max<uint64_t>(1u, cycles);
|
||||
m_eeCycle += elapsed;
|
||||
m_pendingEeTimerInterrupts |= m_runtime.memory().advanceEeTimers(elapsed);
|
||||
m_runtime.advanceIopEeCycles(elapsed);
|
||||
if (m_pendingEeTimerInterrupts != 0u)
|
||||
{
|
||||
m_checkpointPending.store(true, std::memory_order_release);
|
||||
@@ -1278,7 +1279,7 @@ void EeScheduler::dispatchIrq(bool dmac, uint32_t cause)
|
||||
SET_GPR_U32(&invocation.context, 4, cause);
|
||||
SET_GPR_U32(&invocation.context, 5, handler.argument);
|
||||
SET_GPR_U32(&invocation.context, 28, handler.gp);
|
||||
SET_GPR_U32(&invocation.context, 29, handler.sp);
|
||||
SET_GPR_U32(&invocation.context, 29, 0u);
|
||||
SET_GPR_U32(&invocation.context, 31, 0u);
|
||||
queueInvocation(std::move(invocation));
|
||||
}
|
||||
@@ -1918,7 +1919,7 @@ void EeScheduler::processEvent(const EeEvent &event)
|
||||
SET_GPR_U32(&invocation.context, 5, static_cast<uint32_t>(alarm.ticks));
|
||||
SET_GPR_U32(&invocation.context, 6, alarm.argument);
|
||||
SET_GPR_U32(&invocation.context, 28, alarm.gp);
|
||||
SET_GPR_U32(&invocation.context, 29, alarm.sp);
|
||||
SET_GPR_U32(&invocation.context, 29, 0u);
|
||||
SET_GPR_U32(&invocation.context, 31, 0u);
|
||||
queueInvocation(std::move(invocation));
|
||||
break;
|
||||
|
||||
@@ -709,6 +709,7 @@ namespace ps2_stubs
|
||||
|
||||
void sceSifRebootIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
PS2IopTransport::reset(runtime);
|
||||
setReturnS32(ctx, 1);
|
||||
}
|
||||
|
||||
@@ -737,6 +738,7 @@ namespace ps2_stubs
|
||||
|
||||
void sceSifResetIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
PS2IopTransport::reset(runtime);
|
||||
setReturnS32(ctx, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,36 @@ namespace
|
||||
return hash;
|
||||
}
|
||||
|
||||
bool copyGuestBytesBounded(const uint8_t *rdram,
|
||||
uint32_t guestAddr,
|
||||
uint32_t byteCount,
|
||||
uint32_t maxBytes,
|
||||
std::vector<uint8_t> &out)
|
||||
{
|
||||
out.clear();
|
||||
if (byteCount == 0u)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!rdram || guestAddr == 0u || byteCount > maxBytes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
out.resize(byteCount);
|
||||
for (uint32_t i = 0; i < byteCount; ++i)
|
||||
{
|
||||
const uint8_t *src = getConstMemPtr(rdram, guestAddr + i);
|
||||
if (!src)
|
||||
{
|
||||
out.clear();
|
||||
return false;
|
||||
}
|
||||
out[i] = *src;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string makeSifModuleBufferTag(const uint8_t *rdram, uint32_t bufferAddr)
|
||||
{
|
||||
char key[96] = {};
|
||||
@@ -121,6 +151,68 @@ namespace
|
||||
return moduleId;
|
||||
}
|
||||
|
||||
int32_t trackSifModuleLoadExternal(const std::string &path, int32_t moduleId)
|
||||
{
|
||||
if (path.empty() || moduleId <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
const std::string pathKey = normalizeSifModulePathKey(path);
|
||||
if (pathKey.empty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(g_sif_module_mutex);
|
||||
|
||||
auto idIt = g_sif_modules_by_id.find(moduleId);
|
||||
if (idIt != g_sif_modules_by_id.end())
|
||||
{
|
||||
SifModuleRecord &record = idIt->second;
|
||||
if (record.pathKey == pathKey)
|
||||
{
|
||||
record.loaded = true;
|
||||
++record.refCount;
|
||||
return moduleId;
|
||||
}
|
||||
|
||||
if (!record.pathKey.empty())
|
||||
{
|
||||
auto oldPathIt = g_sif_module_id_by_path.find(record.pathKey);
|
||||
if (oldPathIt != g_sif_module_id_by_path.end() && oldPathIt->second == moduleId)
|
||||
{
|
||||
g_sif_module_id_by_path.erase(oldPathIt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto pathIt = g_sif_module_id_by_path.find(pathKey);
|
||||
if (pathIt != g_sif_module_id_by_path.end() && pathIt->second != moduleId)
|
||||
{
|
||||
auto oldIt = g_sif_modules_by_id.find(pathIt->second);
|
||||
if (oldIt != g_sif_modules_by_id.end())
|
||||
{
|
||||
oldIt->second.loaded = false;
|
||||
oldIt->second.refCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
SifModuleRecord record;
|
||||
record.id = moduleId;
|
||||
record.path = path;
|
||||
record.pathKey = pathKey;
|
||||
record.refCount = 1;
|
||||
record.loaded = true;
|
||||
g_sif_module_id_by_path[pathKey] = moduleId;
|
||||
g_sif_modules_by_id[moduleId] = std::move(record);
|
||||
if (moduleId >= g_next_sif_module_id)
|
||||
{
|
||||
g_next_sif_module_id = moduleId + 1;
|
||||
}
|
||||
return moduleId;
|
||||
}
|
||||
|
||||
bool trackSifModuleStop(int32_t moduleId, uint32_t *remainingRefs = nullptr)
|
||||
{
|
||||
if (moduleId <= 0)
|
||||
|
||||
@@ -155,20 +155,22 @@ namespace ps2_syscalls
|
||||
const int32_t moduleId = static_cast<int32_t>(getRegU32(ctx, 4)); // $a0
|
||||
const uint32_t resultAddr = getRegU32(ctx, 7); // $a3 (int* result, optional)
|
||||
|
||||
int32_t moduleResult = -1;
|
||||
const bool stoppedByEmulator = runtime->stopIopModule(moduleId, &moduleResult);
|
||||
uint32_t refsLeft = 0;
|
||||
const bool knownModule = trackSifModuleStop(moduleId, &refsLeft);
|
||||
const int32_t ret = knownModule ? 0 : -1;
|
||||
const int32_t ret = (stoppedByEmulator || knownModule) ? 0 : -1;
|
||||
|
||||
if (resultAddr != 0)
|
||||
{
|
||||
int32_t *hostResult = reinterpret_cast<int32_t *>(getMemPtr(rdram, resultAddr));
|
||||
if (hostResult)
|
||||
{
|
||||
*hostResult = knownModule ? 0 : -1;
|
||||
*hostResult = stoppedByEmulator ? moduleResult : (knownModule ? 0 : -1);
|
||||
}
|
||||
}
|
||||
|
||||
if (knownModule)
|
||||
if (stoppedByEmulator || knownModule)
|
||||
{
|
||||
std::string modulePath;
|
||||
{
|
||||
@@ -179,7 +181,7 @@ namespace ps2_syscalls
|
||||
modulePath = it->second.path;
|
||||
}
|
||||
}
|
||||
logSifModuleAction("stop", moduleId, modulePath, refsLeft);
|
||||
logSifModuleAction(stoppedByEmulator ? "stop-emulated" : "stop", moduleId, modulePath, refsLeft);
|
||||
}
|
||||
|
||||
setReturnS32(ctx, ret);
|
||||
@@ -187,7 +189,9 @@ namespace ps2_syscalls
|
||||
|
||||
void SifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const uint32_t pathAddr = getRegU32(ctx, 4); // $a0
|
||||
const uint32_t pathAddr = getRegU32(ctx, 4); // $a0
|
||||
const uint32_t argumentSize = getRegU32(ctx, 5); // $a1
|
||||
const uint32_t argumentAddr = getRegU32(ctx, 6); // $a2
|
||||
const std::string modulePath = readGuestCStringBounded(rdram, pathAddr, kMaxSifModulePathBytes);
|
||||
if (modulePath.empty())
|
||||
{
|
||||
@@ -195,6 +199,29 @@ namespace ps2_syscalls
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> arguments;
|
||||
constexpr uint32_t kMaxIopModuleArguments = 64u * 1024u;
|
||||
if (!copyGuestBytesBounded(rdram, argumentAddr, argumentSize, kMaxIopModuleArguments, arguments))
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto emulated = runtime->loadIopModule(modulePath, arguments.empty() ? nullptr : arguments.data(), static_cast<uint32_t>(arguments.size()));
|
||||
if (emulated.handled)
|
||||
{
|
||||
if (emulated.moduleId <= 0)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
trackSifModuleLoadExternal(modulePath, emulated.moduleId);
|
||||
logSifModuleAction("load-emulated", emulated.moduleId, modulePath, 1u);
|
||||
setReturnS32(ctx, emulated.moduleId);
|
||||
return;
|
||||
}
|
||||
|
||||
const int32_t moduleId = trackSifModuleLoad(modulePath);
|
||||
if (moduleId <= 0)
|
||||
{
|
||||
@@ -219,10 +246,6 @@ namespace ps2_syscalls
|
||||
void SifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_rpc_mutex);
|
||||
if (runtime)
|
||||
{
|
||||
PS2IopTransport::reset(runtime);
|
||||
}
|
||||
if (!g_rpc_initialized)
|
||||
{
|
||||
g_rpc_servers.clear();
|
||||
@@ -290,9 +313,12 @@ namespace ps2_syscalls
|
||||
g_rpc_clients[clientPtr].sid = rpcId;
|
||||
}
|
||||
|
||||
if (!serverPtr)
|
||||
if (!serverPtr && PS2IopTransport::canBindRpc(runtime, rpcId))
|
||||
{
|
||||
// Allocate a dummy server so bind loops can proceed.
|
||||
// EE-side servers and HLE routes need a descriptor in guest RAM.
|
||||
// With an emulated IOP, only publish it after the IRX has actually
|
||||
// registered the SID; cd->server == nullptr is the SDK's retry
|
||||
// signal while the IOP server thread is still starting.
|
||||
serverPtr = rpcAllocServerAddr(rdram);
|
||||
if (serverPtr)
|
||||
{
|
||||
|
||||
@@ -313,15 +313,39 @@ namespace ps2_syscalls
|
||||
|
||||
void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const uint32_t bufferAddr = getRegU32(ctx, 4); // $a0
|
||||
const uint32_t bufferAddr = getRegU32(ctx, 4); // $a0
|
||||
const uint32_t argumentSize = getRegU32(ctx, 5); // $a1
|
||||
const uint32_t argumentAddr = getRegU32(ctx, 6); // $a2
|
||||
if (!rdram || bufferAddr == 0u)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Match buffer-based module loads to stable synthetic tags so module ID lookup remains deterministic.
|
||||
const std::string moduleTag = makeSifModuleBufferTag(rdram, bufferAddr);
|
||||
std::vector<uint8_t> arguments;
|
||||
constexpr uint32_t kMaxIopModuleArguments = 64u * 1024u;
|
||||
if (!copyGuestBytesBounded(rdram, argumentAddr, argumentSize, kMaxIopModuleArguments, arguments))
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto emulated = runtime->loadIopModuleBuffer(bufferAddr, arguments.empty() ? nullptr : arguments.data(), static_cast<uint32_t>(arguments.size()));
|
||||
if (emulated.handled)
|
||||
{
|
||||
if (emulated.moduleId <= 0)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
return;
|
||||
}
|
||||
trackSifModuleLoadExternal(moduleTag, emulated.moduleId);
|
||||
logSifModuleAction("load-buffer-emulated", emulated.moduleId, moduleTag, 1u);
|
||||
setReturnS32(ctx, emulated.moduleId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Profile mode keeps the existing deterministic synthetic IDs.
|
||||
const int32_t moduleId = trackSifModuleLoad(moduleTag);
|
||||
if (moduleId <= 0)
|
||||
{
|
||||
|
||||
@@ -37,6 +37,11 @@ public:
|
||||
: ps2x::iop::RpcResult{};
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool canBindRpc(const PS2Runtime *runtime, uint32_t sid)
|
||||
{
|
||||
return !runtime || runtime->canBindIopRpc(sid);
|
||||
}
|
||||
|
||||
static void notifyTransfer(
|
||||
PS2Runtime *runtime,
|
||||
uint8_t *rdram,
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <chrono>
|
||||
@@ -481,6 +480,7 @@ PS2Runtime::PS2Runtime()
|
||||
{
|
||||
m_iopHost = std::make_unique<PS2IopHostAdapter>(*this);
|
||||
m_iopSubsystem = std::make_unique<ps2x::iop::IopSubsystem>(*m_iopHost);
|
||||
|
||||
m_eeScheduler = std::make_unique<EeScheduler>(*this);
|
||||
#if defined(PS2X_IOP_ENABLE_PLUGINS) && PS2X_IOP_ENABLE_PLUGINS && \
|
||||
!defined(PLATFORM_VITA) && (defined(_WIN32) || defined(__linux__))
|
||||
@@ -574,11 +574,34 @@ void PS2Runtime::setIopPluginSearchPaths(std::vector<std::filesystem::path> path
|
||||
m_iopSubsystem->setPluginSearchPaths(std::move(paths));
|
||||
}
|
||||
|
||||
ps2x::iop::ModuleLoadResult PS2Runtime::loadIopModule(std::string_view path, const void *arguments, uint32_t argumentSize)
|
||||
{
|
||||
auto scope = m_iopHost->enterCall(nullptr, m_memory.getRDRAM());
|
||||
return m_iopSubsystem->loadModule(path, arguments, argumentSize);
|
||||
}
|
||||
|
||||
ps2x::iop::ModuleLoadResult PS2Runtime::loadIopModuleBuffer(uint32_t guestAddress, const void *arguments, uint32_t argumentSize)
|
||||
{
|
||||
auto scope = m_iopHost->enterCall(nullptr, m_memory.getRDRAM());
|
||||
return m_iopSubsystem->loadModuleBuffer(guestAddress, arguments, argumentSize);
|
||||
}
|
||||
|
||||
bool PS2Runtime::stopIopModule(int32_t moduleId, int32_t *result)
|
||||
{
|
||||
auto scope = m_iopHost->enterCall(nullptr, m_memory.getRDRAM());
|
||||
return m_iopSubsystem->stopModule(moduleId, result);
|
||||
}
|
||||
|
||||
ps2x::iop::RpcAbi PS2Runtime::selectIopRpcAbi(const ps2x::iop::RpcAbiRequest &request) const
|
||||
{
|
||||
return m_iopSubsystem->selectRpcAbi(request);
|
||||
}
|
||||
|
||||
bool PS2Runtime::canBindIopRpc(uint32_t sid) const noexcept
|
||||
{
|
||||
return m_iopSubsystem->canBindRpc(sid);
|
||||
}
|
||||
|
||||
ps2x::iop::RpcResult PS2Runtime::handleIopRpc(uint8_t *rdram, R5900Context *ctx, ps2x::iop::RpcRequest request)
|
||||
{
|
||||
auto scope = m_iopHost->enterCall(ctx, rdram);
|
||||
@@ -592,6 +615,11 @@ void PS2Runtime::notifyIopSifTransfer(uint8_t *rdram, const ps2x::iop::SifTransf
|
||||
m_iopSubsystem->onSifTransfer(transfer);
|
||||
}
|
||||
|
||||
void PS2Runtime::advanceIopEeCycles(uint64_t eeCycles) noexcept
|
||||
{
|
||||
m_iopSubsystem->runEeCycles(eeCycles);
|
||||
}
|
||||
|
||||
void PS2Runtime::resetIop()
|
||||
{
|
||||
m_iopSubsystem->reset();
|
||||
|
||||
Reference in New Issue
Block a user