Core: Adjust emulated memory size automatically.

Dolphin now uses the simulated memory size defined in `bi2.bin` to
adjust the memory size in the emulated console automatically.

If **Enable Emulated Memory Size Override** has been enabled by the
user, the fixed memory size specified for **MEM1** and **MEM2** are
still used as they were before.

Most retail games do not define the simulated memory size (Wii or
Triforce games), or define it to a value that matches the default 24 MiB
(GameCube games), so, in the general case, there is no behavior change.
One game that sets the simulated memory to a non-default value is the
debug build of _Mario Kart: Double Dash!!_, where the value is set to
48 MiB.

This enhancement is focused mainly to the modding community. Prior to
these changes, modded games with extended memory requirements would
fail to launch in Dolphin [with no indication of what the problem is] if
the user failed to set the emulated memory override to the correct
value. Now, modding tools can specify the simulated memory size in the
`bi2.bin` file to produce extended games that _just work_ in Dolphin,
without cumbersome instructions that can be overlooked by the user.
This commit is contained in:
cristian64 2026-04-03 10:58:54 +01:00
parent 03b1f2148b
commit 0cab1731d6
7 changed files with 29 additions and 4 deletions

View File

@ -140,7 +140,8 @@ bool FifoDataFile::Save(const std::string& filename)
header.fileId = FILE_ID;
header.file_version = VERSION_NUMBER;
// Maintain backwards compatibility so long as the RAM sizes aren't overridden.
if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE))
if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE) ||
SConfig::GetInstance().GetSimulatedMemorySize() > Memory::MEM1_SIZE_RETAIL)
header.min_loader_version = MIN_LOADER_VERSION_FOR_RAM_OVERRIDE;
else
header.min_loader_version = MIN_LOADER_VERSION;

View File

@ -25,6 +25,7 @@
#include "Common/MsgHandler.h"
#include "Common/Swap.h"
#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/HW/AudioInterface.h"
#include "Core/HW/DSP.h"
@ -87,10 +88,18 @@ void MemoryManager::InitMMIO(Core::System& system)
void MemoryManager::Init()
{
const auto get_mem1_size = [] {
const auto get_mem1_size = [this] {
if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE))
return Config::Get(Config::MAIN_MEM1_SIZE);
return Memory::MEM1_SIZE_RETAIL;
// The simulated memory size in the game's header was originally used to ask the Apploader to
// simulate a smaller memory size than that in development systems. That is, it could make the
// 48 MiB devkit act like a 24 MiB retail console during testing, or any other config.
//
// Dolphin is reusing this as a signal to automatically adjust the emulated memory size. The
// upper bound is clamped to 64 MiB (maximum supported by Flipper's memory controller),
// reflecting Apploader behaviour of clamping the simulated size to physical memory size. It is
// never desired to emulate less than a retail console, so the lower bound is clapped to 24 MiB.
return std::clamp(m_system.GetSimulatedMemorySize(), MEM1_SIZE_RETAIL, MEM1_SIZE_MAX);
};
const auto get_mem2_size = [] {
if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE))

View File

@ -34,8 +34,10 @@ constexpr u32 MEM1_BASE_ADDR = 0x80000000U;
constexpr u32 MEM2_BASE_ADDR = 0x90000000U;
constexpr u32 MEM1_SIZE_RETAIL = 0x01800000U;
constexpr u32 MEM1_SIZE_GDEV = 0x04000000U;
constexpr u32 MEM1_SIZE_MAX = 0x04000000U;
constexpr u32 MEM2_SIZE_RETAIL = 0x04000000U;
constexpr u32 MEM2_SIZE_NDEV = 0x08000000U;
constexpr u32 MEM2_SIZE_MAX = 0x08000000U;
struct PhysicalMemoryRegion
{

View File

@ -221,8 +221,11 @@ static void ReleasePPCAncast(Core::System& system)
void RAMOverrideForIOSMemoryValues(Memory::MemoryManager& memory, MemorySetupType setup_type)
{
// Don't touch anything if the feature isn't enabled.
if (!Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE))
if (!Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE) ||
SConfig::GetInstance().GetSimulatedMemorySize() <= Memory::MEM1_SIZE_RETAIL)
{
return;
}
// Some unstated constants that can be inferred.
const u32 ipc_buffer_size =

View File

@ -119,6 +119,7 @@ void System::Initialize()
m_separate_cpu_and_gpu_threads = Config::Get(Config::MAIN_CPU_THREAD);
m_mmu_enabled = Config::Get(Config::MAIN_MMU);
m_pause_on_panic_enabled = Config::Get(Config::MAIN_PAUSE_ON_PANIC);
m_simulated_memory_size = SConfig::GetInstance().GetSimulatedMemorySize();
}
SoundStream* System::GetSoundStream() const

View File

@ -5,6 +5,8 @@
#include <memory>
#include "Common/CommonTypes.h"
class GeometryShaderManager;
class Interpreter;
class JitInterface;
@ -147,6 +149,7 @@ public:
bool IsTriforce() const { return m_is_triforce; }
bool IsWii() const { return m_is_wii; }
bool IsBranchWatchIgnoreApploader() { return m_branch_watch_ignore_apploader; }
u32 GetSimulatedMemorySize() const { return m_simulated_memory_size; }
void SetIsMIOS(bool is_mios) { m_is_mios = is_mios; }
void SetIsTriforce(bool is_triforce) { m_is_triforce = is_triforce; }
@ -216,5 +219,6 @@ private:
bool m_is_triforce = false;
bool m_is_wii = false;
bool m_branch_watch_ignore_apploader = false;
u32 m_simulated_memory_size{0};
};
} // namespace Core

View File

@ -10,6 +10,7 @@
#include "Common/Align.h"
#include "Common/CommonTypes.h"
#include "Common/Swap.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/MemTools.h"
#include "Core/PowerPC/BreakPoints.h"
@ -131,6 +132,8 @@ public:
if (!EMM::IsExceptionHandlerSupported())
GTEST_SKIP() << "Skipping PageTableHostMappingTest because exception handler is unsupported.";
SConfig::Init();
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
const u32 host_page_size = memory.GetHostPageSize();
@ -177,6 +180,8 @@ public:
EMM::UninstallExceptionHandler();
Core::UndeclareAsCPUThread();
system.GetMemory().Shutdown();
SConfig::Shutdown();
}
static void SetSR(size_t index, u32 vsid)