diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 38bc5e955..6eb1a310f 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -734,8 +734,8 @@ const HLEFunction ThreadManForUser[] = {0X71BC9871, &WrapI_II, "sceKernelChangeThreadPriority", 'i', "ii" }, {0X446D8DE6, &WrapI_CUUIUU, "sceKernelCreateThread", 'i', "sxxixx", HLE_NOT_IN_INTERRUPT }, {0X9FA03CD3, &WrapI_I, "sceKernelDeleteThread", 'i', "i" }, - {0XBD123D9E, &WrapI_U, "sceKernelDelaySysClockThread", 'i', "x", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, - {0X1181E963, &WrapI_U, "sceKernelDelaySysClockThreadCB", 'i', "x", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, + {0XBD123D9E, &WrapI_U, "sceKernelDelaySysClockThread", 'i', "P", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, + {0X1181E963, &WrapI_U, "sceKernelDelaySysClockThreadCB", 'i', "P", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0XCEADEB47, &WrapI_U, "sceKernelDelayThread", 'i', "x", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0X68DA9E36, &WrapI_U, "sceKernelDelayThreadCB", 'i', "x", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED }, {0XAA73C935, &WrapV_I, "sceKernelExitThread", 'v', "i" }, diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 5685edb47..146d69676 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -2413,6 +2413,17 @@ static s64 __KernelDelayThreadUs(u64 usec) { if (usec < 200) { return 210; } + + if (usec > 0x8000000000000000ULL) { + // Wrap around (behavior seen on firmware) and potentially wake up soon. + usec -= 0x8000000000000000ULL; + } + if (usec > 0x0010000000000000ULL) { + // This will probably overflow when we convert to cycles. + // Note: converting millenia to hundreds of years. Should be safe, basically perma-delay. + usec >>= 12; + } + // It never wakes up right away. It usually takes at least 15 extra us, but let's be nicer. return usec + 10; } @@ -2439,40 +2450,38 @@ int sceKernelDelayThread(u32 usec) { return hleLogSuccessI(SCEKERNEL, 0, "delaying %lld usecs", delayUs); } -int sceKernelDelaySysClockThreadCB(u32 sysclockAddr) -{ +int sceKernelDelaySysClockThreadCB(u32 sysclockAddr) { auto sysclock = PSPPointer::Create(sysclockAddr); if (!sysclock.IsValid()) { - ERROR_LOG(SCEKERNEL, "sceKernelDelaySysClockThreadCB(%08x) - bad pointer", sysclockAddr); - return -1; + // Note: crashes on real firmware. + return hleLogError(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad pointer"); } - // TODO: Which unit? + // This is just a u64 of usecs. All bits are respected, but overflow can happen for very large values. u64 usec = sysclock->lo | ((u64)sysclock->hi << 32); - DEBUG_LOG(SCEKERNEL, "sceKernelDelaySysClockThreadCB(%08x (%llu))", sysclockAddr, usec); SceUID curThread = __KernelGetCurThread(); - __KernelScheduleWakeup(curThread, __KernelDelayThreadUs(usec)); + s64 delayUs = __KernelDelayThreadUs(usec); + __KernelScheduleWakeup(curThread, delayUs); __KernelWaitCurThread(WAITTYPE_DELAY, curThread, 0, 0, true, "thread delayed"); - return 0; + return hleLogSuccessI(SCEKERNEL, 0, "delaying %lld usecs", delayUs); } -int sceKernelDelaySysClockThread(u32 sysclockAddr) -{ +int sceKernelDelaySysClockThread(u32 sysclockAddr) { auto sysclock = PSPPointer::Create(sysclockAddr); if (!sysclock.IsValid()) { - ERROR_LOG(SCEKERNEL, "sceKernelDelaySysClockThread(%08x) - bad pointer", sysclockAddr); - return -1; + // Note: crashes on real firmware. + return hleLogError(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "bad pointer"); } - // TODO: Which unit? + // This is just a u64 of usecs. All bits are respected, but overflow can happen for very large values. u64 usec = sysclock->lo | ((u64)sysclock->hi << 32); - DEBUG_LOG(SCEKERNEL, "sceKernelDelaySysClockThread(%08x (%llu))", sysclockAddr, usec); SceUID curThread = __KernelGetCurThread(); - __KernelScheduleWakeup(curThread, __KernelDelayThreadUs(usec)); + s64 delayUs = __KernelDelayThreadUs(usec); + __KernelScheduleWakeup(curThread, delayUs); __KernelWaitCurThread(WAITTYPE_DELAY, curThread, 0, 0, false, "thread delayed"); - return 0; + return hleLogSuccessI(SCEKERNEL, 0, "delaying %lld usecs", delayUs); } u32 __KernelGetThreadPrio(SceUID id)