diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index b7321ab0a..5b09e01af 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -68,6 +68,7 @@ const WaitTypeNames waitTypeNames[] = { { WAITTYPE_MODULE, "Module" }, { WAITTYPE_HLEDELAY, "HleDelay" }, { WAITTYPE_TLS, "TLS" }, + { WAITTYPE_VMEM, "Volatile Mem" }, }; const char *getWaitTypeName(WaitType type) diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 147677ce1..b52f1b9f6 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -67,7 +67,7 @@ struct SceKernelSysClock { }; -// TODO: Map these to PSP wait types. +// TODO: Map these to PSP wait types. Most of these are wrong. // remember to update the waitTypeNames array in sceKernelThread.cpp when changing these enum WaitType { @@ -93,6 +93,7 @@ enum WaitType WAITTYPE_MODULE = 19, WAITTYPE_HLEDELAY = 20, WAITTYPE_TLS = 21, + WAITTYPE_VMEM = 22, NUM_WAITTYPES }; diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index 2c4c3fb9a..d33836a3d 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include "Common/ChunkFile.h" #include "Core/HLE/HLE.h" #include "Core/CoreTiming.h" @@ -24,6 +25,12 @@ #include "Core/HLE/scePower.h" #include "Core/HLE/sceKernelThread.h" +struct VolatileWaitingThread { + SceUID threadID; + u32 addrPtr; + u32 sizePtr; +}; + const int PSP_POWER_ERROR_TAKEN_SLOT = 0x80000020; const int PSP_POWER_ERROR_SLOTS_FULL = 0x80000022; const int PSP_POWER_ERROR_PRIVATE_SLOT = 0x80000023; @@ -45,6 +52,7 @@ const int numberOfCBPowerSlotsPrivate = 32; static bool volatileMemLocked; static int powerCbSlots[numberOfCBPowerSlots]; +static std::vector volatileWaitingThreads; // this should belong here on in CoreTiming? static int pllFreq = 222; @@ -53,6 +61,7 @@ static int busFreq = 111; void __PowerInit() { memset(powerCbSlots, 0, sizeof(powerCbSlots)); volatileMemLocked = false; + volatileWaitingThreads.clear(); if(g_Config.iLockedCPUSpeed > 0) { CoreTiming::SetClockFrequencyMHz(g_Config.iLockedCPUSpeed); @@ -64,6 +73,7 @@ void __PowerInit() { void __PowerDoState(PointerWrap &p) { p.DoArray(powerCbSlots, ARRAY_SIZE(powerCbSlots)); p.Do(volatileMemLocked); + p.Do(volatileWaitingThreads); p.DoMarker("scePower"); } @@ -199,12 +209,11 @@ int sceKernelPowerTick(int flag) { #define ERROR_POWER_VMEM_IN_USE 0x802b0200 -int sceKernelVolatileMemTryLock(int type, int paddr, int psize) { - if (!volatileMemLocked) { - INFO_LOG(HLE,"sceKernelVolatileMemTryLock(%i, %08x, %i) - success", type, paddr, psize); - volatileMemLocked = true; - } else { - ERROR_LOG_REPORT(HLE, "sceKernelVolatileMemTryLock(%i, %08x, %i) - already locked!", type, paddr, psize); +int __KernelVolatileMemLock(int type, u32 paddr, u32 psize) { + if (type != 0) { + return SCE_KERNEL_ERROR_INVALID_MODE; + } + if (volatileMemLocked) { return ERROR_POWER_VMEM_IN_USE; } @@ -213,22 +222,89 @@ int sceKernelVolatileMemTryLock(int type, int paddr, int psize) { // TODO: Should really reserve this properly! Memory::Write_U32(0x08400000, paddr); Memory::Write_U32(0x00400000, psize); + volatileMemLocked = true; return 0; } +int sceKernelVolatileMemTryLock(int type, u32 paddr, u32 psize) { + int error = __KernelVolatileMemLock(type, paddr, psize); + + switch (error) { + case 0: + INFO_LOG(HLE, "sceKernelVolatileMemTryLock(%i, %08x, %08x) - success", type, paddr, psize); + break; + + case ERROR_POWER_VMEM_IN_USE: + ERROR_LOG(HLE, "sceKernelVolatileMemTryLock(%i, %08x, %08x) - already locked!", type, paddr, psize); + break; + + default: + ERROR_LOG_REPORT(HLE, "%08x=sceKernelVolatileMemTryLock(%i, %08x, %08x) - error", type, paddr, psize, error); + break; + } + + return error; +} + int sceKernelVolatileMemUnlock(int type) { + if (type != 0) { + ERROR_LOG_REPORT(HLE, "sceKernelVolatileMemUnlock(%i) - invalid mode", type); + return SCE_KERNEL_ERROR_INVALID_MODE; + } if (volatileMemLocked) { - INFO_LOG(HLE,"sceKernelVolatileMemUnlock(%i)", type); volatileMemLocked = false; + + // Wake someone, always fifo. + bool wokeThreads = false; + u32 error; + while (!volatileWaitingThreads.empty() && !volatileMemLocked) { + VolatileWaitingThread waitInfo = volatileWaitingThreads.front(); + volatileWaitingThreads.erase(volatileWaitingThreads.begin()); + + int waitID = __KernelGetWaitID(waitInfo.threadID, WAITTYPE_VMEM, error); + // If they were force-released, just skip. + if (waitID == 1 && __KernelVolatileMemLock(0, waitInfo.addrPtr, waitInfo.sizePtr) == 0) { + __KernelResumeThreadFromWait(waitInfo.threadID, 0); + wokeThreads = true; + } + } + + if (wokeThreads) { + INFO_LOG(HLE, "sceKernelVolatileMemUnlock(%i) handed over to another thread", type); + hleReSchedule("volatile mem unlocked"); + } else { + INFO_LOG(HLE, "sceKernelVolatileMemUnlock(%i)", type); + } } else { ERROR_LOG_REPORT(HLE, "sceKernelVolatileMemUnlock(%i) FAILED - not locked", type); } return 0; } -int sceKernelVolatileMemLock(int type, int paddr, int psize) { - return sceKernelVolatileMemTryLock(type, paddr, psize); +int sceKernelVolatileMemLock(int type, u32 paddr, u32 psize) { + int error = __KernelVolatileMemLock(type, paddr, psize); + + switch (error) { + case 0: + INFO_LOG(HLE, "sceKernelVolatileMemLock(%i, %08x, %08x) - success", type, paddr, psize); + break; + + case ERROR_POWER_VMEM_IN_USE: + { + WARN_LOG(HLE, "sceKernelVolatileMemLock(%i, %08x, %08x) - already locked, waiting", type, paddr, psize); + const VolatileWaitingThread waitInfo = { __KernelGetCurThread(), paddr, psize }; + volatileWaitingThreads.push_back(waitInfo); + __KernelWaitCurThread(WAITTYPE_VMEM, 1, 0, 0, false, "volatile mem waited"); + } + break; + + default: + ERROR_LOG_REPORT(HLE, "%08x=sceKernelVolatileMemLock(%i, %08x, %08x) - error", type, paddr, psize, error); + break; + } + + return error; } @@ -368,9 +444,11 @@ static const HLEFunction scePower[] = { {0x0442d852,0,"scePowerRequestColdReset"}, {0xbafa3df0,0,"scePowerGetCallbackMode"}, {0xa9d22232,0,"scePowerSetCallbackMode"}, - {0x23c31ffe,0,"scePowerVolatileMemLock"}, - {0xfa97a599,0,"scePowerVolatileMemTryLock"}, - {0xb3edd801,0,"scePowerVolatileMemUnlock"}, + + // These seem to be aliases. + {0x23c31ffe,&WrapI_IUU,"scePowerVolatileMemLock"}, + {0xfa97a599,&WrapI_IUU,"scePowerVolatileMemTryLock"}, + {0xb3edd801,&WrapI_I,"scePowerVolatileMemUnlock"}, }; //890129c in tyshooter looks bogus @@ -381,9 +459,9 @@ const HLEFunction sceSuspendForUser[] = { // There's an extra 4MB that can be allocated, which seems to be "volatile". These functions // let you grab it. - {0xa14f40b2,&WrapI_III,"sceKernelVolatileMemTryLock"}, + {0xa14f40b2,&WrapI_IUU,"sceKernelVolatileMemTryLock"}, {0xa569e425,&WrapI_I,"sceKernelVolatileMemUnlock"}, - {0x3e0271d3,&WrapI_III,"sceKernelVolatileMemLock"}, //when "acquiring mem pool" (fired up) + {0x3e0271d3,&WrapI_IUU,"sceKernelVolatileMemLock"}, //when "acquiring mem pool" (fired up) };