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>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-21 13:15:30 +02:00
1 parent 4f80c08fc9
commit 0b1e1a9bb3
27 files changed
+556 -10

No files matched your search

+29
View File
@@ -54,6 +54,7 @@
#include "Core/System.h"
#include "Core/PSPLoaders.h"
#include "Core/HLE/sceKernelModule.h"
#include "Core/HLE/PopsBoot.h"
static void UseLargeMem(int memsize) {
if (memsize != 1) {
@@ -658,6 +659,34 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string_view discId, bool load
return __KernelLoadExec(currentMIPS, finalName.c_str(), 0, error_string);
}
// A PS1 EBOOT, run by the firmware's POPS manager, which loads its PS1 emulator for the game.
bool Load_PSP_POPS(FileLoader *fileLoader, std::string *error_string) {
static const char *const popsmanPath = "flash0:/kd/popsman.prx";
if (!pspFileSystem.GetFileInfo(popsmanPath).exists) {
auto er = GetI18NCategory(I18NCat::ERRORS);
*error_string = er->T("PS1NoFirmware", "PS1 games need the PS1 emulator in the PSP's firmware, and no firmware is installed. Install firmware 6.60 or 6.61 from an official PSP update file in Settings > Tools > PSP Firmware. Starting a PSP game whose disc has an updater on it also installs its firmware.");
return false;
}
// Its directory as host0:, as for a homebrew EBOOT.
const Path fullPath = fileLoader->GetPath();
auto fs = std::make_shared<DirectoryFileSystem>(&pspFileSystem, fullPath.NavigateUp(), FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD);
pspFileSystem.Mount("host0:", fs);
pspFileSystem.SetStartingDirectory("host0:/");
__PopsBootInit();
const std::string gamePath = "host0:/" + fullPath.GetFilename();
INFO_LOG(Log::Loader, "POPS: booting %s through %s", gamePath.c_str(), popsmanPath);
if (!__KernelLoadExec(currentMIPS, popsmanPath, 0, error_string)) {
return false;
}
__PopsBootLoadModules();
if (!__PopsBootStartInit(fileLoader, gamePath, error_string)) {
return false;
}
return true;
}
bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string) {
auto umd = std::make_shared<BlobFileSystem>(&pspFileSystem, fileLoader, "data.ppdmp");
pspFileSystem.Mount("disc0:", umd);