mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-03-01 07:15:32 +01:00

The STL has everything we need nowadays. I have tried to not alter any behavior or semantics with this change wherever possible. In particular, WriteLow and WriteHigh in CommandProcessor retain the ability to accidentally undo another thread's write to the upper half or lower half respectively. If that should be fixed, it should be done in a separate commit for clarity. One thing did change: The places where we were using += on a volatile variable (not an atomic operation) are now using fetch_add (actually an atomic operation). Tested with single core and dual core on x86-64 and AArch64.
228 lines
6.8 KiB
C++
228 lines
6.8 KiB
C++
// Copyright 2014 Dolphin Emulator Project
|
|
// Licensed under GPLv2+
|
|
// Refer to the license.txt file included.
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
|
|
#include "Common/Assert.h"
|
|
#include "Common/BitUtils.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(std::atomic<u32>* ptr)
|
|
{
|
|
static_assert(std::atomic<u32>::is_always_lock_free && sizeof(std::atomic<u32>) == sizeof(u32));
|
|
return LowPart(Common::BitCast<u32*>(ptr));
|
|
}
|
|
inline u16* HighPart(u32* ptr)
|
|
{
|
|
return LowPart(ptr) + 1;
|
|
}
|
|
inline u16* HighPart(std::atomic<u32>* ptr)
|
|
{
|
|
static_assert(std::atomic<u32>::is_always_lock_free && sizeof(std::atomic<u32>) == sizeof(u32));
|
|
return HighPart(Common::BitCast<u32*>(ptr));
|
|
}
|
|
} // namespace Utils
|
|
|
|
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);
|
|
}
|
|
} // namespace MMIO
|