| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,383 @@ | ||
| // Copyright 2013 Dolphin Emulator Project | ||
| // Licensed under GPLv2 | ||
| // Refer to the license.txt file included. | ||
|
|
||
| #include "MMIO.h" | ||
| #include "MMIOHandlers.h" | ||
|
|
||
| #include <functional> | ||
|
|
||
| namespace MMIO | ||
| { | ||
|
|
||
| // Base classes for the two handling method hierarchies. Note that a single | ||
| // class can inherit from both. | ||
| // | ||
| // At the moment the only common element between all the handling method is | ||
| // that they should be able to accept a visitor of the appropriate type. | ||
| template <typename T> | ||
| class ReadHandlingMethod | ||
| { | ||
| public: | ||
| virtual ~ReadHandlingMethod() {} | ||
| virtual void AcceptReadVisitor(ReadHandlingMethodVisitor<T>& v) const = 0; | ||
| }; | ||
| template <typename T> | ||
| class WriteHandlingMethod | ||
| { | ||
| public: | ||
| virtual ~WriteHandlingMethod() {} | ||
| virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor<T>& v) const = 0; | ||
| }; | ||
|
|
||
| // Constant: handling method holds a single integer and passes it to the | ||
| // visitor. This is a read only handling method: storing to a constant does not | ||
| // mean anything. | ||
| template <typename T> | ||
| class ConstantHandlingMethod : public ReadHandlingMethod<T> | ||
| { | ||
| public: | ||
| explicit ConstantHandlingMethod(T value) : value_(value) | ||
| { | ||
| } | ||
|
|
||
| virtual ~ConstantHandlingMethod() {} | ||
|
|
||
| virtual void AcceptReadVisitor(ReadHandlingMethodVisitor<T>& v) const | ||
| { | ||
| v.VisitConstant(value_); | ||
| } | ||
|
|
||
| private: | ||
| T value_; | ||
| }; | ||
| template <typename T> | ||
| ReadHandlingMethod<T>* Constant(T value) | ||
| { | ||
| return new ConstantHandlingMethod<T>(value); | ||
| } | ||
|
|
||
| // Nop: extremely simple write handling method that does nothing at all, only | ||
| // respond to visitors and dispatch to the correct method. This is write only | ||
| // since reads should always at least return a value. | ||
| template <typename T> | ||
| class NopHandlingMethod : public WriteHandlingMethod<T> | ||
| { | ||
| public: | ||
| NopHandlingMethod() {} | ||
| virtual ~NopHandlingMethod() {} | ||
| virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor<T>& v) const | ||
| { | ||
| v.VisitNop(); | ||
| } | ||
| }; | ||
| template <typename T> | ||
| WriteHandlingMethod<T>* Nop() | ||
| { | ||
| return new NopHandlingMethod<T>(); | ||
| } | ||
|
|
||
| // Direct: handling method holds a pointer to the value where to read/write the | ||
| // data from, as well as a mask that is used to restrict reading/writing only | ||
| // to a given set of bits. | ||
| template <typename T> | ||
| class DirectHandlingMethod : public ReadHandlingMethod<T>, | ||
| public WriteHandlingMethod<T> | ||
| { | ||
| public: | ||
| DirectHandlingMethod(T* addr, u32 mask) : addr_(addr), mask_(mask) | ||
| { | ||
| } | ||
|
|
||
| virtual ~DirectHandlingMethod() {} | ||
|
|
||
| virtual void AcceptReadVisitor(ReadHandlingMethodVisitor<T>& v) const | ||
| { | ||
| v.VisitDirect(addr_, mask_); | ||
| } | ||
|
|
||
| virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor<T>& v) const | ||
| { | ||
| v.VisitDirect(addr_, mask_); | ||
| } | ||
|
|
||
| private: | ||
| T* addr_; | ||
| u32 mask_; | ||
| }; | ||
| template <typename T> | ||
| ReadHandlingMethod<T>* DirectRead(const T* addr, u32 mask) | ||
| { | ||
| return new DirectHandlingMethod<T>(const_cast<T*>(addr), mask); | ||
| } | ||
| template <typename T> | ||
| ReadHandlingMethod<T>* DirectRead(volatile const T* addr, u32 mask) | ||
| { | ||
| return new DirectHandlingMethod<T>((T*)addr, mask); | ||
| } | ||
| template <typename T> | ||
| WriteHandlingMethod<T>* DirectWrite(T* addr, u32 mask) | ||
| { | ||
| return new DirectHandlingMethod<T>(addr, mask); | ||
| } | ||
| template <typename T> | ||
| WriteHandlingMethod<T>* DirectWrite(volatile T* addr, u32 mask) | ||
| { | ||
| return new DirectHandlingMethod<T>((T*)addr, mask); | ||
| } | ||
|
|
||
| // Complex: holds a lambda that is called when a read or a write is executed. | ||
| // This gives complete control to the user as to what is going to happen during | ||
| // that read or write, but reduces the optimization potential. | ||
| template <typename T> | ||
| class ComplexHandlingMethod : public ReadHandlingMethod<T>, | ||
| public WriteHandlingMethod<T> | ||
| { | ||
| public: | ||
| explicit ComplexHandlingMethod(std::function<T(u32)> read_lambda) | ||
| : read_lambda_(read_lambda), write_lambda_(InvalidWriteLambda()) | ||
| { | ||
| } | ||
|
|
||
| explicit ComplexHandlingMethod(std::function<void(u32, T)> write_lambda) | ||
| : read_lambda_(InvalidReadLambda()), write_lambda_(write_lambda) | ||
| { | ||
| } | ||
|
|
||
| virtual ~ComplexHandlingMethod() {} | ||
|
|
||
| virtual void AcceptReadVisitor(ReadHandlingMethodVisitor<T>& v) const | ||
| { | ||
| v.VisitComplex(read_lambda_); | ||
| } | ||
|
|
||
| virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor<T>& v) const | ||
| { | ||
| v.VisitComplex(write_lambda_); | ||
| } | ||
|
|
||
| private: | ||
| std::function<T(u32)> InvalidReadLambda() const | ||
| { | ||
| return [](u32) { | ||
| _dbg_assert_msg_(MEMMAP, 0, "Called the read lambda on a write " | ||
| "complex handler."); | ||
| return 0; | ||
| }; | ||
| } | ||
|
|
||
| std::function<void(u32, T)> InvalidWriteLambda() const | ||
| { | ||
| return [](u32, T) { | ||
| _dbg_assert_msg_(MEMMAP, 0, "Called the write lambda on a read " | ||
| "complex handler."); | ||
| }; | ||
| } | ||
|
|
||
| std::function<T(u32)> read_lambda_; | ||
| std::function<void(u32, T)> write_lambda_; | ||
| }; | ||
| template <typename T> | ||
| ReadHandlingMethod<T>* ComplexRead(std::function<T(u32)> lambda) | ||
| { | ||
| return new ComplexHandlingMethod<T>(lambda); | ||
| } | ||
| template <typename T> | ||
| WriteHandlingMethod<T>* ComplexWrite(std::function<void(u32, T)> lambda) | ||
| { | ||
| return new ComplexHandlingMethod<T>(lambda); | ||
| } | ||
|
|
||
| // Invalid: specialization of the complex handling type with lambdas that | ||
| // display error messages. | ||
| template <typename T> | ||
| ReadHandlingMethod<T>* InvalidRead() | ||
| { | ||
| return ComplexRead<T>([](u32 addr) { | ||
| ERROR_LOG(MEMMAP, "Trying to read from an invalid MMIO (addr=%08x)", | ||
| addr); | ||
| return -1; | ||
| }); | ||
| } | ||
| template <typename T> | ||
| WriteHandlingMethod<T>* InvalidWrite() | ||
| { | ||
| return ComplexWrite<T>([](u32 addr, T val) { | ||
| ERROR_LOG(MEMMAP, "Trying to write to an invalid MMIO (addr=%08x, val=%08x)", | ||
| addr, (u32)val); | ||
| }); | ||
| } | ||
|
|
||
| // Converters to larger and smaller size. Probably the most complex of these | ||
| // handlers to implement. They do not define new handling method types but | ||
| // instead will internally use the types defined above. | ||
| template <typename T> struct SmallerAccessSize {}; | ||
| template <> struct SmallerAccessSize<u16> { typedef u8 value; }; | ||
| template <> struct SmallerAccessSize<u32> { typedef u16 value; }; | ||
|
|
||
| template <typename T> struct LargerAccessSize {}; | ||
| template <> struct LargerAccessSize<u8> { typedef u16 value; }; | ||
| template <> struct LargerAccessSize<u16> { typedef u32 value; }; | ||
|
|
||
| template <typename T> | ||
| ReadHandlingMethod<T>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr) | ||
| { | ||
| typedef typename SmallerAccessSize<T>::value ST; | ||
|
|
||
| const ReadHandler<ST>* high_part; | ||
| const ReadHandler<ST>* low_part; | ||
| mmio->GetHandlerForRead(high_part_addr, &high_part); | ||
| mmio->GetHandlerForRead(low_part_addr, &low_part); | ||
|
|
||
| // TODO(delroth): optimize | ||
| return ComplexRead<T>([high_part, low_part](u32 addr) { | ||
| return ((T)high_part->Read(addr) << (8 * sizeof (ST))) | low_part->Read(addr); | ||
| }); | ||
| } | ||
|
|
||
| template <typename T> | ||
| WriteHandlingMethod<T>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr) | ||
| { | ||
| typedef typename SmallerAccessSize<T>::value ST; | ||
|
|
||
| const WriteHandler<ST>* high_part; | ||
| const WriteHandler<ST>* low_part; | ||
| mmio->GetHandlerForWrite(high_part_addr, &high_part); | ||
| mmio->GetHandlerForWrite(low_part_addr, &low_part); | ||
|
|
||
| // TODO(delroth): optimize | ||
| return ComplexWrite<T>([high_part, low_part](u32 addr, T val) { | ||
| high_part->Write(addr, val >> (8 * sizeof (ST))); | ||
| low_part->Write(addr, (ST)val); | ||
| }); | ||
| } | ||
|
|
||
| template <typename T> | ||
| ReadHandlingMethod<T>* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift) | ||
| { | ||
| typedef typename LargerAccessSize<T>::value LT; | ||
|
|
||
| const ReadHandler<LT>* large; | ||
| mmio->GetHandlerForRead(larger_addr, &large); | ||
|
|
||
| // TODO(delroth): optimize | ||
| return ComplexRead<T>([large, shift](u32 addr) { | ||
| return large->Read(addr & ~(sizeof (LT) - 1)) >> shift; | ||
| }); | ||
| } | ||
|
|
||
| // Inplementation of the ReadHandler and WriteHandler class. There is a lot of | ||
| // redundant code between these two classes but trying to abstract it away | ||
| // brings more trouble than it fixes. | ||
| template <typename T> | ||
| ReadHandler<T>::ReadHandler() : m_Method(nullptr) | ||
| { | ||
| ResetMethod(InvalidRead<T>()); | ||
| } | ||
|
|
||
| template <typename T> | ||
| ReadHandler<T>::ReadHandler(ReadHandlingMethod<T>* method) | ||
| : m_Method(nullptr) | ||
| { | ||
| ResetMethod(method); | ||
| } | ||
|
|
||
| template <typename T> | ||
| ReadHandler<T>::~ReadHandler() | ||
| { | ||
| } | ||
|
|
||
| template <typename T> | ||
| void ReadHandler<T>::Visit(ReadHandlingMethodVisitor<T>& visitor) const | ||
| { | ||
| m_Method->AcceptReadVisitor(visitor); | ||
| } | ||
|
|
||
| template <typename T> | ||
| void ReadHandler<T>::ResetMethod(ReadHandlingMethod<T>* method) | ||
| { | ||
| m_Method.reset(method); | ||
|
|
||
| struct FuncCreatorVisitor : public ReadHandlingMethodVisitor<T> | ||
| { | ||
| std::function<T(u32)> ret; | ||
|
|
||
| virtual void VisitConstant(T value) | ||
| { | ||
| ret = [value](u32) { return value; }; | ||
| } | ||
|
|
||
| virtual void VisitDirect(const T* addr, u32 mask) | ||
| { | ||
| ret = [addr, mask](u32) { return *addr & mask; }; | ||
| } | ||
|
|
||
| virtual void VisitComplex(std::function<T(u32)> lambda) | ||
| { | ||
| ret = lambda; | ||
| } | ||
| }; | ||
|
|
||
| FuncCreatorVisitor v; | ||
| Visit(v); | ||
| m_ReadFunc = v.ret; | ||
| } | ||
|
|
||
| template <typename T> | ||
| WriteHandler<T>::WriteHandler() : m_Method(nullptr) | ||
| { | ||
| ResetMethod(InvalidWrite<T>()); | ||
| } | ||
|
|
||
| template <typename T> | ||
| WriteHandler<T>::WriteHandler(WriteHandlingMethod<T>* method) | ||
| : m_Method(nullptr) | ||
| { | ||
| ResetMethod(method); | ||
| } | ||
|
|
||
| template <typename T> | ||
| WriteHandler<T>::~WriteHandler() | ||
| { | ||
| } | ||
|
|
||
| template <typename T> | ||
| void WriteHandler<T>::Visit(WriteHandlingMethodVisitor<T>& visitor) const | ||
| { | ||
| m_Method->AcceptWriteVisitor(visitor); | ||
| } | ||
|
|
||
| template <typename T> | ||
| void WriteHandler<T>::ResetMethod(WriteHandlingMethod<T>* method) | ||
| { | ||
| m_Method.reset(method); | ||
|
|
||
| struct FuncCreatorVisitor : public WriteHandlingMethodVisitor<T> | ||
| { | ||
| std::function<void(u32, T)> ret; | ||
|
|
||
| virtual void VisitNop() | ||
| { | ||
| ret = [](u32, T) {}; | ||
| } | ||
|
|
||
| virtual void VisitDirect(T* ptr, u32 mask) | ||
| { | ||
| ret = [ptr, mask](u32, T val) { *ptr = val & mask; }; | ||
| } | ||
|
|
||
| virtual void VisitComplex(std::function<void(u32, T)> lambda) | ||
| { | ||
| ret = lambda; | ||
| } | ||
| }; | ||
|
|
||
| FuncCreatorVisitor v; | ||
| Visit(v); | ||
| m_WriteFunc = v.ret; | ||
| } | ||
|
|
||
| // Define all the public specializations that are exported in MMIOHandlers.h. | ||
| MMIO_PUBLIC_SPECIALIZATIONS(); | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| // Copyright 2013 Dolphin Emulator Project | ||
| // Licensed under GPLv2 | ||
| // Refer to the license.txt file included. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "Common.h" | ||
| #include <array> | ||
| #include <string> | ||
| #include <type_traits> | ||
|
|
||
| #include "MMIOHandlers.h" | ||
|
|
||
| namespace MMIO | ||
| { | ||
|
|
||
| // There are three main MMIO blocks on the Wii (only one on the GameCube): | ||
| // - 0xCC00xxxx: GameCube MMIOs (CP, PE, VI, PI, MI, DSP, DVD, SI, EI, AI, GP) | ||
| // - 0xCD00xxxx: Wii MMIOs and GC mirrors (IPC, DVD, SI, EI, AI) | ||
| // - 0xCD80xxxx: Mirror of 0xCD00xxxx. | ||
| // | ||
| // 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; | ||
|
|
||
| // Compute the internal unique ID for a given MMIO address. This ID is computed | ||
| // from a very simple formula: (1 + block_id) * lower_16_bits(address). | ||
| // | ||
| // The block ID can easily be computed by simply checking bit 24 (CC vs. CD). | ||
| inline u32 UniqueID(u32 address) | ||
| { | ||
| _dbg_assert_msg_(MEMMAP, ((address & 0xFFFF0000) == 0xCC000000) || | ||
| ((address & 0xFFFF0000) == 0xCD000000) || | ||
| ((address & 0xFFFF0000) == 0xCD800000), | ||
| "Trying to get the ID of a non-existing MMIO address."); | ||
|
|
||
| return (1 + ((address >> 24) & 1)) * (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. | ||
| #define REGISTER_FUNCS(Size) \ | ||
| void RegisterRead(u32 addr, ReadHandlingMethod<u##Size>* read) \ | ||
| { \ | ||
| u32 id = UniqueID(addr) / sizeof (u##Size); \ | ||
| m_Read##Size##Handlers[id].ResetMethod(read); \ | ||
| } \ | ||
| void RegisterWrite(u32 addr, WriteHandlingMethod<u##Size>* write) \ | ||
| { \ | ||
| u32 id = UniqueID(addr) / sizeof (u##Size); \ | ||
| m_Write##Size##Handlers[id].ResetMethod(write); \ | ||
| } \ | ||
| void Register(u32 addr, ReadHandlingMethod<u##Size>* read, \ | ||
| WriteHandlingMethod<u##Size>* write) \ | ||
| { \ | ||
| RegisterRead(addr, read); \ | ||
| RegisterWrite(addr, write); \ | ||
| } | ||
| REGISTER_FUNCS(8) REGISTER_FUNCS(16) REGISTER_FUNCS(32) | ||
| #undef REGISTER_FUNCS | ||
|
|
||
| // 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. | ||
| // | ||
| // Note that for reads we cannot simply return the read value because C++ | ||
| // allows overloading only with parameter types, not return types. | ||
| #define READ_FUNC(Size) \ | ||
| void Read(u32 addr, u##Size* val) const \ | ||
| { \ | ||
| u32 id = UniqueID(addr) / sizeof (u##Size); \ | ||
| *val = m_Read##Size##Handlers[id].Read(addr); \ | ||
| } | ||
| READ_FUNC(8) READ_FUNC(16) READ_FUNC(32) | ||
| #undef READ_FUNC | ||
|
|
||
| #define WRITE_FUNC(Size) \ | ||
| void Write(u32 addr, u##Size val) const \ | ||
| { \ | ||
| u32 id = UniqueID(addr) / sizeof (u##Size); \ | ||
| m_Write##Size##Handlers[id].Write(addr, val); \ | ||
| } | ||
| WRITE_FUNC(8) WRITE_FUNC(16) WRITE_FUNC(32) | ||
| #undef WRITE_FUNC | ||
|
|
||
| // 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. | ||
| // | ||
| // Two variants of each GetHandler function are provided: one that returns | ||
| // the handler directly and one that has a pointer parameter to return the | ||
| // value. This second variant is needed because C++ doesn't do overloads | ||
| // based on return type but only based on argument types. | ||
| #define GET_HANDLERS_FUNC(Type, Size) \ | ||
| const Type##Handler<u##Size>& GetHandlerFor##Type##Size(u32 addr) const \ | ||
| { \ | ||
| return m_##Type##Size##Handlers[UniqueID(addr) / sizeof (u##Size)]; \ | ||
| } \ | ||
| void GetHandlerFor##Type(u32 addr, const Type##Handler<u##Size>** h) const \ | ||
| { \ | ||
| *h = &GetHandlerFor##Type##Size(addr); \ | ||
| } | ||
| GET_HANDLERS_FUNC(Read, 8) GET_HANDLERS_FUNC(Read, 16) GET_HANDLERS_FUNC(Read, 32) | ||
| GET_HANDLERS_FUNC(Write, 8) GET_HANDLERS_FUNC(Write, 16) GET_HANDLERS_FUNC(Write, 32) | ||
| #undef GET_HANDLERS_FUNC | ||
|
|
||
| // 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. | ||
| void Read(u32 addr, u64* val) const { _dbg_assert_(MEMMAP, 0); } | ||
| void Write(u32 addr, u64 val) const { _dbg_assert_(MEMMAP, 0); } | ||
|
|
||
| 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). | ||
| #define HANDLERS(Size) \ | ||
| std::array<ReadHandler<u##Size>, NUM_MMIOS / sizeof (u##Size)> m_Read##Size##Handlers; \ | ||
| std::array<WriteHandler<u##Size>, NUM_MMIOS / sizeof (u##Size)> m_Write##Size##Handlers; | ||
| HANDLERS(8) HANDLERS(16) HANDLERS(32) | ||
| #undef HANDLERS | ||
| }; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| // Copyright 2013 Dolphin Emulator Project | ||
| // Licensed under GPLv2 | ||
| // Refer to the license.txt file included. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "Common.h" | ||
|
|
||
| #include <functional> | ||
| #include <memory> | ||
|
|
||
| // 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 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> ReadHandlingMethod<T>* DirectRead(volatile const T* addr, u32 mask = 0xFFFFFFFF); | ||
| template <typename T> WriteHandlingMethod<T>* DirectWrite(T* addr, u32 mask = 0xFFFFFFFF); | ||
| template <typename T> WriteHandlingMethod<T>* DirectWrite(volatile 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(u32)>); | ||
| template <typename T> WriteHandlingMethod<T>* ComplexWrite(std::function<void(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(std::function<T(u32)> lambda) = 0; | ||
| }; | ||
| template <typename T> | ||
| class WriteHandlingMethodVisitor | ||
| { | ||
| public: | ||
| virtual void VisitNop() = 0; | ||
| virtual void VisitDirect(T* addr, u32 mask) = 0; | ||
| virtual void VisitComplex(std::function<void(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 NonCopyable | ||
| { | ||
| public: | ||
| ReadHandler(); | ||
|
|
||
| // Takes ownership of "method". | ||
| ReadHandler(ReadHandlingMethod<T>* method); | ||
|
|
||
| ~ReadHandler(); | ||
|
|
||
| // Entry point for read handling method visitors. | ||
| void Visit(ReadHandlingMethodVisitor<T>& visitor) const; | ||
|
|
||
| T Read(u32 addr) const | ||
| { | ||
| return m_ReadFunc(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: | ||
| std::unique_ptr<ReadHandlingMethod<T>> m_Method; | ||
| std::function<T(u32)> m_ReadFunc; | ||
| }; | ||
| template <typename T> | ||
| class WriteHandler : public NonCopyable | ||
| { | ||
| public: | ||
| WriteHandler(); | ||
|
|
||
| // Takes ownership of "method". | ||
| WriteHandler(WriteHandlingMethod<T>* method); | ||
|
|
||
| ~WriteHandler(); | ||
|
|
||
| // Entry point for write handling method visitors. | ||
| void Visit(WriteHandlingMethodVisitor<T>& visitor) const; | ||
|
|
||
| void Write(u32 addr, T val) const | ||
| { | ||
| m_WriteFunc(addr, 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: | ||
| std::unique_ptr<WriteHandlingMethod<T>> m_Method; | ||
| std::function<void(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 ReadHandlingMethod<T>* DirectRead(volatile const T* addr, u32 mask); \ | ||
| MaybeExtern template WriteHandlingMethod<T>* DirectWrite(T* addr, u32 mask); \ | ||
| MaybeExtern template WriteHandlingMethod<T>* DirectWrite(volatile T* addr, u32 mask); \ | ||
| MaybeExtern template ReadHandlingMethod<T>* ComplexRead<T>(std::function<T(u32)>); \ | ||
| MaybeExtern template WriteHandlingMethod<T>* ComplexWrite<T>(std::function<void(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(MaybeExtern) \ | ||
| MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u8); \ | ||
| MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u16); \ | ||
| MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u32); \ | ||
| MMIO_SPECIAL_PUBLIC_SPECIALIZATIONS(MaybeExtern); | ||
|
|
||
| MMIO_PUBLIC_SPECIALIZATIONS(extern) | ||
|
|
||
| } |