diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt
index 504b8ada47..79f2491d49 100644
--- a/Source/Core/Core/CMakeLists.txt
+++ b/Source/Core/Core/CMakeLists.txt
@@ -107,6 +107,7 @@ set(SRCS ActionReplay.cpp
HW/Memmap.cpp
HW/MemmapFunctions.cpp
HW/MemoryInterface.cpp
+ HW/MMIO.cpp
HW/ProcessorInterface.cpp
HW/SI.cpp
HW/SI_DeviceAMBaseboard.cpp
diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj
index 69b9bda339..fdf6fc4b53 100644
--- a/Source/Core/Core/Core.vcxproj
+++ b/Source/Core/Core/Core.vcxproj
@@ -145,6 +145,7 @@
+
@@ -342,6 +343,8 @@
+
+
diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters
index 40c890398e..99626fecb4 100644
--- a/Source/Core/Core/Core.vcxproj.filters
+++ b/Source/Core/Core/Core.vcxproj.filters
@@ -510,6 +510,9 @@
HW %28Flipper/Hollywood%29
+
+ HW %28Flipper/Hollywood%29
+
HW %28Flipper/Hollywood%29
@@ -1037,6 +1040,12 @@
HW %28Flipper/Hollywood%29
+
+ HW %28Flipper/Hollywood%29
+
+
+ HW %28Flipper/Hollywood%29
+
HW %28Flipper/Hollywood%29
@@ -1219,4 +1228,4 @@
-
\ No newline at end of file
+
diff --git a/Source/Core/Core/HW/MMIO.cpp b/Source/Core/Core/HW/MMIO.cpp
new file mode 100644
index 0000000000..759823f480
--- /dev/null
+++ b/Source/Core/Core/HW/MMIO.cpp
@@ -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
+
+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
+class ReadHandlingMethod
+{
+public:
+ virtual ~ReadHandlingMethod() {}
+ virtual void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const = 0;
+};
+template
+class WriteHandlingMethod
+{
+public:
+ virtual ~WriteHandlingMethod() {}
+ virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor& 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
+class ConstantHandlingMethod : public ReadHandlingMethod
+{
+public:
+ explicit ConstantHandlingMethod(T value) : value_(value)
+ {
+ }
+
+ virtual ~ConstantHandlingMethod() {}
+
+ virtual void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const
+ {
+ v.VisitConstant(value_);
+ }
+
+private:
+ T value_;
+};
+template
+ReadHandlingMethod* Constant(T value)
+{
+ return new ConstantHandlingMethod(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
+class NopHandlingMethod : public WriteHandlingMethod
+{
+public:
+ NopHandlingMethod() {}
+ virtual ~NopHandlingMethod() {}
+ virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const
+ {
+ v.VisitNop();
+ }
+};
+template
+WriteHandlingMethod* Nop()
+{
+ return new NopHandlingMethod();
+}
+
+// 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
+class DirectHandlingMethod : public ReadHandlingMethod,
+ public WriteHandlingMethod
+{
+public:
+ DirectHandlingMethod(T* addr, u32 mask) : addr_(addr), mask_(mask)
+ {
+ }
+
+ virtual ~DirectHandlingMethod() {}
+
+ virtual void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const
+ {
+ v.VisitDirect(addr_, mask_);
+ }
+
+ virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const
+ {
+ v.VisitDirect(addr_, mask_);
+ }
+
+private:
+ T* addr_;
+ u32 mask_;
+};
+template
+ReadHandlingMethod* DirectRead(const T* addr, u32 mask)
+{
+ return new DirectHandlingMethod(const_cast(addr), mask);
+}
+template
+ReadHandlingMethod* DirectRead(volatile const T* addr, u32 mask)
+{
+ return new DirectHandlingMethod((T*)addr, mask);
+}
+template
+WriteHandlingMethod* DirectWrite(T* addr, u32 mask)
+{
+ return new DirectHandlingMethod(addr, mask);
+}
+template
+WriteHandlingMethod* DirectWrite(volatile T* addr, u32 mask)
+{
+ return new DirectHandlingMethod((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
+class ComplexHandlingMethod : public ReadHandlingMethod,
+ public WriteHandlingMethod
+{
+public:
+ explicit ComplexHandlingMethod(std::function read_lambda)
+ : read_lambda_(read_lambda), write_lambda_(InvalidWriteLambda())
+ {
+ }
+
+ explicit ComplexHandlingMethod(std::function write_lambda)
+ : read_lambda_(InvalidReadLambda()), write_lambda_(write_lambda)
+ {
+ }
+
+ virtual ~ComplexHandlingMethod() {}
+
+ virtual void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const
+ {
+ v.VisitComplex(read_lambda_);
+ }
+
+ virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const
+ {
+ v.VisitComplex(write_lambda_);
+ }
+
+private:
+ std::function InvalidReadLambda() const
+ {
+ return [](u32) {
+ _dbg_assert_msg_(MEMMAP, 0, "Called the read lambda on a write "
+ "complex handler.");
+ return 0;
+ };
+ }
+
+ std::function InvalidWriteLambda() const
+ {
+ return [](u32, T) {
+ _dbg_assert_msg_(MEMMAP, 0, "Called the write lambda on a read "
+ "complex handler.");
+ };
+ }
+
+ std::function read_lambda_;
+ std::function write_lambda_;
+};
+template
+ReadHandlingMethod* ComplexRead(std::function lambda)
+{
+ return new ComplexHandlingMethod(lambda);
+}
+template
+WriteHandlingMethod* ComplexWrite(std::function lambda)
+{
+ return new ComplexHandlingMethod(lambda);
+}
+
+// Invalid: specialization of the complex handling type with lambdas that
+// display error messages.
+template
+ReadHandlingMethod* InvalidRead()
+{
+ return ComplexRead([](u32 addr) {
+ ERROR_LOG(MEMMAP, "Trying to read from an invalid MMIO (addr=%08x)",
+ addr);
+ return -1;
+ });
+}
+template
+WriteHandlingMethod* InvalidWrite()
+{
+ return ComplexWrite([](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 struct SmallerAccessSize {};
+template <> struct SmallerAccessSize { typedef u8 value; };
+template <> struct SmallerAccessSize { typedef u16 value; };
+
+template struct LargerAccessSize {};
+template <> struct LargerAccessSize { typedef u16 value; };
+template <> struct LargerAccessSize { typedef u32 value; };
+
+template
+ReadHandlingMethod* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr)
+{
+ typedef typename SmallerAccessSize::value ST;
+
+ const ReadHandler* high_part;
+ const ReadHandler* low_part;
+ mmio->GetHandlerForRead(high_part_addr, &high_part);
+ mmio->GetHandlerForRead(low_part_addr, &low_part);
+
+ // TODO(delroth): optimize
+ return ComplexRead([high_part, low_part](u32 addr) {
+ return ((T)high_part->Read(addr) << (8 * sizeof (ST))) | low_part->Read(addr);
+ });
+}
+
+template
+WriteHandlingMethod* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr)
+{
+ typedef typename SmallerAccessSize::value ST;
+
+ const WriteHandler* high_part;
+ const WriteHandler* low_part;
+ mmio->GetHandlerForWrite(high_part_addr, &high_part);
+ mmio->GetHandlerForWrite(low_part_addr, &low_part);
+
+ // TODO(delroth): optimize
+ return ComplexWrite([high_part, low_part](u32 addr, T val) {
+ high_part->Write(addr, val >> (8 * sizeof (ST)));
+ low_part->Write(addr, (ST)val);
+ });
+}
+
+template
+ReadHandlingMethod* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift)
+{
+ typedef typename LargerAccessSize::value LT;
+
+ const ReadHandler* large;
+ mmio->GetHandlerForRead(larger_addr, &large);
+
+ // TODO(delroth): optimize
+ return ComplexRead([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
+ReadHandler::ReadHandler() : m_Method(nullptr)
+{
+ ResetMethod(InvalidRead());
+}
+
+template
+ReadHandler::ReadHandler(ReadHandlingMethod* method)
+ : m_Method(nullptr)
+{
+ ResetMethod(method);
+}
+
+template
+ReadHandler::~ReadHandler()
+{
+}
+
+template
+void ReadHandler::Visit(ReadHandlingMethodVisitor& visitor) const
+{
+ m_Method->AcceptReadVisitor(visitor);
+}
+
+template
+void ReadHandler::ResetMethod(ReadHandlingMethod* method)
+{
+ m_Method.reset(method);
+
+ struct FuncCreatorVisitor : public ReadHandlingMethodVisitor
+ {
+ std::function 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 lambda)
+ {
+ ret = lambda;
+ }
+ };
+
+ FuncCreatorVisitor v;
+ Visit(v);
+ m_ReadFunc = v.ret;
+}
+
+template
+WriteHandler::WriteHandler() : m_Method(nullptr)
+{
+ ResetMethod(InvalidWrite());
+}
+
+template
+WriteHandler::WriteHandler(WriteHandlingMethod* method)
+ : m_Method(nullptr)
+{
+ ResetMethod(method);
+}
+
+template
+WriteHandler::~WriteHandler()
+{
+}
+
+template
+void WriteHandler::Visit(WriteHandlingMethodVisitor& visitor) const
+{
+ m_Method->AcceptWriteVisitor(visitor);
+}
+
+template
+void WriteHandler::ResetMethod(WriteHandlingMethod* method)
+{
+ m_Method.reset(method);
+
+ struct FuncCreatorVisitor : public WriteHandlingMethodVisitor
+ {
+ std::function 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 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();
+
+}
diff --git a/Source/Core/Core/HW/MMIO.h b/Source/Core/Core/HW/MMIO.h
new file mode 100644
index 0000000000..41353b7858
--- /dev/null
+++ b/Source/Core/Core/HW/MMIO.h
@@ -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
+#include
+#include
+
+#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* read) \
+ { \
+ u32 id = UniqueID(addr) / sizeof (u##Size); \
+ m_Read##Size##Handlers[id].ResetMethod(read); \
+ } \
+ void RegisterWrite(u32 addr, WriteHandlingMethod* write) \
+ { \
+ u32 id = UniqueID(addr) / sizeof (u##Size); \
+ m_Write##Size##Handlers[id].ResetMethod(write); \
+ } \
+ void Register(u32 addr, ReadHandlingMethod* read, \
+ WriteHandlingMethod* 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& GetHandlerFor##Type##Size(u32 addr) const \
+ { \
+ return m_##Type##Size##Handlers[UniqueID(addr) / sizeof (u##Size)]; \
+ } \
+ void GetHandlerFor##Type(u32 addr, const Type##Handler** 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, NUM_MMIOS / sizeof (u##Size)> m_Read##Size##Handlers; \
+ std::array, NUM_MMIOS / sizeof (u##Size)> m_Write##Size##Handlers;
+ HANDLERS(8) HANDLERS(16) HANDLERS(32)
+#undef HANDLERS
+};
+
+}
diff --git a/Source/Core/Core/HW/MMIOHandlers.h b/Source/Core/Core/HW/MMIOHandlers.h
new file mode 100644
index 0000000000..732704efae
--- /dev/null
+++ b/Source/Core/Core/HW/MMIOHandlers.h
@@ -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
+#include
+
+// 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 class ReadHandlingMethod;
+template class WriteHandlingMethod;
+
+// Constant: use when the value read on this MMIO is always the same. This is
+// only for reads.
+template ReadHandlingMethod* Constant(T value);
+
+// Nop: use for writes that shouldn't have any effect and shouldn't log an
+// error either.
+template WriteHandlingMethod* 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 ReadHandlingMethod* DirectRead(const T* addr, u32 mask = 0xFFFFFFFF);
+template ReadHandlingMethod* DirectRead(volatile const T* addr, u32 mask = 0xFFFFFFFF);
+template WriteHandlingMethod* DirectWrite(T* addr, u32 mask = 0xFFFFFFFF);
+template WriteHandlingMethod* 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 ReadHandlingMethod* ComplexRead(std::function);
+template WriteHandlingMethod* ComplexWrite(std::function);
+
+// Invalid: log an error and return -1 in case of a read. These are the default
+// handlers set for all MMIO types.
+template ReadHandlingMethod* InvalidRead();
+template WriteHandlingMethod* 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 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 ReadHandlingMethod* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr);
+template WriteHandlingMethod* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr);
+template ReadHandlingMethod* 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
+class ReadHandlingMethodVisitor
+{
+public:
+ virtual void VisitConstant(T value) = 0;
+ virtual void VisitDirect(const T* addr, u32 mask) = 0;
+ virtual void VisitComplex(std::function lambda) = 0;
+};
+template
+class WriteHandlingMethodVisitor
+{
+public:
+ virtual void VisitNop() = 0;
+ virtual void VisitDirect(T* addr, u32 mask) = 0;
+ virtual void VisitComplex(std::function 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
+class ReadHandler : public NonCopyable
+{
+public:
+ ReadHandler();
+
+ // Takes ownership of "method".
+ ReadHandler(ReadHandlingMethod* method);
+
+ ~ReadHandler();
+
+ // Entry point for read handling method visitors.
+ void Visit(ReadHandlingMethodVisitor& 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* method);
+
+private:
+ std::unique_ptr> m_Method;
+ std::function m_ReadFunc;
+};
+template
+class WriteHandler : public NonCopyable
+{
+public:
+ WriteHandler();
+
+ // Takes ownership of "method".
+ WriteHandler(WriteHandlingMethod* method);
+
+ ~WriteHandler();
+
+ // Entry point for write handling method visitors.
+ void Visit(WriteHandlingMethodVisitor& 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* method);
+
+private:
+ std::unique_ptr> m_Method;
+ std::function 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* Constant(T value); \
+ MaybeExtern template WriteHandlingMethod* Nop(); \
+ MaybeExtern template ReadHandlingMethod* DirectRead(const T* addr, u32 mask); \
+ MaybeExtern template ReadHandlingMethod* DirectRead(volatile const T* addr, u32 mask); \
+ MaybeExtern template WriteHandlingMethod* DirectWrite(T* addr, u32 mask); \
+ MaybeExtern template WriteHandlingMethod* DirectWrite(volatile T* addr, u32 mask); \
+ MaybeExtern template ReadHandlingMethod* ComplexRead(std::function); \
+ MaybeExtern template WriteHandlingMethod* ComplexWrite(std::function); \
+ MaybeExtern template ReadHandlingMethod* InvalidRead(); \
+ MaybeExtern template WriteHandlingMethod* InvalidWrite(); \
+ MaybeExtern template class ReadHandler; \
+ MaybeExtern template class WriteHandler
+
+#define MMIO_SPECIAL_PUBLIC_SPECIALIZATIONS(MaybeExtern) \
+ MaybeExtern template ReadHandlingMethod* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \
+ MaybeExtern template ReadHandlingMethod* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \
+ MaybeExtern template WriteHandlingMethod* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \
+ MaybeExtern template WriteHandlingMethod* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \
+ MaybeExtern template ReadHandlingMethod* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift); \
+ MaybeExtern template ReadHandlingMethod* 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)
+
+}
diff --git a/Source/Core/Core/HW/Memmap.cpp b/Source/Core/Core/HW/Memmap.cpp
index 82f3141040..d10cf90f0a 100644
--- a/Source/Core/Core/HW/Memmap.cpp
+++ b/Source/Core/Core/HW/Memmap.cpp
@@ -34,6 +34,7 @@
#include "../ConfigManager.h"
#include "../Debugger/Debugger_SymbolMap.h"
#include "VideoBackendBase.h"
+#include "MMIO.h"
namespace Memory
{
@@ -83,6 +84,9 @@ u8 *m_pVirtualUncachedEXRAM; // wii only
u8 *m_pVirtualL1Cache;
u8 *m_pVirtualFakeVMEM;
+// MMIO mapping object.
+MMIO::Mapping* mmio_mapping;
+
// =================================
// Read and write shortcuts
// ----------------
@@ -348,6 +352,8 @@ void Init()
if (bFakeVMEM) flags |= MV_FAKE_VMEM;
base = MemoryMap_Setup(views, num_views, flags, &g_arena);
+ mmio_mapping = new MMIO::Mapping();
+
if (wii)
InitHWMemFuncsWii();
else
@@ -382,6 +388,7 @@ void Shutdown()
MemoryMap_Shutdown(views, num_views, flags, &g_arena);
g_arena.ReleaseSpace();
base = NULL;
+ delete mmio_mapping;
INFO_LOG(MEMMAP, "Memory system shut down.");
}
diff --git a/Source/Core/Core/HW/Memmap.h b/Source/Core/Core/HW/Memmap.h
index 1f89148c56..d2ea967496 100644
--- a/Source/Core/Core/HW/Memmap.h
+++ b/Source/Core/Core/HW/Memmap.h
@@ -28,6 +28,7 @@
// Global declarations
class PointerWrap;
+namespace MMIO { class Mapping; }
typedef void (*writeFn8 )(const u8, const u32);
typedef void (*writeFn16)(const u16,const u32);
@@ -83,6 +84,9 @@ enum
#endif
};
+// MMIO mapping object.
+extern MMIO::Mapping* mmio_mapping;
+
// Init and Shutdown
bool IsInitialized();
void Init();
diff --git a/Source/Core/Core/HW/MemmapFunctions.cpp b/Source/Core/Core/HW/MemmapFunctions.cpp
index 40bc5f4a3a..eb335228a5 100644
--- a/Source/Core/Core/HW/MemmapFunctions.cpp
+++ b/Source/Core/Core/HW/MemmapFunctions.cpp
@@ -24,6 +24,7 @@
#include "../Core.h"
#include "../PowerPC/PowerPC.h"
#include "VideoBackendBase.h"
+#include "MMIO.h"
#ifdef USE_GDBSTUB
#include "../PowerPC/GDBStub.h"