(view as text)
diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt
index 504b8ad..79f2491 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 69b9bda..fdf6fc4 100644
--- a/Source/Core/Core/Core.vcxproj
+++ b/Source/Core/Core/Core.vcxproj
@@ -145,6 +145,7 @@
     <ClCompile Include="HW\Memmap.cpp" />
     <ClCompile Include="HW\MemmapFunctions.cpp" />
     <ClCompile Include="HW\MemoryInterface.cpp" />
+    <ClCompile Include="HW\MMIO.cpp" />
     <ClCompile Include="HW\ProcessorInterface.cpp" />
     <ClCompile Include="HW\SI.cpp" />
     <ClCompile Include="HW\SI_Device.cpp" />
@@ -342,6 +343,8 @@
     <ClInclude Include="HW\HW.h" />
     <ClInclude Include="HW\Memmap.h" />
     <ClInclude Include="HW\MemoryInterface.h" />
+    <ClInclude Include="HW\MMIO.h" />
+    <ClInclude Include="HW\MMIOHandlers.h" />
     <ClInclude Include="HW\ProcessorInterface.h" />
     <ClInclude Include="HW\SI.h" />
     <ClInclude Include="HW\SI_Device.h" />
diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters
index 40c8903..99626fe 100644
--- a/Source/Core/Core/Core.vcxproj.filters
+++ b/Source/Core/Core/Core.vcxproj.filters
@@ -510,6 +510,9 @@
     <ClCompile Include="HW\MemmapFunctions.cpp">
       <Filter>HW %28Flipper/Hollywood%29</Filter>
     </ClCompile>
+    <ClCompile Include="HW\MMIO.cpp">
+      <Filter>HW %28Flipper/Hollywood%29</Filter>
+    </ClCompile>
     <ClCompile Include="HW\SystemTimers.cpp">
       <Filter>HW %28Flipper/Hollywood%29</Filter>
     </ClCompile>
@@ -1037,6 +1040,12 @@
     <ClInclude Include="HW\Memmap.h">
       <Filter>HW %28Flipper/Hollywood%29</Filter>
     </ClInclude>
+    <ClInclude Include="HW\MMIO.h">
+      <Filter>HW %28Flipper/Hollywood%29</Filter>
+    </ClInclude>
+    <ClInclude Include="HW\MMIOHandlers.h">
+      <Filter>HW %28Flipper/Hollywood%29</Filter>
+    </ClInclude>
     <ClInclude Include="HW\SystemTimers.h">
       <Filter>HW %28Flipper/Hollywood%29</Filter>
     </ClInclude>
@@ -1219,4 +1228,4 @@
   <ItemGroup>
     <Text Include="CMakeLists.txt" />
   </ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/Source/Core/Core/HW/MMIO.cpp b/Source/Core/Core/HW/MMIO.cpp
new file mode 100644
index 0000000..894f345
--- /dev/null
+++ b/Source/Core/Core/HW/MMIO.cpp
@@ -0,0 +1,340 @@
+// Copyright (C) 2003 Dolphin Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official Git repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#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);
+	});
+}
+
+// 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(, u8);
+MMIO_PUBLIC_SPECIALIZATIONS(, u16);
+MMIO_PUBLIC_SPECIALIZATIONS(, u32);
+
+}
diff --git a/Source/Core/Core/HW/MMIO.h b/Source/Core/Core/HW/MMIO.h
new file mode 100644
index 0000000..14440fd
--- /dev/null
+++ b/Source/Core/Core/HW/MMIO.h
@@ -0,0 +1,149 @@
+// Copyright (C) 2003 Dolphin Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official Git repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#ifndef _MMIO_H
+#define _MMIO_H
+
+#include "Common.h"
+#include <array>
+#include <string>
+#include <type_traits>
+
+#include "MMIOHandlers.h"
+
+// HACK: Remove when the new MMIO interface is used.
+#include "Memmap.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 (address & 0xFFFF) * (1 + ((address >> 24) & 1));
+}
+
+// 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.
+#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
+
+	// 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
+};
+
+}
+
+#endif
diff --git a/Source/Core/Core/HW/MMIOHandlers.h b/Source/Core/Core/HW/MMIOHandlers.h
new file mode 100644
index 0000000..d0fedab
--- /dev/null
+++ b/Source/Core/Core/HW/MMIOHandlers.h
@@ -0,0 +1,181 @@
+// Copyright (C) 2003 Dolphin Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official Git repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#ifndef _MMIOHANDLERS_H
+#define _MMIOHANDLERS_H
+
+#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
+{
+
+// 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();
+
+// 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_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>
+
+MMIO_PUBLIC_SPECIALIZATIONS(extern, u8);
+MMIO_PUBLIC_SPECIALIZATIONS(extern, u16);
+MMIO_PUBLIC_SPECIALIZATIONS(extern, u32);
+
+}
+
+#endif
diff --git a/Source/Core/Core/HW/Memmap.cpp b/Source/Core/Core/HW/Memmap.cpp
index 82f3141..13d0315 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
 // ----------------
@@ -304,6 +308,20 @@ void InitHWMemFuncsWii()
 	hwWriteWii32[AUDIO_START] = AudioInterface::Write32;
 }
 
