@@ -193,8 +193,8 @@ void Reinit(bool hle)
if (SConfig::GetInstance().bWii)
{
s_ARAM.wii_mode = true;
s_ARAM.size = Memory::EXRAM_SIZE;
s_ARAM.mask = Memory::EXRAM_MASK;
s_ARAM.size = Memory::GetExRamSizeReal();
s_ARAM.mask = Memory::GetExRamMask();
s_ARAM.ptr = Memory::m_pEXRAM;
}
else
@@ -589,7 +589,7 @@ u8 ReadARAM(u32 address)
if (address & 0x10000000)
return s_ARAM.ptr[address & s_ARAM.mask];
else
return Memory::Read_U8(address & Memory::RAM_MASK);
return Memory::Read_U8(address & Memory::GetRamMask());
}
else
{
@@ -39,27 +39,27 @@ constexpr bool ExramRead(u32 address)
u8 HLEMemory_Read_U8(u32 address)
{
if (ExramRead(address))
return Memory::m_pEXRAM[address & Memory::EXRAM_MASK];
return Memory::m_pEXRAM[address & Memory::GetExRamMask()];

return Memory::m_pRAM[address & Memory::RAM_MASK];
return Memory::m_pRAM[address & Memory::GetRamMask()];
}

void HLEMemory_Write_U8(u32 address, u8 value)
{
if (ExramRead(address))
Memory::m_pEXRAM[address & Memory::EXRAM_MASK] = value;
Memory::m_pEXRAM[address & Memory::GetExRamMask()] = value;
else
Memory::m_pRAM[address & Memory::RAM_MASK] = value;
Memory::m_pRAM[address & Memory::GetRamMask()] = value;
}

u16 HLEMemory_Read_U16LE(u32 address)
{
u16 value;

if (ExramRead(address))
std::memcpy(&value, &Memory::m_pEXRAM[address & Memory::EXRAM_MASK], sizeof(u16));
std::memcpy(&value, &Memory::m_pEXRAM[address & Memory::GetExRamMask()], sizeof(u16));
else
std::memcpy(&value, &Memory::m_pRAM[address & Memory::RAM_MASK], sizeof(u16));
std::memcpy(&value, &Memory::m_pRAM[address & Memory::GetRamMask()], sizeof(u16));

return value;
}
@@ -72,9 +72,9 @@ u16 HLEMemory_Read_U16(u32 address)
void HLEMemory_Write_U16LE(u32 address, u16 value)
{
if (ExramRead(address))
std::memcpy(&Memory::m_pEXRAM[address & Memory::EXRAM_MASK], &value, sizeof(u16));
std::memcpy(&Memory::m_pEXRAM[address & Memory::GetExRamMask()], &value, sizeof(u16));
else
std::memcpy(&Memory::m_pRAM[address & Memory::RAM_MASK], &value, sizeof(u16));
std::memcpy(&Memory::m_pRAM[address & Memory::GetRamMask()], &value, sizeof(u16));
}

void HLEMemory_Write_U16(u32 address, u16 value)
@@ -87,9 +87,9 @@ u32 HLEMemory_Read_U32LE(u32 address)
u32 value;

if (ExramRead(address))
std::memcpy(&value, &Memory::m_pEXRAM[address & Memory::EXRAM_MASK], sizeof(u32));
std::memcpy(&value, &Memory::m_pEXRAM[address & Memory::GetExRamMask()], sizeof(u32));
else
std::memcpy(&value, &Memory::m_pRAM[address & Memory::RAM_MASK], sizeof(u32));
std::memcpy(&value, &Memory::m_pRAM[address & Memory::GetRamMask()], sizeof(u32));

return value;
}
@@ -102,9 +102,9 @@ u32 HLEMemory_Read_U32(u32 address)
void HLEMemory_Write_U32LE(u32 address, u32 value)
{
if (ExramRead(address))
std::memcpy(&Memory::m_pEXRAM[address & Memory::EXRAM_MASK], &value, sizeof(u32));
std::memcpy(&Memory::m_pEXRAM[address & Memory::GetExRamMask()], &value, sizeof(u32));
else
std::memcpy(&Memory::m_pRAM[address & Memory::RAM_MASK], &value, sizeof(u32));
std::memcpy(&Memory::m_pRAM[address & Memory::GetRamMask()], &value, sizeof(u32));
}

void HLEMemory_Write_U32(u32 address, u32 value)
@@ -115,9 +115,9 @@ void HLEMemory_Write_U32(u32 address, u32 value)
void* HLEMemory_Get_Pointer(u32 address)
{
if (ExramRead(address))
return &Memory::m_pEXRAM[address & Memory::EXRAM_MASK];
return &Memory::m_pEXRAM[address & Memory::GetExRamMask()];

return &Memory::m_pRAM[address & Memory::RAM_MASK];
return &Memory::m_pRAM[address & Memory::GetRamMask()];
}

