mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
On hardware the kernel fills a new thread's stack with interrupts on, so a better thread that wakes during it runs before the call returns, the time it takes doesn't count towards the call, and worse threads get nothing (pspautotests threads/scheduling/preemptsyscall). PPSSPP ate the whole cost at once and only rescheduled at the end. __KernelBusyDelayResult() models such a syscall: the caller waits, an idle thread stands in for it while nothing better wants the CPU, and its remaining cycles only count down while that's the case. When done it goes back ahead of threads of its own priority, having never given up the CPU. sceKernelCreateThread uses it for the stack fill, unless a thread event handler is about to run. Booting 75 games against master shows no difference. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
520 lines
18 KiB
C++
520 lines
18 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#pragma once
|
|
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <list>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
#include "Core/HLE/sceKernel.h"
|
|
#include "Core/HLE/ErrorCodes.h"
|
|
#include "Core/HLE/PSPThreadContext.h"
|
|
#include "Core/HLE/KernelThreadDebugInterface.h"
|
|
|
|
// There's a good description of the thread scheduling rules in:
|
|
// http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/ThreadManForUser.java
|
|
|
|
class PSPThread;
|
|
class DebugInterface;
|
|
class BlockAllocator;
|
|
|
|
int sceKernelChangeThreadPriority(SceUID threadID, int priority);
|
|
SceUID __KernelCreateThreadInternal(const char *threadName, SceUID moduleID, u32 entry, u32 prio, int stacksize, u32 attr);
|
|
// With busyCyclesOut, the cost of filling the stack is left for the caller to take (see
|
|
// __KernelBusyDelayResult) instead of being eaten here.
|
|
int __KernelCreateThread(const char *threadName, SceUID moduleID, u32 entry, u32 prio, int stacksize, u32 attr, u32 optionAddr, bool allowKernel, int *busyCyclesOut = nullptr);
|
|
// For a syscall that keeps the CPU busy for a long time. The caller gets the result after the
|
|
// given cycles, but better threads that wake meanwhile run first, and worse ones don't run.
|
|
u32 __KernelBusyDelayResult(u32 result, int cycles, const char *reason);
|
|
int sceKernelCreateThread(const char *threadName, u32 entry, u32 prio, int stacksize, u32 attr, u32 optionAddr);
|
|
int sceKernelDelayThread(u32 usec);
|
|
int sceKernelDelayThreadCB(u32 usec);
|
|
int sceKernelDelaySysClockThread(u32 sysclockAddr);
|
|
int sceKernelDelaySysClockThreadCB(u32 sysclockAddr);
|
|
void __KernelStopThread(SceUID threadID, int exitStatus, const char *reason);
|
|
u32 __KernelDeleteThread(SceUID threadID, int exitStatus, const char *reason);
|
|
int sceKernelDeleteThread(int threadHandle);
|
|
int sceKernelExitDeleteThread(int exitStatus);
|
|
int sceKernelExitThread(int exitStatus);
|
|
void _sceKernelExitThread(int exitStatus);
|
|
SceUID sceKernelGetThreadId();
|
|
int sceKernelGetThreadCurrentPriority();
|
|
// Warning: will alter v0 in current MIPS state.
|
|
int __KernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr, bool forceArgs = false);
|
|
int __KernelStartThreadValidate(SceUID threadToStartID, int argSize, u32 argBlockPtr, bool forceArgs = false);
|
|
int __KernelGetThreadExitStatus(SceUID threadID);
|
|
int sceKernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr);
|
|
u32 sceKernelSuspendDispatchThread();
|
|
u32 sceKernelResumeDispatchThread(u32 suspended);
|
|
int sceKernelGetUserLevel();
|
|
int sceKernelIsUserModeThread();
|
|
int sceKernelWaitThreadEnd(SceUID threadID, u32 timeoutPtr);
|
|
u32 sceKernelReferThreadStatus(u32 uid, u32 statusPtr);
|
|
u32 sceKernelReferThreadRunStatus(u32 uid, u32 statusPtr);
|
|
int sceKernelReleaseWaitThread(SceUID threadID);
|
|
int sceKernelChangeCurrentThreadAttr(u32 clearAttr, u32 setAttr);
|
|
int sceKernelRotateThreadReadyQueue(int priority);
|
|
int KernelRotateThreadReadyQueue(int priority);
|
|
int sceKernelCheckThreadStack();
|
|
int sceKernelSuspendThread(SceUID threadID);
|
|
int sceKernelResumeThread(SceUID threadID);
|
|
int sceKernelWakeupThread(SceUID threadID);
|
|
int sceKernelCancelWakeupThread(SceUID threadID);
|
|
int sceKernelSleepThread();
|
|
int sceKernelSleepThreadCB();
|
|
int sceKernelTerminateDeleteThread(int threadno);
|
|
int sceKernelTerminateThread(SceUID threadID);
|
|
int sceKernelWaitThreadEndCB(SceUID threadID, u32 timeoutPtr);
|
|
int sceKernelGetThreadExitStatus(SceUID threadID);
|
|
u32 sceKernelGetThreadmanIdType(u32);
|
|
u32 sceKernelGetThreadmanIdList(u32 type, u32 readBufPtr, u32 readBufSize, u32 idCountPtr);
|
|
u32 sceKernelExtendThreadStack(u32 size, u32 entryAddr, u32 entryParameter);
|
|
void __KernelScheduleWakeup(SceUID threadID, s64 usFromNow);
|
|
|
|
struct SceKernelSysClock {
|
|
u32_le lo;
|
|
u32_le hi;
|
|
};
|
|
|
|
|
|
// 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 : int {
|
|
WAITTYPE_NONE = 0,
|
|
WAITTYPE_SLEEP = 1,
|
|
WAITTYPE_DELAY = 2,
|
|
WAITTYPE_SEMA = 3,
|
|
WAITTYPE_EVENTFLAG = 4,
|
|
WAITTYPE_MBX = 5,
|
|
WAITTYPE_VPL = 6,
|
|
WAITTYPE_FPL = 7,
|
|
WAITTYPE_MSGPIPE = 8, // fake
|
|
WAITTYPE_THREADEND = 9,
|
|
WAITTYPE_AUDIOCHANNEL = 10, // this is fake, should be replaced with 8 eventflags ( ?? )
|
|
WAITTYPE_UMD = 11, // this is fake, should be replaced with 1 eventflag ( ?? )
|
|
WAITTYPE_VBLANK = 12, // fake
|
|
WAITTYPE_MUTEX = 13,
|
|
WAITTYPE_LWMUTEX = 14,
|
|
WAITTYPE_CTRL = 15,
|
|
WAITTYPE_IO = 16,
|
|
WAITTYPE_GEDRAWSYNC = 17,
|
|
WAITTYPE_GELISTSYNC = 18,
|
|
WAITTYPE_MODULE = 19,
|
|
WAITTYPE_HLEDELAY = 20,
|
|
WAITTYPE_TLSPL = 21,
|
|
WAITTYPE_VMEM = 22,
|
|
WAITTYPE_ASYNCIO = 23,
|
|
WAITTYPE_MICINPUT = 24, // fake
|
|
WAITTYPE_NET = 25, // fake
|
|
WAITTYPE_USB = 26, // fake
|
|
WAITTYPE_PLUGIN = 27, // this is fake, for when LoadExec thread is waiting for plugins to finish loading
|
|
|
|
NUM_WAITTYPES
|
|
};
|
|
|
|
const char *WaitTypeToString(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);
|
|
|
|
void __KernelRegisterWaitTypeFuncs(WaitType type, WaitBeginCallbackFunc beginFunc, WaitEndCallbackFunc endFunc);
|
|
|
|
#if COMMON_LITTLE_ENDIAN
|
|
typedef WaitType WaitType_le;
|
|
#else
|
|
typedef swap_struct_t<WaitType, swap_32_t<WaitType> > WaitType_le;
|
|
#endif
|
|
|
|
// Real PSP struct, don't change the fields.
|
|
struct SceKernelThreadRunStatus {
|
|
SceSize_le size;
|
|
u32_le status;
|
|
s32_le currentPriority;
|
|
WaitType_le waitType;
|
|
SceUID_le waitID;
|
|
s32_le wakeupCount;
|
|
SceKernelSysClock runForClocks;
|
|
s32_le numInterruptPreempts;
|
|
s32_le numThreadPreempts;
|
|
s32_le numReleases;
|
|
};
|
|
|
|
// Real PSP struct, don't change the fields.
|
|
struct NativeThread {
|
|
u32_le nativeSize;
|
|
char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
|
|
|
|
// Threading stuff
|
|
u32_le attr;
|
|
u32_le status;
|
|
u32_le entrypoint;
|
|
u32_le initialStack;
|
|
u32_le stackSize;
|
|
u32_le gpreg;
|
|
|
|
s32_le initialPriority;
|
|
s32_le currentPriority;
|
|
WaitType_le waitType;
|
|
SceUID_le waitID;
|
|
s32_le wakeupCount;
|
|
s32_le exitStatus;
|
|
SceKernelSysClock runForClocks;
|
|
s32_le numInterruptPreempts;
|
|
s32_le numThreadPreempts;
|
|
s32_le numReleases;
|
|
};
|
|
|
|
struct NativeCallback {
|
|
SceUInt_le size;
|
|
char name[32];
|
|
SceUID_le threadId;
|
|
u32_le entrypoint;
|
|
u32_le commonArgument;
|
|
|
|
s32_le notifyCount;
|
|
s32_le notifyArg;
|
|
};
|
|
|
|
// Exposed here (rather than kept private to sceKernelThread.cpp) so the WebSocket debugger can
|
|
// read a live object's state directly via kernelObjects.Get<PSPCallback>()/Iterate<PSPCallback>()
|
|
// - see HLEKernelObjectSubscriber.cpp. That's a read-only use: nothing outside this file should
|
|
// call DoState() or otherwise mutate a PSPCallback - it's public here for this file's own use as
|
|
// before, not an invitation to write to it from elsewhere.
|
|
class PSPCallback : public KernelObject {
|
|
public:
|
|
const char *GetName() override { return nc.name; }
|
|
const char *GetTypeName() override { return GetStaticTypeName(); }
|
|
static const char *GetStaticTypeName() { return "CallBack"; }
|
|
|
|
void GetQuickInfo(char *ptr, int size) override {
|
|
snprintf(ptr, size, "thread=%i, argument= %08x",
|
|
nc.threadId,
|
|
nc.commonArgument);
|
|
}
|
|
|
|
~PSPCallback() {
|
|
}
|
|
|
|
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_CBID; }
|
|
static int GetStaticIDType() { return SCE_KERNEL_TMID_Callback; }
|
|
int GetIDType() const override { return SCE_KERNEL_TMID_Callback; }
|
|
|
|
void DoState(PointerWrap &p) override;
|
|
|
|
NativeCallback nc;
|
|
};
|
|
|
|
struct ThreadWaitInfo {
|
|
u32 waitValue;
|
|
u32 timeoutPtr;
|
|
};
|
|
|
|
enum {
|
|
PSP_THREAD_ATTR_KERNEL = 0x00001000,
|
|
PSP_THREAD_ATTR_VFPU = 0x00004000,
|
|
PSP_THREAD_ATTR_SCRATCH_SRAM = 0x00008000, // Save/restore scratch as part of context???
|
|
PSP_THREAD_ATTR_NO_FILLSTACK = 0x00100000, // No filling of 0xff.
|
|
PSP_THREAD_ATTR_CLEAR_STACK = 0x00200000, // Clear thread stack when deleted.
|
|
PSP_THREAD_ATTR_LOW_STACK = 0x00400000, // Allocate stack from bottom not top.
|
|
PSP_THREAD_ATTR_USER = 0x80000000,
|
|
PSP_THREAD_ATTR_USBWLAN = 0xa0000000,
|
|
PSP_THREAD_ATTR_VSH = 0xc0000000,
|
|
|
|
// TODO: Support more, not even sure what all of these mean.
|
|
PSP_THREAD_ATTR_USER_MASK = 0xf8f060ff,
|
|
PSP_THREAD_ATTR_USER_ERASE = 0x78800000,
|
|
PSP_THREAD_ATTR_SUPPORTED = (PSP_THREAD_ATTR_KERNEL | PSP_THREAD_ATTR_VFPU | PSP_THREAD_ATTR_NO_FILLSTACK | PSP_THREAD_ATTR_CLEAR_STACK | PSP_THREAD_ATTR_LOW_STACK | PSP_THREAD_ATTR_USER)
|
|
};
|
|
|
|
enum ThreadStatus : u32 {
|
|
THREADSTATUS_RUNNING = 1,
|
|
THREADSTATUS_READY = 2,
|
|
THREADSTATUS_WAIT = 4,
|
|
THREADSTATUS_SUSPEND = 8,
|
|
THREADSTATUS_DORMANT = 16,
|
|
THREADSTATUS_DEAD = 32,
|
|
|
|
THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND
|
|
};
|
|
const char *ThreadStatusToString(ThreadStatus status);
|
|
|
|
class PSPThread : public KernelObject {
|
|
public:
|
|
PSPThread() : debug(&context) {}
|
|
const char *GetName() override { return nt.name; }
|
|
const char *GetTypeName() override { return GetStaticTypeName(); }
|
|
static const char *GetStaticTypeName() { return "Thread"; }
|
|
void GetQuickInfo(char *ptr, int size) override;
|
|
|
|
static u32 GetMissingErrorCode();
|
|
static int GetStaticIDType() { return SCE_KERNEL_TMID_Thread; }
|
|
int GetIDType() const override { return SCE_KERNEL_TMID_Thread; }
|
|
|
|
bool AllocateStack(u32 &stackSize);
|
|
bool FillStack();
|
|
void FreeStack();
|
|
|
|
bool PushExtendedStack(u32 size);
|
|
bool PopExtendedStack();
|
|
|
|
// Can't use a destructor since savestates will call that too.
|
|
void Cleanup();
|
|
|
|
BlockAllocator &StackAllocator();
|
|
|
|
void setReturnValue(u32 retval);
|
|
void setReturnValue(u64 retval);
|
|
void resumeFromWait();
|
|
bool isWaitingFor(WaitType type, int id) const;
|
|
int getWaitID(WaitType type) const;
|
|
ThreadWaitInfo getWaitInfo() const;
|
|
|
|
// Utils
|
|
inline bool isRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; }
|
|
inline bool isStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; }
|
|
inline bool isReady() const { return (nt.status & THREADSTATUS_READY) != 0; }
|
|
inline bool isWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; }
|
|
inline bool isSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; }
|
|
|
|
void DoState(PointerWrap &p) override;
|
|
|
|
NativeThread nt{};
|
|
|
|
ThreadWaitInfo waitInfo{};
|
|
SceUID moduleId = -1;
|
|
KernelThreadDebugInterface debug;
|
|
|
|
bool isProcessingCallbacks = false;
|
|
u32 currentMipscallId = -1;
|
|
SceUID currentCallbackId = -1;
|
|
|
|
PSPThreadContext context{};
|
|
|
|
std::vector<SceUID> callbacks;
|
|
|
|
// TODO: Should probably just be a vector.
|
|
std::list<u32> pendingMipsCalls;
|
|
|
|
struct StackInfo {
|
|
u32 start;
|
|
u32 end;
|
|
};
|
|
// This is a stack of... stacks, since sceKernelExtendThreadStack() can recurse.
|
|
// These are stacks that aren't "active" right now, but will pop off once the func returns.
|
|
std::vector<StackInfo> pushedStacks;
|
|
|
|
StackInfo currentStack{};
|
|
|
|
// For thread end.
|
|
std::vector<SceUID> waitingThreads;
|
|
// Key is the callback id it was for, or if no callback, the thread id.
|
|
std::map<SceUID, u64> pausedWaits;
|
|
};
|
|
|
|
// Internal API, used by implementations of kernel functions
|
|
|
|
void __KernelThreadingInit();
|
|
void __KernelThreadingDoState(PointerWrap &p);
|
|
void __KernelThreadingDoStateLate(PointerWrap &p);
|
|
void __KernelThreadingShutdown();
|
|
|
|
std::string __KernelThreadingSummary();
|
|
|
|
KernelObject *__KernelThreadObject();
|
|
KernelObject *__KernelCallbackObject();
|
|
|
|
SceUID __KernelGetCurThread();
|
|
int KernelCurThreadPriority();
|
|
bool KernelChangeThreadPriority(SceUID threadID, int priority);
|
|
// Whether the running thread belongs to a kernel module. Privilege on the PSP is a property of
|
|
// the caller, not of the syscall - hleIsKernelMode() only says the entry point itself is a
|
|
// kernel-only export, which is a different question.
|
|
bool __KernelCurThreadIsKernelMode();
|
|
u32 __KernelGetCurThreadStack();
|
|
u32 __KernelGetCurThreadStackStart();
|
|
const char *__KernelGetThreadName(SceUID threadID);
|
|
bool KernelIsThreadDormant(SceUID threadID);
|
|
bool KernelIsThreadWaiting(SceUID threadID);
|
|
|
|
void __KernelSaveContext(PSPThreadContext *ctx, bool vfpuEnabled);
|
|
void __KernelLoadContext(const PSPThreadContext *ctx, bool vfpuEnabled);
|
|
|
|
u32 __KernelResumeThreadFromWait(SceUID threadID, u32 retval); // can return an error value
|
|
u32 __KernelResumeThreadFromWait(SceUID threadID, u64 retval);
|
|
|
|
inline u32 __KernelResumeThreadFromWait(SceUID threadID, int retval)
|
|
{
|
|
return __KernelResumeThreadFromWait(threadID, (u32)retval);
|
|
}
|
|
|
|
inline u32 __KernelResumeThreadFromWait(SceUID threadID, s64 retval)
|
|
{
|
|
return __KernelResumeThreadFromWait(threadID, (u64)retval);
|
|
}
|
|
|
|
u32 __KernelGetWaitValue(SceUID threadID, u32 &error);
|
|
u32 __KernelGetWaitTimeoutPtr(SceUID threadID, u32 &error);
|
|
SceUID __KernelGetWaitID(SceUID threadID, WaitType type, u32 &error);
|
|
SceUID __KernelGetCurrentCallbackID(SceUID threadID, u32 &error);
|
|
void __KernelWaitCurThread(WaitType type, SceUID waitId, u32 waitValue, u32 timeoutPtr, bool processCallbacks, const char *reason);
|
|
void __KernelWaitCallbacksCurThread(WaitType type, SceUID waitID, u32 waitValue, u32 timeoutPtr);
|
|
void __KernelReSchedule(const char *reason = "no reason");
|
|
void __KernelReSchedule(bool doCallbacks, const char *reason);
|
|
|
|
SceUID __KernelGetCurThread();
|
|
SceUID __KernelGetCurThreadModuleId();
|
|
SceUID __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(SceUID moduleId);
|
|
void __KernelReturnFromThread(); // Called as HLE function
|
|
u32 __KernelGetThreadPrio(SceUID id);
|
|
bool __KernelThreadSortPriority(SceUID thread1, SceUID thread2);
|
|
bool __KernelIsDispatchEnabled();
|
|
void __KernelReturnFromExtendStack();
|
|
|
|
void __KernelIdle();
|
|
|
|
u32 HLEMipsCallReturnAddress();
|
|
u32 __KernelCallbackReturnAddress();
|
|
u32 __KernelInterruptReturnAddress(); // TODO: remove
|
|
|
|
SceUID sceKernelCreateCallback(const char *name, u32 entrypoint, u32 signalArg);
|
|
int sceKernelDeleteCallback(SceUID cbId);
|
|
int sceKernelNotifyCallback(SceUID cbId, int notifyArg);
|
|
int sceKernelCancelCallback(SceUID cbId);
|
|
int sceKernelGetCallbackCount(SceUID cbId);
|
|
void sceKernelCheckCallback();
|
|
int sceKernelReferCallbackStatus(SceUID cbId, u32 statusAddr);
|
|
|
|
class PSPAction;
|
|
|
|
// Not an official Callback object, just calls a mips function on the current thread.
|
|
// Takes ownership of afterAction.
|
|
void __KernelDirectMipsCall(u32 entryPoint, PSPAction *afterAction, u32 args[], int numargs, bool reschedAfter);
|
|
|
|
void __KernelReturnFromMipsCall(); // Called as HLE function
|
|
bool __KernelInCallback();
|
|
|
|
// Should be called by (nearly) all ...CB functions.
|
|
bool __KernelCheckCallbacks();
|
|
bool __KernelForceCallbacks();
|
|
bool __KernelCurHasReadyCallbacks();
|
|
void __KernelSwitchContext(PSPThread *target, const char *reason);
|
|
bool __KernelExecutePendingMipsCalls(PSPThread *currentThread, bool reschedAfter);
|
|
void __KernelNotifyCallback(SceUID cbId, int notifyArg);
|
|
|
|
// Switch to an idle / non-user thread, if not already on one.
|
|
// Returns whether a switch occurred.
|
|
bool __KernelSwitchOffThread(const char *reason);
|
|
bool __KernelSwitchToThread(SceUID threadID, const char *reason);
|
|
|
|
// Set a thread's return address to a specific FakeSyscall nid.
|
|
// Discards old RA. Only useful for special threads that do special things on exit.
|
|
u32 __KernelSetThreadRA(SceUID threadID, u32 nid);
|
|
|
|
// A call into game code. These can be pending on a thread.
|
|
// Similar to Callback-s (NOT CallbackInfos) in JPCSP.
|
|
typedef PSPAction *(*ActionCreator)();
|
|
PSPAction *__KernelCreateAction(int actionType);
|
|
int __KernelRegisterActionType(ActionCreator creator);
|
|
void __KernelRestoreActionType(int actionType, ActionCreator creator);
|
|
|
|
struct MipsCall {
|
|
MipsCall() {
|
|
doAfter = nullptr;
|
|
}
|
|
|
|
u32 entryPoint;
|
|
u32 cbId;
|
|
u32 args[6];
|
|
int numArgs;
|
|
PSPAction *doAfter;
|
|
u32 savedPc;
|
|
u32 savedV0;
|
|
u32 savedV1;
|
|
std::string tag;
|
|
u32 savedId;
|
|
bool reschedAfter;
|
|
|
|
void DoState(PointerWrap &p);
|
|
void setReturnValue(u32 value);
|
|
void setReturnValue(u64 value);
|
|
inline void setReturnValue(int value)
|
|
{
|
|
setReturnValue((u32)value);
|
|
}
|
|
inline void setReturnValue(s64 value)
|
|
{
|
|
setReturnValue((u64)value);
|
|
}
|
|
};
|
|
|
|
class PSPAction
|
|
{
|
|
public:
|
|
virtual ~PSPAction() {}
|
|
virtual void run(MipsCall &call) = 0;
|
|
virtual void DoState(PointerWrap &p) = 0;
|
|
int actionTypeID;
|
|
};
|
|
|
|
void __KernelChangeThreadState(PSPThread *thread, ThreadStatus newStatus);
|
|
|
|
typedef void (*ThreadCallback)(SceUID threadID);
|
|
void __KernelListenThreadEnd(ThreadCallback callback);
|
|
|
|
struct DebugThreadInfo
|
|
{
|
|
SceUID id;
|
|
char name[KERNELOBJECT_MAX_NAME_LENGTH+1];
|
|
ThreadStatus status;
|
|
u32 curPC;
|
|
u32 entrypoint;
|
|
u32 initialStack;
|
|
int stackSize;
|
|
int priority;
|
|
WaitType waitType;
|
|
SceUID waitID;
|
|
bool isCurrent;
|
|
};
|
|
|
|
std::vector<DebugThreadInfo> GetThreadsInfo();
|
|
DebugInterface *KernelDebugThread(SceUID threadID);
|
|
void __KernelChangeThreadState(SceUID threadId, ThreadStatus newStatus);
|
|
|
|
int LoadExecForUser_362A956B();
|
|
int sceKernelRegisterExitCallback(SceUID cbId);
|
|
|
|
// Dispatch the exit callback registered via sceKernelRegisterExitCallback on its registering thread,
|
|
// so the game has a chance to clean up before we shut down. Returns false if no callback can be
|
|
// dispatched (none registered, the thread is gone, etc.) - in that case, the host should proceed
|
|
// to power down immediately.
|
|
bool __KernelInvokeRegisteredExitCallback();
|
|
// True while a previously-invoked exit callback hasn't yet returned. The host should keep running
|
|
// emulation while this is true, and only tear down after it goes false (or after a timeout).
|
|
bool __KernelIsExitCallbackPending();
|
|
|
|
KernelObject *__KernelThreadEventHandlerObject();
|
|
SceUID sceKernelRegisterThreadEventHandler(const char *name, SceUID threadID, u32 mask, u32 handlerPtr, u32 commonArg);
|
|
int sceKernelReleaseThreadEventHandler(SceUID uid);
|
|
int sceKernelReferThreadEventHandlerStatus(SceUID uid, u32 infoPtr);
|