diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index 49c88fc1f3..6e140c1a76 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -108,7 +108,7 @@ enum PspEventFlagWaitTypes int eventFlagWaitTimer = -1; void __KernelEventFlagBeginCallback(SceUID threadID, SceUID prevCallbackId); -void __KernelEventFlagEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue); +void __KernelEventFlagEndCallback(SceUID threadID, SceUID prevCallbackId); void __KernelEventFlagInit() { @@ -242,7 +242,7 @@ void __KernelEventFlagBeginCallback(SceUID threadID, SceUID prevCallbackId) WARN_LOG_REPORT(HLE, "sceKernelWaitEventFlagCB: beginning callback with bad wait id?"); } -void __KernelEventFlagEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue) +void __KernelEventFlagEndCallback(SceUID threadID, SceUID prevCallbackId) { SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index 451f87bb44..54482b03aa 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -184,7 +184,7 @@ struct Mbx : public KernelObject }; void __KernelMbxBeginCallback(SceUID threadID, SceUID prevCallbackId); -void __KernelMbxEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue); +void __KernelMbxEndCallback(SceUID threadID, SceUID prevCallbackId); void __KernelMbxInit() { @@ -274,7 +274,7 @@ void __KernelMbxBeginCallback(SceUID threadID, SceUID prevCallbackId) WARN_LOG_REPORT(HLE, "sceKernelReceiveMbxCB: beginning callback with bad wait id?"); } -void __KernelMbxEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue) +void __KernelMbxEndCallback(SceUID threadID, SceUID prevCallbackId) { SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index 195e18de95..9f1634def7 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -17,6 +17,8 @@ #include #include +#include +#include #include "Core/HLE/HLE.h" #include "Core/System.h" #include "Core/MIPS/MIPS.h" @@ -53,6 +55,7 @@ struct FplWaitingThread { SceUID threadID; u32 addrPtr; + u64 pausedTimeout; }; struct NativeFPL @@ -118,6 +121,7 @@ struct FPL : public KernelObject p.Do(nextBlock); FplWaitingThread dv = {0}; p.Do(waitingThreads, dv); + p.Do(pausedWaits); p.DoMarker("FPL"); } @@ -127,12 +131,15 @@ struct FPL : public KernelObject int alignedSize; int nextBlock; std::vector waitingThreads; + // Key is the callback id it was for, or if no callback, the thread id. + std::map pausedWaits; }; struct VplWaitingThread { SceUID threadID; u32 addrPtr; + u64 pausedTimeout; }; struct SceKernelVplInfo @@ -162,18 +169,26 @@ struct VPL : public KernelObject VplWaitingThread dv = {0}; p.Do(waitingThreads, dv); alloc.DoState(p); + p.Do(pausedWaits); p.DoMarker("VPL"); } SceKernelVplInfo nv; u32 address; std::vector waitingThreads; + // Key is the callback id it was for, or if no callback, the thread id. + std::map pausedWaits; BlockAllocator alloc; }; void __KernelVplTimeout(u64 userdata, int cyclesLate); void __KernelFplTimeout(u64 userdata, int cyclesLate); +void __KernelVplBeginCallback(SceUID threadID, SceUID prevCallbackId); +void __KernelVplEndCallback(SceUID threadID, SceUID prevCallbackId); +void __KernelFplBeginCallback(SceUID threadID, SceUID prevCallbackId); +void __KernelFplEndCallback(SceUID threadID, SceUID prevCallbackId); + void __KernelMemoryInit() { kernelMemory.Init(PSP_GetKernelMemoryBase(), PSP_GetKernelMemoryEnd()-PSP_GetKernelMemoryBase()); @@ -187,6 +202,9 @@ void __KernelMemoryInit() sdkVersion_ = 0; compilerVersion_ = 0; memset(tlsUsedIndexes, 0, sizeof(tlsUsedIndexes)); + + __KernelRegisterWaitTypeFuncs(WAITTYPE_VPL, __KernelVplBeginCallback, __KernelVplEndCallback); + __KernelRegisterWaitTypeFuncs(WAITTYPE_FPL, __KernelFplBeginCallback, __KernelFplEndCallback); } void __KernelMemoryDoState(PointerWrap &p) @@ -258,6 +276,106 @@ bool __KernelUnlockFplForThread(FPL *fpl, FplWaitingThread &threadInfo, u32 &err return true; } +void __KernelFplBeginCallback(SceUID threadID, SceUID prevCallbackId) +{ + SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; + + u32 error; + SceUID fplID = __KernelGetWaitID(threadID, WAITTYPE_FPL, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + FPL *fpl = fplID == 0 ? NULL : kernelObjects.Get(fplID, error); + if (fpl) + { + // This means two callbacks in a row. PSP crashes if the same callback runs inside itself. + // TODO: Handle this better? + if (fpl->pausedWaits.find(pauseKey) != fpl->pausedWaits.end()) + return; + + FplWaitingThread waitData = {0}; + for (size_t i = 0; i < fpl->waitingThreads.size(); i++) + { + FplWaitingThread *t = &fpl->waitingThreads[i]; + if (t->threadID == threadID) + { + waitData = *t; + // TODO: Hmm, what about priority/fifo order? Does it lose its place in line? + fpl->waitingThreads.erase(fpl->waitingThreads.begin() + i); + break; + } + } + + if (waitData.threadID != threadID) + { + ERROR_LOG_REPORT(HLE, "sceKernelAllocateFplCB: wait not found to pause for callback"); + return; + } + + if (timeoutPtr != 0 && fplWaitTimer != -1) + { + s64 cyclesLeft = CoreTiming::UnscheduleEvent(fplWaitTimer, threadID); + waitData.pausedTimeout = CoreTiming::GetTicks() + cyclesLeft; + } + else + waitData.pausedTimeout = 0; + + fpl->pausedWaits[pauseKey] = waitData; + DEBUG_LOG(HLE, "sceKernelAllocateFplCB: Suspending fpl wait for callback"); + } + else + WARN_LOG_REPORT(HLE, "sceKernelAllocateFplCB: beginning callback with bad wait id?"); +} + +void __KernelFplEndCallback(SceUID threadID, SceUID prevCallbackId) +{ + SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; + + u32 error; + SceUID fplID = __KernelGetWaitID(threadID, WAITTYPE_FPL, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + FPL *fpl = fplID == 0 ? NULL : kernelObjects.Get(fplID, error); + if (!fpl || fpl->pausedWaits.find(pauseKey) == fpl->pausedWaits.end()) + { + // TODO: Since it was deleted, we don't know how long was actually left. + // For now, we just say the full time was taken. + if (timeoutPtr != 0 && fplWaitTimer != -1) + Memory::Write_U32(0, timeoutPtr); + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); + return; + } + + FplWaitingThread waitData = fpl->pausedWaits[pauseKey]; + u64 waitDeadline = waitData.pausedTimeout; + fpl->pausedWaits.erase(pauseKey); + + // TODO: Don't wake up if __KernelCurHasReadyCallbacks()? + + bool wokeThreads; + // Attempt to unlock. + if (__KernelUnlockFplForThread(fpl, waitData, error, 0, wokeThreads)) + return; + + // We only check if it timed out if it couldn't receive. + s64 cyclesLeft = waitDeadline - CoreTiming::GetTicks(); + if (cyclesLeft < 0 && waitDeadline != 0) + { + if (timeoutPtr != 0 && fplWaitTimer != -1) + Memory::Write_U32(0, timeoutPtr); + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); + } + else + { + if (timeoutPtr != 0 && fplWaitTimer != -1) + CoreTiming::ScheduleEvent(cyclesLeft, fplWaitTimer, __KernelGetCurThread()); + + // TODO: Should this not go at the end? + fpl->waitingThreads.push_back(waitData); + + DEBUG_LOG(HLE, "sceKernelAllocateFplCB: Resuming fpl wait from callback"); + } +} + void __KernelFplRemoveThread(FPL *fpl, SceUID threadID) { for (size_t i = 0; i < fpl->waitingThreads.size(); i++) @@ -1147,6 +1265,106 @@ bool __KernelUnlockVplForThread(VPL *vpl, VplWaitingThread &threadInfo, u32 &err return true; } +void __KernelVplBeginCallback(SceUID threadID, SceUID prevCallbackId) +{ + SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; + + u32 error; + SceUID vplID = __KernelGetWaitID(threadID, WAITTYPE_VPL, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + VPL *vpl = vplID == 0 ? NULL : kernelObjects.Get(vplID, error); + if (vpl) + { + // This means two callbacks in a row. PSP crashes if the same callback runs inside itself. + // TODO: Handle this better? + if (vpl->pausedWaits.find(pauseKey) != vpl->pausedWaits.end()) + return; + + VplWaitingThread waitData = {0}; + for (size_t i = 0; i < vpl->waitingThreads.size(); i++) + { + VplWaitingThread *t = &vpl->waitingThreads[i]; + if (t->threadID == threadID) + { + waitData = *t; + // TODO: Hmm, what about priority/fifo order? Does it lose its place in line? + vpl->waitingThreads.erase(vpl->waitingThreads.begin() + i); + break; + } + } + + if (waitData.threadID != threadID) + { + ERROR_LOG_REPORT(HLE, "sceKernelAllocateVplCB: wait not found to pause for callback"); + return; + } + + if (timeoutPtr != 0 && vplWaitTimer != -1) + { + s64 cyclesLeft = CoreTiming::UnscheduleEvent(vplWaitTimer, threadID); + waitData.pausedTimeout = CoreTiming::GetTicks() + cyclesLeft; + } + else + waitData.pausedTimeout = 0; + + vpl->pausedWaits[pauseKey] = waitData; + DEBUG_LOG(HLE, "sceKernelAllocateVplCB: Suspending vpl wait for callback"); + } + else + WARN_LOG_REPORT(HLE, "sceKernelAllocateVplCB: beginning callback with bad wait id?"); +} + +void __KernelVplEndCallback(SceUID threadID, SceUID prevCallbackId) +{ + SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; + + u32 error; + SceUID vplID = __KernelGetWaitID(threadID, WAITTYPE_VPL, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + VPL *vpl = vplID == 0 ? NULL : kernelObjects.Get(vplID, error); + if (!vpl || vpl->pausedWaits.find(pauseKey) == vpl->pausedWaits.end()) + { + // TODO: Since it was deleted, we don't know how long was actually left. + // For now, we just say the full time was taken. + if (timeoutPtr != 0 && vplWaitTimer != -1) + Memory::Write_U32(0, timeoutPtr); + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_DELETE); + return; + } + + VplWaitingThread waitData = vpl->pausedWaits[pauseKey]; + u64 waitDeadline = waitData.pausedTimeout; + vpl->pausedWaits.erase(pauseKey); + + // TODO: Don't wake up if __KernelCurHasReadyCallbacks()? + + bool wokeThreads; + // Attempt to unlock. + if (__KernelUnlockVplForThread(vpl, waitData, error, 0, wokeThreads)) + return; + + // We only check if it timed out if it couldn't receive. + s64 cyclesLeft = waitDeadline - CoreTiming::GetTicks(); + if (cyclesLeft < 0 && waitDeadline != 0) + { + if (timeoutPtr != 0 && vplWaitTimer != -1) + Memory::Write_U32(0, timeoutPtr); + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); + } + else + { + if (timeoutPtr != 0 && vplWaitTimer != -1) + CoreTiming::ScheduleEvent(cyclesLeft, vplWaitTimer, __KernelGetCurThread()); + + // TODO: Should this not go at the end? + vpl->waitingThreads.push_back(waitData); + + DEBUG_LOG(HLE, "sceKernelAllocateVplCB: Resuming vpl wait from callback"); + } +} + void __KernelVplRemoveThread(VPL *vpl, SceUID threadID) { for (size_t i = 0; i < vpl->waitingThreads.size(); i++) diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 1b1ebcb6d9..df4375843d 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -151,9 +151,9 @@ typedef std::multimap MutexMap; static MutexMap mutexHeldLocks; void __KernelMutexBeginCallback(SceUID threadID, SceUID prevCallbackId); -void __KernelMutexEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue); +void __KernelMutexEndCallback(SceUID threadID, SceUID prevCallbackId); void __KernelLwMutexBeginCallback(SceUID threadID, SceUID prevCallbackId); -void __KernelLwMutexEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue); +void __KernelLwMutexEndCallback(SceUID threadID, SceUID prevCallbackId); void __KernelMutexInit() { @@ -306,7 +306,7 @@ void __KernelMutexBeginCallback(SceUID threadID, SceUID prevCallbackId) WARN_LOG_REPORT(HLE, "sceKernelLockMutexCB: beginning callback with bad wait id?"); } -void __KernelMutexEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue) +void __KernelMutexEndCallback(SceUID threadID, SceUID prevCallbackId) { SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; @@ -1028,7 +1028,7 @@ void __KernelLwMutexBeginCallback(SceUID threadID, SceUID prevCallbackId) WARN_LOG_REPORT(HLE, "sceKernelLockLwMutexCB: beginning callback with bad wait id?"); } -void __KernelLwMutexEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue) +void __KernelLwMutexEndCallback(SceUID threadID, SceUID prevCallbackId) { SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index d085acca3a..5060d3e024 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -78,7 +78,7 @@ struct Semaphore : public KernelObject static int semaWaitTimer = -1; void __KernelSemaBeginCallback(SceUID threadID, SceUID prevCallbackId); -void __KernelSemaEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue); +void __KernelSemaEndCallback(SceUID threadID, SceUID prevCallbackId); void __KernelSemaInit() { @@ -164,7 +164,7 @@ void __KernelSemaBeginCallback(SceUID threadID, SceUID prevCallbackId) WARN_LOG_REPORT(HLE, "sceKernelWaitSemaCB: beginning callback with bad wait id?"); } -void __KernelSemaEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue) +void __KernelSemaEndCallback(SceUID threadID, SceUID prevCallbackId) { SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 397baa769c..2a7d71d6c3 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -938,7 +938,7 @@ void __KernelDelayBeginCallback(SceUID threadID, SceUID prevCallbackId) { WARN_LOG_REPORT(HLE, "sceKernelDelayThreadCB: beginning callback with bad wait?"); } -void __KernelDelayEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue) { +void __KernelDelayEndCallback(SceUID threadID, SceUID prevCallbackId) { SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId; if (pausedDelays.find(pauseKey) == pausedDelays.end()) @@ -968,7 +968,7 @@ void __KernelSleepBeginCallback(SceUID threadID, SceUID prevCallbackId) { DEBUG_LOG(HLE, "sceKernelSleepThreadCB: Suspending sleep for callback"); } -void __KernelSleepEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &returnValue) { +void __KernelSleepEndCallback(SceUID threadID, SceUID prevCallbackId) { u32 error; Thread *thread = kernelObjects.Get(threadID, error); if (!thread) @@ -3364,7 +3364,7 @@ void __KernelReturnFromMipsCall() if (cur->nt.waitType != WAITTYPE_NONE) { if (waitTypeFuncs[cur->nt.waitType].endFunc != NULL && call->cbId > 0) - waitTypeFuncs[cur->nt.waitType].endFunc(cur->GetUID(), cur->currentCallbackId, currentMIPS->r[MIPS_REG_V0]); + waitTypeFuncs[cur->nt.waitType].endFunc(cur->GetUID(), cur->currentCallbackId); } // yeah! back in the real world, let's keep going. Should we process more callbacks? diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index aece5716d7..8f723b3cc7 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -103,7 +103,7 @@ const char *getWaitTypeName(WaitType type); // Suspend wait and timeout while a thread enters a callback. typedef void (* WaitBeginCallbackFunc)(SceUID threadID, SceUID prevCallbackId); // Resume wait and timeout as a thread exits a callback. -typedef void (* WaitEndCallbackFunc)(SceUID threadID, SceUID prevCallbackId, u32 &returnValue); +typedef void (* WaitEndCallbackFunc)(SceUID threadID, SceUID prevCallbackId); void __KernelRegisterWaitTypeFuncs(WaitType type, WaitBeginCallbackFunc beginFunc, WaitEndCallbackFunc endFunc);