From b9ede6aade97499efd2265594a0e70021f9bb587 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 22 May 2014 23:38:21 -0700 Subject: [PATCH] Add a class to create helper threads from HLE. This should be usable from sceIo as well. --- CMakeLists.txt | 2 + Core/CMakeLists.txt | 1 + Core/Core.vcxproj | 4 +- Core/Core.vcxproj.filters | 8 ++- Core/HLE/HLEHelperThread.cpp | 82 ++++++++++++++++++++++++++++ Core/HLE/HLEHelperThread.h | 41 ++++++++++++++ Core/HLE/sceKernelThread.cpp | 102 +++++++++++++++++++++-------------- Core/HLE/sceKernelThread.h | 4 ++ android/jni/Android.mk | 1 + 9 files changed, 202 insertions(+), 43 deletions(-) create mode 100644 Core/HLE/HLEHelperThread.cpp create mode 100644 Core/HLE/HLEHelperThread.h diff --git a/CMakeLists.txt b/CMakeLists.txt index dfaf4dd6d1..d3859c2d7b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1016,6 +1016,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/HLE/HLE.h Core/HLE/ReplaceTables.cpp Core/HLE/ReplaceTables.h + Core/HLE/HLEHelperThread.cpp + Core/HLE/HLEHelperThread.h Core/HLE/HLETables.cpp Core/HLE/HLETables.h Core/HLE/KernelWaitHelpers.h diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 2ff2e9e02e..e0b68caca9 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -27,6 +27,7 @@ set(SRCS ELF/PrxDecrypter.cpp Font/PGF.cpp HLE/HLE.cpp + HLE/HLEHelperThread.cpp HLE/HLETables.cpp HLE/sceAtrac.cpp HLE/__sceAudio.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 12c704d434..d95e4f2d51 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -199,6 +199,7 @@ + @@ -472,6 +473,7 @@ + @@ -640,4 +642,4 @@ - + \ No newline at end of file diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 4115dc7e36..250c2b6d6a 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -526,6 +526,9 @@ MIPS\x86 + + HLE + @@ -972,6 +975,9 @@ MIPS\x86 + + HLE + @@ -979,4 +985,4 @@ - + \ No newline at end of file diff --git a/Core/HLE/HLEHelperThread.cpp b/Core/HLE/HLEHelperThread.cpp new file mode 100644 index 0000000000..c5de26e7fc --- /dev/null +++ b/Core/HLE/HLEHelperThread.cpp @@ -0,0 +1,82 @@ +// Copyright (c) 2014- 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 "Common/ChunkFile.h" +#include "Core/MemMap.h" +#include "Core/HLE/HLE.h" +#include "Core/HLE/HLEHelperThread.h" +#include "Core/HLE/sceKernelThread.h" +#include "Core/HLE/sceKernelMemory.h" +#include "Core/MIPS/MIPSCodeUtils.h" + +HLEHelperThread::HLEHelperThread() : id_(-1), entry_(0) { +} + +HLEHelperThread::HLEHelperThread(const char *threadName, u32 instructions[], u32 instrCount, u32 prio, int stacksize) { + u32 bytes = instrCount * sizeof(u32); + u32 size = bytes + sizeof(u32) * 2; + AllocEntry(bytes); + Memory::Memcpy(entry_, instructions, bytes); + + // Just to simplify things, we add the return here. + Memory::Write_U32(MIPS_MAKE_JR_RA(), entry_ + bytes + 0); + Memory::Write_U32(MIPS_MAKE_NOP(), entry_ + bytes + 4); + + Create(threadName, prio, stacksize); +} + +HLEHelperThread::HLEHelperThread(const char *threadName, const char *module, const char *func, u32 prio, int stacksize) { + const u32 bytes = sizeof(u32) * 2; + AllocEntry(bytes); + Memory::Write_U32(MIPS_MAKE_JR_RA(), entry_ + 0); + Memory::Write_U32(MIPS_MAKE_SYSCALL(module, func), entry_ + 4); + + Create(threadName, prio, stacksize); +} + +HLEHelperThread::~HLEHelperThread() { + __KernelDeleteThread(id_, SCE_KERNEL_ERROR_THREAD_TERMINATED, "helper deleted"); + kernelMemory.Free(entry_); +} + +void HLEHelperThread::AllocEntry(u32 size) { + entry_ = kernelMemory.Alloc(size); + currentMIPS->InvalidateICache(entry_, size); +} + +void HLEHelperThread::Create(const char *threadName, u32 prio, int stacksize) { + id_ = __KernelCreateThreadInternal(threadName, __KernelGetCurThreadModuleId(), entry_, prio, stacksize, 0); +} + +void HLEHelperThread::DoState(PointerWrap &p) { + auto s = p.Section("HLEHelperThread", 1); + if (!s) { + return; + } + + p.Do(id_); + p.Do(entry_); +} + +void HLEHelperThread::Start(u32 a0, u32 a1) { + __KernelStartThread(id_, a0, a1, true); +} + +void HLEHelperThread::Terminate() { + __KernelStopThread(id_, SCE_KERNEL_ERROR_THREAD_TERMINATED, "helper terminated"); +} diff --git a/Core/HLE/HLEHelperThread.h b/Core/HLE/HLEHelperThread.h new file mode 100644 index 0000000000..db123b81f6 --- /dev/null +++ b/Core/HLE/HLEHelperThread.h @@ -0,0 +1,41 @@ +// Copyright (c) 2014- 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 "Core/HLE/sceKernel.h" + +class PointerWrap; + +class HLEHelperThread { +public: + // For savestates. + HLEHelperThread(); + HLEHelperThread(const char *threadName, u32 instructions[], u32 instrCount, u32 prio, int stacksize); + HLEHelperThread(const char *threadName, const char *module, const char *func, u32 prio, int stacksize); + ~HLEHelperThread(); + void DoState(PointerWrap &p); + + void Start(u32 a0, u32 a1); + void Terminate(); + +private: + void AllocEntry(u32 size); + void Create(const char *threadName, u32 prio, int stacksize); + + SceUID id_; + u32 entry_; +}; \ No newline at end of file diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index ccc7f8cc9d..db50412ca0 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -2080,6 +2080,16 @@ SceUID __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int return id; } +SceUID __KernelCreateThreadInternal(const char *threadName, SceUID moduleID, u32 entry, u32 prio, int stacksize, u32 attr) +{ + SceUID id; + Thread *newThread = __KernelCreateThread(id, moduleID, threadName, entry, prio, stacksize, attr); + if (newThread->currentStack.start == 0) + return SCE_KERNEL_ERROR_NO_MEMORY; + + return id; +} + int __KernelCreateThread(const char *threadName, SceUID moduleID, u32 entry, u32 prio, int stacksize, u32 attr, u32 optionAddr) { if (threadName == NULL) @@ -2125,9 +2135,8 @@ int __KernelCreateThread(const char *threadName, SceUID moduleID, u32 entry, u32 if ((attr & PSP_THREAD_ATTR_KERNEL) == 0) attr |= PSP_THREAD_ATTR_USER; - SceUID id; - Thread *newThread = __KernelCreateThread(id, moduleID, threadName, entry, prio, stacksize, attr); - if (newThread->currentStack.start == 0) + SceUID id = __KernelCreateThreadInternal(threadName, moduleID, entry, prio, stacksize, attr); + if (id == SCE_KERNEL_ERROR_NO_MEMORY) { ERROR_LOG_REPORT(SCEKERNEL, "sceKernelCreateThread(name=%s): out of memory, %08x stack requested", threadName, stacksize); return SCE_KERNEL_ERROR_NO_MEMORY; @@ -2152,6 +2161,54 @@ int sceKernelCreateThread(const char *threadName, u32 entry, u32 prio, int stack return __KernelCreateThread(threadName, __KernelGetCurThreadModuleId(), entry, prio, stacksize, attr, optionAddr); } +int __KernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr, bool forceArgs) +{ + u32 error; + Thread *startThread = kernelObjects.Get(threadToStartID, error); + if (startThread == 0) + return error; + + Thread *cur = __GetCurrentThread(); + __KernelResetThread(startThread, cur ? cur->nt.currentPriority : 0); + + u32 &sp = startThread->context.r[MIPS_REG_SP]; + // Force args means just use those as a0/a1 without any special treatment. + // This is a hack to avoid allocating memory for helper threads which take args. + if ((argBlockPtr && argSize > 0) || forceArgs) + { + // Make room for the arguments, always 0x10 aligned. + if (!forceArgs) + sp -= (argSize + 0xf) & ~0xf; + startThread->context.r[MIPS_REG_A0] = argSize; + startThread->context.r[MIPS_REG_A1] = sp; + } + else + { + startThread->context.r[MIPS_REG_A0] = 0; + startThread->context.r[MIPS_REG_A1] = 0; + } + + // Now copy argument to stack. + if (!forceArgs && Memory::IsValidAddress(argBlockPtr)) + Memory::Memcpy(sp, Memory::GetPointer(argBlockPtr), argSize); + + // On the PSP, there's an extra 64 bytes of stack eaten after the args. + // This could be stack overflow safety, or just stack eaten by the kernel entry func. + sp -= 64; + + // Smaller is better for priority. Only switch if the new thread is better. + if (cur && cur->nt.currentPriority > startThread->nt.currentPriority) + { + __KernelChangeReadyState(cur, currentThread, true); + hleReSchedule("thread started"); + } + + // Starting a thread automatically resumes the dispatch thread. + dispatchEnabled = true; + + __KernelChangeReadyState(startThread, threadToStartID, true); + return 0; +} // int sceKernelStartThread(SceUID threadToStartID, SceSize argSize, void *argBlock) int sceKernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr) @@ -2185,44 +2242,7 @@ int sceKernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr) } INFO_LOG(SCEKERNEL, "sceKernelStartThread(thread=%i, argSize=%i, argPtr=%08x)", threadToStartID, argSize, argBlockPtr); - - Thread *cur = __GetCurrentThread(); - __KernelResetThread(startThread, cur ? cur->nt.currentPriority : 0); - - u32 &sp = startThread->context.r[MIPS_REG_SP]; - if (argBlockPtr && argSize > 0) - { - // Make room for the arguments, always 0x10 aligned. - sp -= (argSize + 0xf) & ~0xf; - startThread->context.r[MIPS_REG_A0] = argSize; - startThread->context.r[MIPS_REG_A1] = sp; - } - else - { - startThread->context.r[MIPS_REG_A0] = 0; - startThread->context.r[MIPS_REG_A1] = 0; - } - - // Now copy argument to stack. - if (Memory::IsValidAddress(argBlockPtr)) - Memory::Memcpy(sp, Memory::GetPointer(argBlockPtr), argSize); - - // On the PSP, there's an extra 64 bytes of stack eaten after the args. - // This could be stack overflow safety, or just stack eaten by the kernel entry func. - sp -= 64; - - // Smaller is better for priority. Only switch if the new thread is better. - if (cur && cur->nt.currentPriority > startThread->nt.currentPriority) - { - __KernelChangeReadyState(cur, currentThread, true); - hleReSchedule("thread started"); - } - - // Starting a thread automatically resumes the dispatch thread. - dispatchEnabled = true; - - __KernelChangeReadyState(startThread, threadToStartID, true); - return 0; + return __KernelStartThread(threadToStartID, argSize, argBlockPtr); } int sceKernelGetThreadStackFreeSize(SceUID threadID) diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 67da2cc793..a73d6ab815 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -27,18 +27,22 @@ // http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/ThreadManForUser.java int sceKernelChangeThreadPriority(SceUID threadID, int priority); +SceUID __KernelCreateThreadInternal(const char *threadName, SceUID moduleID, u32 entry, u32 prio, int stacksize, u32 attr); int __KernelCreateThread(const char *threadName, SceUID moduleID, u32 entry, u32 prio, int stacksize, u32 attr, u32 optionAddr); 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); void sceKernelExitDeleteThread(int exitStatus); void sceKernelExitThread(int exitStatus); void _sceKernelExitThread(int exitStatus); SceUID sceKernelGetThreadId(); void sceKernelGetThreadCurrentPriority(); +int __KernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr, bool forceArgs = false); int sceKernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr); u32 sceKernelSuspendDispatchThread(); u32 sceKernelResumeDispatchThread(u32 suspended); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index b807923894..711e9368fe 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -197,6 +197,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Dialog/PSPSaveDialog.cpp \ $(SRC)/Core/Dialog/SavedataParam.cpp \ $(SRC)/Core/Font/PGF.cpp \ + $(SRC)/Core/HLE/HLEHelperThread.cpp \ $(SRC)/Core/HLE/HLETables.cpp \ $(SRC)/Core/HLE/ReplaceTables.cpp \ $(SRC)/Core/HLE/HLE.cpp \