More reworking of Callbacks, plus some other little fixes.

This commit is contained in:
Henrik Rydgard 2012-11-07 15:44:48 +01:00
parent cf098dfd67
commit aea0580297
19 changed files with 704 additions and 292 deletions

View File

@ -56,9 +56,9 @@
//zlibdec
const HLEFunction FakeSysCalls[] =
{
{NID_THREADRETURN, _sceKernelReturnFromThread, "_sceKernelReturnFromThread"},
{NID_CALLBACKRETURN, _sceKernelReturnFromCallback, "_sceKernelReturnFromCallback"},
{NID_INTERRUPTRETURN, _sceKernelReturnFromInterrupt, "_sceKernelReturnFromInterrupt"},
{NID_THREADRETURN, __KernelReturnFromThread, "__KernelReturnFromThread"},
{NID_CALLBACKRETURN, __KernelReturnFromMipsCall, "__KernelReturnFromMipsCall"},
{NID_INTERRUPTRETURN, __KernelReturnFromInterrupt, "__KernelReturnFromInterrupt"},
{NID_IDLE, _sceKernelIdle, "_sceKernelIdle"},
};

View File

@ -255,7 +255,7 @@ void sceDisplayGetVcount()
// Too spammy
// DEBUG_LOG(HLE,"%i=sceDisplayGetVcount()", vCount);
// Games like Puyo Puyo call this in a tight loop at end-of-frame. We could have it consume some time from CoreTiming?
CoreTiming::Idle(100000);
CoreTiming::Idle(1000000);
RETURN(vCount);
}

View File

@ -60,6 +60,10 @@ u32 sceGeEdramGetSize()
u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, u32 callbackId, u32 optParamAddr)
{
DEBUG_LOG(HLE,"sceGeListEnQueue(addr=%08x, stall=%08x, cbid=%08x, param=%08x)",
listAddress,stallAddress,callbackId,optParamAddr);
//if (!stallAddress)
// stallAddress = listAddress;
u32 listID = gpu->EnqueueList(listAddress, stallAddress);
// HACKY
if (listID)
@ -67,8 +71,6 @@ u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, u32 callbackId, u32 optP
else
state = SCE_GE_LIST_COMPLETED;
DEBUG_LOG(HLE,"%i=sceGeListEnQueue(addr=%08x, stall=%08x, cbid=%08x, param=%08x)",listID,
listAddress,stallAddress,callbackId,optParamAddr);
DEBUG_LOG(HLE,"List enqueued.");
//return display list ID
return listID;
@ -76,6 +78,8 @@ u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, u32 callbackId, u32 optP
u32 sceGeListEnQueueHead(u32 listAddress, u32 stallAddress, u32 callbackId, u32 optParamAddr)
{
if (!stallAddress)
stallAddress = listAddress;
u32 listID = gpu->EnqueueList(listAddress,stallAddress);
// HACKY
if (listID)

View File

@ -379,13 +379,12 @@ const HLEFunction ThreadManForUser[] =
{0x72F3C145,0,"sceKernelReleaseThreadEventHandler"},
{0x369EEB6B,0,"sceKernelReferThreadEventHandlerStatus"},
{0x349d6d6c,&WrapU_V<sceKernelCheckCallback>,"sceKernelCheckCallback"},
{0x349d6d6c,sceKernelCheckCallback,"sceKernelCheckCallback"},
{0xE81CAF8F,sceKernelCreateCallback,"sceKernelCreateCallback"},
{0xEDBA5844,sceKernelDeleteCallback,"sceKernelDeleteCallback"},
{0xC11BA8C4,sceKernelNotifyCallback,"sceKernelNotifyCallback"},
{0xBA4051D6,sceKernelCancelCallback,"sceKernelCancelCallback"},
{0x2A3D44FF,sceKernelGetCallbackCount,"sceKernelGetCallbackCount"},
{0x349D6D6C,&WrapU_V<sceKernelCheckCallback>,"sceKernelCheckCallback"},
{0x730ED8BC,sceKernelReferCallbackStatus,"sceKernelReferCallbackStatus"},
{0x8125221D,sceKernelCreateMbx,"sceKernelCreateMbx"},
@ -426,6 +425,7 @@ const HLEFunction ThreadManForUser[] =
{0xA8AA591F,sceKernelCancelFpl,"sceKernelCancelFpl"},
{0xD8199E4C,sceKernelReferFplStatus,"sceKernelReferFplStatus"},
// Not sure if these should be hooked up. See below.
{0x0E927AED, _sceKernelReturnFromTimerHandler, "_sceKernelReturnFromTimerHandler"},
{0x532A522E, _sceKernelExitThread,"_sceKernelExitThread"},

View File

@ -171,7 +171,7 @@ retry:
EventFlagTh *t = &e->waitingThreads[i];
if (__KernelEventFlagMatches(&e->nef.currentPattern, t->bits, t->wait, t->outAddr))
{
__KernelResumeThread(t->tid);
__KernelResumeThreadFromWait(t->tid);
wokeThreads = true;
e->nef.numWaitThreads--;
e->waitingThreads.erase(e->waitingThreads.begin() + i);
@ -216,8 +216,8 @@ void sceKernelWaitEventFlag()
timeout = Memory::Read_U32(timeoutPtr);
__KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, 0, false);
// MUST NOT return a value after __KernelWaitCurThread as we may have been rescheduled!
}
RETURN(0);
}
else
{
@ -255,8 +255,8 @@ void sceKernelWaitEventFlagCB()
timeout = Memory::Read_U32(timeoutPtr);
__KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, 0, true);
__KernelCheckCallbacks();
}
RETURN(0);
}
else
{

View File

@ -303,7 +303,7 @@ void __TriggerInterruptWithArg(PSPInterrupt intno, int subintr, int arg)
__RunOnePendingInterrupt();
}
void _sceKernelReturnFromInterrupt()
void __KernelReturnFromInterrupt()
{
DEBUG_LOG(CPU, "Left interrupt handler at %08x", currentMIPS->pc);
inInterrupt = false;
@ -424,6 +424,7 @@ const HLEFunction Kernel_Library[] =
{0xa089eca4,sceKernelMemset, "sceKernelMemset"},
{0xDC692EE3,0, "sceKernelTryLockLwMutex"},
{0xbea46419,0, "sceKernelLockLwMutex"},
{0x1FC64E09,0, "sceKernelLockLwMutexCB"},
{0x15b6446b,0, "sceKernelUnlockLwMutex"},
{0x293b45b8,sceKernelGetThreadId, "sceKernelGetThreadId"},
{0x1839852A,0,"sce_paf_private_memcpy"},

View File

@ -60,8 +60,8 @@ void __InterruptsShutdown();
void __TriggerInterrupt(PSPInterrupt intno, int subInterrupts = -1);
void __TriggerInterruptWithArg(PSPInterrupt intno, int subintr, int arg); // For GE "callbacks"
bool __RunOnePendingInterrupt();
void __KernelReturnFromInterrupt();
void _sceKernelReturnFromInterrupt();
u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handler, u32 handlerArg);
void Register_Kernel_Library();

