duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

bus.cpp (69192B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "bus.h"
      5 #include "bios.h"
      6 #include "cdrom.h"
      7 #include "cpu_code_cache.h"
      8 #include "cpu_core.h"
      9 #include "cpu_core_private.h"
     10 #include "cpu_disasm.h"
     11 #include "dma.h"
     12 #include "gpu.h"
     13 #include "host.h"
     14 #include "interrupt_controller.h"
     15 #include "mdec.h"
     16 #include "pad.h"
     17 #include "psf_loader.h"
     18 #include "settings.h"
     19 #include "sio.h"
     20 #include "spu.h"
     21 #include "system.h"
     22 #include "timers.h"
     23 #include "timing_event.h"
     24 
     25 #include "util/cd_image.h"
     26 #include "util/state_wrapper.h"
     27 
     28 #include "common/align.h"
     29 #include "common/assert.h"
     30 #include "common/error.h"
     31 #include "common/file_system.h"
     32 #include "common/intrin.h"
     33 #include "common/log.h"
     34 #include "common/memmap.h"
     35 #include "common/path.h"
     36 
     37 #include <cstdio>
     38 #include <tuple>
     39 #include <utility>
     40 
     41 Log_SetChannel(Bus);
     42 
     43 // TODO: Get rid of page code bits, instead use page faults to track SMC.
     44 
     45 // Exports for external debugger access
     46 #ifndef __ANDROID__
     47 namespace Exports {
     48 
     49 extern "C" {
     50 #ifdef _WIN32
     51 _declspec(dllexport) uintptr_t RAM;
     52 _declspec(dllexport) u32 RAM_SIZE, RAM_MASK;
     53 #else
     54 __attribute__((visibility("default"), used)) uintptr_t RAM;
     55 __attribute__((visibility("default"), used)) u32 RAM_SIZE, RAM_MASK;
     56 #endif
     57 }
     58 
     59 } // namespace Exports
     60 #endif
     61 
     62 namespace Bus {
     63 
     64 namespace {
     65 union MEMDELAY
     66 {
     67   u32 bits;
     68 
     69   BitField<u32, u8, 4, 4> access_time; // cycles
     70   BitField<u32, bool, 8, 1> use_com0_time;
     71   BitField<u32, bool, 9, 1> use_com1_time;
     72   BitField<u32, bool, 10, 1> use_com2_time;
     73   BitField<u32, bool, 11, 1> use_com3_time;
     74   BitField<u32, bool, 12, 1> data_bus_16bit;
     75   BitField<u32, u8, 16, 5> memory_window_size;
     76 
     77   static constexpr u32 WRITE_MASK = 0b10101111'00011111'11111111'11111111;
     78 };
     79 
     80 union COMDELAY
     81 {
     82   u32 bits;
     83 
     84   BitField<u32, u8, 0, 4> com0;
     85   BitField<u32, u8, 4, 4> com1;
     86   BitField<u32, u8, 8, 4> com2;
     87   BitField<u32, u8, 12, 4> com3;
     88   BitField<u32, u8, 16, 2> comunk;
     89 
     90   static constexpr u32 WRITE_MASK = 0b00000000'00000011'11111111'11111111;
     91 };
     92 
     93 union MEMCTRL
     94 {
     95   u32 regs[MEMCTRL_REG_COUNT];
     96 
     97   struct
     98   {
     99     u32 exp1_base;
    100     u32 exp2_base;
    101     MEMDELAY exp1_delay_size;
    102     MEMDELAY exp3_delay_size;
    103     MEMDELAY bios_delay_size;
    104     MEMDELAY spu_delay_size;
    105     MEMDELAY cdrom_delay_size;
    106     MEMDELAY exp2_delay_size;
    107     COMDELAY common_delay;
    108   };
    109 };
    110 
    111 union RAM_SIZE_REG
    112 {
    113   u32 bits;
    114 
    115   // All other bits unknown/unhandled.
    116   BitField<u32, u8, 9, 3> memory_window;
    117 };
    118 } // namespace
    119 
    120 static void* s_shmem_handle = nullptr;
    121 static std::string s_shmem_name;
    122 
    123 std::bitset<RAM_8MB_CODE_PAGE_COUNT> g_ram_code_bits{};
    124 u8* g_ram = nullptr;
    125 u8* g_unprotected_ram = nullptr;
    126 u32 g_ram_size = 0;
    127 u32 g_ram_mapped_size = 0;
    128 u32 g_ram_mask = 0;
    129 u8* g_bios = nullptr;
    130 void** g_memory_handlers = nullptr;
    131 void** g_memory_handlers_isc = nullptr;
    132 
    133 std::array<TickCount, 3> g_exp1_access_time = {};
    134 std::array<TickCount, 3> g_exp2_access_time = {};
    135 std::array<TickCount, 3> g_bios_access_time = {};
    136 std::array<TickCount, 3> g_cdrom_access_time = {};
    137 std::array<TickCount, 3> g_spu_access_time = {};
    138 
    139 static std::vector<u8> s_exp1_rom;
    140 
    141 static MEMCTRL s_MEMCTRL = {};
    142 static RAM_SIZE_REG s_RAM_SIZE = {};
    143 
    144 static std::string s_tty_line_buffer;
    145 
    146 static CPUFastmemMode s_fastmem_mode = CPUFastmemMode::Disabled;
    147 
    148 #ifdef ENABLE_MMAP_FASTMEM
    149 static SharedMemoryMappingArea s_fastmem_arena;
    150 static std::vector<std::pair<u8*, size_t>> s_fastmem_ram_views;
    151 #endif
    152 
    153 static u8** s_fastmem_lut = nullptr;
    154 
    155 static bool s_kernel_initialize_hook_run = false;
    156 
    157 static bool AllocateMemoryMap(bool export_shared_memory, Error* error);
    158 static void ReleaseMemoryMap();
    159 static void SetRAMSize(bool enable_8mb_ram);
    160 
    161 static std::tuple<TickCount, TickCount, TickCount> CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay);
    162 static void RecalculateMemoryTimings();
    163 
    164 static u8* GetLUTFastmemPointer(u32 address, u8* ram_ptr);
    165 
    166 static void SetRAMPageWritable(u32 page_index, bool writable);
    167 
    168 static void KernelInitializedHook();
    169 static bool SideloadEXE(const std::string& path, Error* error);
    170 
    171 static void SetHandlers();
    172 static void UpdateMappedRAMSize();
    173 
    174 template<typename FP>
    175 static FP* OffsetHandlerArray(void** handlers, MemoryAccessSize size, MemoryAccessType type);
    176 } // namespace Bus
    177 
    178 namespace MemoryMap {
    179 static constexpr size_t RAM_OFFSET = 0;
    180 static constexpr size_t RAM_SIZE = Bus::RAM_8MB_SIZE;
    181 static constexpr size_t BIOS_OFFSET = RAM_OFFSET + RAM_SIZE;
    182 static constexpr size_t BIOS_SIZE = Bus::BIOS_SIZE;
    183 static constexpr size_t LUT_OFFSET = BIOS_OFFSET + BIOS_SIZE;
    184 static constexpr size_t LUT_SIZE = (sizeof(void*) * Bus::MEMORY_LUT_SLOTS) * 2; // normal and isolated
    185 static constexpr size_t TOTAL_SIZE = LUT_OFFSET + LUT_SIZE;
    186 } // namespace MemoryMap
    187 
    188 #define FIXUP_HALFWORD_OFFSET(size, offset) ((size >= MemoryAccessSize::HalfWord) ? (offset) : ((offset) & ~1u))
    189 #define FIXUP_HALFWORD_READ_VALUE(size, offset, value)                                                                 \
    190   ((size >= MemoryAccessSize::HalfWord) ? (value) : ((value) >> (((offset) & u32(1)) * 8u)))
    191 #define FIXUP_HALFWORD_WRITE_VALUE(size, offset, value)                                                                \
    192   ((size >= MemoryAccessSize::HalfWord) ? (value) : ((value) << (((offset) & u32(1)) * 8u)))
    193 
    194 #define FIXUP_WORD_OFFSET(size, offset) ((size == MemoryAccessSize::Word) ? (offset) : ((offset) & ~3u))
    195 #define FIXUP_WORD_READ_VALUE(size, offset, value)                                                                     \
    196   ((size == MemoryAccessSize::Word) ? (value) : ((value) >> (((offset) & 3u) * 8)))
    197 #define FIXUP_WORD_WRITE_VALUE(size, offset, value)                                                                    \
    198   ((size == MemoryAccessSize::Word) ? (value) : ((value) << (((offset) & 3u) * 8)))
    199 
    200 bool Bus::AllocateMemoryMap(bool export_shared_memory, Error* error)
    201 {
    202   INFO_LOG("Allocating{} shared memory map.", export_shared_memory ? " EXPORTED" : "");
    203   if (export_shared_memory)
    204   {
    205     s_shmem_name = MemMap::GetFileMappingName("duckstation");
    206     INFO_LOG("Shared memory object name is \"{}\".", s_shmem_name);
    207   }
    208   s_shmem_handle = MemMap::CreateSharedMemory(s_shmem_name.c_str(), MemoryMap::TOTAL_SIZE, error);
    209   if (!s_shmem_handle)
    210   {
    211 #ifndef __linux__
    212     Error::AddSuffix(error, "\nYou may need to close some programs to free up additional memory.");
    213 #else
    214     Error::AddSuffix(
    215       error, "\nYou may need to close some programs to free up additional memory, or increase the size of /dev/shm.");
    216 #endif
    217     return false;
    218   }
    219 
    220   g_ram = static_cast<u8*>(MemMap::MapSharedMemory(s_shmem_handle, MemoryMap::RAM_OFFSET, nullptr, MemoryMap::RAM_SIZE,
    221                                                    PageProtect::ReadWrite));
    222   g_unprotected_ram = static_cast<u8*>(MemMap::MapSharedMemory(s_shmem_handle, MemoryMap::RAM_OFFSET, nullptr,
    223                                                                MemoryMap::RAM_SIZE, PageProtect::ReadWrite));
    224   if (!g_ram || !g_unprotected_ram)
    225   {
    226     Error::SetStringView(error, "Failed to map memory for RAM");
    227     ReleaseMemoryMap();
    228     return false;
    229   }
    230 
    231   VERBOSE_LOG("RAM is mapped at {}.", static_cast<void*>(g_ram));
    232 
    233   g_bios = static_cast<u8*>(MemMap::MapSharedMemory(s_shmem_handle, MemoryMap::BIOS_OFFSET, nullptr,
    234                                                     MemoryMap::BIOS_SIZE, PageProtect::ReadWrite));
    235   if (!g_bios)
    236   {
    237     Error::SetStringView(error, "Failed to map memory for BIOS");
    238     ReleaseMemoryMap();
    239     return false;
    240   }
    241 
    242   VERBOSE_LOG("BIOS is mapped at {}.", static_cast<void*>(g_bios));
    243 
    244   g_memory_handlers = static_cast<void**>(MemMap::MapSharedMemory(s_shmem_handle, MemoryMap::LUT_OFFSET, nullptr,
    245                                                                   MemoryMap::LUT_SIZE, PageProtect::ReadWrite));
    246   if (!g_memory_handlers)
    247   {
    248     Error::SetStringView(error, "Failed to map memory for LUTs");
    249     ReleaseMemoryMap();
    250     return false;
    251   }
    252 
    253   VERBOSE_LOG("LUTs are mapped at {}.", static_cast<void*>(g_memory_handlers));
    254   g_memory_handlers_isc = g_memory_handlers + MEMORY_LUT_SLOTS;
    255   SetHandlers();
    256 
    257 #ifndef __ANDROID__
    258   Exports::RAM = reinterpret_cast<uintptr_t>(g_unprotected_ram);
    259 #endif
    260 
    261   return true;
    262 }
    263 
    264 void Bus::ReleaseMemoryMap()
    265 {
    266 #ifndef __ANDROID__
    267   Exports::RAM = 0;
    268   Exports::RAM_SIZE = 0;
    269   Exports::RAM_MASK = 0;
    270 #endif
    271 
    272   g_memory_handlers_isc = nullptr;
    273   if (g_memory_handlers)
    274   {
    275     MemMap::UnmapSharedMemory(g_memory_handlers, MemoryMap::LUT_SIZE);
    276     g_memory_handlers = nullptr;
    277   }
    278 
    279   if (g_bios)
    280   {
    281     MemMap::UnmapSharedMemory(g_bios, MemoryMap::BIOS_SIZE);
    282     g_bios = nullptr;
    283   }
    284 
    285   if (g_unprotected_ram)
    286   {
    287     MemMap::UnmapSharedMemory(g_unprotected_ram, MemoryMap::RAM_SIZE);
    288     g_unprotected_ram = nullptr;
    289   }
    290 
    291   if (g_ram)
    292   {
    293     MemMap::UnmapSharedMemory(g_ram, MemoryMap::RAM_SIZE);
    294     g_ram = nullptr;
    295   }
    296 
    297   if (s_shmem_handle)
    298   {
    299     MemMap::DestroySharedMemory(s_shmem_handle);
    300     s_shmem_handle = nullptr;
    301 
    302     if (!s_shmem_name.empty())
    303     {
    304       MemMap::DeleteSharedMemory(s_shmem_name.c_str());
    305       s_shmem_name = {};
    306     }
    307   }
    308 }
    309 
    310 bool Bus::AllocateMemory(bool export_shared_memory, Error* error)
    311 {
    312   if (!AllocateMemoryMap(export_shared_memory, error))
    313     return false;
    314 
    315 #ifdef ENABLE_MMAP_FASTMEM
    316   if (!s_fastmem_arena.Create(FASTMEM_ARENA_SIZE))
    317   {
    318     Error::SetStringView(error, "Failed to create fastmem arena");
    319     ReleaseMemory();
    320     return false;
    321   }
    322 
    323   INFO_LOG("Fastmem base: {}", static_cast<void*>(s_fastmem_arena.BasePointer()));
    324 #endif
    325 
    326   return true;
    327 }
    328 
    329 void Bus::ReleaseMemory()
    330 {
    331 #ifdef ENABLE_MMAP_FASTMEM
    332   DebugAssert(s_fastmem_ram_views.empty());
    333   s_fastmem_arena.Destroy();
    334 #endif
    335 
    336   std::free(s_fastmem_lut);
    337   s_fastmem_lut = nullptr;
    338 
    339   ReleaseMemoryMap();
    340 }
    341 
    342 bool Bus::ReallocateMemoryMap(bool export_shared_memory, Error* error)
    343 {
    344   // Need to back up RAM+BIOS.
    345   DynamicHeapArray<u8> ram_backup;
    346   DynamicHeapArray<u8> bios_backup;
    347 
    348   if (System::IsValid())
    349   {
    350     CPU::CodeCache::InvalidateAllRAMBlocks();
    351     UpdateFastmemViews(CPUFastmemMode::Disabled);
    352 
    353     ram_backup.resize(RAM_8MB_SIZE);
    354     std::memcpy(ram_backup.data(), g_unprotected_ram, RAM_8MB_SIZE);
    355     bios_backup.resize(BIOS_SIZE);
    356     std::memcpy(bios_backup.data(), g_bios, BIOS_SIZE);
    357   }
    358 
    359   ReleaseMemoryMap();
    360   if (!AllocateMemoryMap(export_shared_memory, error)) [[unlikely]]
    361     return false;
    362 
    363   if (System::IsValid())
    364   {
    365     UpdateMappedRAMSize();
    366     std::memcpy(g_unprotected_ram, ram_backup.data(), RAM_8MB_SIZE);
    367     std::memcpy(g_bios, bios_backup.data(), BIOS_SIZE);
    368     UpdateFastmemViews(g_settings.cpu_fastmem_mode);
    369   }
    370 
    371   return true;
    372 }
    373 
    374 void Bus::CleanupMemoryMap()
    375 {
    376 #if !defined(_WIN32) && !defined(__ANDROID__)
    377   // This is only needed on Linux.
    378   if (!s_shmem_name.empty())
    379     MemMap::DeleteSharedMemory(s_shmem_name.c_str());
    380 #endif
    381 }
    382 
    383 bool Bus::Initialize()
    384 {
    385   SetRAMSize(g_settings.enable_8mb_ram);
    386   Reset();
    387   return true;
    388 }
    389 
    390 void Bus::SetRAMSize(bool enable_8mb_ram)
    391 {
    392   g_ram_size = enable_8mb_ram ? RAM_8MB_SIZE : RAM_2MB_SIZE;
    393   g_ram_mask = enable_8mb_ram ? RAM_8MB_MASK : RAM_2MB_MASK;
    394 
    395 #ifndef __ANDROID__
    396   Exports::RAM_SIZE = g_ram_size;
    397   Exports::RAM_MASK = g_ram_mask;
    398 #endif
    399 }
    400 
    401 void Bus::Shutdown()
    402 {
    403   UpdateFastmemViews(CPUFastmemMode::Disabled);
    404   CPU::g_state.fastmem_base = nullptr;
    405 
    406   g_ram_mask = 0;
    407   g_ram_size = 0;
    408 }
    409 
    410 void Bus::Reset()
    411 {
    412   std::memset(g_ram, 0, g_ram_size);
    413   s_MEMCTRL.exp1_base = 0x1F000000;
    414   s_MEMCTRL.exp2_base = 0x1F802000;
    415   s_MEMCTRL.exp1_delay_size.bits = 0x0013243F;
    416   s_MEMCTRL.exp3_delay_size.bits = 0x00003022;
    417   s_MEMCTRL.bios_delay_size.bits = 0x0013243F;
    418   s_MEMCTRL.spu_delay_size.bits = 0x200931E1;
    419   s_MEMCTRL.cdrom_delay_size.bits = 0x00020843;
    420   s_MEMCTRL.exp2_delay_size.bits = 0x00070777;
    421   s_MEMCTRL.common_delay.bits = 0x00031125;
    422   g_ram_code_bits = {};
    423   s_kernel_initialize_hook_run = false;
    424   RecalculateMemoryTimings();
    425 
    426   // Avoid remapping if unchanged.
    427   if (s_RAM_SIZE.bits != 0x00000B88)
    428   {
    429     s_RAM_SIZE.bits = 0x00000B88;
    430     UpdateMappedRAMSize();
    431   }
    432 }
    433 
    434 bool Bus::DoState(StateWrapper& sw)
    435 {
    436   u32 ram_size = g_ram_size;
    437   sw.DoEx(&ram_size, 52, static_cast<u32>(RAM_2MB_SIZE));
    438   if (ram_size != g_ram_size)
    439   {
    440     const bool using_8mb_ram = (ram_size == RAM_8MB_SIZE);
    441     SetRAMSize(using_8mb_ram);
    442     UpdateFastmemViews(s_fastmem_mode);
    443     CPU::UpdateMemoryPointers();
    444   }
    445 
    446   sw.Do(&g_exp1_access_time);
    447   sw.Do(&g_exp2_access_time);
    448   sw.Do(&g_bios_access_time);
    449   sw.Do(&g_cdrom_access_time);
    450   sw.Do(&g_spu_access_time);
    451   sw.DoBytes(g_ram, g_ram_size);
    452 
    453   if (sw.GetVersion() < 58) [[unlikely]]
    454   {
    455     WARNING_LOG("Overwriting loaded BIOS with old save state.");
    456     sw.DoBytes(g_bios, BIOS_SIZE);
    457   }
    458 
    459   sw.DoArray(s_MEMCTRL.regs, countof(s_MEMCTRL.regs));
    460 
    461   const RAM_SIZE_REG old_ram_size_reg = s_RAM_SIZE;
    462   sw.Do(&s_RAM_SIZE.bits);
    463   if (s_RAM_SIZE.memory_window != old_ram_size_reg.memory_window)
    464     UpdateMappedRAMSize();
    465 
    466   sw.Do(&s_tty_line_buffer);
    467 
    468   sw.DoEx(&s_kernel_initialize_hook_run, 68, true);
    469 
    470   return !sw.HasError();
    471 }
    472 
    473 std::tuple<TickCount, TickCount, TickCount> Bus::CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay)
    474 {
    475   // from nocash spec
    476   s32 first = 0, seq = 0, min = 0;
    477   if (mem_delay.use_com0_time)
    478   {
    479     first += s32(common_delay.com0) - 1;
    480     seq += s32(common_delay.com0) - 1;
    481   }
    482   if (mem_delay.use_com2_time)
    483   {
    484     first += s32(common_delay.com2);
    485     seq += s32(common_delay.com2);
    486   }
    487   if (mem_delay.use_com3_time)
    488   {
    489     min = s32(common_delay.com3);
    490   }
    491   if (first < 6)
    492     first++;
    493 
    494   first = first + s32(mem_delay.access_time) + 2;
    495   seq = seq + s32(mem_delay.access_time) + 2;
    496 
    497   if (first < (min + 6))
    498     first = min + 6;
    499   if (seq < (min + 2))
    500     seq = min + 2;
    501 
    502   const TickCount byte_access_time = first;
    503   const TickCount halfword_access_time = mem_delay.data_bus_16bit ? first : (first + seq);
    504   const TickCount word_access_time = mem_delay.data_bus_16bit ? (first + seq) : (first + seq + seq + seq);
    505   return std::tie(std::max(byte_access_time - 1, 0), std::max(halfword_access_time - 1, 0),
    506                   std::max(word_access_time - 1, 0));
    507 }
    508 
    509 void Bus::RecalculateMemoryTimings()
    510 {
    511   std::tie(g_bios_access_time[0], g_bios_access_time[1], g_bios_access_time[2]) =
    512     CalculateMemoryTiming(s_MEMCTRL.bios_delay_size, s_MEMCTRL.common_delay);
    513   std::tie(g_cdrom_access_time[0], g_cdrom_access_time[1], g_cdrom_access_time[2]) =
    514     CalculateMemoryTiming(s_MEMCTRL.cdrom_delay_size, s_MEMCTRL.common_delay);
    515   std::tie(g_spu_access_time[0], g_spu_access_time[1], g_spu_access_time[2]) =
    516     CalculateMemoryTiming(s_MEMCTRL.spu_delay_size, s_MEMCTRL.common_delay);
    517 
    518   TRACE_LOG("BIOS Memory Timing: {} bit bus, byte={}, halfword={}, word={}",
    519             s_MEMCTRL.bios_delay_size.data_bus_16bit ? 16 : 8, g_bios_access_time[0] + 1, g_bios_access_time[1] + 1,
    520             g_bios_access_time[2] + 1);
    521   TRACE_LOG("CDROM Memory Timing: {} bit bus, byte={}, halfword={}, word={}",
    522             s_MEMCTRL.cdrom_delay_size.data_bus_16bit ? 16 : 8, g_cdrom_access_time[0] + 1, g_cdrom_access_time[1] + 1,
    523             g_cdrom_access_time[2] + 1);
    524   TRACE_LOG("SPU Memory Timing: {} bit bus, byte={}, halfword={}, word={}",
    525             s_MEMCTRL.spu_delay_size.data_bus_16bit ? 16 : 8, g_spu_access_time[0] + 1, g_spu_access_time[1] + 1,
    526             g_spu_access_time[2] + 1);
    527 }
    528 
    529 CPUFastmemMode Bus::GetFastmemMode()
    530 {
    531   return s_fastmem_mode;
    532 }
    533 
    534 void* Bus::GetFastmemBase(bool isc)
    535 {
    536 #ifdef ENABLE_MMAP_FASTMEM
    537   if (s_fastmem_mode == CPUFastmemMode::MMap)
    538     return isc ? nullptr : s_fastmem_arena.BasePointer();
    539 #endif
    540   if (s_fastmem_mode == CPUFastmemMode::LUT)
    541     return reinterpret_cast<u8*>(s_fastmem_lut + (isc ? (FASTMEM_LUT_SIZE * sizeof(void*)) : 0));
    542 
    543   return nullptr;
    544 }
    545 
    546 u8* Bus::GetLUTFastmemPointer(u32 address, u8* ram_ptr)
    547 {
    548   return ram_ptr - address;
    549 }
    550 
    551 void Bus::UpdateFastmemViews(CPUFastmemMode mode)
    552 {
    553 #ifndef ENABLE_MMAP_FASTMEM
    554   Assert(mode != CPUFastmemMode::MMap);
    555 #else
    556   for (const auto& it : s_fastmem_ram_views)
    557     s_fastmem_arena.Unmap(it.first, it.second);
    558   s_fastmem_ram_views.clear();
    559 #endif
    560 
    561   s_fastmem_mode = mode;
    562   if (mode == CPUFastmemMode::Disabled)
    563     return;
    564 
    565 #ifdef ENABLE_MMAP_FASTMEM
    566   if (mode == CPUFastmemMode::MMap)
    567   {
    568     auto MapRAM = [](u32 base_address) {
    569       u8* map_address = s_fastmem_arena.BasePointer() + base_address;
    570       if (!s_fastmem_arena.Map(s_shmem_handle, 0, map_address, g_ram_size, PageProtect::ReadWrite)) [[unlikely]]
    571       {
    572         ERROR_LOG("Failed to map RAM at fastmem area {} (offset 0x{:08X})", static_cast<void*>(map_address),
    573                   g_ram_size);
    574         return;
    575       }
    576 
    577       // mark all pages with code as non-writable
    578       for (u32 i = 0; i < static_cast<u32>(g_ram_code_bits.size()); i++)
    579       {
    580         if (g_ram_code_bits[i])
    581         {
    582           u8* page_address = map_address + (i * HOST_PAGE_SIZE);
    583           if (!MemMap::MemProtect(page_address, HOST_PAGE_SIZE, PageProtect::ReadOnly)) [[unlikely]]
    584           {
    585             ERROR_LOG("Failed to write-protect code page at {}", static_cast<void*>(page_address));
    586             s_fastmem_arena.Unmap(map_address, g_ram_size);
    587             return;
    588           }
    589         }
    590       }
    591 
    592       s_fastmem_ram_views.emplace_back(map_address, g_ram_size);
    593     };
    594 
    595     // KUSEG - cached
    596     MapRAM(0x00000000);
    597 
    598     // KSEG0 - cached
    599     MapRAM(0x80000000);
    600 
    601     // KSEG1 - uncached
    602     MapRAM(0xA0000000);
    603 
    604     return;
    605   }
    606 #endif
    607 
    608   if (!s_fastmem_lut)
    609   {
    610     s_fastmem_lut = static_cast<u8**>(std::malloc(sizeof(u8*) * FASTMEM_LUT_SLOTS));
    611     Assert(s_fastmem_lut);
    612 
    613     INFO_LOG("Fastmem base (software): {}", static_cast<void*>(s_fastmem_lut));
    614   }
    615 
    616   // This assumes the top 4KB of address space is not mapped. It shouldn't be on any sane OSes.
    617   for (u32 i = 0; i < FASTMEM_LUT_SLOTS; i++)
    618     s_fastmem_lut[i] = GetLUTFastmemPointer(i << FASTMEM_LUT_PAGE_SHIFT, nullptr);
    619 
    620   auto MapRAM = [](u32 base_address) {
    621     u8* ram_ptr = g_ram + (base_address & g_ram_mask);
    622     for (u32 address = 0; address < g_ram_size; address += FASTMEM_LUT_PAGE_SIZE)
    623     {
    624       const u32 lut_index = (base_address + address) >> FASTMEM_LUT_PAGE_SHIFT;
    625       s_fastmem_lut[lut_index] = GetLUTFastmemPointer(base_address + address, ram_ptr);
    626       ram_ptr += FASTMEM_LUT_PAGE_SIZE;
    627     }
    628   };
    629 
    630   // KUSEG - cached
    631   MapRAM(0x00000000);
    632   MapRAM(0x00200000);
    633   MapRAM(0x00400000);
    634   MapRAM(0x00600000);
    635 
    636   // KSEG0 - cached
    637   MapRAM(0x80000000);
    638   MapRAM(0x80200000);
    639   MapRAM(0x80400000);
    640   MapRAM(0x80600000);
    641 
    642   // KSEG1 - uncached
    643   MapRAM(0xA0000000);
    644   MapRAM(0xA0200000);
    645   MapRAM(0xA0400000);
    646   MapRAM(0xA0600000);
    647 }
    648 
    649 bool Bus::CanUseFastmemForAddress(VirtualMemoryAddress address)
    650 {
    651   const PhysicalMemoryAddress paddr = address & CPU::PHYSICAL_MEMORY_ADDRESS_MASK;
    652 
    653   switch (s_fastmem_mode)
    654   {
    655 #ifdef ENABLE_MMAP_FASTMEM
    656     case CPUFastmemMode::MMap:
    657     {
    658       // Currently since we don't map the mirrors, don't use fastmem for them.
    659       // This is because the swapping of page code bits for SMC is too expensive.
    660       return (paddr < g_ram_size);
    661     }
    662 #endif
    663 
    664     case CPUFastmemMode::LUT:
    665       return (paddr < RAM_MIRROR_END);
    666 
    667     case CPUFastmemMode::Disabled:
    668     default:
    669       return false;
    670   }
    671 }
    672 
    673 bool Bus::IsRAMCodePage(u32 index)
    674 {
    675   return g_ram_code_bits[index];
    676 }
    677 
    678 void Bus::SetRAMCodePage(u32 index)
    679 {
    680   if (g_ram_code_bits[index])
    681     return;
    682 
    683   // protect fastmem pages
    684   g_ram_code_bits[index] = true;
    685   SetRAMPageWritable(index, false);
    686 }
    687 
    688 void Bus::ClearRAMCodePage(u32 index)
    689 {
    690   if (!g_ram_code_bits[index])
    691     return;
    692 
    693   // unprotect fastmem pages
    694   g_ram_code_bits[index] = false;
    695   SetRAMPageWritable(index, true);
    696 }
    697 
    698 void Bus::SetRAMPageWritable(u32 page_index, bool writable)
    699 {
    700   if (!MemMap::MemProtect(&g_ram[page_index * HOST_PAGE_SIZE], HOST_PAGE_SIZE,
    701                           writable ? PageProtect::ReadWrite : PageProtect::ReadOnly)) [[unlikely]]
    702   {
    703     ERROR_LOG("Failed to set RAM host page {} ({}) to {}", page_index,
    704               reinterpret_cast<const void*>(&g_ram[page_index * HOST_PAGE_SIZE]),
    705               writable ? "read-write" : "read-only");
    706   }
    707 
    708 #ifdef ENABLE_MMAP_FASTMEM
    709   if (s_fastmem_mode == CPUFastmemMode::MMap)
    710   {
    711     const PageProtect protect = writable ? PageProtect::ReadWrite : PageProtect::ReadOnly;
    712 
    713     // unprotect fastmem pages
    714     for (const auto& it : s_fastmem_ram_views)
    715     {
    716       u8* page_address = it.first + (page_index * HOST_PAGE_SIZE);
    717       if (!MemMap::MemProtect(page_address, HOST_PAGE_SIZE, protect)) [[unlikely]]
    718       {
    719         ERROR_LOG("Failed to {} code page {} (0x{:08X}) @ {}", writable ? "unprotect" : "protect", page_index,
    720                   page_index * static_cast<u32>(HOST_PAGE_SIZE), static_cast<void*>(page_address));
    721       }
    722     }
    723 
    724     return;
    725   }
    726 #endif
    727 }
    728 
    729 void Bus::ClearRAMCodePageFlags()
    730 {
    731   g_ram_code_bits.reset();
    732 
    733   if (!MemMap::MemProtect(g_ram, RAM_8MB_SIZE, PageProtect::ReadWrite))
    734     ERROR_LOG("Failed to restore RAM protection to read-write.");
    735 
    736 #ifdef ENABLE_MMAP_FASTMEM
    737   if (s_fastmem_mode == CPUFastmemMode::MMap)
    738   {
    739     // unprotect fastmem pages
    740     for (const auto& it : s_fastmem_ram_views)
    741     {
    742       if (!MemMap::MemProtect(it.first, it.second, PageProtect::ReadWrite))
    743         ERROR_LOG("Failed to unprotect code pages for fastmem view @ {}", static_cast<void*>(it.first));
    744     }
    745   }
    746 #endif
    747 }
    748 
    749 bool Bus::IsCodePageAddress(PhysicalMemoryAddress address)
    750 {
    751   return IsRAMAddress(address) ? g_ram_code_bits[(address & g_ram_mask) / HOST_PAGE_SIZE] : false;
    752 }
    753 
    754 bool Bus::HasCodePagesInRange(PhysicalMemoryAddress start_address, u32 size)
    755 {
    756   if (!IsRAMAddress(start_address))
    757     return false;
    758 
    759   start_address = (start_address & g_ram_mask);
    760 
    761   const u32 end_address = start_address + size;
    762   while (start_address < end_address)
    763   {
    764     const u32 code_page_index = start_address / HOST_PAGE_SIZE;
    765     if (g_ram_code_bits[code_page_index])
    766       return true;
    767 
    768     start_address += HOST_PAGE_SIZE;
    769   }
    770 
    771   return false;
    772 }
    773 
    774 const TickCount* Bus::GetMemoryAccessTimePtr(PhysicalMemoryAddress address, MemoryAccessSize size)
    775 {
    776   // Currently only BIOS, but could be EXP1 as well.
    777   if (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_MIRROR_SIZE))
    778     return &g_bios_access_time[static_cast<size_t>(size)];
    779 
    780   return nullptr;
    781 }
    782 
    783 std::optional<Bus::MemoryRegion> Bus::GetMemoryRegionForAddress(PhysicalMemoryAddress address)
    784 {
    785   if (address < RAM_2MB_SIZE)
    786     return MemoryRegion::RAM;
    787   else if (address < RAM_MIRROR_END)
    788     return static_cast<MemoryRegion>(static_cast<u32>(MemoryRegion::RAM) + (address / RAM_2MB_SIZE));
    789   else if (address >= EXP1_BASE && address < (EXP1_BASE + EXP1_SIZE))
    790     return MemoryRegion::EXP1;
    791   else if (address >= CPU::SCRATCHPAD_ADDR && address < (CPU::SCRATCHPAD_ADDR + CPU::SCRATCHPAD_SIZE))
    792     return MemoryRegion::Scratchpad;
    793   else if (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_SIZE))
    794     return MemoryRegion::BIOS;
    795 
    796   return std::nullopt;
    797 }
    798 
    799 static constexpr std::array<std::pair<PhysicalMemoryAddress, PhysicalMemoryAddress>,
    800                             static_cast<u32>(Bus::MemoryRegion::Count)>
    801   s_code_region_ranges = {{
    802     {0, Bus::RAM_2MB_SIZE},
    803     {Bus::RAM_2MB_SIZE, Bus::RAM_2MB_SIZE * 2},
    804     {Bus::RAM_2MB_SIZE * 2, Bus::RAM_2MB_SIZE * 3},
    805     {Bus::RAM_2MB_SIZE * 3, Bus::RAM_MIRROR_END},
    806     {Bus::EXP1_BASE, Bus::EXP1_BASE + Bus::EXP1_SIZE},
    807     {CPU::SCRATCHPAD_ADDR, CPU::SCRATCHPAD_ADDR + CPU::SCRATCHPAD_SIZE},
    808     {Bus::BIOS_BASE, Bus::BIOS_BASE + Bus::BIOS_SIZE},
    809   }};
    810 
    811 PhysicalMemoryAddress Bus::GetMemoryRegionStart(MemoryRegion region)
    812 {
    813   return s_code_region_ranges[static_cast<u32>(region)].first;
    814 }
    815 
    816 PhysicalMemoryAddress Bus::GetMemoryRegionEnd(MemoryRegion region)
    817 {
    818   return s_code_region_ranges[static_cast<u32>(region)].second;
    819 }
    820 
    821 u8* Bus::GetMemoryRegionPointer(MemoryRegion region)
    822 {
    823   switch (region)
    824   {
    825     case MemoryRegion::RAM:
    826       return g_unprotected_ram;
    827 
    828     case MemoryRegion::RAMMirror1:
    829       return (g_unprotected_ram + (RAM_2MB_SIZE & g_ram_mask));
    830 
    831     case MemoryRegion::RAMMirror2:
    832       return (g_unprotected_ram + ((RAM_2MB_SIZE * 2) & g_ram_mask));
    833 
    834     case MemoryRegion::RAMMirror3:
    835       return (g_unprotected_ram + ((RAM_8MB_SIZE * 3) & g_ram_mask));
    836 
    837     case MemoryRegion::EXP1:
    838       return nullptr;
    839 
    840     case MemoryRegion::Scratchpad:
    841       return CPU::g_state.scratchpad.data();
    842 
    843     case MemoryRegion::BIOS:
    844       return g_bios;
    845 
    846     default:
    847       return nullptr;
    848   }
    849 }
    850 
    851 static ALWAYS_INLINE_RELEASE bool MaskedMemoryCompare(const u8* pattern, const u8* mask, u32 pattern_length,
    852                                                       const u8* mem)
    853 {
    854   if (!mask)
    855     return std::memcmp(mem, pattern, pattern_length) == 0;
    856 
    857   for (u32 i = 0; i < pattern_length; i++)
    858   {
    859     if ((mem[i] & mask[i]) != (pattern[i] & mask[i]))
    860       return false;
    861   }
    862 
    863   return true;
    864 }
    865 
    866 std::optional<PhysicalMemoryAddress> Bus::SearchMemory(PhysicalMemoryAddress start_address, const u8* pattern,
    867                                                        const u8* mask, u32 pattern_length)
    868 {
    869   std::optional<MemoryRegion> region = GetMemoryRegionForAddress(start_address);
    870   if (!region.has_value())
    871     return std::nullopt;
    872 
    873   PhysicalMemoryAddress current_address = start_address;
    874   MemoryRegion current_region = region.value();
    875   while (current_region != MemoryRegion::Count)
    876   {
    877     const u8* mem = GetMemoryRegionPointer(current_region);
    878     const PhysicalMemoryAddress region_start = GetMemoryRegionStart(current_region);
    879     const PhysicalMemoryAddress region_end = GetMemoryRegionEnd(current_region);
    880 
    881     if (mem)
    882     {
    883       PhysicalMemoryAddress region_offset = current_address - region_start;
    884       PhysicalMemoryAddress bytes_remaining = region_end - current_address;
    885       while (bytes_remaining >= pattern_length)
    886       {
    887         if (MaskedMemoryCompare(pattern, mask, pattern_length, mem + region_offset))
    888           return region_start + region_offset;
    889 
    890         region_offset++;
    891         bytes_remaining--;
    892       }
    893     }
    894 
    895     // skip RAM mirrors
    896     if (current_region == MemoryRegion::RAM)
    897       current_region = MemoryRegion::EXP1;
    898     else
    899       current_region = static_cast<MemoryRegion>(static_cast<int>(current_region) + 1);
    900 
    901     if (current_region != MemoryRegion::Count)
    902       current_address = GetMemoryRegionStart(current_region);
    903   }
    904 
    905   return std::nullopt;
    906 }
    907 
    908 void Bus::SetExpansionROM(std::vector<u8> data)
    909 {
    910   s_exp1_rom = std::move(data);
    911 }
    912 
    913 void Bus::AddTTYCharacter(char ch)
    914 {
    915   if (ch == '\r')
    916   {
    917   }
    918   else if (ch == '\n')
    919   {
    920     if (!s_tty_line_buffer.empty())
    921     {
    922       Log::FastWrite("TTY", "", LOGLEVEL_INFO, "\033[1;34m{}\033[0m", s_tty_line_buffer);
    923 #ifdef _DEBUG
    924       if (CPU::IsTraceEnabled())
    925         CPU::WriteToExecutionLog("TTY: %s\n", s_tty_line_buffer.c_str());
    926 #endif
    927     }
    928     s_tty_line_buffer.clear();
    929   }
    930   else
    931   {
    932     s_tty_line_buffer += ch;
    933   }
    934 }
    935 
    936 void Bus::AddTTYString(std::string_view str)
    937 {
    938   for (char ch : str)
    939     AddTTYCharacter(ch);
    940 }
    941 
    942 bool Bus::InjectExecutable(std::span<const u8> buffer, bool set_pc, Error* error)
    943 {
    944   BIOS::PSEXEHeader header;
    945   if (buffer.size() < sizeof(header))
    946   {
    947     Error::SetStringView(error, "Executable does not contain a header.");
    948     return false;
    949   }
    950 
    951   std::memcpy(&header, buffer.data(), sizeof(header));
    952   if (!BIOS::IsValidPSExeHeader(header, buffer.size()))
    953   {
    954     Error::SetStringView(error, "Executable does not contain a valid header.");
    955     return false;
    956   }
    957 
    958   if (header.memfill_size > 0)
    959   {
    960     const u32 words_to_write = header.memfill_size / 4;
    961     u32 address = header.memfill_start & ~UINT32_C(3);
    962     for (u32 i = 0; i < words_to_write; i++)
    963     {
    964       CPU::SafeWriteMemoryWord(address, 0);
    965       address += sizeof(u32);
    966     }
    967   }
    968 
    969   const u32 data_load_size =
    970     std::min(static_cast<u32>(static_cast<u32>(buffer.size() - sizeof(BIOS::PSEXEHeader))), header.file_size);
    971   if (data_load_size > 0)
    972   {
    973     if (!CPU::SafeWriteMemoryBytes(header.load_address, &buffer[sizeof(header)], data_load_size))
    974     {
    975       Error::SetStringFmt(error, "Failed to upload {} bytes to memory at address 0x{:08X}.", data_load_size,
    976                           header.load_address);
    977     }
    978   }
    979 
    980   // patch the BIOS to jump to the executable directly
    981   if (set_pc)
    982   {
    983     const u32 r_pc = header.initial_pc;
    984     const u32 r_gp = header.initial_gp;
    985     const u32 r_sp = header.initial_sp_base + header.initial_sp_offset;
    986     CPU::g_state.regs.gp = r_gp;
    987     if (r_sp != 0)
    988     {
    989       CPU::g_state.regs.sp = r_sp;
    990       CPU::g_state.regs.fp = r_sp;
    991     }
    992     CPU::SetPC(r_pc);
    993   }
    994 
    995   return true;
    996 }
    997 
    998 void Bus::KernelInitializedHook()
    999 {
   1000   if (s_kernel_initialize_hook_run)
   1001     return;
   1002 
   1003   INFO_LOG("Kernel initialized.");
   1004   s_kernel_initialize_hook_run = true;
   1005 
   1006   const System::BootMode boot_mode = System::GetBootMode();
   1007   if (boot_mode == System::BootMode::BootEXE || boot_mode == System::BootMode::BootPSF)
   1008   {
   1009     Error error;
   1010     if (((boot_mode == System::BootMode::BootEXE) ? SideloadEXE(System::GetExeOverride(), &error) :
   1011                                                     PSFLoader::Load(System::GetExeOverride(), &error)))
   1012     {
   1013       // Clear all state, since we're blatently overwriting memory.
   1014       CPU::CodeCache::Reset();
   1015       CPU::ClearICache();
   1016 
   1017       // Stop executing the current block and shell init, and jump straight to the new code.
   1018       DebugAssert(!TimingEvents::IsRunningEvents());
   1019       CPU::ExitExecution();
   1020     }
   1021     else
   1022     {
   1023       // Shut down system on load failure.
   1024       Host::ReportErrorAsync("EXE/PSF Load Failed", error.GetDescription());
   1025       System::ShutdownSystem(false);
   1026     }
   1027   }
   1028 }
   1029 
   1030 bool Bus::SideloadEXE(const std::string& path, Error* error)
   1031 {
   1032   // look for a libps.exe next to the exe, if it exists, load it
   1033   bool okay = true;
   1034   if (const std::string libps_path = Path::BuildRelativePath(path, "libps.exe");
   1035       FileSystem::FileExists(libps_path.c_str()))
   1036   {
   1037     const std::optional<DynamicHeapArray<u8>> exe_data = FileSystem::ReadBinaryFile(libps_path.c_str(), error);
   1038     okay = (exe_data.has_value() && InjectExecutable(exe_data->cspan(), false, error));
   1039     if (!okay)
   1040       Error::AddPrefix(error, "Failed to load libps.exe: ");
   1041   }
   1042   if (okay)
   1043   {
   1044     const std::optional<DynamicHeapArray<u8>> exe_data = FileSystem::ReadBinaryFile(System::GetExeOverride().c_str(), error);
   1045     okay = (exe_data.has_value() && InjectExecutable(exe_data->cspan(), true, error));
   1046     if (!okay)
   1047       Error::AddPrefixFmt(error, "Failed to load {}: ", Path::GetFileName(path));
   1048   }
   1049 
   1050   return okay;
   1051 }
   1052 
   1053 #define BUS_CYCLES(n) CPU::g_state.pending_ticks += n
   1054 
   1055 // TODO: Move handlers to own files for better inlining.
   1056 namespace Bus {
   1057 static void ClearHandlers(void** handlers);
   1058 static void SetHandlerForRegion(void** handlers, VirtualMemoryAddress address, u32 size,
   1059                                 MemoryReadHandler read_byte_handler, MemoryReadHandler read_halfword_handler,
   1060                                 MemoryReadHandler read_word_handler, MemoryWriteHandler write_byte_handler,
   1061                                 MemoryWriteHandler write_halfword_handler, MemoryWriteHandler write_word_handler);
   1062 
   1063 // clang-format off
   1064 template<MemoryAccessSize size> static u32 UnknownReadHandler(VirtualMemoryAddress address);
   1065 template<MemoryAccessSize size> static void UnknownWriteHandler(VirtualMemoryAddress address, u32 value);
   1066 template<MemoryAccessSize size> static void IgnoreWriteHandler(VirtualMemoryAddress address, u32 value);
   1067 template<MemoryAccessSize size> static u32 UnmappedReadHandler(VirtualMemoryAddress address);
   1068 template<MemoryAccessSize size> static void UnmappedWriteHandler(VirtualMemoryAddress address, u32 value);
   1069 
   1070 template<MemoryAccessSize size> static u32 RAMReadHandler(VirtualMemoryAddress address);
   1071 template<MemoryAccessSize size> static void RAMWriteHandler(VirtualMemoryAddress address, u32 value);
   1072 
   1073 template<MemoryAccessSize size> static u32 BIOSReadHandler(VirtualMemoryAddress address);
   1074 
   1075 template<MemoryAccessSize size> static u32 ScratchpadReadHandler(VirtualMemoryAddress address);
   1076 template<MemoryAccessSize size> static void ScratchpadWriteHandler(VirtualMemoryAddress address, u32 value);
   1077 
   1078 template<MemoryAccessSize size> static u32 CacheControlReadHandler(VirtualMemoryAddress address);
   1079 template<MemoryAccessSize size> static void CacheControlWriteHandler(VirtualMemoryAddress address, u32 value);
   1080 
   1081 template<MemoryAccessSize size> static u32 ICacheReadHandler(VirtualMemoryAddress address);
   1082 template<MemoryAccessSize size> static void ICacheWriteHandler(VirtualMemoryAddress address, u32 value);
   1083 
   1084 template<MemoryAccessSize size> static u32 EXP1ReadHandler(VirtualMemoryAddress address);
   1085 template<MemoryAccessSize size> static void EXP1WriteHandler(VirtualMemoryAddress address, u32 value);
   1086 template<MemoryAccessSize size> static u32 EXP2ReadHandler(VirtualMemoryAddress address);
   1087 template<MemoryAccessSize size> static void EXP2WriteHandler(VirtualMemoryAddress address, u32 value);
   1088 template<MemoryAccessSize size> static u32 EXP3ReadHandler(VirtualMemoryAddress address);
   1089 template<MemoryAccessSize size> static void EXP3WriteHandler(VirtualMemoryAddress address, u32 value);
   1090 template<MemoryAccessSize size> static u32 SIO2ReadHandler(PhysicalMemoryAddress address);
   1091 template<MemoryAccessSize size> static void SIO2WriteHandler(PhysicalMemoryAddress address, u32 value);
   1092 
   1093 template<MemoryAccessSize size> static u32 HardwareReadHandler(VirtualMemoryAddress address);
   1094 template<MemoryAccessSize size> static void HardwareWriteHandler(VirtualMemoryAddress address, u32 value);
   1095 
   1096 // clang-format on
   1097 } // namespace Bus
   1098 
   1099 template<MemoryAccessSize size>
   1100 u32 Bus::UnknownReadHandler(VirtualMemoryAddress address)
   1101 {
   1102   static constexpr const char* sizes[3] = {"byte", "halfword", "word"};
   1103   ERROR_LOG("Invalid {} read at address 0x{:08X}, pc 0x{:08X}", sizes[static_cast<u32>(size)], address,
   1104             CPU::g_state.pc);
   1105   return 0xFFFFFFFFu;
   1106 }
   1107 
   1108 template<MemoryAccessSize size>
   1109 void Bus::UnknownWriteHandler(VirtualMemoryAddress address, u32 value)
   1110 {
   1111   static constexpr const char* sizes[3] = {"byte", "halfword", "word"};
   1112   ERROR_LOG("Invalid {} write at address 0x{:08X}, value 0x{:08X}, pc 0x{:08X}", sizes[static_cast<u32>(size)], address,
   1113             value, CPU::g_state.pc);
   1114   CPU::g_state.bus_error = true;
   1115 }
   1116 
   1117 template<MemoryAccessSize size>
   1118 void Bus::IgnoreWriteHandler(VirtualMemoryAddress address, u32 value)
   1119 {
   1120   // noop
   1121 }
   1122 
   1123 template<MemoryAccessSize size>
   1124 u32 Bus::UnmappedReadHandler(VirtualMemoryAddress address)
   1125 {
   1126   CPU::g_state.bus_error = true;
   1127   return UnknownReadHandler<size>(address);
   1128 }
   1129 
   1130 template<MemoryAccessSize size>
   1131 void Bus::UnmappedWriteHandler(VirtualMemoryAddress address, u32 value)
   1132 {
   1133   CPU::g_state.bus_error = true;
   1134   UnknownWriteHandler<size>(address, value);
   1135 }
   1136 
   1137 template<MemoryAccessSize size>
   1138 u32 Bus::RAMReadHandler(VirtualMemoryAddress address)
   1139 {
   1140   BUS_CYCLES(RAM_READ_TICKS);
   1141 
   1142   const u32 offset = address & g_ram_mask;
   1143   if constexpr (size == MemoryAccessSize::Byte)
   1144   {
   1145     return ZeroExtend32(g_ram[offset]);
   1146   }
   1147   else if constexpr (size == MemoryAccessSize::HalfWord)
   1148   {
   1149     u16 temp;
   1150     std::memcpy(&temp, &g_ram[offset], sizeof(u16));
   1151     return ZeroExtend32(temp);
   1152   }
   1153   else if constexpr (size == MemoryAccessSize::Word)
   1154   {
   1155     u32 value;
   1156     std::memcpy(&value, &g_ram[offset], sizeof(u32));
   1157     return value;
   1158   }
   1159 }
   1160 
   1161 template<MemoryAccessSize size>
   1162 void Bus::RAMWriteHandler(VirtualMemoryAddress address, u32 value)
   1163 {
   1164   const u32 offset = address & g_ram_mask;
   1165 
   1166   if constexpr (size == MemoryAccessSize::Byte)
   1167   {
   1168     g_ram[offset] = Truncate8(value);
   1169   }
   1170   else if constexpr (size == MemoryAccessSize::HalfWord)
   1171   {
   1172     const u16 temp = Truncate16(value);
   1173     std::memcpy(&g_ram[offset], &temp, sizeof(u16));
   1174   }
   1175   else if constexpr (size == MemoryAccessSize::Word)
   1176   {
   1177     std::memcpy(&g_ram[offset], &value, sizeof(u32));
   1178   }
   1179 }
   1180 
   1181 template<MemoryAccessSize size>
   1182 u32 Bus::BIOSReadHandler(VirtualMemoryAddress address)
   1183 {
   1184   BUS_CYCLES(g_bios_access_time[static_cast<u32>(size)]);
   1185 
   1186   // TODO: Configurable mirroring.
   1187   const u32 offset = address & UINT32_C(0x7FFFF);
   1188   if constexpr (size == MemoryAccessSize::Byte)
   1189   {
   1190     return ZeroExtend32(g_bios[offset]);
   1191   }
   1192   else if constexpr (size == MemoryAccessSize::HalfWord)
   1193   {
   1194     u16 temp;
   1195     std::memcpy(&temp, &g_bios[offset], sizeof(u16));
   1196     return ZeroExtend32(temp);
   1197   }
   1198   else
   1199   {
   1200     u32 value;
   1201     std::memcpy(&value, &g_bios[offset], sizeof(u32));
   1202     return value;
   1203   }
   1204 }
   1205 
   1206 template<MemoryAccessSize size>
   1207 u32 Bus::ScratchpadReadHandler(VirtualMemoryAddress address)
   1208 {
   1209   const PhysicalMemoryAddress cache_offset = address & MEMORY_LUT_PAGE_MASK;
   1210   if (cache_offset >= CPU::SCRATCHPAD_SIZE) [[unlikely]]
   1211     return UnknownReadHandler<size>(address);
   1212 
   1213   if constexpr (size == MemoryAccessSize::Byte)
   1214   {
   1215     return ZeroExtend32(CPU::g_state.scratchpad[cache_offset]);
   1216   }
   1217   else if constexpr (size == MemoryAccessSize::HalfWord)
   1218   {
   1219     u16 temp;
   1220     std::memcpy(&temp, &CPU::g_state.scratchpad[cache_offset], sizeof(temp));
   1221     return ZeroExtend32(temp);
   1222   }
   1223   else
   1224   {
   1225     u32 value;
   1226     std::memcpy(&value, &CPU::g_state.scratchpad[cache_offset], sizeof(value));
   1227     return value;
   1228   }
   1229 }
   1230 
   1231 template<MemoryAccessSize size>
   1232 void Bus::ScratchpadWriteHandler(VirtualMemoryAddress address, u32 value)
   1233 {
   1234   const PhysicalMemoryAddress cache_offset = address & MEMORY_LUT_PAGE_MASK;
   1235   if (cache_offset >= CPU::SCRATCHPAD_SIZE) [[unlikely]]
   1236   {
   1237     UnknownWriteHandler<size>(address, value);
   1238     return;
   1239   }
   1240 
   1241   if constexpr (size == MemoryAccessSize::Byte)
   1242     CPU::g_state.scratchpad[cache_offset] = Truncate8(value);
   1243   else if constexpr (size == MemoryAccessSize::HalfWord)
   1244     std::memcpy(&CPU::g_state.scratchpad[cache_offset], &value, sizeof(u16));
   1245   else if constexpr (size == MemoryAccessSize::Word)
   1246     std::memcpy(&CPU::g_state.scratchpad[cache_offset], &value, sizeof(u32));
   1247 }
   1248 
   1249 template<MemoryAccessSize size>
   1250 u32 Bus::CacheControlReadHandler(VirtualMemoryAddress address)
   1251 {
   1252   if (address != 0xFFFE0130)
   1253     return UnknownReadHandler<size>(address);
   1254 
   1255   return CPU::g_state.cache_control.bits;
   1256 }
   1257 
   1258 template<MemoryAccessSize size>
   1259 void Bus::CacheControlWriteHandler(VirtualMemoryAddress address, u32 value)
   1260 {
   1261   if (address != 0xFFFE0130)
   1262     return UnknownWriteHandler<size>(address, value);
   1263 
   1264   DEV_LOG("Cache control <- 0x{:08X}", value);
   1265   CPU::g_state.cache_control.bits = value;
   1266 }
   1267 
   1268 template<MemoryAccessSize size>
   1269 u32 Bus::ICacheReadHandler(VirtualMemoryAddress address)
   1270 {
   1271   const u32 line = CPU::GetICacheLine(address);
   1272   const u8* line_data = &CPU::g_state.icache_data[line * CPU::ICACHE_LINE_SIZE];
   1273   const u32 offset = CPU::GetICacheLineOffset(address);
   1274   u32 result;
   1275   std::memcpy(&result, &line_data[offset], sizeof(result));
   1276   return result;
   1277 }
   1278 
   1279 template<MemoryAccessSize size>
   1280 void Bus::ICacheWriteHandler(VirtualMemoryAddress address, u32 value)
   1281 {
   1282   const u32 line = CPU::GetICacheLine(address);
   1283   const u32 offset = CPU::GetICacheLineOffset(address);
   1284   CPU::g_state.icache_tags[line] = CPU::GetICacheTagForAddress(address) | CPU::ICACHE_INVALID_BITS;
   1285   if constexpr (size == MemoryAccessSize::Byte)
   1286     std::memcpy(&CPU::g_state.icache_data[line * CPU::ICACHE_LINE_SIZE + offset], &value, sizeof(u8));
   1287   else if constexpr (size == MemoryAccessSize::HalfWord)
   1288     std::memcpy(&CPU::g_state.icache_data[line * CPU::ICACHE_LINE_SIZE + offset], &value, sizeof(u16));
   1289   else
   1290     std::memcpy(&CPU::g_state.icache_data[line * CPU::ICACHE_LINE_SIZE + offset], &value, sizeof(u32));
   1291 }
   1292 
   1293 template<MemoryAccessSize size>
   1294 u32 Bus::EXP1ReadHandler(VirtualMemoryAddress address)
   1295 {
   1296   BUS_CYCLES(g_exp1_access_time[static_cast<u32>(size)]);
   1297 
   1298   const u32 offset = address & EXP1_MASK;
   1299   u32 value;
   1300   if (s_exp1_rom.empty())
   1301   {
   1302     // EXP1 not present.
   1303     value = UINT32_C(0xFFFFFFFF);
   1304   }
   1305   else if (offset == 0x20018)
   1306   {
   1307     // Bit 0 - Action Replay On/Off
   1308     value = UINT32_C(1);
   1309   }
   1310   else
   1311   {
   1312     const u32 transfer_size = u32(1) << static_cast<u32>(size);
   1313     if ((offset + transfer_size) > s_exp1_rom.size())
   1314     {
   1315       value = UINT32_C(0);
   1316     }
   1317     else
   1318     {
   1319       if constexpr (size == MemoryAccessSize::Byte)
   1320       {
   1321         value = ZeroExtend32(s_exp1_rom[offset]);
   1322       }
   1323       else if constexpr (size == MemoryAccessSize::HalfWord)
   1324       {
   1325         u16 halfword;
   1326         std::memcpy(&halfword, &s_exp1_rom[offset], sizeof(halfword));
   1327         value = ZeroExtend32(halfword);
   1328       }
   1329       else
   1330       {
   1331         std::memcpy(&value, &s_exp1_rom[offset], sizeof(value));
   1332       }
   1333 
   1334       // Log_DevPrintf("EXP1 read: 0x%08X -> 0x%08X", address, value);
   1335     }
   1336   }
   1337 
   1338   return value;
   1339 }
   1340 
   1341 template<MemoryAccessSize size>
   1342 void Bus::EXP1WriteHandler(VirtualMemoryAddress address, u32 value)
   1343 {
   1344   WARNING_LOG("EXP1 write: 0x{:08X} <- 0x{:08X}", address, value);
   1345 }
   1346 
   1347 template<MemoryAccessSize size>
   1348 u32 Bus::EXP2ReadHandler(VirtualMemoryAddress address)
   1349 {
   1350   BUS_CYCLES(g_exp2_access_time[static_cast<u32>(size)]);
   1351 
   1352   const u32 offset = address & EXP2_MASK;
   1353   u32 value;
   1354 
   1355   // rx/tx buffer empty
   1356   if (offset == 0x21)
   1357   {
   1358     value = 0x04 | 0x08;
   1359   }
   1360   else if (offset >= 0x60 && offset <= 0x67)
   1361   {
   1362     // nocash expansion area
   1363     value = UINT32_C(0xFFFFFFFF);
   1364   }
   1365   else
   1366   {
   1367     WARNING_LOG("EXP2 read: 0x{:08X}", address);
   1368     value = UINT32_C(0xFFFFFFFF);
   1369   }
   1370 
   1371   return value;
   1372 }
   1373 
   1374 template<MemoryAccessSize size>
   1375 void Bus::EXP2WriteHandler(VirtualMemoryAddress address, u32 value)
   1376 {
   1377   const u32 offset = address & EXP2_MASK;
   1378   if (offset == 0x23 || offset == 0x80)
   1379   {
   1380     AddTTYCharacter(static_cast<char>(value));
   1381   }
   1382   else if (offset == 0x41 || offset == 0x42)
   1383   {
   1384     const u32 post_code = value & UINT32_C(0x0F);
   1385     DEV_LOG("BIOS POST status: {:02X}", post_code);
   1386     if (post_code == 0x07)
   1387       KernelInitializedHook();
   1388   }
   1389   else if (offset == 0x70)
   1390   {
   1391     DEV_LOG("BIOS POST2 status: {:02X}", value & UINT32_C(0x0F));
   1392   }
   1393 #if 0
   1394   // TODO: Put behind configuration variable
   1395   else if (offset == 0x81)
   1396   {
   1397     Log_WarningPrint("pcsx_debugbreak()");
   1398     Host::ReportErrorAsync("Error", "pcsx_debugbreak()");
   1399     System::PauseSystem(true);
   1400     CPU::ExitExecution();
   1401   }
   1402   else if (offset == 0x82)
   1403   {
   1404     Log_WarningFmt("pcsx_exit() with status 0x{:02X}", value & UINT32_C(0xFF));
   1405     Host::ReportErrorAsync("Error", fmt::format("pcsx_exit() with status 0x{:02X}", value & UINT32_C(0xFF)));
   1406     System::ShutdownSystem(false);
   1407     CPU::ExitExecution();
   1408   }
   1409 #endif
   1410   else
   1411   {
   1412     WARNING_LOG("EXP2 write: 0x{:08X} <- 0x{:08X}", address, value);
   1413   }
   1414 }
   1415 
   1416 template<MemoryAccessSize size>
   1417 u32 Bus::EXP3ReadHandler(VirtualMemoryAddress address)
   1418 {
   1419   WARNING_LOG("EXP3 read: 0x{:08X}", address);
   1420   return UINT32_C(0xFFFFFFFF);
   1421 }
   1422 
   1423 template<MemoryAccessSize size>
   1424 void Bus::EXP3WriteHandler(VirtualMemoryAddress address, u32 value)
   1425 {
   1426   const u32 offset = address & EXP3_MASK;
   1427   if (offset == 0)
   1428   {
   1429     const u32 post_code = value & UINT32_C(0x0F);
   1430     WARNING_LOG("BIOS POST3 status: {:02X}", post_code);
   1431     if (post_code == 0x07)
   1432       KernelInitializedHook();
   1433   }
   1434 }
   1435 
   1436 template<MemoryAccessSize size>
   1437 u32 Bus::SIO2ReadHandler(PhysicalMemoryAddress address)
   1438 {
   1439   // Stub for using PS2 BIOS.
   1440   if (const BIOS::ImageInfo* ii = System::GetBIOSImageInfo();
   1441       !ii || ii->fastboot_patch != BIOS::ImageInfo::FastBootPatch::Type2) [[unlikely]]
   1442   {
   1443     // Throw exception when not using PS2 BIOS.
   1444     return UnmappedReadHandler<size>(address);
   1445   }
   1446 
   1447   WARNING_LOG("SIO2 read: 0x{:08X}", address);
   1448   return 0;
   1449 }
   1450 
   1451 template<MemoryAccessSize size>
   1452 void Bus::SIO2WriteHandler(PhysicalMemoryAddress address, u32 value)
   1453 {
   1454   // Stub for using PS2 BIOS.
   1455   if (const BIOS::ImageInfo* ii = System::GetBIOSImageInfo();
   1456       !ii || ii->fastboot_patch != BIOS::ImageInfo::FastBootPatch::Type2) [[unlikely]]
   1457   {
   1458     // Throw exception when not using PS2 BIOS.
   1459     UnmappedWriteHandler<size>(address, value);
   1460     return;
   1461   }
   1462 
   1463   WARNING_LOG("SIO2 write: 0x{:08X} <- 0x{:08X}", address, value);
   1464 }
   1465 
   1466 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   1467 // HARDWARE HANDLERS
   1468 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
   1469 
   1470 namespace Bus::HWHandlers {
   1471 // clang-format off
   1472 template<MemoryAccessSize size> static u32 MemCtrlRead(PhysicalMemoryAddress address);
   1473 template<MemoryAccessSize size> static void MemCtrlWrite(PhysicalMemoryAddress address, u32 value);
   1474 template<MemoryAccessSize size> static u32 PADRead(PhysicalMemoryAddress address);
   1475 template<MemoryAccessSize size> static void PADWrite(PhysicalMemoryAddress address, u32 value);
   1476 template<MemoryAccessSize size> static u32 SIORead(PhysicalMemoryAddress address);
   1477 template<MemoryAccessSize size> static void SIOWrite(PhysicalMemoryAddress address, u32 value);
   1478 template<MemoryAccessSize size> static u32 MemCtrl2Read(PhysicalMemoryAddress address);
   1479 template<MemoryAccessSize size> static void MemCtrl2Write(PhysicalMemoryAddress address, u32 value);
   1480 template<MemoryAccessSize size> static u32 INTCRead(PhysicalMemoryAddress address);
   1481 template<MemoryAccessSize size> static void INTCWrite(PhysicalMemoryAddress address, u32 value);
   1482 template<MemoryAccessSize size> static u32 DMARead(PhysicalMemoryAddress address);
   1483 template<MemoryAccessSize size> static void DMAWrite(PhysicalMemoryAddress address, u32 value);
   1484 template<MemoryAccessSize size> static u32 TimersRead(PhysicalMemoryAddress address);
   1485 template<MemoryAccessSize size> static void TimersWrite(PhysicalMemoryAddress address, u32 value);
   1486 template<MemoryAccessSize size> static u32 CDROMRead(PhysicalMemoryAddress address);
   1487 template<MemoryAccessSize size> static void CDROMWrite(PhysicalMemoryAddress address, u32 value);
   1488 template<MemoryAccessSize size> static u32 GPURead(PhysicalMemoryAddress address);
   1489 template<MemoryAccessSize size> static void GPUWrite(PhysicalMemoryAddress address, u32 value);
   1490 template<MemoryAccessSize size> static u32 MDECRead(PhysicalMemoryAddress address);
   1491 template<MemoryAccessSize size> static void MDECWrite(PhysicalMemoryAddress address, u32 value);
   1492 template<MemoryAccessSize size> static u32 SPURead(PhysicalMemoryAddress address);
   1493 template<MemoryAccessSize size> static void SPUWrite(PhysicalMemoryAddress address, u32 value);
   1494 // clang-format on
   1495 } // namespace Bus::HWHandlers
   1496 
   1497 template<MemoryAccessSize size>
   1498 u32 Bus::HWHandlers::MemCtrlRead(PhysicalMemoryAddress address)
   1499 {
   1500   const u32 offset = address & MEMCTRL_MASK;
   1501   const u32 index = FIXUP_WORD_OFFSET(size, offset) / 4;
   1502   if (index >= std::size(s_MEMCTRL.regs)) [[unlikely]]
   1503     return 0;
   1504 
   1505   u32 value = s_MEMCTRL.regs[index];
   1506   value = FIXUP_WORD_READ_VALUE(size, offset, value);
   1507   BUS_CYCLES(2);
   1508   return value;
   1509 }
   1510 
   1511 template<MemoryAccessSize size>
   1512 void Bus::HWHandlers::MemCtrlWrite(PhysicalMemoryAddress address, u32 value)
   1513 {
   1514   const u32 offset = address & MEMCTRL_MASK;
   1515   const u32 index = FIXUP_WORD_OFFSET(size, offset) / 4;
   1516   if (index >= std::size(s_MEMCTRL.regs)) [[unlikely]]
   1517     return;
   1518 
   1519   value = FIXUP_WORD_WRITE_VALUE(size, offset, value);
   1520 
   1521   const u32 write_mask = (index == 8) ? COMDELAY::WRITE_MASK : MEMDELAY::WRITE_MASK;
   1522   const u32 new_value = (s_MEMCTRL.regs[index] & ~write_mask) | (value & write_mask);
   1523   if (s_MEMCTRL.regs[index] != new_value)
   1524   {
   1525     s_MEMCTRL.regs[index] = new_value;
   1526     RecalculateMemoryTimings();
   1527   }
   1528 }
   1529 
   1530 template<MemoryAccessSize size>
   1531 u32 Bus::HWHandlers::MemCtrl2Read(PhysicalMemoryAddress address)
   1532 {
   1533   const u32 offset = address & MEMCTRL2_MASK;
   1534 
   1535   u32 value;
   1536   if (offset == 0x00)
   1537   {
   1538     value = s_RAM_SIZE.bits;
   1539   }
   1540   else
   1541   {
   1542     return UnknownReadHandler<size>(address);
   1543   }
   1544 
   1545   BUS_CYCLES(2);
   1546   return value;
   1547 }
   1548 
   1549 template<MemoryAccessSize size>
   1550 void Bus::HWHandlers::MemCtrl2Write(PhysicalMemoryAddress address, u32 value)
   1551 {
   1552   const u32 offset = address & MEMCTRL2_MASK;
   1553 
   1554   if (offset == 0x00)
   1555   {
   1556     if (s_RAM_SIZE.bits != value)
   1557     {
   1558       DEV_LOG("RAM size register set to 0x{:08X}", value);
   1559 
   1560       const RAM_SIZE_REG old_ram_size_reg = s_RAM_SIZE;
   1561       s_RAM_SIZE.bits = value;
   1562 
   1563       if (s_RAM_SIZE.memory_window != old_ram_size_reg.memory_window)
   1564         UpdateMappedRAMSize();
   1565     }
   1566   }
   1567   else
   1568   {
   1569     return UnknownWriteHandler<size>(address, value);
   1570   }
   1571 }
   1572 
   1573 template<MemoryAccessSize size>
   1574 u32 Bus::HWHandlers::PADRead(PhysicalMemoryAddress address)
   1575 {
   1576   const u32 offset = address & PAD_MASK;
   1577 
   1578   u32 value = Pad::ReadRegister(FIXUP_HALFWORD_OFFSET(size, offset));
   1579   value = FIXUP_HALFWORD_READ_VALUE(size, offset, value);
   1580   BUS_CYCLES(2);
   1581   return value;
   1582 }
   1583 
   1584 template<MemoryAccessSize size>
   1585 void Bus::HWHandlers::PADWrite(PhysicalMemoryAddress address, u32 value)
   1586 {
   1587   const u32 offset = address & PAD_MASK;
   1588   Pad::WriteRegister(FIXUP_HALFWORD_OFFSET(size, offset), FIXUP_HALFWORD_WRITE_VALUE(size, offset, value));
   1589 }
   1590 
   1591 template<MemoryAccessSize size>
   1592 u32 Bus::HWHandlers::SIORead(PhysicalMemoryAddress address)
   1593 {
   1594   const u32 offset = address & SIO_MASK;
   1595   u32 value = SIO::ReadRegister(FIXUP_HALFWORD_OFFSET(size, offset));
   1596   value = FIXUP_HALFWORD_READ_VALUE(size, offset, value);
   1597   BUS_CYCLES(2);
   1598   return value;
   1599 }
   1600 
   1601 template<MemoryAccessSize size>
   1602 void Bus::HWHandlers::SIOWrite(PhysicalMemoryAddress address, u32 value)
   1603 {
   1604   const u32 offset = address & SIO_MASK;
   1605   SIO::WriteRegister(FIXUP_HALFWORD_OFFSET(size, offset), FIXUP_HALFWORD_WRITE_VALUE(size, offset, value));
   1606 }
   1607 
   1608 template<MemoryAccessSize size>
   1609 u32 Bus::HWHandlers::CDROMRead(PhysicalMemoryAddress address)
   1610 {
   1611   const u32 offset = address & CDROM_MASK;
   1612 
   1613   u32 value;
   1614   switch (size)
   1615   {
   1616     case MemoryAccessSize::Word:
   1617     {
   1618       const u32 b0 = ZeroExtend32(CDROM::ReadRegister(offset));
   1619       const u32 b1 = ZeroExtend32(CDROM::ReadRegister(offset + 1u));
   1620       const u32 b2 = ZeroExtend32(CDROM::ReadRegister(offset + 2u));
   1621       const u32 b3 = ZeroExtend32(CDROM::ReadRegister(offset + 3u));
   1622       value = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
   1623     }
   1624     break;
   1625 
   1626     case MemoryAccessSize::HalfWord:
   1627     {
   1628       const u32 lsb = ZeroExtend32(CDROM::ReadRegister(offset));
   1629       const u32 msb = ZeroExtend32(CDROM::ReadRegister(offset + 1u));
   1630       value = lsb | (msb << 8);
   1631     }
   1632     break;
   1633 
   1634     case MemoryAccessSize::Byte:
   1635     default:
   1636       value = ZeroExtend32(CDROM::ReadRegister(offset));
   1637       break;
   1638   }
   1639 
   1640   BUS_CYCLES(Bus::g_cdrom_access_time[static_cast<u32>(size)]);
   1641   return value;
   1642 }
   1643 
   1644 template<MemoryAccessSize size>
   1645 void Bus::HWHandlers::CDROMWrite(PhysicalMemoryAddress address, u32 value)
   1646 {
   1647   const u32 offset = address & CDROM_MASK;
   1648   switch (size)
   1649   {
   1650     case MemoryAccessSize::Word:
   1651     {
   1652       CDROM::WriteRegister(offset, Truncate8(value & 0xFFu));
   1653       CDROM::WriteRegister(offset + 1u, Truncate8((value >> 8) & 0xFFu));
   1654       CDROM::WriteRegister(offset + 2u, Truncate8((value >> 16) & 0xFFu));
   1655       CDROM::WriteRegister(offset + 3u, Truncate8((value >> 24) & 0xFFu));
   1656     }
   1657     break;
   1658 
   1659     case MemoryAccessSize::HalfWord:
   1660     {
   1661       CDROM::WriteRegister(offset, Truncate8(value & 0xFFu));
   1662       CDROM::WriteRegister(offset + 1u, Truncate8((value >> 8) & 0xFFu));
   1663     }
   1664     break;
   1665 
   1666     case MemoryAccessSize::Byte:
   1667     default:
   1668       CDROM::WriteRegister(offset, Truncate8(value));
   1669       break;
   1670   }
   1671 }
   1672 
   1673 template<MemoryAccessSize size>
   1674 u32 Bus::HWHandlers::GPURead(PhysicalMemoryAddress address)
   1675 {
   1676   const u32 offset = address & GPU_MASK;
   1677   u32 value = g_gpu->ReadRegister(FIXUP_WORD_OFFSET(size, offset));
   1678   value = FIXUP_WORD_READ_VALUE(size, offset, value);
   1679   BUS_CYCLES(2);
   1680   return value;
   1681 }
   1682 
   1683 template<MemoryAccessSize size>
   1684 void Bus::HWHandlers::GPUWrite(PhysicalMemoryAddress address, u32 value)
   1685 {
   1686   const u32 offset = address & GPU_MASK;
   1687   g_gpu->WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
   1688 }
   1689 
   1690 template<MemoryAccessSize size>
   1691 u32 Bus::HWHandlers::MDECRead(PhysicalMemoryAddress address)
   1692 {
   1693   const u32 offset = address & MDEC_MASK;
   1694   u32 value = MDEC::ReadRegister(FIXUP_WORD_OFFSET(size, offset));
   1695   value = FIXUP_WORD_READ_VALUE(size, offset, value);
   1696   BUS_CYCLES(2);
   1697   return value;
   1698 }
   1699 
   1700 template<MemoryAccessSize size>
   1701 void Bus::HWHandlers::MDECWrite(PhysicalMemoryAddress address, u32 value)
   1702 {
   1703   const u32 offset = address & MDEC_MASK;
   1704   MDEC::WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
   1705 }
   1706 
   1707 template<MemoryAccessSize size>
   1708 u32 Bus::HWHandlers::INTCRead(PhysicalMemoryAddress address)
   1709 {
   1710   const u32 offset = address & INTERRUPT_CONTROLLER_MASK;
   1711   u32 value = InterruptController::ReadRegister(FIXUP_WORD_OFFSET(size, offset));
   1712   value = FIXUP_WORD_READ_VALUE(size, offset, value);
   1713   BUS_CYCLES(2);
   1714   return value;
   1715 }
   1716 
   1717 template<MemoryAccessSize size>
   1718 void Bus::HWHandlers::INTCWrite(PhysicalMemoryAddress address, u32 value)
   1719 {
   1720   const u32 offset = address & INTERRUPT_CONTROLLER_MASK;
   1721   InterruptController::WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
   1722 }
   1723 
   1724 template<MemoryAccessSize size>
   1725 u32 Bus::HWHandlers::TimersRead(PhysicalMemoryAddress address)
   1726 {
   1727   const u32 offset = address & TIMERS_MASK;
   1728   u32 value = Timers::ReadRegister(FIXUP_WORD_OFFSET(size, offset));
   1729   value = FIXUP_WORD_READ_VALUE(size, offset, value);
   1730   BUS_CYCLES(2);
   1731   return value;
   1732 }
   1733 
   1734 template<MemoryAccessSize size>
   1735 void Bus::HWHandlers::TimersWrite(PhysicalMemoryAddress address, u32 value)
   1736 {
   1737   const u32 offset = address & TIMERS_MASK;
   1738   Timers::WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
   1739 }
   1740 
   1741 template<MemoryAccessSize size>
   1742 u32 Bus::HWHandlers::SPURead(PhysicalMemoryAddress address)
   1743 {
   1744   const u32 offset = address & SPU_MASK;
   1745   u32 value;
   1746 
   1747   switch (size)
   1748   {
   1749     case MemoryAccessSize::Word:
   1750     {
   1751       // 32-bit reads are read as two 16-bit accesses.
   1752       const u16 lsb = SPU::ReadRegister(offset);
   1753       const u16 msb = SPU::ReadRegister(offset + 2);
   1754       value = ZeroExtend32(lsb) | (ZeroExtend32(msb) << 16);
   1755     }
   1756     break;
   1757 
   1758     case MemoryAccessSize::HalfWord:
   1759     {
   1760       value = ZeroExtend32(SPU::ReadRegister(offset));
   1761     }
   1762     break;
   1763 
   1764     case MemoryAccessSize::Byte:
   1765     default:
   1766     {
   1767       const u16 value16 = SPU::ReadRegister(FIXUP_HALFWORD_OFFSET(size, offset));
   1768       value = FIXUP_HALFWORD_READ_VALUE(size, offset, value16);
   1769     }
   1770     break;
   1771   }
   1772 
   1773   BUS_CYCLES(Bus::g_spu_access_time[static_cast<u32>(size)]);
   1774   return value;
   1775 }
   1776 
   1777 template<MemoryAccessSize size>
   1778 void Bus::HWHandlers::SPUWrite(PhysicalMemoryAddress address, u32 value)
   1779 {
   1780   const u32 offset = address & SPU_MASK;
   1781 
   1782   // 32-bit writes are written as two 16-bit writes.
   1783   switch (size)
   1784   {
   1785     case MemoryAccessSize::Word:
   1786     {
   1787       DebugAssert(Common::IsAlignedPow2(offset, 2));
   1788       SPU::WriteRegister(offset, Truncate16(value));
   1789       SPU::WriteRegister(offset + 2, Truncate16(value >> 16));
   1790       break;
   1791     }
   1792 
   1793     case MemoryAccessSize::HalfWord:
   1794     {
   1795       DebugAssert(Common::IsAlignedPow2(offset, 2));
   1796       SPU::WriteRegister(offset, Truncate16(value));
   1797       break;
   1798     }
   1799 
   1800     case MemoryAccessSize::Byte:
   1801     {
   1802       // Byte writes to unaligned addresses are apparently ignored.
   1803       if (address & 1)
   1804         return;
   1805 
   1806       SPU::WriteRegister(offset, Truncate16(FIXUP_HALFWORD_READ_VALUE(size, offset, value)));
   1807       break;
   1808     }
   1809   }
   1810 }
   1811 
   1812 template<MemoryAccessSize size>
   1813 u32 Bus::HWHandlers::DMARead(PhysicalMemoryAddress address)
   1814 {
   1815   const u32 offset = address & DMA_MASK;
   1816   u32 value = DMA::ReadRegister(FIXUP_WORD_OFFSET(size, offset));
   1817   value = FIXUP_WORD_READ_VALUE(size, offset, value);
   1818   BUS_CYCLES(2);
   1819   return value;
   1820 }
   1821 
   1822 template<MemoryAccessSize size>
   1823 void Bus::HWHandlers::DMAWrite(PhysicalMemoryAddress address, u32 value)
   1824 {
   1825   const u32 offset = address & DMA_MASK;
   1826   DMA::WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
   1827 }
   1828 
   1829 #undef BUS_CYCLES
   1830 
   1831 namespace Bus::HWHandlers {
   1832 // We index hardware registers by bits 15..8.
   1833 template<MemoryAccessType type, MemoryAccessSize size,
   1834          typename RT = std::conditional_t<type == MemoryAccessType::Read, MemoryReadHandler, MemoryWriteHandler>>
   1835 static constexpr std::array<RT, 256> GetHardwareRegisterHandlerTable()
   1836 {
   1837   std::array<RT, 256> ret = {};
   1838   for (size_t i = 0; i < ret.size(); i++)
   1839   {
   1840     if constexpr (type == MemoryAccessType::Read)
   1841       ret[i] = UnmappedReadHandler<size>;
   1842     else
   1843       ret[i] = UnmappedWriteHandler<size>;
   1844   }
   1845 
   1846 #if 0
   1847   // Verifies no region has >1 handler, but doesn't compile on older GCC.
   1848 #define SET(raddr, rsize, read_handler, write_handler)                                                                 \
   1849   static_assert(raddr >= 0x1F801000 && (raddr + rsize) <= 0x1F802000);                                                 \
   1850   for (u32 taddr = raddr; taddr < (raddr + rsize); taddr += 16)                                                        \
   1851   {                                                                                                                    \
   1852     const u32 i = (taddr >> 4) & 0xFFu;                                                                                \
   1853     if constexpr (type == MemoryAccessType::Read)                                                                      \
   1854       ret[i] = (ret[i] == UnmappedReadHandler<size>) ? read_handler<size> : (abort(), read_handler<size>);             \
   1855     else                                                                                                               \
   1856       ret[i] = (ret[i] == UnmappedWriteHandler<size>) ? write_handler<size> : (abort(), write_handler<size>);          \
   1857   }
   1858 #else
   1859 #define SET(raddr, rsize, read_handler, write_handler)                                                                 \
   1860   static_assert(raddr >= 0x1F801000 && (raddr + rsize) <= 0x1F802000);                                                 \
   1861   for (u32 taddr = raddr; taddr < (raddr + rsize); taddr += 16)                                                        \
   1862   {                                                                                                                    \
   1863     const u32 i = (taddr >> 4) & 0xFFu;                                                                                \
   1864     if constexpr (type == MemoryAccessType::Read)                                                                      \
   1865       ret[i] = read_handler<size>;                                                                                     \
   1866     else                                                                                                               \
   1867       ret[i] = write_handler<size>;                                                                                    \
   1868   }
   1869 #endif
   1870 
   1871   SET(MEMCTRL_BASE, MEMCTRL_SIZE, MemCtrlRead, MemCtrlWrite);
   1872   SET(PAD_BASE, PAD_SIZE, PADRead, PADWrite);
   1873   SET(SIO_BASE, SIO_SIZE, SIORead, SIOWrite);
   1874   SET(MEMCTRL2_BASE, MEMCTRL2_SIZE, MemCtrl2Read, MemCtrl2Write);
   1875   SET(INTC_BASE, INTC_SIZE, INTCRead, INTCWrite);
   1876   SET(DMA_BASE, DMA_SIZE, DMARead, DMAWrite);
   1877   SET(TIMERS_BASE, TIMERS_SIZE, TimersRead, TimersWrite);
   1878   SET(CDROM_BASE, CDROM_SIZE, CDROMRead, CDROMWrite);
   1879   SET(GPU_BASE, GPU_SIZE, GPURead, GPUWrite);
   1880   SET(MDEC_BASE, MDEC_SIZE, MDECRead, MDECWrite);
   1881   SET(SPU_BASE, SPU_SIZE, SPURead, SPUWrite);
   1882 
   1883 #undef SET
   1884 
   1885   return ret;
   1886 }
   1887 } // namespace Bus::HWHandlers
   1888 
   1889 template<MemoryAccessSize size>
   1890 u32 Bus::HardwareReadHandler(VirtualMemoryAddress address)
   1891 {
   1892   static constexpr const auto table = HWHandlers::GetHardwareRegisterHandlerTable<MemoryAccessType::Read, size>();
   1893   const u32 table_index = (address >> 4) & 0xFFu;
   1894   return table[table_index](address);
   1895 }
   1896 
   1897 template<MemoryAccessSize size>
   1898 void Bus::HardwareWriteHandler(VirtualMemoryAddress address, u32 value)
   1899 {
   1900   static constexpr const auto table = HWHandlers::GetHardwareRegisterHandlerTable<MemoryAccessType::Write, size>();
   1901   const u32 table_index = (address >> 4) & 0xFFu;
   1902   return table[table_index](address, value);
   1903 }
   1904 
   1905 //////////////////////////////////////////////////////////////////////////
   1906 
   1907 static constexpr u32 KUSEG = 0;
   1908 static constexpr u32 KSEG0 = 0x80000000U;
   1909 static constexpr u32 KSEG1 = 0xA0000000U;
   1910 static constexpr u32 KSEG2 = 0xC0000000U;
   1911 
   1912 void Bus::SetHandlers()
   1913 {
   1914   ClearHandlers(g_memory_handlers);
   1915   ClearHandlers(g_memory_handlers_isc);
   1916 
   1917 #define SET(table, start, size, read_handler, write_handler)                                                           \
   1918   SetHandlerForRegion(table, start, size, read_handler<MemoryAccessSize::Byte>,                                        \
   1919                       read_handler<MemoryAccessSize::HalfWord>, read_handler<MemoryAccessSize::Word>,                  \
   1920                       write_handler<MemoryAccessSize::Byte>, write_handler<MemoryAccessSize::HalfWord>,                \
   1921                       write_handler<MemoryAccessSize::Word>)
   1922 #define SETUC(start, size, read_handler, write_handler)                                                                \
   1923   SET(g_memory_handlers, start, size, read_handler, write_handler);                                                    \
   1924   SET(g_memory_handlers_isc, start, size, read_handler, write_handler)
   1925 
   1926   // KUSEG - Cached
   1927   // Cache isolated appears to affect KUSEG+KSEG0.
   1928   SET(g_memory_handlers, KUSEG | RAM_BASE, RAM_MIRROR_SIZE, RAMReadHandler, RAMWriteHandler);
   1929   SET(g_memory_handlers, KUSEG | CPU::SCRATCHPAD_ADDR, 0x1000, ScratchpadReadHandler, ScratchpadWriteHandler);
   1930   SET(g_memory_handlers, KUSEG | BIOS_BASE, BIOS_MIRROR_SIZE, BIOSReadHandler, IgnoreWriteHandler);
   1931   SET(g_memory_handlers, KUSEG | EXP1_BASE, EXP1_SIZE, EXP1ReadHandler, EXP1WriteHandler);
   1932   SET(g_memory_handlers, KUSEG | HW_BASE, HW_SIZE, HardwareReadHandler, HardwareWriteHandler);
   1933   SET(g_memory_handlers, KUSEG | EXP2_BASE, EXP2_SIZE, EXP2ReadHandler, EXP2WriteHandler);
   1934   SET(g_memory_handlers, KUSEG | EXP3_BASE, EXP3_SIZE, EXP3ReadHandler, EXP3WriteHandler);
   1935   SET(g_memory_handlers, KUSEG | SIO2_BASE, SIO2_SIZE, SIO2ReadHandler, SIO2WriteHandler);
   1936   SET(g_memory_handlers_isc, KUSEG, 0x80000000, ICacheReadHandler, ICacheWriteHandler);
   1937 
   1938   // KSEG0 - Cached
   1939   SET(g_memory_handlers, KSEG0 | RAM_BASE, RAM_MIRROR_SIZE, RAMReadHandler, RAMWriteHandler);
   1940   SET(g_memory_handlers, KSEG0 | CPU::SCRATCHPAD_ADDR, 0x1000, ScratchpadReadHandler, ScratchpadWriteHandler);
   1941   SET(g_memory_handlers, KSEG0 | BIOS_BASE, BIOS_MIRROR_SIZE, BIOSReadHandler, IgnoreWriteHandler);
   1942   SET(g_memory_handlers, KSEG0 | EXP1_BASE, EXP1_SIZE, EXP1ReadHandler, EXP1WriteHandler);
   1943   SET(g_memory_handlers, KSEG0 | HW_BASE, HW_SIZE, HardwareReadHandler, HardwareWriteHandler);
   1944   SET(g_memory_handlers, KSEG0 | EXP2_BASE, EXP2_SIZE, EXP2ReadHandler, EXP2WriteHandler);
   1945   SET(g_memory_handlers, KSEG0 | EXP3_BASE, EXP3_SIZE, EXP3ReadHandler, EXP3WriteHandler);
   1946   SET(g_memory_handlers, KSEG0 | SIO2_BASE, SIO2_SIZE, SIO2ReadHandler, SIO2WriteHandler);
   1947   SET(g_memory_handlers_isc, KSEG0, 0x20000000, ICacheReadHandler, ICacheWriteHandler);
   1948 
   1949   // KSEG1 - Uncached
   1950   SETUC(KSEG1 | RAM_BASE, RAM_MIRROR_SIZE, RAMReadHandler, RAMWriteHandler);
   1951   SETUC(KSEG1 | BIOS_BASE, BIOS_MIRROR_SIZE, BIOSReadHandler, IgnoreWriteHandler);
   1952   SETUC(KSEG1 | EXP1_BASE, EXP1_SIZE, EXP1ReadHandler, EXP1WriteHandler);
   1953   SETUC(KSEG1 | HW_BASE, HW_SIZE, HardwareReadHandler, HardwareWriteHandler);
   1954   SETUC(KSEG1 | EXP2_BASE, EXP2_SIZE, EXP2ReadHandler, EXP2WriteHandler);
   1955   SETUC(KSEG1 | EXP3_BASE, EXP3_SIZE, EXP3ReadHandler, EXP3WriteHandler);
   1956   SETUC(KSEG1 | SIO2_BASE, SIO2_SIZE, SIO2ReadHandler, SIO2WriteHandler);
   1957 
   1958   // KSEG2 - Uncached - 0xFFFE0130
   1959   SETUC(KSEG2 | 0xFFFE0000, 0x1000, CacheControlReadHandler, CacheControlWriteHandler);
   1960 }
   1961 
   1962 void Bus::UpdateMappedRAMSize()
   1963 {
   1964   switch (s_RAM_SIZE.memory_window)
   1965   {
   1966     case 4: // 2MB memory + 6MB unmapped
   1967     {
   1968       // Used by Rock-Climbing - Mitouhou e no Chousen - Alps Hen (Japan).
   1969       // By default, all 8MB is mapped, so we only need to remap the high 6MB.
   1970       constexpr u32 MAPPED_SIZE = RAM_2MB_SIZE;
   1971       constexpr u32 UNMAPPED_START = RAM_BASE + MAPPED_SIZE;
   1972       constexpr u32 UNMAPPED_SIZE = RAM_MIRROR_SIZE - MAPPED_SIZE;
   1973       SET(g_memory_handlers, KUSEG | UNMAPPED_START, UNMAPPED_SIZE, UnmappedReadHandler, UnmappedWriteHandler);
   1974       SET(g_memory_handlers, KSEG0 | UNMAPPED_START, UNMAPPED_SIZE, UnmappedReadHandler, UnmappedWriteHandler);
   1975       SET(g_memory_handlers, KSEG1 | UNMAPPED_START, UNMAPPED_SIZE, UnmappedReadHandler, UnmappedWriteHandler);
   1976       g_ram_mapped_size = MAPPED_SIZE;
   1977     }
   1978     break;
   1979 
   1980     case 0: // 1MB memory + 7MB unmapped
   1981     case 1: // 4MB memory + 4MB unmapped
   1982     case 2: // 1MB memory + 1MB HighZ + 6MB unmapped
   1983     case 3: // 4MB memory + 4MB HighZ
   1984     case 6: // 2MB memory + 2MB HighZ + 4MB unmapped
   1985     case 7: // 8MB memory
   1986     {
   1987       // These aren't implemented because nothing is known to use them, so it can't be tested.
   1988       // If you find something that does, please let us know.
   1989       WARNING_LOG("Unhandled memory window 0x{} (register 0x{:08X}). Please report this game to developers.",
   1990                   s_RAM_SIZE.memory_window.GetValue(), s_RAM_SIZE.bits);
   1991     }
   1992       [[fallthrough]];
   1993 
   1994     case 5: // 8MB memory
   1995     {
   1996       // We only unmap the upper 6MB above, so we only need to remap this as well.
   1997       constexpr u32 REMAP_START = RAM_BASE + RAM_2MB_SIZE;
   1998       constexpr u32 REMAP_SIZE = RAM_MIRROR_SIZE - RAM_2MB_SIZE;
   1999       SET(g_memory_handlers, KUSEG | REMAP_START, REMAP_SIZE, RAMReadHandler, RAMWriteHandler);
   2000       SET(g_memory_handlers, KSEG0 | REMAP_START, REMAP_SIZE, RAMReadHandler, RAMWriteHandler);
   2001       SET(g_memory_handlers, KSEG1 | REMAP_START, REMAP_SIZE, RAMReadHandler, RAMWriteHandler);
   2002       g_ram_mapped_size = RAM_8MB_SIZE;
   2003     }
   2004     break;
   2005   }
   2006 }
   2007 
   2008 #undef SET
   2009 #undef SETUC
   2010 
   2011 void Bus::ClearHandlers(void** handlers)
   2012 {
   2013   for (u32 size = 0; size < 3; size++)
   2014   {
   2015     MemoryReadHandler* read_handlers =
   2016       OffsetHandlerArray<MemoryReadHandler>(handlers, static_cast<MemoryAccessSize>(size), MemoryAccessType::Read);
   2017     const MemoryReadHandler read_handler =
   2018       (size == 0) ?
   2019         UnmappedReadHandler<MemoryAccessSize::Byte> :
   2020         ((size == 1) ? UnmappedReadHandler<MemoryAccessSize::HalfWord> : UnmappedReadHandler<MemoryAccessSize::Word>);
   2021     MemsetPtrs(read_handlers, read_handler, MEMORY_LUT_SIZE);
   2022 
   2023     MemoryWriteHandler* write_handlers =
   2024       OffsetHandlerArray<MemoryWriteHandler>(handlers, static_cast<MemoryAccessSize>(size), MemoryAccessType::Write);
   2025     const MemoryWriteHandler write_handler =
   2026       (size == 0) ?
   2027         UnmappedWriteHandler<MemoryAccessSize::Byte> :
   2028         ((size == 1) ? UnmappedWriteHandler<MemoryAccessSize::HalfWord> : UnmappedWriteHandler<MemoryAccessSize::Word>);
   2029     MemsetPtrs(write_handlers, write_handler, MEMORY_LUT_SIZE);
   2030   }
   2031 }
   2032 
   2033 void Bus::SetHandlerForRegion(void** handlers, VirtualMemoryAddress address, u32 size,
   2034                               MemoryReadHandler read_byte_handler, MemoryReadHandler read_halfword_handler,
   2035                               MemoryReadHandler read_word_handler, MemoryWriteHandler write_byte_handler,
   2036                               MemoryWriteHandler write_halfword_handler, MemoryWriteHandler write_word_handler)
   2037 {
   2038   // Should be 4K aligned.
   2039   DebugAssert(Common::IsAlignedPow2(size, MEMORY_LUT_PAGE_SIZE));
   2040 
   2041   const u32 start_page = (address / MEMORY_LUT_PAGE_SIZE);
   2042   const u32 num_pages = ((size + (MEMORY_LUT_PAGE_SIZE - 1)) / MEMORY_LUT_PAGE_SIZE);
   2043 
   2044   for (u32 acc_size = 0; acc_size < 3; acc_size++)
   2045   {
   2046     MemoryReadHandler* read_handlers =
   2047       OffsetHandlerArray<MemoryReadHandler>(handlers, static_cast<MemoryAccessSize>(acc_size), MemoryAccessType::Read) +
   2048       start_page;
   2049     const MemoryReadHandler read_handler =
   2050       (acc_size == 0) ? read_byte_handler : ((acc_size == 1) ? read_halfword_handler : read_word_handler);
   2051 #if 0
   2052     for (u32 i = 0; i < num_pages; i++)
   2053     {
   2054       DebugAssert((acc_size == 0 && read_handlers[i] == UnmappedReadHandler<MemoryAccessSize::Byte>) ||
   2055                   (acc_size == 1 && read_handlers[i] == UnmappedReadHandler<MemoryAccessSize::HalfWord>) ||
   2056                   (acc_size == 2 && read_handlers[i] == UnmappedReadHandler<MemoryAccessSize::Word>));
   2057 
   2058       read_handlers[i] = read_handler;
   2059     }
   2060 #else
   2061     MemsetPtrs(read_handlers, read_handler, num_pages);
   2062 #endif
   2063 
   2064     MemoryWriteHandler* write_handlers = OffsetHandlerArray<MemoryWriteHandler>(
   2065                                            handlers, static_cast<MemoryAccessSize>(acc_size), MemoryAccessType::Write) +
   2066                                          start_page;
   2067     const MemoryWriteHandler write_handler =
   2068       (acc_size == 0) ? write_byte_handler : ((acc_size == 1) ? write_halfword_handler : write_word_handler);
   2069 #if 0
   2070     for (u32 i = 0; i < num_pages; i++)
   2071     {
   2072       DebugAssert((acc_size == 0 && write_handlers[i] == UnmappedWriteHandler<MemoryAccessSize::Byte>) ||
   2073                   (acc_size == 1 && write_handlers[i] == UnmappedWriteHandler<MemoryAccessSize::HalfWord>) ||
   2074                   (acc_size == 2 && write_handlers[i] == UnmappedWriteHandler<MemoryAccessSize::Word>));
   2075 
   2076       write_handlers[i] = write_handler;
   2077     }
   2078 #else
   2079     MemsetPtrs(write_handlers, write_handler, num_pages);
   2080 #endif
   2081   }
   2082 }
   2083 
   2084 void** Bus::GetMemoryHandlers(bool isolate_cache, bool swap_caches)
   2085 {
   2086   if (!isolate_cache)
   2087     return g_memory_handlers;
   2088 
   2089 #ifdef _DEBUG
   2090   if (swap_caches)
   2091     WARNING_LOG("Cache isolated and swapped, icache will be written instead of scratchpad?");
   2092 #endif
   2093 
   2094   return g_memory_handlers_isc;
   2095 }