UCodeInterface::UCodeInterface(DSPHLE* dsphle, u32 crc)
@@ -10,6 +10,7 @@
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/HW/AddressSpace.h"
#include "Core/HW/AudioInterface.h"
#include "Core/HW/CPU.h"
#include "Core/HW/DSP.h"
@@ -41,7 +42,8 @@ void Init()
SerialInterface::Init();
ProcessorInterface::Init();
ExpansionInterface::Init(); // Needs to be initialized before Memory
Memory::Init();
Memory::Init(); // Needs to be initialized before AddressSpace
AddressSpace::Init();
DSP::Init(SConfig::GetInstance().bDSPHLE);
DVDInterface::Init();
GPFifo::Init();
@@ -10,6 +10,7 @@
#include "Core/HW/Memmap.h"

#include <algorithm>
#include <array>
#include <cstring>
#include <memory>

@@ -18,6 +19,7 @@
#include "Common/Logging/Log.h"
#include "Common/MemArena.h"
#include "Common/Swap.h"
#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/HW/AudioInterface.h"
#include "Core/HW/DSP.h"
@@ -57,6 +59,70 @@ u8* m_pL1Cache;
u8* m_pEXRAM;
u8* m_pFakeVMEM;

// s_ram_size is the amount allocated by the emulator, whereas s_ram_size_real
// is what will be reported in lowmem, and thus used by emulated software.
// Note: Writing to lowmem is done by IPL. If using retail IPL, it will
// always be set to 24MB.
static u32 s_ram_size_real;
static u32 s_ram_size;
static u32 s_ram_mask;
static u32 s_fakevmem_size;
static u32 s_fakevmem_mask;
static u32 s_L1_cache_size;
static u32 s_L1_cache_mask;
static u32 s_io_size;
// s_exram_size is the amount allocated by the emulator, whereas s_exram_size_real
// is what gets used by emulated software. If using retail IOS, it will
// always be set to 64MB.
static u32 s_exram_size_real;
static u32 s_exram_size;
static u32 s_exram_mask;

u32 GetRamSizeReal()
{
return s_ram_size_real;
}
u32 GetRamSize()
{
return s_ram_size;
}
u32 GetRamMask()
{
return s_ram_mask;
}
u32 GetFakeVMemSize()
{
return s_fakevmem_size;
}
u32 GetFakeVMemMask()
{
return s_fakevmem_mask;
}
u32 GetL1CacheSize()
{
return s_L1_cache_size;
}
u32 GetL1CacheMask()
{
return s_L1_cache_mask;
}
u32 GetIOSize()
{
return s_io_size;
}
u32 GetExRamSizeReal()
{
return s_exram_size_real;
}
u32 GetExRamSize()
{
return s_exram_size;
}
u32 GetExRamMask()
{
return s_exram_mask;
}

// MMIO mapping object.
std::unique_ptr<MMIO::Mapping> mmio_mapping;

@@ -154,15 +220,9 @@ struct LogicalMemoryView
//
// Dolphin doesn't emulate the difference between cached and uncached access.
//
// TODO: The actual size of RAM is REALRAM_SIZE (24MB); the other 8MB shouldn't
// be backed by actual memory.
// TODO: The actual size of RAM is 24MB; the other 8MB shouldn't be backed by actual memory.
// TODO: Do we want to handle the mirrors of the GC RAM?
static PhysicalMemoryRegion physical_regions[] = {
{&m_pRAM, 0x00000000, RAM_SIZE, PhysicalMemoryRegion::ALWAYS},
{&m_pL1Cache, 0xE0000000, L1_CACHE_SIZE, PhysicalMemoryRegion::ALWAYS},
{&m_pFakeVMEM, 0x7E000000, FAKEVMEM_SIZE, PhysicalMemoryRegion::FAKE_VMEM},
{&m_pEXRAM, 0x10000000, EXRAM_SIZE, PhysicalMemoryRegion::WII_ONLY},
};
static std::array<PhysicalMemoryRegion, 4> physical_regions;

static std::vector<LogicalMemoryView> logical_mapped_entries;

@@ -189,6 +249,34 @@ static u32 GetFlags()

void Init()
{
const auto get_mem1_size = [] {
if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE))
return Config::Get(Config::MAIN_MEM1_SIZE);
return Memory::MEM1_SIZE_RETAIL;
};
const auto get_mem2_size = [] {
if (Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE))
return Config::Get(Config::MAIN_MEM2_SIZE);
return Memory::MEM2_SIZE_RETAIL;
};
s_ram_size_real = get_mem1_size();
s_ram_size = MathUtil::NextPowerOf2(GetRamSizeReal());
s_ram_mask = GetRamSize() - 1;
s_fakevmem_size = 0x02000000;
s_fakevmem_mask = GetFakeVMemSize() - 1;
s_L1_cache_size = 0x00040000;
s_L1_cache_mask = GetL1CacheSize() - 1;
s_io_size = 0x00010000;
s_exram_size_real = get_mem2_size();
s_exram_size = MathUtil::NextPowerOf2(GetExRamSizeReal());
s_exram_mask = GetExRamSize() - 1;

physical_regions[0] = {&m_pRAM, 0x00000000, GetRamSize(), PhysicalMemoryRegion::ALWAYS};
physical_regions[1] = {&m_pL1Cache, 0xE0000000, GetL1CacheSize(), PhysicalMemoryRegion::ALWAYS};
physical_regions[2] = {&m_pFakeVMEM, 0x7E000000, GetFakeVMemSize(),
PhysicalMemoryRegion::FAKE_VMEM};
physical_regions[3] = {&m_pEXRAM, 0x10000000, GetExRamSize(), PhysicalMemoryRegion::WII_ONLY};

