mirror of https://github.com/stenzek/duckstation
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			1779 lines
		
	
	
		
			59 KiB
		
	
	
	
		
			C++
		
	
			
		
		
	
	
			1779 lines
		
	
	
		
			59 KiB
		
	
	
	
		
			C++
		
	
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
 | 
						|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 | 
						|
 | 
						|
#include "bus.h"
 | 
						|
#include "cdrom.h"
 | 
						|
#include "cpu_code_cache.h"
 | 
						|
#include "cpu_core.h"
 | 
						|
#include "cpu_core_private.h"
 | 
						|
#include "cpu_disasm.h"
 | 
						|
#include "dma.h"
 | 
						|
#include "gpu.h"
 | 
						|
#include "host.h"
 | 
						|
#include "interrupt_controller.h"
 | 
						|
#include "mdec.h"
 | 
						|
#include "pad.h"
 | 
						|
#include "settings.h"
 | 
						|
#include "sio.h"
 | 
						|
#include "spu.h"
 | 
						|
#include "system.h"
 | 
						|
#include "timers.h"
 | 
						|
#include "timing_event.h"
 | 
						|
 | 
						|
#include "util/state_wrapper.h"
 | 
						|
 | 
						|
#include "common/align.h"
 | 
						|
#include "common/assert.h"
 | 
						|
#include "common/intrin.h"
 | 
						|
#include "common/log.h"
 | 
						|
#include "common/memmap.h"
 | 
						|
 | 
						|
#include <cstdio>
 | 
						|
#include <tuple>
 | 
						|
#include <utility>
 | 
						|
 | 
						|
Log_SetChannel(Bus);
 | 
						|
 | 
						|
// TODO: Get rid of page code bits, instead use page faults to track SMC.
 | 
						|
 | 
						|
// Exports for external debugger access
 | 
						|
#ifndef __ANDROID__
 | 
						|
