Merge pull request #90 from gizmo98/patch-2
[libretro-ppsspp.git] / Core / HLE / sceKernelVTimer.cpp
blob276f5db13d18bda3ef1eb50f4863d37f3710fcef
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/.
18 #include <algorithm>
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;
33 struct NativeVTimer {
34 SceSize_le size;
35 char name[KERNELOBJECT_MAX_NAME_LENGTH+1];
36 s32_le active;
37 u64_le base;
38 u64_le current;
39 u64_le schedule;
40 u32_le handlerAddr;
41 u32_le commonAddr;
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);
53 if (!s)
54 return;
56 p.Do(nvt);
57 if (s < 2) {
58 u32 memoryPtr;
59 p.Do(memoryPtr);
63 NativeVTimer nvt;
66 KernelObject *__KernelVTimerObject() {
67 return new VTimer;
70 static u64 __getVTimerRunningTime(VTimer *vt) {
71 if (vt->nvt.active == 0)
72 return 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) {
82 u32 error;
83 VTimer *vt = kernelObjects.Get<VTimer>(id, error);
85 if (!vt)
86 return error;
88 CoreTiming::UnscheduleEvent(vtimerTimer, id);
89 vt->nvt.handlerAddr = 0;
90 return 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) {
103 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);
109 } else {
110 cyclesIntoFuture = usToCycles(goalUs - CoreTiming::GetGlobalTimeUs());
113 CoreTiming::ScheduleEvent(cyclesIntoFuture, vtimerTimer, vt->GetUID());
117 static void __rescheduleVTimer(SceUID id, u32 delay) {
118 u32 error;
119 VTimer *vt = kernelObjects.Get<VTimer>(id, error);
121 if (error)
122 return;
124 __KernelScheduleVTimer(vt, vt->nvt.schedule + delay);
127 class VTimerIntrHandler : public IntrHandler
129 static const int HANDLER_STACK_SPACE = 48;
131 public:
132 VTimerIntrHandler() : IntrHandler(PSP_SYSTIMER1_INTR) {}
134 bool run(PendingInterrupt &pend) override {
135 u32 error;
136 SceUID vtimerID = vtimers.front();
138 VTimer *vtimer = kernelObjects.Get<VTimer>(vtimerID, error);
140 if (error)
141 return false;
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;
158 return true;
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();
167 vtimers.pop_front();
169 runningVTimer = 0;
171 if (result == 0)
172 __KernelCancelVTimer(vtimerID);
173 else
174 __rescheduleVTimer(vtimerID, result);
178 static void __KernelTriggerVTimer(u64 userdata, int cyclesLate) {
179 SceUID uid = (SceUID) userdata;
181 u32 error;
182 VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
183 if (vt) {
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);
191 if (!s)
192 return;
194 p.Do(vtimerTimer);
195 p.Do(vtimers);
196 CoreTiming::RestoreRegisterEvent(vtimerTimer, "VTimer", __KernelTriggerVTimer);
198 if (s >= 2)
199 p.Do(runningVTimer);
200 else
201 runningVTimer = 0;
204 void __KernelVTimerInit() {
205 vtimers.clear();
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.
210 runningVTimer = 0;
213 u32 sceKernelCreateVTimer(const char *name, u32 optParamAddr) {
214 if (!name) {
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);
230 if (size > 4)
231 WARN_LOG_REPORT(SCEKERNEL, "sceKernelCreateVTimer(%s) unsupported options parameter, size = %d", name, size);
234 return id;
237 u32 sceKernelDeleteVTimer(SceUID uid) {
238 DEBUG_LOG(SCEKERNEL, "sceKernelDeleteVTimer(%08x)", uid);
240 u32 error;
241 VTimer* vt = kernelObjects.Get<VTimer>(uid, error);
243 if (error) {
244 WARN_LOG(SCEKERNEL, "%08x=sceKernelDeleteVTimer(%08x)", error, uid);
245 return error;
248 for (std::list<SceUID>::iterator it = vtimers.begin(); it != vtimers.end(); ++it) {
249 if (*it == vt->GetUID()) {
250 vtimers.erase(it);
251 break;
255 return kernelObjects.Destroy<VTimer>(uid);
258 u32 sceKernelGetVTimerBase(SceUID uid, u32 baseClockAddr) {
259 DEBUG_LOG(SCEKERNEL, "sceKernelGetVTimerBase(%08x, %08x)", uid, baseClockAddr);
261 u32 error;
262 VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
264 if (error) {
265 WARN_LOG(SCEKERNEL, "%08x=sceKernelGetVTimerBase(%08x, %08x)", error, uid, baseClockAddr);
266 return error;
269 if (Memory::IsValidAddress(baseClockAddr))
270 Memory::Write_U64(vt->nvt.base, baseClockAddr);
272 return 0;
275 u64 sceKernelGetVTimerBaseWide(SceUID uid) {
276 DEBUG_LOG(SCEKERNEL, "sceKernelGetVTimerBaseWide(%08x)", uid);
278 u32 error;
279 VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
281 if (error) {
282 WARN_LOG(SCEKERNEL, "%08x=sceKernelGetVTimerBaseWide(%08x)", error, uid);
283 return -1;
286 return vt->nvt.base;
289 u32 sceKernelGetVTimerTime(SceUID uid, u32 timeClockAddr) {
290 DEBUG_LOG(SCEKERNEL, "sceKernelGetVTimerTime(%08x, %08x)", uid, timeClockAddr);
292 u32 error;
293 VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
295 if (error) {
296 WARN_LOG(SCEKERNEL, "%08x=sceKernelGetVTimerTime(%08x, %08x)", error, uid, timeClockAddr);
297 return error;
300 u64 time = __getVTimerCurrentTime(vt);
301 if (Memory::IsValidAddress(timeClockAddr))
302 Memory::Write_U64(time, timeClockAddr);
304 return 0;
307 u64 sceKernelGetVTimerTimeWide(SceUID uid) {
308 DEBUG_LOG(SCEKERNEL, "sceKernelGetVTimerTimeWide(%08x)", uid);
310 u32 error;
311 VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
313 if (error) {
314 WARN_LOG(SCEKERNEL, "%08x=sceKernelGetVTimerTimeWide(%08x)", error, uid);
315 return -1;
318 u64 time = __getVTimerCurrentTime(vt);
319 return time;
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);
329 return current;
332 u32 sceKernelSetVTimerTime(SceUID uid, u32 timeClockAddr) {
333 DEBUG_LOG(SCEKERNEL, "sceKernelSetVTimerTime(%08x, %08x)", uid, timeClockAddr);
335 u32 error;
336 VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
338 if (error) {
339 WARN_LOG(SCEKERNEL, "%08x=sceKernelSetVTimerTime(%08x, %08x)", error, uid, timeClockAddr);
340 return error;
343 u64 time = Memory::Read_U64(timeClockAddr);
344 if (Memory::IsValidAddress(timeClockAddr))
345 Memory::Write_U64(__KernelSetVTimer(vt, time), timeClockAddr);
347 return 0;
350 u64 sceKernelSetVTimerTimeWide(SceUID uid, u64 timeClock) {
351 if (__IsInInterrupt()) {
352 WARN_LOG(SCEKERNEL, "sceKernelSetVTimerTimeWide(%08x, %llu): in interrupt", uid, timeClock);
353 return -1;
355 DEBUG_LOG(SCEKERNEL, "sceKernelSetVTimerTimeWide(%08x, %llu)", uid, timeClock);
357 u32 error;
358 VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
360 if (error || vt == NULL) {
361 WARN_LOG(SCEKERNEL, "%08x=sceKernelSetVTimerTimeWide(%08x, %llu)", error, uid, timeClock);
362 return -1;
365 return __KernelSetVTimer(vt, timeClock);
368 static void __startVTimer(VTimer *vt) {
369 vt->nvt.active = 1;
370 vt->nvt.base = CoreTiming::GetGlobalTimeUs();
372 if (vt->nvt.handlerAddr != 0)
373 __KernelScheduleVTimer(vt, vt->nvt.schedule);
376 u32 sceKernelStartVTimer(SceUID uid) {
377 hleEatCycles(12200);
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);
386 u32 error;
387 VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
389 if (vt) {
390 if (vt->nvt.active != 0)
391 return 1;
393 __startVTimer(vt);
394 return 0;
397 return error;
400 static void __stopVTimer(VTimer *vt) {
401 // This increases (__getVTimerCurrentTime includes nvt.current.)
402 vt->nvt.current = __getVTimerCurrentTime(vt);
403 vt->nvt.active = 0;
404 vt->nvt.base = 0;
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);
414 u32 error;
415 VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
417 if (vt) {
418 if (vt->nvt.active == 0)
419 return 0;
421 __stopVTimer(vt);
422 return 1;
425 return error;
428 u32 sceKernelSetVTimerHandler(SceUID uid, u32 scheduleAddr, u32 handlerFuncAddr, u32 commonAddr) {
429 hleEatCycles(900);
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;
435 u32 error;
436 VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
438 if (error) {
439 WARN_LOG(SCEKERNEL, "%08x=sceKernelSetVTimerHandler(%08x, %08x, %08x, %08x)", error, uid, scheduleAddr, handlerFuncAddr, commonAddr);
440 return error;
443 DEBUG_LOG(SCEKERNEL, "sceKernelSetVTimerHandler(%08x, %08x, %08x, %08x)", uid, scheduleAddr, handlerFuncAddr, commonAddr);
444 hleEatCycles(2000);
446 u64 schedule = Memory::Read_U64(scheduleAddr);
447 vt->nvt.handlerAddr = handlerFuncAddr;
448 if (handlerFuncAddr) {
449 vt->nvt.commonAddr = commonAddr;
450 __KernelScheduleVTimer(vt, schedule);
451 } else {
452 __KernelScheduleVTimer(vt, vt->nvt.schedule);
455 return 0;
458 u32 sceKernelSetVTimerHandlerWide(SceUID uid, u64 schedule, u32 handlerFuncAddr, u32 commonAddr) {
459 hleEatCycles(900);
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;
465 u32 error;
466 VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
468 if (error) {
469 WARN_LOG(SCEKERNEL, "%08x=sceKernelSetVTimerHandlerWide(%08x, %llu, %08x, %08x)", error, uid, schedule, handlerFuncAddr, commonAddr);
470 return error;
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);
479 } else {
480 __KernelScheduleVTimer(vt, vt->nvt.schedule);
483 return 0;
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);
501 u32 error;
502 VTimer *vt = kernelObjects.Get<VTimer>(uid, error);
504 if (error) {
505 WARN_LOG(SCEKERNEL, "%08x=sceKernelReferVTimerStatus(%08x, %08x)", error, uid, statusAddr);
506 return error;
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)));
516 return 0;
519 // Not sure why this is exposed...
520 void _sceKernelReturnFromTimerHandler() {
521 ERROR_LOG_REPORT(SCEKERNEL,"_sceKernelReturnFromTimerHandler - should not be called!");