bool wii = SConfig::GetInstance().bWii;
u32 flags = GetFlags();
u32 mem_size = 0;
@@ -304,14 +392,14 @@ void UpdateLogicalMemory(const PowerPC::BatTable& dbat_table)
void DoState(PointerWrap& p)
{
bool wii = SConfig::GetInstance().bWii;
p.DoArray(m_pRAM, RAM_SIZE);
p.DoArray(m_pL1Cache, L1_CACHE_SIZE);
p.DoArray(m_pRAM, GetRamSize());
p.DoArray(m_pL1Cache, GetL1CacheSize());
p.DoMarker("Memory RAM");
if (m_pFakeVMEM)
p.DoArray(m_pFakeVMEM, FAKEVMEM_SIZE);
p.DoArray(m_pFakeVMEM, GetFakeVMemSize());
p.DoMarker("Memory FakeVMEM");
if (wii)
p.DoArray(m_pEXRAM, EXRAM_SIZE);
p.DoArray(m_pEXRAM, GetExRamSize());
p.DoMarker("Memory EXRAM");
}

@@ -363,19 +451,19 @@ void ShutdownFastmemArena()
void Clear()
{
if (m_pRAM)
memset(m_pRAM, 0, RAM_SIZE);
memset(m_pRAM, 0, GetRamSize());
if (m_pL1Cache)
memset(m_pL1Cache, 0, L1_CACHE_SIZE);
memset(m_pL1Cache, 0, GetL1CacheSize());
if (m_pFakeVMEM)
memset(m_pFakeVMEM, 0, FAKEVMEM_SIZE);
memset(m_pFakeVMEM, 0, GetFakeVMemSize());
if (m_pEXRAM)
memset(m_pEXRAM, 0, EXRAM_SIZE);
memset(m_pEXRAM, 0, GetExRamSize());
}

