mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-07 05:03:35 +02:00
Implement a batch of the VSH's unresolved imports (and document three we must not)
Mostly small stubs: - sceImpose: GetParam/SetParam/Changes/SetStatus, plus the 6.60 alias of sceImposeGetBatteryIconStatus. Also fixes that function's first output - it is a plain "is it charging" boolean, not a BATTICON_ value. We wrote PSP_IMPOSE_BATTICON_NONE (0x80000000) there, which games ignore but which the VSH reads as "no battery" and draws the empty-battery indicator for. These are the bulk of the traffic: the VSH calls sceImposeChanges once a frame, so this alone removes ~10000 trapped calls from a boot. - SysMemForKernel: sceKernelSetRebootKernel, sceKernelSetUmdCacheOn. - scePower_driver: scePowerSetWakeupCondition. - sceHprm_driver, sceUsb: one NID-named call each, as in JPCSP. Three groups are deliberately left unresolved, with comments explaining why, because resolving them lets real flash0 drivers walk into hardware we do not emulate and the boot dies where it used to reach the shell: - ThreadManForKernel mutex/fpl NIDs: the NAND and ID storage drivers use these to init, then poll the NAND controller at 0xbd101300 forever. - InterruptManagerForKernel intr registration: 31 calls, then a stall in GE list execution with no plugin module ever started. 73 unresolved import hits over 37 distinct module/NID pairs remain in a VSH boot, mostly sceSysEventForKernel, sceSuspendForKernel and the various *_driver modules that need real hardware behind them. The sceImpose savestate section goes to v2 for the two new state variables. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
This commit is contained in:
co-authored by
Claude Opus 5
parent
4667f5e2da
commit
f449fa6ab2
@@ -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>, "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>, "sceHprm_driver_DC895B2B", 'x', ""},
|
||||
};
|
||||
|
||||
void Register_sceHprm_driver()
|
||||
|
||||
+90
-2
@@ -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>, "sceImpose_driver_B497314D", 'i', "ix"},
|
||||
{0XDC3BECFF, &WrapI_I<sceImposeGetParam>, "sceImposeGetParam", 'i', "i" },
|
||||
{0X3C318569, &WrapI_II<sceImposeSetParam>, "sceImposeSetParam", 'i', "ii"},
|
||||
{0X0F067E16, &WrapI_V<sceImposeChanges>, "sceImposeChanges", 'i', "" },
|
||||
{0XBB12F974, &WrapI_I<sceImposeSetStatus>, "sceImposeSetStatus", 'i', "i" },
|
||||
// The 6.60 name for the same call the user-mode module exports as 0x8C943191.
|
||||
{0X5557F4E2, &WrapU_UU<sceImposeGetBatteryIconStatus>, "sceImposeGetBatteryIconStatus", 'x', "xx"},
|
||||
};
|
||||
|
||||
void Register_sceImpose_driver() {
|
||||
|
||||
@@ -958,6 +958,15 @@ const HLEFunction ThreadManForKernel[] =
|
||||
{0xC11BA8C4, &WrapI_II<sceKernelNotifyCallback>, "sceKernelNotifyCallback", 'i', "ii", HLE_KERNEL_SYSCALL },
|
||||
{0xF6427665, &WrapI_V<sceKernelGetUserLevel>, "sceKernelGetUserLevel", 'i', "", HLE_KERNEL_SYSCALL },
|
||||
{0x85A2A5BF, &WrapI_V<sceKernelIsUserModeThread>, "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()
|
||||
|
||||
@@ -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>, "sceKernelSetRebootKernel", 'i', "x", HLE_KERNEL_SYSCALL },
|
||||
{ 0X1404C1AA, &WrapI_I<sceKernelSetUmdCacheOn>, "sceKernelSetUmdCacheOn", 'i', "i", HLE_KERNEL_SYSCALL },
|
||||
{ 0X636C953B, &WrapI_II<sceKernelAllocHeapMemory>, "sceKernelAllocHeapMemory", 'x', "ii", HLE_KERNEL_SYSCALL },
|
||||
{ 0XC9805775, &WrapI_I<sceKernelDeleteHeap>, "sceKernelDeleteHeap", 'i', "i" , HLE_KERNEL_SYSCALL },
|
||||
{ 0X1C1FBFE7, &WrapI_IIIC<sceKernelCreateHeap>, "sceKernelCreateHeap", 'i', "iixs", HLE_KERNEL_SYSCALL },
|
||||
|
||||
@@ -1052,6 +1052,7 @@ static int sceKernelIsIntrContext() {
|
||||
return hleLogDebug(Log::sceKernel, __IsInInterrupt() ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
const HLEFunction InterruptManagerForKernel[] =
|
||||
{
|
||||
{0x092968F4, &WrapI_V<sceKernelCpuSuspendIntr>, "sceKernelCpuSuspendIntr", 'i', "" ,HLE_KERNEL_SYSCALL },
|
||||
@@ -1073,6 +1074,15 @@ const HLEFunction InterruptManagerForKernel[] =
|
||||
{0X05572A5F, &WrapV_V<sceKernelExitGame>, "sceKernelExitGame", 'v', "" ,HLE_KERNEL_SYSCALL },
|
||||
{0X4AC57943, &WrapI_I<sceKernelRegisterExitCallback>, "sceKernelRegisterExitCallback", 'i', "i" ,HLE_KERNEL_SYSCALL },
|
||||
{0XFE28C6D9, &WrapI_V<sceKernelIsIntrContext>, "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()
|
||||
|
||||
@@ -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>, "scePower_driver_5F5006D2", 'i', "" },
|
||||
{0XBA566CD0, &WrapI_U<scePowerSetWakeupCondition>, "scePowerSetWakeupCondition", 'i', "x" },
|
||||
};
|
||||
|
||||
void Register_scePower_driver() {
|
||||
|
||||
@@ -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>, "sceUsb_8BFC3DE8", 'i', "x" },
|
||||
{0XAE5DE6AF, &WrapI_CUU<sceUsbStart>, "sceUsbStart", 'i', "sxx"},
|
||||
{0XC2464FA0, &WrapI_CUU<sceUsbStop>, "sceUsbStop", 'i', "sxx"},
|
||||
{0XC21645A4, &WrapI_V<sceUsbGetState>, "sceUsbGetState", 'i', "" },
|
||||
|
||||
Reference in New Issue
Block a user