Files
ppsspp/Core/HLE/HLETables.cpp
T
Henrik RydgårdandClaude Opus 5.5 0b1e1a9bb3 POPS: Boot PS1 EBOOTs through the firmware's POPS manager
A PS1 EBOOT now boots the installed firmware's POPS manager
(flash0:/kd/popsman.prx), which loads its PS1 emulator (pops_0Xg.prx) for
the game. Load_PSP_POPS stands in for the kernel's reboot into its POPS
configuration: it loads the manager and that configuration's user modules,
and a helper thread makes the kernel init's scePopsManLoadModule(path, 0)
call. InitForKernel reports a POPS boot from the memory stick.

The emulator plays its sound on the Media Engine, so a PS1 boot always
gets the emulated ME, whatever the homebrew setting says, and is refused
on platforms that can't emulate it. The manager boots the ME with its own
code (POPS's reboot has no me_wrapper, so Sony's ME firmware isn't booted
for it), and its ME loop runs there unchanged, playing through the audio
block.

Everything in POPS that reaches hardware registers is kernel code, so a
POPS boot leaves the ME's kernel mode stopgap (g_kernelModeAnywhere) off:
7% fewer host instructions in Crash Bandicoot.

The manager also drives the GE directly: its stall register and FINISH
status are an MMIO device that only answers during a POPS boot, and stall
writes extend the HLE GPU's current list.

The 6.60 and 6.61 popsman.prx and pops_01g/02g.prx have the same code. The
manager's is checked when it's loaded, and any other one fails the boot
with a message. Savestates include the boot's threads.

Reading a game's disc image needs the next two changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 13:15:30 +02:00

