Map the PSP-2000 extra RAM for homebrew PBPs, outside the user partition

Homebrew written for a PSP-2000+ under custom firmware can use the top
32MB of RAM directly without setting MEMSIZE, which leaves the user
partition at its normal size. NJEMU's slim builds do this (#8925).
Previously we didn't map that memory at all for PBPs; MEMSIZE=1 isn't a
workaround either, since it grows the partition and the heap and stacks
land where the program writes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-09-29 12:40:59 -06:00
1 parent c9a631c336
commit 3062ffd695
5 files changed
+29 -3

No files matched your search

+3 -2
View File
@@ -321,7 +321,7 @@ void __KernelMemoryInit()
{
MemBlockInfoInit();
kernelMemory.Init(PSP_GetKernelMemoryBase(), PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase(), false);
userMemory.Init(PSP_GetUserMemoryBase(), PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase(), false);
userMemory.Init(PSP_GetUserMemoryBase(), PSP_GetUserPartitionEnd() - PSP_GetUserMemoryBase(), false);
volatileMemory.Init(PSP_GetVolatileMemoryStart(), PSP_GetVolatileMemoryEnd() - PSP_GetVolatileMemoryStart(), false);
Memory::Memset(PSP_GetKernelMemoryBase(), 0, PSP_GetKernelMemoryEnd() - PSP_GetKernelMemoryBase());
@@ -1712,7 +1712,8 @@ static u32 SysMemUserForUser_D8DE5C1E() {
// Real name per uofw's src/kd/sysmem/exports.exp: sceKernelTotalMemSize - the total size (not
// free size, see sceKernelMaxFreeMemSize above) of the user memory partition.
static u32 sceKernelTotalMemSize() {
return hleLogDebug(Log::sceKernel, PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase());
// The partition's own range, which survives savestates (see PSP_GetUserPartitionEnd).
return hleLogDebug(Log::sceKernel, userMemory.GetRangeSize());
}
static u32 SysMemUserForUser_945E45DA() {
+1
View File
@@ -84,6 +84,7 @@ static u8 *m_pUncachedVRAM[4];
u32 g_MemorySize;
// Used to store the PSP model on game startup.
u32 g_PSPModel;
u32 g_UserPartitionSize;
static MemMapSetupFlags g_setupFlags;
+5
View File
@@ -83,6 +83,9 @@ extern u8 *base;
// This replaces RAM_NORMAL_SIZE at runtime.
extern u32 g_MemorySize;
extern u32 g_PSPModel;
// Size of RAM (from 0x08000000) that the user partition may extend into. 0 means all of g_MemorySize.
// Smaller only for homebrew on a PSP-2000+ that doesn't ask for the extra RAM (see CPU_Init).
extern u32 g_UserPartitionSize;
// UWP has such limited memory management that we need to mask
// even in 64-bit mode. Also, when using the sanitizer, we need to mask as well.
@@ -673,6 +676,8 @@ constexpr u32 PSP_GetScratchpadMemoryEnd() { return 0x00014000;}
constexpr u32 PSP_GetKernelMemoryBase() { return 0x08000000;}
inline u32 PSP_GetUserMemoryEnd() { return PSP_GetKernelMemoryBase() + Memory::g_MemorySize;}
// Where the user memory partition ends. Usually PSP_GetUserMemoryEnd(), which is the end of mapped RAM.
inline u32 PSP_GetUserPartitionEnd() { return PSP_GetKernelMemoryBase() + (Memory::g_UserPartitionSize ? Memory::g_UserPartitionSize : Memory::g_MemorySize); }
constexpr u32 PSP_GetKernelMemoryEnd() { return 0x08400000;}
// "Volatile" RAM is between 0x08400000 and 0x08800000, can be requested by the
+19 -1
View File
@@ -345,6 +345,7 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
// Default memory settings
// Seems to be the safest place currently..
Memory::g_MemorySize = Memory::RAM_NORMAL_SIZE; // 32 MB of ram by default
Memory::g_UserPartitionSize = 0;
g_RemasterMode = false;
g_DoubleTextureCoordinates = false;
@@ -396,10 +397,27 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin
case IdentifiedFileType::PSP_PBP_DIRECTORY:
// This is normal for homebrew.
// ERROR_LOG(Log::Loader, "PBP directory resolution failed.");
if (LoadParamSFOFromPBP(fileLoader)) {
{
const bool hasSFO = LoadParamSFOFromPBP(fileLoader);
if (hasSFO) {
InitMemorySizeForGame();
}
// Homebrew written for a PSP-2000+ under custom firmware (e.g. 3.71 M33) can use the top
// 32MB of RAM (0x0A000000-0x0C000000) directly, without MEMSIZE=1 in its PARAM.SFO. There
// the user partition stays at its normal size, so the heap and thread stacks stay below
// 0x0A000000 and the top of RAM is left to the program. NJEMU's slim builds (MVSPSP, CPS2PSP)
// work this way (#8925). Map the memory, but don't grow the partition into it: MEMSIZE=1
// would, and the program would then overwrite its own heap and stacks.
// Homebrew is identified by the MG ("memory stick game") category, or no PARAM.SFO at all.
// (g_paramSFO only gets a few keys copied over for a PBP, so the category is in the raw copy.)
const bool homebrew = !hasSFO || g_paramSFORaw.GetValueString("CATEGORY") == "MG";
if (homebrew && Memory::g_PSPModel != PSP_MODEL_FAT && Memory::g_MemorySize == Memory::RAM_NORMAL_SIZE) {
INFO_LOG(Log::Loader, "Homebrew PBP: mapping the PSP-2000 extra RAM, outside the user partition");
Memory::g_MemorySize = Memory::RAM_DOUBLE_SIZE;
Memory::g_UserPartitionSize = Memory::RAM_NORMAL_SIZE;
}
break;
}
case IdentifiedFileType::PSP_ELF:
if (Memory::g_PSPModel != PSP_MODEL_FAT) {
INFO_LOG(Log::Loader, "ELF, using full PSP-2000 memory access");
+1
View File
@@ -53,6 +53,7 @@ public:
u32 GetBlockSizeFromAddress(u32 addr) const;
u32 GetLargestFreeBlockSize() const;
u32 GetTotalFreeBytes() const;
u32 GetRangeSize() const { return rangeSize_; }
const char *GetBlockTag(u32 addr) const;