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 \