View File

@ -229,11 +229,9 @@ void sceKernelAllocateFplCB()
} else {
// TODO: Should block and process callbacks!
__KernelCheckCallbacks();
RETURN(0);
}
DEBUG_LOG(HLE,"sceKernelAllocateFpl(%i, %08x, %i)", id, PARAM(1), timeOut);
RETURN(0);
}
else
{
@ -620,6 +618,35 @@ void sceKernelReferVplStatus()
RETURN(0);
}
void AllocMemoryBlock() {
const char *pname = Memory::GetCharPointer(PARAM(0));
int type = PARAM(1);
u32 size = PARAM(2);
int paramsAddr = PARAM(3);
DEBUG_LOG(HLE,"AllocMemoryBlock(SysMemUserForUser_FE707FDF)(%s, %i, %i, %08x)", pname, type, size, paramsAddr);
// Just support allocating a block in the user region.
u32 blockPtr = userMemory.Alloc(size, false, pname);
// Create a UID object??? Nah, let's just us the UID itself (hack!)
RETURN(blockPtr);
}
void FreeMemoryBlock() {
SceUID uid = PARAM(0);
DEBUG_LOG(HLE, "FreeMemoryBlock(%i)", uid);
userMemory.Free(uid);
RETURN(0);
}
void GetMemoryBlockPtr() {
SceUID uid = PARAM(0);
DEBUG_LOG(HLE, "GetMemoryBlockPtr(%i)", uid);
RETURN(uid);
}
const HLEFunction SysMemUserForUser[] =
{
@ -637,10 +664,12 @@ const HLEFunction SysMemUserForUser[] =
{0xf77d77cb,sceKernelSetCompilerVersion,"sceKernelSetCompilerVersion"},
{0x35669d4c,0,"sceKernelSetCompiledSdkVersion600_602"}, //??
{0x1b4217bc,0,"sceKernelSetCompiledSdkVersion603_605"},
{0xDB83A952,0,"SysMemUserForUser_DB83A952"},
// Obscure raw block API
{0xDB83A952,GetMemoryBlockPtr,"SysMemUserForUser_DB83A952"}, // GetMemoryBlockAddr
{0x91DE343C,0,"SysMemUserForUser_91DE343C"},
{0x50F61D8A,0,"SysMemUserForUser_50F61D8A"},
{0xFE707FDF,0,"SysMemUserForUser_FE707FDF"},
{0x50F61D8A,FreeMemoryBlock,"SysMemUserForUser_50F61D8A"}, // FreeMemoryBlock
{0xFE707FDF,AllocMemoryBlock,"SysMemUserForUser_FE707FDF"}, // AllocMemoryBlock
};

View File

@ -567,6 +567,8 @@ u32 sceKernelLoadModule(const char *name, u32 flags)
return SCE_KERNEL_ERROR_ILLEGAL_OBJECT;
}
DEBUG_LOG(LOADER, "sceKernelLoadModule(%s, %08x)", name, flags);
SceKernelLMOption *lmoption = 0;
int position = 0;
// TODO: Use position to decide whether to load high or low
@ -633,8 +635,8 @@ void sceKernelUnloadModule()
void sceKernelGetModuleIdByAddress()
{
ERROR_LOG(HLE,"UNIMPL sceKernelGetModuleIdByAddress(%08x)", PARAM(0));
if (PARAM(0) == 0x08800000)
ERROR_LOG(HLE,"HACKIMPL sceKernelGetModuleIdByAddress(%08x)", PARAM(0));
if ((PARAM(0) & 0xFFFF0000) == 0x08800000)
RETURN(mainModuleID);
else
RETURN(0);

View File

@ -18,6 +18,7 @@
#include "HLE.h"
#include "sceKernel.h"
#include "sceKernelMsgPipe.h"
#include "sceKernelThread.h"
struct NativeMsgPipe
{
@ -44,6 +45,8 @@ struct MsgPipe : public KernelObject
// Ring buffer
u8 *buffer;
int writePos;
int readPos;
};
void sceKernelCreateMsgPipe()
@ -66,6 +69,9 @@ void sceKernelCreateMsgPipe()
m->nmp.numReceiveWaitThreads = 0;
m->buffer = new u8[size];
m->writePos = 0;
m->readPos = 0;
RETURN(id);
}
@ -93,6 +99,35 @@ void sceKernelSendMsgPipe()
u32 resultAddr = PARAM(4);
u32 timeoutPtr = PARAM(5);
u32 error;
MsgPipe *pipe = kernelObjects.Get<MsgPipe>(uid, error);
if (!pipe) {
ERROR_LOG(HLE, "sceKernelSendMsgPipe(%i) - ERROR %08x", uid, error);
RETURN(error);
return;
}
if (sendSize > pipe->nmp.freeSize) {
// TODO: Block
ERROR_LOG(HLE, "sceKernelSendMsgPipe(%i) - Message won't fit", uid, error);
RETURN(SCE_KERNEL_ERROR_MPP_FULL);
return;
}
const u8 *source = Memory::GetPointer(sendBufAddr);
int destIndex = pipe->writePos;
if (pipe->writePos + sendSize > pipe->nmp.size) {
// Split in two
int firstCopySize = pipe->nmp.size - pipe->writePos;
memcpy(pipe->buffer + pipe->writePos, source, firstCopySize);
memcpy(pipe->buffer, source + firstCopySize, sendSize - firstCopySize);
} else {
memcpy(pipe->buffer + pipe->writePos, source, sendSize);
}
pipe->writePos += sendSize;
pipe->writePos %= pipe->nmp.size;
ERROR_LOG(HLE, "UNIMPL sceKernelSendMsgPipe(%i, %08x, %i, %i, %08x, %08x)", uid, sendBufAddr, sendSize, waitMode, resultAddr, timeoutPtr);
RETURN(0);
}
@ -106,8 +141,38 @@ void sceKernelSendMsgPipeCB()
u32 resultAddr = PARAM(4);
u32 timeoutPtr = PARAM(5);
ERROR_LOG(HLE, "UNIMPL sceKernelSendMsgPipeCB(%i, %08x, %i, %i, %08x, %08x)", uid, sendBufAddr, sendSize, waitMode, resultAddr, timeoutPtr);
u32 error;
MsgPipe *pipe = kernelObjects.Get<MsgPipe>(uid, error);
if (!pipe) {
ERROR_LOG(HLE, "sceKernelSendMsgPipe(%i) - ERROR %08x", uid, error);
RETURN(error);
return;
}
if (sendSize > pipe->nmp.freeSize) {
// TODO: Block
ERROR_LOG(HLE, "sceKernelSendMsgPipe(%i) - Message won't fit", uid, error);
RETURN(SCE_KERNEL_ERROR_MPP_FULL);
return;
}
const u8 *source = Memory::GetPointer(sendBufAddr);
int destIndex = pipe->writePos;
if (pipe->writePos + sendSize > pipe->nmp.size) {
// Split in two
int firstCopySize = pipe->nmp.size - pipe->writePos;
memcpy(pipe->buffer + pipe->writePos, source, firstCopySize);
memcpy(pipe->buffer, source + firstCopySize, sendSize - firstCopySize);
} else {
memcpy(pipe->buffer + pipe->writePos, source, sendSize);
}
pipe->writePos += sendSize;
pipe->writePos %= pipe->nmp.size;
ERROR_LOG(HLE, "UNIMPL sceKernelSendMsgPipe(%i, %08x, %i, %i, %08x, %08x)", uid, sendBufAddr, sendSize, waitMode, resultAddr, timeoutPtr);
RETURN(0);
__KernelCheckCallbacks();
}
void sceKernelTrySendMsgPipe()
@ -117,8 +182,38 @@ void sceKernelTrySendMsgPipe()
u32 sendSize = PARAM(2);
int waitMode = PARAM(3);
u32 resultAddr = PARAM(4);
u32 timeoutPtr = PARAM(5);
ERROR_LOG(HLE, "UNIMPL sceKernelTrySendMsgPipe(%i, %08x, %i, %i, %08x)", uid, sendBufAddr, sendSize, waitMode, resultAddr);
u32 error;
MsgPipe *pipe = kernelObjects.Get<MsgPipe>(uid, error);
if (!pipe) {
ERROR_LOG(HLE, "sceKernelSendMsgPipe(%i) - ERROR %08x", uid, error);
RETURN(error);
return;
}
if (sendSize > pipe->nmp.freeSize) {
// TODO: Block
ERROR_LOG(HLE, "sceKernelSendMsgPipe(%i) - Message won't fit", uid, error);
RETURN(SCE_KERNEL_ERROR_MPP_FULL);
return;
}
const u8 *source = Memory::GetPointer(sendBufAddr);
int destIndex = pipe->writePos;
if (pipe->writePos + sendSize > pipe->nmp.size) {
// Split in two
int firstCopySize = pipe->nmp.size - pipe->writePos;
memcpy(pipe->buffer + pipe->writePos, source, firstCopySize);
memcpy(pipe->buffer, source + firstCopySize, sendSize - firstCopySize);
} else {
memcpy(pipe->buffer + pipe->writePos, source, sendSize);
}
pipe->writePos += sendSize;
pipe->writePos %= pipe->nmp.size;
ERROR_LOG(HLE, "UNIMPL sceKernelSendMsgPipe(%i, %08x, %i, %i, %08x, %08x)", uid, sendBufAddr, sendSize, waitMode, resultAddr, timeoutPtr);
RETURN(0);
}
@ -132,7 +227,7 @@ void sceKernelReceiveMsgPipe()
u32 timeoutPtr = PARAM(5);
ERROR_LOG(HLE, "UNIMPL sceKernelReceiveMsgPipe(%i, %08x, %i, %i, %08x, %08x)", uid, receiveBufAddr, receiveSize, waitMode, resultAddr, timeoutPtr);
RETURN(0);
RETURN(SCE_KERNEL_ERROR_MPP_EMPTY);
}
void sceKernelReceiveMsgPipeCB()
@ -145,7 +240,7 @@ void sceKernelReceiveMsgPipeCB()
u32 timeoutPtr = PARAM(5);
ERROR_LOG(HLE, "UNIMPL sceKernelReceiveMsgPipeCB(%i, %08x, %i, %i, %08x, %08x)", uid, receiveBufAddr, receiveSize, waitMode, resultAddr, timeoutPtr);
RETURN(0);
RETURN(SCE_KERNEL_ERROR_MPP_EMPTY);
}
void sceKernelTryReceiveMsgPipe()
@ -157,7 +252,7 @@ void sceKernelTryReceiveMsgPipe()
u32 resultAddr = PARAM(4);
ERROR_LOG(HLE, "UNIMPL sceKernelTryReceiveMsgPipe(%i, %08x, %i, %i, %08x)", uid, receiveBufAddr, receiveSize, waitMode, resultAddr);
RETURN(0);
RETURN(SCE_KERNEL_ERROR_MPP_EMPTY);
}
void sceKernelCancelMsgPipe()