namespace Exports {
 | 
						|
 | 
						|
extern "C" {
 | 
						|
#ifdef _WIN32
 | 
						|
_declspec(dllexport) uintptr_t RAM;
 | 
						|
_declspec(dllexport) u32 RAM_SIZE, RAM_MASK;
 | 
						|
#else
 | 
						|
__attribute__((visibility("default"), used)) uintptr_t RAM;
 | 
						|
__attribute__((visibility("default"), used)) u32 RAM_SIZE, RAM_MASK;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Exports
 | 
						|
#endif
 | 
						|
 | 
						|
namespace Bus {
 | 
						|
 | 
						|
namespace {
 | 
						|
union MEMDELAY
 | 
						|
{
 | 
						|
  u32 bits;
 | 
						|
 | 
						|
  BitField<u32, u8, 4, 4> access_time; // cycles
 | 
						|
  BitField<u32, bool, 8, 1> use_com0_time;
 | 
						|
  BitField<u32, bool, 9, 1> use_com1_time;
 | 
						|
  BitField<u32, bool, 10, 1> use_com2_time;
 | 
						|
  BitField<u32, bool, 11, 1> use_com3_time;
 | 
						|
  BitField<u32, bool, 12, 1> data_bus_16bit;
 | 
						|
  BitField<u32, u8, 16, 5> memory_window_size;
 | 
						|
 | 
						|
  static constexpr u32 WRITE_MASK = 0b10101111'00011111'11111111'11111111;
 | 
						|
};
 | 
						|
 | 
						|
union COMDELAY
 | 
						|
{
 | 
						|
  u32 bits;
 | 
						|
 | 
						|
  BitField<u32, u8, 0, 4> com0;
 | 
						|
  BitField<u32, u8, 4, 4> com1;
 | 
						|
  BitField<u32, u8, 8, 4> com2;
 | 
						|
  BitField<u32, u8, 12, 4> com3;
 | 
						|
  BitField<u32, u8, 16, 2> comunk;
 | 
						|
 | 
						|
  static constexpr u32 WRITE_MASK = 0b00000000'00000011'11111111'11111111;
 | 
						|
};
 | 
						|
 | 
						|
union MEMCTRL
 | 
						|
{
 | 
						|
  u32 regs[MEMCTRL_REG_COUNT];
 | 
						|
 | 
						|
  struct
 | 
						|
  {
 | 
						|
    u32 exp1_base;
 | 
						|
    u32 exp2_base;
 | 
						|
    MEMDELAY exp1_delay_size;
 | 
						|
    MEMDELAY exp3_delay_size;
 | 
						|
    MEMDELAY bios_delay_size;
 | 
						|
    MEMDELAY spu_delay_size;
 | 
						|
    MEMDELAY cdrom_delay_size;
 | 
						|
    MEMDELAY exp2_delay_size;
 | 
						|
    COMDELAY common_delay;
 | 
						|
  };
 | 
						|
};
 | 
						|
} // namespace
 | 
						|
 | 
						|
static void* s_shmem_handle = nullptr;
 | 
						|
 | 
						|
std::bitset<RAM_8MB_CODE_PAGE_COUNT> g_ram_code_bits{};
 | 
						|
u8* g_ram = nullptr;
 | 
						|
u8* g_unprotected_ram = nullptr;
 | 
						|
u32 g_ram_size = 0;
 | 
						|
u32 g_ram_mask = 0;
 | 
						|
u8* g_bios = nullptr;
 | 
						|
void** g_memory_handlers = nullptr;
 | 
						|
void** g_memory_handlers_isc = nullptr;
 | 
						|
 | 
						|
std::array<TickCount, 3> g_exp1_access_time = {};
 | 
						|
std::array<TickCount, 3> g_exp2_access_time = {};
 | 
						|
std::array<TickCount, 3> g_bios_access_time = {};
 | 
						|
std::array<TickCount, 3> g_cdrom_access_time = {};
 | 
						|
std::array<TickCount, 3> g_spu_access_time = {};
 | 
						|
 | 
						|
static std::vector<u8> s_exp1_rom;
 | 
						|
 | 
						|
static MEMCTRL s_MEMCTRL = {};
 | 
						|
static u32 s_ram_size_reg = 0;
 | 
						|
 | 
						|
static std::string s_tty_line_buffer;
 | 
						|
 | 
						|
static CPUFastmemMode s_fastmem_mode = CPUFastmemMode::Disabled;
 | 
						|
 | 
						|
#ifdef ENABLE_MMAP_FASTMEM
 | 
						|
static SharedMemoryMappingArea s_fastmem_arena;
 | 
						|
static std::vector<std::pair<u8*, size_t>> s_fastmem_ram_views;
 | 
						|
#endif
 | 
						|
 | 
						|
static u8** s_fastmem_lut = nullptr;
 | 
						|
 | 
						|
static void SetRAMSize(bool enable_8mb_ram);
 | 
						|
 | 
						|
static std::tuple<TickCount, TickCount, TickCount> CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay);
 | 
						|
static void RecalculateMemoryTimings();
 | 
						|
 | 
						|
static u8* GetLUTFastmemPointer(u32 address, u8* ram_ptr);
 | 
						|
 | 
						|
static void SetRAMPageWritable(u32 page_index, bool writable);
 | 
						|
 | 
						|
static void SetHandlers();
 | 
						|
 | 
						|
template<typename FP>
 | 
						|
static FP* OffsetHandlerArray(void** handlers, MemoryAccessSize size, MemoryAccessType type);
 | 
						|
} // namespace Bus
 | 
						|
 | 
						|
namespace MemoryMap {
 | 
						|
static constexpr size_t RAM_OFFSET = 0;
 | 
						|
static constexpr size_t RAM_SIZE = Bus::RAM_8MB_SIZE;
 | 
						|
static constexpr size_t BIOS_OFFSET = RAM_OFFSET + RAM_SIZE;
 | 
						|
static constexpr size_t BIOS_SIZE = Bus::BIOS_SIZE;
 | 
						|
static constexpr size_t LUT_OFFSET = BIOS_OFFSET + BIOS_SIZE;
 | 
						|
static constexpr size_t LUT_SIZE = (sizeof(void*) * Bus::MEMORY_LUT_SLOTS) * 2; // normal and isolated
 | 
						|
static constexpr size_t TOTAL_SIZE = LUT_OFFSET + LUT_SIZE;
 | 
						|
} // namespace MemoryMap
 | 
						|
 | 
						|
#define FIXUP_HALFWORD_OFFSET(size, offset) ((size >= MemoryAccessSize::HalfWord) ? (offset) : ((offset) & ~1u))
 | 
						|
#define FIXUP_HALFWORD_READ_VALUE(size, offset, value)                                                                 \
 | 
						|
  ((size >= MemoryAccessSize::HalfWord) ? (value) : ((value) >> (((offset)&u32(1)) * 8u)))
 | 
						|
#define FIXUP_HALFWORD_WRITE_VALUE(size, offset, value)                                                                \
 | 
						|
  ((size >= MemoryAccessSize::HalfWord) ? (value) : ((value) << (((offset)&u32(1)) * 8u)))
 | 
						|
 | 
						|
#define FIXUP_WORD_OFFSET(size, offset) ((size == MemoryAccessSize::Word) ? (offset) : ((offset) & ~3u))
 | 
						|
#define FIXUP_WORD_READ_VALUE(size, offset, value)                                                                     \
 | 
						|
  ((size == MemoryAccessSize::Word) ? (value) : ((value) >> (((offset)&3u) * 8)))
 | 
						|
#define FIXUP_WORD_WRITE_VALUE(size, offset, value)                                                                    \
 | 
						|
  ((size == MemoryAccessSize::Word) ? (value) : ((value) << (((offset)&3u) * 8)))
 | 
						|
 | 
						|
bool Bus::AllocateMemory()
 | 
						|
{
 | 
						|
  s_shmem_handle = MemMap::CreateSharedMemory(MemMap::GetFileMappingName("duckstation").c_str(), MemoryMap::TOTAL_SIZE);
 | 
						|
  if (!s_shmem_handle)
 | 
						|
  {
 | 
						|
    Host::ReportErrorAsync("Error", "Failed to allocate memory");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  g_ram = static_cast<u8*>(MemMap::MapSharedMemory(s_shmem_handle, MemoryMap::RAM_OFFSET, nullptr, MemoryMap::RAM_SIZE,
 | 
						|
                                                   PageProtect::ReadWrite));
 | 
						|
  g_unprotected_ram = static_cast<u8*>(MemMap::MapSharedMemory(s_shmem_handle, MemoryMap::RAM_OFFSET, nullptr,
 | 
						|
                                                               MemoryMap::RAM_SIZE, PageProtect::ReadWrite));
 | 
						|
  if (!g_ram || !g_unprotected_ram)
 | 
						|
  {
 | 
						|
    Host::ReportErrorAsync("Error", "Failed to map memory for RAM");
 | 
						|
    ReleaseMemory();
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  Log_VerboseFmt("RAM is mapped at {}.", static_cast<void*>(g_ram));
 | 
						|
 | 
						|
  g_bios = static_cast<u8*>(MemMap::MapSharedMemory(s_shmem_handle, MemoryMap::BIOS_OFFSET, nullptr,
 | 
						|
                                                    MemoryMap::BIOS_SIZE, PageProtect::ReadWrite));
 | 
						|
  if (!g_bios)
 | 
						|
  {
 | 
						|
    Host::ReportErrorAsync("Error", "Failed to map memory for BIOS");
 | 
						|
    ReleaseMemory();
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  Log_VerboseFmt("BIOS is mapped at {}.", static_cast<void*>(g_bios));
 | 
						|
 | 
						|
  g_memory_handlers = static_cast<void**>(MemMap::MapSharedMemory(s_shmem_handle, MemoryMap::LUT_OFFSET, nullptr,
 | 
						|
                                                                  MemoryMap::LUT_SIZE, PageProtect::ReadWrite));
 | 
						|
  if (!g_memory_handlers)
 | 
						|
  {
 | 
						|
    Host::ReportErrorAsync("Error", "Failed to map memory for LUTs");
 | 
						|
    ReleaseMemory();
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  Log_VerboseFmt("LUTs are mapped at {}.", static_cast<void*>(g_memory_handlers));
 | 
						|
  g_memory_handlers_isc = g_memory_handlers + MEMORY_LUT_SLOTS;
 | 
						|
  SetHandlers();
 | 
						|
 | 
						|
#ifdef ENABLE_MMAP_FASTMEM
 | 
						|
  if (!s_fastmem_arena.Create(FASTMEM_ARENA_SIZE))
 | 
						|
  {
 | 
						|
    // TODO: maybe make this non-fatal?
 | 
						|
    Host::ReportErrorAsync("Error", "Failed to create fastmem arena");
 | 
						|
    ReleaseMemory();
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  Log_InfoPrintf("Fastmem base: %p", s_fastmem_arena.BasePointer());
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef __ANDROID__
 | 
						|
  Exports::RAM = reinterpret_cast<uintptr_t>(g_unprotected_ram);
 | 
						|
#endif
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void Bus::ReleaseMemory()
 | 
						|
{
 | 
						|
#ifndef __ANDROID__
 | 
						|
  Exports::RAM = 0;
 | 
						|
  Exports::RAM_SIZE = 0;
 | 
						|
  Exports::RAM_MASK = 0;
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef ENABLE_MMAP_FASTMEM
 | 
						|
  DebugAssert(s_fastmem_ram_views.empty());
 | 
						|
  s_fastmem_arena.Destroy();
 | 
						|
#endif
 | 
						|
 | 
						|
  std::free(s_fastmem_lut);
 | 
						|
  s_fastmem_lut = nullptr;
 | 
						|
 | 
						|
  g_memory_handlers_isc = nullptr;
 | 
						|
  if (g_memory_handlers)
 | 
						|
  {
 | 
						|
    MemMap::UnmapSharedMemory(g_memory_handlers, MemoryMap::LUT_SIZE);
 | 
						|
    g_memory_handlers = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (g_bios)
 | 
						|
  {
 | 
						|
    MemMap::UnmapSharedMemory(g_bios, MemoryMap::BIOS_SIZE);
 | 
						|
    g_bios = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (g_unprotected_ram)
 | 
						|
  {
 | 
						|
    MemMap::UnmapSharedMemory(g_unprotected_ram, MemoryMap::RAM_SIZE);
 | 
						|
    g_unprotected_ram = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (g_ram)
 | 
						|
  {
 | 
						|
    MemMap::UnmapSharedMemory(g_ram, MemoryMap::RAM_SIZE);
 | 
						|
    g_ram = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (s_shmem_handle)
 | 
						|
  {
 | 
						|
    MemMap::DestroySharedMemory(s_shmem_handle);
 | 
						|
    s_shmem_handle = nullptr;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool Bus::Initialize()
 | 
						|
{
 | 
						|
  SetRAMSize(g_settings.enable_8mb_ram);
 | 
						|
  Reset();
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void Bus::SetRAMSize(bool enable_8mb_ram)
 | 
						|
{
 | 
						|
  g_ram_size = enable_8mb_ram ? RAM_8MB_SIZE : RAM_2MB_SIZE;
 | 
						|
  g_ram_mask = enable_8mb_ram ? RAM_8MB_MASK : RAM_2MB_MASK;
 | 
						|
 | 
						|
#ifndef __ANDROID__
 | 
						|
  Exports::RAM_SIZE = g_ram_size;
 | 
						|
  Exports::RAM_MASK = g_ram_mask;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void Bus::Shutdown()
 | 
						|
{
 | 
						|
  UpdateFastmemViews(CPUFastmemMode::Disabled);
 | 
						|
  CPU::g_state.fastmem_base = nullptr;
 | 
						|
 | 
						|
  g_ram_mask = 0;
 | 
						|
  g_ram_size = 0;
 | 
						|
 | 
						|
#ifndef __ANDROID__
 | 
						|
  Exports::RAM = 0;
 | 
						|
  Exports::RAM_SIZE = 0;
 | 
						|
  Exports::RAM_MASK = 0;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void Bus::Reset()
 | 
						|
{
 | 
						|
  std::memset(g_ram, 0, g_ram_size);
 | 
						|
  s_MEMCTRL.exp1_base = 0x1F000000;
 | 
						|
  s_MEMCTRL.exp2_base = 0x1F802000;
 | 
						|
  s_MEMCTRL.exp1_delay_size.bits = 0x0013243F;
 | 
						|
  s_MEMCTRL.exp3_delay_size.bits = 0x00003022;
 | 
						|
  s_MEMCTRL.bios_delay_size.bits = 0x0013243F;
 | 
						|
  s_MEMCTRL.spu_delay_size.bits = 0x200931E1;
 | 
						|
  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();
 | 
						|
}
 | 
						|
 | 
						|
void Bus::AddTTYCharacter(char ch)
 | 
						|
{
 | 
						|
  if (ch == '\r')
 | 
						|
  {
 | 
						|
  }
 | 
						|
  else if (ch == '\n')
 | 
						|
  {
 | 
						|
    if (!s_tty_line_buffer.empty())
 | 
						|
    {
 | 
						|
      Log::Writef("TTY", "", LOGLEVEL_INFO, "\033[1;34m%s\033[0m", s_tty_line_buffer.c_str());
 | 
						|
#ifdef _DEBUG
 | 
						|
      if (CPU::IsTraceEnabled())
 | 
						|
        CPU::WriteToExecutionLog("TTY: %s\n", s_tty_line_buffer.c_str());
 | 
						|
#endif
 | 
						|
    }
 | 
						|
    s_tty_line_buffer.clear();
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    s_tty_line_buffer += ch;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void Bus::AddTTYString(const std::string_view& str)
 | 
						|
{
 | 
						|
  for (char ch : str)
 | 
						|
    AddTTYCharacter(ch);
 | 
						|
}
 | 
						|
 | 
						|
bool Bus::DoState(StateWrapper& sw)
 | 
						|
{
 | 
						|
  u32 ram_size = g_ram_size;
 | 
						|
  sw.DoEx(&ram_size, 52, static_cast<u32>(RAM_2MB_SIZE));
 | 
						|
  if (ram_size != g_ram_size)
 | 
						|
  {
 | 
						|
    const bool using_8mb_ram = (ram_size == RAM_8MB_SIZE);
 | 
						|
    SetRAMSize(using_8mb_ram);
 | 
						|
    UpdateFastmemViews(s_fastmem_mode);
 | 
						|
    CPU::UpdateMemoryPointers();
 | 
						|
  }
 | 
						|
 | 
						|
  sw.Do(&g_exp1_access_time);
 | 
						|
  sw.Do(&g_exp2_access_time);
 | 
						|
  sw.Do(&g_bios_access_time);
 | 
						|
  sw.Do(&g_cdrom_access_time);
 | 
						|
  sw.Do(&g_spu_access_time);
 | 
						|
  sw.DoBytes(g_ram, g_ram_size);
 | 
						|
 | 
						|
  if (sw.GetVersion() < 58)
 | 
						|
  {
 | 
						|
    Log_WarningPrint("Overwriting loaded BIOS with old save state.");
 | 
						|
    sw.DoBytes(g_bios, BIOS_SIZE);
 | 
						|
  }
 | 
						|
 | 
						|
  sw.DoArray(s_MEMCTRL.regs, countof(s_MEMCTRL.regs));
 | 
						|
  sw.Do(&s_ram_size_reg);
 | 
						|
  sw.Do(&s_tty_line_buffer);
 | 
						|
  return !sw.HasError();
 | 
						|
}
 | 
						|
 | 
						|
void Bus::SetExpansionROM(std::vector<u8> data)
 | 
						|
{
 | 
						|
  s_exp1_rom = std::move(data);
 | 
						|
}
 | 
						|
 | 
						|
std::tuple<TickCount, TickCount, TickCount> Bus::CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay)
 | 
						|
{
 | 
						|
  // from nocash spec
 | 
						|
  s32 first = 0, seq = 0, min = 0;
 | 
						|
  if (mem_delay.use_com0_time)
 | 
						|
  {
 | 
						|
    first += s32(common_delay.com0) - 1;
 | 
						|
    seq += s32(common_delay.com0) - 1;
 | 
						|
  }
 | 
						|
  if (mem_delay.use_com2_time)
 | 
						|
  {
 | 
						|
    first += s32(common_delay.com2);
 | 
						|
    seq += s32(common_delay.com2);
 | 
						|
  }
 | 
						|
  if (mem_delay.use_com3_time)
 | 
						|
  {
 | 
						|
    min = s32(common_delay.com3);
 | 
						|
  }
 | 
						|
  if (first < 6)
 | 
						|
    first++;
 | 
						|
 | 
						|
  first = first + s32(mem_delay.access_time) + 2;
 | 
						|
  seq = seq + s32(mem_delay.access_time) + 2;
 | 
						|
 | 
						|
  if (first < (min + 6))
 | 
						|
    first = min + 6;
 | 
						|
  if (seq < (min + 2))
 | 
						|
    seq = min + 2;
 | 
						|
 | 
						|
  const TickCount byte_access_time = first;
 | 
						|
  const TickCount halfword_access_time = mem_delay.data_bus_16bit ? first : (first + seq);
 | 
						|
  const TickCount word_access_time = mem_delay.data_bus_16bit ? (first + seq) : (first + seq + seq + seq);
 | 
						|
  return std::tie(std::max(byte_access_time - 1, 0), std::max(halfword_access_time - 1, 0),
 | 
						|
                  std::max(word_access_time - 1, 0));
 | 
						|
}
 | 
						|
 | 
						|
void Bus::RecalculateMemoryTimings()
 | 
						|
{
 | 
						|
  std::tie(g_bios_access_time[0], g_bios_access_time[1], g_bios_access_time[2]) =
 | 
						|
    CalculateMemoryTiming(s_MEMCTRL.bios_delay_size, s_MEMCTRL.common_delay);
 | 
						|
  std::tie(g_cdrom_access_time[0], g_cdrom_access_time[1], g_cdrom_access_time[2]) =
 | 
						|
    CalculateMemoryTiming(s_MEMCTRL.cdrom_delay_size, s_MEMCTRL.common_delay);
 | 
						|
  std::tie(g_spu_access_time[0], g_spu_access_time[1], g_spu_access_time[2]) =
 | 
						|
    CalculateMemoryTiming(s_MEMCTRL.spu_delay_size, s_MEMCTRL.common_delay);
 | 
						|
 | 
						|
  Log_TracePrintf("BIOS Memory Timing: %u bit bus, byte=%d, halfword=%d, word=%d",
 | 
						|
                  s_MEMCTRL.bios_delay_size.data_bus_16bit ? 16 : 8, g_bios_access_time[0] + 1,
 | 
						|
                  g_bios_access_time[1] + 1, g_bios_access_time[2] + 1);
 | 
						|
  Log_TracePrintf("CDROM Memory Timing: %u bit bus, byte=%d, halfword=%d, word=%d",
 | 
						|
                  s_MEMCTRL.cdrom_delay_size.data_bus_16bit ? 16 : 8, g_cdrom_access_time[0] + 1,
 | 
						|
                  g_cdrom_access_time[1] + 1, g_cdrom_access_time[2] + 1);
 | 
						|
  Log_TracePrintf("SPU Memory Timing: %u bit bus, byte=%d, halfword=%d, word=%d",
 | 
						|
                  s_MEMCTRL.spu_delay_size.data_bus_16bit ? 16 : 8, g_spu_access_time[0] + 1, g_spu_access_time[1] + 1,
 | 
						|
                  g_spu_access_time[2] + 1);
 | 
						|
}
 | 
						|
 | 
						|
CPUFastmemMode Bus::GetFastmemMode()
 | 
						|
{
 | 
						|
  return s_fastmem_mode;
 | 
						|
}
 | 
						|
 | 
						|
void* Bus::GetFastmemBase(bool isc)
 | 
						|
{
 | 
						|
#ifdef ENABLE_MMAP_FASTMEM
 | 
						|
  if (s_fastmem_mode == CPUFastmemMode::MMap)
 | 
						|
    return isc ? nullptr : s_fastmem_arena.BasePointer();
 | 
						|
#endif
 | 
						|
  if (s_fastmem_mode == CPUFastmemMode::LUT)
 | 
						|
    return reinterpret_cast<u8*>(s_fastmem_lut + (isc ? (FASTMEM_LUT_SIZE * sizeof(void*)) : 0));
 | 
						|
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
u8* Bus::GetLUTFastmemPointer(u32 address, u8* ram_ptr)
 | 
						|
{
 | 
						|
  return ram_ptr - address;
 | 
						|
}
 | 
						|
 | 
						|
void Bus::UpdateFastmemViews(CPUFastmemMode mode)
 | 
						|
{
 | 
						|
#ifndef ENABLE_MMAP_FASTMEM
 | 
						|
  Assert(mode != CPUFastmemMode::MMap);
 | 
						|
#else
 | 
						|
  for (const auto& it : s_fastmem_ram_views)
 | 
						|
    s_fastmem_arena.Unmap(it.first, it.second);
 | 
						|
  s_fastmem_ram_views.clear();
 | 
						|
#endif
 | 
						|
 | 
						|
  s_fastmem_mode = mode;
 | 
						|
  if (mode == CPUFastmemMode::Disabled)
 | 
						|
    return;
 | 
						|
 | 
						|
#ifdef ENABLE_MMAP_FASTMEM
 | 
						|
  if (mode == CPUFastmemMode::MMap)
 | 
						|
  {
 | 
						|
    auto MapRAM = [](u32 base_address) {
 | 
						|
      u8* map_address = s_fastmem_arena.BasePointer() + base_address;
 | 
						|
      if (!s_fastmem_arena.Map(s_shmem_handle, 0, map_address, g_ram_size, PageProtect::ReadWrite))
 | 
						|
      {
 | 
						|
        Log_ErrorPrintf("Failed to map RAM at fastmem area %p (offset 0x%08X)", map_address, g_ram_size);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      // mark all pages with code as non-writable
 | 
						|
      for (u32 i = 0; i < static_cast<u32>(g_ram_code_bits.size()); i++)
 | 
						|
      {
 | 
						|
        if (g_ram_code_bits[i])
 | 
						|
        {
 | 
						|
          u8* page_address = map_address + (i * HOST_PAGE_SIZE);
 | 
						|
          if (!MemMap::MemProtect(page_address, HOST_PAGE_SIZE, PageProtect::ReadOnly))
 | 
						|
          {
 | 
						|
            Log_ErrorPrintf("Failed to write-protect code page at %p", page_address);
 | 
						|
            s_fastmem_arena.Unmap(map_address, g_ram_size);
 | 
						|
            return;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      s_fastmem_ram_views.emplace_back(map_address, g_ram_size);
 | 
						|
    };
 | 
						|
 | 
						|
    // KUSEG - cached
 | 
						|
    MapRAM(0x00000000);
 | 
						|
 | 
						|
    // KSEG0 - cached
 | 
						|
    MapRAM(0x80000000);
 | 
						|
 | 
						|
    // KSEG1 - uncached
 | 
						|
    MapRAM(0xA0000000);
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  if (!s_fastmem_lut)
 | 
						|
  {
 | 
						|
    s_fastmem_lut = static_cast<u8**>(std::malloc(sizeof(u8*) * FASTMEM_LUT_SLOTS));
 | 
						|
    Assert(s_fastmem_lut);
 | 
						|
 | 
						|
    Log_InfoPrintf("Fastmem base (software): %p", s_fastmem_lut);
 | 
						|
  }
 | 
						|
 | 
						|
  std::memset(s_fastmem_lut, 0, sizeof(u8*) * FASTMEM_LUT_SLOTS);
 | 
						|
 | 
						|
  auto MapRAM = [](u32 base_address) {
 | 
						|
    u8* ram_ptr = g_ram + (base_address & g_ram_mask);
 | 
						|
    for (u32 address = 0; address < g_ram_size; address += FASTMEM_LUT_PAGE_SIZE)
 | 
						|
    {
 | 
						|
      const u32 lut_index = (base_address + address) >> FASTMEM_LUT_PAGE_SHIFT;
 | 
						|
      s_fastmem_lut[lut_index] = GetLUTFastmemPointer(base_address + address, ram_ptr);
 | 
						|
      ram_ptr += FASTMEM_LUT_PAGE_SIZE;
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  // KUSEG - cached
 | 
						|
  MapRAM(0x00000000);
 | 
						|
  MapRAM(0x00200000);
 | 
						|
  MapRAM(0x00400000);
 | 
						|
  MapRAM(0x00600000);
 | 
						|
 | 
						|
  // KSEG0 - cached
 | 
						|
  MapRAM(0x80000000);
 | 
						|
  MapRAM(0x80200000);
 | 
						|
  MapRAM(0x80400000);
 | 
						|
  MapRAM(0x80600000);
 | 
						|
 | 
						|
  // KSEG1 - uncached
 | 
						|
  MapRAM(0xA0000000);
 | 
						|
  MapRAM(0xA0200000);
 | 
						|
  MapRAM(0xA0400000);
 | 
						|
  MapRAM(0xA0600000);
 | 
						|
}
 | 
						|
 | 
						|
bool Bus::CanUseFastmemForAddress(VirtualMemoryAddress address)
 | 
						|
{
 | 
						|
  const PhysicalMemoryAddress paddr = address & CPU::PHYSICAL_MEMORY_ADDRESS_MASK;
 | 
						|
 | 
						|
  switch (s_fastmem_mode)
 | 
						|
  {
 | 
						|
#ifdef ENABLE_MMAP_FASTMEM
 | 
						|
    case CPUFastmemMode::MMap:
 | 
						|
    {
 | 
						|
      // Currently since we don't map the mirrors, don't use fastmem for them.
 | 
						|
      // This is because the swapping of page code bits for SMC is too expensive.
 | 
						|
      return (paddr < RAM_MIRROR_END);
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    case CPUFastmemMode::LUT:
 | 
						|
      return (paddr < g_ram_size);
 | 
						|
 | 
						|
    case CPUFastmemMode::Disabled:
 | 
						|
    default:
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool Bus::IsRAMCodePage(u32 index)
 | 
						|
{
 | 
						|
  return g_ram_code_bits[index];
 | 
						|
}
 | 
						|
 | 
						|
void Bus::SetRAMCodePage(u32 index)
 | 
						|
{
 | 
						|
  if (g_ram_code_bits[index])
 | 
						|
    return;
 | 
						|
 | 
						|
  // protect fastmem pages
 | 
						|
  g_ram_code_bits[index] = true;
 | 
						|
  SetRAMPageWritable(index, false);
 | 
						|
}
 | 
						|
 | 
						|
void Bus::ClearRAMCodePage(u32 index)
 | 
						|
{
 | 
						|
  if (!g_ram_code_bits[index])
 | 
						|
    return;
 | 
						|
 | 
						|
  // unprotect fastmem pages
 | 
						|
  g_ram_code_bits[index] = false;
 | 
						|
  SetRAMPageWritable(index, true);
 | 
						|
}
 | 
						|
 | 
						|
void Bus::SetRAMPageWritable(u32 page_index, bool writable)
 | 
						|
{
 | 
						|
  if (!MemMap::MemProtect(&g_ram[page_index * HOST_PAGE_SIZE], HOST_PAGE_SIZE,
 | 
						|
                          writable ? PageProtect::ReadWrite : PageProtect::ReadOnly)) [[unlikely]]
 | 
						|
  {
 | 
						|
    Log_ErrorFmt("Failed to set RAM host page {} ({}) to {}", page_index,
 | 
						|
                 reinterpret_cast<const void*>(&g_ram[page_index * HOST_PAGE_SIZE]),
 | 
						|
                 writable ? "read-write" : "read-only");
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef ENABLE_MMAP_FASTMEM
 | 
						|
  if (s_fastmem_mode == CPUFastmemMode::MMap)
 | 
						|
  {
 | 
						|
    const PageProtect protect = writable ? PageProtect::ReadWrite : PageProtect::ReadOnly;
 | 
						|
 | 
						|
    // unprotect fastmem pages
 | 
						|
    for (const auto& it : s_fastmem_ram_views)
 | 
						|
    {
 | 
						|
      u8* page_address = it.first + (page_index * HOST_PAGE_SIZE);
 | 
						|
      if (!MemMap::MemProtect(page_address, HOST_PAGE_SIZE, protect)) [[unlikely]]
 | 
						|
      {
 | 
						|
        Log_ErrorPrintf("Failed to %s code page %u (0x%08X) @ %p", writable ? "unprotect" : "protect", page_index,
 | 
						|
                        page_index * static_cast<u32>(HOST_PAGE_SIZE), page_address);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void Bus::ClearRAMCodePageFlags()
 | 
						|
{
 | 
						|
  g_ram_code_bits.reset();
 | 
						|
 | 
						|
  if (!MemMap::MemProtect(g_ram, RAM_8MB_SIZE, PageProtect::ReadWrite))
 | 
						|
    Log_ErrorPrint("Failed to restore RAM protection to read-write.");
 | 
						|
 | 
						|
#ifdef ENABLE_MMAP_FASTMEM
 | 
						|
  if (s_fastmem_mode == CPUFastmemMode::MMap)
 | 
						|
  {
 | 
						|
    // unprotect fastmem pages
 | 
						|
    for (const auto& it : s_fastmem_ram_views)
 | 
						|
    {
 | 
						|
      if (!MemMap::MemProtect(it.first, it.second, PageProtect::ReadWrite))
 | 
						|
      {
 | 
						|
        Log_ErrorPrintf("Failed to unprotect code pages for fastmem view @ %p", it.first);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
bool Bus::IsCodePageAddress(PhysicalMemoryAddress address)
 | 
						|
{
 | 
						|
  return IsRAMAddress(address) ? g_ram_code_bits[(address & g_ram_mask) / HOST_PAGE_SIZE] : false;
 | 
						|
}
 | 
						|
 | 
						|
bool Bus::HasCodePagesInRange(PhysicalMemoryAddress start_address, u32 size)
 | 
						|
{
 | 
						|
  if (!IsRAMAddress(start_address))
 | 
						|
    return false;
 | 
						|
 | 
						|
  start_address = (start_address & g_ram_mask);
 | 
						|
 | 
						|
  const u32 end_address = start_address + size;
 | 
						|
  while (start_address < end_address)
 | 
						|
  {
 | 
						|
    const u32 code_page_index = start_address / HOST_PAGE_SIZE;
 | 
						|
    if (g_ram_code_bits[code_page_index])
 | 
						|
      return true;
 | 
						|
 | 
						|
    start_address += HOST_PAGE_SIZE;
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
std::optional<Bus::MemoryRegion> Bus::GetMemoryRegionForAddress(PhysicalMemoryAddress address)
 | 
						|
{
 | 
						|
  if (address < RAM_2MB_SIZE)
 | 
						|
    return MemoryRegion::RAM;
 | 
						|
  else if (address < RAM_MIRROR_END)
 | 
						|
    return static_cast<MemoryRegion>(static_cast<u32>(MemoryRegion::RAM) + (address / RAM_2MB_SIZE));
 | 
						|
  else if (address >= EXP1_BASE && address < (EXP1_BASE + EXP1_SIZE))
 | 
						|
    return MemoryRegion::EXP1;
 | 
						|
  else if (address >= CPU::DCACHE_LOCATION && address < (CPU::DCACHE_LOCATION + CPU::DCACHE_SIZE))
 | 
						|
    return MemoryRegion::Scratchpad;
 | 
						|
  else if (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_SIZE))
 | 
						|
    return MemoryRegion::BIOS;
 | 
						|
 | 
						|
  return std::nullopt;
 | 
						|
}
 | 
						|
 | 
						|
static constexpr std::array<std::pair<PhysicalMemoryAddress, PhysicalMemoryAddress>,
 | 
						|
                            static_cast<u32>(Bus::MemoryRegion::Count)>
 | 
						|
  s_code_region_ranges = {{
 | 
						|
    {0, Bus::RAM_2MB_SIZE},
 | 
						|
    {Bus::RAM_2MB_SIZE, Bus::RAM_2MB_SIZE * 2},
 | 
						|
    {Bus::RAM_2MB_SIZE * 2, Bus::RAM_2MB_SIZE * 3},
 | 
						|
    {Bus::RAM_2MB_SIZE * 3, Bus::RAM_MIRROR_END},
 | 
						|
    {Bus::EXP1_BASE, Bus::EXP1_BASE + Bus::EXP1_SIZE},
 | 
						|
    {CPU::DCACHE_LOCATION, CPU::DCACHE_LOCATION + CPU::DCACHE_SIZE},
 | 
						|
    {Bus::BIOS_BASE, Bus::BIOS_BASE + Bus::BIOS_SIZE},
 | 
						|
  }};
 | 
						|
 | 
						|
PhysicalMemoryAddress Bus::GetMemoryRegionStart(MemoryRegion region)
 | 
						|
{
 | 
						|
  return s_code_region_ranges[static_cast<u32>(region)].first;
 | 
						|
}
 | 
						|
 | 
						|
PhysicalMemoryAddress Bus::GetMemoryRegionEnd(MemoryRegion region)
 | 
						|
{
 | 
						|
  return s_code_region_ranges[static_cast<u32>(region)].second;
 | 
						|
}
 | 
						|
 | 
						|
u8* Bus::GetMemoryRegionPointer(MemoryRegion region)
 | 
						|
{
 | 
						|
  switch (region)
 | 
						|
  {
 | 
						|
    case MemoryRegion::RAM:
 | 
						|
      return g_unprotected_ram;
 | 
						|
 | 
						|
    case MemoryRegion::RAMMirror1:
 | 
						|
      return (g_unprotected_ram + (RAM_2MB_SIZE & g_ram_mask));
 | 
						|
 | 
						|
    case MemoryRegion::RAMMirror2:
 | 
						|
      return (g_unprotected_ram + ((RAM_2MB_SIZE * 2) & g_ram_mask));
 | 
						|
 | 
						|
    case MemoryRegion::RAMMirror3:
 | 
						|
      return (g_unprotected_ram + ((RAM_8MB_SIZE * 3) & g_ram_mask));
 | 
						|
 | 
						|
    case MemoryRegion::EXP1:
 | 
						|
      return nullptr;
 | 
						|
 | 
						|
    case MemoryRegion::Scratchpad:
 | 
						|
      return CPU::g_state.dcache.data();
 | 
						|
 | 
						|
    case MemoryRegion::BIOS:
 | 
						|
      return g_bios;
 | 
						|
 | 
						|
    default:
 | 
						|
      return nullptr;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static ALWAYS_INLINE_RELEASE bool MaskedMemoryCompare(const u8* pattern, const u8* mask, u32 pattern_length,
 | 
						|
                                                      const u8* mem)
 | 
						|
{
 | 
						|
  if (!mask)
 | 
						|
    return std::memcmp(mem, pattern, pattern_length) == 0;
 | 
						|
 | 
						|
  for (u32 i = 0; i < pattern_length; i++)
 | 
						|
  {
 | 
						|
    if ((mem[i] & mask[i]) != (pattern[i] & mask[i]))
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
std::optional<PhysicalMemoryAddress> Bus::SearchMemory(PhysicalMemoryAddress start_address, const u8* pattern,
 | 
						|
                                                       const u8* mask, u32 pattern_length)
 | 
						|
{
 | 
						|
  std::optional<MemoryRegion> region = GetMemoryRegionForAddress(start_address);
 | 
						|
  if (!region.has_value())
 | 
						|
    return std::nullopt;
 | 
						|
 | 
						|
  PhysicalMemoryAddress current_address = start_address;
 | 
						|
  MemoryRegion current_region = region.value();
 | 
						|
  while (current_region != MemoryRegion::Count)
 | 
						|
  {
 | 
						|
    const u8* mem = GetMemoryRegionPointer(current_region);
 | 
						|
    const PhysicalMemoryAddress region_start = GetMemoryRegionStart(current_region);
 | 
						|
    const PhysicalMemoryAddress region_end = GetMemoryRegionEnd(current_region);
 | 
						|
 | 
						|
    if (mem)
 | 
						|
    {
 | 
						|
      PhysicalMemoryAddress region_offset = current_address - region_start;
 | 
						|
      PhysicalMemoryAddress bytes_remaining = region_end - current_address;
 | 
						|
      while (bytes_remaining >= pattern_length)
 | 
						|
      {
 | 
						|
        if (MaskedMemoryCompare(pattern, mask, pattern_length, mem + region_offset))
 | 
						|
          return region_start + region_offset;
 | 
						|
 | 
						|
        region_offset++;
 | 
						|
        bytes_remaining--;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // skip RAM mirrors
 | 
						|
    if (current_region == MemoryRegion::RAM)
 | 
						|
      current_region = MemoryRegion::EXP1;
 | 
						|
    else
 | 
						|
      current_region = static_cast<MemoryRegion>(static_cast<int>(current_region) + 1);
 | 
						|
 | 
						|
    if (current_region != MemoryRegion::Count)
 | 
						|
      current_address = GetMemoryRegionStart(current_region);
 | 
						|
  }
 | 
						|
 | 
						|
  return std::nullopt;
 | 
						|
}
 | 
						|
 | 
						|
#define BUS_CYCLES(n) CPU::g_state.pending_ticks += n
 | 
						|
 | 
						|
// TODO: Move handlers to own files for better inlining.
 | 
						|
namespace Bus {
 | 
						|
static void ClearHandlers(void** handlers);
 | 
						|
static void SetHandlerForRegion(void** handlers, VirtualMemoryAddress address, u32 size,
 | 
						|
                                MemoryReadHandler read_byte_handler, MemoryReadHandler read_halfword_handler,
 | 
						|
                                MemoryReadHandler read_word_handler, MemoryWriteHandler write_byte_handler,
 | 
						|
                                MemoryWriteHandler write_halfword_handler, MemoryWriteHandler write_word_handler);
 | 
						|
 | 
						|
// clang-format off
 | 
						|
template<MemoryAccessSize size> static u32 UnknownReadHandler(VirtualMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void UnknownWriteHandler(VirtualMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static void IgnoreWriteHandler(VirtualMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static u32 UnmappedReadHandler(VirtualMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void UnmappedWriteHandler(VirtualMemoryAddress address, u32 value);
 | 
						|
 | 
						|
template<MemoryAccessSize size> static u32 RAMReadHandler(VirtualMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void RAMWriteHandler(VirtualMemoryAddress address, u32 value);
 | 
						|
 | 
						|
template<MemoryAccessSize size> static u32 BIOSReadHandler(VirtualMemoryAddress address);
 | 
						|
 | 
						|
template<MemoryAccessSize size> static u32 ScratchpadReadHandler(VirtualMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void ScratchpadWriteHandler(VirtualMemoryAddress address, u32 value);
 | 
						|
 | 
						|
template<MemoryAccessSize size> static u32 CacheControlReadHandler(VirtualMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void CacheControlWriteHandler(VirtualMemoryAddress address, u32 value);
 | 
						|
 | 
						|
template<MemoryAccessSize size> static u32 ICacheReadHandler(VirtualMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void ICacheWriteHandler(VirtualMemoryAddress address, u32 value);
 | 
						|
 | 
						|
template<MemoryAccessSize size> static u32 EXP1ReadHandler(VirtualMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void EXP1WriteHandler(VirtualMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static u32 EXP2ReadHandler(VirtualMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void EXP2WriteHandler(VirtualMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static u32 EXP3ReadHandler(VirtualMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void EXP3WriteHandler(VirtualMemoryAddress address, u32 value);
 | 
						|
 | 
						|
template<MemoryAccessSize size> static u32 HardwareReadHandler(VirtualMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void HardwareWriteHandler(VirtualMemoryAddress address, u32 value);
 | 
						|
 | 
						|
// clang-format on
 | 
						|
} // namespace Bus
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::UnknownReadHandler(VirtualMemoryAddress address)
 | 
						|
{
 | 
						|
  static constexpr const char* sizes[3] = {"byte", "halfword", "word"};
 | 
						|
  Log_ErrorFmt("Invalid {} read at address 0x{:08X}, pc 0x{:08X}", sizes[static_cast<u32>(size)], address,
 | 
						|
               CPU::g_state.pc);
 | 
						|
  return 0xFFFFFFFFu;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::UnknownWriteHandler(VirtualMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  static constexpr const char* sizes[3] = {"byte", "halfword", "word"};
 | 
						|
  Log_ErrorFmt("Invalid {} write at address 0x{:08X}, value 0x{:08X}, pc 0x{:08X}", sizes[static_cast<u32>(size)],
 | 
						|
               address, value, CPU::g_state.pc);
 | 
						|
  CPU::g_state.bus_error = true;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::IgnoreWriteHandler(VirtualMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  // noop
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::UnmappedReadHandler(VirtualMemoryAddress address)
 | 
						|
{
 | 
						|
  CPU::g_state.bus_error = true;
 | 
						|
  return UnknownReadHandler<size>(address);
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::UnmappedWriteHandler(VirtualMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  CPU::g_state.bus_error = true;
 | 
						|
  UnknownWriteHandler<size>(address, value);
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::RAMReadHandler(VirtualMemoryAddress address)
 | 
						|
{
 | 
						|
  BUS_CYCLES(RAM_READ_TICKS);
 | 
						|
 | 
						|
  const u32 offset = address & g_ram_mask;
 | 
						|
  if constexpr (size == MemoryAccessSize::Byte)
 | 
						|
  {
 | 
						|
    return ZeroExtend32(g_ram[offset]);
 | 
						|
  }
 | 
						|
  else if constexpr (size == MemoryAccessSize::HalfWord)
 | 
						|
  {
 | 
						|
    u16 temp;
 | 
						|
    std::memcpy(&temp, &g_ram[offset], sizeof(u16));
 | 
						|
    return ZeroExtend32(temp);
 | 
						|
  }
 | 
						|
  else if constexpr (size == MemoryAccessSize::Word)
 | 
						|
  {
 | 
						|
    u32 value;
 | 
						|
    std::memcpy(&value, &g_ram[offset], sizeof(u32));
 | 
						|
    return value;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::RAMWriteHandler(VirtualMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & g_ram_mask;
 | 
						|
 | 
						|
  if constexpr (size == MemoryAccessSize::Byte)
 | 
						|
  {
 | 
						|
    g_ram[offset] = Truncate8(value);
 | 
						|
  }
 | 
						|
  else if constexpr (size == MemoryAccessSize::HalfWord)
 | 
						|
  {
 | 
						|
    const u16 temp = Truncate16(value);
 | 
						|
    std::memcpy(&g_ram[offset], &temp, sizeof(u16));
 | 
						|
  }
 | 
						|
  else if constexpr (size == MemoryAccessSize::Word)
 | 
						|
  {
 | 
						|
    std::memcpy(&g_ram[offset], &value, sizeof(u32));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::BIOSReadHandler(VirtualMemoryAddress address)
 | 
						|
{
 | 
						|
  BUS_CYCLES(g_bios_access_time[static_cast<u32>(size)]);
 | 
						|
 | 
						|
  // TODO: Configurable mirroring.
 | 
						|
  const u32 offset = address & UINT32_C(0x7FFFF);
 | 
						|
  if constexpr (size == MemoryAccessSize::Byte)
 | 
						|
  {
 | 
						|
    return ZeroExtend32(g_bios[offset]);
 | 
						|
  }
 | 
						|
  else if constexpr (size == MemoryAccessSize::HalfWord)
 | 
						|
  {
 | 
						|
    u16 temp;
 | 
						|
    std::memcpy(&temp, &g_bios[offset], sizeof(u16));
 | 
						|
    return ZeroExtend32(temp);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    u32 value;
 | 
						|
    std::memcpy(&value, &g_bios[offset], sizeof(u32));
 | 
						|
    return value;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::ScratchpadReadHandler(VirtualMemoryAddress address)
 | 
						|
{
 | 
						|
  const PhysicalMemoryAddress cache_offset = address - CPU::DCACHE_LOCATION;
 | 
						|
  if (cache_offset >= CPU::DCACHE_SIZE) [[unlikely]]
 | 
						|
    return UnknownReadHandler<size>(address);
 | 
						|
 | 
						|
  if constexpr (size == MemoryAccessSize::Byte)
 | 
						|
  {
 | 
						|
    return ZeroExtend32(CPU::g_state.dcache[cache_offset]);
 | 
						|
  }
 | 
						|
  else if constexpr (size == MemoryAccessSize::HalfWord)
 | 
						|
  {
 | 
						|
    u16 temp;
 | 
						|
    std::memcpy(&temp, &CPU::g_state.dcache[cache_offset], sizeof(temp));
 | 
						|
    return ZeroExtend32(temp);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    u32 value;
 | 
						|
    std::memcpy(&value, &CPU::g_state.dcache[cache_offset], sizeof(value));
 | 
						|
    return value;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::ScratchpadWriteHandler(VirtualMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const PhysicalMemoryAddress cache_offset = address - CPU::DCACHE_LOCATION;
 | 
						|
  if (cache_offset >= CPU::DCACHE_SIZE) [[unlikely]]
 | 
						|
  {
 | 
						|
    UnknownWriteHandler<size>(address, value);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if constexpr (size == MemoryAccessSize::Byte)
 | 
						|
    CPU::g_state.dcache[cache_offset] = Truncate8(value);
 | 
						|
  else if constexpr (size == MemoryAccessSize::HalfWord)
 | 
						|
    std::memcpy(&CPU::g_state.dcache[cache_offset], &value, sizeof(u16));
 | 
						|
  else if constexpr (size == MemoryAccessSize::Word)
 | 
						|
    std::memcpy(&CPU::g_state.dcache[cache_offset], &value, sizeof(u32));
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::CacheControlReadHandler(VirtualMemoryAddress address)
 | 
						|
{
 | 
						|
  if (address != 0xFFFE0130)
 | 
						|
    return UnknownReadHandler<size>(address);
 | 
						|
 | 
						|
  return CPU::g_state.cache_control.bits;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::CacheControlWriteHandler(VirtualMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  if (address != 0xFFFE0130)
 | 
						|
    return UnknownWriteHandler<size>(address, value);
 | 
						|
 | 
						|
  Log_DevFmt("Cache control <- 0x{:08X}", value);
 | 
						|
  CPU::g_state.cache_control.bits = value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::ICacheReadHandler(VirtualMemoryAddress address)
 | 
						|
{
 | 
						|
  const u32 line = CPU::GetICacheLine(address);
 | 
						|
  const u8* line_data = &CPU::g_state.icache_data[line * CPU::ICACHE_LINE_SIZE];
 | 
						|
  const u32 offset = CPU::GetICacheLineOffset(address);
 | 
						|
  u32 result;
 | 
						|
  std::memcpy(&result, &line_data[offset], sizeof(result));
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::ICacheWriteHandler(VirtualMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 line = CPU::GetICacheLine(address);
 | 
						|
  const u32 offset = CPU::GetICacheLineOffset(address);
 | 
						|
  CPU::g_state.icache_tags[line] = CPU::GetICacheTagForAddress(address) | CPU::ICACHE_INVALID_BITS;
 | 
						|
  if constexpr (size == MemoryAccessSize::Byte)
 | 
						|
    std::memcpy(&CPU::g_state.icache_data[line * CPU::ICACHE_LINE_SIZE + offset], &value, sizeof(u8));
 | 
						|
  else if constexpr (size == MemoryAccessSize::HalfWord)
 | 
						|
    std::memcpy(&CPU::g_state.icache_data[line * CPU::ICACHE_LINE_SIZE + offset], &value, sizeof(u16));
 | 
						|
  else
 | 
						|
    std::memcpy(&CPU::g_state.icache_data[line * CPU::ICACHE_LINE_SIZE + offset], &value, sizeof(u32));
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::EXP1ReadHandler(VirtualMemoryAddress address)
 | 
						|
{
 | 
						|
  BUS_CYCLES(g_exp1_access_time[static_cast<u32>(size)]);
 | 
						|
 | 
						|
  const u32 offset = address & EXP1_MASK;
 | 
						|
  u32 value;
 | 
						|
  if (s_exp1_rom.empty())
 | 
						|
  {
 | 
						|
    // EXP1 not present.
 | 
						|
    value = UINT32_C(0xFFFFFFFF);
 | 
						|
  }
 | 
						|
  else if (offset == 0x20018)
 | 
						|
  {
 | 
						|
    // Bit 0 - Action Replay On/Off
 | 
						|
    value = UINT32_C(1);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    const u32 transfer_size = u32(1) << static_cast<u32>(size);
 | 
						|
    if ((offset + transfer_size) > s_exp1_rom.size())
 | 
						|
    {
 | 
						|
      value = UINT32_C(0);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if constexpr (size == MemoryAccessSize::Byte)
 | 
						|
      {
 | 
						|
        value = ZeroExtend32(s_exp1_rom[offset]);
 | 
						|
      }
 | 
						|
      else if constexpr (size == MemoryAccessSize::HalfWord)
 | 
						|
      {
 | 
						|
        u16 halfword;
 | 
						|
        std::memcpy(&halfword, &s_exp1_rom[offset], sizeof(halfword));
 | 
						|
        value = ZeroExtend32(halfword);
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        std::memcpy(&value, &s_exp1_rom[offset], sizeof(value));
 | 
						|
      }
 | 
						|
 | 
						|
      // Log_DevPrintf("EXP1 read: 0x%08X -> 0x%08X", address, value);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::EXP1WriteHandler(VirtualMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  Log_WarningFmt("EXP1 write: 0x{:08X} <- 0x{:08X}", address, value);
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::EXP2ReadHandler(VirtualMemoryAddress address)
 | 
						|
{
 | 
						|
  BUS_CYCLES(g_exp2_access_time[static_cast<u32>(size)]);
 | 
						|
 | 
						|
  const u32 offset = address & EXP2_MASK;
 | 
						|
  u32 value;
 | 
						|
 | 
						|
  // rx/tx buffer empty
 | 
						|
  if (offset == 0x21)
 | 
						|
  {
 | 
						|
    value = 0x04 | 0x08;
 | 
						|
  }
 | 
						|
  else if (offset >= 0x60 && offset <= 0x67)
 | 
						|
  {
 | 
						|
    // nocash expansion area
 | 
						|
    value = UINT32_C(0xFFFFFFFF);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    Log_WarningFmt("EXP2 read: 0x{:08X}", address);
 | 
						|
    value = UINT32_C(0xFFFFFFFF);
 | 
						|
  }
 | 
						|
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::EXP2WriteHandler(VirtualMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & EXP2_MASK;
 | 
						|
  if (offset == 0x23 || offset == 0x80)
 | 
						|
  {
 | 
						|
    AddTTYCharacter(static_cast<char>(value));
 | 
						|
  }
 | 
						|
  else if (offset == 0x41 || offset == 0x42)
 | 
						|
  {
 | 
						|
    Log_DevFmt("BIOS POST status: {:02X}", value & UINT32_C(0x0F));
 | 
						|
  }
 | 
						|
  else if (offset == 0x70)
 | 
						|
  {
 | 
						|
    Log_DevFmt("BIOS POST2 status: {:02X}", value & UINT32_C(0x0F));
 | 
						|
  }
 | 
						|
#if 0
 | 
						|
  // TODO: Put behind configuration variable
 | 
						|
  else if (offset == 0x81)
 | 
						|
  {
 | 
						|
    Log_WarningPrint("pcsx_debugbreak()");
 | 
						|
    Host::ReportErrorAsync("Error", "pcsx_debugbreak()");
 | 
						|
    System::PauseSystem(true);
 | 
						|
    CPU::ExitExecution();
 | 
						|
  }
 | 
						|
  else if (offset == 0x82)
 | 
						|
  {
 | 
						|
    Log_WarningFmt("pcsx_exit() with status 0x{:02X}", value & UINT32_C(0xFF));
 | 
						|
    Host::ReportErrorAsync("Error", fmt::format("pcsx_exit() with status 0x{:02X}", value & UINT32_C(0xFF)));
 | 
						|
    System::ShutdownSystem(false);
 | 
						|
    CPU::ExitExecution();
 | 
						|
  }
 | 
						|
#endif
 | 
						|
  else
 | 
						|
  {
 | 
						|
    Log_WarningFmt("EXP2 write: 0x{:08X} <- 0x{:08X}", address, value);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::EXP3ReadHandler(VirtualMemoryAddress address)
 | 
						|
{
 | 
						|
  Log_WarningFmt("EXP3 read: 0x{:08X}", address);
 | 
						|
  return UINT32_C(0xFFFFFFFF);
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::EXP3WriteHandler(VirtualMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & EXP3_MASK;
 | 
						|
  if (offset == 0)
 | 
						|
    Log_WarningFmt("BIOS POST3 status: {:02X}", value & UINT32_C(0x0F));
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
						|
// HARDWARE HANDLERS
 | 
						|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
namespace Bus::HWHandlers {
 | 
						|
// clang-format off
 | 
						|
template<MemoryAccessSize size> static u32 MemCtrlRead(PhysicalMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void MemCtrlWrite(PhysicalMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static u32 PADRead(PhysicalMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void PADWrite(PhysicalMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static u32 SIORead(PhysicalMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void SIOWrite(PhysicalMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static u32 MemCtrl2Read(PhysicalMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void MemCtrl2Write(PhysicalMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static u32 INTCRead(PhysicalMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void INTCWrite(PhysicalMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static u32 DMARead(PhysicalMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void DMAWrite(PhysicalMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static u32 TimersRead(PhysicalMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void TimersWrite(PhysicalMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static u32 CDROMRead(PhysicalMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void CDROMWrite(PhysicalMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static u32 GPURead(PhysicalMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void GPUWrite(PhysicalMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static u32 MDECRead(PhysicalMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void MDECWrite(PhysicalMemoryAddress address, u32 value);
 | 
						|
template<MemoryAccessSize size> static u32 SPURead(PhysicalMemoryAddress address);
 | 
						|
template<MemoryAccessSize size> static void SPUWrite(PhysicalMemoryAddress address, u32 value);
 | 
						|
// clang-format on
 | 
						|
} // namespace Bus::HWHandlers
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::HWHandlers::MemCtrlRead(PhysicalMemoryAddress address)
 | 
						|
{
 | 
						|
  const u32 offset = address & MEMCTRL_MASK;
 | 
						|
 | 
						|
  u32 value = s_MEMCTRL.regs[FIXUP_WORD_OFFSET(size, offset) / 4];
 | 
						|
  value = FIXUP_WORD_READ_VALUE(size, offset, value);
 | 
						|
  BUS_CYCLES(2);
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::HWHandlers::MemCtrlWrite(PhysicalMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & MEMCTRL_MASK;
 | 
						|
  const u32 index = FIXUP_WORD_OFFSET(size, offset) / 4;
 | 
						|
  value = FIXUP_WORD_WRITE_VALUE(size, offset, value);
 | 
						|
 | 
						|
  const u32 write_mask = (index == 8) ? COMDELAY::WRITE_MASK : MEMDELAY::WRITE_MASK;
 | 
						|
  const u32 new_value = (s_MEMCTRL.regs[index] & ~write_mask) | (value & write_mask);
 | 
						|
  if (s_MEMCTRL.regs[index] != new_value)
 | 
						|
  {
 | 
						|
    s_MEMCTRL.regs[index] = new_value;
 | 
						|
    RecalculateMemoryTimings();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::HWHandlers::MemCtrl2Read(PhysicalMemoryAddress address)
 | 
						|
{
 | 
						|
  const u32 offset = address & MEMCTRL2_MASK;
 | 
						|
 | 
						|
  u32 value;
 | 
						|
  if (offset == 0x00)
 | 
						|
  {
 | 
						|
    value = s_ram_size_reg;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    return UnknownReadHandler<size>(address);
 | 
						|
  }
 | 
						|
 | 
						|
  BUS_CYCLES(2);
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::HWHandlers::MemCtrl2Write(PhysicalMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & MEMCTRL2_MASK;
 | 
						|
 | 
						|
  if (offset == 0x00)
 | 
						|
  {
 | 
						|
    s_ram_size_reg = value;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    return UnknownWriteHandler<size>(address, value);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::HWHandlers::PADRead(PhysicalMemoryAddress address)
 | 
						|
{
 | 
						|
  const u32 offset = address & PAD_MASK;
 | 
						|
 | 
						|
  u32 value = Pad::ReadRegister(FIXUP_HALFWORD_OFFSET(size, offset));
 | 
						|
  value = FIXUP_HALFWORD_READ_VALUE(size, offset, value);
 | 
						|
  BUS_CYCLES(2);
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::HWHandlers::PADWrite(PhysicalMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & PAD_MASK;
 | 
						|
  Pad::WriteRegister(FIXUP_HALFWORD_OFFSET(size, offset), FIXUP_HALFWORD_WRITE_VALUE(size, offset, value));
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::HWHandlers::SIORead(PhysicalMemoryAddress address)
 | 
						|
{
 | 
						|
  const u32 offset = address & SIO_MASK;
 | 
						|
  u32 value = SIO::ReadRegister(FIXUP_HALFWORD_OFFSET(size, offset));
 | 
						|
  value = FIXUP_HALFWORD_READ_VALUE(size, offset, value);
 | 
						|
  BUS_CYCLES(2);
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::HWHandlers::SIOWrite(PhysicalMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & SIO_MASK;
 | 
						|
  SIO::WriteRegister(FIXUP_HALFWORD_OFFSET(size, offset), FIXUP_HALFWORD_WRITE_VALUE(size, offset, value));
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::HWHandlers::CDROMRead(PhysicalMemoryAddress address)
 | 
						|
{
 | 
						|
  const u32 offset = address & CDROM_MASK;
 | 
						|
 | 
						|
  u32 value;
 | 
						|
  switch (size)
 | 
						|
  {
 | 
						|
    case MemoryAccessSize::Word:
 | 
						|
    {
 | 
						|
      const u32 b0 = ZeroExtend32(CDROM::ReadRegister(offset));
 | 
						|
      const u32 b1 = ZeroExtend32(CDROM::ReadRegister(offset + 1u));
 | 
						|
      const u32 b2 = ZeroExtend32(CDROM::ReadRegister(offset + 2u));
 | 
						|
      const u32 b3 = ZeroExtend32(CDROM::ReadRegister(offset + 3u));
 | 
						|
      value = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
 | 
						|
    }
 | 
						|
 | 
						|
    case MemoryAccessSize::HalfWord:
 | 
						|
    {
 | 
						|
      const u32 lsb = ZeroExtend32(CDROM::ReadRegister(offset));
 | 
						|
      const u32 msb = ZeroExtend32(CDROM::ReadRegister(offset + 1u));
 | 
						|
      value = lsb | (msb << 8);
 | 
						|
    }
 | 
						|
 | 
						|
    case MemoryAccessSize::Byte:
 | 
						|
    default:
 | 
						|
      value = ZeroExtend32(CDROM::ReadRegister(offset));
 | 
						|
  }
 | 
						|
 | 
						|
  BUS_CYCLES(Bus::g_cdrom_access_time[static_cast<u32>(size)]);
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::HWHandlers::CDROMWrite(PhysicalMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & CDROM_MASK;
 | 
						|
  switch (size)
 | 
						|
  {
 | 
						|
    case MemoryAccessSize::Word:
 | 
						|
    {
 | 
						|
      CDROM::WriteRegister(offset, Truncate8(value & 0xFFu));
 | 
						|
      CDROM::WriteRegister(offset + 1u, Truncate8((value >> 8) & 0xFFu));
 | 
						|
      CDROM::WriteRegister(offset + 2u, Truncate8((value >> 16) & 0xFFu));
 | 
						|
      CDROM::WriteRegister(offset + 3u, Truncate8((value >> 24) & 0xFFu));
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
    case MemoryAccessSize::HalfWord:
 | 
						|
    {
 | 
						|
      CDROM::WriteRegister(offset, Truncate8(value & 0xFFu));
 | 
						|
      CDROM::WriteRegister(offset + 1u, Truncate8((value >> 8) & 0xFFu));
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
    case MemoryAccessSize::Byte:
 | 
						|
    default:
 | 
						|
      CDROM::WriteRegister(offset, Truncate8(value));
 | 
						|
      break;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::HWHandlers::GPURead(PhysicalMemoryAddress address)
 | 
						|
{
 | 
						|
  const u32 offset = address & GPU_MASK;
 | 
						|
  u32 value = g_gpu->ReadRegister(FIXUP_WORD_OFFSET(size, offset));
 | 
						|
  value = FIXUP_WORD_READ_VALUE(size, offset, value);
 | 
						|
  BUS_CYCLES(2);
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::HWHandlers::GPUWrite(PhysicalMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & GPU_MASK;
 | 
						|
  g_gpu->WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::HWHandlers::MDECRead(PhysicalMemoryAddress address)
 | 
						|
{
 | 
						|
  const u32 offset = address & MDEC_MASK;
 | 
						|
  u32 value = MDEC::ReadRegister(FIXUP_WORD_OFFSET(size, offset));
 | 
						|
  value = FIXUP_WORD_READ_VALUE(size, offset, value);
 | 
						|
  BUS_CYCLES(2);
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::HWHandlers::MDECWrite(PhysicalMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & MDEC_MASK;
 | 
						|
  MDEC::WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::HWHandlers::INTCRead(PhysicalMemoryAddress address)
 | 
						|
{
 | 
						|
  const u32 offset = address & INTERRUPT_CONTROLLER_MASK;
 | 
						|
  u32 value = InterruptController::ReadRegister(FIXUP_WORD_OFFSET(size, offset));
 | 
						|
  value = FIXUP_WORD_READ_VALUE(size, offset, value);
 | 
						|
  BUS_CYCLES(2);
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::HWHandlers::INTCWrite(PhysicalMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & INTERRUPT_CONTROLLER_MASK;
 | 
						|
  InterruptController::WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::HWHandlers::TimersRead(PhysicalMemoryAddress address)
 | 
						|
{
 | 
						|
  const u32 offset = address & TIMERS_MASK;
 | 
						|
  u32 value = Timers::ReadRegister(FIXUP_WORD_OFFSET(size, offset));
 | 
						|
  value = FIXUP_WORD_READ_VALUE(size, offset, value);
 | 
						|
  BUS_CYCLES(2);
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::HWHandlers::TimersWrite(PhysicalMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & TIMERS_MASK;
 | 
						|
  Timers::WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::HWHandlers::SPURead(PhysicalMemoryAddress address)
 | 
						|
{
 | 
						|
  const u32 offset = address & SPU_MASK;
 | 
						|
  u32 value;
 | 
						|
 | 
						|
  switch (size)
 | 
						|
  {
 | 
						|
    case MemoryAccessSize::Word:
 | 
						|
    {
 | 
						|
      // 32-bit reads are read as two 16-bit accesses.
 | 
						|
      const u16 lsb = SPU::ReadRegister(offset);
 | 
						|
      const u16 msb = SPU::ReadRegister(offset + 2);
 | 
						|
      value = ZeroExtend32(lsb) | (ZeroExtend32(msb) << 16);
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
    case MemoryAccessSize::HalfWord:
 | 
						|
    {
 | 
						|
      value = ZeroExtend32(SPU::ReadRegister(offset));
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
    case MemoryAccessSize::Byte:
 | 
						|
    default:
 | 
						|
    {
 | 
						|
      const u16 value16 = SPU::ReadRegister(FIXUP_HALFWORD_OFFSET(size, offset));
 | 
						|
      value = FIXUP_HALFWORD_READ_VALUE(size, offset, value16);
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  BUS_CYCLES(Bus::g_spu_access_time[static_cast<u32>(size)]);
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::HWHandlers::SPUWrite(PhysicalMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & SPU_MASK;
 | 
						|
 | 
						|
  // 32-bit writes are written as two 16-bit writes.
 | 
						|
  // TODO: Ignore if address is not aligned.
 | 
						|
  switch (size)
 | 
						|
  {
 | 
						|
    case MemoryAccessSize::Word:
 | 
						|
    {
 | 
						|
      DebugAssert(Common::IsAlignedPow2(offset, 2));
 | 
						|
      SPU::WriteRegister(offset, Truncate16(value));
 | 
						|
      SPU::WriteRegister(offset + 2, Truncate16(value >> 16));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    case MemoryAccessSize::HalfWord:
 | 
						|
    {
 | 
						|
      DebugAssert(Common::IsAlignedPow2(offset, 2));
 | 
						|
      SPU::WriteRegister(offset, Truncate16(value));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    case MemoryAccessSize::Byte:
 | 
						|
    {
 | 
						|
      SPU::WriteRegister(FIXUP_HALFWORD_OFFSET(size, offset),
 | 
						|
                         Truncate16(FIXUP_HALFWORD_READ_VALUE(size, offset, value)));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::HWHandlers::DMARead(PhysicalMemoryAddress address)
 | 
						|
{
 | 
						|
  const u32 offset = address & DMA_MASK;
 | 
						|
  u32 value = DMA::ReadRegister(FIXUP_WORD_OFFSET(size, offset));
 | 
						|
  value = FIXUP_WORD_READ_VALUE(size, offset, value);
 | 
						|
  BUS_CYCLES(2);
 | 
						|
  return value;
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::HWHandlers::DMAWrite(PhysicalMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  const u32 offset = address & DMA_MASK;
 | 
						|
  DMA::WriteRegister(FIXUP_WORD_OFFSET(size, offset), FIXUP_WORD_WRITE_VALUE(size, offset, value));
 | 
						|
}
 | 
						|
 | 
						|
#undef BUS_CYCLES
 | 
						|
 | 
						|
namespace Bus::HWHandlers {
 | 
						|
// We index hardware registers by bits 15..8.
 | 
						|
template<MemoryAccessType type, MemoryAccessSize size,
 | 
						|
         typename RT = std::conditional_t<type == MemoryAccessType::Read, MemoryReadHandler, MemoryWriteHandler>>
 | 
						|
static constexpr std::array<RT, 256> GetHardwareRegisterHandlerTable()
 | 
						|
{
 | 
						|
  std::array<RT, 256> ret = {};
 | 
						|
  for (size_t i = 0; i < ret.size(); i++)
 | 
						|
  {
 | 
						|
    if constexpr (type == MemoryAccessType::Read)
 | 
						|
      ret[i] = UnmappedReadHandler<size>;
 | 
						|
    else
 | 
						|
      ret[i] = UnmappedWriteHandler<size>;
 | 
						|
  }
 | 
						|
 | 
						|
#if 0
 | 
						|
  // Verifies no region has >1 handler, but doesn't compile on older GCC.
 | 
						|
#define SET(raddr, rsize, read_handler, write_handler)                                                                 \
 | 
						|
  static_assert(raddr >= 0x1F801000 && (raddr + rsize) <= 0x1F802000);                                                 \
 | 
						|
  for (u32 taddr = raddr; taddr < (raddr + rsize); taddr += 16)                                                        \
 | 
						|
  {                                                                                                                    \
 | 
						|
    const u32 i = (taddr >> 4) & 0xFFu;                                                                                \
 | 
						|
    if constexpr (type == MemoryAccessType::Read)                                                                      \
 | 
						|
      ret[i] = (ret[i] == UnmappedReadHandler<size>) ? read_handler<size> : (abort(), read_handler<size>);             \
 | 
						|
    else                                                                                                               \
 | 
						|
      ret[i] = (ret[i] == UnmappedWriteHandler<size>) ? write_handler<size> : (abort(), write_handler<size>);          \
 | 
						|
  }
 | 
						|
#else
 | 
						|
#define SET(raddr, rsize, read_handler, write_handler)                                                                 \
 | 
						|
  static_assert(raddr >= 0x1F801000 && (raddr + rsize) <= 0x1F802000);                                                 \
 | 
						|
  for (u32 taddr = raddr; taddr < (raddr + rsize); taddr += 16)                                                        \
 | 
						|
  {                                                                                                                    \
 | 
						|
    const u32 i = (taddr >> 4) & 0xFFu;                                                                                \
 | 
						|
    if constexpr (type == MemoryAccessType::Read)                                                                      \
 | 
						|
      ret[i] = read_handler<size>;                                                                                     \
 | 
						|
    else                                                                                                               \
 | 
						|
      ret[i] = write_handler<size>;                                                                                    \
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  SET(MEMCTRL_BASE, MEMCTRL_SIZE, MemCtrlRead, MemCtrlWrite);
 | 
						|
  SET(PAD_BASE, PAD_SIZE, PADRead, PADWrite);
 | 
						|
  SET(SIO_BASE, SIO_SIZE, SIORead, SIOWrite);
 | 
						|
  SET(MEMCTRL2_BASE, MEMCTRL2_SIZE, MemCtrl2Read, MemCtrl2Write);
 | 
						|
  SET(INTC_BASE, INTC_SIZE, INTCRead, INTCWrite);
 | 
						|
  SET(DMA_BASE, DMA_SIZE, DMARead, DMAWrite);
 | 
						|
  SET(TIMERS_BASE, TIMERS_SIZE, TimersRead, TimersWrite);
 | 
						|
  SET(CDROM_BASE, CDROM_SIZE, CDROMRead, CDROMWrite);
 | 
						|
  SET(GPU_BASE, GPU_SIZE, GPURead, GPUWrite);
 | 
						|
  SET(MDEC_BASE, MDEC_SIZE, MDECRead, MDECWrite);
 | 
						|
  SET(SPU_BASE, SPU_SIZE, SPURead, SPUWrite);
 | 
						|
 | 
						|
#undef SET
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
} // namespace Bus::HWHandlers
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
u32 Bus::HardwareReadHandler(VirtualMemoryAddress address)
 | 
						|
{
 | 
						|
  static constexpr const auto table = HWHandlers::GetHardwareRegisterHandlerTable<MemoryAccessType::Read, size>();
 | 
						|
  const u32 table_index = (address >> 4) & 0xFFu;
 | 
						|
  return table[table_index](address);
 | 
						|
}
 | 
						|
 | 
						|
template<MemoryAccessSize size>
 | 
						|
void Bus::HardwareWriteHandler(VirtualMemoryAddress address, u32 value)
 | 
						|
{
 | 
						|
  static constexpr const auto table = HWHandlers::GetHardwareRegisterHandlerTable<MemoryAccessType::Write, size>();
 | 
						|
  const u32 table_index = (address >> 4) & 0xFFu;
 | 
						|
  return table[table_index](address, value);
 | 
						|
}
 | 
						|
 | 
						|
//////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
void Bus::SetHandlers()
 | 
						|
{
 | 
						|
  ClearHandlers(g_memory_handlers);
 | 
						|
  ClearHandlers(g_memory_handlers_isc);
 | 
						|
 | 
						|
#define SET(table, start, size, read_handler, write_handler)                                                           \
 | 
						|
  SetHandlerForRegion(table, start, size, read_handler<MemoryAccessSize::Byte>,                                        \
 | 
						|
                      read_handler<MemoryAccessSize::HalfWord>, read_handler<MemoryAccessSize::Word>,                  \
 | 
						|
                      write_handler<MemoryAccessSize::Byte>, write_handler<MemoryAccessSize::HalfWord>,                \
 | 
						|
                      write_handler<MemoryAccessSize::Word>)
 | 
						|
 | 
						|
  static constexpr u32 KUSEG = 0;
 | 
						|
  static constexpr u32 KSEG0 = 0x80000000U;
 | 
						|
  static constexpr u32 KSEG1 = 0xA0000000U;
 | 
						|
  static constexpr u32 KSEG2 = 0xC0000000U;
 | 
						|
 | 
						|
  // KUSEG - Cached
 | 
						|
  SET(g_memory_handlers, KUSEG | RAM_BASE, RAM_MIRROR_SIZE, RAMReadHandler, RAMWriteHandler);
 | 
						|
  SET(g_memory_handlers, KUSEG | CPU::DCACHE_LOCATION, 0x1000, ScratchpadReadHandler, ScratchpadWriteHandler);
 | 
						|
  SET(g_memory_handlers, KUSEG | BIOS_BASE, BIOS_SIZE, BIOSReadHandler, IgnoreWriteHandler);
 | 
						|
  SET(g_memory_handlers, KUSEG | EXP1_BASE, EXP1_SIZE, EXP1ReadHandler, EXP1WriteHandler);
 | 
						|
  SET(g_memory_handlers, KUSEG | HW_BASE, HW_SIZE, HardwareReadHandler, HardwareWriteHandler);
 | 
						|
  SET(g_memory_handlers, KUSEG | EXP2_BASE, EXP2_SIZE, EXP2ReadHandler, EXP2WriteHandler);
 | 
						|
  SET(g_memory_handlers, KUSEG | EXP3_BASE, EXP3_SIZE, EXP3ReadHandler, EXP3WriteHandler);
 | 
						|
 | 
						|
  // KSEG0 - Cached
 | 
						|
  SET(g_memory_handlers, KSEG0 | RAM_BASE, RAM_MIRROR_SIZE, RAMReadHandler, RAMWriteHandler);
 | 
						|
  SET(g_memory_handlers, KSEG0 | CPU::DCACHE_LOCATION, 0x1000, ScratchpadReadHandler, ScratchpadWriteHandler);
 | 
						|
  SET(g_memory_handlers, KSEG0 | BIOS_BASE, BIOS_SIZE, BIOSReadHandler, IgnoreWriteHandler);
 | 
						|
  SET(g_memory_handlers, KSEG0 | EXP1_BASE, EXP1_SIZE, EXP1ReadHandler, EXP1WriteHandler);
 | 
						|
  SET(g_memory_handlers, KSEG0 | HW_BASE, HW_SIZE, HardwareReadHandler, HardwareWriteHandler);
 | 
						|
  SET(g_memory_handlers, KSEG0 | EXP2_BASE, EXP2_SIZE, EXP2ReadHandler, EXP2WriteHandler);
 | 
						|
  SET(g_memory_handlers, KSEG0 | EXP3_BASE, EXP3_SIZE, EXP3ReadHandler, EXP3WriteHandler);
 | 
						|
 | 
						|
  // KSEG1 - Uncached
 | 
						|
  SET(g_memory_handlers, KSEG1 | RAM_BASE, RAM_MIRROR_SIZE, RAMReadHandler, RAMWriteHandler);
 | 
						|
  SET(g_memory_handlers, KSEG1 | BIOS_BASE, BIOS_SIZE, BIOSReadHandler, IgnoreWriteHandler);
 | 
						|
  SET(g_memory_handlers, KSEG1 | EXP1_BASE, EXP1_SIZE, EXP1ReadHandler, EXP1WriteHandler);
 | 
						|
  SET(g_memory_handlers, KSEG1 | HW_BASE, HW_SIZE, HardwareReadHandler, HardwareWriteHandler);
 | 
						|
  SET(g_memory_handlers, KSEG1 | EXP2_BASE, EXP2_SIZE, EXP2ReadHandler, EXP2WriteHandler);
 | 
						|
  SET(g_memory_handlers, KSEG1 | EXP3_BASE, EXP3_SIZE, EXP3ReadHandler, EXP3WriteHandler);
 | 
						|
 | 
						|
  // KSEG2 - Uncached - 0xFFFE0130
 | 
						|
  SET(g_memory_handlers, KSEG2 | 0xFFFE0000, 0x1000, CacheControlReadHandler, CacheControlWriteHandler);
 | 
						|
 | 
						|
  // When cache isolated, only allow writes to cache? Or should we still allow KSEG1?
 | 
						|
  SET(g_memory_handlers_isc, KUSEG | RAM_BASE, RAM_MIRROR_SIZE, RAMReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KUSEG | CPU::DCACHE_LOCATION, 0x1000, ScratchpadReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KUSEG | BIOS_BASE, BIOS_SIZE, BIOSReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KUSEG | EXP1_BASE, EXP1_SIZE, EXP1ReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KUSEG | HW_BASE, HW_SIZE, HardwareReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KUSEG | EXP2_BASE, EXP2_SIZE, EXP2ReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KUSEG | EXP3_BASE, EXP3_SIZE, EXP3ReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG0 | RAM_BASE, RAM_MIRROR_SIZE, RAMReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG0 | CPU::DCACHE_LOCATION, 0x1000, ScratchpadReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG0 | BIOS_BASE, BIOS_SIZE, BIOSReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG0 | EXP1_BASE, EXP1_SIZE, EXP1ReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG0 | HW_BASE, HW_SIZE, HardwareReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG0 | EXP2_BASE, EXP2_SIZE, EXP2ReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG0 | EXP3_BASE, EXP3_SIZE, EXP3ReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG1 | RAM_BASE, RAM_MIRROR_SIZE, RAMReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG1 | BIOS_BASE, BIOS_SIZE, BIOSReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG1 | EXP1_BASE, EXP1_SIZE, EXP1ReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG1 | HW_BASE, HW_SIZE, HardwareReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG1 | EXP2_BASE, EXP2_SIZE, EXP2ReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG1 | EXP3_BASE, EXP3_SIZE, EXP3ReadHandler, ICacheWriteHandler);
 | 
						|
  SET(g_memory_handlers_isc, KSEG2 | 0xFFFE0000, 0x1000, CacheControlReadHandler, CacheControlWriteHandler);
 | 
						|
 | 
						|
#undef SET
 | 
						|
}
 | 
						|
 | 
						|
void Bus::ClearHandlers(void** handlers)
 | 
						|
{
 | 
						|
  for (u32 size = 0; size < 3; size++)
 | 
						|
  {
 | 
						|
    MemoryReadHandler* read_handlers =
 | 
						|
      OffsetHandlerArray<MemoryReadHandler>(handlers, static_cast<MemoryAccessSize>(size), MemoryAccessType::Read);
 | 
						|
    const MemoryReadHandler read_handler =
 | 
						|
      (size == 0) ?
 | 
						|
        UnmappedReadHandler<MemoryAccessSize::Byte> :
 | 
						|
        ((size == 1) ? UnmappedReadHandler<MemoryAccessSize::HalfWord> : UnmappedReadHandler<MemoryAccessSize::Word>);
 | 
						|
    MemsetPtrs(read_handlers, read_handler, MEMORY_LUT_SIZE);
 | 
						|
 | 
						|
    MemoryWriteHandler* write_handlers =
 | 
						|
      OffsetHandlerArray<MemoryWriteHandler>(handlers, static_cast<MemoryAccessSize>(size), MemoryAccessType::Write);
 | 
						|
    const MemoryWriteHandler write_handler =
 | 
						|
      (size == 0) ?
 | 
						|
        UnmappedWriteHandler<MemoryAccessSize::Byte> :
 | 
						|
        ((size == 1) ? UnmappedWriteHandler<MemoryAccessSize::HalfWord> : UnmappedWriteHandler<MemoryAccessSize::Word>);
 | 
						|
    MemsetPtrs(write_handlers, write_handler, MEMORY_LUT_SIZE);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void Bus::SetHandlerForRegion(void** handlers, VirtualMemoryAddress address, u32 size,
 | 
						|
                              MemoryReadHandler read_byte_handler, MemoryReadHandler read_halfword_handler,
 | 
						|
                              MemoryReadHandler read_word_handler, MemoryWriteHandler write_byte_handler,
 | 
						|
                              MemoryWriteHandler write_halfword_handler, MemoryWriteHandler write_word_handler)
 | 
						|
{
 | 
						|
  // Should be 4K aligned.
 | 
						|
  DebugAssert(Common::IsAlignedPow2(size, MEMORY_LUT_PAGE_SIZE));
 | 
						|
 | 
						|
  const u32 start_page = (address / MEMORY_LUT_PAGE_SIZE);
 | 
						|
  const u32 num_pages = ((size + (MEMORY_LUT_PAGE_SIZE - 1)) / MEMORY_LUT_PAGE_SIZE);
 | 
						|
 | 
						|
  for (u32 acc_size = 0; acc_size < 3; acc_size++)
 | 
						|
  {
 | 
						|
    MemoryReadHandler* read_handlers =
 | 
						|
      OffsetHandlerArray<MemoryReadHandler>(handlers, static_cast<MemoryAccessSize>(acc_size), MemoryAccessType::Read) +
 | 
						|
      start_page;
 | 
						|
    const MemoryReadHandler read_handler =
 | 
						|
      (acc_size == 0) ? read_byte_handler : ((acc_size == 1) ? read_halfword_handler : read_word_handler);
 | 
						|
#if 0
 | 
						|
    for (u32 i = 0; i < num_pages; i++)
 | 
						|
    {
 | 
						|
      DebugAssert((acc_size == 0 && read_handlers[i] == UnmappedReadHandler<MemoryAccessSize::Byte>) ||
 | 
						|
                  (acc_size == 1 && read_handlers[i] == UnmappedReadHandler<MemoryAccessSize::HalfWord>) ||
 | 
						|
                  (acc_size == 2 && read_handlers[i] == UnmappedReadHandler<MemoryAccessSize::Word>));
 | 
						|
 | 
						|
      read_handlers[i] = read_handler;
 | 
						|
    }
 | 
						|
#else
 | 
						|
    MemsetPtrs(read_handlers, read_handler, num_pages);
 | 
						|
#endif
 | 
						|
 | 
						|
    MemoryWriteHandler* write_handlers = OffsetHandlerArray<MemoryWriteHandler>(
 | 
						|
                                           handlers, static_cast<MemoryAccessSize>(acc_size), MemoryAccessType::Write) +
 | 
						|
                                         start_page;
 | 
						|
    const MemoryWriteHandler write_handler =
 | 
						|
      (acc_size == 0) ? write_byte_handler : ((acc_size == 1) ? write_halfword_handler : write_word_handler);
 | 
						|
#if 0
 | 
						|
    for (u32 i = 0; i < num_pages; i++)
 | 
						|
    {
 | 
						|
      DebugAssert((acc_size == 0 && write_handlers[i] == UnmappedWriteHandler<MemoryAccessSize::Byte>) ||
 | 
						|
                  (acc_size == 1 && write_handlers[i] == UnmappedWriteHandler<MemoryAccessSize::HalfWord>) ||
 | 
						|
                  (acc_size == 2 && write_handlers[i] == UnmappedWriteHandler<MemoryAccessSize::Word>));
 | 
						|
 | 
						|
      write_handlers[i] = write_handler;
 | 
						|
    }
 | 
						|
#else
 | 
						|
    MemsetPtrs(write_handlers, write_handler, num_pages);
 | 
						|
#endif
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void** Bus::GetMemoryHandlers(bool isolate_cache, bool swap_caches)
 | 
						|
{
 | 
						|
  if (!isolate_cache)
 | 
						|
    return g_memory_handlers;
 | 
						|
 | 
						|
#ifdef _DEBUG
 | 
						|
  if (swap_caches)
 | 
						|
    Log_WarningPrint("Cache isolated and swapped, icache will be written instead of scratchpad?");
 | 
						|
#endif
 | 
						|
 | 
						|
  return g_memory_handlers_isc;
 | 
						|
}
 |