dolphin/Source/Core/Core/HW/MMIOHandlers.h

222 lines
9.7 KiB
C++

// Copyright 2014 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <functional>
#include <memory>
#include "Common/CommonTypes.h"
// All the templated and very repetitive MMIO-related code is isolated in this
// file for easier reading. It mostly contains code related to handling methods
// (including the declaration of the public functions for creating handling
// method objects), visitors for these handling methods, and interface of the
// handler classes.
//
// This code is very genericized (aka. lots of templates) in order to handle
// u8/u16/u32 with the same code while providing type safety: it is impossible
// to mix code from these types, and the type system enforces it.
namespace Core
{
class System;
}
namespace MMIO
{
class Mapping;
// Read and write handling methods are separated for type safety. On top of
// that, some handling methods require different arguments for reads and writes
// (Complex, for example).
template <typename T>
class ReadHandlingMethod;
template <typename T>
class WriteHandlingMethod;
// Constant: use when the value read on this MMIO is always the same. This is
// only for reads.
template <typename T>
ReadHandlingMethod<T>* Constant(T value);
// Nop: use for writes that shouldn't have any effect and shouldn't log an
// error either.
template <typename T>
WriteHandlingMethod<T>* Nop();
// Direct: use when all the MMIO does is read/write the given value to/from a
// global variable, with an optional mask applied on the read/written value.
template <typename T>
ReadHandlingMethod<T>* DirectRead(const T* addr, u32 mask = 0xFFFFFFFF);
template <typename T>
WriteHandlingMethod<T>* DirectWrite(T* addr, u32 mask = 0xFFFFFFFF);
// Complex: use when no other handling method fits your needs. These allow you
// to directly provide a function that will be called when a read/write needs
// to be done.
template <typename T>
ReadHandlingMethod<T>* ComplexRead(std::function<T(Core::System&, u32)>);
template <typename T>
WriteHandlingMethod<T>* ComplexWrite(std::function<void(Core::System&, u32, T)>);
// Invalid: log an error and return -1 in case of a read. These are the default
// handlers set for all MMIO types.
template <typename T>
ReadHandlingMethod<T>* InvalidRead();
template <typename T>
WriteHandlingMethod<T>* InvalidWrite();
// {Read,Write}To{Smaller,Larger}: these functions are not themselves handling
// methods but will try to combine accesses to two handlers into one new
// handler object.
//
// This is used for example when 32 bit reads have the exact same handling as
// 16 bit. Handlers need to be registered for both 32 and 16, and it would be
// repetitive and unoptimal to require users to write the same handling code in
// both cases. Instead, an MMIO module can simply define all handlers in terms
// of 16 bit reads, then use ReadToSmaller<u32> to convert u32 reads to u16
// reads.
//
// Internally, these size conversion functions have some magic to make the
// combined handlers as fast as possible. For example, if the two underlying
// u16 handlers for a u32 reads are Direct to consecutive memory addresses,
// they can be transformed into a Direct u32 access.
//
// Warning: unlike the other handling methods, *ToSmaller are obviously not
// available for u8, and *ToLarger are not available for u32.
template <typename T>
ReadHandlingMethod<T>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr);
template <typename T>
WriteHandlingMethod<T>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr);
template <typename T>
ReadHandlingMethod<T>* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift);
// Use these visitors interfaces if you need to write code that performs
// different actions based on the handling method used by a handler. Write your
// visitor implementing that interface, then use handler->VisitHandlingMethod
// to run the proper function.
template <typename T>
class ReadHandlingMethodVisitor
{
public:
virtual void VisitConstant(T value) = 0;
virtual void VisitDirect(const T* addr, u32 mask) = 0;
virtual void VisitComplex(const std::function<T(Core::System&, u32)>* lambda) = 0;
};
template <typename T>
class WriteHandlingMethodVisitor
{
public:
virtual void VisitNop() = 0;
virtual void VisitDirect(T* addr, u32 mask) = 0;
virtual void VisitComplex(const std::function<void(Core::System&, u32, T)>* lambda) = 0;
};
// These classes are INTERNAL. Do not use outside of the MMIO implementation
// code. Unfortunately, because we want to make Read() and Write() fast and
// inlinable, we need to provide some of the implementation of these two
// classes here and can't just use a forward declaration.
template <typename T>
class ReadHandler
{
public:
ReadHandler();
// Takes ownership of "method".
ReadHandler(ReadHandlingMethod<T>* method);
~ReadHandler();
// Entry point for read handling method visitors.
void Visit(ReadHandlingMethodVisitor<T>& visitor);
T Read(Core::System& system, u32 addr);
// Internal method called when changing the internal method object. Its
// main role is to make sure the read function is updated at the same time.
void ResetMethod(ReadHandlingMethod<T>* method);
private:
// Initialize this handler to an invalid handler. Done lazily to avoid
// useless initialization of thousands of unused handler objects.
void InitializeInvalid();
std::unique_ptr<ReadHandlingMethod<T>> m_Method;
std::function<T(Core::System&, u32)> m_ReadFunc;
};
template <typename T>
class WriteHandler
{
public:
WriteHandler();
// Takes ownership of "method".
WriteHandler(WriteHandlingMethod<T>* method);
~WriteHandler();
// Entry point for write handling method visitors.
void Visit(WriteHandlingMethodVisitor<T>& visitor);
void Write(Core::System& system, u32 addr, T val);
// Internal method called when changing the internal method object. Its
// main role is to make sure the write function is updated at the same
// time.
void ResetMethod(WriteHandlingMethod<T>* method);
private:
// Initialize this handler to an invalid handler. Done lazily to avoid
// useless initialization of thousands of unused handler objects.
void InitializeInvalid();
std::unique_ptr<WriteHandlingMethod<T>> m_Method;
std::function<void(Core::System&, u32, T)> m_WriteFunc;
};
// Boilerplate boilerplate boilerplate.
//
// This is used to be able to avoid putting the templates implementation in the
// header files and slow down compilation times. Instead, we declare 3
// specializations in the header file as already implemented in another
// compilation unit: u8, u16, u32.
//
// The "MaybeExtern" is there because that same macro is used for declaration
// (where MaybeExtern = "extern") and definition (MaybeExtern = "").
#define MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, T) \
MaybeExtern template ReadHandlingMethod<T>* Constant<T>(T value); \
MaybeExtern template WriteHandlingMethod<T>* Nop<T>(); \
MaybeExtern template ReadHandlingMethod<T>* DirectRead(const T* addr, u32 mask); \
MaybeExtern template WriteHandlingMethod<T>* DirectWrite(T* addr, u32 mask); \
MaybeExtern template ReadHandlingMethod<T>* ComplexRead<T>( \
std::function<T(Core::System&, u32)>); \
MaybeExtern template WriteHandlingMethod<T>* ComplexWrite<T>( \
std::function<void(Core::System&, u32, T)>); \
MaybeExtern template ReadHandlingMethod<T>* InvalidRead<T>(); \
MaybeExtern template WriteHandlingMethod<T>* InvalidWrite<T>(); \
MaybeExtern template class ReadHandler<T>; \
MaybeExtern template class WriteHandler<T>
#define MMIO_SPECIAL_PUBLIC_SPECIALIZATIONS(MaybeExtern) \
MaybeExtern template ReadHandlingMethod<u16>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, \
u32 low_part_addr); \
MaybeExtern template ReadHandlingMethod<u32>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, \
u32 low_part_addr); \
MaybeExtern template WriteHandlingMethod<u16>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, \
u32 low_part_addr); \
MaybeExtern template WriteHandlingMethod<u32>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, \
u32 low_part_addr); \
MaybeExtern template ReadHandlingMethod<u8>* ReadToLarger(Mapping* mmio, u32 larger_addr, \
u32 shift); \
MaybeExtern template ReadHandlingMethod<u16>* ReadToLarger(Mapping* mmio, u32 larger_addr, \
u32 shift)
#define MMIO_PUBLIC_SPECIALIZATIONS() \
MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u8); \
MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u16); \
MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u32); \
MMIO_SPECIAL_PUBLIC_SPECIALIZATIONS(MaybeExtern);
#define MaybeExtern extern
MMIO_PUBLIC_SPECIALIZATIONS()
#undef MaybeExtern
} // namespace MMIO