diff --git a/src/core/achievements.cpp b/src/core/achievements.cpp index f0748cc8d..ba7c3f0d7 100644 --- a/src/core/achievements.cpp +++ b/src/core/achievements.cpp @@ -920,8 +920,8 @@ uint32_t Achievements::ClientReadMemory(uint32_t address, uint8_t* buffer, uint3 if ((address + num_bytes) > 0x200400U) [[unlikely]] return 0; - const u8* src = (address >= 0x200000U) ? CPU::g_state.scratchpad.data() : Bus::g_ram; - const u32 offset = (address & Bus::RAM_2MB_MASK); // size guarded by check above + const u8* src = (address >= Bus::RAM_DEFAULT_SIZE) ? CPU::g_state.scratchpad.data() : Bus::g_ram; + const u32 offset = (address & Bus::RAM_DEFAULT_MASK); // size guarded by check above switch (num_bytes) { @@ -3626,8 +3626,8 @@ void Achievements::RAIntegrationWriteMemoryCallback(uint32_t address, uint8_t* b // This can be called on the UI thread, so always queue it. llvm::SmallVector data(buffer, buffer + num_bytes); Host::RunOnCoreThread([address, data = std::move(data)]() { - u8* src = (address >= 0x200000U) ? CPU::g_state.scratchpad.data() : Bus::g_ram; - const u32 offset = (address & Bus::RAM_2MB_MASK); // size guarded by check above + u8* src = (address >= Bus::RAM_DEFAULT_SIZE) ? CPU::g_state.scratchpad.data() : Bus::g_ram; + const u32 offset = (address & Bus::RAM_DEFAULT_MASK); // size guarded by check above switch (data.size()) { diff --git a/src/core/bus.cpp b/src/core/bus.cpp index c405a6d02..1463440af 100644 --- a/src/core/bus.cpp +++ b/src/core/bus.cpp @@ -122,7 +122,10 @@ union RAM_SIZE_REG static void* s_shmem_handle = nullptr; static std::string s_shmem_name; -std::bitset g_ram_code_bits{}; +// Sanity checks. +static_assert((Bus::RAM_MAX_SIZE % MIN_HOST_PAGE_SIZE) == 0, "RAM max size must be a multiple of host page size"); + +std::bitset g_ram_code_bits{}; u8* g_ram = nullptr; u8* g_unprotected_ram = nullptr; u32 g_ram_size = 0; @@ -154,7 +157,7 @@ static bool s_kernel_initialize_hook_run = false; static bool AllocateMemoryMap(bool export_shared_memory, Error* error); static void ReleaseMemoryMap(); -static void SetRAMSize(bool enable_8mb_ram); +static void SetRAMSize(u8 size); static std::tuple CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay); static void RecalculateMemoryTimings(); @@ -177,7 +180,7 @@ static void UpdateMappedRAMSize(); namespace MemoryMap { static constexpr size_t RAM_OFFSET = 0; -static constexpr size_t RAM_SIZE = Bus::RAM_8MB_SIZE; +static constexpr size_t RAM_SIZE = Bus::RAM_MAX_SIZE; static constexpr size_t BIOS_OFFSET = RAM_OFFSET + RAM_SIZE; static constexpr size_t BIOS_SIZE = Bus::BIOS_SIZE; static constexpr size_t LUT_OFFSET = BIOS_OFFSET + BIOS_SIZE; @@ -199,6 +202,8 @@ static constexpr size_t TOTAL_SIZE = LUT_OFFSET + LUT_SIZE; bool Bus::AllocateMemoryMap(bool export_shared_memory, Error* error) { + AssertMsg((RAM_MAX_SIZE % HOST_PAGE_SIZE) == 0, "Page size alignment is required for memory mapping"); + INFO_LOG("Allocating{} shared memory map.", export_shared_memory ? " EXPORTED" : ""); if (export_shared_memory) { @@ -252,7 +257,7 @@ bool Bus::AllocateMemoryMap(bool export_shared_memory, Error* error) VERBOSE_LOG("LUTs are mapped at {}.", static_cast(g_memory_handlers)); g_memory_handlers_isc = g_memory_handlers + MEMORY_LUT_SLOTS; - g_ram_mapped_size = RAM_8MB_SIZE; + g_ram_mapped_size = RAM_DEFAULT_SIZE; SetHandlers(); #ifndef __ANDROID__ @@ -351,8 +356,8 @@ bool Bus::ReallocateMemoryMap(bool export_shared_memory, Error* error) CPU::CodeCache::InvalidateAllRAMBlocks(); UnmapFastmemViews(); - ram_backup.resize(RAM_8MB_SIZE); - std::memcpy(ram_backup.data(), g_unprotected_ram, RAM_8MB_SIZE); + ram_backup.resize(RAM_MAX_SIZE); + std::memcpy(ram_backup.data(), g_unprotected_ram, RAM_MAX_SIZE); bios_backup.resize(BIOS_SIZE); std::memcpy(bios_backup.data(), g_bios, BIOS_SIZE); } @@ -364,7 +369,7 @@ bool Bus::ReallocateMemoryMap(bool export_shared_memory, Error* error) if (System::IsValid()) { UpdateMappedRAMSize(); - std::memcpy(g_unprotected_ram, ram_backup.data(), RAM_8MB_SIZE); + std::memcpy(g_unprotected_ram, ram_backup.data(), RAM_MAX_SIZE); std::memcpy(g_bios, bios_backup.data(), BIOS_SIZE); MapFastmemViews(); } @@ -383,14 +388,29 @@ void Bus::CleanupMemoryMap() void Bus::Initialize() { - SetRAMSize(g_settings.cpu_enable_8mb_ram); + SetRAMSize(g_settings.cpu_ram_size); MapFastmemViews(); } -void Bus::SetRAMSize(bool enable_8mb_ram) +void Bus::SetRAMSize(u8 size) { - g_ram_size = enable_8mb_ram ? RAM_8MB_SIZE : RAM_2MB_SIZE; - g_ram_mask = enable_8mb_ram ? RAM_8MB_MASK : RAM_2MB_MASK; + static constexpr u32 one_mb = 1048576; + + if (size != 2 && size != 4 && size != 8 && size != 16) + { + ERROR_LOG("Invalid RAM size: {} MB. Defaulting to {} MB.", size, RAM_DEFAULT_SIZE / one_mb); + size = RAM_DEFAULT_SIZE / one_mb; + } + + const u32 new_size = size * one_mb; + DebugAssert(Common::IsPow2(new_size)); + + // Ensure no old protection was left. + if (new_size > g_ram_size) + MemMap::MemProtect(g_ram + g_ram_size, new_size - g_ram_size, PageProtect::ReadWrite); + + g_ram_size = size * one_mb; + g_ram_mask = g_ram_size - 1; #ifndef __ANDROID__ Exports::RAM_SIZE = g_ram_size; @@ -434,11 +454,10 @@ void Bus::Reset() bool Bus::DoState(StateWrapper& sw) { u32 ram_size = g_ram_size; - sw.DoEx(&ram_size, 52, static_cast(RAM_2MB_SIZE)); + sw.DoEx(&ram_size, 52, static_cast(RAM_DEFAULT_SIZE)); if (ram_size != g_ram_size) { - const bool using_8mb_ram = (ram_size == RAM_8MB_SIZE); - SetRAMSize(using_8mb_ram); + SetRAMSize(static_cast(ram_size / 1048576)); RemapFastmemViews(); } @@ -595,7 +614,7 @@ void Bus::MapFastmemViews() MapRAM(0xA0000000); // Mirrors of 2MB - if (g_ram_size == RAM_2MB_SIZE) + if (g_ram_size == RAM_DEFAULT_SIZE) { // Instead of mapping all the RAM mirrors, we only map the KSEG0 uppermost mirror. // This is where some games place their stack, so we avoid the backpatching overhead/slowdown, @@ -621,7 +640,37 @@ void Bus::MapFastmemViews() for (u32 i = 0; i < FASTMEM_LUT_SLOTS; i++) s_fastmem_lut[i] = GetLUTFastmemPointer(i << FASTMEM_LUT_PAGE_SHIFT, nullptr); - auto MapRAM = [](u32 base_address) { + static constexpr const std::array ranges = { + // KUSEG - cached + 0x00000000u, + 0x00200000u, + 0x00400000u, + 0x00600000u, + 0x00800000u, + 0x00A00000u, + + // KSEG0 - cached + 0x80000000u, + 0x80200000u, + 0x80400000u, + 0x80600000u, + 0x80800000u, + 0x80A00000u, + 0x80C00000u, + 0x80E00000u, + + // KSEG1 - uncached + 0xA0000000u, + 0xA0200000u, + 0xA0400000u, + 0xA0600000u, + 0xA0800000u, + 0xA0A00000u, + 0xA0C00000u, + 0xA0E00000u, + }; + for (const u32 base_address : ranges) + { // Don't map RAM that isn't accessible. if (CPU::VirtualAddressToPhysical(base_address) >= g_ram_mapped_size) return; @@ -633,25 +682,7 @@ void Bus::MapFastmemViews() s_fastmem_lut[lut_index] = GetLUTFastmemPointer(base_address + address, ram_ptr); ram_ptr += FASTMEM_LUT_PAGE_SIZE; } - }; - - // KUSEG - cached - MapRAM(0x00000000); - MapRAM(0x00200000); - MapRAM(0x00400000); - MapRAM(0x00600000); - - // KSEG0 - cached - MapRAM(0x80000000); - MapRAM(0x80200000); - MapRAM(0x80400000); - MapRAM(0x80600000); - - // KSEG1 - uncached - MapRAM(0xA0000000); - MapRAM(0xA0200000); - MapRAM(0xA0400000); - MapRAM(0xA0600000); + } } CPU::UpdateMemoryPointers(); @@ -689,7 +720,7 @@ bool Bus::CanUseFastmemForAddress(VirtualMemoryAddress address) #endif case CPUFastmemMode::LUT: - return (paddr < RAM_MIRROR_END); + return (paddr < g_ram_size); case CPUFastmemMode::Disabled: default: @@ -752,7 +783,7 @@ void Bus::ClearRAMCodePageFlags() { g_ram_code_bits.reset(); - if (!MemMap::MemProtect(g_ram, RAM_8MB_SIZE, PageProtect::ReadWrite)) + if (!MemMap::MemProtect(g_ram, g_ram_size, PageProtect::ReadWrite)) ERROR_LOG("Failed to restore RAM protection to read-write."); #ifdef ENABLE_MMAP_FASTMEM @@ -806,10 +837,8 @@ const TickCount* Bus::GetMemoryAccessTimePtr(PhysicalMemoryAddress address, Memo std::optional Bus::GetMemoryRegionForAddress(PhysicalMemoryAddress address) { - if (address < RAM_2MB_SIZE) + if (address < g_ram_size) return MemoryRegion::RAM; - else if (address < RAM_MIRROR_END) - return static_cast(static_cast(MemoryRegion::RAM) + (address / RAM_2MB_SIZE)); else if (address >= EXP1_BASE && address < (EXP1_BASE + EXP1_SIZE)) return MemoryRegion::EXP1; else if (address >= CPU::SCRATCHPAD_ADDR && address < (CPU::SCRATCHPAD_ADDR + CPU::SCRATCHPAD_SIZE)) @@ -823,10 +852,7 @@ std::optional Bus::GetMemoryRegionForAddress(PhysicalMemoryAd static constexpr std::array, static_cast(Bus::MemoryRegion::Count)> s_code_region_ranges = {{ - {0, Bus::RAM_2MB_SIZE, true}, - {Bus::RAM_2MB_SIZE, Bus::RAM_2MB_SIZE * 2, true}, - {Bus::RAM_2MB_SIZE * 2, Bus::RAM_2MB_SIZE * 3, true}, - {Bus::RAM_2MB_SIZE * 3, Bus::RAM_MIRROR_END, true}, + {0, Bus::RAM_DEFAULT_SIZE, true}, {Bus::EXP1_BASE, Bus::EXP1_BASE + Bus::EXP1_SIZE, false}, {CPU::SCRATCHPAD_ADDR, CPU::SCRATCHPAD_ADDR + CPU::SCRATCHPAD_SIZE, true}, {Bus::BIOS_BASE, Bus::BIOS_BASE + Bus::BIOS_SIZE, false}, @@ -839,6 +865,9 @@ PhysicalMemoryAddress Bus::GetMemoryRegionStart(MemoryRegion region) PhysicalMemoryAddress Bus::GetMemoryRegionEnd(MemoryRegion region) { + if (region == MemoryRegion::RAM) + return g_ram_mask + 1; + return std::get<1>(s_code_region_ranges[static_cast(region)]); } @@ -854,15 +883,6 @@ u8* Bus::GetMemoryRegionPointer(MemoryRegion region) case MemoryRegion::RAM: return g_unprotected_ram; - case MemoryRegion::RAMMirror1: - return (g_unprotected_ram + (RAM_2MB_SIZE & g_ram_mask)); - - case MemoryRegion::RAMMirror2: - return (g_unprotected_ram + ((RAM_2MB_SIZE * 2) & g_ram_mask)); - - case MemoryRegion::RAMMirror3: - return (g_unprotected_ram + ((RAM_8MB_SIZE * 3) & g_ram_mask)); - case MemoryRegion::EXP1: return nullptr; @@ -2135,7 +2155,7 @@ void Bus::SetHandlers() // KUSEG - Cached // Cache isolated appears to affect KUSEG+KSEG0. - SET(g_memory_handlers, KUSEG | RAM_BASE, RAM_MIRROR_SIZE, RAMReadHandler, RAMWriteHandler); + SET(g_memory_handlers, KUSEG | RAM_BASE, g_ram_mapped_size, RAMReadHandler, RAMWriteHandler); SET(g_memory_handlers, KUSEG | CPU::SCRATCHPAD_ADDR, 0x1000, ScratchpadReadHandler, ScratchpadWriteHandler); SET(g_memory_handlers, KUSEG | BIOS_BASE, BIOS_MIRROR_SIZE, BIOSReadHandler, IgnoreWriteHandler); SET(g_memory_handlers, KUSEG | EXP1_BASE, EXP1_SIZE, EXP1ReadHandler, EXP1WriteHandler); @@ -2146,7 +2166,7 @@ void Bus::SetHandlers() SET(g_memory_handlers_isc, KUSEG, 0x80000000, ICacheReadHandler, ICacheWriteHandler); // KSEG0 - Cached - SET(g_memory_handlers, KSEG0 | RAM_BASE, RAM_MIRROR_SIZE, RAMReadHandler, RAMWriteHandler); + SET(g_memory_handlers, KSEG0 | RAM_BASE, g_ram_mapped_size, RAMReadHandler, RAMWriteHandler); SET(g_memory_handlers, KSEG0 | CPU::SCRATCHPAD_ADDR, 0x1000, ScratchpadReadHandler, ScratchpadWriteHandler); SET(g_memory_handlers, KSEG0 | BIOS_BASE, BIOS_MIRROR_SIZE, BIOSReadHandler, IgnoreWriteHandler); SET(g_memory_handlers, KSEG0 | EXP1_BASE, EXP1_SIZE, EXP1ReadHandler, EXP1WriteHandler); @@ -2157,7 +2177,7 @@ void Bus::SetHandlers() SET(g_memory_handlers_isc, KSEG0, 0x20000000, ICacheReadHandler, ICacheWriteHandler); // KSEG1 - Uncached - SETUC(KSEG1 | RAM_BASE, RAM_MIRROR_SIZE, RAMReadHandler, RAMWriteHandler); + SETUC(KSEG1 | RAM_BASE, g_ram_mapped_size, RAMReadHandler, RAMWriteHandler); SETUC(KSEG1 | BIOS_BASE, BIOS_MIRROR_SIZE, BIOSReadHandler, IgnoreWriteHandler); SETUC(KSEG1 | EXP1_BASE, EXP1_SIZE, EXP1ReadHandler, EXP1WriteHandler); SETUC(KSEG1 | HW_BASE, HW_SIZE, HardwareReadHandler, HardwareWriteHandler); @@ -2173,52 +2193,42 @@ void Bus::UpdateMappedRAMSize() { const u32 prev_mapped_size = g_ram_mapped_size; - switch (s_RAM_SIZE.memory_window) - { - case 4: // 2MB memory + 6MB unmapped - { - // Used by Rock-Climbing - Mitouhou e no Chousen - Alps Hen (Japan). - // By default, all 8MB is mapped, so we only need to remap the high 6MB. - constexpr u32 MAPPED_SIZE = RAM_2MB_SIZE; - constexpr u32 UNMAPPED_START = RAM_BASE + MAPPED_SIZE; - constexpr u32 UNMAPPED_SIZE = RAM_MIRROR_SIZE - MAPPED_SIZE; - SET(g_memory_handlers, KUSEG | UNMAPPED_START, UNMAPPED_SIZE, UnmappedReadHandler, UnmappedWriteHandler); - SET(g_memory_handlers, KSEG0 | UNMAPPED_START, UNMAPPED_SIZE, UnmappedReadHandler, UnmappedWriteHandler); - SET(g_memory_handlers, KSEG1 | UNMAPPED_START, UNMAPPED_SIZE, UnmappedReadHandler, UnmappedWriteHandler); - g_ram_mapped_size = MAPPED_SIZE; - } - break; + // https://psx-spx.consoledev.net/memorycontrol/#1f801060h-ram_size-rw-usually-00000b88h-or-00000888h + static constexpr const u32 one_mb = 1024 * 1024; + static constexpr const u32 ram_mapped_sizes[] = { + 1 * one_mb, // 000 = 1MB bank on /RAS0 + 15MB unmapped + 4 * one_mb, // 001 = 4MB bank on /RAS0 + 12MB unmapped + 2 * one_mb, // 010 = 1MB bank on /RAS0 + 1MB bank on /RAS1 (?) + 14MB unmapped + 8 * one_mb, // 011 = 4MB bank on /RAS0 + 4MB bank on /RAS1 (?) + 8MB unmapped + 2 * one_mb, // 100 = 2MB bank on /RAS0 + 14MB unmapped + 16 * one_mb, // 101 = 8MB bank on /RAS0 + 8MB unmapped + 4 * one_mb, // 110 = 2MB bank on /RAS0 + 2MB bank on /RAS1 (?) + 12MB unmapped + 16 * one_mb, // 111 = 8MB bank on /RAS0 + 8MB bank on /RAS1 (?) + }; - case 0: // 1MB memory + 7MB unmapped - case 1: // 4MB memory + 4MB unmapped - case 2: // 1MB memory + 1MB HighZ + 6MB unmapped - case 3: // 4MB memory + 4MB HighZ - case 6: // 2MB memory + 2MB HighZ + 4MB unmapped - case 7: // 8MB memory - { - // These aren't implemented because nothing is known to use them, so it can't be tested. - // If you find something that does, please let us know. - WARNING_LOG("Unhandled memory window 0x{} (register 0x{:08X}). Please report this game to developers.", - s_RAM_SIZE.memory_window.GetValue(), s_RAM_SIZE.bits); - } - [[fallthrough]]; + const u32 mapped_size = ram_mapped_sizes[s_RAM_SIZE.memory_window]; + if (mapped_size == prev_mapped_size) + return; - case 5: // 8MB memory - { - // We only unmap the upper 6MB above, so we only need to remap this as well. - constexpr u32 REMAP_START = RAM_BASE + RAM_2MB_SIZE; - constexpr u32 REMAP_SIZE = RAM_MIRROR_SIZE - RAM_2MB_SIZE; - SET(g_memory_handlers, KUSEG | REMAP_START, REMAP_SIZE, RAMReadHandler, RAMWriteHandler); - SET(g_memory_handlers, KSEG0 | REMAP_START, REMAP_SIZE, RAMReadHandler, RAMWriteHandler); - SET(g_memory_handlers, KSEG1 | REMAP_START, REMAP_SIZE, RAMReadHandler, RAMWriteHandler); - g_ram_mapped_size = RAM_8MB_SIZE; - } - break; + WARNING_LOG("RAM mapped size changed to {} MB", mapped_size / one_mb); + + SET(g_memory_handlers, KUSEG | RAM_BASE, mapped_size, RAMReadHandler, RAMWriteHandler); + SET(g_memory_handlers, KSEG0 | RAM_BASE, mapped_size, RAMReadHandler, RAMWriteHandler); + SET(g_memory_handlers, KSEG1 | RAM_BASE, mapped_size, RAMReadHandler, RAMWriteHandler); + + const u32 unmapped_size = RAM_MAX_SIZE - mapped_size; + if (unmapped_size > 0) + { + const u32 unmapped_base = RAM_BASE + mapped_size; + SET(g_memory_handlers, KUSEG | unmapped_base, unmapped_size, UnmappedReadHandler, UnmappedWriteHandler); + SET(g_memory_handlers, KSEG0 | unmapped_base, unmapped_size, UnmappedReadHandler, UnmappedWriteHandler); + SET(g_memory_handlers, KSEG1 | unmapped_base, unmapped_size, UnmappedReadHandler, UnmappedWriteHandler); } + g_ram_mapped_size = mapped_size; + // Fastmem needs to be remapped. - if (prev_mapped_size != g_ram_mapped_size) - RemapFastmemViews(); + RemapFastmemViews(); } #undef SET diff --git a/src/core/bus.h b/src/core/bus.h index 68d421945..d3948ec81 100644 --- a/src/core/bus.h +++ b/src/core/bus.h @@ -20,12 +20,10 @@ namespace Bus { enum : u32 { RAM_BASE = 0x00000000, - RAM_2MB_SIZE = 0x200000, - RAM_2MB_MASK = RAM_2MB_SIZE - 1, - RAM_8MB_SIZE = 0x800000, - RAM_8MB_MASK = RAM_8MB_SIZE - 1, - RAM_MIRROR_END = 0x800000, - RAM_MIRROR_SIZE = 0x800000, + RAM_DEFAULT_SIZE = 0x200000, + RAM_DEFAULT_MASK = RAM_DEFAULT_SIZE - 1, + RAM_MAX_SIZE = 0x1000000, + RAM_MAX_MASK = RAM_MAX_SIZE - 1, EXP1_BASE = 0x1F000000, EXP1_SIZE = 0x800000, EXP1_MASK = EXP1_SIZE - 1, @@ -91,8 +89,7 @@ enum : TickCount enum : u32 { - RAM_2MB_CODE_PAGE_COUNT = (RAM_2MB_SIZE + (MIN_HOST_PAGE_SIZE - 1)) / MIN_HOST_PAGE_SIZE, - RAM_8MB_CODE_PAGE_COUNT = (RAM_8MB_SIZE + (MIN_HOST_PAGE_SIZE - 1)) / MIN_HOST_PAGE_SIZE, + RAM_MAX_CODE_PAGE_COUNT = RAM_MAX_SIZE / MIN_HOST_PAGE_SIZE, MEMORY_LUT_PAGE_SIZE = 4096, MEMORY_LUT_PAGE_SHIFT = 12, @@ -144,7 +141,7 @@ void* GetFastmemBase(bool isc); void RemapFastmemViews(); bool CanUseFastmemForAddress(VirtualMemoryAddress address); -extern std::bitset g_ram_code_bits; +extern std::bitset g_ram_code_bits; extern u8* g_ram; // 2MB-8MB RAM extern u8* g_unprotected_ram; // RAM without page protection, use for debugger access. extern u32 g_ram_size; // Active size of RAM. @@ -160,7 +157,7 @@ extern std::array g_spu_access_time; /// Returns true if the address specified is writable (RAM). ALWAYS_INLINE bool IsRAMAddress(PhysicalMemoryAddress address) { - return address < RAM_MIRROR_END; + return (address < RAM_MAX_SIZE); } /// Returns the code page index for a RAM address. @@ -175,6 +172,12 @@ ALWAYS_INLINE bool IsRAMCodePage(u32 index) return g_ram_code_bits[index]; } +/// Returns the number of RAM code pages for the current memory size. +ALWAYS_INLINE u32 GetRAMCodePageCount() +{ + return (g_ram_size >> HOST_PAGE_SHIFT); +} + /// Flags a RAM region as code, so we know when to invalidate blocks. void SetRAMCodePage(u32 index); @@ -206,9 +209,6 @@ const TickCount* GetMemoryAccessTimePtr(PhysicalMemoryAddress address, MemoryAcc enum class MemoryRegion { RAM, - RAMMirror1, - RAMMirror2, - RAMMirror3, EXP1, Scratchpad, BIOS, diff --git a/src/core/cheats.cpp b/src/core/cheats.cpp index 2ad6458cc..f6b06f47c 100644 --- a/src/core/cheats.cpp +++ b/src/core/cheats.cpp @@ -176,6 +176,7 @@ public: bool has_options : 1; bool disable_widescreen_rendering : 1; bool enable_8mb_ram : 1; + bool enable_16mb_ram : 1; bool disallow_for_achievements : 1; }; @@ -286,7 +287,7 @@ Cheats::CheatCode::~CheatCode() = default; bool Cheats::CheatCode::HasAnySettingOverrides() const { - return (m_metadata.disable_widescreen_rendering || m_metadata.enable_8mb_ram || + return (m_metadata.disable_widescreen_rendering || m_metadata.enable_8mb_ram || m_metadata.enable_16mb_ram || m_metadata.override_aspect_ratio.has_value() || m_metadata.override_cpu_overclock.has_value()); } @@ -297,10 +298,15 @@ void Cheats::CheatCode::ApplySettingOverrides() DEV_LOG("Disabling widescreen rendering from {} patch.", GetName()); g_settings.gpu_widescreen_hack = false; } - if (m_metadata.enable_8mb_ram && !g_settings.cpu_enable_8mb_ram) + if (m_metadata.enable_16mb_ram && g_settings.cpu_ram_size != 16) + { + DEV_LOG("Enabling 16MB ram from {} patch.", GetName()); + g_settings.cpu_ram_size = 16; + } + else if (m_metadata.enable_8mb_ram && g_settings.cpu_ram_size != 8) { DEV_LOG("Enabling 8MB ram from {} patch.", GetName()); - g_settings.cpu_enable_8mb_ram = true; + g_settings.cpu_ram_size = 8; } if (m_metadata.override_aspect_ratio.has_value() && g_settings.display_aspect_ratio == DisplayAspectRatio::Auto()) { @@ -1643,7 +1649,13 @@ void Cheats::ParseFile(CheatCodeList* dst_list, const std::string_view file_cont } else if (key == "Enable8MBRAM") { - next_code_metadata.enable_8mb_ram = StringUtil::FromChars(value).value_or(false); + next_code_metadata.enable_8mb_ram = + !next_code_metadata.enable_16mb_ram && StringUtil::FromChars(value).value_or(false); + } + else if (key == "Enable16MBRAM") + { + next_code_metadata.enable_16mb_ram = StringUtil::FromChars(value).value_or(false); + next_code_metadata.enable_8mb_ram = next_code_metadata.enable_8mb_ram && !next_code_metadata.enable_16mb_ram; } else if (key == "DisallowForAchievements") { diff --git a/src/core/cpu_code_cache.cpp b/src/core/cpu_code_cache.cpp index 0b85a6c27..08718cd8a 100644 --- a/src/core/cpu_code_cache.cpp +++ b/src/core/cpu_code_cache.cpp @@ -39,7 +39,7 @@ LOG_CHANNEL(CodeCache); namespace CPU::CodeCache { using LUTRangeList = std::array, 9>; -using PageProtectionArray = std::array; +using PageProtectionArray = std::array; using BlockInstructionInfoPair = std::pair; using BlockInstructionList = std::vector; @@ -565,7 +565,7 @@ void CPU::CodeCache::RemoveBlockFromPageList(Block* block) void CPU::CodeCache::InvalidateBlocksWithPageIndex(u32 index) { - DebugAssert(index < Bus::RAM_8MB_CODE_PAGE_COUNT); + DebugAssert(index < Bus::RAM_MAX_CODE_PAGE_COUNT); Bus::ClearRAMCodePage(index); BlockState new_block_state = BlockState::Invalidated; @@ -662,7 +662,8 @@ void CPU::CodeCache::InvalidateAllRAMBlocks() void CPU::CodeCache::ClearBlocks() { - for (u32 i = 0; i < Bus::RAM_8MB_CODE_PAGE_COUNT; i++) + const u32 page_count = Bus::GetRAMCodePageCount(); + for (u32 i = 0; i < page_count; i++) { PageProtectionInfo& ppi = s_page_protection[i]; if (ppi.mode == PageProtectionMode::WriteProtected && ppi.first_block_in_page) @@ -689,7 +690,7 @@ PageFaultHandler::HandlerResult PageFaultHandler::HandlePageFault(void* exceptio bool is_write) { if (Bus::g_ram && static_cast(fault_address) >= Bus::g_ram && - static_cast(fault_address) < (Bus::g_ram + Bus::RAM_8MB_SIZE)) + static_cast(fault_address) < (Bus::g_ram + Bus::g_ram_size)) { // Writing to protected RAM. DebugAssert(is_write); diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index c78acbf33..b6c364940 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -514,26 +514,22 @@ ALWAYS_INLINE_RELEASE void CPU::WriteRegDelayed(Reg rd, u32 value) ALWAYS_INLINE_RELEASE bool CPU::IsCop0ExecutionBreakpointUnmasked() { - static constexpr const u32 code_address_ranges[][2] = { - // KUSEG - {Bus::RAM_BASE, Bus::RAM_BASE | Bus::RAM_8MB_MASK}, - {Bus::BIOS_BASE, Bus::BIOS_BASE | Bus::BIOS_MASK}, - - // KSEG0 - {0x80000000u | Bus::RAM_BASE, 0x80000000u | Bus::RAM_BASE | Bus::RAM_8MB_MASK}, - {0x80000000u | Bus::BIOS_BASE, 0x80000000u | Bus::BIOS_BASE | Bus::BIOS_MASK}, - - // KSEG1 - {0xA0000000u | Bus::RAM_BASE, 0xA0000000u | Bus::RAM_BASE | Bus::RAM_8MB_MASK}, - {0xA0000000u | Bus::BIOS_BASE, 0xA0000000u | Bus::BIOS_BASE | Bus::BIOS_MASK}, - }; - + // TODO: Needs testing + const u32 ram_mapped_mask = Bus::g_ram_mapped_size - 1; const u32 bpc = g_state.cop0_regs.BPC; const u32 bpcm = g_state.cop0_regs.BPCM; const u32 masked_bpc = bpc & bpcm; - for (const auto& [range_start, range_end] : code_address_ranges) + for (const u32 region : {0x00000000u /* KUSEG*/, 0x80000000u /* KSEG0 */, 0xA0000000u /* KSEG1 */}) { - if (masked_bpc >= (range_start & bpcm) && masked_bpc <= (range_end & bpcm)) + const u32 ram_start = region | Bus::RAM_BASE; + const u32 ram_end = ram_start | ram_mapped_mask; + + if (masked_bpc >= (ram_start & bpcm) && masked_bpc <= (ram_end & bpcm)) + return true; + + const u32 bios_start = region | Bus::BIOS_BASE; + const u32 bios_end = bios_start | Bus::BIOS_MASK; + if (masked_bpc >= (bios_start & bpcm) && masked_bpc <= (bios_end & bpcm)) return true; } @@ -2798,7 +2794,7 @@ ALWAYS_INLINE_RELEASE bool CPU::DoInstructionRead(PhysicalMemoryAddress address, DebugAssert(VirtualAddressToPhysical(address) == (address & KSEG_MASK)); address &= KSEG_MASK; - if (address < RAM_MIRROR_END) + if (address < g_ram_mapped_size) { std::memcpy(data, &g_ram[address & g_ram_mask], sizeof(u32) * word_count); if constexpr (add_ticks) @@ -2842,7 +2838,7 @@ TickCount CPU::GetInstructionReadTicks(VirtualMemoryAddress address) DebugAssert(VirtualAddressToPhysical(address) == (address & KSEG_MASK)); address &= KSEG_MASK; - if (address < RAM_MIRROR_END) + if (address < RAM_MAX_SIZE) { return RAM_READ_TICKS; } @@ -2863,7 +2859,7 @@ TickCount CPU::GetICacheFillTicks(VirtualMemoryAddress address) DebugAssert(VirtualAddressToPhysical(address) == (address & KSEG_MASK)); address &= KSEG_MASK; - if (address < RAM_MIRROR_END) + if (address < RAM_MAX_SIZE) { return 1 * ((ICACHE_LINE_SIZE - (address & (ICACHE_LINE_SIZE - 1))) / sizeof(u32)); } @@ -3128,7 +3124,7 @@ ALWAYS_INLINE bool CPU::DoSafeMemoryAccess(VirtualMemoryAddress address, u32& va break; } - if (address < RAM_MIRROR_END) + if (address < g_ram_mapped_size) { const u32 offset = address & g_ram_mask; if constexpr (type == MemoryAccessType::Read) @@ -3308,7 +3304,7 @@ bool CPU::SafeReadMemoryBytes(VirtualMemoryAddress addr, void* data, u32 length) using namespace Bus; const u32 seg = (addr >> 29); - if ((seg != 0 && seg != 4 && seg != 5) || (((addr + length) & KSEG_MASK) >= RAM_MIRROR_END) || + if ((seg != 0 && seg != 4 && seg != 5) || (((addr + length) & KSEG_MASK) >= g_ram_mapped_size) || (((addr & g_ram_mask) + length) > g_ram_size)) { u8* ptr = static_cast(data); @@ -3332,7 +3328,7 @@ bool CPU::SafeWriteMemoryBytes(VirtualMemoryAddress addr, const void* data, u32 using namespace Bus; const u32 seg = (addr >> 29); - if ((seg != 0 && seg != 4 && seg != 5) || (((addr + length) & KSEG_MASK) >= RAM_MIRROR_END) || + if ((seg != 0 && seg != 4 && seg != 5) || (((addr + length) & KSEG_MASK) >= g_ram_mapped_size) || (((addr & g_ram_mask) + length) > g_ram_size)) { const u8* ptr = static_cast(data); @@ -3361,7 +3357,7 @@ bool CPU::SafeZeroMemoryBytes(VirtualMemoryAddress addr, u32 length) using namespace Bus; const u32 seg = (addr >> 29); - if ((seg != 0 && seg != 4 && seg != 5) || (((addr + length) & KSEG_MASK) >= RAM_MIRROR_END) || + if ((seg != 0 && seg != 4 && seg != 5) || (((addr + length) & KSEG_MASK) >= g_ram_mapped_size) || (((addr & g_ram_mask) + length) > g_ram_size)) { while ((addr & 3u) != 0 && length > 0) @@ -3406,7 +3402,7 @@ void* CPU::GetDirectReadMemoryPointer(VirtualMemoryAddress address, MemoryAccess return nullptr; const PhysicalMemoryAddress paddr = VirtualAddressToPhysical(address); - if (paddr < RAM_MIRROR_END) + if (paddr < g_ram_mapped_size) { if (read_ticks) *read_ticks = RAM_READ_TICKS; @@ -3443,7 +3439,7 @@ void* CPU::GetDirectWriteMemoryPointer(VirtualMemoryAddress address, MemoryAcces const PhysicalMemoryAddress paddr = address & KSEG_MASK; - if (paddr < RAM_MIRROR_END) + if (paddr < g_ram_mapped_size) return &g_ram[paddr & g_ram_mask]; if ((paddr & SCRATCHPAD_ADDR_MASK) == SCRATCHPAD_ADDR) diff --git a/src/core/cpu_pgxp.cpp b/src/core/cpu_pgxp.cpp index d983664b6..bd252749e 100644 --- a/src/core/cpu_pgxp.cpp +++ b/src/core/cpu_pgxp.cpp @@ -36,8 +36,8 @@ enum : u32 VERTEX_CACHE_WIDTH = 2048, VERTEX_CACHE_HEIGHT = 2048, VERTEX_CACHE_SIZE = VERTEX_CACHE_WIDTH * VERTEX_CACHE_HEIGHT, - PGXP_MEM_SIZE = (static_cast(Bus::RAM_8MB_SIZE) + static_cast(CPU::SCRATCHPAD_SIZE)) / 4, - PGXP_MEM_SCRATCH_OFFSET = Bus::RAM_8MB_SIZE / 4, + PGXP_MEM_SIZE = (static_cast(Bus::RAM_MAX_SIZE) + static_cast(CPU::SCRATCHPAD_SIZE)) / 4, + PGXP_MEM_SCRATCH_OFFSET = Bus::RAM_MAX_SIZE / 4, }; enum : u32 @@ -140,6 +140,7 @@ void CPU::PGXP::Initialize() std::memset(g_state.pgxp_cop0, 0, sizeof(g_state.pgxp_cop0)); std::memset(g_state.pgxp_gte, 0, sizeof(g_state.pgxp_gte)); + // TODO: Needs to be moved into the memory map if (!s_mem) { s_mem = static_cast(std::calloc(PGXP_MEM_SIZE, sizeof(PGXPValue))); @@ -297,7 +298,7 @@ ALWAYS_INLINE_RELEASE CPU::PGXPValue* CPU::PGXP::GetPtr(u32 addr) // Don't worry about >512MB here for performance reasons. const u32 paddr = (addr & KSEG_MASK); - if (paddr < Bus::RAM_MIRROR_END) + if (paddr < Bus::g_ram_size) return &s_mem[(paddr & Bus::g_ram_mask) >> 2]; else return nullptr; diff --git a/src/core/fullscreenui_settings.cpp b/src/core/fullscreenui_settings.cpp index a4dba8222..2f730b93b 100644 --- a/src/core/fullscreenui_settings.cpp +++ b/src/core/fullscreenui_settings.cpp @@ -3027,7 +3027,9 @@ void FullscreenUI::DrawEmulationSettingsPage() const u32 resolution_scale = GetEffectiveUIntSetting(bsi, "GPU", "ResolutionScale", 1); const u32 multisamples = GetEffectiveUIntSetting(bsi, "GPU", "Multisamples", 1); const bool use_software_renderer = GetEffectiveBoolSetting(bsi, "GPU", "UseSoftwareRendererForMemoryStates", false); - const bool enable_8mb_ram = GetEffectiveBoolSetting(bsi, "Console", "Enable8MBRAM", false); + const u8 ram_size = + static_cast(std::clamp(GetEffectiveUIntSetting(bsi, "CPU", "RAMSize", Settings::DEFAULT_CPU_RAM_SIZE), + Settings::MIN_CPU_RAM_SIZE, Settings::MAX_CPU_RAM_SIZE)); const float rewind_frequency = GetEffectiveFloatSetting(bsi, "Main", "RewindFrequency", 10.0f); const s32 rewind_save_slots = GetEffectiveIntSetting(bsi, "Main", "RewindSaveSlots", 10); const float duration = @@ -3036,7 +3038,7 @@ void FullscreenUI::DrawEmulationSettingsPage() u64 ram_usage, vram_usage; System::CalculateRewindMemoryUsage(rewind_save_slots, resolution_scale, multisamples, use_software_renderer, - enable_8mb_ram, &ram_usage, &vram_usage); + ram_size, &ram_usage, &vram_usage); if (vram_usage > 0) { rewind_summary.format( diff --git a/src/core/imgui_overlays.cpp b/src/core/imgui_overlays.cpp index 29efc56f7..7e526a66b 100644 --- a/src/core/imgui_overlays.cpp +++ b/src/core/imgui_overlays.cpp @@ -587,8 +587,8 @@ void ImGuiManager::DrawEnhancementsOverlay(const GPUBackend* gpu) if (g_settings.cpu_overclock_active) text.append_format(" CPU={}%", g_settings.GetCPUOverclockPercent()); - if (g_settings.cpu_enable_8mb_ram) - text.append(" 8MB"); + if (g_settings.cpu_ram_size != Settings::DEFAULT_CPU_RAM_SIZE) + text.append_format(" {}MB", g_settings.cpu_ram_size); if (g_settings.cdrom_read_speedup != 1) text.append_format(" CDR={}x", g_settings.cdrom_read_speedup); if (g_settings.cdrom_seek_speedup != 1) diff --git a/src/core/memory_scanner.cpp b/src/core/memory_scanner.cpp index f1ec92e9c..4b3230fe5 100644 --- a/src/core/memory_scanner.cpp +++ b/src/core/memory_scanner.cpp @@ -25,7 +25,7 @@ static bool IsValidScanAddress(VirtualMemoryAddress address) } const PhysicalMemoryAddress phys_address = CPU::VirtualAddressToPhysical(address); - if (phys_address < Bus::RAM_MIRROR_END) + if (phys_address < Bus::g_ram_mapped_size) return true; if (phys_address >= Bus::BIOS_BASE && phys_address < (Bus::BIOS_BASE + Bus::BIOS_SIZE)) diff --git a/src/core/settings.cpp b/src/core/settings.cpp index af8c6e74e..d3c7d7de0 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -207,7 +207,6 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro ParseConsoleRegionName( si.GetStringValue("Console", "Region", Settings::GetConsoleRegionName(Settings::DEFAULT_CONSOLE_REGION)).c_str()) .value_or(DEFAULT_CONSOLE_REGION); - cpu_enable_8mb_ram = si.GetBoolValue("Console", "Enable8MBRAM", false); emulation_speed = si.GetFloatValue("Main", "EmulationSpeed", 1.0f); fast_forward_speed = si.GetFloatValue("Main", "FastForwardSpeed", 0.0f); @@ -245,6 +244,7 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro cpu_fastmem_mode = ParseCPUFastmemMode( si.GetStringValue("CPU", "FastmemMode", GetCPUFastmemModeName(DEFAULT_CPU_FASTMEM_MODE)).c_str()) .value_or(DEFAULT_CPU_FASTMEM_MODE); + cpu_ram_size = static_cast(std::clamp(si.GetUIntValue("CPU", "RAMSize", DEFAULT_CPU_RAM_SIZE), 2u, 16u)); gpu_renderer = ParseRendererName(si.GetStringValue("GPU", "Renderer", GetRendererName(DEFAULT_GPU_RENDERER)).c_str()) .value_or(DEFAULT_GPU_RENDERER); @@ -606,7 +606,6 @@ void Settings::LoadPGXPSettings(const SettingsInterface& si) void Settings::Save(SettingsInterface& si, bool ignore_base) const { si.SetStringValue("Console", "Region", GetConsoleRegionName(region)); - si.SetBoolValue("Console", "Enable8MBRAM", cpu_enable_8mb_ram); si.SetFloatValue("Main", "EmulationSpeed", emulation_speed); si.SetFloatValue("Main", "FastForwardSpeed", fast_forward_speed); @@ -643,6 +642,7 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const si.SetBoolValue("CPU", "RecompilerBlockLinking", cpu_recompiler_block_linking); si.SetBoolValue("CPU", "RecompilerICache", cpu_recompiler_icache); si.SetStringValue("CPU", "FastmemMode", GetCPUFastmemModeName(cpu_fastmem_mode)); + si.SetBoolValue("CPU", "RAMSize", cpu_ram_size); si.SetStringValue("GPU", "Renderer", GetRendererName(gpu_renderer)); si.SetStringValue("GPU", "Adapter", gpu_adapter.c_str()); @@ -1068,7 +1068,7 @@ void Settings::ApplySettingRestrictions() region = ConsoleRegion::Auto; cpu_overclock_enable = false; cpu_overclock_active = false; - cpu_enable_8mb_ram = false; + cpu_ram_size = DEFAULT_CPU_RAM_SIZE; gpu_resolution_scale = 1; gpu_multisamples = 1; gpu_automatic_resolution_scale = false; @@ -1421,6 +1421,26 @@ const char* Settings::GetDiscRegionDisplayName(DiscRegion region) return Host::TranslateToCString("Settings", s_disc_region_display_names[static_cast(region)], "DiscRegion"); } +std::span Settings::GetCPURAMSizeOptions() +{ + static constexpr const u8 options[] = {2, 4, 8, 16}; + return options; +} + +const char* Settings::GetCPURAMSizeDisplayName(u8 size) +{ + if (size == 2) + return TRANSLATE("Settings", "2 MB (Retail Console)", "CPURAMSize"); + else if (size == 4) + return TRANSLATE("Settings", "4 MB (Arcade Boards)", "CPURAMSize"); + else if (size == 8) + return TRANSLATE("Settings", "8 MB (Dev Console)", "CPURAMSize"); + else if (size == 16) + return TRANSLATE("Settings", "16 MB (Mods/Arcade Boards)", "CPURAMSize"); + else + return "Unknown"; +} + static constexpr const std::array s_cpu_execution_mode_names = { "Interpreter", "CachedInterpreter", diff --git a/src/core/settings.h b/src/core/settings.h index c602204a9..f21cc5311 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -292,7 +292,6 @@ struct Settings : public GPUSettings bool cpu_recompiler_memory_exceptions : 1 = false; bool cpu_recompiler_block_linking : 1 = true; bool cpu_recompiler_icache : 1 = false; - bool cpu_enable_8mb_ram : 1 = false; bool mdec_use_old_routines : 1 = false; bool mdec_disable_cdrom_speedup : 1 = false; @@ -340,6 +339,8 @@ struct Settings : public GPUSettings u32 cdrom_max_seek_speedup_cycles = DEFAULT_CDROM_MAX_SEEK_SPEEDUP_CYCLES; u32 cdrom_max_read_speedup_cycles = DEFAULT_CDROM_MAX_READ_SPEEDUP_CYCLES; + u8 cpu_ram_size = DEFAULT_CPU_RAM_SIZE; // TODO: Move me up + u8 audio_output_volume = 100; u8 audio_fast_forward_volume = 100; @@ -488,6 +489,9 @@ struct Settings : public GPUSettings static const char* GetDiscRegionName(DiscRegion region); static const char* GetDiscRegionDisplayName(DiscRegion region); + static std::span GetCPURAMSizeOptions(); + static const char* GetCPURAMSizeDisplayName(u8 size); + static std::optional ParseCPUExecutionMode(const char* str); static const char* GetCPUExecutionModeName(CPUExecutionMode mode); static const char* GetCPUExecutionModeDisplayName(CPUExecutionMode mode); @@ -621,6 +625,9 @@ struct Settings : public GPUSettings #else static constexpr CPUFastmemMode DEFAULT_CPU_FASTMEM_MODE = CPUFastmemMode::LUT; #endif + static constexpr u8 DEFAULT_CPU_RAM_SIZE = 2; + static constexpr u8 MIN_CPU_RAM_SIZE = 2; + static constexpr u8 MAX_CPU_RAM_SIZE = 16; static constexpr u8 DEFAULT_CDROM_READAHEAD_SECTORS = 8; static constexpr u32 DEFAULT_CDROM_MAX_SEEK_SPEEDUP_CYCLES = 30000; diff --git a/src/core/system.cpp b/src/core/system.cpp index 938a9e8f3..a35d2b493 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -710,7 +710,7 @@ std::string_view System::GetTaintDisplayName(Taint taint) TRANSLATE_DISAMBIG_NOOP("System", "CD-ROM Read Speedup", "Taint"), TRANSLATE_DISAMBIG_NOOP("System", "CD-ROM Seek Speedup", "Taint"), TRANSLATE_DISAMBIG_NOOP("System", "Force Frame Timings", "Taint"), - TRANSLATE_DISAMBIG_NOOP("System", "8MB RAM", "Taint"), + TRANSLATE_DISAMBIG_NOOP("System", "Expanded RAM", "Taint"), TRANSLATE_DISAMBIG_NOOP("System", "Cheats", "Taint"), TRANSLATE_DISAMBIG_NOOP("System", "Game Patches", "Taint"), TRANSLATE_DISAMBIG_NOOP("System", "Memory Card Mismatch", "Taint"), @@ -2734,7 +2734,7 @@ bool System::AllocateMemoryStates(size_t state_count, bool recycle_old_textures) // Allocate CPU buffers. // TODO: Maybe look at host memory limits here... - const size_t size = GetMaxMemorySaveStateSize(g_settings.cpu_enable_8mb_ram, CPU::PGXP::ShouldSavePGXPState()); + const size_t size = GetMaxMemorySaveStateSize(g_settings.cpu_ram_size, CPU::PGXP::ShouldSavePGXPState()); for (MemorySaveState& mss : s_state.memory_save_states) { mss.state_size = 0; @@ -3012,17 +3012,15 @@ bool System::SetBootMode(BootMode new_boot_mode, DiscRegion disc_region, Error* return true; } -size_t System::GetMaxSaveStateSize(bool enable_8mb_ram) +size_t System::GetMaxSaveStateSize(u8 ram_size) { // 5 megabytes is sufficient for now, at the moment they're around 4.3MB, or 10.3MB with 8MB RAM enabled. - static constexpr u32 MAX_2MB_SAVE_STATE_SIZE = 5 * 1024 * 1024; - static constexpr u32 MAX_8MB_SAVE_STATE_SIZE = 11 * 1024 * 1024; - return enable_8mb_ram ? MAX_8MB_SAVE_STATE_SIZE : MAX_2MB_SAVE_STATE_SIZE; + return (3 + ram_size) * 1024 * 1024; } -size_t System::GetMaxMemorySaveStateSize(bool enable_8mb_ram, bool pgxp) +size_t System::GetMaxMemorySaveStateSize(u8 ram_size, bool pgxp) { - return GetMaxSaveStateSize(enable_8mb_ram) + (pgxp ? CPU::PGXP::GetStateSize() : 0); + return GetMaxSaveStateSize(ram_size) + (pgxp ? CPU::PGXP::GetStateSize() : 0); } std::string System::GetMediaPathFromSaveState(const char* path) @@ -3560,7 +3558,7 @@ bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screen // write data if (buffer->state_data.empty()) - buffer->state_data.resize(GetMaxSaveStateSize(Bus::g_ram_size > Bus::RAM_2MB_SIZE)); + buffer->state_data.resize(GetMaxSaveStateSize(static_cast(Bus::g_ram_size / 1048576))); return SaveStateDataToBuffer(buffer->state_data, &buffer->state_size, error); } @@ -5043,8 +5041,8 @@ void System::SetTaintsFromSettings() SetTaint(Taint::CPUOverclock); if (g_settings.gpu_force_video_timing != ForceVideoTimingMode::Disabled) SetTaint(Taint::ForceFrameTimings); - if (g_settings.cpu_enable_8mb_ram) - SetTaint(Taint::RAM8MB); + if (g_settings.cpu_ram_size) + SetTaint(Taint::ExpandedRAM); if (Cheats::GetActivePatchCount() > 0) SetTaint(Taint::Patches); if (Cheats::GetActiveCheatCount() > 0) @@ -5102,8 +5100,8 @@ void System::WarnAboutUnsafeSettings() { if (g_settings.cpu_overclock_active) append(TRANSLATE_SV("System", "Overclock disabled.")); - if (g_settings.cpu_enable_8mb_ram) - append(TRANSLATE_SV("System", "8MB RAM disabled.")); + if (g_settings.cpu_ram_size != Settings::DEFAULT_CPU_RAM_SIZE) + append(TRANSLATE_SV("System", "RAM size set to default.")); if (g_settings.gpu_resolution_scale != 1) append(TRANSLATE_SV("System", "Resolution scale set to 1x.")); if (g_settings.gpu_multisamples != 1) @@ -5191,8 +5189,8 @@ void System::WarnAboutUnsafeSettings() TRANSLATE_SV("System", "PGXP Geometry Tolerance is not set to default. This may cause rendering errors.")); } } - if (g_settings.cpu_enable_8mb_ram) - append(TRANSLATE_SV("System", "8MB RAM is enabled, this may be incompatible with some games.")); + if (g_settings.cpu_ram_size != Settings::DEFAULT_CPU_RAM_SIZE) + append(TRANSLATE_SV("System", "Expanded RAM is enabled, this may be incompatible with some games.")); if (g_settings.cpu_execution_mode == CPUExecutionMode::CachedInterpreter) append(TRANSLATE_SV("System", "Cached interpreter is being used, this may be incompatible with some games.")); @@ -5256,11 +5254,10 @@ void System::LogUnsafeSettingsToConsole(const SmallStringBase& messages) } void System::CalculateRewindMemoryUsage(u32 num_saves, u32 resolution_scale, u32 multisamples, - bool use_software_renderer, bool enable_8mb_ram, u64* ram_usage, - u64* vram_usage) + bool use_software_renderer, u8 ram_size, u64* ram_usage, u64* vram_usage) { const u32 real_resolution_scale = std::max(resolution_scale, 1u); - *ram_usage = GetMaxMemorySaveStateSize(enable_8mb_ram, false) * static_cast(num_saves); + *ram_usage = GetMaxMemorySaveStateSize(ram_size, false) * static_cast(num_saves); *vram_usage = use_software_renderer ? 0 : ((static_cast(VRAM_WIDTH * real_resolution_scale) * static_cast(VRAM_HEIGHT * real_resolution_scale) * 4) * @@ -5290,7 +5287,7 @@ void System::UpdateMemorySaveStateSettings() u64 ram_usage, vram_usage; CalculateRewindMemoryUsage(g_settings.rewind_save_slots, g_settings.gpu_resolution_scale, g_settings.gpu_multisamples, g_settings.gpu_use_software_renderer_for_memory_states, - g_settings.cpu_enable_8mb_ram, &ram_usage, &vram_usage); + g_settings.cpu_ram_size, &ram_usage, &vram_usage); INFO_LOG("Rewind is enabled, saving every {} frames, with {} slots and {}MB RAM and {}MB VRAM usage", std::max(s_state.rewind_save_frequency, 1), g_settings.rewind_save_slots, ram_usage / 1048576, vram_usage / 1048576); diff --git a/src/core/system.h b/src/core/system.h index f969bb7b3..dfcda8f32 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -118,7 +118,7 @@ enum class Taint : u8 CDROMReadSpeedup, CDROMSeekSpeedup, ForceFrameTimings, - RAM8MB, + ExpandedRAM, Cheats, Patches, MemoryCardMismatch, @@ -281,10 +281,10 @@ void ResetSystem(); bool CanPauseSystem(bool display_message); /// Returns the maximum size of a save state, considering the current configuration. -size_t GetMaxSaveStateSize(bool enable_8mb_ram); +size_t GetMaxSaveStateSize(u8 ram_size); /// Returns the maximum size of a save state that is not expected to be serialized to file. -size_t GetMaxMemorySaveStateSize(bool enable_8mb_ram, bool pgxp); +size_t GetMaxMemorySaveStateSize(u8 ram_size, bool pgxp); /// Loads state from the specified path. std::optional LoadState(const char* path, Error* error, bool save_undo_state, bool force_update_display); @@ -457,7 +457,7 @@ std::string GetImageForLoadingScreen(const std::string& game_path); // Memory Save States (Rewind and Runahead) ////////////////////////////////////////////////////////////////////////// void CalculateRewindMemoryUsage(u32 num_saves, u32 resolution_scale, u32 multisamples, bool use_software_renderer, - bool enable_8mb_ram, u64* ram_usage, u64* vram_usage); + u8 ram_size, u64* ram_usage, u64* vram_usage); void ClearMemorySaveStates(bool reallocate_resources, bool recycle_textures); void SetRunaheadReplayFlag(bool is_analog_input); diff --git a/src/duckstation-qt/consolesettingswidget.cpp b/src/duckstation-qt/consolesettingswidget.cpp index 10ed158b0..40ef49bc2 100644 --- a/src/duckstation-qt/consolesettingswidget.cpp +++ b/src/duckstation-qt/consolesettingswidget.cpp @@ -16,7 +16,7 @@ #include "moc_consolesettingswidget.cpp" -static constexpr const int CDROM_SPEEDUP_VALUES[] = {1, 2, 3, 4, 5, 6, 0}; +static constexpr const u8 CDROM_SPEEDUP_VALUES[] = {1, 2, 3, 4, 5, 6, 0}; ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsWindow* dialog, QWidget* parent) : QWidget(parent), m_dialog(dialog) @@ -40,7 +40,9 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsWindow* dialog, QWidget* pa }); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "BIOS", "PatchFastBoot", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastForwardBoot, "BIOS", "FastForwardBoot", false); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enable8MBRAM, "Console", "Enable8MBRAM", false); + for (const u8& size : Settings::GetCPURAMSizeOptions()) + m_ui.ramSize->addItem(Settings::GetCPURAMSizeDisplayName(size), QVariant(static_cast(size))); + SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.ramSize, "CPU", "RAMSize", Settings::DEFAULT_CPU_RAM_SIZE, Settings::GetCPURAMSizeOptions()); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastForwardMemoryCardAccess, "MemoryCards", "FastForwardAccess", false); connect(m_ui.fastBoot, &QCheckBox::checkStateChanged, this, &ConsoleSettingsWidget::onFastBootChanged); @@ -72,9 +74,9 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsWindow* dialog, QWidget* pa } SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cdromSeekSpeedup, "CDROM", "SeekSpeedup", 1, - CDROM_SPEEDUP_VALUES); + std::span(CDROM_SPEEDUP_VALUES)); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cdromReadSpeedup, "CDROM", "ReadSpeedup", 1, - CDROM_SPEEDUP_VALUES); + std::span(CDROM_SPEEDUP_VALUES)); dialog->registerWidgetHelp(m_ui.region, tr("Region"), tr("Auto-Detect"), tr("Determines the emulated hardware type.")); @@ -93,7 +95,7 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsWindow* dialog, QWidget* pa tr("Fast forwards through memory card access, both loading and saving. Can reduce " "waiting times in games that frequently access memory cards.")); dialog->registerWidgetHelp( - m_ui.enable8MBRAM, tr("Enable 8MB RAM (Dev Console)"), tr("Unchecked"), + m_ui.ramSize, tr("Enable 8MB RAM (Dev Console)"), tr("Unchecked"), tr("Enables an additional 6MB of RAM to obtain a total of 2+6 = 8MB, usually present on dev consoles. Games have " "to use a larger heap size for " "this additional RAM to be usable. Titles which rely on memory mirrors may break, so it should only be used " diff --git a/src/duckstation-qt/consolesettingswidget.ui b/src/duckstation-qt/consolesettingswidget.ui index ee6aa875f..6ed113dfd 100644 --- a/src/duckstation-qt/consolesettingswidget.ui +++ b/src/duckstation-qt/consolesettingswidget.ui @@ -7,7 +7,7 @@ 0 0 600 - 487 + 520 @@ -49,7 +49,17 @@ - + + + + Installed RAM: + + + + + + + @@ -72,13 +82,6 @@ - - - - Enable 8MB RAM (Dev Console) - - - diff --git a/src/duckstation-qt/emulationsettingswidget.cpp b/src/duckstation-qt/emulationsettingswidget.cpp index d5f76df48..7f8b74a04 100644 --- a/src/duckstation-qt/emulationsettingswidget.cpp +++ b/src/duckstation-qt/emulationsettingswidget.cpp @@ -265,14 +265,16 @@ void EmulationSettingsWidget::updateRewind() const u32 multisamples = m_dialog->getEffectiveIntValue("GPU", "Multisamples", 1); const bool use_software_renderer = m_dialog->getEffectiveBoolValue("GPU", "UseSoftwareRendererForMemoryStates", false); - const bool enable_8mb_ram = m_dialog->getEffectiveBoolValue("Console", "Enable8MBRAM", false); + const u8 ram_size = + static_cast(std::clamp(m_dialog->getEffectiveIntValue("CPU", "RAMSize", Settings::DEFAULT_CPU_RAM_SIZE), + Settings::MIN_CPU_RAM_SIZE, Settings::MAX_CPU_RAM_SIZE)); const u32 frames = static_cast(m_ui.rewindSaveSlots->value()); const float frequency = static_cast(m_ui.rewindSaveFrequency->value()); const float duration = ((frequency <= std::numeric_limits::epsilon()) ? (1.0f / 60.0f) : frequency) * static_cast(frames); u64 ram_usage, vram_usage; - System::CalculateRewindMemoryUsage(frames, resolution_scale, multisamples, use_software_renderer, enable_8mb_ram, + System::CalculateRewindMemoryUsage(frames, resolution_scale, multisamples, use_software_renderer, ram_size, &ram_usage, &vram_usage); m_ui.rewindSummary->setText( diff --git a/src/duckstation-qt/settingwidgetbinder.h b/src/duckstation-qt/settingwidgetbinder.h index d583bab1e..02e5ac020 100644 --- a/src/duckstation-qt/settingwidgetbinder.h +++ b/src/duckstation-qt/settingwidgetbinder.h @@ -813,16 +813,16 @@ inline void BindWidgetToIntSetting(SettingsInterface* sif, WidgetType* widget, s } } -template +template inline void BindWidgetToIntSetting(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, - int default_value, std::span values) + int default_value, std::span values) { using Accessor = SettingAccessor; - static constexpr auto value_to_index = [](s32 value, const std::span values) { + static constexpr auto value_to_index = [](s32 value, const std::span values) { for (size_t i = 0; i < values.size(); i++) { - if (values[i] == value) + if (static_cast(values[i]) == value) return static_cast(i); } diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index a506b41ba..de8ac6b12 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -722,7 +722,7 @@ void RegTestHost::DumpSystemStateHashes() // don't save full state on gpu dump, it's not going to be complete... if (!System::IsReplayingGPUDump()) { - DynamicHeapArray state_data(System::GetMaxSaveStateSize(g_settings.cpu_enable_8mb_ram)); + DynamicHeapArray state_data(System::GetMaxSaveStateSize(g_settings.cpu_ram_size)); size_t state_data_size; if (!System::SaveStateDataToBuffer(state_data, &state_data_size, &error)) {