Files
ppsspp/Core/HLE/sceKernelTime.cpp
T
Henrik RydgårdandClaude Opus 5.5 436db85cf5 Reschedule on IO completion, drop the reschedule in time queries
The time queries rescheduled since 2013, so that a game spinning on the
clock would let a thread that a timing event had woken run (it fixed
audio in Crimson Gem Saga and Where Is My Heart?). In 2014 audio and
delay wakeups started rescheduling themselves, but IO completion never
did, and a movie reader thread in Driver 76 was only getting in through
the time queries. Now IO completion dispatches like any other wakeup.

The PSP doesn't dispatch in a time query, and doing so let a thread
that a terminate woke run too early. threads/threads/terminate now
passes.

Checked by booting 75 games against master: the same in all of them,
with Asphalt Urban GT2 getting further in the same time.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 09:19:32 -06:00

205 lines
5.8 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/.
#ifdef _WIN32
#include "Common/CommonWindows.h"
#else
#include <sys/time.h>
#endif
#include <time.h>
#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Core/CoreTiming.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/sceKernelTime.h"
#include "Core/HLE/sceKernelThread.h"
#include "Core/HLE/sceRtc.h"
#include "Core/MemMap.h"
#include "Core/System.h"
#include "StringUtils.h"
// The time when the game started.
static time_t start_time;
void __KernelTimeInit()
{
time(&start_time);
if (PSP_CoreParameter().compat.flags().DateLimited) {
// Car Jack Streets(NPUZ00043) requires that the date cannot exceed a certain time.
// 2011 year makes it work fine.
tm *tm;
tm = localtime(&start_time);
tm->tm_year = 111;// 2011 year.
start_time = mktime(tm);
}
}
void __KernelTimeDoState(PointerWrap &p)
{
auto s = p.Section("sceKernelTime", 1, 2);
if (!s)
return;
if (s < 2) {
Do(p, start_time);
} else {
u64 t = start_time;
Do(p, t);
start_time = (time_t)t;
}
}
int sceKernelGetSystemTime(u32 sysclockPtr)
{
u64 t = CoreTiming::GetGlobalTimeUs();
if (Memory::IsValid4AlignedRange(sysclockPtr, 8)) {
Memory::WriteUnchecked_U64(t, sysclockPtr);
}
VERBOSE_LOG(Log::sceKernel, "sceKernelGetSystemTime(out:%16llx)", t);
hleEatCycles(265);
return hleNoLog(0);
}
u32 sceKernelGetSystemTimeLow()
{
// This clock should tick at 1 Mhz.
u64 t = CoreTiming::GetGlobalTimeUs();
VERBOSE_LOG(Log::sceKernel,"%08x=sceKernelGetSystemTimeLow()",(u32)t);
hleEatCycles(165);
if (PSP_CoreParameter().compat.flags().KernelGetSystemTimeLowEatMoreCycles) {
hleEatCycles(70000);
}
return hleNoLog((u32)t);
}
u64 sceKernelGetSystemTimeWide()
{
u64 t = CoreTiming::GetGlobalTimeUsScaled();
VERBOSE_LOG(Log::sceKernel,"%i=sceKernelGetSystemTimeWide()",(u32)t);
hleEatCycles(250);
return hleNoLog(t);
}
int sceKernelUSec2SysClock(u32 usec, u32 clockPtr)
{
VERBOSE_LOG(Log::sceKernel, "sceKernelUSec2SysClock(%i, %08x)", usec, clockPtr);
if (Memory::IsValid4AlignedRange(clockPtr, 8)) {
Memory::WriteUnchecked_U64((usec & 0xFFFFFFFFL), clockPtr);
}
hleEatCycles(165);
return hleNoLog(0);
}
u64 sceKernelUSec2SysClockWide(u32 usec)
{
VERBOSE_LOG(Log::sceKernel, "sceKernelUSec2SysClockWide(%i)", usec);
hleEatCycles(150);
return hleNoLog(usec);
}
int sceKernelSysClock2USec(u32 sysclockPtr, u32 highPtr, u32 lowPtr) {
DEBUG_LOG(Log::sceKernel, "sceKernelSysClock2USec(clock = %08x, lo = %08x, hi = %08x)", sysclockPtr, highPtr, lowPtr);
if (!Memory::IsValidRange(sysclockPtr, 8)) {
return hleLogError(Log::sceKernel, -1, "sceKernelSysClock2USec: Invalid sysclockPtr %08x", sysclockPtr);
}
u64 time = Memory::ReadUnchecked_U64(sysclockPtr);
u32 highResult = (u32)(time / 1000000);
u32 lowResult = (u32)(time % 1000000);
if (Memory::IsValid4AlignedAddress(highPtr))
Memory::WriteUnchecked_U32(highResult, highPtr);
if (Memory::IsValid4AlignedAddress(lowPtr))
Memory::WriteUnchecked_U32(lowResult, lowPtr);
hleEatCycles(415);
return hleNoLog(0);
}
int sceKernelSysClock2USecWide(u32 lowClock, u32 highClock, u32 lowPtr, u32 highPtr) {
u64 sysClock = lowClock | ((u64)highClock << 32);
DEBUG_LOG(Log::sceKernel, "sceKernelSysClock2USecWide(clock = %llu, lo = %08x, hi = %08x)", sysClock, lowPtr, highPtr);
if (Memory::IsValid4AlignedAddress(lowPtr)) {
Memory::WriteUnchecked_U32((u32)(sysClock / 1000000), lowPtr);
if (Memory::IsValid4AlignedAddress(highPtr))
Memory::WriteUnchecked_U32((u32)(sysClock % 1000000), highPtr);
} else {
if (Memory::IsValid4AlignedAddress(highPtr)) {
Memory::WriteUnchecked_U32((int)sysClock, highPtr);
}
}
hleEatCycles(385);
return hleNoLog(0);
}
u32 sceKernelLibcClock()
{
u32 retVal = (u32) CoreTiming::GetGlobalTimeUs();
hleEatCycles(330);
return hleLogDebug(Log::sceKernel, retVal);
}
u32 sceKernelLibcTime(u32 outPtr)
{
u32 t = (u32) start_time + (u32) (CoreTiming::GetGlobalTimeUs() / 1000000ULL);
// The PSP sure takes its sweet time on this function.
hleEatCycles(3385);
if (Memory::IsValidAddress(outPtr))
Memory::WriteUnchecked_U32(t, outPtr);
else if (outPtr != 0)
return 0;
return hleLogDebug(Log::sceKernel, t);
}
u32 sceKernelLibcGettimeofday(u32 timeAddr, u32 tzAddr)
{
// TODO: tzAddr?
if (Memory::IsValidAddress(timeAddr))
{
PSPTimeval *tv = (PSPTimeval *)Memory::GetPointerOrException(timeAddr);
__RtcTimeOfDay(tv);
}
DEBUG_LOG(Log::sceKernel,"sceKernelLibcGettimeofday(%08x, %08x)", timeAddr, tzAddr);
hleEatCycles(1885);
return hleNoLog(0);
}
std::string KernelTimeNowFormatted() {
time_t emulatedTime = (time_t)start_time + (u32)(CoreTiming::GetGlobalTimeUs() / 1000000ULL);
tm* timePtr = localtime(&emulatedTime);
bool DST = timePtr->tm_isdst != 0;
u8 seconds = timePtr->tm_sec;
u8 minutes = timePtr->tm_min;
u8 hours = timePtr->tm_hour;
if (DST)
hours = timePtr->tm_hour + 1;
u8 days = timePtr->tm_mday;
u8 months = timePtr->tm_mon + 1;
u16 years = timePtr->tm_year + 1900;
std::string timestamp = StringFromFormat("%04d-%02d-%02d_%02d-%02d-%02d", years, months, days, hours, minutes, seconds);
return timestamp;
}
void KernelTimeSetBase(int64_t seconds) {
start_time = (time_t)seconds;
}