diff --git a/Common/Action.h b/Common/Action.h index dbe7fb3759..ae3524b923 100644 --- a/Common/Action.h +++ b/Common/Action.h @@ -1,6 +1,7 @@ #pragma once #include +#include "ChunkFile.h" // Sometimes you want to set something to happen later, without that later place really needing // to know about all the things that might happen. That's when you use an Action, and add it @@ -17,4 +18,6 @@ class Action public: virtual ~Action() {} virtual void run() = 0; + virtual void DoState(PointerWrap &p) = 0; + int actionTypeID; }; diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 76e690465c..3e18b4c6b8 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -143,10 +143,9 @@ void __KernelDoState(PointerWrap &p) kernelObjects.DoState(p); p.DoMarker("KernelObjects"); - // TODO: Put these in the correct order... __InterruptsDoState(p); __KernelMemoryDoState(p); - // TODO: __KernelThreadingDoState(p); + __KernelThreadingDoState(p); __KernelAlarmDoState(p); __KernelEventFlagDoState(p); __KernelMbxDoState(p); @@ -155,6 +154,9 @@ void __KernelDoState(PointerWrap &p) __KernelSemaDoState(p); // TODO: non-kernel modules // TODO: PPGe + + __InterruptsDoStateLate(p); + __KernelThreadingDoStateLate(p); } bool __KernelIsRunning() { diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index 445546f349..ecb1bef957 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -247,14 +247,20 @@ void __InterruptsDoState(PointerWrap &p) } intState.DoState(p); - for (int i = 0; i < PSP_NUMBER_INTERRUPTS; ++i) - intrHandlers[i].DoState(p); p.Do(pendingInterrupts, PendingInterrupt(0, 0)); p.Do(interruptsEnabled); p.Do(inInterrupt); p.DoMarker("sceKernelInterrupt"); } +void __InterruptsDoStateLate(PointerWrap &p) +{ + // We do these later to ensure the handlers have been registered. + for (int i = 0; i < PSP_NUMBER_INTERRUPTS; ++i) + intrHandlers[i].DoState(p); + p.DoMarker("sceKernelInterrupt Late"); +} + void __InterruptsShutdown() { for (int i = 0; i < PSP_NUMBER_INTERRUPTS; ++i) diff --git a/Core/HLE/sceKernelInterrupt.h b/Core/HLE/sceKernelInterrupt.h index 3a298b955b..6b60a7870f 100644 --- a/Core/HLE/sceKernelInterrupt.h +++ b/Core/HLE/sceKernelInterrupt.h @@ -109,6 +109,7 @@ typedef SubIntrHandler *(*SubIntrCreator)(); bool __IsInInterrupt(); void __InterruptsInit(); void __InterruptsDoState(PointerWrap &p); +void __InterruptsDoStateLate(PointerWrap &p); void __InterruptsShutdown(); void __TriggerInterrupt(int type, PSPInterrupt intno, int subInterrupts = -1); void __TriggerInterruptWithArg(int type, PSPInterrupt intno, int subintr, int arg); // For GE "callbacks" diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index d35e5d5138..701b51297c 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -136,6 +136,25 @@ KernelObject *__KernelModuleObject() return new Module; } +class AfterModuleEntryCall : public Action { +public: + AfterModuleEntryCall() {} + SceUID moduleID_; + u32 retValAddr; + virtual void run(); + virtual void DoState(PointerWrap &p) { + p.Do(moduleID_); + p.Do(retValAddr); + } + static Action *Create() { + return new AfterModuleEntryCall; + } +}; + +void AfterModuleEntryCall::run() { + Memory::Write_U32(retValAddr, currentMIPS->r[2]); +} + ////////////////////////////////////////////////////////////////////////// // MODULES ////////////////////////////////////////////////////////////////////////// @@ -168,13 +187,21 @@ struct SceKernelSMOption { ////////////////////////////////////////////////////////////////////////// // STATE BEGIN +static int actionAfterModule; static SceUID mainModuleID; // hack // STATE END ////////////////////////////////////////////////////////////////////////// +void __KernelModuleInit() +{ + actionAfterModule = __KernelRegisterActionType(AfterModuleEntryCall::Create); +} + void __KernelModuleDoState(PointerWrap &p) { p.Do(mainModuleID); + p.Do(actionAfterModule); + __KernelRestoreActionType(actionAfterModule, AfterModuleEntryCall::Create); p.DoMarker("sceKernelModule"); } @@ -533,6 +560,7 @@ bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param, std:: if (__KernelIsRunning()) __KernelShutdown(); + __KernelModuleInit(); __KernelInit(); PSPFileInfo info = pspFileSystem.GetFileInfo(filename); @@ -657,18 +685,6 @@ u32 sceKernelLoadModule(const char *name, u32 flags) return module->GetUID(); } -class AfterModuleEntryCall : public Action { -public: - AfterModuleEntryCall() {} - SceUID moduleID_; - u32 retValAddr; - virtual void run(); -}; - -void AfterModuleEntryCall::run() { - Memory::Write_U32(retValAddr, currentMIPS->r[2]); -} - void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, u32 optionAddr) { ERROR_LOG(HLE,"UNIMPL sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x)", diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 66a9172675..3743bb2a4b 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -193,13 +193,62 @@ public: return temp; } void clear() { + // TODO: Should this delete them? (it should probably just own them.) calls_.clear(); idGen_ = 0; } + int registerType(ActionCreator creator) { + types_.push_back(creator); + return types_.size() - 1; + } + + void restoreType(int actionType, ActionCreator creator) { + if (actionType >= (int) types_.size()) + types_.resize(actionType + 1, NULL); + types_[actionType] = creator; + } + + Action *createByType(int actionType) { + if (actionType < (int) types_.size() && types_[actionType] != NULL) { + Action *a = types_[actionType](); + a->actionTypeID = actionType; + return a; + } + return NULL; + } + + void DoState(PointerWrap &p) { + p.Do(idGen_); + + size_t n = (int) calls_.size(); + p.Do(n); + + if (p.mode == p.MODE_READ) { + // TODO: Delete them? + calls_.clear(); + for (size_t i = 0; i < n; ++i) { + int k; + p.Do(k); + // TODO: Presumably leaks? MipsCallManager may need to own these. + MipsCall *call = new MipsCall(); + call->DoState(p); + calls_[k] = call; + } + } else { + std::map::iterator it, end; + for (it = calls_.begin(), end = calls_.end(); it != end; ++it) { + p.Do(it->first); + it->second->DoState(p); + } + } + p.DoMarker("MipsCallManager"); + } + private: int genId() { return ++idGen_; } std::map calls_; + std::vector types_; int idGen_; }; @@ -207,7 +256,37 @@ class ActionAfterMipsCall : public Action { public: virtual void run(); - Thread *thread; + + static Action *Create() + { + return new ActionAfterMipsCall; + } + + virtual void DoState(PointerWrap &p) + { + p.Do(threadID); + p.Do(status); + p.Do(waitType); + p.Do(waitID); + p.Do(waitInfo); + p.Do(isProcessingCallbacks); + + p.DoMarker("ActionAfterMipsCall"); + + int chainedActionType = 0; + if (chainedAction != NULL) + chainedActionType = chainedAction->actionTypeID; + p.Do(chainedActionType); + + if (chainedActionType != 0) + { + if (p.mode == p.MODE_READ) + chainedAction = __KernelCreateAction(chainedActionType); + chainedAction->DoState(p); + } + } + + SceUID threadID; // Saved thread state int status; @@ -219,6 +298,31 @@ public: Action *chainedAction; }; +class ActionAfterCallback : public Action +{ +public: + ActionAfterCallback() {} + virtual void run(); + + static Action *Create() + { + return new ActionAfterCallback; + } + + void setCallback(SceUID cbId_) + { + cbId = cbId_; + } + + void DoState(PointerWrap &p) + { + p.Do(cbId); + p.DoMarker("ActionAfterCallback"); + } + + SceUID cbId; +}; + class Thread : public KernelObject { public: @@ -383,6 +487,8 @@ int eventScheduledWakeup; bool dispatchEnabled = true; MipsCallManager mipsCalls; +int actionAfterCallback; +int actionAfterMipsCall; // This seems nasty SceUID curModule; @@ -391,6 +497,50 @@ SceUID curModule; //STATE END ////////////////////////////////////////////////////////////////////////// +int __KernelRegisterActionType(ActionCreator creator) +{ + return mipsCalls.registerType(creator); +} + +void __KernelRestoreActionType(int actionType, ActionCreator creator) +{ + mipsCalls.restoreType(actionType, creator); +} + +Action *__KernelCreateAction(int actionType) +{ + return mipsCalls.createByType(actionType); +} + +void MipsCall::DoState(PointerWrap &p) +{ + p.Do(entryPoint); + p.Do(cbId); + p.DoArray(args, ARRAY_SIZE(args)); + p.Do(numArgs); + p.Do(savedIdRegister); + p.Do(savedRa); + p.Do(savedPc); + p.Do(savedV0); + p.Do(savedV1); + p.Do(returnVoid); + p.Do(tag); + p.Do(savedId); + p.Do(reschedAfter); + + p.DoMarker("MipsCall"); + + int actionTypeID = 0; + if (doAfter != NULL) + actionTypeID = doAfter->actionTypeID; + p.Do(actionTypeID); + if (actionTypeID != 0) + { + if (p.mode == p.MODE_READ) + doAfter = __KernelCreateAction(actionTypeID); + doAfter->DoState(p); + } +} // TODO: Should move to this wrapper so we can keep the current thread as a SceUID instead // of a dangerous raw pointer. @@ -443,6 +593,8 @@ void __KernelThreadingInit() WriteSyscall("FakeSysCalls", NID_INTERRUPTRETURN, intReturnHackAddr); eventScheduledWakeup = CoreTiming::RegisterEvent("ScheduledWakeup", &hleScheduledWakeup); + actionAfterMipsCall = __KernelRegisterActionType(ActionAfterMipsCall::Create); + actionAfterCallback = __KernelRegisterActionType(ActionAfterCallback::Create); // Create the two idle threads, as well. With the absolute minimal possible priority. // 4096 stack size - don't know what the right value is. Hm, if callbacks are ever to run on these threads... @@ -453,6 +605,37 @@ void __KernelThreadingInit() __KernelListenThreadEnd(__KernelCancelWakeup); } +void __KernelThreadingDoState(PointerWrap &p) +{ + p.Do(g_inCbCount); + p.Do(idleThreadHackAddr); + p.Do(threadReturnHackAddr); + p.Do(cbReturnHackAddr); + p.Do(intReturnHackAddr); + + p.Do(currentThread); + p.Do(threadqueue); + p.DoArray(threadIdleID, ARRAY_SIZE(threadIdleID)); + p.Do(dispatchEnabled); + p.Do(curModule); + + p.Do(eventScheduledWakeup); + CoreTiming::RestoreRegisterEvent(eventScheduledWakeup, "ScheduledWakeup", &hleScheduledWakeup); + p.Do(actionAfterMipsCall); + __KernelRestoreActionType(actionAfterMipsCall, ActionAfterMipsCall::Create); + p.Do(actionAfterCallback); + __KernelRestoreActionType(actionAfterCallback, ActionAfterCallback::Create); + + p.DoMarker("sceKernelThread"); +} + +void __KernelThreadingDoStateLate(PointerWrap &p) +{ + // We do this late to give modules time to register actions. + mipsCalls.DoState(p); + p.DoMarker("sceKernelThread Late"); +} + KernelObject *__KernelThreadObject() { return new Thread; @@ -1698,11 +1881,15 @@ void sceKernelReferCallbackStatus() } void ActionAfterMipsCall::run() { - thread->nt.status = status; - thread->nt.waitType = waitType; - thread->nt.waitID = waitID; - thread->waitInfo = waitInfo; - thread->isProcessingCallbacks = isProcessingCallbacks; + u32 error; + Thread *thread = kernelObjects.Get(threadID, error); + if (thread) { + thread->nt.status = status; + thread->nt.waitType = waitType; + thread->nt.waitID = waitID; + thread->waitInfo = waitInfo; + thread->isProcessingCallbacks = isProcessingCallbacks; + } if (chainedAction) { chainedAction->run(); @@ -1877,9 +2064,9 @@ bool __CanExecuteCallbackNow(Thread *thread) { void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, bool returnVoid, std::vector args, bool reschedAfter) { if (thread) { - ActionAfterMipsCall *after = new ActionAfterMipsCall(); + ActionAfterMipsCall *after = (ActionAfterMipsCall *) __KernelCreateAction(actionAfterMipsCall); after->chainedAction = afterAction; - after->thread = thread; + after->threadID = thread->GetUID(); after->status = thread->nt.status; after->waitType = thread->nt.waitType; after->waitID = thread->nt.waitID; @@ -1986,6 +2173,7 @@ void __KernelReturnFromMipsCall() // Should also save/restore wait state here. if (call->doAfter) call->doAfter->run(); + // TODO: Do we need to delete call->doAfter? currentMIPS->pc = call->savedPc; currentMIPS->r[MIPS_REG_RA] = call->savedRa; @@ -2005,6 +2193,8 @@ void __KernelReturnFromMipsCall() if (call->reschedAfter || threadReady == 0) __KernelReSchedule("return from callback"); } + + // TODO: Do we need to delete call? } bool __KernelExecutePendingMipsCalls(bool reschedAfter) @@ -2027,15 +2217,6 @@ bool __KernelExecutePendingMipsCalls(bool reschedAfter) 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, bool reschedAfter) { @@ -2060,7 +2241,12 @@ void __KernelRunCallbackOnThread(SceUID cbId, Thread *thread, bool reschedAfter) cb->nc.notifyCount = 0; cb->nc.notifyArg = 0; - Action *action = new ActionAfterCallback(cbId); + ActionAfterCallback *action = (ActionAfterCallback *) __KernelCreateAction(actionAfterCallback); + if (action != NULL) + action->setCallback(cbId); + else + ERROR_LOG(HLE, "Something went wrong creating a restore action for a callback."); + __KernelCallAddress(thread, cb->nc.entrypoint, action, false, args, reschedAfter); } diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index d11a33c17c..b63fe58312 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -97,6 +97,8 @@ struct ThreadContext // Internal API, used by implementations of kernel functions void __KernelThreadingInit(); +void __KernelThreadingDoState(PointerWrap &p); +void __KernelThreadingDoStateLate(PointerWrap &p); void __KernelThreadingShutdown(); KernelObject *__KernelThreadObject(); KernelObject *__KernelCallbackObject(); @@ -184,6 +186,10 @@ bool __KernelSwitchOffThread(const char *reason); // A call into game code. These can be pending on a thread. // Similar to Callback-s (NOT CallbackInfos) in JPCSP. class Action; +typedef Action *(*ActionCreator)(); +Action *__KernelCreateAction(int actionType); +int __KernelRegisterActionType(ActionCreator creator); +void __KernelRestoreActionType(int actionType, ActionCreator creator); struct MipsCall { u32 entryPoint; u32 cbId; @@ -196,9 +202,11 @@ struct MipsCall { u32 savedV0; u32 savedV1; bool returnVoid; - const char *tag; + std::string tag; u32 savedId; bool reschedAfter; + + void DoState(PointerWrap &p); }; enum ThreadStatus {