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

+13 -4
View File
@@ -28,6 +28,7 @@
#include "Core/ELF/PrxDecrypter.h"
#include "Core/FileSystems/MetaFileSystem.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/PopsBoot.h"
#include "Core/HLE/sceKernelInterrupt.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/HW/MeMMIO.h"
@@ -69,9 +70,10 @@ enum : u32 {
MEState *g_meState = nullptr;
static int meIntrEvent = -1;
// Booting the VSH counts as homebrew: vshmain.prx loads as an ELF.
bool Wanted(bool homebrew) {
return homebrew && g_Config.bEmulateMediaEngine;
// Booting the VSH counts as homebrew: vshmain.prx loads as an ELF. A PS1 game always gets the ME:
// the firmware's PS1 emulator plays its sound on it.
bool Wanted(bool homebrew, bool pops) {
return pops || (homebrew && g_Config.bEmulateMediaEngine);
}
static void MeIntrCallback(u64 userdata, int cyclesLate) {
@@ -124,7 +126,9 @@ bool Init() {
g_meState->tickBase = CoreTiming::GetTicks(&mipsr4k);
PowerOn(g_meState);
meIntrEvent = CoreTiming::RegisterEvent("MeIntr", &MeIntrCallback);
Memory::g_kernelModeAnywhere = true;
// Not for POPS, whose code that reaches hardware is all kernel code anyway. A savestate's POPS boot
// is known by now; a new one turns it off in KernelInit().
Memory::g_kernelModeAnywhere = !PopsBootActive();
INFO_LOG(Log::ME, "Media Engine created, %d KB eDRAM", space->edramSize / 1024);
return true;
}
@@ -260,6 +264,11 @@ static u32 CreateSysEventHandler(const char *name) {
void KernelInit() {
if (!g_meState)
return;
// The POPS manager boots the ME with its own code (POPS's reboot has no me_wrapper).
if (PopsBootActive()) {
Memory::g_kernelModeAnywhere = false;
return;
}
if (const u32 data = LoadFirmwareImage()) {
BootFirmware(g_meState, data);
g_meState->booting = true;