View File

@ -110,13 +110,14 @@ u32 sceKernelLockMutex(u32 id, u32 count, u32 timeoutPtr)
else
{
// Yeah, we need to block. Somehow.
ERROR_LOG(HLE,"Mutex should block!");
}
return 0;
}
u32 sceKernelLockMutexCB(u32 id, u32 count, u32 timeoutPtr)
{
DEBUG_LOG(HLE,"UNIMPL sceKernelLockMutexCB(%i, %i, %08x)", id, count, timeoutPtr);
ERROR_LOG(HLE,"UNIMPL sceKernelLockMutexCB(%i, %i, %08x)", id, count, timeoutPtr);
return 0;
}
@ -148,30 +149,36 @@ struct NativeLwMutex
void sceKernelCreateLwMutex()
{
DEBUG_LOG(HLE,"UNIMPL sceKernelCreateLwMutex()");
ERROR_LOG(HLE,"UNIMPL sceKernelCreateLwMutex()");
RETURN(0);
}
void sceKernelDeleteLwMutex()
{
DEBUG_LOG(HLE,"UNIMPL sceKernelDeleteLwMutex()");
ERROR_LOG(HLE,"UNIMPL sceKernelDeleteLwMutex()");
RETURN(0);
}
void sceKernelTryLockLwMutex()
{
DEBUG_LOG(HLE,"UNIMPL sceKernelTryLockLwMutex()");
ERROR_LOG(HLE,"UNIMPL sceKernelTryLockLwMutex()");
RETURN(0);
}
void sceKernelLockLwMutex()
{
DEBUG_LOG(HLE,"UNIMPL sceKernelLockLwMutex()");
ERROR_LOG(HLE,"UNIMPL sceKernelLockLwMutex()");
RETURN(0);
}
void sceKernelLockLwMutexCB()
{
ERROR_LOG(HLE,"UNIMPL sceKernelLockLwMutexCB()");
RETURN(0);
}
void sceKernelUnlockLwMutex()
{
DEBUG_LOG(HLE,"UNIMPL void sceKernelUnlockLwMutex()");
ERROR_LOG(HLE,"UNIMPL void sceKernelUnlockLwMutex()");
RETURN(0);
}

View File

@ -28,4 +28,5 @@ void sceKernelCreateLwMutex();
void sceKernelDeleteLwMutex();
void sceKernelTryLockLwMutex();
void sceKernelLockLwMutex();
void sceKernelLockLwMutexCB();
void sceKernelUnlockLwMutex();

View File

