From 2bfe327dbdf654a8d8e6923a31f7cb907e6fb41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 31 Mar 2025 10:24:03 +0200 Subject: [PATCH] Expose PSPThread in the same manner --- CMakeLists.txt | 1 + Common/Math/expression_parser.h | 7 +- Core/Core.vcxproj | 1 + Core/Core.vcxproj.filters | 5 +- Core/Debugger/Breakpoints.h | 1 + Core/HLE/KernelThreadDebugInterface.h | 4 +- Core/HLE/PSPThreadContext.h | 35 +++ Core/HLE/sceKernelModule.cpp | 5 - Core/HLE/sceKernelThread.cpp | 434 ++++++++++---------------- Core/HLE/sceKernelThread.h | 192 +++++++++--- Core/MIPS/MIPSDebugInterface.h | 2 +- GPU/GPUCommon.cpp | 3 +- UI/ImDebugger/ImDebugger.cpp | 15 - 13 files changed, 360 insertions(+), 345 deletions(-) create mode 100644 Core/HLE/PSPThreadContext.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9682863a04..cb88266791 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2170,6 +2170,7 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/HLETables.cpp Core/HLE/HLETables.h Core/HLE/KernelWaitHelpers.h + Core/HLE/PSPThreadContext.h Core/HLE/KUBridge.h Core/HLE/KUBridge.cpp Core/HLE/Plugins.h diff --git a/Common/Math/expression_parser.h b/Common/Math/expression_parser.h index ee6330a929..3a3aca5da4 100644 --- a/Common/Math/expression_parser.h +++ b/Common/Math/expression_parser.h @@ -1,21 +1,18 @@ #pragma once #include -#include #include #include typedef std::pair ExpressionPair; typedef std::vector PostfixExpression; -enum ExpressionType -{ +enum ExpressionType { EXPR_TYPE_UINT = 0, EXPR_TYPE_FLOAT = 2, }; -class IExpressionFunctions -{ +class IExpressionFunctions { public: virtual ~IExpressionFunctions() {} virtual bool parseReference(char* str, uint32_t& referenceIndex) = 0; diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 2f8dfa962b..0ab243b90d 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -1195,6 +1195,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index cf8a987d33..496f07b415 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -2178,6 +2178,9 @@ Core + + HLE\Kernel + @@ -2206,4 +2209,4 @@ Ext - + \ No newline at end of file diff --git a/Core/Debugger/Breakpoints.h b/Core/Debugger/Breakpoints.h index 08a30a5757..ea5aa05ca7 100644 --- a/Core/Debugger/Breakpoints.h +++ b/Core/Debugger/Breakpoints.h @@ -22,6 +22,7 @@ #include #include "Core/MIPS/MIPSDebugInterface.h" +#include "Common/Math/expression_parser.h" enum BreakAction : u32 { BREAK_ACTION_IGNORE = 0x00, diff --git a/Core/HLE/KernelThreadDebugInterface.h b/Core/HLE/KernelThreadDebugInterface.h index 5e8cee5b22..4fbd9d54ca 100644 --- a/Core/HLE/KernelThreadDebugInterface.h +++ b/Core/HLE/KernelThreadDebugInterface.h @@ -18,9 +18,11 @@ #pragma once #include -#include "Core/HLE/sceKernelThread.h" +#include "Core/HLE/PSPThreadContext.h" #include "Core/MIPS/MIPSDebugInterface.h" +struct PSPThreadContext; + class KernelThreadDebugInterface : public DebugInterface { public: KernelThreadDebugInterface(PSPThreadContext &t) : ctx(t) {} diff --git a/Core/HLE/PSPThreadContext.h b/Core/HLE/PSPThreadContext.h new file mode 100644 index 0000000000..02992d2a81 --- /dev/null +++ b/Core/HLE/PSPThreadContext.h @@ -0,0 +1,35 @@ +#pragma once + +// Had to break this out into its own header to solve a circular include issue. + +#include "Common/CommonTypes.h" + +struct PSPThreadContext { + void reset(); + + // r must be followed by f. + u32 r[32]; + union { + float f[32]; + u32 fi[32]; + int fs[32]; + }; + union { + float v[128]; + u32 vi[128]; + }; + u32 vfpuCtrl[16]; + + union { + struct { + u32 pc; + + u32 lo; + u32 hi; + + u32 fcr31; + u32 fpcond; + }; + u32 other[6]; + }; +}; diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 866f840eb4..5f8939a18d 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -74,11 +74,6 @@ #include "GPU/GPUCommon.h" #include "GPU/GPUState.h" -enum { - PSP_THREAD_ATTR_KERNEL = 0x00001000, - PSP_THREAD_ATTR_USER = 0x80000000, -}; - enum : u32 { // Function exports. NID_MODULE_START = 0xD632ACDB, diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 14cf3ac428..1c236af5b5 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -105,23 +105,6 @@ enum ThreadEventType { bool __KernelThreadTriggerEvent(bool isKernel, SceUID threadID, ThreadEventType type); -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) -}; - struct NativeCallback { SceUInt_le size; @@ -173,58 +156,6 @@ public: NativeCallback nc; }; -#if COMMON_LITTLE_ENDIAN -typedef WaitType WaitType_le; -#else -typedef swap_struct_t > 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 ThreadWaitInfo { - u32 waitValue; - u32 timeoutPtr; -}; - // Owns outstanding MIPS calls and provides a way to get them by ID. class MipsCallManager { public: @@ -344,8 +275,7 @@ public: PSPAction *chainedAction; }; -class ActionAfterCallback : public PSPAction -{ +class ActionAfterCallback : public PSPAction { public: ActionAfterCallback() {} void run(MipsCall &call) override; @@ -354,13 +284,11 @@ public: return new ActionAfterCallback; } - void setCallback(SceUID cbId_) - { + void setCallback(SceUID cbId_) { cbId = cbId_; } - void DoState(PointerWrap &p) override - { + void DoState(PointerWrap &p) override { auto s = p.Section("ActionAfterCallback", 1); if (!s) return; @@ -371,233 +299,172 @@ public: SceUID cbId; }; -class PSPThread : public KernelObject { -public: - PSPThread() : debug(context) {} +u32 PSPThread::GetMissingErrorCode() { + return SCE_KERNEL_ERROR_UNKNOWN_THID; +} - 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 { - snprintf(ptr, size, "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" : "", - (int)nt.waitType, - nt.waitID, - waitInfo.waitValue); +void PSPThread::GetQuickInfo(char *ptr, int size) { + snprintf(ptr, size, "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" : "", + (int)nt.waitType, + nt.waitID, + waitInfo.waitValue); +} + +BlockAllocator &PSPThread::StackAllocator() { + if (nt.attr & PSP_THREAD_ATTR_KERNEL) { + return kernelMemory; } + return userMemory; +} - static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; } - static int GetStaticIDType() { return SCE_KERNEL_TMID_Thread; } - int GetIDType() const override { return SCE_KERNEL_TMID_Thread; } +bool PSPThread::AllocateStack(u32 &stackSize) { + _assert_msg_(stackSize >= 0x200, "thread stack should be 256 bytes or larger"); - bool AllocateStack(u32 &stackSize) { - _assert_msg_(stackSize >= 0x200, "thread stack should be 256 bytes or larger"); + FreeStack(); - FreeStack(); - - bool fromTop = (nt.attr & PSP_THREAD_ATTR_LOW_STACK) == 0; - currentStack.start = StackAllocator().Alloc(stackSize, fromTop, StringFromFormat("stack/%s", nt.name).c_str()); - if (currentStack.start == (u32)-1) - { - currentStack.start = 0; - nt.initialStack = 0; - ERROR_LOG(Log::sceKernel, "Failed to allocate stack for thread"); - return false; - } - - nt.initialStack = currentStack.start; - nt.stackSize = stackSize; - return true; - } - - bool FillStack() { - // Fill the stack. - if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) { - Memory::Memset(currentStack.start, 0xFF, nt.stackSize, "ThreadFillStack"); - } - context.r[MIPS_REG_SP] = currentStack.start + nt.stackSize; - currentStack.end = context.r[MIPS_REG_SP]; - // The k0 section is 256 bytes at the top of the stack. - context.r[MIPS_REG_SP] -= 256; - context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP]; - u32 k0 = context.r[MIPS_REG_K0]; - Memory::Memset(k0, 0, 0x100, "ThreadK0"); - Memory::Write_U32(GetUID(), k0 + 0xc0); - Memory::Write_U32(nt.initialStack, k0 + 0xc8); - Memory::Write_U32(0xffffffff, k0 + 0xf8); - Memory::Write_U32(0xffffffff, k0 + 0xfc); - // After k0 comes the arguments, which is done by sceKernelStartThread(). - - Memory::Write_U32(GetUID(), nt.initialStack); - return true; - } - - void FreeStack() { - if (currentStack.start != 0) { - DEBUG_LOG(Log::sceKernel, "Freeing thread stack %s", nt.name); - - if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) { - Memory::Memset(nt.initialStack, 0, nt.stackSize, "ThreadFreeStack"); - } - - StackAllocator().Free(currentStack.start); - currentStack.start = 0; - } - } - - bool PushExtendedStack(u32 size) + bool fromTop = (nt.attr & PSP_THREAD_ATTR_LOW_STACK) == 0; + currentStack.start = StackAllocator().Alloc(stackSize, fromTop, StringFromFormat("stack/%s", nt.name).c_str()); + if (currentStack.start == (u32)-1) { - u32 stack = userMemory.Alloc(size, true, StringFromFormat("extended/%s", nt.name).c_str()); - if (stack == (u32)-1) - return false; - - pushedStacks.push_back(currentStack); - currentStack.start = stack; - currentStack.end = stack + size; - nt.initialStack = currentStack.start; - nt.stackSize = currentStack.end - currentStack.start; - - // We still drop the threadID at the bottom and fill it, but there's no k0. - Memory::Memset(currentStack.start, 0xFF, nt.stackSize, "ThreadExtendStack"); - Memory::Write_U32(GetUID(), nt.initialStack); - return true; + currentStack.start = 0; + nt.initialStack = 0; + ERROR_LOG(Log::sceKernel, "Failed to allocate stack for thread"); + return false; } - bool PopExtendedStack() - { - if (pushedStacks.size() == 0) - return false; + nt.initialStack = currentStack.start; + nt.stackSize = stackSize; + return true; +} - userMemory.Free(currentStack.start); - currentStack = pushedStacks.back(); - pushedStacks.pop_back(); - nt.initialStack = currentStack.start; - nt.stackSize = currentStack.end - currentStack.start; - return true; +bool PSPThread::FillStack() { + // Fill the stack. + if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) { + Memory::Memset(currentStack.start, 0xFF, nt.stackSize, "ThreadFillStack"); } + context.r[MIPS_REG_SP] = currentStack.start + nt.stackSize; + currentStack.end = context.r[MIPS_REG_SP]; + // The k0 section is 256 bytes at the top of the stack. + context.r[MIPS_REG_SP] -= 256; + context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP]; + u32 k0 = context.r[MIPS_REG_K0]; + Memory::Memset(k0, 0, 0x100, "ThreadK0"); + Memory::Write_U32(GetUID(), k0 + 0xc0); + Memory::Write_U32(nt.initialStack, k0 + 0xc8); + Memory::Write_U32(0xffffffff, k0 + 0xf8); + Memory::Write_U32(0xffffffff, k0 + 0xfc); + // After k0 comes the arguments, which is done by sceKernelStartThread(). - // Can't use a destructor since savestates will call that too. - void Cleanup() - { - // Callbacks are automatically deleted when their owning thread is deleted. - for (auto it = callbacks.begin(), end = callbacks.end(); it != end; ++it) - kernelObjects.Destroy(*it); + Memory::Write_U32(GetUID(), nt.initialStack); + return true; +} - if (pushedStacks.size() != 0) - { - WARN_LOG_REPORT(Log::sceKernel, "Thread ended within an extended stack"); - for (size_t i = 0; i < pushedStacks.size(); ++i) - userMemory.Free(pushedStacks[i].start); +void PSPThread::FreeStack() { + if (currentStack.start != 0) { + DEBUG_LOG(Log::sceKernel, "Freeing thread stack %s", nt.name); + + if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) { + Memory::Memset(nt.initialStack, 0, nt.stackSize, "ThreadFreeStack"); } - FreeStack(); + + StackAllocator().Free(currentStack.start); + currentStack.start = 0; } +} - BlockAllocator &StackAllocator() { - if (nt.attr & PSP_THREAD_ATTR_KERNEL) { - return kernelMemory; - } - return userMemory; +bool PSPThread::PushExtendedStack(u32 size) { + u32 stack = userMemory.Alloc(size, true, StringFromFormat("extended/%s", nt.name).c_str()); + if (stack == (u32)-1) + return false; + + pushedStacks.push_back(currentStack); + currentStack.start = stack; + currentStack.end = stack + size; + nt.initialStack = currentStack.start; + nt.stackSize = currentStack.end - currentStack.start; + + // We still drop the threadID at the bottom and fill it, but there's no k0. + Memory::Memset(currentStack.start, 0xFF, nt.stackSize, "ThreadExtendStack"); + Memory::Write_U32(GetUID(), nt.initialStack); + return true; +} + +bool PSPThread::PopExtendedStack() { + if (pushedStacks.size() == 0) + return false; + + userMemory.Free(currentStack.start); + currentStack = pushedStacks.back(); + pushedStacks.pop_back(); + nt.initialStack = currentStack.start; + nt.stackSize = currentStack.end - currentStack.start; + return true; +} + +void PSPThread::Cleanup() { + // Callbacks are automatically deleted when their owning thread is deleted. + for (auto it = callbacks.begin(), end = callbacks.end(); it != end; ++it) + kernelObjects.Destroy(*it); + + if (pushedStacks.size() != 0) { + WARN_LOG_REPORT(Log::sceKernel, "Thread ended within an extended stack"); + for (size_t i = 0; i < pushedStacks.size(); ++i) + userMemory.Free(pushedStacks[i].start); } + FreeStack(); +} - 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; +void PSPThread::DoState(PointerWrap &p) { + auto s = p.Section("Thread", 1, 5); + if (!s) + return; - // 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; } + Do(p, nt); + Do(p, waitInfo); + Do(p, moduleId); + Do(p, isProcessingCallbacks); + Do(p, currentMipscallId); + Do(p, currentCallbackId); - void DoState(PointerWrap &p) override - { - auto s = p.Section("Thread", 1, 5); - if (!s) - return; + // TODO: If we want to "version" a DoState method here, we can just use minVer = 0. + Do(p, context); - Do(p, nt); - Do(p, waitInfo); - Do(p, moduleId); - Do(p, isProcessingCallbacks); - Do(p, currentMipscallId); - Do(p, currentCallbackId); - - // TODO: If we want to "version" a DoState method here, we can just use minVer = 0. - Do(p, context); - - if (s <= 3) - { - // We must have been loading an old state if we're here. - // Reorder VFPU data to new order. - float temp[128]; - memcpy(temp, context.v, 128 * sizeof(float)); - for (int i = 0; i < 128; i++) { - context.v[voffset[i]] = temp[i]; - } - } - - if (s <= 2) - { - context.other[4] = context.other[5]; - context.other[3] = context.other[4]; - } - if (s <= 4) - std::swap(context.hi, context.lo); - - Do(p, callbacks); - - Do(p, pendingMipsCalls); - Do(p, pushedStacks); - Do(p, currentStack); - - if (s >= 2) - { - Do(p, waitingThreads); - Do(p, pausedWaits); + if (s <= 3) { + // We must have been loading an old state if we're here. + // Reorder VFPU data to new order. + float temp[128]; + memcpy(temp, context.v, 128 * sizeof(float)); + for (int i = 0; i < 128; i++) { + context.v[voffset[i]] = temp[i]; } } - NativeThread nt{}; + if (s <= 2) { + context.other[4] = context.other[5]; + context.other[3] = context.other[4]; + } + if (s <= 4) + std::swap(context.hi, context.lo); - ThreadWaitInfo waitInfo{}; - SceUID moduleId = -1; + Do(p, callbacks); - bool isProcessingCallbacks = false; - u32 currentMipscallId = -1; - SceUID currentCallbackId = -1; + Do(p, pendingMipsCalls); + Do(p, pushedStacks); + Do(p, currentStack); - PSPThreadContext context{}; - KernelThreadDebugInterface debug; + if (s >= 2) { + Do(p, waitingThreads); + Do(p, pausedWaits); + } +} - std::vector callbacks; - - std::list 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 pushedStacks; - - StackInfo currentStack{}; - - // For thread end. - std::vector waitingThreads; - // Key is the callback id it was for, or if no callback, the thread id. - std::map pausedWaits; -}; struct WaitTypeFuncs { @@ -3104,7 +2971,20 @@ void __KernelChangeThreadState(PSPThread *thread, ThreadStatus newStatus) { } } - +const char *ThreadStatusToString(ThreadStatus status) { + switch (status) { + case THREADSTATUS_RUNNING: return "Running"; + case THREADSTATUS_READY: return "Ready"; + case THREADSTATUS_WAIT: return "Wait"; + case THREADSTATUS_SUSPEND: return "Suspended"; + case THREADSTATUS_DORMANT: return "Dormant"; + case THREADSTATUS_DEAD: return "Dead"; + case THREADSTATUS_WAITSUSPEND: return "WaitSuspended"; + default: + break; + } + return "(unk)"; +} static bool __CanExecuteCallbackNow(PSPThread *thread) { return currentCallbackThreadID == 0 && g_inCbCount == 0; @@ -3540,7 +3420,7 @@ std::vector GetThreadsInfo() { info.id = uid; strncpy(info.name,t->GetName(),KERNELOBJECT_MAX_NAME_LENGTH); info.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0; - info.status = t->nt.status; + info.status = (ThreadStatus)t->nt.status; info.entrypoint = t->nt.entrypoint; info.initialStack = t->nt.initialStack; info.stackSize = (u32)t->nt.stackSize; diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index c99a66ae07..504de7c2aa 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -19,15 +19,20 @@ #include #include +#include +#include #include "Common/CommonTypes.h" #include "Core/HLE/sceKernel.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); @@ -82,8 +87,7 @@ struct SceKernelSysClock { // 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 -{ +enum WaitType : int { WAITTYPE_NONE = 0, WAITTYPE_SLEEP = 1, WAITTYPE_DELAY = 2, @@ -124,34 +128,156 @@ typedef void (* WaitEndCallbackFunc)(SceUID threadID, SceUID prevCallbackId); void __KernelRegisterWaitTypeFuncs(WaitType type, WaitBeginCallbackFunc beginFunc, WaitEndCallbackFunc endFunc); -struct PSPThreadContext { - void reset(); +#if COMMON_LITTLE_ENDIAN +typedef WaitType WaitType_le; +#else +typedef swap_struct_t > WaitType_le; +#endif - // r must be followed by f. - u32 r[32]; - union { - float f[32]; - u32 fi[32]; - int fs[32]; +// 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 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 callbacks; + + // TODO: Should probably just be a vector. + std::list pendingMipsCalls; + + struct StackInfo { + u32 start; + u32 end; }; - union { - float v[128]; - u32 vi[128]; - }; - u32 vfpuCtrl[16]; + // 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 pushedStacks; - union { - struct { - u32 pc; + StackInfo currentStack{}; - u32 lo; - u32 hi; - - u32 fcr31; - u32 fpcond; - }; - u32 other[6]; - }; + // For thread end. + std::vector waitingThreads; + // Key is the callback id it was for, or if no callback, the thread id. + std::map pausedWaits; }; // Internal API, used by implementations of kernel functions @@ -296,18 +422,6 @@ public: int actionTypeID; }; -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(PSPThread *thread, ThreadStatus newStatus); typedef void (*ThreadCallback)(SceUID threadID); @@ -317,7 +431,7 @@ struct DebugThreadInfo { SceUID id; char name[KERNELOBJECT_MAX_NAME_LENGTH+1]; - u32 status; + ThreadStatus status; u32 curPC; u32 entrypoint; u32 initialStack; diff --git a/Core/MIPS/MIPSDebugInterface.h b/Core/MIPS/MIPSDebugInterface.h index 4dae39a517..e4356ddee1 100644 --- a/Core/MIPS/MIPSDebugInterface.h +++ b/Core/MIPS/MIPSDebugInterface.h @@ -18,7 +18,7 @@ #pragma once #include -#include + #include "Common/Math/expression_parser.h" #include "Core/MIPS/MIPS.h" #include "Core/Debugger/DebugInterface.h" diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index a12caa9d88..cbe22c1ec4 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -25,7 +25,6 @@ #include "Core/HLE/ErrorCodes.h" #include "Core/HLE/sceKernelMemory.h" #include "Core/HLE/sceKernelInterrupt.h" -#include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceGe.h" #include "Core/Util/PPGeDraw.h" #include "Core/MemMapHelpers.h" @@ -37,6 +36,8 @@ #include "GPU/Debugger/Record.h" #include "GPU/Debugger/Stepping.h" +bool __KernelIsDispatchEnabled(); + void GPUCommon::Flush() { drawEngineCommon_->Flush(); } diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index cece4f4a88..94a2d6a1fa 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -306,21 +306,6 @@ static void DrawVFPU(ImConfig &config, ImControl &control, const MIPSDebugInterf ImGui::End(); } -static const char *ThreadStatusToString(u32 status) { - switch (status) { - case THREADSTATUS_RUNNING: return "Running"; - case THREADSTATUS_READY: return "Ready"; - case THREADSTATUS_WAIT: return "Wait"; - case THREADSTATUS_SUSPEND: return "Suspended"; - case THREADSTATUS_DORMANT: return "Dormant"; - case THREADSTATUS_DEAD: return "Dead"; - case THREADSTATUS_WAITSUSPEND: return "WaitSuspended"; - default: - break; - } - return "(unk)"; -} - void WaitIDToString(WaitType waitType, SceUID waitID, char *buffer, size_t bufSize) { switch (waitType) { case WAITTYPE_AUDIOCHANNEL: