1 // Copyright 2014 Dolphin Emulator Project 2 // Licensed under GPLv2+ 3 // Refer to the license.txt file included. 4 5 #pragma once 6 7 #include <functional> 8 #include <memory> 9 10 #include "Common/CommonTypes.h" 11 12 // All the templated and very repetitive MMIO-related code is isolated in this 13 // file for easier reading. It mostly contains code related to handling methods 14 // (including the declaration of the public functions for creating handling 15 // method objects), visitors for these handling methods, and interface of the 16 // handler classes. 17 // 18 // This code is very genericized (aka. lots of templates) in order to handle 19 // u8/u16/u32 with the same code while providing type safety: it is impossible 20 // to mix code from these types, and the type system enforces it. 21 22 namespace MMIO 23 { 24 class Mapping; 25 26 // Read and write handling methods are separated for type safety. On top of 27 // that, some handling methods require different arguments for reads and writes 28 // (Complex, for example). 29 template <typename T> 30 class ReadHandlingMethod; 31 template <typename T> 32 class WriteHandlingMethod; 33 34 // Constant: use when the value read on this MMIO is always the same. This is 35 // only for reads. 36 template <typename T> 37 ReadHandlingMethod<T>* Constant(T value); 38 39 // Nop: use for writes that shouldn't have any effect and shouldn't log an 40 // error either. 41 template <typename T> 42 WriteHandlingMethod<T>* Nop(); 43 44 // Direct: use when all the MMIO does is read/write the given value to/from a 45 // global variable, with an optional mask applied on the read/written value. 46 template <typename T> 47 ReadHandlingMethod<T>* DirectRead(const T* addr, u32 mask = 0xFFFFFFFF); 48 template <typename T> 49 ReadHandlingMethod<T>* DirectRead(volatile const T* addr, u32 mask = 0xFFFFFFFF); 50 template <typename T> 51 WriteHandlingMethod<T>* DirectWrite(T* addr, u32 mask = 0xFFFFFFFF); 52 template <typename T> 53 WriteHandlingMethod<T>* DirectWrite(volatile T* addr, u32 mask = 0xFFFFFFFF); 54 55 // Complex: use when no other handling method fits your needs. These allow you 56 // to directly provide a function that will be called when a read/write needs 57 // to be done. 58 template <typename T> 59 ReadHandlingMethod<T>* ComplexRead(std::function<T(u32)>); 60 template <typename T> 61 WriteHandlingMethod<T>* ComplexWrite(std::function<void(u32, T)>); 62 63 // Invalid: log an error and return -1 in case of a read. These are the default 64 // handlers set for all MMIO types. 65 template <typename T> 66 ReadHandlingMethod<T>* InvalidRead(); 67 template <typename T> 68 WriteHandlingMethod<T>* InvalidWrite(); 69 70 // {Read,Write}To{Smaller,Larger}: these functions are not themselves handling 71 // methods but will try to combine accesses to two handlers into one new 72 // handler object. 73 // 74 // This is used for example when 32 bit reads have the exact same handling as 75 // 16 bit. Handlers need to be registered for both 32 and 16, and it would be 76 // repetitive and unoptimal to require users to write the same handling code in 77 // both cases. Instead, an MMIO module can simply define all handlers in terms 78 // of 16 bit reads, then use ReadToSmaller<u32> to convert u32 reads to u16 79 // reads. 80 // 81 // Internally, these size conversion functions have some magic to make the 82 // combined handlers as fast as possible. For example, if the two underlying 83 // u16 handlers for a u32 reads are Direct to consecutive memory addresses, 84 // they can be transformed into a Direct u32 access. 85 // 86 // Warning: unlike the other handling methods, *ToSmaller are obviously not 87 // available for u8, and *ToLarger are not available for u32. 88 template <typename T> 89 ReadHandlingMethod<T>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); 90 template <typename T> 91 WriteHandlingMethod<T>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); 92 template <typename T> 93 ReadHandlingMethod<T>* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift); 94 95 // Use these visitors interfaces if you need to write code that performs 96 // different actions based on the handling method used by a handler. Write your 97 // visitor implementing that interface, then use handler->VisitHandlingMethod 98 // to run the proper function. 99 template <typename T> 100 class ReadHandlingMethodVisitor 101 { 102 public: 103 virtual void VisitConstant(T value) = 0; 104 virtual void VisitDirect(const T* addr, u32 mask) = 0; 105 virtual void VisitComplex(const std::function<T(u32)>* lambda) = 0; 106 }; 107 template <typename T> 108 class WriteHandlingMethodVisitor 109 { 110 public: 111 virtual void VisitNop() = 0; 112 virtual void VisitDirect(T* addr, u32 mask) = 0; 113 virtual void VisitComplex(const std::function<void(u32, T)>* lambda) = 0; 114 }; 115 116 // These classes are INTERNAL. Do not use outside of the MMIO implementation 117 // code. Unfortunately, because we want to make Read() and Write() fast and 118 // inlinable, we need to provide some of the implementation of these two 119 // classes here and can't just use a forward declaration. 120 template <typename T> 121 class ReadHandler 122 { 123 public: 124 ReadHandler(); 125 126 // Takes ownership of "method". 127 ReadHandler(ReadHandlingMethod<T>* method); 128 129 ~ReadHandler(); 130 131 // Entry point for read handling method visitors. 132 void Visit(ReadHandlingMethodVisitor<T>& visitor); 133 Read(u32 addr)134 T Read(u32 addr) 135 { 136 // Check if the handler has already been initialized. For real 137 // handlers, this will always be the case, so this branch should be 138 // easily predictable. 139 if (!m_Method) 140 InitializeInvalid(); 141 142 return m_ReadFunc(addr); 143 } 144 145 // Internal method called when changing the internal method object. Its 146 // main role is to make sure the read function is updated at the same time. 147 void ResetMethod(ReadHandlingMethod<T>* method); 148 149 private: 150 // Initialize this handler to an invalid handler. Done lazily to avoid 151 // useless initialization of thousands of unused handler objects. InitializeInvalid()152 void InitializeInvalid() { ResetMethod(InvalidRead<T>()); } 153 std::unique_ptr<ReadHandlingMethod<T>> m_Method; 154 std::function<T(u32)> m_ReadFunc; 155 }; 156 template <typename T> 157 class WriteHandler 158 { 159 public: 160 WriteHandler(); 161 162 // Takes ownership of "method". 163 WriteHandler(WriteHandlingMethod<T>* method); 164 165 ~WriteHandler(); 166 167 // Entry point for write handling method visitors. 168 void Visit(WriteHandlingMethodVisitor<T>& visitor); 169 Write(u32 addr,T val)170 void Write(u32 addr, T val) 171 { 172 // Check if the handler has already been initialized. For real 173 // handlers, this will always be the case, so this branch should be 174 // easily predictable. 175 if (!m_Method) 176 InitializeInvalid(); 177 178 m_WriteFunc(addr, val); 179 } 180 181 // Internal method called when changing the internal method object. Its 182 // main role is to make sure the write function is updated at the same 183 // time. 184 void ResetMethod(WriteHandlingMethod<T>* method); 185 186 private: 187 // Initialize this handler to an invalid handler. Done lazily to avoid 188 // useless initialization of thousands of unused handler objects. InitializeInvalid()189 void InitializeInvalid() { ResetMethod(InvalidWrite<T>()); } 190 std::unique_ptr<WriteHandlingMethod<T>> m_Method; 191 std::function<void(u32, T)> m_WriteFunc; 192 }; 193 194 // Boilerplate boilerplate boilerplate. 195 // 196 // This is used to be able to avoid putting the templates implementation in the 197 // header files and slow down compilation times. Instead, we declare 3 198 // specializations in the header file as already implemented in another 199 // compilation unit: u8, u16, u32. 200 // 201 // The "MaybeExtern" is there because that same macro is used for declaration 202 // (where MaybeExtern = "extern") and definition (MaybeExtern = ""). 203 #define MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, T) \ 204 MaybeExtern template ReadHandlingMethod<T>* Constant<T>(T value); \ 205 MaybeExtern template WriteHandlingMethod<T>* Nop<T>(); \ 206 MaybeExtern template ReadHandlingMethod<T>* DirectRead(const T* addr, u32 mask); \ 207 MaybeExtern template ReadHandlingMethod<T>* DirectRead(volatile const T* addr, u32 mask); \ 208 MaybeExtern template WriteHandlingMethod<T>* DirectWrite(T* addr, u32 mask); \ 209 MaybeExtern template WriteHandlingMethod<T>* DirectWrite(volatile T* addr, u32 mask); \ 210 MaybeExtern template ReadHandlingMethod<T>* ComplexRead<T>(std::function<T(u32)>); \ 211 MaybeExtern template WriteHandlingMethod<T>* ComplexWrite<T>(std::function<void(u32, T)>); \ 212 MaybeExtern template ReadHandlingMethod<T>* InvalidRead<T>(); \ 213 MaybeExtern template WriteHandlingMethod<T>* InvalidWrite<T>(); \ 214 MaybeExtern template class ReadHandler<T>; \ 215 MaybeExtern template class WriteHandler<T> 216 217 #define MMIO_SPECIAL_PUBLIC_SPECIALIZATIONS(MaybeExtern) \ 218 MaybeExtern template ReadHandlingMethod<u16>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, \ 219 u32 low_part_addr); \ 220 MaybeExtern template ReadHandlingMethod<u32>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, \ 221 u32 low_part_addr); \ 222 MaybeExtern template WriteHandlingMethod<u16>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, \ 223 u32 low_part_addr); \ 224 MaybeExtern template WriteHandlingMethod<u32>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, \ 225 u32 low_part_addr); \ 226 MaybeExtern template ReadHandlingMethod<u8>* ReadToLarger(Mapping* mmio, u32 larger_addr, \ 227 u32 shift); \ 228 MaybeExtern template ReadHandlingMethod<u16>* ReadToLarger(Mapping* mmio, u32 larger_addr, \ 229 u32 shift) 230 231 #define MMIO_PUBLIC_SPECIALIZATIONS() \ 232 MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u8); \ 233 MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u16); \ 234 MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u32); \ 235 MMIO_SPECIAL_PUBLIC_SPECIALIZATIONS(MaybeExtern); 236 237 #define MaybeExtern extern 238 MMIO_PUBLIC_SPECIALIZATIONS() 239 #undef MaybeExtern 240 } // namespace MMIO 241