static inline u8* GetPointerForRange(u32 address, size_t size)
{
// Make sure we don't have a range spanning 2 separate banks
if (size >= EXRAM_SIZE)
if (size >= GetExRamSizeReal())
return nullptr;

// Check that the beginning and end of the range are valid
@@ -450,13 +538,13 @@ u8* GetPointer(u32 address)
// TODO: Should we be masking off more bits here? Can all devices access
// EXRAM?
address &= 0x3FFFFFFF;
if (address < REALRAM_SIZE)
if (address < GetRamSizeReal())
return m_pRAM + address;

if (m_pEXRAM)
{
if ((address >> 28) == 0x1 && (address & 0x0fffffff) < EXRAM_SIZE)
return m_pEXRAM + (address & EXRAM_MASK);
if ((address >> 28) == 0x1 && (address & 0x0fffffff) < GetExRamSizeReal())
return m_pEXRAM + (address & GetExRamMask());
}

PanicAlert("Unknown Pointer 0x%08x PC 0x%08x LR 0x%08x", address, PC, LR);
@@ -37,23 +37,24 @@ extern u8* m_pEXRAM;
extern u8* m_pL1Cache;
extern u8* m_pFakeVMEM;

enum
{
// RAM_SIZE is the amount allocated by the emulator, whereas REALRAM_SIZE is
// what will be reported in lowmem, and thus used by emulated software.
// Note: Writing to lowmem is done by IPL. If using retail IPL, it will
// always be set to 24MB.
REALRAM_SIZE = 0x01800000,
RAM_SIZE = MathUtil::NextPowerOf2(REALRAM_SIZE),
RAM_MASK = RAM_SIZE - 1,
FAKEVMEM_SIZE = 0x02000000,
FAKEVMEM_MASK = FAKEVMEM_SIZE - 1,
L1_CACHE_SIZE = 0x00040000,
L1_CACHE_MASK = L1_CACHE_SIZE - 1,
IO_SIZE = 0x00010000,
EXRAM_SIZE = 0x04000000,
EXRAM_MASK = EXRAM_SIZE - 1,
};
u32 GetRamSizeReal();
u32 GetRamSize();
u32 GetRamMask();
u32 GetFakeVMemSize();
u32 GetFakeVMemMask();
u32 GetL1CacheSize();
u32 GetL1CacheMask();
u32 GetIOSize();
u32 GetExRamSizeReal();
u32 GetExRamSize();
u32 GetExRamMask();

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 MEM2_SIZE_RETAIL = 0x04000000U;
constexpr u32 MEM2_SIZE_NDEV = 0x08000000U;

// MMIO mapping object.
extern std::unique_ptr<MMIO::Mapping> mmio_mapping;
@@ -22,6 +22,7 @@
#include "Core/Boot/DolReader.h"
#include "Core/Boot/ElfReader.h"
#include "Core/CommonTitles.h"
#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
@@ -85,8 +86,8 @@ constexpr u32 ADDR_HOLLYWOOD_REVISION = 0x3138;
constexpr u32 ADDR_PH3 = 0x313c;
constexpr u32 ADDR_IOS_VERSION = 0x3140;
constexpr u32 ADDR_IOS_DATE = 0x3144;
constexpr u32 ADDR_UNKNOWN_BEGIN = 0x3148;
constexpr u32 ADDR_UNKNOWN_END = 0x314c;
constexpr u32 ADDR_IOS_RESERVED_BEGIN = 0x3148;
constexpr u32 ADDR_IOS_RESERVED_END = 0x314c;
constexpr u32 ADDR_PH4 = 0x3150;
constexpr u32 ADDR_PH5 = 0x3154;
constexpr u32 ADDR_RAM_VENDOR = 0x3158;
@@ -96,12 +97,6 @@ constexpr u32 ADDR_DEVKIT_BOOT_PROGRAM_VERSION = 0x315e;
constexpr u32 ADDR_SYSMENU_SYNC = 0x3160;
constexpr u32 PLACEHOLDER = 0xDEADBEEF;

enum class MemorySetupType
{
IOSReload,
Full,
};

static bool SetupMemory(u64 ios_title_id, MemorySetupType setup_type)
{
auto target_imv = std::find_if(
@@ -134,8 +129,10 @@ static bool SetupMemory(u64 ios_title_id, MemorySetupType setup_type)
Memory::Write_U32(target_imv->mem2_arena_end, ADDR_MEM2_ARENA_END);
Memory::Write_U32(target_imv->ipc_buffer_begin, ADDR_IPC_BUFFER_BEGIN);
Memory::Write_U32(target_imv->ipc_buffer_end, ADDR_IPC_BUFFER_END);
Memory::Write_U32(target_imv->unknown_begin, ADDR_UNKNOWN_BEGIN);
Memory::Write_U32(target_imv->unknown_end, ADDR_UNKNOWN_END);
Memory::Write_U32(target_imv->ios_reserved_begin, ADDR_IOS_RESERVED_BEGIN);
Memory::Write_U32(target_imv->ios_reserved_end, ADDR_IOS_RESERVED_END);

RAMOverrideForIOSMemoryValues(setup_type);

return true;
}
@@ -158,18 +155,68 @@ static bool SetupMemory(u64 ios_title_id, MemorySetupType setup_type)
Memory::Write_U32(PLACEHOLDER, ADDR_PH3);
Memory::Write_U32(target_imv->ios_version, ADDR_IOS_VERSION);
Memory::Write_U32(target_imv->ios_date, ADDR_IOS_DATE);
Memory::Write_U32(target_imv->unknown_begin, ADDR_UNKNOWN_BEGIN);
Memory::Write_U32(target_imv->unknown_end, ADDR_UNKNOWN_END);
Memory::Write_U32(target_imv->ios_reserved_begin, ADDR_IOS_RESERVED_BEGIN);
Memory::Write_U32(target_imv->ios_reserved_end, ADDR_IOS_RESERVED_END);
Memory::Write_U32(PLACEHOLDER, ADDR_PH4);
Memory::Write_U32(PLACEHOLDER, ADDR_PH5);
Memory::Write_U32(target_imv->ram_vendor, ADDR_RAM_VENDOR);
Memory::Write_U8(0xDE, ADDR_BOOT_FLAG);
Memory::Write_U8(0xAD, ADDR_APPLOADER_FLAG);
Memory::Write_U16(0xBEEF, ADDR_DEVKIT_BOOT_PROGRAM_VERSION);
Memory::Write_U32(target_imv->sysmenu_sync, ADDR_SYSMENU_SYNC);

RAMOverrideForIOSMemoryValues(setup_type);

return true;
}

void RAMOverrideForIOSMemoryValues(MemorySetupType setup_type)
{
// Don't touch anything if the feature isn't enabled.
if (!Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE))
return;

// Some unstated constants that can be inferred.
const u32 ipc_buffer_size =
Memory::Read_U32(ADDR_IPC_BUFFER_END) - Memory::Read_U32(ADDR_IPC_BUFFER_BEGIN);
const u32 ios_reserved_size =
Memory::Read_U32(ADDR_IOS_RESERVED_END) - Memory::Read_U32(ADDR_IOS_RESERVED_BEGIN);

const u32 mem1_physical_size = Memory::GetRamSizeReal();
const u32 mem1_simulated_size = Memory::GetRamSizeReal();
const u32 mem1_end = Memory::MEM1_BASE_ADDR + mem1_simulated_size;
const u32 mem1_arena_begin = 0;
const u32 mem1_arena_end = mem1_end;
const u32 mem2_physical_size = Memory::GetExRamSizeReal();
const u32 mem2_simulated_size = Memory::GetExRamSizeReal();
const u32 mem2_end = Memory::MEM2_BASE_ADDR + mem2_simulated_size - ios_reserved_size;
const u32 mem2_arena_begin = Memory::MEM2_BASE_ADDR + 0x800U;
const u32 mem2_arena_end = mem2_end - ipc_buffer_size;
const u32 ipc_buffer_begin = mem2_arena_end;
const u32 ipc_buffer_end = mem2_end;
const u32 ios_reserved_begin = mem2_end;
const u32 ios_reserved_end = Memory::MEM2_BASE_ADDR + mem2_simulated_size;

if (setup_type == MemorySetupType::Full)
{
// Overwriting these after the game's apploader sets them would be bad
Memory::Write_U32(mem1_physical_size, ADDR_MEM1_SIZE);
Memory::Write_U32(mem1_simulated_size, ADDR_MEM1_SIM_SIZE);
Memory::Write_U32(mem1_end, ADDR_MEM1_END);
Memory::Write_U32(mem1_arena_begin, ADDR_MEM1_ARENA_BEGIN);
Memory::Write_U32(mem1_arena_end, ADDR_MEM1_ARENA_END);
}
Memory::Write_U32(mem2_physical_size, ADDR_MEM2_SIZE);
Memory::Write_U32(mem2_simulated_size, ADDR_MEM2_SIM_SIZE);
Memory::Write_U32(mem2_end, ADDR_MEM2_END);
Memory::Write_U32(mem2_arena_begin, ADDR_MEM2_ARENA_BEGIN);
Memory::Write_U32(mem2_arena_end, ADDR_MEM2_ARENA_END);
Memory::Write_U32(ipc_buffer_begin, ADDR_IPC_BUFFER_BEGIN);
Memory::Write_U32(ipc_buffer_end, ADDR_IPC_BUFFER_END);
Memory::Write_U32(ios_reserved_begin, ADDR_IOS_RESERVED_BEGIN);
Memory::Write_U32(ios_reserved_end, ADDR_IOS_RESERVED_END);
}

