1 // Copyright (c) 2012- PPSSPP Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
19 #include "Core/CoreTiming.h"
20 #include "Core/MemMapHelpers.h"
21 #include "Core/Reporting.h"
22 #include "Core/HLE/sceKernel.h"
23 #include "Core/HLE/sceKernelInterrupt.h"
24 #include "Core/HLE/sceKernelMemory.h"
25 #include "Core/HLE/sceKernelVTimer.h"
26 #include "Core/HLE/HLE.h"
27 #include "Common/ChunkFile.h"
29 static int vtimerTimer
= -1;
30 static SceUID runningVTimer
= 0;
31 static std::list
<SceUID
> vtimers
;
35 char name
[KERNELOBJECT_MAX_NAME_LENGTH
+1];
44 struct VTimer
: public KernelObject
{
45 const char *GetName() override
{ return nvt
.name
; }
46 const char *GetTypeName() override
{ return "VTimer"; }
47 static u32
GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_VTID
; }
48 static int GetStaticIDType() { return SCE_KERNEL_TMID_VTimer
; }
49 int GetIDType() const override
{ return SCE_KERNEL_TMID_VTimer
; }
51 void DoState(PointerWrap
&p
) override
{
52 auto s
= p
.Section("VTimer", 1, 2);
66 KernelObject
*__KernelVTimerObject() {
70 static u64
__getVTimerRunningTime(VTimer
*vt
) {
71 if (vt
->nvt
.active
== 0)
74 return CoreTiming::GetGlobalTimeUs() - vt
->nvt
.base
;
77 static u64
__getVTimerCurrentTime(VTimer
* vt
) {
78 return vt
->nvt
.current
+ __getVTimerRunningTime(vt
);
81 static int __KernelCancelVTimer(SceUID id
) {
83 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(id
, error
);
88 CoreTiming::UnscheduleEvent(vtimerTimer
, id
);
89 vt
->nvt
.handlerAddr
= 0;
93 static void __KernelScheduleVTimer(VTimer
*vt
, u64 schedule
) {
94 CoreTiming::UnscheduleEvent(vtimerTimer
, vt
->GetUID());
96 vt
->nvt
.schedule
= schedule
;
98 if (vt
->nvt
.active
== 1 && vt
->nvt
.handlerAddr
!= 0) {
99 // The "real" base is base + current. But when setting the time, base is important.
100 // The schedule is relative to those.
101 u64 cyclesIntoFuture
;
102 if (schedule
< 250) {
105 s64 goalUs
= (u64
)vt
->nvt
.base
+ schedule
- (u64
)vt
->nvt
.current
;
106 s64 minGoalUs
= CoreTiming::GetGlobalTimeUs() + 250;
107 if (goalUs
< minGoalUs
) {
108 cyclesIntoFuture
= usToCycles(250);
110 cyclesIntoFuture
= usToCycles(goalUs
- CoreTiming::GetGlobalTimeUs());
113 CoreTiming::ScheduleEvent(cyclesIntoFuture
, vtimerTimer
, vt
->GetUID());
117 static void __rescheduleVTimer(SceUID id
, u32 delay
) {
119 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(id
, error
);
124 __KernelScheduleVTimer(vt
, vt
->nvt
.schedule
+ delay
);
127 class VTimerIntrHandler
: public IntrHandler
129 static const int HANDLER_STACK_SPACE
= 48;
132 VTimerIntrHandler() : IntrHandler(PSP_SYSTIMER1_INTR
) {}
134 bool run(PendingInterrupt
&pend
) override
{
136 SceUID vtimerID
= vtimers
.front();
138 VTimer
*vtimer
= kernelObjects
.Get
<VTimer
>(vtimerID
, error
);
143 // Reserve some stack space for arguments.
144 u32 argArea
= currentMIPS
->r
[MIPS_REG_SP
];
145 currentMIPS
->r
[MIPS_REG_SP
] -= HANDLER_STACK_SPACE
;
147 Memory::Write_U64(vtimer
->nvt
.schedule
, argArea
- 16);
148 Memory::Write_U64(__getVTimerCurrentTime(vtimer
), argArea
- 8);
150 currentMIPS
->pc
= vtimer
->nvt
.handlerAddr
;
151 currentMIPS
->r
[MIPS_REG_A0
] = vtimer
->GetUID();
152 currentMIPS
->r
[MIPS_REG_A1
] = argArea
- 16;
153 currentMIPS
->r
[MIPS_REG_A2
] = argArea
- 8;
154 currentMIPS
->r
[MIPS_REG_A3
] = vtimer
->nvt
.commonAddr
;
156 runningVTimer
= vtimerID
;
161 void handleResult(PendingInterrupt
&pend
) override
{
162 u32 result
= currentMIPS
->r
[MIPS_REG_V0
];
164 currentMIPS
->r
[MIPS_REG_SP
] += HANDLER_STACK_SPACE
;
166 int vtimerID
= vtimers
.front();
172 __KernelCancelVTimer(vtimerID
);
174 __rescheduleVTimer(vtimerID
, result
);
178 static void __KernelTriggerVTimer(u64 userdata
, int cyclesLate
) {
179 SceUID uid
= (SceUID
) userdata
;
182 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(uid
, error
);
184 vtimers
.push_back(uid
);
185 __TriggerInterrupt(PSP_INTR_IMMEDIATE
, PSP_SYSTIMER1_INTR
);
189 void __KernelVTimerDoState(PointerWrap
&p
) {
190 auto s
= p
.Section("sceKernelVTimer", 1, 2);
196 CoreTiming::RestoreRegisterEvent(vtimerTimer
, "VTimer", __KernelTriggerVTimer
);
204 void __KernelVTimerInit() {
206 __RegisterIntrHandler(PSP_SYSTIMER1_INTR
, new VTimerIntrHandler());
207 vtimerTimer
= CoreTiming::RegisterEvent("VTimer", __KernelTriggerVTimer
);
209 // Intentionally starts at 0. This explains the behavior where 0 is treated differently outside a timer.
213 u32
sceKernelCreateVTimer(const char *name
, u32 optParamAddr
) {
215 WARN_LOG_REPORT(SCEKERNEL
, "%08x=sceKernelCreateVTimer(): invalid name", SCE_KERNEL_ERROR_ERROR
);
216 return SCE_KERNEL_ERROR_ERROR
;
218 DEBUG_LOG(SCEKERNEL
, "sceKernelCreateVTimer(%s, %08x)", name
, optParamAddr
);
220 VTimer
*vtimer
= new VTimer
;
221 SceUID id
= kernelObjects
.Create(vtimer
);
223 memset(&vtimer
->nvt
, 0, sizeof(NativeVTimer
));
224 vtimer
->nvt
.size
= sizeof(NativeVTimer
);
225 strncpy(vtimer
->nvt
.name
, name
, KERNELOBJECT_MAX_NAME_LENGTH
);
226 vtimer
->nvt
.name
[KERNELOBJECT_MAX_NAME_LENGTH
] = '\0';
228 if (optParamAddr
!= 0) {
229 u32 size
= Memory::Read_U32(optParamAddr
);
231 WARN_LOG_REPORT(SCEKERNEL
, "sceKernelCreateVTimer(%s) unsupported options parameter, size = %d", name
, size
);
237 u32
sceKernelDeleteVTimer(SceUID uid
) {
238 DEBUG_LOG(SCEKERNEL
, "sceKernelDeleteVTimer(%08x)", uid
);
241 VTimer
* vt
= kernelObjects
.Get
<VTimer
>(uid
, error
);
244 WARN_LOG(SCEKERNEL
, "%08x=sceKernelDeleteVTimer(%08x)", error
, uid
);
248 for (std::list
<SceUID
>::iterator it
= vtimers
.begin(); it
!= vtimers
.end(); ++it
) {
249 if (*it
== vt
->GetUID()) {
255 return kernelObjects
.Destroy
<VTimer
>(uid
);
258 u32
sceKernelGetVTimerBase(SceUID uid
, u32 baseClockAddr
) {
259 DEBUG_LOG(SCEKERNEL
, "sceKernelGetVTimerBase(%08x, %08x)", uid
, baseClockAddr
);
262 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(uid
, error
);
265 WARN_LOG(SCEKERNEL
, "%08x=sceKernelGetVTimerBase(%08x, %08x)", error
, uid
, baseClockAddr
);
269 if (Memory::IsValidAddress(baseClockAddr
))
270 Memory::Write_U64(vt
->nvt
.base
, baseClockAddr
);
275 u64
sceKernelGetVTimerBaseWide(SceUID uid
) {
276 DEBUG_LOG(SCEKERNEL
, "sceKernelGetVTimerBaseWide(%08x)", uid
);
279 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(uid
, error
);
282 WARN_LOG(SCEKERNEL
, "%08x=sceKernelGetVTimerBaseWide(%08x)", error
, uid
);
289 u32
sceKernelGetVTimerTime(SceUID uid
, u32 timeClockAddr
) {
290 DEBUG_LOG(SCEKERNEL
, "sceKernelGetVTimerTime(%08x, %08x)", uid
, timeClockAddr
);
293 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(uid
, error
);
296 WARN_LOG(SCEKERNEL
, "%08x=sceKernelGetVTimerTime(%08x, %08x)", error
, uid
, timeClockAddr
);
300 u64 time
= __getVTimerCurrentTime(vt
);
301 if (Memory::IsValidAddress(timeClockAddr
))
302 Memory::Write_U64(time
, timeClockAddr
);
307 u64
sceKernelGetVTimerTimeWide(SceUID uid
) {
308 DEBUG_LOG(SCEKERNEL
, "sceKernelGetVTimerTimeWide(%08x)", uid
);
311 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(uid
, error
);
314 WARN_LOG(SCEKERNEL
, "%08x=sceKernelGetVTimerTimeWide(%08x)", error
, uid
);
318 u64 time
= __getVTimerCurrentTime(vt
);
322 static u64
__KernelSetVTimer(VTimer
*vt
, u64 time
) {
323 u64 current
= __getVTimerCurrentTime(vt
);
324 vt
->nvt
.current
= time
- __getVTimerRunningTime(vt
);
326 // Run if we're now passed the schedule.
327 __KernelScheduleVTimer(vt
, vt
->nvt
.schedule
);
332 u32
sceKernelSetVTimerTime(SceUID uid
, u32 timeClockAddr
) {
333 DEBUG_LOG(SCEKERNEL
, "sceKernelSetVTimerTime(%08x, %08x)", uid
, timeClockAddr
);
336 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(uid
, error
);
339 WARN_LOG(SCEKERNEL
, "%08x=sceKernelSetVTimerTime(%08x, %08x)", error
, uid
, timeClockAddr
);
343 u64 time
= Memory::Read_U64(timeClockAddr
);
344 if (Memory::IsValidAddress(timeClockAddr
))
345 Memory::Write_U64(__KernelSetVTimer(vt
, time
), timeClockAddr
);
350 u64
sceKernelSetVTimerTimeWide(SceUID uid
, u64 timeClock
) {
351 if (__IsInInterrupt()) {
352 WARN_LOG(SCEKERNEL
, "sceKernelSetVTimerTimeWide(%08x, %llu): in interrupt", uid
, timeClock
);
355 DEBUG_LOG(SCEKERNEL
, "sceKernelSetVTimerTimeWide(%08x, %llu)", uid
, timeClock
);
358 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(uid
, error
);
360 if (error
|| vt
== NULL
) {
361 WARN_LOG(SCEKERNEL
, "%08x=sceKernelSetVTimerTimeWide(%08x, %llu)", error
, uid
, timeClock
);
365 return __KernelSetVTimer(vt
, timeClock
);
368 static void __startVTimer(VTimer
*vt
) {
370 vt
->nvt
.base
= CoreTiming::GetGlobalTimeUs();
372 if (vt
->nvt
.handlerAddr
!= 0)
373 __KernelScheduleVTimer(vt
, vt
->nvt
.schedule
);
376 u32
sceKernelStartVTimer(SceUID uid
) {
379 if (uid
== runningVTimer
) {
380 WARN_LOG(SCEKERNEL
, "sceKernelStartVTimer(%08x): invalid vtimer", uid
);
381 return SCE_KERNEL_ERROR_ILLEGAL_VTID
;
384 DEBUG_LOG(SCEKERNEL
, "sceKernelStartVTimer(%08x)", uid
);
387 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(uid
, error
);
390 if (vt
->nvt
.active
!= 0)
400 static void __stopVTimer(VTimer
*vt
) {
401 // This increases (__getVTimerCurrentTime includes nvt.current.)
402 vt
->nvt
.current
= __getVTimerCurrentTime(vt
);
407 u32
sceKernelStopVTimer(SceUID uid
) {
408 if (uid
== runningVTimer
) {
409 WARN_LOG(SCEKERNEL
, "sceKernelStopVTimer(%08x): invalid vtimer", uid
);
410 return SCE_KERNEL_ERROR_ILLEGAL_VTID
;
412 DEBUG_LOG(SCEKERNEL
, "sceKernelStopVTimer(%08x)", uid
);
415 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(uid
, error
);
418 if (vt
->nvt
.active
== 0)
428 u32
sceKernelSetVTimerHandler(SceUID uid
, u32 scheduleAddr
, u32 handlerFuncAddr
, u32 commonAddr
) {
430 if (uid
== runningVTimer
) {
431 WARN_LOG(SCEKERNEL
, "sceKernelSetVTimerHandler(%08x, %08x, %08x, %08x): invalid vtimer", uid
, scheduleAddr
, handlerFuncAddr
, commonAddr
);
432 return SCE_KERNEL_ERROR_ILLEGAL_VTID
;
436 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(uid
, error
);
439 WARN_LOG(SCEKERNEL
, "%08x=sceKernelSetVTimerHandler(%08x, %08x, %08x, %08x)", error
, uid
, scheduleAddr
, handlerFuncAddr
, commonAddr
);
443 DEBUG_LOG(SCEKERNEL
, "sceKernelSetVTimerHandler(%08x, %08x, %08x, %08x)", uid
, scheduleAddr
, handlerFuncAddr
, commonAddr
);
446 u64 schedule
= Memory::Read_U64(scheduleAddr
);
447 vt
->nvt
.handlerAddr
= handlerFuncAddr
;
448 if (handlerFuncAddr
) {
449 vt
->nvt
.commonAddr
= commonAddr
;
450 __KernelScheduleVTimer(vt
, schedule
);
452 __KernelScheduleVTimer(vt
, vt
->nvt
.schedule
);
458 u32
sceKernelSetVTimerHandlerWide(SceUID uid
, u64 schedule
, u32 handlerFuncAddr
, u32 commonAddr
) {
460 if (uid
== runningVTimer
) {
461 WARN_LOG(SCEKERNEL
, "sceKernelSetVTimerHandlerWide(%08x, %llu, %08x, %08x): invalid vtimer", uid
, schedule
, handlerFuncAddr
, commonAddr
);
462 return SCE_KERNEL_ERROR_ILLEGAL_VTID
;
466 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(uid
, error
);
469 WARN_LOG(SCEKERNEL
, "%08x=sceKernelSetVTimerHandlerWide(%08x, %llu, %08x, %08x)", error
, uid
, schedule
, handlerFuncAddr
, commonAddr
);
473 DEBUG_LOG(SCEKERNEL
, "sceKernelSetVTimerHandlerWide(%08x, %llu, %08x, %08x)", uid
, schedule
, handlerFuncAddr
, commonAddr
);
475 vt
->nvt
.handlerAddr
= handlerFuncAddr
;
476 if (handlerFuncAddr
) {
477 vt
->nvt
.commonAddr
= commonAddr
;
478 __KernelScheduleVTimer(vt
, schedule
);
480 __KernelScheduleVTimer(vt
, vt
->nvt
.schedule
);
486 u32
sceKernelCancelVTimerHandler(SceUID uid
) {
487 if (uid
== runningVTimer
) {
488 WARN_LOG(SCEKERNEL
, "sceKernelCancelVTimerHandler(%08x): invalid vtimer", uid
);
489 return SCE_KERNEL_ERROR_ILLEGAL_VTID
;
492 DEBUG_LOG(SCEKERNEL
, "sceKernelCancelVTimerHandler(%08x)", uid
);
494 //__cancelVTimer checks if uid is valid
495 return __KernelCancelVTimer(uid
);
498 u32
sceKernelReferVTimerStatus(SceUID uid
, u32 statusAddr
) {
499 DEBUG_LOG(SCEKERNEL
, "sceKernelReferVTimerStatus(%08x, %08x)", uid
, statusAddr
);
502 VTimer
*vt
= kernelObjects
.Get
<VTimer
>(uid
, error
);
505 WARN_LOG(SCEKERNEL
, "%08x=sceKernelReferVTimerStatus(%08x, %08x)", error
, uid
, statusAddr
);
509 if (Memory::IsValidAddress(statusAddr
)) {
510 NativeVTimer status
= vt
->nvt
;
511 u32 size
= Memory::Read_U32(statusAddr
);
512 status
.current
= __getVTimerCurrentTime(vt
);
513 Memory::Memcpy(statusAddr
, &status
, std::min(size
, (u32
)sizeof(status
)));
519 // Not sure why this is exposed...
520 void _sceKernelReturnFromTimerHandler() {
521 ERROR_LOG_REPORT(SCEKERNEL
,"_sceKernelReturnFromTimerHandler - should not be called!");