From 0b5287f1999668300256264326c0e0f523414f6a Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 24 Jul 2024 00:14:16 +1000 Subject: [PATCH] Bus: Implement RAM_SIZE register Fixes Rock-Climbing - Mitouhou e no Chousen - Alps Hen (Japan) crashing on boot. Silly game does a 512 word DMA from CDROM to 0x001FFF04, with the mapped memory size set to 4MB, expecting the DMA to trigger a bus error, because if it doesn't, then your kernel memory goes bye bye. A bunch of other games do set the upper 4MB to be unmapped, but this change appears to have no effect on them. As expected, they're probably doing the correct thing. --- src/core/bus.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++------ src/core/bus.h | 1 + src/core/dma.cpp | 2 +- 3 files changed, 87 insertions(+), 13 deletions(-) diff --git a/src/core/bus.cpp b/src/core/bus.cpp index 757d8bcf0..780187a11 100644 --- a/src/core/bus.cpp +++ b/src/core/bus.cpp @@ -102,6 +102,14 @@ union MEMCTRL COMDELAY common_delay; }; }; + +union RAM_SIZE_REG +{ + u32 bits; + + // All other bits unknown/unhandled. + BitField memory_window; +}; } // namespace static void* s_shmem_handle = nullptr; @@ -110,6 +118,7 @@ std::bitset g_ram_code_bits{}; u8* g_ram = nullptr; u8* g_unprotected_ram = nullptr; u32 g_ram_size = 0; +u32 g_ram_mapped_size = 0; u32 g_ram_mask = 0; u8* g_bios = nullptr; void** g_memory_handlers = nullptr; @@ -124,7 +133,7 @@ std::array g_spu_access_time = {}; static std::vector s_exp1_rom; static MEMCTRL s_MEMCTRL = {}; -static u32 s_ram_size_reg = 0; +static RAM_SIZE_REG s_RAM_SIZE = {}; static std::string s_tty_line_buffer; @@ -147,6 +156,7 @@ static u8* GetLUTFastmemPointer(u32 address, u8* ram_ptr); static void SetRAMPageWritable(u32 page_index, bool writable); static void SetHandlers(); +static void UpdateMappedRAMSize(); template static FP* OffsetHandlerArray(void** handlers, MemoryAccessSize size, MemoryAccessType type); @@ -337,9 +347,15 @@ void Bus::Reset() s_MEMCTRL.cdrom_delay_size.bits = 0x00020843; s_MEMCTRL.exp2_delay_size.bits = 0x00070777; s_MEMCTRL.common_delay.bits = 0x00031125; - s_ram_size_reg = UINT32_C(0x00000B88); g_ram_code_bits = {}; RecalculateMemoryTimings(); + + // Avoid remapping if unchanged. + if (s_RAM_SIZE.bits != 0x00000B88) + { + s_RAM_SIZE.bits = 0x00000B88; + UpdateMappedRAMSize(); + } } void Bus::AddTTYCharacter(char ch) @@ -397,7 +413,12 @@ bool Bus::DoState(StateWrapper& sw) } sw.DoArray(s_MEMCTRL.regs, countof(s_MEMCTRL.regs)); - sw.Do(&s_ram_size_reg); + + const RAM_SIZE_REG old_ram_size_reg = s_RAM_SIZE; + sw.Do(&s_RAM_SIZE.bits); + if (s_RAM_SIZE.memory_window != old_ram_size_reg.memory_window) + UpdateMappedRAMSize(); + sw.Do(&s_tty_line_buffer); return !sw.HasError(); } @@ -1281,7 +1302,7 @@ u32 Bus::HWHandlers::MemCtrl2Read(PhysicalMemoryAddress address) u32 value; if (offset == 0x00) { - value = s_ram_size_reg; + value = s_RAM_SIZE.bits; } else { @@ -1299,10 +1320,16 @@ void Bus::HWHandlers::MemCtrl2Write(PhysicalMemoryAddress address, u32 value) if (offset == 0x00) { - if (s_ram_size_reg != value) + if (s_RAM_SIZE.bits != value) + { DEV_LOG("RAM size register set to 0x{:08X}", value); - s_ram_size_reg = value; + const RAM_SIZE_REG old_ram_size_reg = s_RAM_SIZE; + s_RAM_SIZE.bits = value; + + if (s_RAM_SIZE.memory_window != old_ram_size_reg.memory_window) + UpdateMappedRAMSize(); + } } else { @@ -1641,6 +1668,11 @@ void Bus::HardwareWriteHandler(VirtualMemoryAddress address, u32 value) ////////////////////////////////////////////////////////////////////////// +static constexpr u32 KUSEG = 0; +static constexpr u32 KSEG0 = 0x80000000U; +static constexpr u32 KSEG1 = 0xA0000000U; +static constexpr u32 KSEG2 = 0xC0000000U; + void Bus::SetHandlers() { ClearHandlers(g_memory_handlers); @@ -1655,11 +1687,6 @@ void Bus::SetHandlers() SET(g_memory_handlers, start, size, read_handler, write_handler); \ SET(g_memory_handlers_isc, start, size, read_handler, write_handler) - static constexpr u32 KUSEG = 0; - static constexpr u32 KSEG0 = 0x80000000U; - static constexpr u32 KSEG1 = 0xA0000000U; - static constexpr u32 KSEG2 = 0xC0000000U; - // KUSEG - Cached // Cache isolated appears to affect KUSEG+KSEG0. SET(g_memory_handlers, KUSEG | RAM_BASE, RAM_MIRROR_SIZE, RAMReadHandler, RAMWriteHandler); @@ -1691,10 +1718,56 @@ void Bus::SetHandlers() // KSEG2 - Uncached - 0xFFFE0130 SETUC(KSEG2 | 0xFFFE0000, 0x1000, CacheControlReadHandler, CacheControlWriteHandler); +} + +void Bus::UpdateMappedRAMSize() +{ + 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; + + 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]]; + + 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; + } +} #undef SET #undef SETUC -} void Bus::ClearHandlers(void** handlers) { diff --git a/src/core/bus.h b/src/core/bus.h index 930dc9283..ead3a359a 100644 --- a/src/core/bus.h +++ b/src/core/bus.h @@ -143,6 +143,7 @@ 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. +extern u32 g_ram_mapped_size; // Maximum mapped address for RAM, determined by RAM size register. extern u32 g_ram_mask; // Active address bits for RAM. extern u8* g_bios; // 512K BIOS ROM extern std::array g_exp1_access_time; diff --git a/src/core/dma.cpp b/src/core/dma.cpp index ecf5ea870..ea90daa15 100644 --- a/src/core/dma.cpp +++ b/src/core/dma.cpp @@ -506,7 +506,7 @@ ALWAYS_INLINE_RELEASE bool DMA::CheckForBusError(Channel channel, ChannelState& u32 size) { // Relying on a transfer partially happening at the end of RAM, then hitting a bus error would be pretty silly. - if ((address + size) > Bus::RAM_8MB_SIZE) [[unlikely]] + if ((address + size) >= Bus::g_ram_mapped_size) [[unlikely]] { DEBUG_LOG("DMA bus error on channel {} at address 0x{:08X} size {}", channel, address, size); cs.channel_control.enable_busy = false;