Files
ppsspp/Core/HLE/sceKernelCallback.cpp
T

246 lines
5.9 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// 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/.
// Refer to http://code.google.com/p/yaupspe/source/detail?r=741987a938f87e55ac284f8c2af6d40b4fb30ffc#
#include "HLE.h"
#include "../MIPS/MIPS.h"
#include "../MIPS/MIPSCodeUtils.h"
#include "sceKernel.h"
#include "sceKernelThread.h"
#include "sceKernelMemory.h"
#include "sceKernelCallback.h"
class Callback : public KernelObject
{
public:
const char *GetName() {return name;}
const char *GetTypeName() {return "CallBack";}
void GetQuickInfo(char *ptr, int size)
{
sprintf(ptr, "thread=%i, argument= %08x",
//hackAddress,
threadId,
argument);
}
~Callback()
{
// kernelMemory.Free(hackAddress);
}
/*
void GrabAndWriteHack()
{
u32 blockSize = 8;
hackAddress = kernelMemory.Alloc(blockSize);
Memory::Write_U32(MIPS_MAKE_JAL(entrypoint), hackAddress);
Memory::Write_U32(MIPS_MAKE_NOP(), hackAddress+4);
}*/
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_CBID; }
int GetIDType() const { return SCE_KERNEL_TMID_Callback; }
SceUInt size;
char name[32];
u32 entrypoint;
SceUID threadId;
u32 argument;
u32 notifyCount;
// u32 hackAddress;
u32 savedPC;
u32 savedRA;
u32 savedV0;
u32 savedV1;
u32 savedIdRegister;
int numParameters;
u32 parameters[6];
/*
SceUInt attr;
SceUInt initPattern;
SceUInt currentPattern;
int numWaitThreads;
*/
bool forceDelete;
Action *actionAfter;
};
//////////////////////////////////////////////////////////////////////////
// CALLBACKS
//////////////////////////////////////////////////////////////////////////
// Internal API
u32 __KernelCreateCallback(const char *name, u32 entrypoint, u32 callbackArg, Action *actionAfter)
{
Callback *c = new Callback;
SceUID id = kernelObjects.Create(c);
c->size = sizeof(Callback);
strcpy(c->name, name);
c->entrypoint = entrypoint;
c->threadId = __KernelGetCurThread();
c->argument = callbackArg;
c->actionAfter = actionAfter;
return id;
}
//extern Thread *currentThread;
void sceKernelCreateCallback()
{
u32 entrypoint = PARAM(1);
u32 callbackArg = PARAM(2);
const char *name = Memory::GetCharPointer(PARAM(0));
u32 id = __KernelCreateCallback(name, entrypoint, callbackArg);
DEBUG_LOG(HLE,"%i=sceKernelCreateCallback(name=%s,entry= %08x, callbackArg = %08x)", id, name, entrypoint, callbackArg);
RETURN(id);
}
void sceKernelDeleteCallback()
{
SceUID id = PARAM(0);
DEBUG_LOG(HLE,"sceKernelDeleteCallback(%i)", id);
RETURN(kernelObjects.Destroy<Callback>(id));
}
void sceKernelNotifyCallback()
{
SceUID cbid = PARAM(0);
u32 arg = PARAM(1);
DEBUG_LOG(HLE,"sceKernelNotifyCallback(%i, %i)", cbid, arg);
__KernelNotifyCallback(__KernelGetCurThread(), cbid, arg);
RETURN(0);
}
void sceKernelCancelCallback()
{
SceUID cbid = PARAM(0);
ERROR_LOG(HLE,"UNIMPL sceKernelCancelCallback(%i)", cbid);
//__KernelCancelCallback(__KernelGetCurThread(), cbid);
RETURN(0);
}
void sceKernelGetCallbackCount()
{
SceUID cbid = PARAM(0);
ERROR_LOG(HLE,"UNIMPL sceKernelGetCallbackCount(%i)", cbid);
RETURN(0);
}
void sceKernelReferCallbackStatus()
{
SceUID cbid = PARAM(0);
u32 statusAddr = PARAM(1);
ERROR_LOG(HLE,"UNIMPL sceKernelReferCallbackStatus(%i, %08x)", cbid, statusAddr);
RETURN(0);
}
void __KernelCallCallback(SceUID id)
{
//First, make sure we're on the right thread
u32 error;
Callback *c = kernelObjects.Get<Callback>(id, error);
if (c)
{
if (c->threadId == __KernelGetCurThread())
{
//Alright, we're on the right thread
// Save the few regs that need saving
c->savedPC = currentMIPS->pc;
c->savedRA = currentMIPS->r[MIPS_REG_RA];
c->savedV0 = currentMIPS->r[MIPS_REG_V0];
c->savedV1 = currentMIPS->r[MIPS_REG_V1];
c->savedIdRegister = currentMIPS->r[MIPS_REG_CB_ID];
// Set up the new state
for (int i = 0; i < c->numParameters; c++)
currentMIPS->r[4 + i] = c->parameters[i];
currentMIPS->pc = c->entrypoint;
currentMIPS->r[MIPS_REG_RA] = __KernelCallbackReturnAddress();
}
}
else
{
//ARGH!
}
}
void _sceKernelReturnFromCallback()
{
SceUID cbid = currentMIPS->r[MIPS_REG_CB_ID]; // yeah!
DEBUG_LOG(HLE,"_sceKernelReturnFromCallback(cbid=%i)", cbid);
// Value returned by the callback function
u32 retVal = currentMIPS->r[MIPS_REG_V0];
u32 error;
Callback *cb = kernelObjects.Get<Callback>(cbid, error);
if (!cb)
{
ERROR_LOG(HLE, "_sceKernelReturnFromCallback(): INVALID CBID in register! we're screwed");
return;
}
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);
}
if (cb->actionAfter)
{
cb->actionAfter->run();
}
// yeah! back in the real world, let's keep going....
}
void sceKernelCheckCallback()
{
//only check those of current thread
ERROR_LOG(HLE,"UNIMPL sceKernelCheckCallback()");
// HACK that makes the audio thread work in Puyo Puyo Fever - the main thread never yields!
// Probably because callbacks aren't working right.
__KernelReSchedule("checkcallbackhack");
RETURN(0);
}