+void InitMMIO(MMIO::Mapping* mmio)
+{
+	VideoInterface::RegisterMMIO(mmio, 0xCC002000);
+	ProcessorInterface::RegisterMMIO(mmio, 0xCC003000);
+	MemoryInterface::RegisterMMIO(mmio, 0xCC004000);
+}
+
+void InitMMIOWii(MMIO::Mapping* mmio)
+{
+	VideoInterface::RegisterMMIO(mmio, 0xCC002000);
+	ProcessorInterface::RegisterMMIO(mmio, 0xCC003000);
+	MemoryInterface::RegisterMMIO(mmio, 0xCC004000);
+}
+
 writeFn32 GetHWWriteFun32(const u32 _Address)
 {
 	return hwWrite32[(_Address >> HWSHIFT) & (NUMHWMEMFUN-1)];
@@ -348,11 +366,18 @@ 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
 		InitHWMemFuncs();
 
+	if (wii)
+		InitMMIOWii(mmio_mapping);
+	else
+		InitMMIO(mmio_mapping);
+
 	INFO_LOG(MEMMAP, "Memory system initialized. RAM at %p (mirrors at 0 @ %p, 0x80000000 @ %p , 0xC0000000 @ %p)",
 		m_pRAM, m_pPhysicalRAM, m_pVirtualCachedRAM, m_pVirtualUncachedRAM);
 	m_IsInitialized = true;
@@ -382,6 +407,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 819829c..7451045 100644
--- a/Source/Core/Core/HW/Memmap.h
+++ b/Source/Core/Core/HW/Memmap.h
@@ -29,6 +29,7 @@
 
 // Global declarations
 class PointerWrap;
+namespace MMIO { class Mapping; }
 
 typedef void (*writeFn8 )(const u8, const u32);
 typedef void (*writeFn16)(const u16,const u32);
@@ -84,6 +85,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 40bc5f4..eb33522 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"
diff --git a/Source/Core/Core/HW/MemoryInterface.cpp b/Source/Core/Core/HW/MemoryInterface.cpp
index 80f4516..a803ea8 100644
--- a/Source/Core/Core/HW/MemoryInterface.cpp
+++ b/Source/Core/Core/HW/MemoryInterface.cpp
@@ -7,6 +7,7 @@
 
 #include "../PowerPC/PowerPC.h"
 #include "MemoryInterface.h"
+#include "MMIO.h"
 
 namespace MemoryInterface
 {
@@ -14,68 +15,220 @@ namespace MemoryInterface
 // internal hardware addresses
 enum
 {
-	MEM_CHANNEL0_HI		= 0x000,
-	MEM_CHANNEL0_LO		= 0x002,
-	MEM_CHANNEL1_HI		= 0x004,
-	MEM_CHANNEL1_LO		= 0x006,
-	MEM_CHANNEL2_HI		= 0x008,
-	MEM_CHANNEL2_LO		= 0x00A,
-	MEM_CHANNEL3_HI		= 0x00C,
-	MEM_CHANNEL3_LO		= 0x00E,
-	MEM_CHANNEL_CTRL	= 0x010
+	MI_REGION0_FIRST	= 0x000,
+	MI_REGION0_LAST		= 0x002,
+	MI_REGION1_FIRST	= 0x004,
+	MI_REGION1_LAST		= 0x006,
+	MI_REGION2_FIRST	= 0x008,
+	MI_REGION2_LAST		= 0x00A,
+	MI_REGION3_FIRST	= 0x00C,
+	MI_REGION3_LAST		= 0x00E,
+	MI_PROT_TYPE		= 0x010,
+	MI_IRQMASK			= 0x01C,
+	MI_IRQFLAG			= 0x01E,
+	MI_UNKNOWN1			= 0x020,
+	MI_PROT_ADDR_LO		= 0x022,
+	MI_PROT_ADDR_HI		= 0x024,
+	MI_TIMER0_HI		= 0x032,
+	MI_TIMER0_LO		= 0x034,
+	MI_TIMER1_HI		= 0x036,
+	MI_TIMER1_LO		= 0x038,
+	MI_TIMER2_HI		= 0x03A,
+	MI_TIMER2_LO		= 0x03C,
+	MI_TIMER3_HI		= 0x03E,
+	MI_TIMER3_LO		= 0x040,
+	MI_TIMER4_HI		= 0x042,
+	MI_TIMER4_LO		= 0x044,
+	MI_TIMER5_HI		= 0x046,
+	MI_TIMER5_LO		= 0x048,
+	MI_TIMER6_HI		= 0x04A,
+	MI_TIMER6_LO		= 0x04C,
+	MI_TIMER7_HI		= 0x04E,
+	MI_TIMER7_LO		= 0x050,
+	MI_TIMER8_HI		= 0x052,
+	MI_TIMER8_LO		= 0x054,
+	MI_TIMER9_HI		= 0x056,
+	MI_TIMER9_LO		= 0x058,
+	MI_UNKNOWN2			= 0x05A,
+};
+
+union MIRegion
+{
+	u32 hex;
+	struct { u16 first_page; u16 last_page; };
+};
+
+union MIProtType
+{
+	u16 hex;
+	struct
+	{
+		u16 reg0 : 2;
+		u16 reg1 : 2;
+		u16 reg2 : 2;
+		u16 reg3 : 2;
+		u16 : 8;
+	};
+};
+
+union MIIRQMask
+{
+	u16 hex;
+	struct
+	{
+		u16 reg0 : 1;
+		u16 reg1 : 1;
+		u16 reg2 : 1;
+		u16 reg3 : 1;
+		u16 all_regs : 1;
+		u16 : 11;
+	};
+};
+
+union MIIRQFlag
+{
+	u16 hex;
+	struct
+	{
+		u16 reg0 : 1;
+		u16 reg1 : 1;
+		u16 reg2 : 1;
+		u16 reg3 : 1;
+		u16 all_regs : 1;
+		u16 : 11;
+	};
+};
+
+union MIProtAddr
+{
+	u32 hex;
+	struct { u16 lo; u16 hi; };
+	struct
+	{
+		u32 : 5;
+		u32 addr : 25;
+		u32 : 2;
+	};
+};
+
+union MITimer
+{
+	u32 hex;
+	struct { u16 lo; u16 hi; };
 };
 
 struct MIMemStruct
 {
-	u32 Channel0_Addr;
-	u32 Channel1_Addr;
-	u32 Channel2_Addr;
-	u32 Channel3_Addr;
-	u32 Channel_Ctrl;
+	MIRegion regions[4];
+	MIProtType prot_type;
+	MIIRQMask irq_mask;
+	MIIRQFlag irq_flag;
+	u16 unknown1;
+	MIProtAddr prot_addr;
+	MITimer timers[10];
+	u16 unknown2;
 };
 
 // STATE_TO_SAVE
-static MIMemStruct miMem;
+static MIMemStruct g_mi_mem;
 
 void DoState(PointerWrap &p)
 {
-	p.Do(miMem);
+	p.Do(g_mi_mem);
+}
+
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
+{
+	for (int i = 0; i < MI_REGION0_FIRST; i += 4)
+	{
+		auto& region = g_mi_mem.regions[i / 4];
+		mmio->Register(base | i,
+			MMIO::DirectRead<u16>(&region.first_page),
+			MMIO::DirectWrite<u16>(&region.first_page)
+		);
+		mmio->Register(base | (i + 2),
+			MMIO::DirectRead<u16>(&region.last_page),
+			MMIO::DirectWrite<u16>(&region.last_page)
+		);
+	}
+
+	mmio->Register(base | MI_PROT_TYPE,
+		MMIO::DirectRead<u16>(&g_mi_mem.prot_type.hex),
+		MMIO::DirectWrite<u16>(&g_mi_mem.prot_type.hex)
+	);
+
+	mmio->Register(base | MI_IRQMASK,
+		MMIO::DirectRead<u16>(&g_mi_mem.irq_mask.hex),
+		MMIO::DirectWrite<u16>(&g_mi_mem.irq_mask.hex)
+	);
+
+	mmio->Register(base | MI_IRQFLAG,
+		MMIO::DirectRead<u16>(&g_mi_mem.irq_flag.hex),
+		MMIO::DirectWrite<u16>(&g_mi_mem.irq_flag.hex)
+	);
+
+	mmio->Register(base | MI_UNKNOWN1,
+		MMIO::DirectRead<u16>(&g_mi_mem.unknown1),
+		MMIO::DirectWrite<u16>(&g_mi_mem.unknown1)
+	);
+
+	// The naming is confusing here: the registed contains the lower part of
+	// the address (hence MI_..._LO but this is still the high part of the
+	// overall register.
+	mmio->Register(base | MI_PROT_ADDR_LO,
+		MMIO::DirectRead<u16>(&g_mi_mem.prot_addr.hi),
+		MMIO::DirectWrite<u16>(&g_mi_mem.prot_addr.hi)
+	);
+	mmio->Register(base | MI_PROT_ADDR_HI,
+		MMIO::DirectRead<u16>(&g_mi_mem.prot_addr.lo),
+		MMIO::DirectWrite<u16>(&g_mi_mem.prot_addr.lo)
+	);
+
+	for (int i = 0; i < 10; ++i)
+	{
+		auto& timer = g_mi_mem.timers[i];
+		mmio->Register(base | (MI_TIMER0_HI + 4 * i),
+			MMIO::DirectRead<u16>(&timer.hi),
+			MMIO::DirectWrite<u16>(&timer.hi)
+		);
+		mmio->Register(base | (MI_TIMER0_LO + 4 * i),
+			MMIO::DirectRead<u16>(&timer.lo),
+			MMIO::DirectWrite<u16>(&timer.lo)
+		);
+	}
+
+	mmio->Register(base | MI_UNKNOWN2,
+		MMIO::DirectRead<u16>(&g_mi_mem.unknown2),
+		MMIO::DirectWrite<u16>(&g_mi_mem.unknown2)
+	);
+
+	// TODO: register mapping from 32 bit reads/writes to 16 bit.
 }
 
 void Read16(u16& _uReturnValue, const u32 _iAddress)
 {
-	//0x30 -> 0x5a : gp memory metrics
-	INFO_LOG(MEMMAP, "(r16) 0x%04x @ 0x%08x", 0, _iAddress);
-	_uReturnValue = 0;
+	// HACK: Remove this function when the new MMIO interface is used.
+	Memory::mmio_mapping->Read(_iAddress, _uReturnValue);
 }
 
-void Read32(u32& _uReturnValue, const u32 _iAddress)
+void Write16(const u16 _iValue, const u32 _iAddress)
 {
-	INFO_LOG(MEMMAP, "(r32) 0x%08x @ 0x%08x", 0, _iAddress);
-	_uReturnValue = 0;
+	// HACK: Remove this function when the new MMIO interface is used.
+	Memory::mmio_mapping->Write(_iAddress, _iValue);
 }
 
-void Write32(const u32 _iValue, const u32 _iAddress)
+void Read32(u32& _uReturnValue, const u32 _iAddress)
 {
-	INFO_LOG(MEMMAP, "(w32) 0x%08x @ 0x%08x", _iValue, _iAddress);
+	u16 hi, lo;
+	Read16(hi, _iAddress);
+	Read16(lo, _iAddress +2);
+	_uReturnValue = (hi << 16) | lo;
 }
 
-//TODO : check
-void Write16(const u16 _iValue, const u32 _iAddress)
+void Write32(const u32 _iValue, const u32 _iAddress)
 {
-	INFO_LOG(MEMMAP, "(w16) 0x%04x @ 0x%08x", _iValue, _iAddress);
-	switch(_iAddress & 0xFFF)
-	{
-	case MEM_CHANNEL0_HI: miMem.Channel0_Addr = (miMem.Channel0_Addr & 0xFFFF) | (_iValue<<16); return;
-	case MEM_CHANNEL0_LO: miMem.Channel0_Addr = (miMem.Channel0_Addr & 0xFFFF0000) | (_iValue); return;
-	case MEM_CHANNEL1_HI: miMem.Channel1_Addr = (miMem.Channel1_Addr & 0xFFFF) | (_iValue<<16); return;
-	case MEM_CHANNEL1_LO: miMem.Channel1_Addr = (miMem.Channel1_Addr & 0xFFFF0000) | (_iValue); return;
-	case MEM_CHANNEL2_HI: miMem.Channel2_Addr = (miMem.Channel2_Addr & 0xFFFF) | (_iValue<<16); return;
-	case MEM_CHANNEL2_LO: miMem.Channel2_Addr = (miMem.Channel2_Addr & 0xFFFF0000) | (_iValue); return;
-	case MEM_CHANNEL3_HI: miMem.Channel3_Addr = (miMem.Channel3_Addr & 0xFFFF) | (_iValue<<16); return;
-	case MEM_CHANNEL3_LO: miMem.Channel3_Addr = (miMem.Channel3_Addr & 0xFFFF0000) | (_iValue); return;
-	case MEM_CHANNEL_CTRL: miMem.Channel_Ctrl = _iValue; return;
-	}
+	Write16(_iValue >> 16, _iAddress);
+	Write16(_iValue & 0xFFFF, _iAddress + 2);
 }
 
 } // end of namespace MemoryInterface
diff --git a/Source/Core/Core/HW/MemoryInterface.h b/Source/Core/Core/HW/MemoryInterface.h
index 2a4fa2a..6dc8c54 100644
--- a/Source/Core/Core/HW/MemoryInterface.h
+++ b/Source/Core/Core/HW/MemoryInterface.h
@@ -6,12 +6,16 @@
 #define _MEMORYINTERFACE_H
 
 #include "Common.h"
+
+namespace MMIO { class Mapping; }
 class PointerWrap;
 
 namespace MemoryInterface
 {
 void DoState(PointerWrap &p);
 
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
+
 void Read16(u16& _uReturnValue, const u32 _iAddress);
 void Read32(u32& _uReturnValue, const u32 _iAddress);
 void Write32(const u32 _iValue, const u32 _iAddress);
diff --git a/Source/Core/Core/HW/ProcessorInterface.cpp b/Source/Core/Core/HW/ProcessorInterface.cpp
index 68b0ce9..49adad2 100644
--- a/Source/Core/Core/HW/ProcessorInterface.cpp
+++ b/Source/Core/Core/HW/ProcessorInterface.cpp
@@ -14,6 +14,7 @@
 #include "ProcessorInterface.h"
 #include "GPFifo.h"
 #include "VideoBackendBase.h"
+#include "MMIO.h"
 
 namespace ProcessorInterface
 {
@@ -90,6 +91,59 @@ void Init()
 	toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback);
 }
 
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
+{
+	mmio->Register(base | PI_INTERRUPT_CAUSE,
+		MMIO::DirectRead<u32>(&m_InterruptCause),
+		MMIO::ComplexWrite<u32>([](u32, u32 val) {
+			Common::AtomicAnd(m_InterruptCause, ~val);
+			UpdateException();
+		})
+	);
+
+	mmio->Register(base | PI_INTERRUPT_MASK,
+		MMIO::DirectRead<u32>(&m_InterruptMask),
+		MMIO::ComplexWrite<u32>([](u32, u32 val) {
+			m_InterruptMask = val;
+			UpdateException();
+		})
+	);
+
+	mmio->Register(base | PI_FIFO_BASE,
+		MMIO::DirectRead<u32>(&Fifo_CPUBase),
+		MMIO::DirectWrite<u32>(&Fifo_CPUBase, 0xFFFFFFE0)
+	);
+
+	mmio->Register(base | PI_FIFO_END,
+		MMIO::DirectRead<u32>(&Fifo_CPUEnd),
+		MMIO::DirectWrite<u32>(&Fifo_CPUEnd, 0xFFFFFFE0)
+	);
+
+	mmio->Register(base | PI_FIFO_WPTR,
+		MMIO::DirectRead<u32>(&Fifo_CPUWritePointer),
+		MMIO::DirectWrite<u32>(&Fifo_CPUWritePointer, 0xFFFFFFE0)
+	);
+
+	mmio->Register(base | PI_FIFO_RESET,
+		MMIO::InvalidRead<u32>(),
+		MMIO::ComplexWrite<u32>([](u32, u32 val) {
+			WARN_LOG(PROCESSORINTERFACE, "Fifo reset (%08x)", val);
+		})
+	);
+
+	mmio->Register(base | PI_RESET_CODE,
+		MMIO::DirectRead<u32>(&m_ResetCode),
+		MMIO::DirectWrite<u32>(&m_ResetCode)
+	);
+
+	mmio->Register(base | PI_FLIPPER_REV,
+		MMIO::DirectRead<u32>(&m_FlipperRev),
+		MMIO::InvalidWrite<u32>()
+	);
+
+	// TODO: register mapping from 16 bit reads to 32 bit + mask/shift.
+}
+
 void Read16(u16& _uReturnValue, const u32 _iAddress)
 {
 	u32 word;
@@ -99,105 +153,14 @@ void Read16(u16& _uReturnValue, const u32 _iAddress)
 
 void Read32(u32& _uReturnValue, const u32 _iAddress)
 {
-	//INFO_LOG(PROCESSORINTERFACE, "(r32) 0x%08x", _iAddress);
-
-	switch(_iAddress & 0xFFF)
-	{
-	case PI_INTERRUPT_CAUSE:
-		_uReturnValue = m_InterruptCause;
-		return;
-
-	case PI_INTERRUPT_MASK:
-		_uReturnValue = m_InterruptMask;
-		return;
-
-	case PI_FIFO_BASE:
-		DEBUG_LOG(PROCESSORINTERFACE, "Read CPU FIFO base, value = %08x", Fifo_CPUBase);
-		_uReturnValue = Fifo_CPUBase;
-		return;
-
-	case PI_FIFO_END:
-		DEBUG_LOG(PROCESSORINTERFACE, "Read CPU FIFO end, value = %08x", Fifo_CPUEnd);
-		_uReturnValue = Fifo_CPUEnd;
-		return;
-
-	case PI_FIFO_WPTR:
-		DEBUG_LOG(PROCESSORINTERFACE, "Read writepointer, value = %08x", Fifo_CPUWritePointer);
-		_uReturnValue = Fifo_CPUWritePointer;  //really writes in 32-byte chunks
-		// Monk's gcube does some crazy align trickery here.
-		return;
-
-	case PI_RESET_CODE:
-		INFO_LOG(PROCESSORINTERFACE, "Read reset code, 0x%08x", m_ResetCode);
-		_uReturnValue = m_ResetCode;
-		return;
-
-	case PI_FLIPPER_REV:
-		INFO_LOG(PROCESSORINTERFACE, "Read flipper rev, 0x%08x", m_FlipperRev);
-		_uReturnValue = m_FlipperRev;
-		return;
-
-	default:
-		ERROR_LOG(PROCESSORINTERFACE, "!!!!Unknown write!!!! 0x%08x", _iAddress);
-		break;
-	}
-
-	_uReturnValue = 0xAFFE0000;
+	// HACK: Remove this function when the new MMIO interface is used.
+	Memory::mmio_mapping->Read(_iAddress, _uReturnValue);
 }
 
 void Write32(const u32 _uValue, const u32 _iAddress)
 {
-	//INFO_LOG(PROCESSORINTERFACE, "(w32) 0x%08x @ 0x%08x", _uValue, _iAddress);
-	switch(_iAddress & 0xFFF)
-	{
-	case PI_INTERRUPT_CAUSE:
-		Common::AtomicAnd(m_InterruptCause, ~_uValue); // writes turn them off
-		UpdateException();
-		return;
-
-	case PI_INTERRUPT_MASK:
-		m_InterruptMask = _uValue;
-		DEBUG_LOG(PROCESSORINTERFACE,"New Interrupt mask: %08x", m_InterruptMask);
-		UpdateException();
-		return;
-
-	case PI_FIFO_BASE:
-		Fifo_CPUBase = _uValue & 0xFFFFFFE0;
-		DEBUG_LOG(PROCESSORINTERFACE,"Fifo base = %08x", _uValue);
-		break;
-
-	case PI_FIFO_END:
-		Fifo_CPUEnd = _uValue & 0xFFFFFFE0;
-		DEBUG_LOG(PROCESSORINTERFACE,"Fifo end = %08x", _uValue);
-		break;
-
-	case PI_FIFO_WPTR:
-		Fifo_CPUWritePointer = _uValue & 0xFFFFFFE0;
-		DEBUG_LOG(PROCESSORINTERFACE,"Fifo writeptr = %08x", _uValue);
-		break;
-
-	case PI_FIFO_RESET:
-		//Abort the actual frame
-		//g_video_backend->Video_AbortFrame();
-		//Fifo_CPUWritePointer = Fifo_CPUBase; ??
-		//PanicAlert("Unknown write to PI_FIFO_RESET (%08x)", _uValue);
-		WARN_LOG(PROCESSORINTERFACE, "Fifo reset (%08x)", _uValue);
-		break;
-
-	case PI_RESET_CODE:
-		DEBUG_LOG(PROCESSORINTERFACE, "Write %08x to PI_RESET_CODE", _uValue);
-		m_ResetCode = _uValue;
-		break;
-
-	case PI_FLIPPER_UNK:
-		DEBUG_LOG(PROCESSORINTERFACE, "Write %08x to unknown PI register %08x", _uValue, _iAddress);
-		break;
-
-	default:
-		ERROR_LOG(PROCESSORINTERFACE,"!!!!Unknown PI write!!!! 0x%08x", _iAddress);
-		PanicAlert("Unknown write to PI: %08x", _iAddress);
-		break;
-	}
+	// HACK: Remove this function when the new MMIO interface is used.
+	Memory::mmio_mapping->Write(_iAddress, _uValue);
 }
 
 void UpdateException()
diff --git a/Source/Core/Core/HW/ProcessorInterface.h b/Source/Core/Core/HW/ProcessorInterface.h
index 7feefa2..29a8750 100644
--- a/Source/Core/Core/HW/ProcessorInterface.h
+++ b/Source/Core/Core/HW/ProcessorInterface.h
@@ -8,6 +8,8 @@
 #include "CommonTypes.h"
 class PointerWrap;
 
+namespace MMIO { class Mapping; }
+
 // Holds statuses of things like the write gatherer used for fifos, and interrupts from various sources
 
 namespace ProcessorInterface
@@ -44,6 +46,8 @@ extern u32 Fifo_CPUWritePointer;
 void Init();
 void DoState(PointerWrap &p);
 
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
+
 void Read16(u16& _uReturnValue, const u32 _iAddress);
 
 void Read32(u32& _uReturnValue, const u32 _iAddress);
diff --git a/Source/Core/Core/HW/VideoInterface.cpp b/Source/Core/Core/HW/VideoInterface.cpp
index d01d19d..b9f5c43 100644
--- a/Source/Core/Core/HW/VideoInterface.cpp
+++ b/Source/Core/Core/HW/VideoInterface.cpp
@@ -13,6 +13,7 @@
 #include "../CoreTiming.h"
 #include "SystemTimers.h"
 #include "StringUtil.h"
+#include "MMIO.h"
 
 #include "VideoBackendBase.h"
 #include "State.h"
@@ -182,6 +183,194 @@ void Init()
 	UpdateParameters();
 }
 
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
+{
+	struct {
+		u32 addr;
+		u16* ptr;
+	} directly_mapped_vars[] = {
+		{ VI_VERTICAL_TIMING, &m_VerticalTimingRegister.Hex },
+		{ VI_HORIZONTAL_TIMING_0_HI, &m_HTiming0.Hi },
+		{ VI_HORIZONTAL_TIMING_0_LO, &m_HTiming0.Lo },
+		{ VI_HORIZONTAL_TIMING_1_HI, &m_HTiming1.Hi },
+		{ VI_HORIZONTAL_TIMING_1_LO, &m_HTiming1.Lo },
+		{ VI_VBLANK_TIMING_ODD_HI, &m_VBlankTimingOdd.Hi },
+		{ VI_VBLANK_TIMING_ODD_LO, &m_VBlankTimingOdd.Lo },
+		{ VI_VBLANK_TIMING_EVEN_HI, &m_VBlankTimingEven.Hi },
+		{ VI_VBLANK_TIMING_EVEN_LO, &m_VBlankTimingEven.Lo },
+		{ VI_BURST_BLANKING_ODD_HI, &m_BurstBlankingOdd.Hi },
+		{ VI_BURST_BLANKING_ODD_LO, &m_BurstBlankingOdd.Lo },
+		{ VI_BURST_BLANKING_EVEN_HI, &m_BurstBlankingEven.Hi },
+		{ VI_BURST_BLANKING_EVEN_LO, &m_BurstBlankingEven.Lo },
+		{ VI_FB_LEFT_TOP_LO, &m_XFBInfoTop.Lo },
+		{ VI_FB_RIGHT_TOP_LO, &m_3DFBInfoTop.Lo },
+		{ VI_FB_LEFT_BOTTOM_LO, &m_XFBInfoBottom.Lo },
+		{ VI_FB_RIGHT_BOTTOM_LO, &m_3DFBInfoBottom.Lo },
+		{ VI_PRERETRACE_LO, &m_InterruptRegister[0].Lo },
+		{ VI_POSTRETRACE_LO, &m_InterruptRegister[1].Lo },
+		{ VI_DISPLAY_INTERRUPT_2_LO, &m_InterruptRegister[2].Lo },
+		{ VI_DISPLAY_INTERRUPT_3_LO, &m_InterruptRegister[3].Lo },
+		{ VI_DISPLAY_LATCH_0_HI, &m_LatchRegister[0].Hi },
+		{ VI_DISPLAY_LATCH_0_LO, &m_LatchRegister[0].Lo },
+		{ VI_DISPLAY_LATCH_1_HI, &m_LatchRegister[1].Hi },
+		{ VI_DISPLAY_LATCH_1_LO, &m_LatchRegister[1].Lo },
+		{ VI_HSCALEW, &m_HorizontalStepping.Hex },
+		{ VI_HSCALER, &m_HorizontalScaling.Hex },
+		{ VI_FILTER_COEF_0_HI, &m_FilterCoefTables.Tables02[0].Hi },
+		{ VI_FILTER_COEF_0_LO, &m_FilterCoefTables.Tables02[0].Lo },
+		{ VI_FILTER_COEF_1_HI, &m_FilterCoefTables.Tables02[1].Hi },
+		{ VI_FILTER_COEF_1_LO, &m_FilterCoefTables.Tables02[1].Lo },
+		{ VI_FILTER_COEF_2_HI, &m_FilterCoefTables.Tables02[2].Hi },
+		{ VI_FILTER_COEF_2_LO, &m_FilterCoefTables.Tables02[2].Lo },
+		{ VI_FILTER_COEF_3_HI, &m_FilterCoefTables.Tables36[0].Hi },
+		{ VI_FILTER_COEF_3_LO, &m_FilterCoefTables.Tables36[0].Lo },
+		{ VI_FILTER_COEF_4_HI, &m_FilterCoefTables.Tables36[1].Hi },
+		{ VI_FILTER_COEF_4_LO, &m_FilterCoefTables.Tables36[1].Lo },
+		{ VI_FILTER_COEF_5_HI, &m_FilterCoefTables.Tables36[2].Hi },
+		{ VI_FILTER_COEF_5_LO, &m_FilterCoefTables.Tables36[2].Lo },
+		{ VI_FILTER_COEF_6_HI, &m_FilterCoefTables.Tables36[3].Hi },
+		{ VI_FILTER_COEF_6_LO, &m_FilterCoefTables.Tables36[3].Lo },
+		{ VI_CLOCK, &m_Clock },
+		{ VI_DTV_STATUS, &m_DTVStatus.Hex },
+		{ VI_FBWIDTH, &m_FBWidth },
+		{ VI_BORDER_BLANK_END, &m_BorderHBlank.Lo },
+		{ VI_BORDER_BLANK_START, &m_BorderHBlank.Hi },
+	};
+
+	// Declare all the boilerplate direct MMIOs.
+	for (auto& mapped_var : directly_mapped_vars)
+	{
+		mmio->Register(base | mapped_var.addr,
+			MMIO::DirectRead<u16>(mapped_var.ptr),
+			MMIO::DirectWrite<u16>(mapped_var.ptr)
+		);
+	}
+
+	// XFB related MMIOs that require special handling on writes.
+	mmio->Register(base | VI_FB_LEFT_TOP_HI,
+		MMIO::DirectRead<u16>(&m_XFBInfoTop.Hi),
+		MMIO::ComplexWrite<u16>([](u32, u16 val) {
+			m_XFBInfoTop.Hi = val;
+			if (m_XFBInfoTop.CLRPOFF) m_XFBInfoTop.POFF = 0;
+		})
+	);
+	mmio->Register(base | VI_FB_LEFT_BOTTOM_HI,
+		MMIO::DirectRead<u16>(&m_XFBInfoBottom.Hi),
+		MMIO::ComplexWrite<u16>([](u32, u16 val) {
+			m_XFBInfoBottom.Hi = val;
+			if (m_XFBInfoBottom.CLRPOFF) m_XFBInfoBottom.POFF = 0;
+		})
+	);
+	mmio->Register(base | VI_FB_RIGHT_TOP_HI,
+		MMIO::DirectRead<u16>(&m_3DFBInfoTop.Hi),
+		MMIO::ComplexWrite<u16>([](u32, u16 val) {
+			m_3DFBInfoTop.Hi = val;
+			if (m_3DFBInfoTop.CLRPOFF) m_3DFBInfoTop.POFF = 0;
+		})
+	);
+	mmio->Register(base | VI_FB_RIGHT_BOTTOM_HI,
+		MMIO::DirectRead<u16>(&m_3DFBInfoBottom.Hi),
+		MMIO::ComplexWrite<u16>([](u32, u16 val) {
+			m_3DFBInfoBottom.Hi = val;
+			if (m_3DFBInfoBottom.CLRPOFF) m_3DFBInfoBottom.POFF = 0;
+		})
+	);
+
+	// MMIOs with unimplemented writes that trigger warnings.
+	mmio->Register(base | VI_VERTICAL_BEAM_POSITION,
+		MMIO::DirectRead<u16>(&m_VBeamPos),
+		MMIO::ComplexWrite<u16>([](u32, u16 val) {
+			WARN_LOG(VIDEOINTERFACE, "Changing vertical beam position to 0x%04x - not documented or implemented yet", val);
+		})
+	);
+	mmio->Register(base | VI_HORIZONTAL_BEAM_POSITION,
+		MMIO::DirectRead<u16>(&m_HBeamPos),
+		MMIO::ComplexWrite<u16>([](u32, u16 val) {
+			WARN_LOG(VIDEOINTERFACE, "Changing horizontal beam position to 0x%04x - not documented or implemented yet", val);
+		})
+	);
+
+	// The following MMIOs are interrupts related and update interrupt status
+	// on writes.
+	mmio->Register(base | VI_PRERETRACE_HI,
+		MMIO::DirectRead<u16>(&m_InterruptRegister[0].Hi),
+		MMIO::ComplexWrite<u16>([](u32, u16 val) {
+			m_InterruptRegister[0].Hi = val;
+			UpdateInterrupts();
+		})
+	);
+	mmio->Register(base | VI_POSTRETRACE_HI,
+		MMIO::DirectRead<u16>(&m_InterruptRegister[1].Hi),
+		MMIO::ComplexWrite<u16>([](u32, u16 val) {
+			m_InterruptRegister[1].Hi = val;
+			UpdateInterrupts();
+		})
+	);
+	mmio->Register(base | VI_DISPLAY_INTERRUPT_2_HI,
+		MMIO::DirectRead<u16>(&m_InterruptRegister[2].Hi),
+		MMIO::ComplexWrite<u16>([](u32, u16 val) {
+			m_InterruptRegister[2].Hi = val;
+			UpdateInterrupts();
+		})
+	);
+	mmio->Register(base | VI_DISPLAY_INTERRUPT_3_HI,
+		MMIO::DirectRead<u16>(&m_InterruptRegister[3].Hi),
+		MMIO::ComplexWrite<u16>([](u32, u16 val) {
+			m_InterruptRegister[3].Hi = val;
+			UpdateInterrupts();
+		})
+	);
+
+	// Unknown anti-aliasing related MMIO register: puts a warning on log and
+	// needs to shift/mask when reading/writing.
+	mmio->Register(base | VI_UNK_AA_REG_HI,
+		MMIO::ComplexRead<u16>([](u32) {
+			return m_UnkAARegister >> 16;
+		}),
+		MMIO::ComplexWrite<u16>([](u32, u16 val) {
+			m_UnkAARegister = (m_UnkAARegister & 0x0000FFFF) | ((u32)val << 16);
+			WARN_LOG(VIDEOINTERFACE, "Writing to the unknown AA register (hi)");
+		})
+	);
+	mmio->Register(base | VI_UNK_AA_REG_LO,
+		MMIO::ComplexRead<u16>([](u32) {
+			return m_UnkAARegister & 0xFFFF;
+		}),
+		MMIO::ComplexWrite<u16>([](u32, u16 val) {
+			m_UnkAARegister = (m_UnkAARegister & 0xFFFF0000) | val;
+			WARN_LOG(VIDEOINTERFACE, "Writing to the unknown AA register (lo)");
+		})
+	);
+
+	// Control register writes only updates some select bits, and additional
+	// processing needs to be done if a reset is requested.
+	mmio->Register(base | VI_CONTROL_REGISTER,
+		MMIO::DirectRead<u16>(&m_DisplayControlRegister.Hex),
+		MMIO::ComplexWrite<u16>([](u32, u16 val) {
+			UVIDisplayControlRegister tmpConfig(val);
+			m_DisplayControlRegister.ENB = tmpConfig.ENB;
+			m_DisplayControlRegister.NIN = tmpConfig.NIN;
+			m_DisplayControlRegister.DLR = tmpConfig.DLR;
+			m_DisplayControlRegister.LE0 = tmpConfig.LE0;
+			m_DisplayControlRegister.LE1 = tmpConfig.LE1;
+			m_DisplayControlRegister.FMT = tmpConfig.FMT;
+
+			if (tmpConfig.RST)
+			{
+				// shuffle2 clear all data, reset to default vals, and enter idle mode
+				m_DisplayControlRegister.RST = 0;
+				for (int i = 0; i < 4; i++)
+					m_InterruptRegister[i].Hex = 0;
+				UpdateInterrupts();
+			}
+
+			UpdateParameters();
+		})
+	);
+
+	// TODO: register mapping from 8/32 bit reads/writes to 16 bit.
+}
+
 void SetRegionReg(char region)
 {
 	if (!Core::g_CoreStartupParameter.bForceNTSCJ)
@@ -210,472 +399,14 @@ void Read8(u8& _uReturnValue, const u32 _iAddress)
 
 void Read16(u16& _uReturnValue, const u32 _iAddress)
 {
-	switch (_iAddress & 0xFFF)
-	{
-	case VI_VERTICAL_TIMING:
-		_uReturnValue = m_VerticalTimingRegister.Hex;
-		return;
-
-	case VI_CONTROL_REGISTER:
-		_uReturnValue = m_DisplayControlRegister.Hex;
-		return;
-
-	case VI_HORIZONTAL_TIMING_0_HI:
-		_uReturnValue = m_HTiming0.Hi;
-		break;
-	case VI_HORIZONTAL_TIMING_0_LO:
-		_uReturnValue = m_HTiming0.Lo;
-		break;
-
-	case VI_HORIZONTAL_TIMING_1_HI:
-		_uReturnValue = m_HTiming1.Hi;
-		break;
-	case VI_HORIZONTAL_TIMING_1_LO:
-		_uReturnValue = m_HTiming1.Lo;
-		break;
-
-	case VI_VBLANK_TIMING_ODD_HI:
-		_uReturnValue = m_VBlankTimingOdd.Hi;
-		break;
-	case VI_VBLANK_TIMING_ODD_LO:
-		_uReturnValue = m_VBlankTimingOdd.Lo;
-		break;
-
-	case VI_VBLANK_TIMING_EVEN_HI:
-		_uReturnValue = m_VBlankTimingEven.Hi;
-		break;
-	case VI_VBLANK_TIMING_EVEN_LO:
-		_uReturnValue = m_VBlankTimingEven.Lo;
-		break;
-
-	case VI_BURST_BLANKING_ODD_HI:
-		_uReturnValue = m_BurstBlankingOdd.Hi;
-		break;
-	case VI_BURST_BLANKING_ODD_LO:
-		_uReturnValue = m_BurstBlankingOdd.Lo;
-		break;
-
-	case VI_BURST_BLANKING_EVEN_HI:
-		_uReturnValue = m_BurstBlankingEven.Hi;
-		break;
-	case VI_BURST_BLANKING_EVEN_LO:
-		_uReturnValue = m_BurstBlankingEven.Lo;
-		break;
-
-	case VI_FB_LEFT_TOP_HI:
-		_uReturnValue = m_XFBInfoTop.Hi;
-		break;
-	case VI_FB_LEFT_TOP_LO:
-		_uReturnValue = m_XFBInfoTop.Lo;
-		break;
-
-	case VI_FB_RIGHT_TOP_HI:
-		_uReturnValue = m_3DFBInfoTop.Hi;
-		break;
-	case VI_FB_RIGHT_TOP_LO:
-		_uReturnValue = m_3DFBInfoTop.Lo;
-		break;
-
-	case VI_FB_LEFT_BOTTOM_HI:
-		_uReturnValue = m_XFBInfoBottom.Hi;
-		break;
-	case VI_FB_LEFT_BOTTOM_LO:
-		_uReturnValue = m_XFBInfoBottom.Lo;
-		break;
-
-	case VI_FB_RIGHT_BOTTOM_HI:
-		_uReturnValue = m_3DFBInfoBottom.Hi;
-		break;
-	case VI_FB_RIGHT_BOTTOM_LO:
-		_uReturnValue = m_3DFBInfoBottom.Lo;
-		break;
-
-	case VI_VERTICAL_BEAM_POSITION:
-		_uReturnValue = m_VBeamPos;
-		return;
-
-	case VI_HORIZONTAL_BEAM_POSITION:
-		_uReturnValue = m_HBeamPos;
-		return;
-
-	// RETRACE STUFF ...
-	case VI_PRERETRACE_HI:
-		_uReturnValue =	m_InterruptRegister[0].Hi;
-		return;
-	case VI_PRERETRACE_LO:
-		_uReturnValue =	m_InterruptRegister[0].Lo;
-		return;
-
-	case VI_POSTRETRACE_HI:
-		_uReturnValue =	m_InterruptRegister[1].Hi;
-		return;
-	case VI_POSTRETRACE_LO:
-		_uReturnValue =	m_InterruptRegister[1].Lo;
-		return;
-
-	case VI_DISPLAY_INTERRUPT_2_HI:
-		_uReturnValue =	m_InterruptRegister[2].Hi;
-		return;
-	case VI_DISPLAY_INTERRUPT_2_LO:
-		_uReturnValue =	m_InterruptRegister[2].Lo;
-		return;
-
-	case VI_DISPLAY_INTERRUPT_3_HI:
-		_uReturnValue =	m_InterruptRegister[3].Hi;
-		return;
-	case VI_DISPLAY_INTERRUPT_3_LO:
-		_uReturnValue =	m_InterruptRegister[3].Lo;
-		return;
-
-	case VI_DISPLAY_LATCH_0_HI:
-		_uReturnValue = m_LatchRegister[0].Hi;
-		break;
-	case VI_DISPLAY_LATCH_0_LO:
-		_uReturnValue = m_LatchRegister[0].Lo;
-		break;
-
-	case VI_DISPLAY_LATCH_1_HI:
-		_uReturnValue = m_LatchRegister[1].Hi;
-		break;
-	case VI_DISPLAY_LATCH_1_LO:
-		_uReturnValue = m_LatchRegister[1].Lo;
-		break;
-
-	case VI_HSCALEW:
-		_uReturnValue = m_HorizontalStepping.Hex;
-		break;
-
-	case VI_HSCALER:
-		_uReturnValue = m_HorizontalScaling.Hex;
-		break;
-
-	case VI_FILTER_COEF_0_HI:
-		_uReturnValue = m_FilterCoefTables.Tables02[0].Hi;
-		break;
-	case VI_FILTER_COEF_0_LO:
-		_uReturnValue = m_FilterCoefTables.Tables02[0].Lo;
-		break;
-	case VI_FILTER_COEF_1_HI:
-		_uReturnValue = m_FilterCoefTables.Tables02[1].Hi;
-		break;
-	case VI_FILTER_COEF_1_LO:
-		_uReturnValue = m_FilterCoefTables.Tables02[1].Lo;
-		break;
-	case VI_FILTER_COEF_2_HI:
-		_uReturnValue = m_FilterCoefTables.Tables02[2].Hi;
-		break;
-	case VI_FILTER_COEF_2_LO:
-		_uReturnValue = m_FilterCoefTables.Tables02[2].Lo;
-		break;
-	case VI_FILTER_COEF_3_HI:
-		_uReturnValue = m_FilterCoefTables.Tables36[0].Hi;
-		break;
-	case VI_FILTER_COEF_3_LO:
-		_uReturnValue = m_FilterCoefTables.Tables36[0].Lo;
-		break;
-	case VI_FILTER_COEF_4_HI:
-		_uReturnValue = m_FilterCoefTables.Tables36[1].Hi;
-		break;
-	case VI_FILTER_COEF_4_LO:
-		_uReturnValue = m_FilterCoefTables.Tables36[1].Lo;
-		break;
-	case VI_FILTER_COEF_5_HI:
-		_uReturnValue = m_FilterCoefTables.Tables36[2].Hi;
-		break;
-	case VI_FILTER_COEF_5_LO:
-		_uReturnValue = m_FilterCoefTables.Tables36[2].Lo;
-		break;
-	case VI_FILTER_COEF_6_HI:
-		_uReturnValue = m_FilterCoefTables.Tables36[3].Hi;
-		break;
-	case VI_FILTER_COEF_6_LO:
-		_uReturnValue = m_FilterCoefTables.Tables36[3].Lo;
-		break;
-
-	case VI_UNK_AA_REG_HI:
-		_uReturnValue = (m_UnkAARegister & 0xffff0000) >> 16;
-		WARN_LOG(VIDEOINTERFACE, "(r16) unknown AA register, not sure what it does :)");
-		break;
-	case VI_UNK_AA_REG_LO:
-		_uReturnValue = m_UnkAARegister & 0x0000ffff;
-		WARN_LOG(VIDEOINTERFACE, "(r16) unknown AA register, not sure what it does :)");
-		break;
-
-	case VI_CLOCK:
-		_uReturnValue = m_Clock;
-		break;
-
-	case VI_DTV_STATUS:
-		_uReturnValue = m_DTVStatus.Hex;
-		break;
-
-	case VI_FBWIDTH:
-		_uReturnValue = m_FBWidth;
-		break;
-
-	case VI_BORDER_BLANK_END:
-		_uReturnValue = m_BorderHBlank.Lo;
-		break;
-	case VI_BORDER_BLANK_START:
-		_uReturnValue = m_BorderHBlank.Hi;
-		break;
-
-	default:
-		ERROR_LOG(VIDEOINTERFACE, "(r16) unknown reg %x", _iAddress & 0xfff);
-		_uReturnValue = 0x0;
-		break;
-	}
-
-	DEBUG_LOG(VIDEOINTERFACE, "(r16): 0x%04x, 0x%08x", _uReturnValue, _iAddress);
+	// HACK: Remove this function when the new MMIO interface is used.
+	Memory::mmio_mapping->Read(_iAddress, _uReturnValue);
 }
 
 void Write16(const u16 _iValue, const u32 _iAddress)
 {
-	DEBUG_LOG(VIDEOINTERFACE, "(w16): 0x%04x, 0x%08x",_iValue,_iAddress);
-
-	//Somewhere it sets screen width.. we need to communicate this to the gfx backend...
-
-	switch (_iAddress & 0xFFF)
-	{
-	case VI_VERTICAL_TIMING:
-		m_VerticalTimingRegister.Hex = _iValue;
-		break;
-
-	case VI_CONTROL_REGISTER:
-		{
-			UVIDisplayControlRegister tmpConfig(_iValue);
-			m_DisplayControlRegister.ENB = tmpConfig.ENB;
-			m_DisplayControlRegister.NIN = tmpConfig.NIN;
-			m_DisplayControlRegister.DLR = tmpConfig.DLR;
-			m_DisplayControlRegister.LE0 = tmpConfig.LE0;
-			m_DisplayControlRegister.LE1 = tmpConfig.LE1;
-			m_DisplayControlRegister.FMT = tmpConfig.FMT;
-
-			if (tmpConfig.RST)
-			{
-				// shuffle2 clear all data, reset to default vals, and enter idle mode
-				m_DisplayControlRegister.RST = 0;
-				for (int i = 0; i < 4; i++)
-					m_InterruptRegister[i].Hex = 0;
-				UpdateInterrupts();
-			}
-
-			UpdateParameters();
-		}
-		break;
-
-	case VI_HORIZONTAL_TIMING_0_HI:
-		m_HTiming0.Hi = _iValue;
-		break;
-	case VI_HORIZONTAL_TIMING_0_LO:
-		m_HTiming0.Lo = _iValue;
-		break;
-
-	case VI_HORIZONTAL_TIMING_1_HI:
-		m_HTiming1.Hi = _iValue;
-		break;
-	case VI_HORIZONTAL_TIMING_1_LO:
-		m_HTiming1.Lo = _iValue;
-		break;
-
-	case VI_VBLANK_TIMING_ODD_HI:
-		m_VBlankTimingOdd.Hi = _iValue;
-		break;
-	case VI_VBLANK_TIMING_ODD_LO:
-		m_VBlankTimingOdd.Lo = _iValue;
-		break;
-
-	case VI_VBLANK_TIMING_EVEN_HI:
-		m_VBlankTimingEven.Hi = _iValue;
-		break;
-	case VI_VBLANK_TIMING_EVEN_LO:
-		m_VBlankTimingEven.Lo = _iValue;
-		break;
-
-	case VI_BURST_BLANKING_ODD_HI:
-		m_BurstBlankingOdd.Hi = _iValue;
-		break;
-	case VI_BURST_BLANKING_ODD_LO:
-		m_BurstBlankingOdd.Lo = _iValue;
-		break;
-
-	case VI_BURST_BLANKING_EVEN_HI:
-		m_BurstBlankingEven.Hi = _iValue;
-		break;
-	case VI_BURST_BLANKING_EVEN_LO:
-		m_BurstBlankingEven.Lo = _iValue;
-		break;
-
-	case VI_FB_LEFT_TOP_HI:
-		m_XFBInfoTop.Hi = _iValue;
-		if (m_XFBInfoTop.CLRPOFF) m_XFBInfoTop.POFF = 0;
-		break;
-	case VI_FB_LEFT_TOP_LO:
-		m_XFBInfoTop.Lo = _iValue;
-		break;
-
-	case VI_FB_RIGHT_TOP_HI:
-		m_3DFBInfoTop.Hi = _iValue;
-		if (m_3DFBInfoTop.CLRPOFF) m_3DFBInfoTop.POFF = 0;
-		break;
-	case VI_FB_RIGHT_TOP_LO:
-		m_3DFBInfoTop.Lo = _iValue;
-		break;
-
-	case VI_FB_LEFT_BOTTOM_HI:
-		m_XFBInfoBottom.Hi = _iValue;
-		if (m_XFBInfoBottom.CLRPOFF) m_XFBInfoBottom.POFF = 0;
-		break;
-	case VI_FB_LEFT_BOTTOM_LO:
-		m_XFBInfoBottom.Lo = _iValue;
-		break;
-
-	case VI_FB_RIGHT_BOTTOM_HI:
-		m_3DFBInfoBottom.Hi = _iValue;
-		if (m_3DFBInfoBottom.CLRPOFF) m_3DFBInfoBottom.POFF = 0;
-		break;
-	case VI_FB_RIGHT_BOTTOM_LO:
-		m_3DFBInfoBottom.Lo = _iValue;
-		break;
-
-	case VI_VERTICAL_BEAM_POSITION:
-		WARN_LOG(VIDEOINTERFACE, "Change Vertical Beam Position to 0x%04x - Not documented or implemented", _iValue);
-		break;
-
-	case VI_HORIZONTAL_BEAM_POSITION:
-		WARN_LOG(VIDEOINTERFACE, "Change Horizontal Beam Position to 0x%04x - Not documented or implemented", _iValue);
-		break;
-
-		// RETRACE STUFF ...
-	case VI_PRERETRACE_HI:
-		m_InterruptRegister[0].Hi = _iValue;
-		UpdateInterrupts();
-		break;
-	case VI_PRERETRACE_LO:
-		m_InterruptRegister[0].Lo = _iValue;
-		break;
-
-	case VI_POSTRETRACE_HI:
-		m_InterruptRegister[1].Hi = _iValue;
-		UpdateInterrupts();
-		break;
-	case VI_POSTRETRACE_LO:
-		m_InterruptRegister[1].Lo = _iValue;
-		break;
-
-	case VI_DISPLAY_INTERRUPT_2_HI:
-		m_InterruptRegister[2].Hi = _iValue;
-		UpdateInterrupts();
-		break;
-	case VI_DISPLAY_INTERRUPT_2_LO:
-		m_InterruptRegister[2].Lo = _iValue;
-		break;
-
-	case VI_DISPLAY_INTERRUPT_3_HI:
-		m_InterruptRegister[3].Hi = _iValue;
-		UpdateInterrupts();
-		break;
-	case VI_DISPLAY_INTERRUPT_3_LO:
-		m_InterruptRegister[3].Lo = _iValue;
-		break;
-
-	case VI_DISPLAY_LATCH_0_HI:
-		m_LatchRegister[0].Hi = _iValue;
-		break;
-	case VI_DISPLAY_LATCH_0_LO:
-		m_LatchRegister[0].Lo = _iValue;
-		break;
-
-	case VI_DISPLAY_LATCH_1_HI:
-		m_LatchRegister[1].Hi = _iValue;
-		break;
-	case VI_DISPLAY_LATCH_1_LO:
-		m_LatchRegister[1].Lo = _iValue;
-		break;
-
-	case VI_HSCALEW:
-		m_HorizontalStepping.Hex = _iValue;
-		break;
-
-	case VI_HSCALER:
-		m_HorizontalScaling.Hex = _iValue;
-		break;
-
-	case VI_FILTER_COEF_0_HI:
-		m_FilterCoefTables.Tables02[0].Hi = _iValue;
-		break;
-	case VI_FILTER_COEF_0_LO:
-		m_FilterCoefTables.Tables02[0].Lo = _iValue;
-		break;
-	case VI_FILTER_COEF_1_HI:
-		m_FilterCoefTables.Tables02[1].Hi = _iValue;
-		break;
-	case VI_FILTER_COEF_1_LO:
-		m_FilterCoefTables.Tables02[1].Lo = _iValue;
-		break;
-	case VI_FILTER_COEF_2_HI:
-		m_FilterCoefTables.Tables02[2].Hi = _iValue;
-		break;
-	case VI_FILTER_COEF_2_LO:
-		m_FilterCoefTables.Tables02[2].Lo = _iValue;
-		break;
-	case VI_FILTER_COEF_3_HI:
-		m_FilterCoefTables.Tables36[0].Hi = _iValue;
-		break;
-	case VI_FILTER_COEF_3_LO:
-		m_FilterCoefTables.Tables36[0].Lo = _iValue;
-		break;
-	case VI_FILTER_COEF_4_HI:
-		m_FilterCoefTables.Tables36[1].Hi = _iValue;
-		break;
-	case VI_FILTER_COEF_4_LO:
-		m_FilterCoefTables.Tables36[1].Lo = _iValue;
-		break;
-	case VI_FILTER_COEF_5_HI:
-		m_FilterCoefTables.Tables36[2].Hi = _iValue;
-		break;
-	case VI_FILTER_COEF_5_LO:
-		m_FilterCoefTables.Tables36[2].Lo = _iValue;
-		break;
-	case VI_FILTER_COEF_6_HI:
-		m_FilterCoefTables.Tables36[3].Hi = _iValue;
-		break;
-	case VI_FILTER_COEF_6_LO:
-		m_FilterCoefTables.Tables36[3].Lo = _iValue;
-		break;
-
-	case VI_UNK_AA_REG_HI:
-		m_UnkAARegister = (m_UnkAARegister & 0x0000ffff) | (u32)(_iValue << 16);
-		WARN_LOG(VIDEOINTERFACE, "(w16) to unknown AA register, not sure what it does :)");
-		break;
-	case VI_UNK_AA_REG_LO:
-		m_UnkAARegister = (m_UnkAARegister & 0xffff0000) | _iValue;
-		WARN_LOG(VIDEOINTERFACE, "(w16) to unknown AA register, not sure what it does :)");
-		break;
-
-	case VI_CLOCK:
-		m_Clock = _iValue;
-		break;
-
-	case VI_DTV_STATUS:
-		m_DTVStatus.Hex = _iValue;
-		break;
-
-	case VI_FBWIDTH:
-		m_FBWidth = _iValue;
-		break;
-
-	case VI_BORDER_BLANK_END:
-		m_BorderHBlank.Lo = _iValue;
-		break;
-	case VI_BORDER_BLANK_START:
-		m_BorderHBlank.Hi = _iValue;
-		break;
-
-	default:
-		ERROR_LOG(VIDEOINTERFACE, "(w16) %04x to unknown register %x", _iValue, _iAddress & 0xfff);
-		break;
-	}
+	// HACK: Remove this function when the new MMIO interface is used.
+	Memory::mmio_mapping->Write(_iAddress, _iValue);
 }
 
 void Read32(u32& _uReturnValue, const u32 _iAddress)
diff --git a/Source/Core/Core/HW/VideoInterface.h b/Source/Core/Core/HW/VideoInterface.h
index aaaedf5..c9216a3 100644
--- a/Source/Core/Core/HW/VideoInterface.h
+++ b/Source/Core/Core/HW/VideoInterface.h
@@ -6,7 +6,9 @@
 #define _VIDEOINTERFACE_H
 
 #include "CommonTypes.h"
+
 class PointerWrap;
+namespace MMIO { class Mapping; }
 
 namespace VideoInterface
 {
@@ -325,6 +327,8 @@ union UVIDTVStatus
 	void SetRegionReg(char region);
 	void DoState(PointerWrap &p);
 
+	void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
+
 	void Read8(u8& _uReturnValue, const u32 _uAddress);
 	void Read16(u16& _uReturnValue, const u32 _uAddress);
 	void Read32(u32& _uReturnValue, const u32 _uAddress);
diff --git a/Source/Core/VideoBackends/Software/SWCommandProcessor.h b/Source/Core/VideoBackends/Software/SWCommandProcessor.h
index 51cde8d..c2381a2 100644
--- a/Source/Core/VideoBackends/Software/SWCommandProcessor.h
+++ b/Source/Core/VideoBackends/Software/SWCommandProcessor.h
@@ -98,14 +98,15 @@ namespace SWCommandProcessor
 		UCPStatusReg status;    // 0x00
 		UCPCtrlReg ctrl;        // 0x02
 		UCPClearReg clear;      // 0x04
-		u32 unk0;               // 0x06
-		u32 unk1;               // 0x0a
+		u32 unk0;               // 0x08
+		u16 unk1;               // 0x0c
 		u16 token;              // 0x0e
 		u16 bboxleft;           // 0x10
 		u16 bboxtop;            // 0x12
 		u16 bboxright;          // 0x14
 		u16 bboxbottom;         // 0x16
-		u16 unk2;               // 0x18
+		u32 unk2;               // 0x18
+		u32 unk3;               // 0x1c
 		u32 fifobase;           // 0x20
 		u32 fifoend;            // 0x24
 		u32 hiwatermark;        // 0x28
diff --git a/Tools/buildbot-try.sh b/Tools/buildbot-try.sh
index b34db15..6cc85db 100755
--- a/Tools/buildbot-try.sh
+++ b/Tools/buildbot-try.sh
@@ -17,7 +17,7 @@ if ! which buildbot >/dev/null 2>&1; then
     exit 1
 fi
 
-if ! git branch | grep -q '^* '; then
+if true || ! git branch | grep -q '^* '; then
     echo "Unable to determine the current Git branch. Input the Git branch name:"
     read branchname
 else