diff --git a/Core/HLE/sceHprm.cpp b/Core/HLE/sceHprm.cpp index f6dba1f759..718fc9e781 100644 --- a/Core/HLE/sceHprm.cpp +++ b/Core/HLE/sceHprm.cpp @@ -81,9 +81,16 @@ void Register_sceHprm() // Kernel-mode variant library used by VSH modules (e.g. vshbridge). NID 0xE9B776BE is the // firmware 6.60+ alias of sceHprmReadLatch, listed as nid=0xE9B776BE version=660 alongside the // 0x40D2F9F0 version=150 NID in JPCSP's sceHprm.java - both resolve to the same function there. +static u32 sceHprm_driver_DC895B2B() { + return hleLogWarning(Log::sceMisc, 0, "UNIMPL"); +} + const HLEFunction sceHprm_driver[] = { {0XE9B776BE, &WrapU_U, "sceHprmReadLatch", 'x', "x"}, + // Purpose unknown - JPCSP names it after its NID too, and returns 0. Present so the VSH's + // one startup call resolves instead of trapping. + {0XDC895B2B, &WrapU_V, "sceHprm_driver_DC895B2B", 'x', ""}, }; void Register_sceHprm_driver() diff --git a/Core/HLE/sceImpose.cpp b/Core/HLE/sceImpose.cpp index f2b9690a22..d01be2228a 100644 --- a/Core/HLE/sceImpose.cpp +++ b/Core/HLE/sceImpose.cpp @@ -19,6 +19,7 @@ #include "Common/Serialize/SerializeFuncs.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" +#include "Core/HLE/ErrorCodes.h" #include "Core/HLE/sceImpose.h" #include "Core/HLE/sceUtility.h" #include "Core/MIPS/MIPS.h" @@ -39,6 +40,9 @@ static u32 language = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH; static u32 buttonValue = PSP_SYSTEMPARAM_BUTTON_CIRCLE; static u32 umdPopup = PSP_UMD_POPUP_DISABLE; static u32 backlightOffTime; +// See sceImposeChanges, further down. +static u32 imposeChanges; +static u32 imposeAvls; void __ImposeInit() { language = GetPSPLanguage(); @@ -50,10 +54,12 @@ void __ImposeInit() { buttonValue = PSP_CoreParameter().compat.flags().ForceCircleButtonConfirm ? PSP_SYSTEMPARAM_BUTTON_CIRCLE : g_Config.iButtonPreference; umdPopup = PSP_UMD_POPUP_DISABLE; backlightOffTime = 0; + imposeChanges = 0; + imposeAvls = 0; } void __ImposeDoState(PointerWrap &p) { - auto s = p.Section("sceImpose", 1); + auto s = p.Section("sceImpose", 1, 2); if (!s) return; @@ -61,12 +67,20 @@ void __ImposeDoState(PointerWrap &p) { Do(p, buttonValue); Do(p, umdPopup); Do(p, backlightOffTime); + if (s >= 2) { + Do(p, imposeChanges); + Do(p, imposeAvls); + } } static u32 sceImposeGetBatteryIconStatus(u32 chargingPtr, u32 iconStatusPtr) { + // The first output is a plain "is it charging" boolean, not a BATTICON_ value - we used to + // write PSP_IMPOSE_BATTICON_NONE (0x80000000) here, which games ignore but which the VSH + // reads as "no battery" and draws the empty-battery indicator for. Matches JPCSP now: + // charging is 0/1, and the icon status is the charge level in four steps. if (Memory::IsValidAddress(chargingPtr)) - Memory::WriteUnchecked_U32(PSP_IMPOSE_BATTICON_NONE, chargingPtr); + Memory::WriteUnchecked_U32(0, chargingPtr); if (Memory::IsValidAddress(iconStatusPtr)) Memory::WriteUnchecked_U32(3, iconStatusPtr); return hleLogDebug(Log::sceUtility, 0); @@ -137,8 +151,82 @@ static int sceImpose_driver_B497314D(int param, u32 resultAddr) { return hleLogDebug(Log::sceUtility, 0, "UNTESTED"); } +// The settings sceImposeGetParam/SetParam address, as a bitfield - sceImposeChanges reports which +// of them have been changed since it was last asked, so the VSH can refresh only what moved. +enum { + PSP_IMPOSE_MAIN_VOLUME = 0x1, + PSP_IMPOSE_BACKLIGHT_BRIGHTNESS = 0x2, + PSP_IMPOSE_EQUALIZER_MODE = 0x4, + PSP_IMPOSE_MUTE = 0x8, + PSP_IMPOSE_AVLS = 0x10, + PSP_IMPOSE_TIME_FORMAT = 0x20, + PSP_IMPOSE_DATE_FORMAT = 0x40, + PSP_IMPOSE_LANGUAGE = 0x80, + PSP_IMPOSE_BACKLIGHT_OFF_INTERVAL = 0x200, + PSP_IMPOSE_SOUND_REDUCTION = 0x400, +}; + +// Returns the current value of one setting. Values follow JPCSP's, which are the plausible +// defaults for a console with nothing unusual configured. Anything we don't model reads as 0 +// rather than failing - the VSH polls this every frame for its indicators, so returning an error +// would be far more disruptive than returning a quiet default. +static int sceImposeGetParam(int param) { + switch (param) { + case PSP_IMPOSE_MAIN_VOLUME: + return hleLogDebug(Log::sceUtility, 30); // Full, on a 0-30 scale. + case PSP_IMPOSE_BACKLIGHT_BRIGHTNESS: + return hleLogDebug(Log::sceUtility, 3); + case PSP_IMPOSE_AVLS: + return hleLogDebug(Log::sceUtility, imposeAvls); + case PSP_IMPOSE_MUTE: + case PSP_IMPOSE_SOUND_REDUCTION: + case PSP_IMPOSE_BACKLIGHT_OFF_INTERVAL: + return hleLogDebug(Log::sceUtility, 0); + default: + return hleLogDebug(Log::sceUtility, 0, "unmodelled param %08x", param); + } +} + +static int sceImposeSetParam(int param, int value) { + switch (param) { + case PSP_IMPOSE_AVLS: + if (value < 0 || value > 1) + return hleLogWarning(Log::sceUtility, SCE_KERNEL_ERROR_INVALID_VALUE); + imposeAvls = value; + imposeChanges |= PSP_IMPOSE_AVLS | PSP_IMPOSE_MAIN_VOLUME; + break; + case PSP_IMPOSE_MAIN_VOLUME: + if (value < 0 || value >= 31) + return hleLogWarning(Log::sceUtility, SCE_KERNEL_ERROR_INVALID_VALUE); + imposeChanges |= PSP_IMPOSE_MAIN_VOLUME; + break; + default: + // Recording the change is the part callers actually observe, through sceImposeChanges. + imposeChanges |= param; + break; + } + return hleLogDebug(Log::sceUtility, 0); +} + +// Which settings changed since the last call - reading clears them. +static int sceImposeChanges() { + const u32 changes = imposeChanges; + imposeChanges = 0; + return hleLogDebug(Log::sceUtility, changes); +} + +static int sceImposeSetStatus(int status) { + return hleLogDebug(Log::sceUtility, 0, "UNIMPL"); +} + const HLEFunction sceImpose_driver[] = { {0XB497314D, &WrapI_IU, "sceImpose_driver_B497314D", 'i', "ix"}, + {0XDC3BECFF, &WrapI_I, "sceImposeGetParam", 'i', "i" }, + {0X3C318569, &WrapI_II, "sceImposeSetParam", 'i', "ii"}, + {0X0F067E16, &WrapI_V, "sceImposeChanges", 'i', "" }, + {0XBB12F974, &WrapI_I, "sceImposeSetStatus", 'i', "i" }, + // The 6.60 name for the same call the user-mode module exports as 0x8C943191. + {0X5557F4E2, &WrapU_UU, "sceImposeGetBatteryIconStatus", 'x', "xx"}, }; void Register_sceImpose_driver() { diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 15b93b2a16..ed561d6a2e 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -958,6 +958,15 @@ const HLEFunction ThreadManForKernel[] = {0xC11BA8C4, &WrapI_II, "sceKernelNotifyCallback", 'i', "ii", HLE_KERNEL_SYSCALL }, {0xF6427665, &WrapI_V, "sceKernelGetUserLevel", 'i', "", HLE_KERNEL_SYSCALL }, {0x85A2A5BF, &WrapI_V, "sceKernelIsUserModeThread", 'i', "", HLE_KERNEL_SYSCALL }, + // NOT added on purpose, even though we implement all four for user mode already: + // sceKernelCreateMutex (0xB7D098C6), sceKernelLockMutex (0xB011B11F), sceKernelUnlockMutex + // (0x6B30100F) and sceKernelAllocateFpl (0xD979E9BF). They are what the real flash0 NAND and + // ID storage drivers use to set themselves up while booting the VSH, and leaving them + // unresolved is load-bearing: the drivers fail their init early and the boot moves on. Resolve + // them and the drivers instead get all the way through to talking to the NAND controller, which + // we don't emulate - sceIdStorage_Service then polls 0xbd101300 forever, no plugin module ever + // starts, and the boot is dead where it used to reach the shell. Adding these needs NAND MMIO + // (or HLE'ing sceIdStorage/sceNand above the driver) to land first. }; void Register_ThreadManForUser() diff --git a/Core/HLE/sceKernelHeap.cpp b/Core/HLE/sceKernelHeap.cpp index d2269abdfb..94aed7764b 100644 --- a/Core/HLE/sceKernelHeap.cpp +++ b/Core/HLE/sceKernelHeap.cpp @@ -165,7 +165,20 @@ static int sceKernelGetModel() { return hleLogWarning(Log::sceKernel, model - 1); } +// Both configure things PPSSPP has no equivalent of - which kernel image a reboot would use, and +// whether the UMD read cache is on. Accepted and ignored; the VSH calls them once each during +// startup and only cares that they succeed. +static int sceKernelSetRebootKernel(u32 arg) { + return hleLogWarning(Log::sceKernel, 0, "UNIMPL"); +} + +static int sceKernelSetUmdCacheOn(int on) { + return hleLogWarning(Log::sceKernel, 0, "UNIMPL"); +} + const HLEFunction SysMemForKernel[] = { + { 0X96A3CE2C, &WrapI_U, "sceKernelSetRebootKernel", 'i', "x", HLE_KERNEL_SYSCALL }, + { 0X1404C1AA, &WrapI_I, "sceKernelSetUmdCacheOn", 'i', "i", HLE_KERNEL_SYSCALL }, { 0X636C953B, &WrapI_II, "sceKernelAllocHeapMemory", 'x', "ii", HLE_KERNEL_SYSCALL }, { 0XC9805775, &WrapI_I, "sceKernelDeleteHeap", 'i', "i" , HLE_KERNEL_SYSCALL }, { 0X1C1FBFE7, &WrapI_IIIC, "sceKernelCreateHeap", 'i', "iixs", HLE_KERNEL_SYSCALL }, diff --git a/Core/HLE/sceKernelInterrupt.cpp b/Core/HLE/sceKernelInterrupt.cpp index 31e4f00263..00fe428219 100644 --- a/Core/HLE/sceKernelInterrupt.cpp +++ b/Core/HLE/sceKernelInterrupt.cpp @@ -1052,6 +1052,7 @@ static int sceKernelIsIntrContext() { return hleLogDebug(Log::sceKernel, __IsInInterrupt() ? 1 : 0); } + const HLEFunction InterruptManagerForKernel[] = { {0x092968F4, &WrapI_V, "sceKernelCpuSuspendIntr", 'i', "" ,HLE_KERNEL_SYSCALL }, @@ -1073,6 +1074,15 @@ const HLEFunction InterruptManagerForKernel[] = {0X05572A5F, &WrapV_V, "sceKernelExitGame", 'v', "" ,HLE_KERNEL_SYSCALL }, {0X4AC57943, &WrapI_I, "sceKernelRegisterExitCallback", 'i', "i" ,HLE_KERNEL_SYSCALL }, {0XFE28C6D9, &WrapI_V, "sceKernelIsIntrContext", 'i', "" ,HLE_KERNEL_SYSCALL }, + // NOT added on purpose, even though JPCSP implements all four: sceKernelRegisterIntrHandler + // (0x58DD8978), sceKernelReleaseIntrHandler (0xF987B1F0), sceKernelEnableIntr (0x4D6E7305) + // and sceKernelDisableIntr (0xD774BA45). JPCSP can honour them because it emulates the + // interrupt controller as MMIO; we dispatch the few interrupts we emulate ourselves (see + // __RegisterIntrHandler and its callers in sceGe/sceKernelAlarm/sceKernelVTimer) and have no + // way to run a guest handler for one. Stubbing them to return success is therefore a lie the + // real flash0 drivers act on - measured while booting the VSH, they make 31 such calls + // (interrupts 4, 12, 15-18, 20-24, 31), and the boot then stalls in GE list execution without + // ever starting a plugin module, where leaving them unresolved reaches the shell. }; void Register_InterruptManagerForKernel() diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index e062c648b9..711a8e392e 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -205,7 +205,7 @@ static int scePowerIsLowBattery() { } static int scePowerIsSuspendRequired() { - return hleLogInfo(Log::HLE, 0); + return hleLogDebug(Log::HLE, 0); } static int scePowerCancelRequest(u32 something) { @@ -660,8 +660,15 @@ static int scePower_driver_5F5006D2() { return hleLogDebug(Log::HLE, 0, "UNTESTED"); } +// Configures which events would wake the console from suspend. We never suspend, so there's +// nothing to arm - but the VSH calls it during startup and wants a success back. +static int scePowerSetWakeupCondition(u32 condition) { + return hleLogWarning(Log::sceMisc, 0, "UNIMPL"); +} + const HLEFunction scePower_driver[] = { {0X5F5006D2, &WrapI_V, "scePower_driver_5F5006D2", 'i', "" }, + {0XBA566CD0, &WrapI_U, "scePowerSetWakeupCondition", 'i', "x" }, }; void Register_scePower_driver() { diff --git a/Core/HLE/sceUsb.cpp b/Core/HLE/sceUsb.cpp index d890000f41..069218e3ab 100644 --- a/Core/HLE/sceUsb.cpp +++ b/Core/HLE/sceUsb.cpp @@ -210,8 +210,14 @@ static int sceUsbstorBootSetCapacity(u32 capacity) { return hleReportError(Log::HLE, 0, "unimplemented"); } +// Purpose unknown - JPCSP names it after its NID and returns 0. +static int sceUsb_8BFC3DE8(u32 arg) { + return hleLogWarning(Log::sceMisc, 0, "UNIMPL"); +} + const HLEFunction sceUsb[] = { + {0X8BFC3DE8, &WrapI_U, "sceUsb_8BFC3DE8", 'i', "x" }, {0XAE5DE6AF, &WrapI_CUU, "sceUsbStart", 'i', "sxx"}, {0XC2464FA0, &WrapI_CUU, "sceUsbStop", 'i', "sxx"}, {0XC21645A4, &WrapI_V, "sceUsbGetState", 'i', "" },