Feature/random fixes platform support (#179)

* feat: implement memory card IOP

* feat: IOP trace

* feat: move IOP logic to ps2xIOP
refactor: small refactor on audio api on runtime

* feat: android support
feat: prevent race on GS
feat: a bit cleanup and reimplement on memory card

* feat: finish guest thread
feat: android build support
feat: vita build support with suspicious setup scripts

* feat: remove idea from track
This commit is contained in:
Ranieri
2026-07-22 02:37:53 -03:00
committed by GitHub
parent 1176609890
commit f3687c5ae6
40 changed files with 1133 additions and 146 deletions
+33 -20
View File
@@ -706,30 +706,42 @@ namespace ps2_stubs
}
uint32_t steps = 0u;
while (callbackCtx.pc != 0u && !runtime->isStopRequested() && steps < 1024u)
bool reschedulePending = false;
uint64_t handoffBaseline = 0u;
{
if (!runtime->hasFunction(callbackCtx.pc))
{
if (g_gs_sync_v_callback_bad_pc_logs < 16u)
{
std::cerr << "[sceGsSyncVCallback:bad-pc] pc=0x" << std::hex << callbackCtx.pc
<< " ra=0x" << getRegU32(&callbackCtx, 31)
<< " sp=0x" << getRegU32(&callbackCtx, 29)
<< " gp=0x" << getRegU32(&callbackCtx, 28)
<< std::dec << std::endl;
++g_gs_sync_v_callback_bad_pc_logs;
}
callbackCtx.pc = 0u;
break;
}
PS2Runtime::GuestExecutionScope guestExecution(runtime);
PS2Runtime::DeferredGuestYieldScope deferYield(reschedulePending);
auto step = runtime->lookupFunction(callbackCtx.pc);
if (!step)
while (callbackCtx.pc != 0u && !runtime->isStopRequested() && steps < 1024u)
{
break;
if (!runtime->hasFunction(callbackCtx.pc))
{
if (g_gs_sync_v_callback_bad_pc_logs < 16u)
{
std::cerr << "[sceGsSyncVCallback:bad-pc] pc=0x" << std::hex << callbackCtx.pc
<< " ra=0x" << getRegU32(&callbackCtx, 31)
<< " sp=0x" << getRegU32(&callbackCtx, 29)
<< " gp=0x" << getRegU32(&callbackCtx, 28)
<< std::dec << std::endl;
++g_gs_sync_v_callback_bad_pc_logs;
}
callbackCtx.pc = 0u;
break;
}
auto step = runtime->lookupFunction(callbackCtx.pc);
if (!step)
{
break;
}
++steps;
step(rdram, &callbackCtx, runtime);
}
++steps;
step(rdram, &callbackCtx, runtime);
handoffBaseline = runtime->guestExecutionHandoffEpochSnapshot();
}
if (reschedulePending && !runtime->isStopRequested())
{
runtime->waitForGuestExecutionHandoff(handoffBaseline);
}
if (shouldLogDispatch)
@@ -909,6 +921,7 @@ namespace ps2_stubs
mem.writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0);
mem.processPendingTransfers();
ps2TraceGuestRangeWrite(rdram, dstAddr, totalImageBytes, "sceGsExecStoreImage", ctx);
runtime->gs().consumeLocalToHostBytes(dst, totalImageBytes);
runtime->guestFree(pktAddr);
+59 -19
View File
@@ -1,6 +1,11 @@
#include "Common.h"
#include "MPEG.h"
#if !defined(PS2X_HAS_FFMPEG)
#define PS2X_HAS_FFMPEG 1
#endif
#if PS2X_HAS_FFMPEG
extern "C"
{
#include <libavcodec/avcodec.h>
@@ -8,6 +13,7 @@ extern "C"
#include <libavutil/log.h>
#include <libswscale/swscale.h>
}
#endif
#include <chrono>
#include <condition_variable>
@@ -27,6 +33,7 @@ namespace ps2_stubs
std::vector<uint8_t> rgba;
};
#if PS2X_HAS_FFMPEG
std::string ffmpegErrorString(int err)
{
std::array<char, AV_ERROR_MAX_STRING_SIZE> buffer{};
@@ -419,6 +426,30 @@ namespace ps2_stubs
bool m_drained = false;
bool m_seenKeyframe = false;
};
#else
// TODO
class MpegFfmpegDecoder
{
public:
bool feed(const uint8_t *, size_t, std::deque<MpegDecodedFrame> &)
{
static bool s_warnedNoFfmpeg = false;
if (!s_warnedNoFfmpeg)
{
std::cerr << "[MPEG] runtime built without FFmpeg; MPEG video decode is disabled." << std::endl;
s_warnedNoFfmpeg = true;
}
return false;
}
bool flush(std::deque<MpegDecodedFrame> &)
{
return true;
}
void reset() {}
};
#endif
struct MpegRegisteredCallback
{
@@ -1316,33 +1347,42 @@ namespace ps2_stubs
callbackCtx.pc = callback.func;
uint32_t steps = 0u;
while (callbackCtx.pc != 0u && !runtime->isStopRequested() && steps < kMpegCallbackMaxSteps)
bool reschedulePending = false;
uint64_t handoffBaseline = 0u;
{
if (!runtime->hasFunction(callbackCtx.pc))
PS2Runtime::GuestExecutionScope guestExecution(runtime);
PS2Runtime::DeferredGuestYieldScope deferYield(reschedulePending);
while (callbackCtx.pc != 0u && !runtime->isStopRequested() && steps < kMpegCallbackMaxSteps)
{
static uint32_t badPcLogCount = 0u;
if (badPcLogCount < 16u)
if (!runtime->hasFunction(callbackCtx.pc))
{
std::cerr << "[MPEG:callback:bad-pc] cb=0x" << std::hex << callback.func
<< " pc=0x" << callbackCtx.pc
<< " ra=0x" << getRegU32(&callbackCtx, 31)
<< std::dec << std::endl;
++badPcLogCount;
static uint32_t badPcLogCount = 0u;
if (badPcLogCount < 16u)
{
std::cerr << "[MPEG:callback:bad-pc] cb=0x" << std::hex << callback.func
<< " pc=0x" << callbackCtx.pc
<< " ra=0x" << getRegU32(&callbackCtx, 31)
<< std::dec << std::endl;
++badPcLogCount;
}
break;
}
break;
}
PS2Runtime::RecompiledFunction step = runtime->lookupFunction(callbackCtx.pc);
if (!step)
{
break;
}
PS2Runtime::RecompiledFunction step = runtime->lookupFunction(callbackCtx.pc);
if (!step)
{
break;
}
{
PS2Runtime::GuestExecutionScope guestExecution(runtime);
step(rdram, &callbackCtx, runtime);
++steps;
}
++steps;
handoffBaseline = runtime->guestExecutionHandoffEpochSnapshot();
}
if (reschedulePending && !runtime->isStopRequested())
{
runtime->waitForGuestExecutionHandoff(handoffBaseline);
}
if (steps >= kMpegCallbackMaxSteps)
+54 -17
View File
@@ -85,14 +85,27 @@ namespace ps2_stubs
std::mutex g_mcStateMutex;
int32_t g_mcNextFd = 1;
int32_t g_mcLastCmd = 0;
bool g_mcCommandPending = false;
int32_t g_mcLastResult = 0;
std::unordered_map<int32_t, McOpenFile> g_mcFiles;
std::array<McPortState, 2> g_mcPorts{};
int32_t g_cvMcFileCursor = 0;
constexpr int32_t kCvMcFreeCapacityBytes = 0x01000000;
constexpr int32_t kCvMcSaveCapacityBytes = 0x00080000;
constexpr int32_t kCvMcConfigCapacityBytes = 0x00008000;
constexpr int32_t kCvMcIconCapacityBytes = 0x00004000;
constexpr int32_t kCvMcSaveFileBytes = 0x838;
constexpr int32_t kCvMcConfigFileBytes = 0x34;
constexpr int32_t kCvMcIconInfoBytes = 0x3C4;
constexpr int32_t kCvMcIconFileBytes = 0xB3F8;
constexpr int32_t cvMcKilobytes(int32_t bytes)
{
return (bytes + 1023) / 1024;
}
constexpr int32_t kCvMcRequiredFreeKb =
cvMcKilobytes(kCvMcSaveFileBytes) * 15 +
cvMcKilobytes(kCvMcConfigFileBytes) +
cvMcKilobytes(kCvMcIconInfoBytes) +
cvMcKilobytes(kCvMcIconFileBytes) + 11;
bool isValidMcPortSlot(int32_t port, int32_t slot)
{
@@ -351,6 +364,7 @@ namespace ps2_stubs
{
g_mcLastCmd = cmd;
g_mcLastResult = result;
g_mcCommandPending = true;
}
void closeMcFilesLocked()
@@ -464,7 +478,6 @@ namespace ps2_stubs
}
}
MemoryCardDebugSnapshot getMemoryCardDebugSnapshot()
{
MemoryCardDebugSnapshot snapshot{};
@@ -492,9 +505,7 @@ namespace ps2_stubs
snapshot.openFiles.push_back(std::move(row));
}
std::sort(snapshot.openFiles.begin(), snapshot.openFiles.end(), [](const MemoryCardDebugOpenFile &a, const MemoryCardDebugOpenFile &b)
{
return a.fd < b.fd;
});
{ return a.fd < b.fd; });
return snapshot;
}
@@ -544,6 +555,8 @@ namespace ps2_stubs
setMcCommandResultLocked(kMcCmdChdir, result);
}
RUNTIME_LOG("[MC] Chdir port=" << port << " '" << requestedDir
<< "' -> result=" << result << " cwd='" << currentDir << "'");
writeMcCString(rdram, currentDirAddr, currentDir);
setReturnS32(ctx, 0);
}
@@ -624,6 +637,7 @@ namespace ps2_stubs
g_mcNextFd = 1;
g_mcLastCmd = 0;
g_mcLastResult = 0;
g_mcCommandPending = false;
for (McPortState &state : g_mcPorts)
{
state.currentDir = "/";
@@ -685,8 +699,8 @@ namespace ps2_stubs
const int32_t port = static_cast<int32_t>(getRegU32(ctx, 4));
const int32_t slot = static_cast<int32_t>(getRegU32(ctx, 5));
const std::string rawPath = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen);
const int32_t maxEntries = static_cast<int32_t>(readStackU32(rdram, ctx, 16));
const uint32_t tableAddr = readStackU32(rdram, ctx, 20);
const int32_t maxEntries = static_cast<int32_t>(getRegU32(ctx, 8));
const uint32_t tableAddr = getRegU32(ctx, 9);
std::vector<SceMcTblGetDir> entries;
int32_t result = kMcResultNoEntry;
@@ -828,12 +842,18 @@ namespace ps2_stubs
setMcCommandResultLocked(kMcCmdGetDir, result);
}
RUNTIME_LOG("[MC] GetDir port=" << port << " '" << rawPath
<< "' maxent=" << maxEntries << " -> result=" << result);
setReturnS32(ctx, 0);
}
void sceMcGetEntSpace(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, 1024);
{
std::lock_guard<std::mutex> lock(g_mcStateMutex);
setMcCommandResultLocked(kMcCmdGetEntSpace, 1024);
}
setReturnS32(ctx, 0);
}
void sceMcGetInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
@@ -842,7 +862,7 @@ namespace ps2_stubs
const int32_t slot = static_cast<int32_t>(getRegU32(ctx, 5));
const uint32_t typePtr = getRegU32(ctx, 6);
const uint32_t freePtr = getRegU32(ctx, 7);
const uint32_t formatPtr = readStackU32(rdram, ctx, 16);
const uint32_t formatPtr = getRegU32(ctx, 8);
int32_t cardType = 0;
int32_t freeBlocks = 0;
@@ -885,6 +905,9 @@ namespace ps2_stubs
}
}
RUNTIME_LOG("[MC] GetInfo port=" << port << " type=" << cardType
<< " free=" << freeBlocks << " format=" << format
<< " result=" << result);
setReturnS32(ctx, 0);
}
@@ -901,6 +924,7 @@ namespace ps2_stubs
g_mcNextFd = 1;
g_mcLastCmd = 0;
g_mcLastResult = 0;
g_mcCommandPending = false;
for (McPortState &state : g_mcPorts)
{
state.currentDir = "/";
@@ -1172,12 +1196,25 @@ namespace ps2_stubs
const uint32_t resultPtr = getRegU32(ctx, 6);
int32_t cmd = 0;
int32_t result = 0;
bool hadPending = false;
{
std::lock_guard<std::mutex> lock(g_mcStateMutex);
hadPending = g_mcCommandPending;
g_mcCommandPending = false;
cmd = g_mcLastCmd;
result = g_mcLastResult;
}
// libmc semantics: -1 means no async operation was executing; games rely
// on it to tell idle polling apart from command completion.
if (!hadPending)
{
setReturnS32(ctx, -1);
return;
}
RUNTIME_LOG("[MC] Sync cmd=" << cmd << " result=" << result);
if (cmdPtr != 0u)
{
if (uint8_t *out = getMemPtr(rdram, cmdPtr))
@@ -1366,7 +1403,7 @@ namespace ps2_stubs
void mcGetConfigCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, kCvMcConfigCapacityBytes);
setReturnS32(ctx, kCvMcConfigFileBytes);
}
void mcGetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
@@ -1376,17 +1413,17 @@ namespace ps2_stubs
void mcGetFreeCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, kCvMcFreeCapacityBytes);
setReturnS32(ctx, kCvMcRequiredFreeKb);
}
void mcGetIconCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, kCvMcIconCapacityBytes);
setReturnS32(ctx, kCvMcIconInfoBytes);
}
void mcGetIconFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, kCvMcIconCapacityBytes);
setReturnS32(ctx, kCvMcIconFileBytes);
}
void mcGetPortSelectDirInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
@@ -1396,7 +1433,7 @@ namespace ps2_stubs
void mcGetSaveFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
setReturnS32(ctx, kCvMcSaveCapacityBytes);
setReturnS32(ctx, kCvMcSaveFileBytes);
}
void mcGetStringEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
+2
View File
@@ -618,6 +618,7 @@ namespace ps2_stubs
portState->transientState = 0u;
if (dmaStr)
{
ps2TraceGuestRangeWrite(rdram, dmaAddr, 32u, "scePadPortOpen", ctx);
std::memset(dmaStr, 0, 32);
}
setReturnS32(ctx, 1);
@@ -635,6 +636,7 @@ namespace ps2_stubs
return;
}
ps2TraceGuestRangeWrite(rdram, dataAddr, 32u, "scePadRead", ctx);
if (!readPadPortData(port, slot, runtime, data, dataAddr))
{
setReturnS32(ctx, 0);
+2
View File
@@ -256,6 +256,8 @@ namespace ps2_stubs
return true;
}
ps2TraceGuestRangeWrite(rdram, dstAddr, sizeBytes, "sifCopyGuestByteRange", nullptr);
const uint64_t srcBegin = srcAddr;
const uint64_t srcEnd = srcBegin + static_cast<uint64_t>(sizeBytes);
const uint64_t dstBegin = dstAddr;
@@ -196,6 +196,10 @@ namespace ps2_syscalls
std::lock_guard<std::mutex> lock(g_sys_fd_mutex);
bytesRead = fread(hostBuf, 1, size, fp);
}
if (bytesRead > 0)
{
ps2TraceGuestRangeWrite(rdram, bufAddr, static_cast<uint32_t>(bytesRead), "fioRead", ctx);
}
if (bytesRead < size && ferror(fp))
{
@@ -258,6 +258,8 @@ static void rpcCopyToRdram(uint8_t *rdram, uint32_t dst, uint32_t src, size_t si
if (!rdram || size == 0)
return;
ps2TraceGuestRangeWrite(rdram, dst, static_cast<uint32_t>(size), "rpcCopyToRdram", nullptr);
constexpr size_t kMaxRpcTransferBytes = 1u * 1024u * 1024u;
const size_t clampedSize = std::min(size, kMaxRpcTransferBytes);
if (clampedSize != size)
@@ -292,6 +294,8 @@ static void rpcZeroRdram(uint8_t *rdram, uint32_t dst, size_t size)
if (!rdram || size == 0)
return;
ps2TraceGuestRangeWrite(rdram, dst, static_cast<uint32_t>(size), "rpcZeroRdram", nullptr);
constexpr size_t kMaxRpcTransferBytes = 1u * 1024u * 1024u;
const size_t clampedSize = std::min(size, kMaxRpcTransferBytes);
if (clampedSize != size)
@@ -2,6 +2,7 @@
#include <cstddef>
#include <cstdint>
#include <thread>
inline std::unordered_map<int, FILE *> g_fileDescriptors;
inline int g_nextFd = 3; // Start after stdin, stdout, stderr
@@ -11,6 +11,7 @@ namespace ps2_syscalls
constexpr uint32_t kIntcVblankEnd = 3u;
constexpr auto kVblankPeriod = std::chrono::microseconds(16667);
constexpr int kMaxCatchupTicks = 4;
constexpr uint32_t kMaxIrqHandlerSteps = 4096u;
std::mutex g_irq_handler_mutex;
std::mutex g_irq_worker_mutex;
@@ -168,16 +169,37 @@ namespace ps2_syscalls
SET_GPR_U32(&irqCtx, 7, 0u);
irqCtx.pc = info.handler;
while (irqCtx.pc != 0u && runtime && !runtime->isStopRequested())
bool reschedulePending = false;
uint64_t handoffBaseline = 0u;
uint32_t steps = 0u;
{
PS2Runtime::RecompiledFunction step = runtime->lookupFunction(irqCtx.pc);
if (!step)
PS2Runtime::GuestExecutionScope guestExecution(runtime);
PS2Runtime::DeferredGuestYieldScope deferYield(reschedulePending);
while (irqCtx.pc != 0u && runtime && !runtime->isStopRequested() && steps < kMaxIrqHandlerSteps)
{
break;
PS2Runtime::RecompiledFunction step = runtime->lookupFunction(irqCtx.pc);
if (!step)
{
break;
}
step(rdram, &irqCtx, runtime);
++steps;
}
// Interrupt handlers must be able to preempt a guest thread that is
// spinning on interrupt-produced state, such as a vblank counter.
step(rdram, &irqCtx, runtime);
handoffBaseline = runtime->guestExecutionHandoffEpochSnapshot();
}
if (steps >= kMaxIrqHandlerSteps)
{
static uint32_t s_stepLimitLogCount = 0u;
if (s_stepLimitLogCount < 16u)
{
std::cerr << "[INTC:step-limit] handler=0x" << std::hex << info.handler << " pc=0x" << irqCtx.pc << std::dec << std::endl;
++s_stepLimitLogCount;
}
}
if (reschedulePending && !runtime->isStopRequested())
{
runtime->waitForGuestExecutionHandoff(handoffBaseline);
}
}
catch (const ThreadExitException &)
@@ -252,14 +274,39 @@ namespace ps2_syscalls
SET_GPR_U32(&irqCtx, 7, 0u);
irqCtx.pc = info.handler;
while (irqCtx.pc != 0u && runtime && !runtime->isStopRequested())
bool reschedulePending = false;
uint64_t handoffBaseline = 0u;
uint32_t steps = 0u;
{
PS2Runtime::RecompiledFunction step = runtime->lookupFunction(irqCtx.pc);
if (!step)
PS2Runtime::GuestExecutionScope guestExecution(runtime);
PS2Runtime::DeferredGuestYieldScope deferYield(reschedulePending);
while (irqCtx.pc != 0u && runtime && !runtime->isStopRequested() &&
steps < kMaxIrqHandlerSteps)
{
break;
PS2Runtime::RecompiledFunction step = runtime->lookupFunction(irqCtx.pc);
if (!step)
{
break;
}
step(rdram, &irqCtx, runtime);
++steps;
}
step(rdram, &irqCtx, runtime);
handoffBaseline = runtime->guestExecutionHandoffEpochSnapshot();
}
if (steps >= kMaxIrqHandlerSteps)
{
static uint32_t s_stepLimitLogCount = 0u;
if (s_stepLimitLogCount < 16u)
{
std::cerr << "[DMAC:step-limit] handler=0x" << std::hex << info.handler
<< " pc=0x" << irqCtx.pc << std::dec << std::endl;
++s_stepLimitLogCount;
}
}
if (reschedulePending && !runtime->isStopRequested())
{
runtime->waitForGuestExecutionHandoff(handoffBaseline);
}
}
catch (const ThreadExitException &)
@@ -304,6 +351,7 @@ namespace ps2_syscalls
{
std::lock_guard<std::mutex> lock(g_vsync_flag_mutex);
reg = g_vsync_registration;
g_vsync_registration = {};
tickValue = ++g_vsync_tick_counter;
}
@@ -353,9 +401,20 @@ namespace ps2_syscalls
for (int i = 0; i < ticksToProcess; ++i)
{
const uint64_t tickValue = signalVSyncFlag(rdram, runtime);
ps2_stubs::dispatchGsSyncVCallback(rdram, runtime, tickValue);
dispatchIntcHandlersForCause(rdram, runtime, kIntcVblankStart);
bool reschedulePending = false;
uint64_t handoffBaseline = 0u;
{
PS2Runtime::GuestExecutionScope guestExecution(runtime);
PS2Runtime::DeferredGuestYieldScope deferYield(reschedulePending);
const uint64_t tickValue = signalVSyncFlag(rdram, runtime);
ps2_stubs::dispatchGsSyncVCallback(rdram, runtime, tickValue);
dispatchIntcHandlersForCause(rdram, runtime, kIntcVblankStart);
handoffBaseline = runtime->guestExecutionHandoffEpochSnapshot();
}
if (reschedulePending && !runtime->isStopRequested())
{
runtime->waitForGuestExecutionHandoff(handoffBaseline);
}
std::this_thread::sleep_for(std::chrono::microseconds(500));
dispatchIntcHandlersForCause(rdram, runtime, kIntcVblankEnd);
}
@@ -1118,6 +1118,7 @@ namespace ps2_syscalls
const uint8_t *srcPtr = getConstMemPtr(rdram, src);
if (destPtr && srcPtr)
{
ps2TraceGuestRangeWrite(rdram, dest, size, "syscallCopy", ctx);
std::memcpy(destPtr, srcPtr, size);
}
}
@@ -460,10 +460,13 @@ namespace ps2_syscalls
<< std::hex << pc << std::dec << std::endl;
throw ThreadExitException();
}
uint64_t handoffBaseline = 0u;
{
PS2Runtime::GuestExecutionScope guestExecution(runtime);
step(rdram, threadCtx, runtime);
handoffBaseline = runtime->guestExecutionHandoffEpochSnapshot();
}
runtime->waitForGuestExecutionHandoff(handoffBaseline);
}
}
catch (const ThreadExitException &)
@@ -0,0 +1,3 @@
// TODO: on-screen touch joystick / button overlay.
#if defined(__ANDROID__)
#endif // __ANDROID__
-2
View File
@@ -4,8 +4,6 @@
namespace GSMem
{
using enum PixelStorageMode;
using C32Traits = PixelStorageTraits<C32>;
using Z32Traits = PixelStorageTraits<Z32>;
using C16Traits = PixelStorageTraits<C16>;
+6 -19
View File
@@ -159,6 +159,8 @@ bool PS2IopHostAdapter::writeGuest(uint32_t address, const void *source, size_t
}
if (size != 0)
{
uint8_t *const rdram = m_activeRdram ? m_activeRdram : m_runtime.memory().getRDRAM();
ps2TraceGuestRangeWrite(rdram, address, static_cast<uint32_t>(size), "IopHost::writeGuest", nullptr);
std::memcpy(destination, source, size);
}
return true;
@@ -173,6 +175,8 @@ bool PS2IopHostAdapter::zeroGuest(uint32_t address, size_t size)
}
if (size != 0)
{
uint8_t *const rdram = m_activeRdram ? m_activeRdram : m_runtime.memory().getRDRAM();
ps2TraceGuestRangeWrite(rdram, address, static_cast<uint32_t>(size), "IopHost::zeroGuest", nullptr);
std::memset(destination, 0, size);
}
return true;
@@ -432,29 +436,12 @@ int32_t PS2IopHostAdapter::memoryCard(const ps2x::iop::MemoryCardRequest &reques
setRegU32(&context, static_cast<int>(4 + i), request.arguments[i]);
}
uint32_t stackAddress = 0u;
if (request.arguments[4] != 0u)
{
stackAddress = allocateGuest(32u, 16u);
if (stackAddress == 0u ||
!writeGuest(stackAddress + 16u, &request.arguments[4], sizeof(uint32_t)))
{
if (stackAddress != 0u)
{
freeGuest(stackAddress);
}
return -1;
}
setRegU32(&context, 29, stackAddress);
}
// EE n32 ABI: the fifth argument travels in $t0, matching the sceMc* stubs.
setRegU32(&context, 8, request.arguments[4]);
handler(m_activeRdram ? m_activeRdram : m_runtime.memory().getRDRAM(),
&context,
&m_runtime);
if (stackAddress != 0u)
{
freeGuest(stackAddress);
}
return ps2_stubs::getMemoryCardDebugSnapshot().lastResult;
}
+84 -3
View File
@@ -108,6 +108,8 @@ namespace
thread_local DispatchHistory g_dispatchHistory;
thread_local std::unordered_map<PS2Runtime *, uint32_t> g_guestExecutionDepths;
thread_local uint32_t g_deferredGuestYieldDepth = 0u;
thread_local bool g_deferredGuestYieldPending = false;
bool computeFileCrc32(const std::string &path, uint32_t &crcOut)
{
@@ -1962,11 +1964,15 @@ void PS2Runtime::dispatchLoop(uint8_t *rdram, R5900Context *ctx)
const uint32_t dispatchedPc = pc;
const uint32_t dispatchedRa = static_cast<uint32_t>(_mm_extract_epi32(ctx->r[31], 0));
uint64_t handoffBaseline = 0u;
{
GuestExecutionScope guestExecution(this);
fn(rdram, ctx, this);
handoffBaseline = guestExecutionHandoffEpochSnapshot();
}
waitForGuestExecutionHandoff(handoffBaseline);
if (ctx->pc == 0u)
{
const uint32_t ra = static_cast<uint32_t>(_mm_extract_epi32(ctx->r[31], 0));
@@ -1991,10 +1997,19 @@ void PS2Runtime::dispatchLoop(uint8_t *rdram, R5900Context *ctx)
void PS2Runtime::enterGuestExecution()
{
uint32_t &depth = g_guestExecutionDepths[this];
if (depth != 0u)
{
m_guestExecutionMutex.lock();
++depth;
return;
}
m_guestExecutionWaiters.fetch_add(1u, std::memory_order_acq_rel);
m_guestExecutionMutex.lock();
m_guestExecutionWaiters.fetch_sub(1u, std::memory_order_acq_rel);
++g_guestExecutionDepths[this];
depth = 1u;
markGuestExecutionAcquired();
}
@@ -2039,13 +2054,22 @@ void PS2Runtime::reacquireGuestExecution(uint32_t depth)
}
uint32_t &heldDepth = g_guestExecutionDepths[this];
for (uint32_t i = 0; i < depth; ++i)
uint32_t remaining = depth;
if (heldDepth == 0u)
{
m_guestExecutionWaiters.fetch_add(1u, std::memory_order_acq_rel);
m_guestExecutionMutex.lock();
m_guestExecutionWaiters.fetch_sub(1u, std::memory_order_acq_rel);
++heldDepth;
heldDepth = 1u;
markGuestExecutionAcquired();
--remaining;
}
for (uint32_t i = 0; i < remaining; ++i)
{
m_guestExecutionMutex.lock();
++heldDepth;
}
}
@@ -2058,8 +2082,65 @@ void PS2Runtime::markGuestExecutionAcquired()
m_guestExecutionHandoffCv.notify_all();
}
void PS2Runtime::waitForGuestExecutionHandoff()
{
waitForGuestExecutionHandoff(guestExecutionHandoffEpochSnapshot());
}
void PS2Runtime::waitForGuestExecutionHandoff(uint64_t baselineEpoch)
{
// Lock-free fast path
if (m_guestExecutionWaiters.load(std::memory_order_acquire) == 0u)
{
return;
}
std::unique_lock<std::mutex> lock(m_guestExecutionHandoffMutex);
if (m_guestExecutionWaiters.load(std::memory_order_acquire) == 0u)
{
return;
}
const bool handedOff = m_guestExecutionHandoffCv.wait_for(
lock,
std::chrono::milliseconds(2),
[&]()
{
return m_guestExecutionWaiters.load(std::memory_order_acquire) == 0u ||
m_guestExecutionHandoffEpoch.load(std::memory_order_relaxed) != baselineEpoch ||
isStopRequested();
});
if (!handedOff)
{
m_guestExecutionHandoffTimeouts.fetch_add(1u, std::memory_order_relaxed);
}
}
PS2Runtime::DeferredGuestYieldScope::DeferredGuestYieldScope(bool &pendingOut) noexcept
: m_pendingOut(pendingOut)
{
++g_deferredGuestYieldDepth;
}
PS2Runtime::DeferredGuestYieldScope::~DeferredGuestYieldScope()
{
if (--g_deferredGuestYieldDepth == 0u && g_deferredGuestYieldPending)
{
g_deferredGuestYieldPending = false;
m_pendingOut = true;
}
}
void PS2Runtime::yieldGuestExecutionAfterWake()
{
if (g_deferredGuestYieldDepth != 0u)
{
g_deferredGuestYieldPending = true;
return;
}
auto it = g_guestExecutionDepths.find(this);
if (it == g_guestExecutionDepths.end() || it->second == 0u)
{
+64
View File
@@ -0,0 +1,64 @@
#if defined(PLATFORM_VITA)
#include <cstdarg>
#include <cstdio>
// Heap sizes must live in a native object as well(bc of LTO).
// the newlib malloc/new arena (PS2 guest RAM 32MB + GS + buffers live here).
extern "C" unsigned int _newlib_heap_size_user = 96u * 1024u * 1024u;
extern "C" unsigned int sceLibcHeapSize = 8u * 1024u * 1024u;
namespace
{
void ttyPuts(const char *text)
{
if (text)
{
std::fputs(text, stdout); // Vita TTY (capturable via PrincessLog etc.)
}
}
}
// Overrides raylib's debugnet transport with TTY output.
extern "C"
{
int debugNetInit(const char *, int, int)
{
return 0;
}
void debugNetFinish(void)
{
}
int debugNetUDPSend(const char *text)
{
ttyPuts(text);
return 0;
}
int debugNetUDPPrintf(const char *fmt, ...)
{
char line[512];
va_list args;
va_start(args, fmt);
std::vsnprintf(line, sizeof(line), fmt, args);
va_end(args);
ttyPuts(line);
return 0;
}
int debugNetPrintf(int level, const char *fmt, ...)
{
(void)level;
char line[512];
va_list args;
va_start(args, fmt);
std::vsnprintf(line, sizeof(line), fmt, args);
va_end(args);
ttyPuts(line);
return 0;
}
}
#endif // PLATFORM_VITA
+47
View File
@@ -15,8 +15,52 @@
#include <algorithm>
#include <cstdlib>
#if defined(__ANDROID__)
#include <android/log.h>
#include <unistd.h>
#include <thread>
#include <cstdio>
#include <cstring>
#endif
namespace
{
#if defined(__ANDROID__)
void redirectStdioToLogcat()
{
static int pipeFds[2];
if (pipe(pipeFds) != 0)
{
return;
}
setvbuf(stdout, nullptr, _IOLBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
dup2(pipeFds[1], STDOUT_FILENO);
dup2(pipeFds[1], STDERR_FILENO);
std::thread([]()
{
FILE *reader = fdopen(pipeFds[0], "r");
if (!reader)
{
return;
}
char line[1024];
while (fgets(line, sizeof(line), reader))
{
size_t len = std::strlen(line);
if (len > 0 && line[len - 1] == '\n')
{
line[len - 1] = '\0';
}
__android_log_write(ANDROID_LOG_INFO, "ps2x", line);
}
})
.detach();
}
#endif
void setupTerminateLogger() // to help on release build crashs
{
std::set_terminate([]()
@@ -91,6 +135,9 @@ namespace
int main(int argc, char *argv[])
{
#if defined(__ANDROID__)
redirectStdioToLogcat();
#endif
setupTerminateLogger();
try