void WriteReturnValue(s32 value, u32 address)
{
Memory::Write_U32(static_cast<u32>(value), address);
@@ -55,6 +55,14 @@ enum IPCCommandType : u32
IPC_REPLY = 8,
};

enum class MemorySetupType
{
IOSReload,
Full,
};

void RAMOverrideForIOSMemoryValues(MemorySetupType setup_type);

void WriteReturnValue(s32 value, u32 address);

// HLE for the IOS kernel: IPC, device management, syscalls, and Dolphin-specific, IOS-wide calls.
@@ -32,7 +32,7 @@ static void ReinitHardware()
SConfig::GetInstance().bWii = false;

// IOS clears mem2 and overwrites it with pseudo-random data (for security).
std::memset(Memory::m_pEXRAM, 0, Memory::EXRAM_SIZE);
std::memset(Memory::m_pEXRAM, 0, Memory::GetExRamSizeReal());
// MIOS appears to only reset the DI and the PPC.
// HACK However, resetting DI will reset the DTK config, which is set by the system menu
// (and not by MIOS), causing games that use DTK to break. Perhaps MIOS doesn't actually
@@ -29,8 +29,8 @@ struct MemoryValues
u32 ipc_buffer_end;
u32 hollywood_revision;
u32 ram_vendor;
u32 unknown_begin;
u32 unknown_end;
u32 ios_reserved_begin;
u32 ios_reserved_end;
u32 sysmenu_sync;
};

@@ -211,22 +211,22 @@ static T ReadFromHardware(u32 em_address)
{
// Handle RAM; the masking intentionally discards bits (essentially creating
// mirrors of memory).
// TODO: Only the first REALRAM_SIZE is supposed to be backed by actual memory.
// TODO: Only the first GetRamSizeReal() is supposed to be backed by actual memory.
T value;
std::memcpy(&value, &Memory::m_pRAM[em_address & Memory::RAM_MASK], sizeof(T));
std::memcpy(&value, &Memory::m_pRAM[em_address & Memory::GetRamMask()], sizeof(T));
return bswap(value);
}

if (Memory::m_pEXRAM && (em_address >> 28) == 0x1 &&
(em_address & 0x0FFFFFFF) < Memory::EXRAM_SIZE)
(em_address & 0x0FFFFFFF) < Memory::GetExRamSizeReal())
{
T value;
std::memcpy(&value, &Memory::m_pEXRAM[em_address & 0x0FFFFFFF], sizeof(T));
return bswap(value);
}

// Locked L1 technically doesn't have a fixed address, but games all use 0xE0000000.
if ((em_address >> 28) == 0xE && (em_address < (0xE0000000 + Memory::L1_CACHE_SIZE)))
if ((em_address >> 28) == 0xE && (em_address < (0xE0000000 + Memory::GetL1CacheSize())))
{
T value;
std::memcpy(&value, &Memory::m_pL1Cache[em_address & 0x0FFFFFFF], sizeof(T));
@@ -238,7 +238,7 @@ static T ReadFromHardware(u32 em_address)
if (Memory::m_pFakeVMEM && ((em_address & 0xFE000000) == 0x7E000000))
{
T value;
std::memcpy(&value, &Memory::m_pFakeVMEM[em_address & Memory::RAM_MASK], sizeof(T));
std::memcpy(&value, &Memory::m_pFakeVMEM[em_address & Memory::GetRamMask()], sizeof(T));
return bswap(value);
}

@@ -300,22 +300,22 @@ static void WriteToHardware(u32 em_address, const T data)
{
// Handle RAM; the masking intentionally discards bits (essentially creating
// mirrors of memory).
// TODO: Only the first REALRAM_SIZE is supposed to be backed by actual memory.
// TODO: Only the first GetRamSizeReal() is supposed to be backed by actual memory.
const T swapped_data = bswap(data);
std::memcpy(&Memory::m_pRAM[em_address & Memory::RAM_MASK], &swapped_data, sizeof(T));
std::memcpy(&Memory::m_pRAM[em_address & Memory::GetRamMask()], &swapped_data, sizeof(T));
return;
}

if (Memory::m_pEXRAM && (em_address >> 28) == 0x1 &&
(em_address & 0x0FFFFFFF) < Memory::EXRAM_SIZE)
(em_address & 0x0FFFFFFF) < Memory::GetExRamSizeReal())
{
const T swapped_data = bswap(data);
std::memcpy(&Memory::m_pEXRAM[em_address & 0x0FFFFFFF], &swapped_data, sizeof(T));
return;
}