@ -137,7 +137,7 @@ retry:
int wVal = (int)__KernelGetWaitValue(id, error);
if (wVal <= s->ns.currentCount)
{
__KernelResumeThread(id);
__KernelResumeThreadFromWait(id);
s->ns.currentCount -= wVal;
wokeThreads = true;
s->waitingThreads.erase(iter);

View File

@ -34,15 +34,6 @@
#include "sceKernelModule.h"
#include "sceKernelInterrupt.h"
enum ThreadStatus
{
THREADSTATUS_RUNNING = 1,
THREADSTATUS_READY = 2,
THREADSTATUS_WAIT = 4,
THREADSTATUS_SUSPEND = 8,
THREADSTATUS_DORMANT = 16,
THREADSTATUS_DEAD = 32,
};
enum {
ERROR_KERNEL_THREAD_ALREADY_DORMANT = 0x800201a2,
@ -87,106 +78,6 @@ struct SceKernelSysClock {
u32 hi;
};
// Real PSP struct, don't change the fields
struct NativeThread
{
u32 nativeSize;
char name[KERNELOBJECT_MAX_NAME_LENGTH+1];
// Threading stuff
u32 attr;
u32 status;
u32 entrypoint;
u32 initialStack;
u32 stackSize;
u32 gpreg;
int initialPriority;
int currentPriority;
WaitType waitType;
SceUID waitID;
int wakeupCount;
int exitStatus;
SceKernelSysClock runForClocks;
int numInterruptPreempts;
int numThreadPreempts;
int numReleases;
};
class Thread : public KernelObject
{
public:
const char *GetName() {return nt.name;}
const char *GetTypeName() {return "Thread";}
void GetQuickInfo(char *ptr, int size)
{
sprintf(ptr, "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" : "",
nt.waitType,
nt.waitID,
waitValue);
}
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; }
int GetIDType() const { return SCE_KERNEL_TMID_Thread; }
bool AllocateStack(u32 &stackSize)
{
if (nt.attr & PSP_THREAD_ATTR_KERNEL)
{
// Allocate stacks for kernel threads (idle) in kernel RAM
stackBlock = kernelMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str());
}
else
{
stackBlock = userMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str());
}
if (stackBlock == (u32)-1)
{
ERROR_LOG(HLE, "Failed to allocate stack for thread");
return false;
}
// Fill the stack.
Memory::Memset(stackBlock, 0xFF, stackSize);
context.r[MIPS_REG_SP] = stackBlock + stackSize;
nt.initialStack = context.r[MIPS_REG_SP];
nt.stackSize = stackSize;
// What's this 512?
context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP] - 512;
context.r[MIPS_REG_SP] -= 512;
return true;
}
~Thread()
{
if (nt.attr & PSP_THREAD_ATTR_KERNEL) {
kernelMemory.Free(stackBlock);
} else {
userMemory.Free(stackBlock);
}
}
NativeThread nt;
u32 waitValue;
bool sleeping;
bool isProcessingCallbacks;
ThreadContext context;
std::set<SceUID> registeredCallbacks[THREAD_CALLBACK_NUM_TYPES];
std::list<SceUID> readyCallbacks[THREAD_CALLBACK_NUM_TYPES];
u32 stackBlock;
};
struct NativeCallback
{
@ -239,6 +130,123 @@ public:
bool forceDelete;
};
// Real PSP struct, don't change the fields
struct NativeThread
{
u32 nativeSize;
char name[KERNELOBJECT_MAX_NAME_LENGTH+1];
// Threading stuff
u32 attr;
u32 status;
u32 entrypoint;
u32 initialStack;
u32 stackSize;
u32 gpreg;
int initialPriority;
int currentPriority;
WaitType waitType;
SceUID waitID;
int wakeupCount;
int exitStatus;
SceKernelSysClock runForClocks;
int numInterruptPreempts;
int numThreadPreempts;
int numReleases;
};
struct ThreadWaitInfo {
u32 waitValue;
};
class Thread : public KernelObject
{
public:
const char *GetName() {return nt.name;}
const char *GetTypeName() {return "Thread";}
void GetQuickInfo(char *ptr, int size)
{
sprintf(ptr, "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" : "",
nt.waitType,
nt.waitID,
waitInfo.waitValue);
}
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; }
int GetIDType() const { return SCE_KERNEL_TMID_Thread; }
bool AllocateStack(u32 &stackSize)
{
if (nt.attr & PSP_THREAD_ATTR_KERNEL)
{
// Allocate stacks for kernel threads (idle) in kernel RAM
stackBlock = kernelMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str());
}
else
{
stackBlock = userMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str());
}
if (stackBlock == (u32)-1)
{
ERROR_LOG(HLE, "Failed to allocate stack for thread");
return false;
}
// Fill the stack.
Memory::Memset(stackBlock, 0xFF, stackSize);
context.r[MIPS_REG_SP] = stackBlock + stackSize;
nt.initialStack = context.r[MIPS_REG_SP];
nt.stackSize = stackSize;
// What's this 512?
context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP] - 512;
context.r[MIPS_REG_SP] -= 512;
return true;
}
~Thread()
{
if (nt.attr & PSP_THREAD_ATTR_KERNEL) {
kernelMemory.Free(stackBlock);
} else {
userMemory.Free(stackBlock);
}
}
// Utils
bool isRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; }
bool isStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; }
bool isReady() const { return (nt.status & THREADSTATUS_DORMANT) != 0; }
bool isWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; }
bool isSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; }
NativeThread nt;
ThreadWaitInfo waitInfo;
bool sleeping;
bool isProcessingCallbacks;
ThreadContext context;
std::set<SceUID> registeredCallbacks[THREAD_CALLBACK_NUM_TYPES];
std::list<SceUID> readyCallbacks[THREAD_CALLBACK_NUM_TYPES];
std::list<int> pendingMipsCalls;
u32 stackBlock;
};
void __KernelExecuteMipsCallOnCurrentThread(int callId);
int g_inCbCount = 0;
Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr);
@ -257,7 +265,7 @@ SceUID threadIdleID[2];
int eventScheduledWakeup;
bool dispatchSuspended = false;
bool dispatchEnabled = true;
// This seems nasty
@ -273,7 +281,7 @@ Thread *__GetCurrentThread() {
return currentThread;
}
u32 __KernelCallbackReturnAddress()
u32 __KernelMipsCallReturnAddress()
{
return cbReturnHackAddr;
}
@ -289,7 +297,7 @@ void __KernelThreadingInit()
{
u32 blockSize = 4 * 4 + 4 * 2 * 3; // One 16-byte thread plus 3 8-byte "hacks"
dispatchSuspended = false;
dispatchEnabled = true;
idleThreadHackAddr = kernelMemory.Alloc(blockSize, false, "threadrethack");
// Make sure it got allocated where we expect it... at the very start of kernel RAM
@ -362,7 +370,7 @@ u32 __KernelGetWaitValue(SceUID threadID, u32 &error)
Thread *t = kernelObjects.Get<Thread>(threadID, error);
if (t)
{
return t->waitValue;
return t->waitInfo.waitValue;
}
else
{
@ -529,7 +537,7 @@ bool __KernelTriggerWait(WaitType type, int id, bool dontSwitch)
return true;
}
u32 __KernelResumeThread(SceUID threadID)
u32 __KernelResumeThreadFromWait(SceUID threadID)
{
u32 error;
Thread *t = kernelObjects.Get<Thread>(threadID, error);
@ -538,11 +546,12 @@ u32 __KernelResumeThread(SceUID threadID)
t->nt.status &= ~THREADSTATUS_WAIT;
if (!(t->nt.status & (THREADSTATUS_SUSPEND | THREADSTATUS_WAIT)))
t->nt.status |= THREADSTATUS_READY;
t->isProcessingCallbacks = false;
return 0;
}
else
{
ERROR_LOG(HLE, "__KernelResumeThread(%d): bad thread: %08x", threadID, error);
ERROR_LOG(HLE, "__KernelResumeThreadFromWait(%d): bad thread: %08x", threadID, error);
return error;
}
}
@ -550,12 +559,11 @@ u32 __KernelResumeThread(SceUID threadID)
// makes the current thread wait for an event
void __KernelWaitCurThread(WaitType type, SceUID waitID, u32 waitValue, int timeout, bool processCallbacks)
{
currentThread->nt.status = THREADSTATUS_WAIT;
currentThread->nt.waitID = waitID;
currentThread->nt.waitType = type;
__KernelChangeThreadState(currentThread, THREADSTATUS_WAIT);
currentThread->nt.numReleases++;
currentThread->waitValue = waitValue;
currentThread->isProcessingCallbacks = processCallbacks;
currentThread->waitInfo.waitValue = waitValue;
if (timeout)
{
// TODO:
@ -566,7 +574,8 @@ void __KernelWaitCurThread(WaitType type, SceUID waitID, u32 waitValue, int time
// TODO: time waster
char temp[256];
sprintf(temp, "started wait %s", waitTypeStrings[(int)type]);
__KernelReSchedule(temp);
__KernelReSchedule(processCallbacks, temp);
// TODO: Remove thread from Ready queue?
}
@ -594,23 +603,15 @@ void __KernelRemoveFromThreadQueue(Thread *t)
}
}
void __KernelReSchedule(const char *reason)
{
// cancel rescheduling when in interrupt or callback, otherwise everything will be fucked up
if (__IsInInterrupt() || __KernelInCallback())
{
reason = "WTF";
return;
}
Thread *__KernelNextThread() {
// round-robin scheduler
// seems to work ?
// not accurate!
retry:
int bestthread = -1;
int prio=0xffffff;
int prio = 0xffffff;
int next = 0;
for (size_t i=0; i<threadqueue.size(); i++)
for (size_t i = 0; i < threadqueue.size(); i++)
{
if (currentThread == threadqueue[i])
{
@ -619,7 +620,7 @@ retry:
}
}
for (size_t i=0; i<threadqueue.size(); i++)
for (size_t i = 0; i < threadqueue.size(); i++)
{
next = (next + 1) % threadqueue.size();
@ -635,30 +636,55 @@ retry:
}
if (bestthread != -1)
return threadqueue[bestthread];
else
return 0;
}
void __KernelReSchedule(const char *reason)
{
// cancel rescheduling when in interrupt or callback, otherwise everything will be fucked up
if (__IsInInterrupt() || __KernelInCallback())
{
reason = "In Interrupt Or Callback";
return;
}
retry:
Thread *nextThread = __KernelNextThread();
if (nextThread)
{
if (currentThread) // It might just have been deleted.
{
__KernelSaveContext(&currentThread->context);
DEBUG_LOG(HLE,"Context saved (%s): %i - %s - pc: %08x", reason, currentThread->GetUID(), currentThread->GetName(), currentMIPS->pc);
}
currentThread = threadqueue[bestthread];
__KernelLoadContext(&currentThread->context);
DEBUG_LOG(HLE,"Context loaded (%s): %i - %s - pc: %08x", reason, currentThread->GetUID(), currentThread->GetName(), currentMIPS->pc);
__KernelSwitchContext(nextThread, reason);
return;
}
else
{
_dbg_assert_msg_(HLE,0,"No threads available to schedule! There should be at least one idle thread available.");
// This shouldn't happen anymore now that we have idle threads.
// No threads want to run : increase timers, skip time in general
// MessageBox(0,"Error: no thread to transition to",0,0);
// DEBUG_LOG(HLE,"No thread to transition to, idling");
_dbg_assert_msg_(HLE,0,"No threads available to schedule! There should be at least one idle thread available.");
CoreTiming::Idle();
goto retry;
}
}
void __KernelReSchedule(bool doCallbacks, const char *reason)
{
Thread *thread = currentThread;
if (doCallbacks)
{
if (thread)
thread->isProcessingCallbacks = doCallbacks;
__KernelCheckCallbacks();
}
__KernelReSchedule(reason);
if (doCallbacks && thread == currentThread) {
if (thread->isRunning()) {
thread->isProcessingCallbacks = false;
}
}
}
//////////////////////////////////////////////////////////////////////////
// Thread Management
@ -729,7 +755,7 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32
t->nt.status = THREADSTATUS_DORMANT;
t->nt.waitType = WAITTYPE_NONE;
t->nt.waitID = 0;
t->waitValue = 0;
memset(&t->waitInfo, 0, sizeof(t->waitInfo));
t->nt.exitStatus = 0;
t->nt.numInterruptPreempts = 0;
t->nt.numReleases = 0;
@ -737,6 +763,7 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32
t->nt.runForClocks.low = 0;
t->nt.runForClocks.hi = 0;
t->nt.wakeupCount = 0;
t->isProcessingCallbacks = false;
if (moduleID)
t->nt.gpreg = __KernelGetModuleGP(moduleID);
else
@ -863,10 +890,10 @@ void sceKernelGetThreadStackFreeSize()
RETURN(sz & ~3);
}
void _sceKernelReturnFromThread()
// Internal function
void __KernelReturnFromThread()
{
INFO_LOG(HLE,"_sceKernelReturnFromThread : %s", currentThread->GetName());
INFO_LOG(HLE,"__KernelReturnFromThread : %s", currentThread->GetName());
currentThread->nt.exitStatus = currentThread->context.r[2];
currentThread->nt.status = THREADSTATUS_DORMANT;
@ -935,17 +962,17 @@ void sceKernelExitDeleteThread()
u32 sceKernelSuspendDispatchThread()
{
u32 oldDispatchSuspended = dispatchSuspended;
dispatchSuspended = true;
u32 oldDispatchSuspended = !dispatchEnabled;
dispatchEnabled = false;
DEBUG_LOG(HLE,"%i=sceKernelSuspendDispatchThread()", oldDispatchSuspended);
return oldDispatchSuspended;
}
u32 sceKernelResumeDispatchThread(u32 suspended)
{
u32 oldDispatchSuspended = dispatchSuspended;
dispatchSuspended = suspended;
DEBUG_LOG(HLE,"%i=sceKernelResumeDispatchThread(%i)",oldDispatchSuspended, suspended);
u32 oldDispatchSuspended = !dispatchEnabled;
dispatchEnabled = !suspended;
DEBUG_LOG(HLE,"%i=sceKernelResumeDispatchThread(%i)", oldDispatchSuspended, suspended);
return oldDispatchSuspended;
}
@ -1063,6 +1090,7 @@ void sceKernelDelayThreadCB()
__KernelScheduleWakeup(curThread, usec);
__KernelWaitCurThread(WAITTYPE_DELAY, curThread, 0, 0, true);
__KernelCheckCallbacks();
__KernelExecutePendingMipsCalls();
}
void sceKernelDelayThread()
@ -1080,48 +1108,45 @@ void sceKernelDelayThread()
//////////////////////////////////////////////////////////////////////////
void sceKernelWakeupThread()
{
SceUID id = PARAM(0);
SceUID uid = PARAM(0);
u32 error;
Thread *t = kernelObjects.Get<Thread>(id, error);
Thread *t = kernelObjects.Get<Thread>(uid, error);
if (t)
{
t->nt.wakeupCount++;
DEBUG_LOG(HLE,"sceKernelWakeupThread(%i) - wakeupCount incremented to %i",id,t->nt.wakeupCount);
if (t->nt.waitType == WAITTYPE_SLEEP && t->nt.wakeupCount>=0)
{
__KernelResumeThread(id);
if (t->nt.waitType != WAITTYPE_SLEEP) {
t->nt.wakeupCount++;
DEBUG_LOG(HLE,"sceKernelWakeupThread(%i) - wakeupCount incremented to %i", uid, t->nt.wakeupCount);
RETURN(0);
} else {
__KernelResumeThreadFromWait(uid);
__KernelReSchedule("wakeup");
}
}
RETURN(0);
}
static void __KernelSleepThread(bool doCallbacks) {
DEBUG_LOG(HLE,"sceKernelSleepThread() - wakeupCount decremented to %i", currentThread->nt.wakeupCount);
if (currentThread->nt.wakeupCount > 0) {
currentThread->nt.wakeupCount--;
RETURN(0);
} else {
RETURN(0);
__KernelWaitCurThread(WAITTYPE_SLEEP, 0, 0, 0, doCallbacks);
}
}
void sceKernelSleepThread()
{
currentThread->nt.wakeupCount--;
DEBUG_LOG(HLE,"sceKernelSleepThread() - wakeupCount decremented to %i", currentThread->nt.wakeupCount);
if (currentThread->nt.wakeupCount < 0)
__KernelWaitCurThread(WAITTYPE_SLEEP, 0, 0, 0, false);
else
{
RETURN(0);
}
__KernelSleepThread(false);
}
//the homebrew PollCallbacks
void sceKernelSleepThreadCB()
{
DEBUG_LOG(HLE,"sceKernelSleepThreadCB()");
//set it to waiting
currentThread->nt.wakeupCount--;
DEBUG_LOG(HLE,"sceKernelSleepThreadCB() - wakeupCount decremented to %i", currentThread->nt.wakeupCount);
if (currentThread->nt.wakeupCount < 0) {
__KernelWaitCurThread(WAITTYPE_SLEEP, 0, 0, 0, true);
__KernelCheckCallbacks();
}
else
{
RETURN(0);
}
DEBUG_LOG(HLE, "sceKernelSleepThreadCB()");
__KernelSleepThread(true);
__KernelCheckCallbacks();
__KernelExecutePendingMipsCalls();
}
void sceKernelWaitThreadEnd()
@ -1179,6 +1204,8 @@ void sceKernelResumeThread()
RETURN(0);
}
//////////////////////////////////////////////////////////////////////////
// CALLBACKS
//////////////////////////////////////////////////////////////////////////
@ -1287,84 +1314,280 @@ void sceKernelReferCallbackStatus()
}
}
// Context switches to the thread and executes the callback.
u32 __KernelRunCallbackOnThread(SceUID cbId, Thread *thread)
{
if (g_inCbCount > 0) {
WARN_LOG(HLE, "__KernelRunCallbackOnThread: Already in a callback!");
// Owns outstanding MIPS calls and provides a way to get them by ID.
// TODO: MipsCall structs are kinda big, try to cut down on the copying by owning pointers instead.
class MipsCallManager {
public:
MipsCallManager() : idGen_(0) {}
int add(MipsCall *call) {
int id = genId();
calls_.insert(std::pair<int, MipsCall *>(id, call));
return id;
}
MipsCall *get(int id) {
return calls_[id];
}
MipsCall *pop(int id) {
MipsCall *temp = calls_[id];
calls_.erase(id);
return temp;
}
private:
int genId() { return ++idGen_; }
std::map<int, MipsCall *> calls_;
int idGen_;
};
MipsCallManager mipsCalls;
class ActionAfterMipsCall : public Action
{
public:
virtual void run();
Thread *thread;
// Saved thread state
int status;
WaitType waitType;
int waitId;
ThreadWaitInfo waitInfo;
bool isProcessingCallbacks;
Action *chainedAction;
};
void ActionAfterMipsCall::run() {
thread->nt.status = status;
thread->nt.waitType = waitType;
thread->nt.waitID = waitId;
thread->waitInfo = waitInfo;
thread->isProcessingCallbacks = isProcessingCallbacks;
if (chainedAction) {
chainedAction->run();
delete chainedAction;
}
}
void __KernelSwitchContext(Thread *target, const char *reason)
{
if (currentThread) // It might just have been deleted.
{
__KernelSaveContext(&currentThread->context);
DEBUG_LOG(HLE,"Context saved (%s): %i - %s - pc: %08x", reason, currentThread->GetUID(), currentThread->GetName(), currentMIPS->pc);
}
currentThread = target;
__KernelLoadContext(&currentThread->context);
DEBUG_LOG(HLE,"Context loaded (%s): %i - %s - pc: %08x", reason, currentThread->GetUID(), currentThread->GetName(), currentMIPS->pc);
__KernelExecutePendingMipsCalls();
}
void __KernelChangeThreadState(Thread *thread, ThreadStatus newStatus) {
if (!thread || thread->nt.status == newStatus)
return;
if (!dispatchEnabled && thread == currentThread && newStatus != THREADSTATUS_RUNNING) {
ERROR_LOG(HLE, "Dispatching suspended, not changing thread state");
return;
}
// TODO: JPSCP has many conditions here, like removing wait timeout actions etc.
// if (thread->nt.status == THREADSTATUS_WAIT && newStatus != THREADSTATUS_WAITSUSPEND) {
thread->nt.status = newStatus;
if (newStatus == THREADSTATUS_WAIT) {
if (thread->nt.waitType == WAITTYPE_NONE) {
ERROR_LOG(HLE, "Waittype none not allowed here");
}
// Schedule deletion of stopped threads here. if (thread->isStopped())
}
}
bool __CanExecuteCallbackNow(Thread *thread) {
return g_inCbCount == 0;
}
void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, bool returnVoid, std::vector<int> args) {
if (thread) {
ActionAfterMipsCall *after = new ActionAfterMipsCall();
after->chainedAction = afterAction;
after->thread = thread;
after->status = thread->nt.status;
after->waitType = thread->nt.waitType;
after->waitId = thread->nt.waitID;
after->waitInfo = thread->waitInfo;
afterAction = after;
// Release thread from waiting
thread->nt.waitType = WAITTYPE_NONE;
__KernelChangeThreadState(thread, THREADSTATUS_READY);
}
MipsCall *call = new MipsCall();
call->entryPoint = entryPoint;
for (int i = 0; i < args.size(); i++) {
call->args[i] = args[i];
}
call->numArgs = args.size();
call->doAfter = afterAction;
call->tag = "callAddress";
int callId = mipsCalls.add(call);
bool called = false;
if (!thread || thread == currentThread) {
if (__CanExecuteCallbackNow(thread)) {
thread = currentThread;
__KernelChangeThreadState(thread, THREADSTATUS_RUNNING);
__KernelExecuteMipsCallOnCurrentThread(callId);
called = true;
}
}
if (!called) {
DEBUG_LOG(HLE, "Making mipscall pending on thread");
thread->pendingMipsCalls.push_back(callId);
}
}
void __KernelExecuteMipsCallOnCurrentThread(int callId)
{
if (g_inCbCount > 0) {
WARN_LOG(HLE, "__KernelExecuteMipsCallOnCurrentThread: Already in a callback!");
}
DEBUG_LOG(HLE, "Executing mipscall %i", callId);
MipsCall *call = mipsCalls.get(callId);
// Save the few regs that need saving
call->savedPc = currentMIPS->pc;
call->savedRa = currentMIPS->r[MIPS_REG_RA];
call->savedV0 = currentMIPS->r[MIPS_REG_V0];
call->savedV1 = currentMIPS->r[MIPS_REG_V1];
call->savedIdRegister = currentMIPS->r[MIPS_REG_CALL_ID];
call->returnVoid = false;
// Set up the new state
currentMIPS->pc = call->entryPoint;
currentMIPS->r[MIPS_REG_RA] = __KernelMipsCallReturnAddress();
currentMIPS->r[MIPS_REG_CALL_ID] = callId;
for (int i = 0; i < call->numArgs; i++) {
currentMIPS->r[MIPS_REG_A0 + i] = call->args[i];
}
g_inCbCount++;
}
void __KernelReturnFromMipsCall()
{
int callId = currentMIPS->r[MIPS_REG_CALL_ID];
MipsCall *call = mipsCalls.pop(callId);
// Value returned by the callback function
u32 retVal = currentMIPS->r[MIPS_REG_V0];
DEBUG_LOG(HLE,"__KernelReturnFromMipsCall(), returned %08x", retVal);
// Should also save/restore wait state here.
if (call->doAfter)
call->doAfter->run();
currentMIPS->pc = call->savedPc;
currentMIPS->r[MIPS_REG_RA] = call->savedRa;
currentMIPS->r[MIPS_REG_V0] = call->savedV0;
currentMIPS->r[MIPS_REG_V1] = call->savedV1;
currentMIPS->r[MIPS_REG_CALL_ID] = call->savedIdRegister;
g_inCbCount--;
// yeah! back in the real world, let's keep going. Should we process more callbacks?
__KernelCheckCallbacks();
if (!__KernelExecutePendingMipsCalls())
{
// We should definitely reschedule as we might still be asleep. - except if we came from checkcallbacks?
__KernelReSchedule("return from callback");
}
}
bool __KernelExecutePendingMipsCalls()
{
Thread *thread = __GetCurrentThread();
if (thread->pendingMipsCalls.empty()) {
// Nothing to do
return false;
}
if (__CanExecuteCallbackNow(thread))
{
// Pop off the first pending mips call
int callId = thread->pendingMipsCalls.front();
thread->pendingMipsCalls.pop_front();
__KernelExecuteMipsCallOnCurrentThread(callId);
return true;
}
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)
{
u32 error;
Callback *cb = kernelObjects.Get<Callback>(cbId, error);
if (!cb) {
return error;
ERROR_LOG(HLE, "__KernelRunCallbackOnThread: Bad cbId %i", cbId);
return;
}
// First, context switch to the thread.
Thread *curThread = __GetCurrentThread();
if (thread != curThread) {
__KernelSaveContext(&curThread->context);
__KernelLoadContext(&thread->context);
}
//Alright, we're on the right thread
DEBUG_LOG(HLE, "__KernelRunCallbackOnThread: Turning callback %i into pending mipscall", cbId);
// Save the few regs that need saving
cb->savedPC = currentMIPS->pc;
cb->savedRA = currentMIPS->r[MIPS_REG_RA];
cb->savedV0 = currentMIPS->r[MIPS_REG_V0];
cb->savedV1 = currentMIPS->r[MIPS_REG_V1];
cb->savedIdRegister = currentMIPS->r[MIPS_REG_CB_ID];
// Alright, we're on the right thread
// Should save/restore wait state?
// Set up the new state
// TODO: check?
currentMIPS->r[MIPS_REG_A0] = cb->nc.notifyCount;
currentMIPS->r[MIPS_REG_A1] = cb->nc.notifyArg;
currentMIPS->r[MIPS_REG_A2] = cb->nc.commonArgument;
currentMIPS->pc = cb->nc.entrypoint;
currentMIPS->r[MIPS_REG_RA] = __KernelCallbackReturnAddress();
currentMIPS->r[MIPS_REG_CB_ID] = cbId;
std::vector<int> args;
args.push_back(cb->nc.notifyCount);
args.push_back(cb->nc.notifyArg);
args.push_back(cb->nc.commonArgument);
// Clear the notify count / arg
cb->nc.notifyCount = 0;
cb->nc.notifyArg = 0;
g_inCbCount++;
return 0;
Action *action = new ActionAfterCallback(cbId);
__KernelCallAddress(thread, cb->nc.entrypoint, action, false, args);
}
void _sceKernelReturnFromCallback()
{
SceUID cbId = currentMIPS->r[MIPS_REG_CB_ID];
// Value returned by the callback function
u32 retVal = currentMIPS->r[MIPS_REG_V0];
DEBUG_LOG(HLE,"_sceKernelReturnFromCallback(cbId=%i), returned %08x", cbId, retVal);
u32 error;
Callback *cb = kernelObjects.Get<Callback>(cbId, error);
if (!cb)
{
ERROR_LOG(HLE, "_sceKernelReturnFromCallback(): INVALID CBID %i in register! we're screwed", cbId);
return;
void ActionAfterCallback::run() {
if (cbId != -1) {
u32 error;
Callback *cb = kernelObjects.Get<Callback>(cbId, error);
if (cb)
{
DEBUG_LOG(HLE, "Left callback %i - %s", cbId, cb->nc.name);
// Callbacks that don't return 0 are deleted. But should this be done here?
if (currentMIPS->r[MIPS_REG_V0] != 0 || cb->forceDelete)
{
DEBUG_LOG(HLE, "ActionAfterCallback::run(): Callback returned non-zero, gets deleted!");
kernelObjects.Destroy<Callback>(cbId);
}
}
}
currentMIPS->pc = cb->savedPC;
currentMIPS->r[MIPS_REG_RA] = cb->savedRA;
currentMIPS->r[MIPS_REG_V0] = cb->savedV0;
currentMIPS->r[MIPS_REG_V1] = cb->savedV1;
currentMIPS->r[MIPS_REG_CB_ID] = cb->savedIdRegister;
// Callbacks that don't return 0 are deleted. But should this be done here?
if (retVal != 0 || cb->forceDelete)
{
DEBUG_LOG(HLE, "_sceKernelReturnFromCallback(): Callback returned non-zero, gets deleted!");
kernelObjects.Destroy<Callback>(cbId);
}
g_inCbCount--;
// yeah! back in the real world, let's keep going. Should we process more callbacks?
}
// Check callbacks on the current thread only.
@ -1377,7 +1600,7 @@ bool __KernelCheckThreadCallbacks(Thread *thread) {
if (thread->readyCallbacks[i].size()) {
SceUID readyCallback = thread->readyCallbacks[i].front();
thread->readyCallbacks[i].pop_front();
__KernelRunCallbackOnThread(readyCallback, thread);
__KernelRunCallbackOnThread(readyCallback, thread); // makes pending
return true;
}
}
@ -1386,7 +1609,8 @@ bool __KernelCheckThreadCallbacks(Thread *thread) {
// Checks for callbacks on all threads
bool __KernelCheckCallbacks() {
SceUID currentThread = __KernelGetCurThread();
// SceUID currentThread = __KernelGetCurThread();
// currentThread->isProcessingCallbacks = true;
// do {
bool processed = false;
@ -1400,25 +1624,25 @@ bool __KernelCheckCallbacks() {
return processed;
}
u32 sceKernelCheckCallback()
{
void sceKernelCheckCallback() {
Thread *curThread = __GetCurrentThread();
// This thread can now process callbacks.
curThread->isProcessingCallbacks = true;
int callbacksProcessed = __KernelCheckThreadCallbacks(curThread) ? 1 : 0;
bool callbacksProcessed = __KernelCheckThreadCallbacks(curThread);
// Note - same thread as above - checking callbacks may switch threads.
curThread->isProcessingCallbacks = false;
if (callbacksProcessed) {
RETURN(1);
ERROR_LOG(HLE,"sceKernelCheckCallback() - processed a callback.");
__KernelExecutePendingMipsCalls();
} else {
RETURN(0);
DEBUG_LOG(HLE,"sceKernelCheckCallback() - no callbacks to process, doing nothing");
}
return callbacksProcessed;
}
bool __KernelInCallback()

View File

@ -106,8 +106,7 @@ u32 __KernelResumeThread(SceUID threadID); // can return an error value
u32 __KernelGetWaitValue(SceUID threadID, u32 &error);
void __KernelWaitCurThread(WaitType type, SceUID waitId, u32 waitValue, int timeout, bool processCallbacks);
void __KernelReSchedule(const char *reason = "no reason");
void __KernelReSchedule(bool doCallbacks, const char *reason);
// Registered callback types
enum RegisteredCallbackType {
@ -133,12 +132,12 @@ u32 __KernelNotifyCallbackType(RegisteredCallbackType type, SceUID cbId, int not
SceUID __KernelGetCurThread();
void __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();
void __KernelReturnFromThread(); // Called as HLE function
void _sceKernelReturnFromThread();
void _sceKernelIdle();
u32 __KernelCallbackReturnAddress();
u32 __KernelInterruptReturnAddress();
u32 __KernelMipsCallReturnAddress();
u32 __KernelInterruptReturnAddress(); // TODO: remove
// Internal access - used by sceSetGeCallback
u32 __KernelCreateCallback(const char *name, u32 entrypoint, u32 signalArg);
@ -148,11 +147,46 @@ void sceKernelDeleteCallback();
void sceKernelNotifyCallback();
void sceKernelCancelCallback();
void sceKernelGetCallbackCount();
void _sceKernelReturnFromCallback();
u32 sceKernelCheckCallback();
void sceKernelCheckCallback();
void sceKernelGetCallbackCount();
void sceKernelReferCallbackStatus();
void __KernelReturnFromMipsCall(); // Called as HLE function
bool __KernelInCallback();
// Should be called by (nearly) all ...CB functions.
bool __KernelCheckCallbacks();
class Thread;
void __KernelSwitchContext(Thread *target, const char *reason);
u32 __KernelResumeThreadFromWait(SceUID threadID);
bool __KernelExecutePendingMipsCalls();
// A call into game code. These can be pending on a thread.
// Similar to Callback-s (NOT CallbackInfos) in JPCSP.
class Action;
struct MipsCall {
u32 entryPoint;
u32 cbId;
u32 args[6];
int numArgs;
Action *doAfter;
u32 savedIdRegister;
u32 savedRa;
u32 savedPc;
u32 savedV0;
u32 savedV1;
bool returnVoid;
const char *tag;
};
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(Thread *thread, ThreadStatus newStatus);

View File

@ -70,5 +70,6 @@ void sceKernelSetVTimerHandler()
// Not sure why this is exposed...
void _sceKernelReturnFromTimerHandler()
{
DEBUG_LOG(HLE,"_sceKernelReturnFromTimerHandler");
}

View File

@ -53,6 +53,12 @@ void sceMpegRingbufferConstruct()
RETURN(0);
}
void sceMpegRegistStream()
{
DEBUG_LOG(HLE, "HACK sceMpegRegistStream(...)");
RETURN(0);
}
const HLEFunction sceMpeg[] =
{
{0xe1ce83a7,0,"sceMpegGetAtracAu"},
@ -62,7 +68,7 @@ const HLEFunction sceMpeg[] =
{0xc132e22f,sceMpegQueryMemSize,"sceMpegQueryMemSize"},
{0x21ff80e4,0,"sceMpegQueryStreamOffset"},
{0x611e9e11,0,"sceMpegQueryStreamSize"},
{0x42560f23,0,"sceMpegRegistStream"},
{0x42560f23,sceMpegRegistStream,"sceMpegRegistStream"},
{0x591a4aa2,0,"sceMpegUnRegistStream"},
{0x707b7629,0,"sceMpegFlushAllStream"},
{0xa780cf7e,0,"sceMpegMallocAvcEsBuf"},

View File

@ -464,6 +464,14 @@ const HLEFunction sceUtility[] =
{0xAB083EA9, 0, "sceUtilityScreenshotUpdate"},
{0xD81957B7, 0, "sceUtilityScreenshotGetStatus"},
{0x86A03A27, 0, "sceUtilityScreenshotContStart"},
{0x0D5BC6D2, 0, "sceUtilityLoadUsbModule"},
{0xF64910F0, 0, "sceUtilityUnloadUsbModule"},
{0x24AC31EB, 0, "sceUtilityGamedataInstallInitStart"},
{0x32E32DCB, 0, "sceUtilityGamedataInstallShutdownStart"},
{0x4AECD179, 0, "sceUtilityGamedataInstallUpdate"},
{0xB57E95D9, 0, "sceUtilityGamedataInstallGetStatus"},
};
void Register_sceUtility()

View File

@ -50,8 +50,8 @@ enum
MIPS_REG_FP=30,
MIPS_REG_RA=31,
// ID for callback is stored here - from JPCSP
MIPS_REG_CB_ID=MIPS_REG_S0,
// ID for mipscall "callback" is stored here - from JPCSP
MIPS_REG_CALL_ID=MIPS_REG_S0,
};
enum