370 lines
18 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/.
#include "Core/HLE/HLE.h"
#include "Core/HLE/HLETables.h"
#include "Core/HLE/FunctionWrappers.h"
#include "sceAtrac.h"
#include "sceAudio.h"
#include "sceAudiocodec.h"
#include "sceAudioRouting.h"
#include "sceCcc.h"
#include "sceChnnlsv.h"
#include "sceCtrl.h"
#include "sceChkreg.h"
#include "sceDeflt.h"
#include "sceDisplay.h"
#include "sceDmac.h"
#include "sceFont.h"
#include "sceGameUpdate.h"
#include "sceGe.h"
#include "sceHeap.h"
#include "sceHprm.h"
#include "sceHttp.h"
#include "sceImpose.h"
#include "sceIo.h"
#include "sceJpeg.h"
#include "sceKernel.h"
#include "sceKernelEventFlag.h"
#include "sceKernelHeap.h"
#include "sceKernelMemory.h"
#include "sceKernelInterrupt.h"
#include "sceKernelModule.h"
#include "sceKernelSemaphore.h"
#include "sceKernelThread.h"
#include "sceKernelTime.h"
#include "sceMd5.h"
#include "sceMp4.h"
#include "sceAac.h"
#include "sceMp3.h"
#include "sceNet.h"
#include "sceNetAdhoc.h"
#include "sceNetAdhocMatching.h"
#include "sceNp.h"
#include "sceMpeg.h"
#include "sceMpegbase.h"
#include "sceOpenPSID.h"
#include "sceResmgr.h"
#include "sceP3da.h"
#include "sceParseHttp.h"
#include "sceParseUri.h"
#include "scePauth.h"
#include "scePower.h"
#include "scePspNpDrm_user.h"
#include "scePsmf.h"
#include "sceReg.h"
#include "sceRtc.h"
#include "sceSas.h"
#include "sceSircs.h"
#include "sceSsl.h"
#include "sceUmd.h"
#include "sceUsb.h"
#include "sceUsbAcc.h"
#include "sceUsbCam.h"
#include "sceUsbGps.h"
#include "sceUsbMic.h"
#include "sceUtility.h"
#include "sceVaudio.h"
#include "sceVshBridge.h"
#include "sceMt19937.h"
#include "sceSha256.h"
#include "sceAdler.h"
#include "sceSfmt19937.h"
#include "sceG729.h"
#include "KUBridge.h"
#include "PopsBoot.h"
#include "sceAmctrl.h"
#include "sceNetInet.h"
#include "sceNetResolver.h"
// #include "sceNp2.h"
#include "sceNet_lib.h"
#include "sceVideocodec.h"
#include "sceSysreg.h"
static const HLEFunction FakeSysCalls[] = {
{NID_THREADRETURN, __KernelReturnFromThread, "__KernelReturnFromThread", 'x', ""},
{NID_CALLBACKRETURN, __KernelReturnFromMipsCall, "__KernelReturnFromMipsCall", 'x', ""},
{NID_INTERRUPTRETURN, __KernelReturnFromInterrupt, "__KernelReturnFromInterrupt", 'x', ""},
{NID_EXTENDRETURN, __KernelReturnFromExtendStack, "__KernelReturnFromExtendStack", 'x', ""},
{NID_MODULERETURN, __KernelReturnFromModuleFunc, "__KernelReturnFromModuleFunc", 'x', ""},
{NID_IDLE, __KernelIdle, "_sceKernelIdle", 'x', ""},
{NID_GPUREPLAY, &WrapI_V<__KernelGPUReplay>, "__KernelGPUReplay", 'x', ""},
{NID_HLECALLRETURN, HLEReturnFromMipsCall, "HLEReturnFromMipsCall", 'x', ""},
};
static const HLEFunction UtilsForUser[] = {
{0X91E4F6A7, &WrapU_V<sceKernelLibcClock>, "sceKernelLibcClock", 'x', "" },
{0X27CC57F0, &WrapU_U<sceKernelLibcTime>, "sceKernelLibcTime", 'x', "x" },
{0X71EC4271, &WrapU_UU<sceKernelLibcGettimeofday>, "sceKernelLibcGettimeofday", 'x', "xx" },
{0XBFA98062, &WrapI_UI<sceKernelDcacheInvalidateRange>, "sceKernelDcacheInvalidateRange", 'i', "xi" },
{0XC8186A58, &WrapI_UIU<sceKernelUtilsMd5Digest>, "sceKernelUtilsMd5Digest", 'i', "xix"},
{0X9E5C5086, &WrapI_U<sceKernelUtilsMd5BlockInit>, "sceKernelUtilsMd5BlockInit", 'i', "x" },
{0X61E1E525, &WrapI_UUI<sceKernelUtilsMd5BlockUpdate>, "sceKernelUtilsMd5BlockUpdate", 'i', "xxi"},
{0XB8D24E78, &WrapI_UU<sceKernelUtilsMd5BlockResult>, "sceKernelUtilsMd5BlockResult", 'i', "xx" },
{0X840259F1, &WrapI_UIU<sceKernelUtilsSha1Digest>, "sceKernelUtilsSha1Digest", 'i', "xix"},
{0XF8FCD5BA, &WrapI_U<sceKernelUtilsSha1BlockInit>, "sceKernelUtilsSha1BlockInit", 'i', "x" },
{0X346F6DA8, &WrapI_UUI<sceKernelUtilsSha1BlockUpdate>, "sceKernelUtilsSha1BlockUpdate", 'i', "xxi"},
{0X585F1C09, &WrapI_UU<sceKernelUtilsSha1BlockResult>, "sceKernelUtilsSha1BlockResult", 'i', "xx" },
{0XE860E75E, &WrapU_UU<sceKernelUtilsMt19937Init>, "sceKernelUtilsMt19937Init", 'x', "xx" },
{0X06FB8A63, &WrapU_U<sceKernelUtilsMt19937UInt>, "sceKernelUtilsMt19937UInt", 'x', "x" },
{0X37FB5C42, &WrapU_V<sceKernelGetGPI>, "sceKernelGetGPI", 'x', "" },
{0X6AD345D7, &WrapV_U<sceKernelSetGPO>, "sceKernelSetGPO", 'v', "x" },
{0X79D1C3FA, &WrapI_V<sceKernelDcacheWritebackAll>, "sceKernelDcacheWritebackAll", 'i', "" },
{0XB435DEC5, &WrapI_V<sceKernelDcacheWritebackInvalidateAll>, "sceKernelDcacheWritebackInvalidateAll", 'i', "" },
{0X3EE30821, &WrapI_UI<sceKernelDcacheWritebackRange>, "sceKernelDcacheWritebackRange", 'i', "xi" },
{0X34B9FA9E, &WrapI_UI<sceKernelDcacheWritebackInvalidateRange>, "sceKernelDcacheWritebackInvalidateRange", 'i', "xi" },
{0XC2DF770E, &WrapI_UI<sceKernelIcacheInvalidateRange>, "sceKernelIcacheInvalidateRange", 'i', "xi" },
{0X80001C4C, nullptr, "sceKernelDcacheProbe", '?', "" },
{0X16641D70, nullptr, "sceKernelDcacheReadTag", '?', "" },
{0X4FD31C9D, nullptr, "sceKernelIcacheProbe", '?', "" },
{0XFB05FAD0, nullptr, "sceKernelIcacheReadTag", '?', "" },
{0X920F104A, &WrapU_V<sceKernelIcacheInvalidateAll>, "sceKernelIcacheInvalidateAll", 'x', "" }
};
static const HLEFunction LoadCoreForKernel[] = {
{0XACE23476, nullptr, "sceKernelCheckPspConfig", '?', "" },
{0X7BE1421C, nullptr, "sceKernelCheckExecFile", '?', "" },
{0XBF983EF2, nullptr, "sceKernelProbeExecutableObject", '?', "" },
{0X7068E6BA, nullptr, "sceKernelLoadExecutableObject", '?', "" },
{0XB4D6FECC, nullptr, "sceKernelApplyElfRelSection", '?', "" },
{0X54AB2675, nullptr, "sceKernelApplyPspRelSection", '?', "" },
{0X2952F5AC, nullptr, "sceKernelDcacheWBinvAll", '?', "" },
{0xD8779AC6, &WrapU_V<sceKernelIcacheClearAll>, "sceKernelIcacheClearAll", 'x', "", HLE_KERNEL_SYSCALL },
{0X99A695F0, nullptr, "sceKernelRegisterLibrary", '?', "" },
{0X5873A31F, nullptr, "sceKernelRegisterLibraryForUser", '?', "" },
{0X0B464512, nullptr, "sceKernelReleaseLibrary", '?', "" },
{0X9BAF90F6, nullptr, "sceKernelCanReleaseLibrary", '?', "" },
{0X0E760DBA, nullptr, "sceKernelLinkLibraryEntries", '?', "" },
{0X0DE1F600, nullptr, "sceKernelLinkLibraryEntriesForUser", '?', "" },
{0XDA1B09AA, nullptr, "sceKernelUnLinkLibraryEntries", '?', "" },
{0XC99DD47A, &WrapU_V<sceKernelQueryLoadCoreCB>, "sceKernelQueryLoadCoreCB", 'x', "", HLE_KERNEL_SYSCALL },
{0X616FCCCD, nullptr, "sceKernelSetBootCallbackLevel", '?', "" },
{0XF32A2940, nullptr, "sceKernelModuleFromUID", '?', "" },
{0XCD0F3BAC, nullptr, "sceKernelCreateModule", '?', "" },
{0X6B2371C2, nullptr, "sceKernelDeleteModule", '?', "" },
{0X7320D964, nullptr, "sceKernelModuleAssign", '?', "" },
{0X44B292AB, nullptr, "sceKernelAllocModule", '?', "" },
{0XBD61D4D5, nullptr, "sceKernelFreeModule", '?', "" },
{0XAE7C6E76, nullptr, "sceKernelRegisterModule", '?', "" },
{0X74CF001A, nullptr, "sceKernelReleaseModule", '?', "" },
{0XFB8AE27D, nullptr, "sceKernelFindModuleByAddress", '?', "" },
{0XCCE4A157, &WrapU_U<sceKernelFindModuleByUID>, "sceKernelFindModuleByUID", 'x', "x" , HLE_KERNEL_SYSCALL },
{0X82CE54ED, nullptr, "sceKernelModuleCount", '?', "" },
{0XC0584F0C, nullptr, "sceKernelGetModuleList", '?', "" },
{0XCF8A41B1, &WrapU_C<sceKernelFindModuleByName>, "sceKernelFindModuleByName", 'x', "s", HLE_KERNEL_SYSCALL },
{0XB95FA50D, nullptr, "LoadCoreForKernel_B95FA50D", '?', "" },
{0x696594C8, &WrapU_V<sceKernelQueryLoadCoreCB>, "sceKernelQueryLoadCoreCB660", 'x', "", HLE_KERNEL_SYSCALL },
};
// sceKernelSm1ReferOperations() returns a pointer to a driver-registered "SM1 operations"
// table (set up via the sibling sceKernelSm1RegisterOperations(), also unimplemented here),
// or NULL if nothing has registered one.
static u32 sceKernelSm1ReferOperations() {
return hleLogDebug(Log::sceKernel, 0);
}
// The kernel's debug printf. On hardware it formats the string and hands it to whatever
// sceKernelRegisterKprintfHandler() installed, which on a retail PSP means the serial port -
// so nothing a player ever sees, and games that call it are just leaving debug output in.
// We format it and put it in the log, which is the useful thing to do with it.
// Declared void in the SDK headers, so the return value doesn't matter, but 0 is what the
// firmware leaves in v0.
static u32 Kprintf(MIPSState *mips, u32 fmtAddr) {
std::string formatted;
if (!HLEFormatPrintf(mips, fmtAddr, 1, &formatted)) {
return hleLogError(Log::Printf, 0, "bad format string or args");
}
// Each log line is already a line, so don't double space.
if (!formatted.empty() && formatted.back() == '\n') {
formatted.pop_back();
}
return hleLogInfo(Log::Printf, 0, "\"%s\"", formatted.c_str());
}
static const HLEFunction KDebugForKernel[] = {
{0XE7A3874D, nullptr, "sceKernelRegisterAssertHandler", '?', "" },
{0X2FF4E9F9, nullptr, "sceKernelAssert", '?', "" },
{0X9B868276, nullptr, "sceKernelGetDebugPutchar", '?', "" },
{0XE146606D, nullptr, "sceKernelRegisterDebugPutchar", '?', "" },
{0X7CEB2C09, &WrapU_V<sceKernelRegisterKprintfHandler>, "sceKernelRegisterKprintfHandler", 'x', "", HLE_KERNEL_SYSCALL },
{0X84F370BC, &WrapU_U<Kprintf>, "Kprintf", 'x', "x", HLE_KERNEL_SYSCALL },
{0X5CE9838B, nullptr, "sceKernelDebugWrite", '?', "" },
{0X66253C4E, nullptr, "sceKernelRegisterDebugWrite", '?', "" },
{0XDBB5597F, nullptr, "sceKernelDebugRead", '?', "" },
{0XE6554FDA, nullptr, "sceKernelRegisterDebugRead", '?', "" },
{0XB9C643C9, nullptr, "sceKernelDebugEcho", '?', "" },
{0X7D1C74F0, nullptr, "sceKernelDebugEchoSet", '?', "" },
{0X24C32559, nullptr, "sceKernelDipsw", '?', "" },
{0XD636B827, nullptr, "sceKernelRemoveByDebugSection", '?', "" },
{0X5282DD5E, nullptr, "sceKernelDipswSet", '?', "" },
{0X9F8703E4, nullptr, "sceKernelDipswCpTime", '?', "" },
{0X333DCEC7, nullptr, "sceKernelSm1RegisterOperations", '?', "" },
{0XE892D9A1, &WrapU_V<sceKernelSm1ReferOperations>, "sceKernelSm1ReferOperations", 'x', "" },
{0XA126F497, nullptr, "KDebugForKernel_A126F497", '?', "" },
{0XB7251823, nullptr, "sceKernelAcceptMbogoSig", '?', "" },
};
static const HLEFunction pspeDebug[] = {
{0XDEADBEAF, nullptr, "pspeDebugWrite", '?', "" },
};
static const HLEModule moduleList[] = {
{"FakeSysCalls", ARRAY_SIZE(FakeSysCalls), FakeSysCalls},
{"UtilsForUser", ARRAY_SIZE(UtilsForUser), UtilsForUser},
{"KDebugForKernel", ARRAY_SIZE(KDebugForKernel), KDebugForKernel},
{"sceSAScore"},
{"SceBase64_Library"},
{"sceCert_Loader"},
{"SceFont_Library"},
{"sceNetApctl"},
{"sceSIRCS_IrDA_Driver"},
{"Pspnet_Scan"},
{"Pspnet_Show_MacAddr"},
{"pspeDebug", ARRAY_SIZE(pspeDebug), pspeDebug},
};
static const int numModules = ARRAY_SIZE(moduleList);
void RegisterAllModules() {
Register_Kernel_Library();
Register_ThreadManForUser();
Register_ThreadManForKernel();
Register_LoadExecForUser();
Register_UtilsForKernel();
Register_SysMemUserForUser();
Register_InterruptManager();
Register_IoFileMgrForUser();
Register_ModuleMgrForUser();
Register_ModuleMgrForKernel();
Register_StdioForUser();
Register_sceHprm();
Register_sceCcc();
Register_sceCtrl();
Register_sceDisplay();
Register_sceAudio();
Register_sceSasCore();
Register_sceFont();
Register_sceNet();
Register_sceNetResolver();
Register_sceNetInet();
Register_sceNetApctl();
Register_sceNetAdhoc();
Register_sceNetAdhocMatching();
Register_sceNetAdhocDiscover();
Register_sceNetAdhocctl();
Register_sceRtc();
Register_sceWlanDrv();
Register_sceMpeg();
Register_sceMp3();
Register_sceHttp();
Register_scePower();
Register_sceImpose();
Register_sceSuspendForUser();
Register_sceGe_user();
Register_sceUmdUser();
Register_sceDmac();
Register_sceUtility();
Register_sceAtrac3plus();
Register_scePsmf();
Register_scePsmfPlayer();
Register_sceOpenPSID();
Register_sceParseUri();
Register_sceSsl();
Register_sceParseHttp();
Register_sceVaudio();
Register_sceUsb();
Register_sceChnnlsv();
Register_sceNpDrm();
Register_sceP3da();
Register_sceGameUpdate();
Register_sceDeflt();
Register_sceMp4();
Register_sceAac();
Register_scePauth();
Register_sceNp();
Register_sceNpCommerce2();
Register_sceNpService();
Register_sceNpAuth();
Register_sceMd5();
Register_sceJpeg();
Register_sceAudiocodec();
Register_sceHeap();
for (int i = 0; i < numModules; i++) {
RegisterHLEModule(moduleList[i].name, moduleList[i].numFunctions, moduleList[i].funcTable);
}
// IMPORTANT: New modules have to be added at the end, or they will break savestates.
Register_StdioForKernel();
RegisterHLEModule("LoadCoreForKernel", ARRAY_SIZE(LoadCoreForKernel), LoadCoreForKernel);
Register_IoFileMgrForKernel();
Register_LoadExecForKernel();
Register_SysMemForKernel();
Register_sceMt19937();
Register_SysclibForKernel();
Register_sceCtrl_driver();
Register_sceDisplay_driver();
Register_sceMpegbase();
Register_sceUsbGps();
Register_sceLibFttt();
Register_sceSha256();
Register_sceAdler();
Register_sceSfmt19937();
Register_sceAudioRouting();
Register_sceUsbCam();
Register_sceG729();
Register_sceNetUpnp();
Register_sceNetIfhandle();
Register_KUBridge();
Register_sceUsbAcc();
Register_sceUsbMic();
Register_sceOpenPSID_driver();
Register_semaphore();
Register_sceDdrdb();
Register_mp4msv();
Register_InterruptManagerForKernel();
Register_sceSircs();
Register_sceNet_lib();
Register_sceReg();
Register_sceRtc_driver();
Register_scePower_driver();
Register_sceImpose_driver();
Register_sceHprm_driver();
Register_sceChkreg();
Register_sceVshBridge();
Register_sceResmgr();
Register_sceVideocodec();
// add new modules here.
Register_SystemCtrlForKernel();
Register_sceGe_driver();
Register_sceMePower_driver();
Register_SystemCtrlForUser();
Register_ExceptionManagerForKernel();
Register_sceSysreg_driver();
Register_sceSysEventForKernel();
Register_sceAmctrl_driver();
Register_InitForKernel();
// Not ready to enable this due to apparent softlocks in Patapon 3.
// Register_sceNpMatching2();
}