// Locked L1 technically doesn't have a fixed address, but games all use 0xE0000000.
if ((em_address >> 28 == 0xE) && (em_address < (0xE0000000 + Memory::L1_CACHE_SIZE)))
if ((em_address >> 28 == 0xE) && (em_address < (0xE0000000 + Memory::GetL1CacheSize())))
{
const T swapped_data = bswap(data);
std::memcpy(&Memory::m_pL1Cache[em_address & 0x0FFFFFFF], &swapped_data, sizeof(T));
@@ -328,7 +328,7 @@ static void WriteToHardware(u32 em_address, const T data)
if (Memory::m_pFakeVMEM && ((em_address & 0xFE000000) == 0x7E000000))
{
const T swapped_data = bswap(data);
std::memcpy(&Memory::m_pFakeVMEM[em_address & Memory::RAM_MASK], &swapped_data, sizeof(T));
std::memcpy(&Memory::m_pFakeVMEM[em_address & Memory::GetRamMask()], &swapped_data, sizeof(T));
return;
}

@@ -412,7 +412,7 @@ TryReadInstResult TryReadInstruction(u32 address)
// TODO: Refactor this. This icache implementation is totally wrong if used with the fake vmem.
if (Memory::m_pFakeVMEM && ((address & 0xFE000000) == 0x7E000000))
{
hex = Common::swap32(&Memory::m_pFakeVMEM[address & Memory::FAKEVMEM_MASK]);
hex = Common::swap32(&Memory::m_pFakeVMEM[address & Memory::GetFakeVMemMask()]);
}
else
{
@@ -667,13 +667,14 @@ static bool IsRAMAddress(u32 address, bool translate)
}

u32 segment = address >> 28;
if (segment == 0x0 && (address & 0x0FFFFFFF) < Memory::REALRAM_SIZE)
if (segment == 0x0 && (address & 0x0FFFFFFF) < Memory::GetRamSizeReal())
return true;
else if (Memory::m_pEXRAM && segment == 0x1 && (address & 0x0FFFFFFF) < Memory::EXRAM_SIZE)
else if (Memory::m_pEXRAM && segment == 0x1 &&
(address & 0x0FFFFFFF) < Memory::GetExRamSizeReal())
return true;
else if (Memory::m_pFakeVMEM && ((address & 0xFE000000) == 0x7E000000))
return true;
else if (segment == 0xE && (address < (0xE0000000 + Memory::L1_CACHE_SIZE)))
else if (segment == 0xE && (address < (0xE0000000 + Memory::GetL1CacheSize())))
return true;
return false;
}
@@ -1212,13 +1213,13 @@ static void UpdateBATs(BatTable& bat_table, u32 base_spr)
u32 valid_bit = BAT_MAPPED_BIT;
if (Memory::m_pFakeVMEM && (physical_address & 0xFE000000) == 0x7E000000)
valid_bit |= BAT_PHYSICAL_BIT;
else if (physical_address < Memory::REALRAM_SIZE)
else if (physical_address < Memory::GetRamSizeReal())
valid_bit |= BAT_PHYSICAL_BIT;
else if (Memory::m_pEXRAM && physical_address >> 28 == 0x1 &&
(physical_address & 0x0FFFFFFF) < Memory::EXRAM_SIZE)
(physical_address & 0x0FFFFFFF) < Memory::GetExRamSizeReal())
valid_bit |= BAT_PHYSICAL_BIT;
else if (physical_address >> 28 == 0xE &&
physical_address < 0xE0000000 + Memory::L1_CACHE_SIZE)
physical_address < 0xE0000000 + Memory::GetL1CacheSize())
valid_bit |= BAT_PHYSICAL_BIT;

// Fastmem doesn't support memchecks, so disable it for all overlapping virtual pages.
@@ -1239,7 +1240,7 @@ static void UpdateFakeMMUBat(BatTable& bat_table, u32 start_addr)
// Map from 0x4XXXXXXX or 0x7XXXXXXX to the range
// [0x7E000000,0x80000000).
u32 e_address = i + (start_addr >> BAT_INDEX_SHIFT);
u32 p_address = 0x7E000000 | (i << BAT_INDEX_SHIFT & Memory::FAKEVMEM_MASK);
u32 p_address = 0x7E000000 | (i << BAT_INDEX_SHIFT & Memory::GetFakeVMemMask());
u32 flags = BAT_MAPPED_BIT | BAT_PHYSICAL_BIT;

if (PowerPC::memchecks.OverlapsMemcheck(e_address << BAT_INDEX_SHIFT, BAT_PAGE_SIZE))
@@ -30,6 +30,7 @@
#include "Core/CoreTiming.h"
#include "Core/GeckoCode.h"
#include "Core/HW/HW.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/Wiimote.h"
#include "Core/Host.h"
#include "Core/Movie.h"
@@ -73,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread;

