mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-10-01 18:31:21 -04:00
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:
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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__
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
namespace GSMem
|
||||
{
|
||||
using enum PixelStorageMode;
|
||||
|
||||
using C32Traits = PixelStorageTraits<C32>;
|
||||
using Z32Traits = PixelStorageTraits<Z32>;
|
||||
using C16Traits = PixelStorageTraits<C16>;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user