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