// Don't forget to increase this after doing changes on the savestate system
constexpr u32 STATE_VERSION = 114; // Last changed in PR 8394
constexpr u32 STATE_VERSION = 115; // Last changed in PR 8722

// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,
@@ -171,6 +172,24 @@ static void DoState(PointerWrap& p)
return;
}

// Check to make sure the emulated memory sizes are the same as the savestate
u32 state_mem1_size = Memory::GetRamSizeReal();
u32 state_mem2_size = Memory::GetExRamSizeReal();
p.Do(state_mem1_size);
p.Do(state_mem2_size);
if (state_mem1_size != Memory::GetRamSizeReal() || state_mem2_size != Memory::GetExRamSizeReal())
{
OSD::AddMessage(fmt::format("Memory size mismatch!\n"
"Current | MEM1 {:08X} ({:3}MB) MEM2 {:08X} ({:3}MB)\n"
"State | MEM1 {:08X} ({:3}MB) MEM2 {:08X} ({:3}MB)",
Memory::GetRamSizeReal(), Memory::GetRamSizeReal() / 0x100000U,
Memory::GetExRamSizeReal(), Memory::GetExRamSizeReal() / 0x100000U,
state_mem1_size, state_mem1_size / 0x100000U, state_mem2_size,
state_mem2_size / 0x100000U));
p.SetMode(PointerWrap::MODE_MEASURE);
return;
}

// Movie must be done before the video backend, because the window is redrawn in the video backend
// state load, and the frame number must be up-to-date.
Movie::DoState(p);
@@ -571,7 +571,7 @@ void CheatsManager::NewSearch()
return;

