2018-04-12 21:28:39 +02:00

224 lines
6.5 KiB
C++

// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <string>
#include <tuple>
#include <type_traits>
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Core/ConfigManager.h"
#include "Core/HW/MMIOHandlers.h"
namespace MMIO
{
// There are three main MMIO blocks on the Wii (only one on the GameCube):
// - 0x0C00xxxx: GameCube MMIOs (CP, PE, VI, PI, MI, DSP, DVD, SI, EI, AI, GP)
// - 0x0D00xxxx: Wii MMIOs and GC mirrors (IPC, DVD, SI, EI, AI)
// - 0x0D80xxxx: Mirror of 0x0D00xxxx.
//
// In practice, since the third block is a mirror of the second one, we can
// assume internally that there are only two blocks: one for GC, one for Wii.
enum Block
{
GC_BLOCK = 0,
WII_BLOCK = 1,
NUM_BLOCKS
};
const u32 BLOCK_SIZE = 0x10000;
const u32 NUM_MMIOS = NUM_BLOCKS * BLOCK_SIZE;
// Checks if a given physical memory address refers to the MMIO address range.
// In practice, most games use a virtual memory mapping (via BATs set in the
// IPL) that matches the physical memory mapping for MMIOs.
//
// We have a special exception here for FIFO writes: these are handled via a
// different mechanism and should not go through the normal MMIO access
// interface.
inline bool IsMMIOAddress(u32 address)
{
if (address == 0x0C008000)
return false; // WG Pipe
if ((address & 0xFFFF0000) == 0x0C000000)
return true; // GameCube MMIOs
if (SConfig::GetInstance().bWii)
{
return ((address & 0xFFFF0000) == 0x0D000000) || // Wii MMIOs
((address & 0xFFFF0000) == 0x0D800000); // Mirror of Wii MMIOs
}
return false;
}
// Compute the internal unique ID for a given MMIO address. This ID is computed
// from a very simple formula: (block_id << 16) | lower_16_bits(address).
//
// The block ID can easily be computed by simply checking bit 24 (CC vs. CD).
inline u32 UniqueID(u32 address)
{
DEBUG_ASSERT_MSG(MEMMAP,
((address & 0xFFFF0000) == 0x0C000000) ||
((address & 0xFFFF0000) == 0x0D000000) ||
((address & 0xFFFF0000) == 0x0D800000),
"Trying to get the ID of a non-existing MMIO address.");
return (((address >> 24) & 1) << 16) | (address & 0xFFFF);
}
// Some utilities functions to define MMIO mappings.
namespace Utils
{
// Allow grabbing pointers to the high and low part of a 32 bits pointer.
inline u16* LowPart(u32* ptr)
{
return (u16*)ptr;
}
inline u16* LowPart(volatile u32* ptr)
{
return (u16*)ptr;
}
inline u16* HighPart(u32* ptr)
{
return LowPart(ptr) + 1;
}
inline u16* HighPart(volatile u32* ptr)
{
return LowPart(ptr) + 1;
}
}
class Mapping
{
public:
// MMIO registration interface. Use this to register new MMIO handlers.
//
// Example usages can be found in just about any HW/ module in Dolphin's
// codebase.
template <typename Unit>
void RegisterRead(u32 addr, ReadHandlingMethod<Unit>* read)
{
GetHandlerForRead<Unit>(addr).ResetMethod(read);
}
template <typename Unit>
void RegisterWrite(u32 addr, WriteHandlingMethod<Unit>* write)
{
GetHandlerForWrite<Unit>(addr).ResetMethod(write);
}
template <typename Unit>
void Register(u32 addr, ReadHandlingMethod<Unit>* read, WriteHandlingMethod<Unit>* write)
{
RegisterRead(addr, read);
RegisterWrite(addr, write);
}
// Direct read/write interface.
//
// These functions allow reading/writing an MMIO register at a given
// address. They are used by the Memory:: access functions, which are
// called in interpreter mode, from Dolphin's own code, or from JIT'd code
// where the access address could not be predicted.
template <typename Unit>
Unit Read(u32 addr)
{
return GetHandlerForRead<Unit>(addr).Read(addr);
}
template <typename Unit>
void Write(u32 addr, Unit val)
{
GetHandlerForWrite<Unit>(addr).Write(addr, val);
}
// Handlers access interface.
//
// Use when you care more about how to access the MMIO register for an
// address than the current value of that register. For example, this is
// what could be used to implement fast MMIO accesses in Dolphin's JIT.
template <typename Unit>
ReadHandler<Unit>& GetHandlerForRead(u32 addr)
{
return GetReadHandler<Unit>(UniqueID(addr) / sizeof(Unit));
}
template <typename Unit>
WriteHandler<Unit>& GetHandlerForWrite(u32 addr)
{
return GetWriteHandler<Unit>(UniqueID(addr) / sizeof(Unit));
}
private:
// These arrays contain the handlers for each MMIO access type: read/write
// to 8/16/32 bits. They are indexed using the UniqueID(addr) function
// defined earlier, which maps an MMIO address to a unique ID by using the
// MMIO block ID.
//
// Each array contains NUM_MMIOS / sizeof (AccessType) because larger
// access types mean less possible adresses (assuming aligned only
// accesses).
template <typename Unit>
struct HandlerArray
{
using Read = std::array<ReadHandler<Unit>, NUM_MMIOS / sizeof(Unit)>;
using Write = std::array<WriteHandler<Unit>, NUM_MMIOS / sizeof(Unit)>;
};
HandlerArray<u8>::Read m_read_handlers8;
HandlerArray<u16>::Read m_read_handlers16;
HandlerArray<u32>::Read m_read_handlers32;
HandlerArray<u8>::Write m_write_handlers8;
HandlerArray<u16>::Write m_write_handlers16;
HandlerArray<u32>::Write m_write_handlers32;
// Getter functions for the handler arrays.
template <typename Unit>
ReadHandler<Unit>& GetReadHandler(size_t index)
{
static_assert(std::is_same<Unit, u8>() || std::is_same<Unit, u16>() ||
std::is_same<Unit, u32>(),
"Invalid unit used");
auto handlers = std::tie(m_read_handlers8, m_read_handlers16, m_read_handlers32);
using ArrayType = typename HandlerArray<Unit>::Read;
return std::get<ArrayType&>(handlers)[index];
}
template <typename Unit>
WriteHandler<Unit>& GetWriteHandler(size_t index)
{
static_assert(std::is_same<Unit, u8>() || std::is_same<Unit, u16>() ||
std::is_same<Unit, u32>(),
"Invalid unit used");
auto handlers = std::tie(m_write_handlers8, m_write_handlers16, m_write_handlers32);
using ArrayType = typename HandlerArray<Unit>::Write;
return std::get<ArrayType&>(handlers)[index];
}
};
// Dummy 64 bits variants of these functions. While 64 bits MMIO access is
// not supported, we need these in order to make the code compile.
template <>
inline u64 Mapping::Read<u64>(u32 addr)
{
DEBUG_ASSERT(0);
return 0;
}
template <>
inline void Mapping::Write(u32 addr, u64 val)
{
DEBUG_ASSERT(0);
}
}