From aea0580297c966cf106f522b41d54137c7cc7742 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 7 Nov 2012 15:44:48 +0100 Subject: [PATCH] More reworking of Callbacks, plus some other little fixes. --- Core/HLE/HLETables.cpp | 6 +- Core/HLE/sceDisplay.cpp | 2 +- Core/HLE/sceGe.cpp | 8 +- Core/HLE/sceKernel.cpp | 4 +- Core/HLE/sceKernelEventFlag.cpp | 6 +- Core/HLE/sceKernelInterrupt.cpp | 3 +- Core/HLE/sceKernelInterrupt.h | 2 +- Core/HLE/sceKernelMemory.cpp | 39 +- Core/HLE/sceKernelModule.cpp | 6 +- Core/HLE/sceKernelMsgPipe.cpp | 105 ++++- Core/HLE/sceKernelMutex.cpp | 19 +- Core/HLE/sceKernelMutex.h | 1 + Core/HLE/sceKernelSemaphore.cpp | 2 +- Core/HLE/sceKernelThread.cpp | 724 +++++++++++++++++++++----------- Core/HLE/sceKernelThread.h | 48 ++- Core/HLE/sceKernelVTimer.cpp | 1 + Core/HLE/sceMpeg.cpp | 8 +- Core/HLE/sceUtility.cpp | 8 + Core/MIPS/MIPS.h | 4 +- 19 files changed, 704 insertions(+), 292 deletions(-) diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index 9cee2dddc9..9ebf97064e 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -56,9 +56,9 @@ //zlibdec const HLEFunction FakeSysCalls[] = { - {NID_THREADRETURN, _sceKernelReturnFromThread, "_sceKernelReturnFromThread"}, - {NID_CALLBACKRETURN, _sceKernelReturnFromCallback, "_sceKernelReturnFromCallback"}, - {NID_INTERRUPTRETURN, _sceKernelReturnFromInterrupt, "_sceKernelReturnFromInterrupt"}, + {NID_THREADRETURN, __KernelReturnFromThread, "__KernelReturnFromThread"}, + {NID_CALLBACKRETURN, __KernelReturnFromMipsCall, "__KernelReturnFromMipsCall"}, + {NID_INTERRUPTRETURN, __KernelReturnFromInterrupt, "__KernelReturnFromInterrupt"}, {NID_IDLE, _sceKernelIdle, "_sceKernelIdle"}, }; diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index aaacddecfd..9edd172532 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -255,7 +255,7 @@ void sceDisplayGetVcount() // Too spammy // DEBUG_LOG(HLE,"%i=sceDisplayGetVcount()", vCount); // Games like Puyo Puyo call this in a tight loop at end-of-frame. We could have it consume some time from CoreTiming? - CoreTiming::Idle(100000); + CoreTiming::Idle(1000000); RETURN(vCount); } diff --git a/Core/HLE/sceGe.cpp b/Core/HLE/sceGe.cpp index f5f616fc35..269e2b84a1 100644 --- a/Core/HLE/sceGe.cpp +++ b/Core/HLE/sceGe.cpp @@ -60,6 +60,10 @@ u32 sceGeEdramGetSize() u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, u32 callbackId, u32 optParamAddr) { + DEBUG_LOG(HLE,"sceGeListEnQueue(addr=%08x, stall=%08x, cbid=%08x, param=%08x)", + listAddress,stallAddress,callbackId,optParamAddr); + //if (!stallAddress) + // stallAddress = listAddress; u32 listID = gpu->EnqueueList(listAddress, stallAddress); // HACKY if (listID) @@ -67,8 +71,6 @@ u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, u32 callbackId, u32 optP else state = SCE_GE_LIST_COMPLETED; - DEBUG_LOG(HLE,"%i=sceGeListEnQueue(addr=%08x, stall=%08x, cbid=%08x, param=%08x)",listID, - listAddress,stallAddress,callbackId,optParamAddr); DEBUG_LOG(HLE,"List enqueued."); //return display list ID return listID; @@ -76,6 +78,8 @@ u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, u32 callbackId, u32 optP u32 sceGeListEnQueueHead(u32 listAddress, u32 stallAddress, u32 callbackId, u32 optParamAddr) { + if (!stallAddress) + stallAddress = listAddress; u32 listID = gpu->EnqueueList(listAddress,stallAddress); // HACKY if (listID) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 368cd1342a..a91ba5816e 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -379,13 +379,12 @@ const HLEFunction ThreadManForUser[] = {0x72F3C145,0,"sceKernelReleaseThreadEventHandler"}, {0x369EEB6B,0,"sceKernelReferThreadEventHandlerStatus"}, - {0x349d6d6c,&WrapU_V,"sceKernelCheckCallback"}, + {0x349d6d6c,sceKernelCheckCallback,"sceKernelCheckCallback"}, {0xE81CAF8F,sceKernelCreateCallback,"sceKernelCreateCallback"}, {0xEDBA5844,sceKernelDeleteCallback,"sceKernelDeleteCallback"}, {0xC11BA8C4,sceKernelNotifyCallback,"sceKernelNotifyCallback"}, {0xBA4051D6,sceKernelCancelCallback,"sceKernelCancelCallback"}, {0x2A3D44FF,sceKernelGetCallbackCount,"sceKernelGetCallbackCount"}, - {0x349D6D6C,&WrapU_V,"sceKernelCheckCallback"}, {0x730ED8BC,sceKernelReferCallbackStatus,"sceKernelReferCallbackStatus"}, {0x8125221D,sceKernelCreateMbx,"sceKernelCreateMbx"}, @@ -426,6 +425,7 @@ const HLEFunction ThreadManForUser[] = {0xA8AA591F,sceKernelCancelFpl,"sceKernelCancelFpl"}, {0xD8199E4C,sceKernelReferFplStatus,"sceKernelReferFplStatus"}, + // Not sure if these should be hooked up. See below. {0x0E927AED, _sceKernelReturnFromTimerHandler, "_sceKernelReturnFromTimerHandler"}, {0x532A522E, _sceKernelExitThread,"_sceKernelExitThread"}, diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index a1b91fea21..a5e7518ece 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -171,7 +171,7 @@ retry: EventFlagTh *t = &e->waitingThreads[i]; if (__KernelEventFlagMatches(&e->nef.currentPattern, t->bits, t->wait, t->outAddr)) { - __KernelResumeThread(t->tid); + __KernelResumeThreadFromWait(t->tid); wokeThreads = true; e->nef.numWaitThreads--; e->waitingThreads.erase(e->waitingThreads.begin() + i); @@ -216,8 +216,8 @@ void sceKernelWaitEventFlag() timeout = Memory::Read_U32(timeoutPtr); __KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, 0, false); + // MUST NOT return a value after __KernelWaitCurThread as we may have been rescheduled! } - RETURN(0); } else { @@ -255,8 +255,8 @@ void sceKernelWaitEventFlagCB() timeout = Memory::Read_U32(timeoutPtr); __KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, 0, true); + __KernelCheckCallbacks(); } - RETURN(0); } else { diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index 387bbb20bf..8fc9681a61 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -303,7 +303,7 @@ void __TriggerInterruptWithArg(PSPInterrupt intno, int subintr, int arg) __RunOnePendingInterrupt(); } -void _sceKernelReturnFromInterrupt() +void __KernelReturnFromInterrupt() { DEBUG_LOG(CPU, "Left interrupt handler at %08x", currentMIPS->pc); inInterrupt = false; @@ -424,6 +424,7 @@ const HLEFunction Kernel_Library[] = {0xa089eca4,sceKernelMemset, "sceKernelMemset"}, {0xDC692EE3,0, "sceKernelTryLockLwMutex"}, {0xbea46419,0, "sceKernelLockLwMutex"}, + {0x1FC64E09,0, "sceKernelLockLwMutexCB"}, {0x15b6446b,0, "sceKernelUnlockLwMutex"}, {0x293b45b8,sceKernelGetThreadId, "sceKernelGetThreadId"}, {0x1839852A,0,"sce_paf_private_memcpy"}, diff --git a/Core/HLE/sceKernelInterrupt.h b/Core/HLE/sceKernelInterrupt.h index f4eaf349a4..baca7e4eb2 100644 --- a/Core/HLE/sceKernelInterrupt.h +++ b/Core/HLE/sceKernelInterrupt.h @@ -60,8 +60,8 @@ void __InterruptsShutdown(); void __TriggerInterrupt(PSPInterrupt intno, int subInterrupts = -1); void __TriggerInterruptWithArg(PSPInterrupt intno, int subintr, int arg); // For GE "callbacks" bool __RunOnePendingInterrupt(); +void __KernelReturnFromInterrupt(); -void _sceKernelReturnFromInterrupt(); u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handler, u32 handlerArg); void Register_Kernel_Library(); diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index 8c7a79d2db..c0dba7012f 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -229,11 +229,9 @@ void sceKernelAllocateFplCB() } else { // TODO: Should block and process callbacks! __KernelCheckCallbacks(); - RETURN(0); } DEBUG_LOG(HLE,"sceKernelAllocateFpl(%i, %08x, %i)", id, PARAM(1), timeOut); - RETURN(0); } else { @@ -620,6 +618,35 @@ void sceKernelReferVplStatus() RETURN(0); } +void AllocMemoryBlock() { + const char *pname = Memory::GetCharPointer(PARAM(0)); + int type = PARAM(1); + u32 size = PARAM(2); + int paramsAddr = PARAM(3); + + DEBUG_LOG(HLE,"AllocMemoryBlock(SysMemUserForUser_FE707FDF)(%s, %i, %i, %08x)", pname, type, size, paramsAddr); + + // Just support allocating a block in the user region. + + u32 blockPtr = userMemory.Alloc(size, false, pname); + + // Create a UID object??? Nah, let's just us the UID itself (hack!) + + RETURN(blockPtr); +} + +void FreeMemoryBlock() { + SceUID uid = PARAM(0); + DEBUG_LOG(HLE, "FreeMemoryBlock(%i)", uid); + userMemory.Free(uid); + RETURN(0); +} + +void GetMemoryBlockPtr() { + SceUID uid = PARAM(0); + DEBUG_LOG(HLE, "GetMemoryBlockPtr(%i)", uid); + RETURN(uid); +} const HLEFunction SysMemUserForUser[] = { @@ -637,10 +664,12 @@ const HLEFunction SysMemUserForUser[] = {0xf77d77cb,sceKernelSetCompilerVersion,"sceKernelSetCompilerVersion"}, {0x35669d4c,0,"sceKernelSetCompiledSdkVersion600_602"}, //?? {0x1b4217bc,0,"sceKernelSetCompiledSdkVersion603_605"}, - {0xDB83A952,0,"SysMemUserForUser_DB83A952"}, + + // Obscure raw block API + {0xDB83A952,GetMemoryBlockPtr,"SysMemUserForUser_DB83A952"}, // GetMemoryBlockAddr {0x91DE343C,0,"SysMemUserForUser_91DE343C"}, - {0x50F61D8A,0,"SysMemUserForUser_50F61D8A"}, - {0xFE707FDF,0,"SysMemUserForUser_FE707FDF"}, + {0x50F61D8A,FreeMemoryBlock,"SysMemUserForUser_50F61D8A"}, // FreeMemoryBlock + {0xFE707FDF,AllocMemoryBlock,"SysMemUserForUser_FE707FDF"}, // AllocMemoryBlock }; diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 2c1947007f..6950ac453e 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -567,6 +567,8 @@ u32 sceKernelLoadModule(const char *name, u32 flags) return SCE_KERNEL_ERROR_ILLEGAL_OBJECT; } + DEBUG_LOG(LOADER, "sceKernelLoadModule(%s, %08x)", name, flags); + SceKernelLMOption *lmoption = 0; int position = 0; // TODO: Use position to decide whether to load high or low @@ -633,8 +635,8 @@ void sceKernelUnloadModule() void sceKernelGetModuleIdByAddress() { - ERROR_LOG(HLE,"UNIMPL sceKernelGetModuleIdByAddress(%08x)", PARAM(0)); - if (PARAM(0) == 0x08800000) + ERROR_LOG(HLE,"HACKIMPL sceKernelGetModuleIdByAddress(%08x)", PARAM(0)); + if ((PARAM(0) & 0xFFFF0000) == 0x08800000) RETURN(mainModuleID); else RETURN(0); diff --git a/Core/HLE/sceKernelMsgPipe.cpp b/Core/HLE/sceKernelMsgPipe.cpp index 39ffb032e9..80fdad860f 100644 --- a/Core/HLE/sceKernelMsgPipe.cpp +++ b/Core/HLE/sceKernelMsgPipe.cpp @@ -18,6 +18,7 @@ #include "HLE.h" #include "sceKernel.h" #include "sceKernelMsgPipe.h" +#include "sceKernelThread.h" struct NativeMsgPipe { @@ -44,6 +45,8 @@ struct MsgPipe : public KernelObject // Ring buffer u8 *buffer; + int writePos; + int readPos; }; void sceKernelCreateMsgPipe() @@ -66,6 +69,9 @@ void sceKernelCreateMsgPipe() m->nmp.numReceiveWaitThreads = 0; m->buffer = new u8[size]; + m->writePos = 0; + m->readPos = 0; + RETURN(id); } @@ -93,6 +99,35 @@ void sceKernelSendMsgPipe() u32 resultAddr = PARAM(4); u32 timeoutPtr = PARAM(5); + u32 error; + MsgPipe *pipe = kernelObjects.Get(uid, error); + if (!pipe) { + ERROR_LOG(HLE, "sceKernelSendMsgPipe(%i) - ERROR %08x", uid, error); + RETURN(error); + return; + } + + if (sendSize > pipe->nmp.freeSize) { + // TODO: Block + ERROR_LOG(HLE, "sceKernelSendMsgPipe(%i) - Message won't fit", uid, error); + RETURN(SCE_KERNEL_ERROR_MPP_FULL); + return; + } + + const u8 *source = Memory::GetPointer(sendBufAddr); + + int destIndex = pipe->writePos; + if (pipe->writePos + sendSize > pipe->nmp.size) { + // Split in two + int firstCopySize = pipe->nmp.size - pipe->writePos; + memcpy(pipe->buffer + pipe->writePos, source, firstCopySize); + memcpy(pipe->buffer, source + firstCopySize, sendSize - firstCopySize); + } else { + memcpy(pipe->buffer + pipe->writePos, source, sendSize); + } + pipe->writePos += sendSize; + pipe->writePos %= pipe->nmp.size; + ERROR_LOG(HLE, "UNIMPL sceKernelSendMsgPipe(%i, %08x, %i, %i, %08x, %08x)", uid, sendBufAddr, sendSize, waitMode, resultAddr, timeoutPtr); RETURN(0); } @@ -106,8 +141,38 @@ void sceKernelSendMsgPipeCB() u32 resultAddr = PARAM(4); u32 timeoutPtr = PARAM(5); - ERROR_LOG(HLE, "UNIMPL sceKernelSendMsgPipeCB(%i, %08x, %i, %i, %08x, %08x)", uid, sendBufAddr, sendSize, waitMode, resultAddr, timeoutPtr); + u32 error; + MsgPipe *pipe = kernelObjects.Get(uid, error); + if (!pipe) { + ERROR_LOG(HLE, "sceKernelSendMsgPipe(%i) - ERROR %08x", uid, error); + RETURN(error); + return; + } + + if (sendSize > pipe->nmp.freeSize) { + // TODO: Block + ERROR_LOG(HLE, "sceKernelSendMsgPipe(%i) - Message won't fit", uid, error); + RETURN(SCE_KERNEL_ERROR_MPP_FULL); + return; + } + + const u8 *source = Memory::GetPointer(sendBufAddr); + + int destIndex = pipe->writePos; + if (pipe->writePos + sendSize > pipe->nmp.size) { + // Split in two + int firstCopySize = pipe->nmp.size - pipe->writePos; + memcpy(pipe->buffer + pipe->writePos, source, firstCopySize); + memcpy(pipe->buffer, source + firstCopySize, sendSize - firstCopySize); + } else { + memcpy(pipe->buffer + pipe->writePos, source, sendSize); + } + pipe->writePos += sendSize; + pipe->writePos %= pipe->nmp.size; + + ERROR_LOG(HLE, "UNIMPL sceKernelSendMsgPipe(%i, %08x, %i, %i, %08x, %08x)", uid, sendBufAddr, sendSize, waitMode, resultAddr, timeoutPtr); RETURN(0); + __KernelCheckCallbacks(); } void sceKernelTrySendMsgPipe() @@ -117,8 +182,38 @@ void sceKernelTrySendMsgPipe() u32 sendSize = PARAM(2); int waitMode = PARAM(3); u32 resultAddr = PARAM(4); + u32 timeoutPtr = PARAM(5); - ERROR_LOG(HLE, "UNIMPL sceKernelTrySendMsgPipe(%i, %08x, %i, %i, %08x)", uid, sendBufAddr, sendSize, waitMode, resultAddr); + u32 error; + MsgPipe *pipe = kernelObjects.Get(uid, error); + if (!pipe) { + ERROR_LOG(HLE, "sceKernelSendMsgPipe(%i) - ERROR %08x", uid, error); + RETURN(error); + return; + } + + if (sendSize > pipe->nmp.freeSize) { + // TODO: Block + ERROR_LOG(HLE, "sceKernelSendMsgPipe(%i) - Message won't fit", uid, error); + RETURN(SCE_KERNEL_ERROR_MPP_FULL); + return; + } + + const u8 *source = Memory::GetPointer(sendBufAddr); + + int destIndex = pipe->writePos; + if (pipe->writePos + sendSize > pipe->nmp.size) { + // Split in two + int firstCopySize = pipe->nmp.size - pipe->writePos; + memcpy(pipe->buffer + pipe->writePos, source, firstCopySize); + memcpy(pipe->buffer, source + firstCopySize, sendSize - firstCopySize); + } else { + memcpy(pipe->buffer + pipe->writePos, source, sendSize); + } + pipe->writePos += sendSize; + pipe->writePos %= pipe->nmp.size; + + ERROR_LOG(HLE, "UNIMPL sceKernelSendMsgPipe(%i, %08x, %i, %i, %08x, %08x)", uid, sendBufAddr, sendSize, waitMode, resultAddr, timeoutPtr); RETURN(0); } @@ -132,7 +227,7 @@ void sceKernelReceiveMsgPipe() u32 timeoutPtr = PARAM(5); ERROR_LOG(HLE, "UNIMPL sceKernelReceiveMsgPipe(%i, %08x, %i, %i, %08x, %08x)", uid, receiveBufAddr, receiveSize, waitMode, resultAddr, timeoutPtr); - RETURN(0); + RETURN(SCE_KERNEL_ERROR_MPP_EMPTY); } void sceKernelReceiveMsgPipeCB() @@ -145,7 +240,7 @@ void sceKernelReceiveMsgPipeCB() u32 timeoutPtr = PARAM(5); ERROR_LOG(HLE, "UNIMPL sceKernelReceiveMsgPipeCB(%i, %08x, %i, %i, %08x, %08x)", uid, receiveBufAddr, receiveSize, waitMode, resultAddr, timeoutPtr); - RETURN(0); + RETURN(SCE_KERNEL_ERROR_MPP_EMPTY); } void sceKernelTryReceiveMsgPipe() @@ -157,7 +252,7 @@ void sceKernelTryReceiveMsgPipe() u32 resultAddr = PARAM(4); ERROR_LOG(HLE, "UNIMPL sceKernelTryReceiveMsgPipe(%i, %08x, %i, %i, %08x)", uid, receiveBufAddr, receiveSize, waitMode, resultAddr); - RETURN(0); + RETURN(SCE_KERNEL_ERROR_MPP_EMPTY); } void sceKernelCancelMsgPipe() diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 01be46714b..40448dca8a 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -110,13 +110,14 @@ u32 sceKernelLockMutex(u32 id, u32 count, u32 timeoutPtr) else { // Yeah, we need to block. Somehow. + ERROR_LOG(HLE,"Mutex should block!"); } return 0; } u32 sceKernelLockMutexCB(u32 id, u32 count, u32 timeoutPtr) { - DEBUG_LOG(HLE,"UNIMPL sceKernelLockMutexCB(%i, %i, %08x)", id, count, timeoutPtr); + ERROR_LOG(HLE,"UNIMPL sceKernelLockMutexCB(%i, %i, %08x)", id, count, timeoutPtr); return 0; } @@ -148,30 +149,36 @@ struct NativeLwMutex void sceKernelCreateLwMutex() { - DEBUG_LOG(HLE,"UNIMPL sceKernelCreateLwMutex()"); + ERROR_LOG(HLE,"UNIMPL sceKernelCreateLwMutex()"); RETURN(0); } void sceKernelDeleteLwMutex() { - DEBUG_LOG(HLE,"UNIMPL sceKernelDeleteLwMutex()"); + ERROR_LOG(HLE,"UNIMPL sceKernelDeleteLwMutex()"); RETURN(0); } void sceKernelTryLockLwMutex() { - DEBUG_LOG(HLE,"UNIMPL sceKernelTryLockLwMutex()"); + ERROR_LOG(HLE,"UNIMPL sceKernelTryLockLwMutex()"); RETURN(0); } void sceKernelLockLwMutex() { - DEBUG_LOG(HLE,"UNIMPL sceKernelLockLwMutex()"); + ERROR_LOG(HLE,"UNIMPL sceKernelLockLwMutex()"); + RETURN(0); +} + +void sceKernelLockLwMutexCB() +{ + ERROR_LOG(HLE,"UNIMPL sceKernelLockLwMutexCB()"); RETURN(0); } void sceKernelUnlockLwMutex() { - DEBUG_LOG(HLE,"UNIMPL void sceKernelUnlockLwMutex()"); + ERROR_LOG(HLE,"UNIMPL void sceKernelUnlockLwMutex()"); RETURN(0); } \ No newline at end of file diff --git a/Core/HLE/sceKernelMutex.h b/Core/HLE/sceKernelMutex.h index eac78d4e4f..f5f19c67fb 100644 --- a/Core/HLE/sceKernelMutex.h +++ b/Core/HLE/sceKernelMutex.h @@ -28,4 +28,5 @@ void sceKernelCreateLwMutex(); void sceKernelDeleteLwMutex(); void sceKernelTryLockLwMutex(); void sceKernelLockLwMutex(); +void sceKernelLockLwMutexCB(); void sceKernelUnlockLwMutex(); \ No newline at end of file diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index 493572b714..161ff6efb8 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -137,7 +137,7 @@ retry: int wVal = (int)__KernelGetWaitValue(id, error); if (wVal <= s->ns.currentCount) { - __KernelResumeThread(id); + __KernelResumeThreadFromWait(id); s->ns.currentCount -= wVal; wokeThreads = true; s->waitingThreads.erase(iter); diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index deb06d73e2..2b52bb6af5 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -34,15 +34,6 @@ #include "sceKernelModule.h" #include "sceKernelInterrupt.h" -enum ThreadStatus -{ - THREADSTATUS_RUNNING = 1, - THREADSTATUS_READY = 2, - THREADSTATUS_WAIT = 4, - THREADSTATUS_SUSPEND = 8, - THREADSTATUS_DORMANT = 16, - THREADSTATUS_DEAD = 32, -}; enum { ERROR_KERNEL_THREAD_ALREADY_DORMANT = 0x800201a2, @@ -87,106 +78,6 @@ struct SceKernelSysClock { u32 hi; }; -// Real PSP struct, don't change the fields -struct NativeThread -{ - u32 nativeSize; - char name[KERNELOBJECT_MAX_NAME_LENGTH+1]; - - // Threading stuff - u32 attr; - u32 status; - u32 entrypoint; - u32 initialStack; - u32 stackSize; - u32 gpreg; - - int initialPriority; - int currentPriority; - WaitType waitType; - SceUID waitID; - int wakeupCount; - int exitStatus; - SceKernelSysClock runForClocks; - int numInterruptPreempts; - int numThreadPreempts; - int numReleases; -}; - -class Thread : public KernelObject -{ -public: - const char *GetName() {return nt.name;} - const char *GetTypeName() {return "Thread";} - void GetQuickInfo(char *ptr, int size) - { - sprintf(ptr, "pc= %08x sp= %08x %s %s %s %s %s %s (wt=%i wid=%i wv= %08x )", - context.pc, context.r[MIPS_REG_SP], - (nt.status & THREADSTATUS_RUNNING) ? "RUN" : "", - (nt.status & THREADSTATUS_READY) ? "READY" : "", - (nt.status & THREADSTATUS_WAIT) ? "WAIT" : "", - (nt.status & THREADSTATUS_SUSPEND) ? "SUSPEND" : "", - (nt.status & THREADSTATUS_DORMANT) ? "DORMANT" : "", - (nt.status & THREADSTATUS_DEAD) ? "DEAD" : "", - nt.waitType, - nt.waitID, - waitValue); - } - - static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; } - - int GetIDType() const { return SCE_KERNEL_TMID_Thread; } - - bool AllocateStack(u32 &stackSize) - { - if (nt.attr & PSP_THREAD_ATTR_KERNEL) - { - // Allocate stacks for kernel threads (idle) in kernel RAM - stackBlock = kernelMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str()); - } - else - { - stackBlock = userMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str()); - } - if (stackBlock == (u32)-1) - { - ERROR_LOG(HLE, "Failed to allocate stack for thread"); - return false; - } - // Fill the stack. - Memory::Memset(stackBlock, 0xFF, stackSize); - context.r[MIPS_REG_SP] = stackBlock + stackSize; - nt.initialStack = context.r[MIPS_REG_SP]; - nt.stackSize = stackSize; - // What's this 512? - context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP] - 512; - context.r[MIPS_REG_SP] -= 512; - return true; - } - - ~Thread() - { - if (nt.attr & PSP_THREAD_ATTR_KERNEL) { - kernelMemory.Free(stackBlock); - } else { - userMemory.Free(stackBlock); - } - } - - NativeThread nt; - - u32 waitValue; - bool sleeping; - - bool isProcessingCallbacks; - - ThreadContext context; - - std::set registeredCallbacks[THREAD_CALLBACK_NUM_TYPES]; - std::list readyCallbacks[THREAD_CALLBACK_NUM_TYPES]; - - u32 stackBlock; -}; struct NativeCallback { @@ -239,6 +130,123 @@ public: bool forceDelete; }; +// Real PSP struct, don't change the fields +struct NativeThread +{ + u32 nativeSize; + char name[KERNELOBJECT_MAX_NAME_LENGTH+1]; + + // Threading stuff + u32 attr; + u32 status; + u32 entrypoint; + u32 initialStack; + u32 stackSize; + u32 gpreg; + + int initialPriority; + int currentPriority; + WaitType waitType; + SceUID waitID; + int wakeupCount; + int exitStatus; + SceKernelSysClock runForClocks; + int numInterruptPreempts; + int numThreadPreempts; + int numReleases; +}; + +struct ThreadWaitInfo { + u32 waitValue; +}; + +class Thread : public KernelObject +{ +public: + const char *GetName() {return nt.name;} + const char *GetTypeName() {return "Thread";} + void GetQuickInfo(char *ptr, int size) + { + sprintf(ptr, "pc= %08x sp= %08x %s %s %s %s %s %s (wt=%i wid=%i wv= %08x )", + context.pc, context.r[MIPS_REG_SP], + (nt.status & THREADSTATUS_RUNNING) ? "RUN" : "", + (nt.status & THREADSTATUS_READY) ? "READY" : "", + (nt.status & THREADSTATUS_WAIT) ? "WAIT" : "", + (nt.status & THREADSTATUS_SUSPEND) ? "SUSPEND" : "", + (nt.status & THREADSTATUS_DORMANT) ? "DORMANT" : "", + (nt.status & THREADSTATUS_DEAD) ? "DEAD" : "", + nt.waitType, + nt.waitID, + waitInfo.waitValue); + } + + static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; } + + int GetIDType() const { return SCE_KERNEL_TMID_Thread; } + + bool AllocateStack(u32 &stackSize) + { + if (nt.attr & PSP_THREAD_ATTR_KERNEL) + { + // Allocate stacks for kernel threads (idle) in kernel RAM + stackBlock = kernelMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str()); + } + else + { + stackBlock = userMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str()); + } + if (stackBlock == (u32)-1) + { + ERROR_LOG(HLE, "Failed to allocate stack for thread"); + return false; + } + // Fill the stack. + Memory::Memset(stackBlock, 0xFF, stackSize); + context.r[MIPS_REG_SP] = stackBlock + stackSize; + nt.initialStack = context.r[MIPS_REG_SP]; + nt.stackSize = stackSize; + // What's this 512? + context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP] - 512; + context.r[MIPS_REG_SP] -= 512; + return true; + } + + ~Thread() + { + if (nt.attr & PSP_THREAD_ATTR_KERNEL) { + kernelMemory.Free(stackBlock); + } else { + userMemory.Free(stackBlock); + } + } + + // Utils + bool isRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; } + bool isStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } + bool isReady() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } + bool isWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } + bool isSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } + + NativeThread nt; + + ThreadWaitInfo waitInfo; + bool sleeping; + + bool isProcessingCallbacks; + + ThreadContext context; + + std::set registeredCallbacks[THREAD_CALLBACK_NUM_TYPES]; + std::list readyCallbacks[THREAD_CALLBACK_NUM_TYPES]; + + std::list pendingMipsCalls; + + u32 stackBlock; +}; + +void __KernelExecuteMipsCallOnCurrentThread(int callId); + + int g_inCbCount = 0; Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr); @@ -257,7 +265,7 @@ SceUID threadIdleID[2]; int eventScheduledWakeup; -bool dispatchSuspended = false; +bool dispatchEnabled = true; // This seems nasty @@ -273,7 +281,7 @@ Thread *__GetCurrentThread() { return currentThread; } -u32 __KernelCallbackReturnAddress() +u32 __KernelMipsCallReturnAddress() { return cbReturnHackAddr; } @@ -289,7 +297,7 @@ void __KernelThreadingInit() { u32 blockSize = 4 * 4 + 4 * 2 * 3; // One 16-byte thread plus 3 8-byte "hacks" - dispatchSuspended = false; + dispatchEnabled = true; idleThreadHackAddr = kernelMemory.Alloc(blockSize, false, "threadrethack"); // Make sure it got allocated where we expect it... at the very start of kernel RAM @@ -362,7 +370,7 @@ u32 __KernelGetWaitValue(SceUID threadID, u32 &error) Thread *t = kernelObjects.Get(threadID, error); if (t) { - return t->waitValue; + return t->waitInfo.waitValue; } else { @@ -529,7 +537,7 @@ bool __KernelTriggerWait(WaitType type, int id, bool dontSwitch) return true; } -u32 __KernelResumeThread(SceUID threadID) +u32 __KernelResumeThreadFromWait(SceUID threadID) { u32 error; Thread *t = kernelObjects.Get(threadID, error); @@ -538,11 +546,12 @@ u32 __KernelResumeThread(SceUID threadID) t->nt.status &= ~THREADSTATUS_WAIT; if (!(t->nt.status & (THREADSTATUS_SUSPEND | THREADSTATUS_WAIT))) t->nt.status |= THREADSTATUS_READY; + t->isProcessingCallbacks = false; return 0; } else { - ERROR_LOG(HLE, "__KernelResumeThread(%d): bad thread: %08x", threadID, error); + ERROR_LOG(HLE, "__KernelResumeThreadFromWait(%d): bad thread: %08x", threadID, error); return error; } } @@ -550,12 +559,11 @@ u32 __KernelResumeThread(SceUID threadID) // makes the current thread wait for an event void __KernelWaitCurThread(WaitType type, SceUID waitID, u32 waitValue, int timeout, bool processCallbacks) { - currentThread->nt.status = THREADSTATUS_WAIT; currentThread->nt.waitID = waitID; currentThread->nt.waitType = type; + __KernelChangeThreadState(currentThread, THREADSTATUS_WAIT); currentThread->nt.numReleases++; - currentThread->waitValue = waitValue; - currentThread->isProcessingCallbacks = processCallbacks; + currentThread->waitInfo.waitValue = waitValue; if (timeout) { // TODO: @@ -566,7 +574,8 @@ void __KernelWaitCurThread(WaitType type, SceUID waitID, u32 waitValue, int time // TODO: time waster char temp[256]; sprintf(temp, "started wait %s", waitTypeStrings[(int)type]); - __KernelReSchedule(temp); + + __KernelReSchedule(processCallbacks, temp); // TODO: Remove thread from Ready queue? } @@ -594,23 +603,15 @@ void __KernelRemoveFromThreadQueue(Thread *t) } } -void __KernelReSchedule(const char *reason) -{ - // cancel rescheduling when in interrupt or callback, otherwise everything will be fucked up - if (__IsInInterrupt() || __KernelInCallback()) - { - reason = "WTF"; - return; - } +Thread *__KernelNextThread() { // round-robin scheduler // seems to work ? // not accurate! -retry: int bestthread = -1; - int prio=0xffffff; + int prio = 0xffffff; int next = 0; - for (size_t i=0; icontext); - DEBUG_LOG(HLE,"Context saved (%s): %i - %s - pc: %08x", reason, currentThread->GetUID(), currentThread->GetName(), currentMIPS->pc); - } - currentThread = threadqueue[bestthread]; - __KernelLoadContext(¤tThread->context); - DEBUG_LOG(HLE,"Context loaded (%s): %i - %s - pc: %08x", reason, currentThread->GetUID(), currentThread->GetName(), currentMIPS->pc); + __KernelSwitchContext(nextThread, reason); return; } else { - _dbg_assert_msg_(HLE,0,"No threads available to schedule! There should be at least one idle thread available."); // This shouldn't happen anymore now that we have idle threads. - - // No threads want to run : increase timers, skip time in general - // MessageBox(0,"Error: no thread to transition to",0,0); - - // DEBUG_LOG(HLE,"No thread to transition to, idling"); + _dbg_assert_msg_(HLE,0,"No threads available to schedule! There should be at least one idle thread available."); CoreTiming::Idle(); goto retry; } } + +void __KernelReSchedule(bool doCallbacks, const char *reason) +{ + Thread *thread = currentThread; + if (doCallbacks) + { + if (thread) + thread->isProcessingCallbacks = doCallbacks; + __KernelCheckCallbacks(); + } + __KernelReSchedule(reason); + if (doCallbacks && thread == currentThread) { + if (thread->isRunning()) { + thread->isProcessingCallbacks = false; + } + } +} + + ////////////////////////////////////////////////////////////////////////// // Thread Management @@ -729,7 +755,7 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 t->nt.status = THREADSTATUS_DORMANT; t->nt.waitType = WAITTYPE_NONE; t->nt.waitID = 0; - t->waitValue = 0; + memset(&t->waitInfo, 0, sizeof(t->waitInfo)); t->nt.exitStatus = 0; t->nt.numInterruptPreempts = 0; t->nt.numReleases = 0; @@ -737,6 +763,7 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 t->nt.runForClocks.low = 0; t->nt.runForClocks.hi = 0; t->nt.wakeupCount = 0; + t->isProcessingCallbacks = false; if (moduleID) t->nt.gpreg = __KernelGetModuleGP(moduleID); else @@ -863,10 +890,10 @@ void sceKernelGetThreadStackFreeSize() RETURN(sz & ~3); } - -void _sceKernelReturnFromThread() +// Internal function +void __KernelReturnFromThread() { - INFO_LOG(HLE,"_sceKernelReturnFromThread : %s", currentThread->GetName()); + INFO_LOG(HLE,"__KernelReturnFromThread : %s", currentThread->GetName()); currentThread->nt.exitStatus = currentThread->context.r[2]; currentThread->nt.status = THREADSTATUS_DORMANT; @@ -935,17 +962,17 @@ void sceKernelExitDeleteThread() u32 sceKernelSuspendDispatchThread() { - u32 oldDispatchSuspended = dispatchSuspended; - dispatchSuspended = true; + u32 oldDispatchSuspended = !dispatchEnabled; + dispatchEnabled = false; DEBUG_LOG(HLE,"%i=sceKernelSuspendDispatchThread()", oldDispatchSuspended); return oldDispatchSuspended; } u32 sceKernelResumeDispatchThread(u32 suspended) { - u32 oldDispatchSuspended = dispatchSuspended; - dispatchSuspended = suspended; - DEBUG_LOG(HLE,"%i=sceKernelResumeDispatchThread(%i)",oldDispatchSuspended, suspended); + u32 oldDispatchSuspended = !dispatchEnabled; + dispatchEnabled = !suspended; + DEBUG_LOG(HLE,"%i=sceKernelResumeDispatchThread(%i)", oldDispatchSuspended, suspended); return oldDispatchSuspended; } @@ -1063,6 +1090,7 @@ void sceKernelDelayThreadCB() __KernelScheduleWakeup(curThread, usec); __KernelWaitCurThread(WAITTYPE_DELAY, curThread, 0, 0, true); __KernelCheckCallbacks(); + __KernelExecutePendingMipsCalls(); } void sceKernelDelayThread() @@ -1080,48 +1108,45 @@ void sceKernelDelayThread() ////////////////////////////////////////////////////////////////////////// void sceKernelWakeupThread() { - SceUID id = PARAM(0); + SceUID uid = PARAM(0); u32 error; - Thread *t = kernelObjects.Get(id, error); + Thread *t = kernelObjects.Get(uid, error); if (t) { - t->nt.wakeupCount++; - DEBUG_LOG(HLE,"sceKernelWakeupThread(%i) - wakeupCount incremented to %i",id,t->nt.wakeupCount); - if (t->nt.waitType == WAITTYPE_SLEEP && t->nt.wakeupCount>=0) - { - __KernelResumeThread(id); + if (t->nt.waitType != WAITTYPE_SLEEP) { + t->nt.wakeupCount++; + DEBUG_LOG(HLE,"sceKernelWakeupThread(%i) - wakeupCount incremented to %i", uid, t->nt.wakeupCount); + RETURN(0); + } else { + __KernelResumeThreadFromWait(uid); + __KernelReSchedule("wakeup"); } } - RETURN(0); +} + +static void __KernelSleepThread(bool doCallbacks) { + DEBUG_LOG(HLE,"sceKernelSleepThread() - wakeupCount decremented to %i", currentThread->nt.wakeupCount); + if (currentThread->nt.wakeupCount > 0) { + currentThread->nt.wakeupCount--; + RETURN(0); + } else { + RETURN(0); + __KernelWaitCurThread(WAITTYPE_SLEEP, 0, 0, 0, doCallbacks); + } } void sceKernelSleepThread() { - currentThread->nt.wakeupCount--; - DEBUG_LOG(HLE,"sceKernelSleepThread() - wakeupCount decremented to %i", currentThread->nt.wakeupCount); - if (currentThread->nt.wakeupCount < 0) - __KernelWaitCurThread(WAITTYPE_SLEEP, 0, 0, 0, false); - else - { - RETURN(0); - } + __KernelSleepThread(false); } //the homebrew PollCallbacks void sceKernelSleepThreadCB() { - DEBUG_LOG(HLE,"sceKernelSleepThreadCB()"); - //set it to waiting - currentThread->nt.wakeupCount--; - DEBUG_LOG(HLE,"sceKernelSleepThreadCB() - wakeupCount decremented to %i", currentThread->nt.wakeupCount); - if (currentThread->nt.wakeupCount < 0) { - __KernelWaitCurThread(WAITTYPE_SLEEP, 0, 0, 0, true); - __KernelCheckCallbacks(); - } - else - { - RETURN(0); - } + DEBUG_LOG(HLE, "sceKernelSleepThreadCB()"); + __KernelSleepThread(true); + __KernelCheckCallbacks(); + __KernelExecutePendingMipsCalls(); } void sceKernelWaitThreadEnd() @@ -1179,6 +1204,8 @@ void sceKernelResumeThread() RETURN(0); } + + ////////////////////////////////////////////////////////////////////////// // CALLBACKS ////////////////////////////////////////////////////////////////////////// @@ -1287,84 +1314,280 @@ void sceKernelReferCallbackStatus() } } - -// Context switches to the thread and executes the callback. -u32 __KernelRunCallbackOnThread(SceUID cbId, Thread *thread) -{ - if (g_inCbCount > 0) { - WARN_LOG(HLE, "__KernelRunCallbackOnThread: Already in a callback!"); +// Owns outstanding MIPS calls and provides a way to get them by ID. +// TODO: MipsCall structs are kinda big, try to cut down on the copying by owning pointers instead. +class MipsCallManager { +public: + MipsCallManager() : idGen_(0) {} + int add(MipsCall *call) { + int id = genId(); + calls_.insert(std::pair(id, call)); + return id; + } + MipsCall *get(int id) { + return calls_[id]; + } + MipsCall *pop(int id) { + MipsCall *temp = calls_[id]; + calls_.erase(id); + return temp; } +private: + int genId() { return ++idGen_; } + std::map calls_; + int idGen_; +}; + +MipsCallManager mipsCalls; + + +class ActionAfterMipsCall : public Action +{ +public: + virtual void run(); + Thread *thread; + + // Saved thread state + int status; + WaitType waitType; + int waitId; + ThreadWaitInfo waitInfo; + bool isProcessingCallbacks; + + Action *chainedAction; +}; + +void ActionAfterMipsCall::run() { + thread->nt.status = status; + thread->nt.waitType = waitType; + thread->nt.waitID = waitId; + thread->waitInfo = waitInfo; + thread->isProcessingCallbacks = isProcessingCallbacks; + + if (chainedAction) { + chainedAction->run(); + delete chainedAction; + } +} + +void __KernelSwitchContext(Thread *target, const char *reason) +{ + if (currentThread) // It might just have been deleted. + { + __KernelSaveContext(¤tThread->context); + DEBUG_LOG(HLE,"Context saved (%s): %i - %s - pc: %08x", reason, currentThread->GetUID(), currentThread->GetName(), currentMIPS->pc); + } + currentThread = target; + __KernelLoadContext(¤tThread->context); + DEBUG_LOG(HLE,"Context loaded (%s): %i - %s - pc: %08x", reason, currentThread->GetUID(), currentThread->GetName(), currentMIPS->pc); + + __KernelExecutePendingMipsCalls(); +} + +void __KernelChangeThreadState(Thread *thread, ThreadStatus newStatus) { + if (!thread || thread->nt.status == newStatus) + return; + + if (!dispatchEnabled && thread == currentThread && newStatus != THREADSTATUS_RUNNING) { + ERROR_LOG(HLE, "Dispatching suspended, not changing thread state"); + return; + } + + // TODO: JPSCP has many conditions here, like removing wait timeout actions etc. + // if (thread->nt.status == THREADSTATUS_WAIT && newStatus != THREADSTATUS_WAITSUSPEND) { + + thread->nt.status = newStatus; + + if (newStatus == THREADSTATUS_WAIT) { + if (thread->nt.waitType == WAITTYPE_NONE) { + ERROR_LOG(HLE, "Waittype none not allowed here"); + } + + // Schedule deletion of stopped threads here. if (thread->isStopped()) + } +} + + +bool __CanExecuteCallbackNow(Thread *thread) { + return g_inCbCount == 0; +} + +void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, bool returnVoid, std::vector args) { + if (thread) { + ActionAfterMipsCall *after = new ActionAfterMipsCall(); + after->chainedAction = afterAction; + after->thread = thread; + after->status = thread->nt.status; + after->waitType = thread->nt.waitType; + after->waitId = thread->nt.waitID; + after->waitInfo = thread->waitInfo; + + afterAction = after; + + // Release thread from waiting + thread->nt.waitType = WAITTYPE_NONE; + + __KernelChangeThreadState(thread, THREADSTATUS_READY); + } + + MipsCall *call = new MipsCall(); + call->entryPoint = entryPoint; + for (int i = 0; i < args.size(); i++) { + call->args[i] = args[i]; + } + call->numArgs = args.size(); + call->doAfter = afterAction; + call->tag = "callAddress"; + + int callId = mipsCalls.add(call); + + bool called = false; + if (!thread || thread == currentThread) { + if (__CanExecuteCallbackNow(thread)) { + thread = currentThread; + __KernelChangeThreadState(thread, THREADSTATUS_RUNNING); + __KernelExecuteMipsCallOnCurrentThread(callId); + called = true; + } + } + + if (!called) { + DEBUG_LOG(HLE, "Making mipscall pending on thread"); + thread->pendingMipsCalls.push_back(callId); + } +} + +void __KernelExecuteMipsCallOnCurrentThread(int callId) +{ + if (g_inCbCount > 0) { + WARN_LOG(HLE, "__KernelExecuteMipsCallOnCurrentThread: Already in a callback!"); + } + DEBUG_LOG(HLE, "Executing mipscall %i", callId); + MipsCall *call = mipsCalls.get(callId); + + // Save the few regs that need saving + call->savedPc = currentMIPS->pc; + call->savedRa = currentMIPS->r[MIPS_REG_RA]; + call->savedV0 = currentMIPS->r[MIPS_REG_V0]; + call->savedV1 = currentMIPS->r[MIPS_REG_V1]; + call->savedIdRegister = currentMIPS->r[MIPS_REG_CALL_ID]; + call->returnVoid = false; + + // Set up the new state + currentMIPS->pc = call->entryPoint; + currentMIPS->r[MIPS_REG_RA] = __KernelMipsCallReturnAddress(); + currentMIPS->r[MIPS_REG_CALL_ID] = callId; + for (int i = 0; i < call->numArgs; i++) { + currentMIPS->r[MIPS_REG_A0 + i] = call->args[i]; + } + + g_inCbCount++; +} + +void __KernelReturnFromMipsCall() +{ + int callId = currentMIPS->r[MIPS_REG_CALL_ID]; + + MipsCall *call = mipsCalls.pop(callId); + + // Value returned by the callback function + u32 retVal = currentMIPS->r[MIPS_REG_V0]; + DEBUG_LOG(HLE,"__KernelReturnFromMipsCall(), returned %08x", retVal); + + // Should also save/restore wait state here. + if (call->doAfter) + call->doAfter->run(); + + currentMIPS->pc = call->savedPc; + currentMIPS->r[MIPS_REG_RA] = call->savedRa; + currentMIPS->r[MIPS_REG_V0] = call->savedV0; + currentMIPS->r[MIPS_REG_V1] = call->savedV1; + currentMIPS->r[MIPS_REG_CALL_ID] = call->savedIdRegister; + + g_inCbCount--; + + // yeah! back in the real world, let's keep going. Should we process more callbacks? + __KernelCheckCallbacks(); + if (!__KernelExecutePendingMipsCalls()) + { + // We should definitely reschedule as we might still be asleep. - except if we came from checkcallbacks? + __KernelReSchedule("return from callback"); + } +} + +bool __KernelExecutePendingMipsCalls() +{ + Thread *thread = __GetCurrentThread(); + + if (thread->pendingMipsCalls.empty()) { + // Nothing to do + return false; + } + + if (__CanExecuteCallbackNow(thread)) + { + // Pop off the first pending mips call + int callId = thread->pendingMipsCalls.front(); + thread->pendingMipsCalls.pop_front(); + __KernelExecuteMipsCallOnCurrentThread(callId); + return true; + } + return false; +} + + +class ActionAfterCallback : public Action +{ +public: + ActionAfterCallback(SceUID cbId_) : cbId(cbId_) {} + virtual void run(); + SceUID cbId; +}; + +// Executes the callback, when it next is context switched to. +void __KernelRunCallbackOnThread(SceUID cbId, Thread *thread) +{ u32 error; Callback *cb = kernelObjects.Get(cbId, error); if (!cb) { - return error; + ERROR_LOG(HLE, "__KernelRunCallbackOnThread: Bad cbId %i", cbId); + return; } - // First, context switch to the thread. - Thread *curThread = __GetCurrentThread(); - if (thread != curThread) { - __KernelSaveContext(&curThread->context); - __KernelLoadContext(&thread->context); - } - - //Alright, we're on the right thread + DEBUG_LOG(HLE, "__KernelRunCallbackOnThread: Turning callback %i into pending mipscall", cbId); - // Save the few regs that need saving - cb->savedPC = currentMIPS->pc; - cb->savedRA = currentMIPS->r[MIPS_REG_RA]; - cb->savedV0 = currentMIPS->r[MIPS_REG_V0]; - cb->savedV1 = currentMIPS->r[MIPS_REG_V1]; - cb->savedIdRegister = currentMIPS->r[MIPS_REG_CB_ID]; + // Alright, we're on the right thread + // Should save/restore wait state? - // Set up the new state - // TODO: check? - currentMIPS->r[MIPS_REG_A0] = cb->nc.notifyCount; - currentMIPS->r[MIPS_REG_A1] = cb->nc.notifyArg; - currentMIPS->r[MIPS_REG_A2] = cb->nc.commonArgument; - currentMIPS->pc = cb->nc.entrypoint; - currentMIPS->r[MIPS_REG_RA] = __KernelCallbackReturnAddress(); - currentMIPS->r[MIPS_REG_CB_ID] = cbId; + std::vector args; + args.push_back(cb->nc.notifyCount); + args.push_back(cb->nc.notifyArg); + args.push_back(cb->nc.commonArgument); // Clear the notify count / arg cb->nc.notifyCount = 0; cb->nc.notifyArg = 0; - g_inCbCount++; - return 0; + Action *action = new ActionAfterCallback(cbId); + __KernelCallAddress(thread, cb->nc.entrypoint, action, false, args); } -void _sceKernelReturnFromCallback() -{ - SceUID cbId = currentMIPS->r[MIPS_REG_CB_ID]; - // Value returned by the callback function - u32 retVal = currentMIPS->r[MIPS_REG_V0]; - DEBUG_LOG(HLE,"_sceKernelReturnFromCallback(cbId=%i), returned %08x", cbId, retVal); - - u32 error; - Callback *cb = kernelObjects.Get(cbId, error); - if (!cb) - { - ERROR_LOG(HLE, "_sceKernelReturnFromCallback(): INVALID CBID %i in register! we're screwed", cbId); - return; +void ActionAfterCallback::run() { + if (cbId != -1) { + u32 error; + Callback *cb = kernelObjects.Get(cbId, error); + if (cb) + { + DEBUG_LOG(HLE, "Left callback %i - %s", cbId, cb->nc.name); + // Callbacks that don't return 0 are deleted. But should this be done here? + if (currentMIPS->r[MIPS_REG_V0] != 0 || cb->forceDelete) + { + DEBUG_LOG(HLE, "ActionAfterCallback::run(): Callback returned non-zero, gets deleted!"); + kernelObjects.Destroy(cbId); + } + } } - - currentMIPS->pc = cb->savedPC; - currentMIPS->r[MIPS_REG_RA] = cb->savedRA; - currentMIPS->r[MIPS_REG_V0] = cb->savedV0; - currentMIPS->r[MIPS_REG_V1] = cb->savedV1; - currentMIPS->r[MIPS_REG_CB_ID] = cb->savedIdRegister; - - // Callbacks that don't return 0 are deleted. But should this be done here? - if (retVal != 0 || cb->forceDelete) - { - DEBUG_LOG(HLE, "_sceKernelReturnFromCallback(): Callback returned non-zero, gets deleted!"); - kernelObjects.Destroy(cbId); - } - - g_inCbCount--; - - // yeah! back in the real world, let's keep going. Should we process more callbacks? } // Check callbacks on the current thread only. @@ -1377,7 +1600,7 @@ bool __KernelCheckThreadCallbacks(Thread *thread) { if (thread->readyCallbacks[i].size()) { SceUID readyCallback = thread->readyCallbacks[i].front(); thread->readyCallbacks[i].pop_front(); - __KernelRunCallbackOnThread(readyCallback, thread); + __KernelRunCallbackOnThread(readyCallback, thread); // makes pending return true; } } @@ -1386,7 +1609,8 @@ bool __KernelCheckThreadCallbacks(Thread *thread) { // Checks for callbacks on all threads bool __KernelCheckCallbacks() { - SceUID currentThread = __KernelGetCurThread(); + // SceUID currentThread = __KernelGetCurThread(); + // currentThread->isProcessingCallbacks = true; // do { bool processed = false; @@ -1400,25 +1624,25 @@ bool __KernelCheckCallbacks() { return processed; } -u32 sceKernelCheckCallback() -{ +void sceKernelCheckCallback() { Thread *curThread = __GetCurrentThread(); // This thread can now process callbacks. curThread->isProcessingCallbacks = true; - int callbacksProcessed = __KernelCheckThreadCallbacks(curThread) ? 1 : 0; + bool callbacksProcessed = __KernelCheckThreadCallbacks(curThread); // Note - same thread as above - checking callbacks may switch threads. curThread->isProcessingCallbacks = false; if (callbacksProcessed) { + RETURN(1); ERROR_LOG(HLE,"sceKernelCheckCallback() - processed a callback."); + __KernelExecutePendingMipsCalls(); } else { + RETURN(0); DEBUG_LOG(HLE,"sceKernelCheckCallback() - no callbacks to process, doing nothing"); } - - return callbacksProcessed; } bool __KernelInCallback() diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index a9ff6316b5..c1bd307547 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -106,8 +106,7 @@ u32 __KernelResumeThread(SceUID threadID); // can return an error value u32 __KernelGetWaitValue(SceUID threadID, u32 &error); void __KernelWaitCurThread(WaitType type, SceUID waitId, u32 waitValue, int timeout, bool processCallbacks); void __KernelReSchedule(const char *reason = "no reason"); - - +void __KernelReSchedule(bool doCallbacks, const char *reason); // Registered callback types enum RegisteredCallbackType { @@ -133,12 +132,12 @@ u32 __KernelNotifyCallbackType(RegisteredCallbackType type, SceUID cbId, int not SceUID __KernelGetCurThread(); void __KernelSetupRootThread(SceUID moduleId, int args, const char *argp, int prio, int stacksize, int attr); //represents the real PSP elf loader, run before execution void __KernelStartIdleThreads(); +void __KernelReturnFromThread(); // Called as HLE function -void _sceKernelReturnFromThread(); void _sceKernelIdle(); -u32 __KernelCallbackReturnAddress(); -u32 __KernelInterruptReturnAddress(); +u32 __KernelMipsCallReturnAddress(); +u32 __KernelInterruptReturnAddress(); // TODO: remove // Internal access - used by sceSetGeCallback u32 __KernelCreateCallback(const char *name, u32 entrypoint, u32 signalArg); @@ -148,11 +147,46 @@ void sceKernelDeleteCallback(); void sceKernelNotifyCallback(); void sceKernelCancelCallback(); void sceKernelGetCallbackCount(); -void _sceKernelReturnFromCallback(); -u32 sceKernelCheckCallback(); +void sceKernelCheckCallback(); void sceKernelGetCallbackCount(); void sceKernelReferCallbackStatus(); +void __KernelReturnFromMipsCall(); // Called as HLE function bool __KernelInCallback(); // Should be called by (nearly) all ...CB functions. bool __KernelCheckCallbacks(); +class Thread; +void __KernelSwitchContext(Thread *target, const char *reason); +u32 __KernelResumeThreadFromWait(SceUID threadID); +bool __KernelExecutePendingMipsCalls(); + +// A call into game code. These can be pending on a thread. +// Similar to Callback-s (NOT CallbackInfos) in JPCSP. +class Action; +struct MipsCall { + u32 entryPoint; + u32 cbId; + u32 args[6]; + int numArgs; + Action *doAfter; + u32 savedIdRegister; + u32 savedRa; + u32 savedPc; + u32 savedV0; + u32 savedV1; + bool returnVoid; + const char *tag; +}; +enum ThreadStatus +{ + THREADSTATUS_RUNNING = 1, + THREADSTATUS_READY = 2, + THREADSTATUS_WAIT = 4, + THREADSTATUS_SUSPEND = 8, + THREADSTATUS_DORMANT = 16, + THREADSTATUS_DEAD = 32, + + THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND +}; + +void __KernelChangeThreadState(Thread *thread, ThreadStatus newStatus); diff --git a/Core/HLE/sceKernelVTimer.cpp b/Core/HLE/sceKernelVTimer.cpp index b4c6858fcc..299e47fd6b 100644 --- a/Core/HLE/sceKernelVTimer.cpp +++ b/Core/HLE/sceKernelVTimer.cpp @@ -70,5 +70,6 @@ void sceKernelSetVTimerHandler() // Not sure why this is exposed... void _sceKernelReturnFromTimerHandler() { + DEBUG_LOG(HLE,"_sceKernelReturnFromTimerHandler"); } diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 8f76a63064..4f8b26f116 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -53,6 +53,12 @@ void sceMpegRingbufferConstruct() RETURN(0); } +void sceMpegRegistStream() +{ + DEBUG_LOG(HLE, "HACK sceMpegRegistStream(...)"); + RETURN(0); +} + const HLEFunction sceMpeg[] = { {0xe1ce83a7,0,"sceMpegGetAtracAu"}, @@ -62,7 +68,7 @@ const HLEFunction sceMpeg[] = {0xc132e22f,sceMpegQueryMemSize,"sceMpegQueryMemSize"}, {0x21ff80e4,0,"sceMpegQueryStreamOffset"}, {0x611e9e11,0,"sceMpegQueryStreamSize"}, - {0x42560f23,0,"sceMpegRegistStream"}, + {0x42560f23,sceMpegRegistStream,"sceMpegRegistStream"}, {0x591a4aa2,0,"sceMpegUnRegistStream"}, {0x707b7629,0,"sceMpegFlushAllStream"}, {0xa780cf7e,0,"sceMpegMallocAvcEsBuf"}, diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index fbde6010fc..530fa203cd 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -464,6 +464,14 @@ const HLEFunction sceUtility[] = {0xAB083EA9, 0, "sceUtilityScreenshotUpdate"}, {0xD81957B7, 0, "sceUtilityScreenshotGetStatus"}, {0x86A03A27, 0, "sceUtilityScreenshotContStart"}, + + {0x0D5BC6D2, 0, "sceUtilityLoadUsbModule"}, + {0xF64910F0, 0, "sceUtilityUnloadUsbModule"}, + + {0x24AC31EB, 0, "sceUtilityGamedataInstallInitStart"}, + {0x32E32DCB, 0, "sceUtilityGamedataInstallShutdownStart"}, + {0x4AECD179, 0, "sceUtilityGamedataInstallUpdate"}, + {0xB57E95D9, 0, "sceUtilityGamedataInstallGetStatus"}, }; void Register_sceUtility() diff --git a/Core/MIPS/MIPS.h b/Core/MIPS/MIPS.h index 522971f996..d08410e515 100644 --- a/Core/MIPS/MIPS.h +++ b/Core/MIPS/MIPS.h @@ -50,8 +50,8 @@ enum MIPS_REG_FP=30, MIPS_REG_RA=31, - // ID for callback is stored here - from JPCSP - MIPS_REG_CB_ID=MIPS_REG_S0, + // ID for mipscall "callback" is stored here - from JPCSP + MIPS_REG_CALL_ID=MIPS_REG_S0, }; enum