Core::RunAsCPUThread([&] {
for (u32 i = 0; i < Memory::REALRAM_SIZE - GetTypeSize(); i++)
for (u32 i = 0; i < Memory::GetRamSizeReal() - GetTypeSize(); i++)
{
if (PowerPC::HostIsRAMAddress(base_address + i) && matches_func(base_address + i))
m_results.push_back(
@@ -26,6 +26,7 @@
#include "Core/Debugger/RSO.h"
#include "Core/HLE/HLE.h"
#include "Core/HW/AddressSpace.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/WiiSave.h"
#include "Core/HW/Wiimote.h"
#include "Core/IOS/ES/ES.h"
@@ -1178,13 +1179,15 @@ void MenuBar::ClearSymbols()

void MenuBar::GenerateSymbolsFromAddress()
{
PPCAnalyst::FindFunctions(0x80000000, 0x81800000, &g_symbolDB);
PPCAnalyst::FindFunctions(Memory::MEM1_BASE_ADDR,
Memory::MEM1_BASE_ADDR + Memory::GetRamSizeReal(), &g_symbolDB);
emit NotifySymbolsUpdated();
}

void MenuBar::GenerateSymbolsFromSignatureDB()
{
PPCAnalyst::FindFunctions(0x80000000, 0x81800000, &g_symbolDB);
PPCAnalyst::FindFunctions(Memory::MEM1_BASE_ADDR,
Memory::MEM1_BASE_ADDR + Memory::GetRamSizeReal(), &g_symbolDB);
SignatureDB db(SignatureDB::HandlerType::DSY);
if (db.Load(File::GetSysDirectory() + TOTALDB))
{
@@ -1315,7 +1318,8 @@ void MenuBar::LoadSymbolMap()
if (!map_exists)
{
g_symbolDB.Clear();
PPCAnalyst::FindFunctions(0x81300000, 0x81800000, &g_symbolDB);
PPCAnalyst::FindFunctions(Memory::MEM1_BASE_ADDR + 0x1300000,
Memory::MEM1_BASE_ADDR + Memory::GetRamSizeReal(), &g_symbolDB);
SignatureDB db(SignatureDB::HandlerType::DSY);
if (db.Load(File::GetSysDirectory() + TOTALDB))
db.Apply(&g_symbolDB);
@@ -1554,7 +1558,8 @@ void MenuBar::SearchInstruction()
return;

bool found = false;
for (u32 addr = 0x80000000; addr < 0x81800000; addr += 4)
for (u32 addr = Memory::MEM1_BASE_ADDR; addr < Memory::MEM1_BASE_ADDR + Memory::GetRamSizeReal();
addr += 4)
{
auto ins_name =
QString::fromStdString(PPCTables::GetInstructionName(PowerPC::HostRead_U32(addr)));
@@ -96,6 +96,44 @@ void AdvancedPane::CreateLayout()
cpu_clock_override_description->setWordWrap(true);
clock_override_layout->addWidget(cpu_clock_override_description);

auto* ram_override = new QGroupBox(tr("Memory Override"));
auto* ram_override_layout = new QVBoxLayout();
ram_override->setLayout(ram_override_layout);
main_layout->addWidget(ram_override);

m_ram_override_checkbox = new QCheckBox(tr("Enable Emulated Memory Size Override"));
ram_override_layout->addWidget(m_ram_override_checkbox);

auto* mem1_override_slider_layout = new QHBoxLayout();
mem1_override_slider_layout->setContentsMargins(0, 0, 0, 0);
ram_override_layout->addLayout(mem1_override_slider_layout);

m_mem1_override_slider = new QSlider(Qt::Horizontal);
m_mem1_override_slider->setRange(24, 64);
mem1_override_slider_layout->addWidget(m_mem1_override_slider);

m_mem1_override_slider_label = new QLabel();
mem1_override_slider_layout->addWidget(m_mem1_override_slider_label);

auto* mem2_override_slider_layout = new QHBoxLayout();
mem2_override_slider_layout->setContentsMargins(0, 0, 0, 0);
ram_override_layout->addLayout(mem2_override_slider_layout);

m_mem2_override_slider = new QSlider(Qt::Horizontal);
m_mem2_override_slider->setRange(64, 128);
mem2_override_slider_layout->addWidget(m_mem2_override_slider);

m_mem2_override_slider_label = new QLabel();
mem2_override_slider_layout->addWidget(m_mem2_override_slider_label);

auto* ram_override_description =
new QLabel(tr("Adjusts the emulated sizes of MEM1 and MEM2.\n\n"
"Some titles may recognize the larger memory arena(s) and take "
"advantage of it, though retail titles should be optimized for "
"the retail memory limitations."));
ram_override_description->setWordWrap(true);
ram_override_layout->addWidget(ram_override_description);

auto* rtc_options = new QGroupBox(tr("Custom RTC Options"));
rtc_options->setLayout(new QVBoxLayout());
main_layout->addWidget(rtc_options);
@@ -154,6 +192,24 @@ void AdvancedPane::ConnectLayout()
Update();
});

m_ram_override_checkbox->setChecked(Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE));
connect(m_ram_override_checkbox, &QCheckBox::toggled, [this](bool enable_ram_override) {
Config::SetBaseOrCurrent(Config::MAIN_RAM_OVERRIDE_ENABLE, enable_ram_override);
Update();
});

connect(m_mem1_override_slider, &QSlider::valueChanged, [this](int slider_value) {
const u32 mem1_size = m_mem1_override_slider->value() * 0x100000;
Config::SetBaseOrCurrent(Config::MAIN_MEM1_SIZE, mem1_size);
Update();
});

connect(m_mem2_override_slider, &QSlider::valueChanged, [this](int slider_value) {
const u32 mem2_size = m_mem2_override_slider->value() * 0x100000;
Config::SetBaseOrCurrent(Config::MAIN_MEM2_SIZE, mem2_size);
Update();
});

m_custom_rtc_checkbox->setChecked(SConfig::GetInstance().bEnableCustomRTC);
connect(m_custom_rtc_checkbox, &QCheckBox::toggled, [this](bool enable_custom_rtc) {
SConfig::GetInstance().bEnableCustomRTC = enable_custom_rtc;
@@ -173,6 +229,7 @@ void AdvancedPane::Update()
{
const bool running = Core::GetState() != Core::State::Uninitialized;
const bool enable_cpu_clock_override_widgets = SConfig::GetInstance().m_OCEnable;
const bool enable_ram_override_widgets = Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE);
const bool enable_custom_rtc_widgets = SConfig::GetInstance().bEnableCustomRTC && !running;

const std::vector<PowerPC::CPUCore>& available_cpu_cores = PowerPC::AvailableCPUCores();
@@ -208,6 +265,36 @@ void AdvancedPane::Update()
return tr("%1 % (%2 MHz)").arg(QString::number(percent), QString::number(clock));
}());

m_ram_override_checkbox->setEnabled(!running);

m_mem1_override_slider->setEnabled(enable_ram_override_widgets && !running);
m_mem1_override_slider_label->setEnabled(enable_ram_override_widgets && !running);

{
const QSignalBlocker blocker(m_mem1_override_slider);
const u32 mem1_size = Config::Get(Config::MAIN_MEM1_SIZE) / 0x100000;
m_mem1_override_slider->setValue(mem1_size);
}

m_mem1_override_slider_label->setText([] {
const u32 mem1_size = Config::Get(Config::MAIN_MEM1_SIZE) / 0x100000;
return tr("%1MB (MEM1)").arg(QString::number(mem1_size));
}());

m_mem2_override_slider->setEnabled(enable_ram_override_widgets && !running);
m_mem2_override_slider_label->setEnabled(enable_ram_override_widgets && !running);

{
const QSignalBlocker blocker(m_mem2_override_slider);
const u32 mem2_size = Config::Get(Config::MAIN_MEM2_SIZE) / 0x100000;
m_mem2_override_slider->setValue(mem2_size);
}

m_mem2_override_slider_label->setText([] {
const u32 mem2_size = Config::Get(Config::MAIN_MEM2_SIZE) / 0x100000;
return tr("%1MB (MEM2)").arg(QString::number(mem2_size));
}());

m_custom_rtc_checkbox->setEnabled(!running);
m_custom_rtc_datetime->setEnabled(enable_custom_rtc_widgets);
}
@@ -40,4 +40,11 @@ class AdvancedPane final : public QWidget

QCheckBox* m_custom_rtc_checkbox;
QDateTimeEdit* m_custom_rtc_datetime;

QCheckBox* m_ram_override_checkbox;
QSlider* m_mem1_override_slider;
QLabel* m_mem1_override_slider_label;
QSlider* m_mem2_override_slider;
QLabel* m_mem2_override_slider_label;
QLabel* m_ram_override_description;
};