Files
ppsspp/Core/HLE/sceKernelVTimer.cpp
T
Unknown W. Brackets 7edebd1c2f Put vtimer args on the stack, skip allocation.
This is roughly where they are on the PSP based on tests, and it avoids
having to manage memory or etc.
2013-10-22 07:45:47 -07:00

469 lines
12 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 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/.
#include "Core/CoreTiming.h"
#include "Core/Reporting.h"
#include "sceKernel.h"
#include "sceKernelInterrupt.h"
#include "sceKernelMemory.h"
#include "sceKernelVTimer.h"
#include "HLE.h"
#include "ChunkFile.h"
static int vtimerTimer = -1;
static std::list<SceUID> vtimers;
struct NativeVTimer {
SceSize_le size;
char name[KERNELOBJECT_MAX_NAME_LENGTH+1];
s32_le active;
u64_le base;
u64_le current;
u64_le schedule;
u32_le handlerAddr;
u32_le commonAddr;
};
struct VTimer : public KernelObject {
const char *GetName() {return nvt.name;}
const char *GetTypeName() {return "VTimer";}
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_VTID; }
static int GetStaticIDType() { return SCE_KERNEL_TMID_VTimer; }
int GetIDType() const { return SCE_KERNEL_TMID_VTimer; }
virtual void DoState(PointerWrap &p) {
auto s = p.Section("VTimer", 1, 2);
if (!s)
return;
p.Do(nvt);
if (s < 2) {
u32 memoryPtr;
p.Do(memoryPtr);
}
}
NativeVTimer nvt;
};
KernelObject *__KernelVTimerObject() {
return new VTimer;
}
u64 __getVTimerRunningTime(VTimer *vt) {
if (vt->nvt.active == 0)
return 0;
return cyclesToUs(CoreTiming::GetTicks()) - vt->nvt.base;
}
u64 __getVTimerCurrentTime(VTimer* vt) {
return vt->nvt.current + __getVTimerRunningTime(vt);
}
void __cancelVTimer(SceUID id) {
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(id, error);
if (error)
return;
CoreTiming::UnscheduleEvent(vtimerTimer, id);
vt->nvt.schedule = 0;
vt->nvt.handlerAddr = 0;
vt->nvt.commonAddr = 0;
}
void __KernelScheduleVTimer(VTimer *vt, u64 schedule) {
CoreTiming::UnscheduleEvent(vtimerTimer, vt->GetUID());
vt->nvt.schedule = schedule;
if (vt->nvt.active == 1 && vt->nvt.handlerAddr != 0)
CoreTiming::ScheduleEvent(usToCycles((u64)vt->nvt.schedule), vtimerTimer, vt->GetUID());
}
void __rescheduleVTimer(SceUID id, int delay) {
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(id, error);
if (error)
return;
if (delay < 0)
delay = 100;
__KernelScheduleVTimer(vt, delay);
}
class VTimerIntrHandler : public IntrHandler
{
static const int HANDLER_STACK_SPACE = 48;
public:
VTimerIntrHandler() : IntrHandler(PSP_SYSTIMER1_INTR) {}
virtual bool run(PendingInterrupt &pend) {
u32 error;
SceUID vtimerID = vtimers.front();
VTimer *vtimer = kernelObjects.Get<VTimer>(vtimerID, error);
if (error)
return false;
// Reserve some stack space for arguments.
u32 argArea = currentMIPS->r[MIPS_REG_SP];
currentMIPS->r[MIPS_REG_SP] -= HANDLER_STACK_SPACE;
Memory::Write_U64(vtimer->nvt.schedule, argArea - 16);
Memory::Write_U64(__getVTimerCurrentTime(vtimer), argArea - 8);
currentMIPS->pc = vtimer->nvt.handlerAddr;
currentMIPS->r[MIPS_REG_A0] = vtimer->GetUID();
currentMIPS->r[MIPS_REG_A1] = argArea - 16;
currentMIPS->r[MIPS_REG_A2] = argArea - 8;
currentMIPS->r[MIPS_REG_A3] = vtimer->nvt.commonAddr;
return true;
}
virtual void handleResult(PendingInterrupt &pend) {
int result = currentMIPS->r[MIPS_REG_V0];
currentMIPS->r[MIPS_REG_SP] += HANDLER_STACK_SPACE;
int vtimerID = vtimers.front();
vtimers.pop_front();
if (result == 0)
__cancelVTimer(vtimerID);
else
__rescheduleVTimer(vtimerID, result);
}
};
void __KernelTriggerVTimer(u64 userdata, int cyclesLate) {
SceUID uid = (SceUID) userdata;
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
if (vt) {
vtimers.push_back(uid);
__TriggerInterrupt(PSP_INTR_IMMEDIATE, PSP_SYSTIMER1_INTR);
}
}
void __KernelVTimerDoState(PointerWrap &p) {
auto s = p.Section("sceKernelVTimer", 1);
if (!s)
return;
p.Do(vtimerTimer);
p.Do(vtimers);
CoreTiming::RestoreRegisterEvent(vtimerTimer, "VTimer", __KernelTriggerVTimer);
}
void __KernelVTimerInit() {
vtimers.clear();
__RegisterIntrHandler(PSP_SYSTIMER1_INTR, new VTimerIntrHandler());
vtimerTimer = CoreTiming::RegisterEvent("VTimer", __KernelTriggerVTimer);
}
u32 sceKernelCreateVTimer(const char *name, u32 optParamAddr) {
if (!name) {
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateVTimer(): invalid name", SCE_KERNEL_ERROR_ERROR);
return SCE_KERNEL_ERROR_ERROR;
}
DEBUG_LOG(SCEKERNEL, "sceKernelCreateVTimer(%s, %08x)", name, optParamAddr);
VTimer *vtimer = new VTimer;
SceUID id = kernelObjects.Create(vtimer);
memset(&vtimer->nvt, 0, sizeof(NativeVTimer));
vtimer->nvt.size = sizeof(NativeVTimer);
strncpy(vtimer->nvt.name, name, KERNELOBJECT_MAX_NAME_LENGTH);
vtimer->nvt.name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0';
if (optParamAddr != 0) {
u32 size = Memory::Read_U32(optParamAddr);
if (size > 4)
WARN_LOG_REPORT(SCEKERNEL, "sceKernelCreateVTimer(%s) unsupported options parameter, size = %d", name, size);
}
return id;
}
u32 sceKernelDeleteVTimer(u32 uid) {
DEBUG_LOG(SCEKERNEL, "sceKernelDeleteVTimer(%08x)", uid);
u32 error;
VTimer* vt = kernelObjects.Get<VTimer>(uid, error);
if (error) {
WARN_LOG(SCEKERNEL, "%08x=sceKernelDeleteVTimer(%08x)", error, uid);
return error;
}
for (std::list<SceUID>::iterator it = vtimers.begin(); it != vtimers.end(); ++it) {
if (*it == vt->GetUID()) {
vtimers.erase(it);
break;
}
}
return kernelObjects.Destroy<VTimer>(uid);
}
u32 sceKernelGetVTimerBase(u32 uid, u32 baseClockAddr) {
DEBUG_LOG(SCEKERNEL, "sceKernelGetVTimerBase(%08x, %08x)", uid, baseClockAddr);
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
if (error) {
WARN_LOG(SCEKERNEL, "%08x=sceKernelGetVTimerBase(%08x, %08x)", error, uid, baseClockAddr);
return error;
}
if (Memory::IsValidAddress(baseClockAddr))
Memory::Write_U64(vt->nvt.base, baseClockAddr);
return 0;
}
u64 sceKernelGetVTimerBaseWide(u32 uid) {
DEBUG_LOG(SCEKERNEL, "sceKernelGetVTimerBaseWide(%08x)", uid);
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
if (error) {
WARN_LOG(SCEKERNEL, "%08x=sceKernelGetVTimerBaseWide(%08x)", error, uid);
return error;
}
return vt->nvt.base;
}
u32 sceKernelGetVTimerTime(u32 uid, u32 timeClockAddr) {
DEBUG_LOG(SCEKERNEL, "sceKernelGetVTimerTime(%08x, %08x)", uid, timeClockAddr);
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
if (error) {
WARN_LOG(SCEKERNEL, "%08x=sceKernelGetVTimerTime(%08x, %08x)", error, uid, timeClockAddr);
return error;
}
u64 time = __getVTimerCurrentTime(vt);
if (Memory::IsValidAddress(timeClockAddr))
Memory::Write_U64(time, timeClockAddr);
return 0;
}
u64 sceKernelGetVTimerTimeWide(u32 uid) {
DEBUG_LOG(SCEKERNEL, "sceKernelGetVTimerTimeWide(%08x)", uid);
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
if (error) {
WARN_LOG(SCEKERNEL, "%08x=sceKernelGetVTimerTimeWide(%08x)", error, uid);
return error;
}
u64 time = __getVTimerCurrentTime(vt);
return time;
}
u64 __setVTimer(VTimer *vt, u64 time) {
u64 current = __getVTimerCurrentTime(vt);
vt->nvt.base = vt->nvt.base + __getVTimerCurrentTime(vt) - time;
vt->nvt.current = 0;
return current;
}
u32 sceKernelSetVTimerTime(u32 uid, u32 timeClockAddr) {
DEBUG_LOG(SCEKERNEL, "sceKernelSetVTimerTime(%08x, %08x)", uid, timeClockAddr);
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
if (error) {
WARN_LOG(SCEKERNEL, "%08x=sceKernelSetVTimerTime(%08x, %08x)", error, uid, timeClockAddr);
return error;
}
u64 time = Memory::Read_U64(timeClockAddr);
if (Memory::IsValidAddress(timeClockAddr))
Memory::Write_U64(__setVTimer(vt, time), timeClockAddr);
return 0;
}
u32 sceKernelSetVTimerTimeWide(u32 uid, u64 timeClock) {
DEBUG_LOG(SCEKERNEL, "sceKernelSetVTimerTimeWide(%08x, %llu", uid, timeClock);
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
if (error) {
WARN_LOG(SCEKERNEL, "%08x=sceKernelSetVTimerTimeWide(%08x, %llu)", error, uid, timeClock);
return error;
}
if (vt == NULL) {
return -1;
}
return __setVTimer(vt, timeClock);
}
void __startVTimer(VTimer *vt) {
vt->nvt.active = 1;
vt->nvt.base = cyclesToUs(CoreTiming::GetTicks());
if (vt->nvt.handlerAddr != 0)
__KernelScheduleVTimer(vt, vt->nvt.schedule);
}
u32 sceKernelStartVTimer(u32 uid) {
DEBUG_LOG(SCEKERNEL, "sceKernelStartVTimer(%08x)", uid);
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
if (vt) {
if (vt->nvt.active != 0)
return 1;
__startVTimer(vt);
return 0;
}
return error;
}
void __stopVTimer(VTimer *vt) {
vt->nvt.current += __getVTimerCurrentTime(vt);
vt->nvt.active = 0;
vt->nvt.base = 0;
}
u32 sceKernelStopVTimer(u32 uid) {
DEBUG_LOG(SCEKERNEL, "sceKernelStopVTimer(%08x)", uid);
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
if (vt) {
if (vt->nvt.active == 0)
return 0;
__stopVTimer(vt);
return 1;
}
return error;
}
u32 sceKernelSetVTimerHandler(u32 uid, u32 scheduleAddr, u32 handlerFuncAddr, u32 commonAddr) {
if (uid == 0) {
WARN_LOG(SCEKERNEL, "sceKernelSetVTimerHandler(%08x, %08x, %08x, %08x): invalid vtimer", uid, scheduleAddr, handlerFuncAddr, commonAddr);
return SCE_KERNEL_ERROR_ILLEGAL_VTID;
}
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
if (error) {
WARN_LOG(SCEKERNEL, "%08x=sceKernelSetVTimerHandler(%08x, %08x, %08x, %08x)", error, uid, scheduleAddr, handlerFuncAddr, commonAddr);
return error;
}
DEBUG_LOG(SCEKERNEL, "sceKernelSetVTimerHandler(%08x, %08x, %08x, %08x)", uid, scheduleAddr, handlerFuncAddr, commonAddr);
u64 schedule = Memory::Read_U64(scheduleAddr);
vt->nvt.handlerAddr = handlerFuncAddr;
if (handlerFuncAddr)
vt->nvt.commonAddr = commonAddr;
__KernelScheduleVTimer(vt, schedule);
return 0;
}
u32 sceKernelSetVTimerHandlerWide(u32 uid, u64 schedule, u32 handlerFuncAddr, u32 commonAddr) {
if (uid == 0) {
WARN_LOG(SCEKERNEL, "sceKernelSetVTimerHandlerWide(%08x, %llu, %08x, %08x): invalid vtimer", uid, schedule, handlerFuncAddr, commonAddr);
return SCE_KERNEL_ERROR_ILLEGAL_VTID;
}
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
if (error) {
WARN_LOG(SCEKERNEL, "%08x=sceKernelSetVTimerHandlerWide(%08x, %llu, %08x, %08x)", error, uid, schedule, handlerFuncAddr, commonAddr);
return error;
}
DEBUG_LOG(SCEKERNEL, "sceKernelSetVTimerHandlerWide(%08x, %llu, %08x, %08x)", uid, schedule, handlerFuncAddr, commonAddr);
vt->nvt.handlerAddr = handlerFuncAddr;
if (handlerFuncAddr)
vt->nvt.commonAddr = commonAddr;
__KernelScheduleVTimer(vt, schedule);
return 0;
}
u32 sceKernelCancelVTimerHandler(u32 uid) {
DEBUG_LOG(SCEKERNEL, "sceKernelCancelVTimerHandler(%08x)", uid);
//__cancelVTimer checks if uid is valid
__cancelVTimer(uid);
return 0;
}
u32 sceKernelReferVTimerStatus(u32 uid, u32 statusAddr) {
DEBUG_LOG(SCEKERNEL, "sceKernelReferVTimerStatus(%08x, %08x)", uid, statusAddr);
u32 error;
VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
if (error) {
WARN_LOG(SCEKERNEL, "%08x=sceKernelReferVTimerStatus(%08x, %08x)", error, uid, statusAddr);
return error;
}
if (Memory::IsValidAddress(statusAddr))
Memory::WriteStruct(statusAddr, &vt->nvt);
return 0;
}
// Not sure why this is exposed...
void _sceKernelReturnFromTimerHandler() {
ERROR_LOG_REPORT(SCEKERNEL,"_sceKernelReturnFromTimerHandler - should not be called!");
}