diff --git a/Source/Core/Common/Log.h b/Source/Core/Common/Log.h
index 3284daabf8..544245e847 100644
--- a/Source/Core/Common/Log.h
+++ b/Source/Core/Common/Log.h
@@ -47,7 +47,6 @@ enum LOG_TYPE
STREAMINGINTERFACE,
VIDEO,
VIDEOINTERFACE,
- WII_IOB,
WII_IPC,
WII_IPC_DVD,
WII_IPC_ES,
diff --git a/Source/Core/Common/LogManager.cpp b/Source/Core/Common/LogManager.cpp
index c73624c68c..246c0e404d 100644
--- a/Source/Core/Common/LogManager.cpp
+++ b/Source/Core/Common/LogManager.cpp
@@ -60,7 +60,6 @@ LogManager::LogManager()
m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console");
m_Log[LogTypes::OSREPORT] = new LogContainer("OSREPORT", "OSReport");
m_Log[LogTypes::WIIMOTE] = new LogContainer("Wiimote", "Wiimote");
- m_Log[LogTypes::WII_IOB] = new LogContainer("WII_IOB", "WII IO Bridge");
m_Log[LogTypes::WII_IPC] = new LogContainer("WII_IPC", "WII IPC");
m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID");
m_Log[LogTypes::WII_IPC_HLE] = new LogContainer("WII_IPC_HLE", "WII IPC HLE");
diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt
index 504b8ada47..71f9a8eb7b 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
@@ -119,7 +120,6 @@ set(SRCS ActionReplay.cpp
HW/StreamADPCM.cpp
HW/SystemTimers.cpp
HW/VideoInterface.cpp
- HW/WII_IOB.cpp
HW/WII_IPC.cpp
HW/Wiimote.cpp
HW/WiimoteEmu/WiimoteEmu.cpp
diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj
index 69b9bda339..c5d85d6bf3 100644
--- a/Source/Core/Core/Core.vcxproj
+++ b/Source/Core/Core/Core.vcxproj
@@ -145,6 +145,7 @@
+
@@ -170,7 +171,6 @@
-
@@ -342,6 +342,8 @@
+
+
@@ -368,7 +370,6 @@
-
diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters
index 40c890398e..6471169406 100644
--- a/Source/Core/Core/Core.vcxproj.filters
+++ b/Source/Core/Core/Core.vcxproj.filters
@@ -492,9 +492,6 @@
HW %28Flipper/Hollywood%29\Wiimote\Real
-
- HW %28Flipper/Hollywood%29\Wii IO Bridge
-
HW %28Flipper/Hollywood%29\Wii IPC
@@ -510,6 +507,9 @@
HW %28Flipper/Hollywood%29
+
+ HW %28Flipper/Hollywood%29
+
HW %28Flipper/Hollywood%29
@@ -1022,9 +1022,6 @@
HW %28Flipper/Hollywood%29\Wiimote\Real
-
- HW %28Flipper/Hollywood%29\Wii IO Bridge
-
HW %28Flipper/Hollywood%29\Wii IPC
@@ -1037,6 +1034,12 @@
HW %28Flipper/Hollywood%29
+
+ HW %28Flipper/Hollywood%29
+
+
+ HW %28Flipper/Hollywood%29
+
HW %28Flipper/Hollywood%29
@@ -1219,4 +1222,4 @@
-
\ No newline at end of file
+
diff --git a/Source/Core/Core/HW/AudioInterface.cpp b/Source/Core/Core/HW/AudioInterface.cpp
index 70a4dd099c..b103f7a5f9 100644
--- a/Source/Core/Core/HW/AudioInterface.cpp
+++ b/Source/Core/Core/HW/AudioInterface.cpp
@@ -62,6 +62,7 @@ This file mainly deals with the [Drive I/F], however [AIDFR] controls
#include "../PowerPC/PowerPC.h"
#include "../CoreTiming.h"
#include "SystemTimers.h"
+#include "MMIO.h"
namespace AudioInterface
{
@@ -170,43 +171,12 @@ void Shutdown()
{
}
-void Read32(u32& _rReturnValue, const u32 _Address)
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
{
- switch (_Address & 0xFFFF)
- {
- case AI_CONTROL_REGISTER:
- _rReturnValue = m_Control.hex;
- break;
-
- case AI_VOLUME_REGISTER:
- _rReturnValue = m_Volume.hex;
- break;
-
- case AI_SAMPLE_COUNTER:
- Update(0, 0);
- _rReturnValue = m_SampleCounter;
- break;
-
- case AI_INTERRUPT_TIMING:
- _rReturnValue = m_InterruptTiming;
- break;
-
- default:
- ERROR_LOG(AUDIO_INTERFACE, "Unknown read 0x%08x", _Address);
- _dbg_assert_msg_(AUDIO_INTERFACE, 0, "AudioInterface - Read from 0x%08x", _Address);
- _rReturnValue = 0;
- return;
- }
- DEBUG_LOG(AUDIO_INTERFACE, "r32 %08x %08x", _Address, _rReturnValue);
-}
-
-void Write32(const u32 _Value, const u32 _Address)
-{
- switch (_Address & 0xFFFF)
- {
- case AI_CONTROL_REGISTER:
- {
- AICR tmpAICtrl(_Value);
+ mmio->Register(base | AI_CONTROL_REGISTER,
+ MMIO::DirectRead(&m_Control.hex),
+ MMIO::ComplexWrite([](u32, u32 val) {
+ AICR tmpAICtrl(val);
m_Control.AIINTMSK = tmpAICtrl.AIINTMSK;
m_Control.AIINTVLD = tmpAICtrl.AIINTVLD;
@@ -260,32 +230,30 @@ void Write32(const u32 _Value, const u32 _Address)
}
UpdateInterrupts();
- }
- break;
+ })
+ );
- case AI_VOLUME_REGISTER:
- m_Volume.hex = _Value;
- DEBUG_LOG(AUDIO_INTERFACE, "Set volume: left(%02x) right(%02x)", m_Volume.left, m_Volume.right);
- break;
+ mmio->Register(base | AI_VOLUME_REGISTER,
+ MMIO::DirectRead(&m_Volume.hex),
+ MMIO::DirectWrite(&m_Volume.hex)
+ );
- case AI_SAMPLE_COUNTER:
- // Why was this commented out? Does something do this?
- _dbg_assert_msg_(AUDIO_INTERFACE, 0, "AIS - sample counter is read only");
- m_SampleCounter = _Value;
- break;
+ mmio->Register(base | AI_SAMPLE_COUNTER,
+ MMIO::ComplexRead([](u32) {
+ Update(0, 0);
+ return m_SampleCounter;
+ }),
+ MMIO::DirectWrite(&m_SampleCounter)
+ );
- case AI_INTERRUPT_TIMING:
- m_InterruptTiming = _Value;
- CoreTiming::RemoveEvent(et_AI);
- CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2), et_AI);
- DEBUG_LOG(AUDIO_INTERFACE, "Set interrupt: %08x samples", m_InterruptTiming);
- break;
-
- default:
- ERROR_LOG(AUDIO_INTERFACE, "Unknown write %08x @ %08x", _Value, _Address);
- _dbg_assert_msg_(AUDIO_INTERFACE,0,"AIS - Write %08x to %08x", _Value, _Address);
- break;
- }
+ mmio->Register(base | AI_INTERRUPT_TIMING,
+ MMIO::DirectRead(&m_InterruptTiming),
+ MMIO::ComplexWrite([](u32, u32 val) {
+ m_InterruptTiming = val;
+ CoreTiming::RemoveEvent(et_AI);
+ CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2), et_AI);
+ })
+ );
}
static void UpdateInterrupts()
diff --git a/Source/Core/Core/HW/AudioInterface.h b/Source/Core/Core/HW/AudioInterface.h
index 25086b3df4..e79d1c3fc7 100644
--- a/Source/Core/Core/HW/AudioInterface.h
+++ b/Source/Core/Core/HW/AudioInterface.h
@@ -9,6 +9,7 @@
#include "CommonTypes.h"
class PointerWrap;
+namespace MMIO { class Mapping; }
namespace AudioInterface
{
@@ -17,15 +18,14 @@ void Init();
void Shutdown();
void DoState(PointerWrap &p);
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
+
void Update(u64 userdata, int cyclesLate);
// Called by DSP emulator
void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DACSampleRate);
unsigned int Callback_GetStreaming(short* _pDestBuffer, unsigned int _numSamples, unsigned int _sampleRate = 48000);
-void Read32(u32& _uReturnValue, const u32 _iAddress);
-void Write32(const u32 _iValue, const u32 _iAddress);
-
// Get the audio rates (48000 or 32000 only)
unsigned int GetAIDSampleRate();
diff --git a/Source/Core/Core/HW/DSP.cpp b/Source/Core/Core/HW/DSP.cpp
index 821ba69925..9ab9d3fa49 100644
--- a/Source/Core/Core/HW/DSP.cpp
+++ b/Source/Core/Core/HW/DSP.cpp
@@ -30,6 +30,7 @@
#include "CPU.h"
#include "MemoryUtil.h"
#include "Memmap.h"
+#include "MMIO.h"
#include "ProcessorInterface.h"
#include "AudioInterface.h"
#include "../PowerPC/PowerPC.h"
@@ -287,124 +288,89 @@ void Shutdown()
dsp_emulator = NULL;
}
-void Read16(u16& _uReturnValue, const u32 _iAddress)
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
{
- switch (_iAddress & 0xFFFF)
+ // Declare all the boilerplate direct MMIOs.
+ struct {
+ u32 addr;
+ u16* ptr;
+ bool align_writes_on_32_bytes;
+ } directly_mapped_vars[] = {
+ { AR_INFO, &g_ARAM_Info.Hex },
+ { AR_MODE, &g_AR_MODE },
+ { AR_REFRESH, &g_AR_REFRESH },
+ { AR_DMA_MMADDR_H, MMIO::Utils::HighPart(&g_arDMA.MMAddr) },
+ { AR_DMA_MMADDR_L, MMIO::Utils::LowPart(&g_arDMA.MMAddr), true },
+ { AR_DMA_ARADDR_H, MMIO::Utils::HighPart(&g_arDMA.ARAddr) },
+ { AR_DMA_ARADDR_L, MMIO::Utils::LowPart(&g_arDMA.ARAddr), true },
+ { AR_DMA_CNT_H, MMIO::Utils::HighPart(&g_arDMA.Cnt.Hex) },
+ // AR_DMA_CNT_L triggers DMA
+ { AUDIO_DMA_START_HI, MMIO::Utils::HighPart(&g_audioDMA.SourceAddress) },
+ { AUDIO_DMA_START_LO, MMIO::Utils::LowPart(&g_audioDMA.SourceAddress) },
+ };
+ for (auto& mapped_var : directly_mapped_vars)
{
- // DSP
- case DSP_MAIL_TO_DSP_HI:
- if (dsp_slice > DSP_MAIL_SLICE && dsp_is_lle) {
- dsp_emulator->DSP_Update(DSP_MAIL_SLICE);
- dsp_slice -= DSP_MAIL_SLICE;
- }
- _uReturnValue = dsp_emulator->DSP_ReadMailBoxHigh(true);
- break;
-
- case DSP_MAIL_TO_DSP_LO:
- _uReturnValue = dsp_emulator->DSP_ReadMailBoxLow(true);
- break;
-
- case DSP_MAIL_FROM_DSP_HI:
- if (dsp_slice > DSP_MAIL_SLICE && dsp_is_lle) {
- dsp_emulator->DSP_Update(DSP_MAIL_SLICE);
- dsp_slice -= DSP_MAIL_SLICE;
- }
- _uReturnValue = dsp_emulator->DSP_ReadMailBoxHigh(false);
- break;
-
- case DSP_MAIL_FROM_DSP_LO:
- _uReturnValue = dsp_emulator->DSP_ReadMailBoxLow(false);
- break;
-
- case DSP_CONTROL:
- _uReturnValue = (g_dspState.DSPControl.Hex & ~DSP_CONTROL_MASK) |
- (dsp_emulator->DSP_ReadControlRegister() & DSP_CONTROL_MASK);
- break;
-
- // ARAM
- case AR_INFO:
- //PanicAlert("Read %x %x", g_ARAM_Info.Hex,PowerPC::ppcState.pc);
- _uReturnValue = g_ARAM_Info.Hex;
- break;
-
- case AR_MODE:
- _uReturnValue = g_AR_MODE;
- break;
-
- case AR_REFRESH:
- _uReturnValue = g_AR_REFRESH;
- break;
-
- case AR_DMA_MMADDR_H: _uReturnValue = g_arDMA.MMAddr >> 16; return;
- case AR_DMA_MMADDR_L: _uReturnValue = g_arDMA.MMAddr & 0xFFFF; return;
- case AR_DMA_ARADDR_H: _uReturnValue = g_arDMA.ARAddr >> 16; return;
- case AR_DMA_ARADDR_L: _uReturnValue = g_arDMA.ARAddr & 0xFFFF; return;
- case AR_DMA_CNT_H: _uReturnValue = g_arDMA.Cnt.Hex >> 16; return;
- case AR_DMA_CNT_L: _uReturnValue = g_arDMA.Cnt.Hex & 0xFFFF; return;
-
- // AI
- case AUDIO_DMA_BLOCKS_LEFT:
- _uReturnValue = g_audioDMA.BlocksLeft > 0 ? g_audioDMA.BlocksLeft - 1 : 0; // AUDIO_DMA_BLOCKS_LEFT is zero based
- break;
-
- case AUDIO_DMA_START_LO:
- _uReturnValue = g_audioDMA.SourceAddress & 0xFFFF;
- break;
-
- case AUDIO_DMA_START_HI:
- _uReturnValue = g_audioDMA.SourceAddress >> 16;
- break;
-
- case AUDIO_DMA_CONTROL_LEN:
- _uReturnValue = g_audioDMA.AudioDMAControl.Hex;
- break;
-
- default:
- _uReturnValue = 0;
- _dbg_assert_(DSPINTERFACE,0);
- break;
+ u16 write_mask = mapped_var.align_writes_on_32_bytes ? 0xFFE0 : 0xFFFF;
+ mmio->Register(base | mapped_var.addr,
+ MMIO::DirectRead(mapped_var.ptr),
+ MMIO::DirectWrite(mapped_var.ptr, write_mask)
+ );
}
- if (_iAddress != (0xCC000000 + DSP_MAIL_FROM_DSP_HI))
- {
- DEBUG_LOG(DSPINTERFACE, "DSPInterface(r16) 0x%08x (0x%04x) (%08x)", _iAddress, _uReturnValue, PowerPC::ppcState.pc);
- }
-}
+ // DSP mail MMIOs call DSP emulator functions to get results or write data.
+ mmio->Register(base | DSP_MAIL_TO_DSP_HI,
+ MMIO::ComplexRead([](u32) {
+ if (dsp_slice > DSP_MAIL_SLICE && dsp_is_lle)
+ {
+ dsp_emulator->DSP_Update(DSP_MAIL_SLICE);
+ dsp_slice -= DSP_MAIL_SLICE;
+ }
+ return dsp_emulator->DSP_ReadMailBoxHigh(true);
+ }),
+ MMIO::ComplexWrite([](u32, u16 val) {
+ dsp_emulator->DSP_WriteMailBoxHigh(true, val);
+ })
+ );
+ mmio->Register(base | DSP_MAIL_TO_DSP_LO,
+ MMIO::ComplexRead([](u32) {
+ return dsp_emulator->DSP_ReadMailBoxLow(true);
+ }),
+ MMIO::ComplexWrite([](u32, u16 val) {
+ dsp_emulator->DSP_WriteMailBoxLow(true, val);
+ })
+ );
+ mmio->Register(base | DSP_MAIL_FROM_DSP_HI,
+ MMIO::ComplexRead([](u32) {
+ if (dsp_slice > DSP_MAIL_SLICE && dsp_is_lle)
+ {
+ dsp_emulator->DSP_Update(DSP_MAIL_SLICE);
+ dsp_slice -= DSP_MAIL_SLICE;
+ }
+ return dsp_emulator->DSP_ReadMailBoxHigh(false);
+ }),
+ MMIO::InvalidWrite()
+ );
+ mmio->Register(base | DSP_MAIL_FROM_DSP_LO,
+ MMIO::ComplexRead([](u32) {
+ return dsp_emulator->DSP_ReadMailBoxLow(false);
+ }),
+ MMIO::InvalidWrite()
+ );
-void Write16(const u16 _Value, const u32 _Address)
-{
- DEBUG_LOG(DSPINTERFACE, "DSPInterface(w16) 0x%08x (0x%04x) (%08x)", _Address, _Value, PowerPC::ppcState.pc);
-
- switch (_Address & 0xFFFF)
- {
- // DSP
- case DSP_MAIL_TO_DSP_HI:
- dsp_emulator->DSP_WriteMailBoxHigh(true, _Value);
- break;
-
- case DSP_MAIL_TO_DSP_LO:
- dsp_emulator->DSP_WriteMailBoxLow(true, _Value);
- break;
-
- case DSP_MAIL_FROM_DSP_HI:
- _dbg_assert_msg_(DSPINTERFACE, 0, "W16: DSP_MAIL_FROM_DSP_HI");
- break;
-
- case DSP_MAIL_FROM_DSP_LO:
- _dbg_assert_msg_(DSPINTERFACE, 0, "W16: DSP_MAIL_FROM_DSP_LO");
- break;
-
- // Control Register
- case DSP_CONTROL:
- {
+ mmio->Register(base | DSP_CONTROL,
+ MMIO::ComplexRead([](u32) {
+ return (g_dspState.DSPControl.Hex & ~DSP_CONTROL_MASK) |
+ (dsp_emulator->DSP_ReadControlRegister() & DSP_CONTROL_MASK);
+ }),
+ MMIO::ComplexWrite([](u32, u16 val) {
UDSPControl tmpControl;
- tmpControl.Hex = (_Value & ~DSP_CONTROL_MASK) |
- (dsp_emulator->DSP_WriteControlRegister(_Value) & DSP_CONTROL_MASK);
+ tmpControl.Hex = (val & ~DSP_CONTROL_MASK) |
+ (dsp_emulator->DSP_WriteControlRegister(val) & DSP_CONTROL_MASK);
// Not really sure if this is correct, but it works...
// Kind of a hack because DSP_CONTROL_MASK should make this bit
// only viewable to dsp emulator
- if (_Value & 1 /*DSPReset*/)
+ if (val & 1 /*DSPReset*/)
{
g_audioDMA.AudioDMAControl.Hex = 0;
}
@@ -430,180 +396,51 @@ void Write16(const u16 _Value, const u32 _Address)
g_dspState.DSPControl.pad = tmpControl.pad;
if (g_dspState.DSPControl.pad != 0)
{
- PanicAlert("DSPInterface (w) g_dspState.DSPControl (CC00500A) gets a value with junk in the padding %08x", _Value);
+ PanicAlert("DSPInterface (w) g_dspState.DSPControl (CC00500A) gets a value with junk in the padding %08x", val);
}
UpdateInterrupts();
- }
- break;
+ })
+ );
- // ARAM
- // DMA back and forth between ARAM and RAM
- case AR_INFO:
- //PanicAlert("AR_INFO %x PC: %x", _Value, PowerPC::ppcState.pc);
- ERROR_LOG(DSPINTERFACE, "AR_INFO %x PC: %x", _Value, PowerPC::ppcState.pc);
- g_ARAM_Info.Hex = _Value;
+ // ARAM MMIO controlling the DMA start.
+ mmio->Register(base | AR_DMA_CNT_L,
+ MMIO::DirectRead(MMIO::Utils::LowPart(&g_arDMA.Cnt.Hex)),
+ MMIO::ComplexWrite([](u32, u16 val) {
+ g_arDMA.Cnt.Hex = (g_arDMA.Cnt.Hex & 0xFFFF0000) | (val & ~31);
+ Do_ARAM_DMA();
+ })
+ );
- // 0x43
- // Monster Hunter Tri, DKCR
+ // Audio DMA MMIO controlling the DMA start.
+ mmio->Register(base | AUDIO_DMA_CONTROL_LEN,
+ MMIO::DirectRead(&g_audioDMA.AudioDMAControl.Hex),
+ MMIO::ComplexWrite([](u32, u16 val) {
+ g_audioDMA.AudioDMAControl.Hex = val;
+ g_audioDMA.ReadAddress = g_audioDMA.SourceAddress;
+ g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks;
+ })
+ );
- // 0x43, 0x63:
- // Rebel Strike, Clone Wars, WWE DOR2, Mario Golf
+ // Audio DMA blocks remaining is invalid to write to, and requires logic on
+ // the read side.
+ mmio->Register(base | AUDIO_DMA_BLOCKS_LEFT,
+ MMIO::ComplexRead([](u32) {
+ return (g_audioDMA.BlocksLeft > 0 ? g_audioDMA.BlocksLeft - 1 : 0);
+ }),
+ MMIO::InvalidWrite()
+ );
- // 0x43, 0x64, 0x63
- // Transworld Surf, Smashing Drive, SSBM, Cel Damage
-
- // __OSInitAudioSystem sets to 0x43 -> expects 16bit adressing and mapping to dsp iram?
- // __OSCheckSize sets = 0x20 | 3 (keeps upper bits)
- // 0x23 -> Zelda standard mode (standard ARAM access ??)
- // 0x43 -> Set by __OSInitAudioSystem
- // 0x58 -> Transworld Surf, Cel Damage, SSBM
- // 0x60 -> Transworld Surf, Cel Damage, SSBM
- // 0x63 -> ARCheckSize Mode (access AR-registers ??) or no exception ??
- // 0x64 -> Transworld Surf, Cel Damage, SSBM
-
- // 0x00 -> Switch to external ARAM
- // 0x04 -> Switch to internal ARAM
- break;
-
- case AR_MODE:
- g_AR_MODE = _Value;
- break;
-
- case AR_REFRESH:
- // 0x9c -> Set by Eternal Darkness
- g_AR_REFRESH = _Value;
- break;
-
- case AR_DMA_MMADDR_H:
- g_arDMA.MMAddr = (g_arDMA.MMAddr & 0xFFFF) | (_Value<<16);
- break;
-
- case AR_DMA_MMADDR_L:
- // Align MMAddr to the 32 byte boundary. Verified on real HW
- g_arDMA.MMAddr = ((g_arDMA.MMAddr & 0xFFFF0000) | (_Value)) & ~31;
- break;
-
- case AR_DMA_ARADDR_H:
- g_arDMA.ARAddr = (g_arDMA.ARAddr & 0xFFFF) | (_Value<<16);
- break;
-
- case AR_DMA_ARADDR_L:
- // Align ARAddr to the 32 byte boundary. Verified on real HW
- g_arDMA.ARAddr = ((g_arDMA.ARAddr & 0xFFFF0000) | (_Value)) & ~31;
- break;
-
- case AR_DMA_CNT_H:
- g_arDMA.Cnt.Hex = (g_arDMA.Cnt.Hex & 0xFFFF) | (_Value<<16);
- break;
-
- case AR_DMA_CNT_L:
- // Align count to the 32 byte boundary. Verified on real HW
- g_arDMA.Cnt.Hex = ((g_arDMA.Cnt.Hex & 0xFFFF0000) | (_Value)) & ~31;
- Do_ARAM_DMA();
- break;
-
- // AI
- // This is the DMA that goes straight out the speaker.
- case AUDIO_DMA_START_HI:
- g_audioDMA.SourceAddress = (g_audioDMA.SourceAddress & 0xFFFF) | (_Value<<16);
- break;
-
- case AUDIO_DMA_START_LO:
- g_audioDMA.SourceAddress = (g_audioDMA.SourceAddress & 0xFFFF0000) | (_Value);
- break;
-
- case AUDIO_DMA_CONTROL_LEN: // called by AIStartDMA()
- g_audioDMA.AudioDMAControl.Hex = _Value;
- g_audioDMA.ReadAddress = g_audioDMA.SourceAddress;
- g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks;
- INFO_LOG(DSPINTERFACE, "AID DMA started - source address %08x, length %i blocks", g_audioDMA.SourceAddress, g_audioDMA.AudioDMAControl.NumBlocks);
- break;
-
- case AUDIO_DMA_BLOCKS_LEFT:
- _dbg_assert_(DSPINTERFACE,0);
- break;
-
- default:
- _dbg_assert_(DSPINTERFACE,0);
- break;
- }
-}
-
-void Read32(u32& _uReturnValue, const u32 _iAddress)
-{
- INFO_LOG(DSPINTERFACE, "DSPInterface(r32) 0x%08x", _iAddress);
-
- switch (_iAddress & 0xFFFF)
+ // 32 bit reads/writes are a combination of two 16 bit accesses.
+ for (int i = 0; i < 0x1000; i += 4)
{
- // DSP
- case DSP_MAIL_TO_DSP_HI:
- _uReturnValue = (dsp_emulator->DSP_ReadMailBoxHigh(true) << 16) | dsp_emulator->DSP_ReadMailBoxLow(true);
- break;
-
- // AI
- case AUDIO_DMA_START_HI:
- _uReturnValue = g_audioDMA.SourceAddress;
- break;
-
- // ARAM
- case AR_DMA_ARADDR_H:
- _uReturnValue = g_arDMA.ARAddr;
- break;
-
- case AR_DMA_CNT_H:
- _uReturnValue = g_arDMA.Cnt.Hex;
- break;
-
- case AR_DMA_MMADDR_H:
- _uReturnValue = g_arDMA.MMAddr;
- break;
-
- default:
- _uReturnValue = 0;
- _dbg_assert_(DSPINTERFACE,0);
- break;
+ mmio->Register(base | i,
+ MMIO::ReadToSmaller(mmio, base | i, base | (i + 2)),
+ MMIO::WriteToSmaller(mmio, base | i, base | (i + 2))
+ );
}
}
-void Write32(const u32 _iValue, const u32 _iAddress)
-{
- INFO_LOG(DSPINTERFACE, "DSPInterface(w32) 0x%08x 0x%08x", _iValue, _iAddress);
-
- switch (_iAddress & 0xFFFF)
- {
- // DSP
- case DSP_MAIL_TO_DSP_HI:
- dsp_emulator->DSP_WriteMailBoxHigh(true, _iValue >> 16);
- dsp_emulator->DSP_WriteMailBoxLow(true, (u16)_iValue);
- break;
-
- // AI
- case AUDIO_DMA_START_HI:
- g_audioDMA.SourceAddress = _iValue;
- break;
-
- // ARAM
- case AR_DMA_MMADDR_H:
- g_arDMA.MMAddr = _iValue & ~31;
- break;
-
- case AR_DMA_ARADDR_H:
- g_arDMA.ARAddr = _iValue & ~31;
- break;
-
- case AR_DMA_CNT_H:
- g_arDMA.Cnt.Hex = _iValue & ~31;
- Do_ARAM_DMA();
- break;
-
- default:
- _dbg_assert_(DSPINTERFACE,0);
- break;
- }
-}
-
-
// UpdateInterrupts
void UpdateInterrupts()
{
diff --git a/Source/Core/Core/HW/DSP.h b/Source/Core/Core/HW/DSP.h
index 002207c842..5d6b192538 100644
--- a/Source/Core/Core/HW/DSP.h
+++ b/Source/Core/Core/HW/DSP.h
@@ -7,6 +7,7 @@
#include "Common.h"
class PointerWrap;
class DSPEmulator;
+namespace MMIO { class Mapping; }
namespace DSP
{
@@ -28,6 +29,8 @@ enum
void Init(bool hle);
void Shutdown();
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
+
DSPEmulator *GetDSPEmulator();
void DoState(PointerWrap &p);
@@ -35,14 +38,6 @@ void DoState(PointerWrap &p);
void GenerateDSPInterrupt(DSPInterruptType _DSPInterruptType, bool _bSet = true);
void GenerateDSPInterruptFromDSPEmu(DSPInterruptType _DSPInterruptType, bool _bSet = true);
-// Read32
-void Read16(u16& _uReturnValue, const u32 _uAddress);
-void Read32(u32& _uReturnValue, const u32 _uAddress);
-
-// Write
-void Write16(const u16 _uValue, const u32 _uAddress);
-void Write32(const u32 _uValue, const u32 _uAddress);
-
// Audio/DSP Helper
u8 ReadARAM(const u32 _uAddress);
void WriteARAM(u8 value, u32 _uAddress);
diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp
index 90e2bbf2eb..90b5f66c3a 100644
--- a/Source/Core/Core/HW/DVDInterface.cpp
+++ b/Source/Core/Core/HW/DVDInterface.cpp
@@ -17,6 +17,7 @@
#include "../VolumeHandler.h"
#include "AudioInterface.h"
#include "../Movie.h"
+#include "MMIO.h"
// Disc transfer rate measured in bytes per second
static const u32 DISC_TRANSFER_RATE_GC = 5 * 1024 * 1024;
@@ -404,38 +405,12 @@ bool DVDReadADPCM(u8* _pDestBuffer, u32 _iNumSamples)
}
}
-void Read32(u32& _uReturnValue, const u32 _iAddress)
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
{
- switch (_iAddress & 0xFF)
- {
- case DI_STATUS_REGISTER: _uReturnValue = m_DISR.Hex; break;
- case DI_COVER_REGISTER: _uReturnValue = m_DICVR.Hex; break;
- case DI_COMMAND_0: _uReturnValue = m_DICMDBUF[0].Hex; break;
- case DI_COMMAND_1: _uReturnValue = m_DICMDBUF[1].Hex; break;
- case DI_COMMAND_2: _uReturnValue = m_DICMDBUF[2].Hex; break;
- case DI_DMA_ADDRESS_REGISTER: _uReturnValue = m_DIMAR.Hex; break;
- case DI_DMA_LENGTH_REGISTER: _uReturnValue = m_DILENGTH.Hex; break;
- case DI_DMA_CONTROL_REGISTER: _uReturnValue = m_DICR.Hex; break;
- case DI_IMMEDIATE_DATA_BUFFER: _uReturnValue = m_DIIMMBUF.Hex; break;
- case DI_CONFIG_REGISTER: _uReturnValue = m_DICFG.Hex; break;
-
- default:
- _dbg_assert_(DVDINTERFACE, 0);
- _uReturnValue = 0;
- break;
- }
- DEBUG_LOG(DVDINTERFACE, "(r32): 0x%08x - 0x%08x", _iAddress, _uReturnValue);
-}
-
-void Write32(const u32 _iValue, const u32 _iAddress)
-{
- DEBUG_LOG(DVDINTERFACE, "(w32): 0x%08x @ 0x%08x", _iValue, _iAddress);
-
- switch (_iAddress & 0xFF)
- {
- case DI_STATUS_REGISTER:
- {
- UDISR tmpStatusReg(_iValue);
+ mmio->Register(base | DI_STATUS_REGISTER,
+ MMIO::DirectRead(&m_DISR.Hex),
+ MMIO::ComplexWrite([](u32, u32 val) {
+ UDISR tmpStatusReg(val);
m_DISR.DEINITMASK = tmpStatusReg.DEINITMASK;
m_DISR.TCINTMASK = tmpStatusReg.TCINTMASK;
@@ -457,12 +432,13 @@ void Write32(const u32 _iValue, const u32 _iAddress)
}
UpdateInterrupts();
- }
- break;
+ })
+ );
- case DI_COVER_REGISTER:
- {
- UDICVR tmpCoverReg(_iValue);
+ mmio->Register(base | DI_COVER_REGISTER,
+ MMIO::DirectRead(&m_DICVR.Hex),
+ MMIO::ComplexWrite([](u32, u32 val) {
+ UDICVR tmpCoverReg(val);
m_DICVR.CVRINTMASK = tmpCoverReg.CVRINTMASK;
@@ -470,26 +446,32 @@ void Write32(const u32 _iValue, const u32 _iAddress)
m_DICVR.CVRINT = 0;
UpdateInterrupts();
- }
- break;
+ })
+ );
- case DI_COMMAND_0: m_DICMDBUF[0].Hex = _iValue; break;
- case DI_COMMAND_1: m_DICMDBUF[1].Hex = _iValue; break;
- case DI_COMMAND_2: m_DICMDBUF[2].Hex = _iValue; break;
+ // Command registers are very similar and we can register them with a
+ // simple loop.
+ for (int i = 0; i < 3; ++i)
+ mmio->Register(base | (DI_COMMAND_0 + 4 * i),
+ MMIO::DirectRead(&m_DICMDBUF[i].Hex),
+ MMIO::DirectWrite(&m_DICMDBUF[i].Hex)
+ );
- case DI_DMA_ADDRESS_REGISTER:
- {
- m_DIMAR.Hex = _iValue & ~0xfc00001f;
- }
- break;
- case DI_DMA_LENGTH_REGISTER:
- {
- m_DILENGTH.Hex = _iValue & ~0x1f;
- }
- break;
- case DI_DMA_CONTROL_REGISTER:
- {
- m_DICR.Hex = _iValue & 7;
+ // DMA related registers. Mostly direct accesses (+ masking for writes to
+ // handle things like address alignment) and complex write on the DMA
+ // control register that will trigger the DMA.
+ mmio->Register(base | DI_DMA_ADDRESS_REGISTER,
+ MMIO::DirectRead(&m_DIMAR.Hex),
+ MMIO::DirectWrite(&m_DIMAR.Hex, ~0xFC00001F)
+ );
+ mmio->Register(base | DI_DMA_LENGTH_REGISTER,
+ MMIO::DirectRead(&m_DILENGTH.Hex),
+ MMIO::DirectWrite(&m_DILENGTH.Hex, ~0x1F)
+ );
+ mmio->Register(base | DI_DMA_CONTROL_REGISTER,
+ MMIO::DirectRead(&m_DICR.Hex),
+ MMIO::ComplexWrite([](u32, u32 val) {
+ m_DICR.Hex = val & 7;
if (m_DICR.TSTART)
{
if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed)
@@ -504,21 +486,19 @@ void Write32(const u32 _iValue, const u32 _iAddress)
ExecuteCommand(m_DICR);
}
}
- }
- break;
+ })
+ );
- case DI_IMMEDIATE_DATA_BUFFER: m_DIIMMBUF.Hex = _iValue; break;
+ mmio->Register(base | DI_IMMEDIATE_DATA_BUFFER,
+ MMIO::DirectRead(&m_DIIMMBUF.Hex),
+ MMIO::DirectWrite(&m_DIIMMBUF.Hex)
+ );
- case DI_CONFIG_REGISTER:
- {
- WARN_LOG(DVDINTERFACE, "Write to DICFG, ignored as it's read-only");
- }
- break;
-
- default:
- _dbg_assert_msg_(DVDINTERFACE, 0, "Write to unknown DI address 0x%08x", _iAddress);
- break;
- }
+ // DI config register is read only.
+ mmio->Register(base | DI_CONFIG_REGISTER,
+ MMIO::DirectRead(&m_DICFG.Hex),
+ MMIO::InvalidWrite()
+ );
}
void UpdateInterrupts()
diff --git a/Source/Core/Core/HW/DVDInterface.h b/Source/Core/Core/HW/DVDInterface.h
index a51a474604..ede1dc6bdc 100644
--- a/Source/Core/Core/HW/DVDInterface.h
+++ b/Source/Core/Core/HW/DVDInterface.h
@@ -6,6 +6,7 @@
#include "CommonTypes.h"
class PointerWrap;
+namespace MMIO { class Mapping; }
namespace DVDInterface
{
@@ -14,6 +15,8 @@ void Init();
void Shutdown();
void DoState(PointerWrap &p);
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
+
// Disc detection and swapping
void SetDiscInside(bool _DiscInside);
bool IsDiscInside();
@@ -32,12 +35,6 @@ bool DVDRead(u32 _iDVDOffset, u32 _iRamAddress, u32 _iLength);
bool DVDReadADPCM(u8* _pDestBuffer, u32 _iNumSamples);
extern bool g_bStream;
-// Read32
-void Read32(u32& _uReturnValue, const u32 _iAddress);
-
-// Write32
-void Write32(const u32 _iValue, const u32 _iAddress);
-
// Not sure about endianness here. I'll just name them like this...
enum DIErrorLow
diff --git a/Source/Core/Core/HW/EXI.cpp b/Source/Core/Core/HW/EXI.cpp
index 4e9c9057ef..fed520fc11 100644
--- a/Source/Core/Core/HW/EXI.cpp
+++ b/Source/Core/Core/HW/EXI.cpp
@@ -9,6 +9,7 @@
#include "ProcessorInterface.h"
#include "../PowerPC/PowerPC.h"
+#include "MMIO.h"
#include "EXI.h"
#include "Sram.h"
@@ -62,6 +63,19 @@ void PauseAndLock(bool doLock, bool unpauseOnUnlock)
channel->PauseAndLock(doLock, unpauseOnUnlock);
}
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
+{
+ for (int i = 0; i < MAX_EXI_CHANNELS; ++i)
+ {
+ _dbg_assert_(EXPANSIONINTERFACE, g_Channels[i] != nullptr);
+ // Each channel has 5 32 bit registers assigned to it. We offset the
+ // base that we give to each channel for registration.
+ //
+ // Be careful: this means the base is no longer aligned on a page
+ // boundary and using "base | FOO" is not valid!
+ g_Channels[i]->RegisterMMIO(mmio, base + 5 * 4 * i);
+ }
+}
void ChangeDeviceCallback(u64 userdata, int cyclesLate)
{
@@ -99,38 +113,6 @@ void Update()
g_Channels[2]->Update();
}
-void Read32(u32& _uReturnValue, const u32 _iAddress)
-{
- // TODO 0xfff00000 is mapped to EXI -> mapped to first MB of maskrom
- u32 iAddr = _iAddress & 0x3FF;
- u32 iRegister = (iAddr >> 2) % 5;
- u32 iChannel = (iAddr >> 2) / 5;
-
- _dbg_assert_(EXPANSIONINTERFACE, iChannel < MAX_EXI_CHANNELS);
-
- if (iChannel < MAX_EXI_CHANNELS)
- {
- g_Channels[iChannel]->Read32(_uReturnValue, iRegister);
- }
- else
- {
- _uReturnValue = 0;
- }
-}
-
-void Write32(const u32 _iValue, const u32 _iAddress)
-{
- // TODO 0xfff00000 is mapped to EXI -> mapped to first MB of maskrom
- u32 iAddr = _iAddress & 0x3FF;
- u32 iRegister = (iAddr >> 2) % 5;
- u32 iChannel = (iAddr >> 2) / 5;
-
- _dbg_assert_(EXPANSIONINTERFACE, iChannel < MAX_EXI_CHANNELS);
-
- if (iChannel < MAX_EXI_CHANNELS)
- g_Channels[iChannel]->Write32(_iValue, iRegister);
-}
-
void UpdateInterrupts()
{
// Interrupts are mapped a bit strangely:
diff --git a/Source/Core/Core/HW/EXI.h b/Source/Core/Core/HW/EXI.h
index b60dbda105..f35ed1a191 100644
--- a/Source/Core/Core/HW/EXI.h
+++ b/Source/Core/Core/HW/EXI.h
@@ -8,6 +8,7 @@
#include "EXI_Channel.h"
#include "Thread.h"
class PointerWrap;
+namespace MMIO { class Mapping; }
enum
{
@@ -22,6 +23,8 @@ void Shutdown();
void DoState(PointerWrap &p);
void PauseAndLock(bool doLock, bool unpauseOnUnlock);
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
+
void Update();
void UpdateInterrupts();
@@ -29,7 +32,4 @@ void ChangeDeviceCallback(u64 userdata, int cyclesLate);
void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num);
IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex=-1);
-void Read32(u32& _uReturnValue, const u32 _iAddress);
-void Write32(const u32 _iValue, const u32 _iAddress);
-
} // end of namespace ExpansionInterface
diff --git a/Source/Core/Core/HW/EXI_Channel.cpp b/Source/Core/Core/HW/EXI_Channel.cpp
index a57b06914c..52fd79fa3c 100644
--- a/Source/Core/Core/HW/EXI_Channel.cpp
+++ b/Source/Core/Core/HW/EXI_Channel.cpp
@@ -7,6 +7,7 @@
#include "EXI.h"
#include "../ConfigManager.h"
#include "../Movie.h"
+#include "MMIO.h"
#define EXI_READ 0
#define EXI_WRITE 1
@@ -41,6 +42,117 @@ CEXIChannel::~CEXIChannel()
RemoveDevices();
}
+void CEXIChannel::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
+{
+ // Warning: the base is not aligned on a page boundary here. We can't use |
+ // to select a register address, instead we need to use +.
+
+ mmio->Register(base + EXI_STATUS,
+ MMIO::ComplexRead([this](u32) {
+ // check if external device is present
+ // pretty sure it is memcard only, not entirely sure
+ if (m_ChannelId == 2)
+ {
+ m_Status.EXT = 0;
+ }
+ else
+ {
+ m_Status.EXT = GetDevice(1)->IsPresent() ? 1 : 0;
+ }
+
+ return m_Status.Hex;
+ }),
+ MMIO::ComplexWrite([this](u32, u32 val) {
+ UEXI_STATUS newStatus(val);
+
+ m_Status.EXIINTMASK = newStatus.EXIINTMASK;
+ if (newStatus.EXIINT)
+ m_Status.EXIINT = 0;
+
+ m_Status.TCINTMASK = newStatus.TCINTMASK;
+ if (newStatus.TCINT)
+ m_Status.TCINT = 0;
+
+ m_Status.CLK = newStatus.CLK;
+
+ if (m_ChannelId == 0 || m_ChannelId == 1)
+ {
+ m_Status.EXTINTMASK = newStatus.EXTINTMASK;
+
+ if (newStatus.EXTINT)
+ m_Status.EXTINT = 0;
+ }
+
+ if (m_ChannelId == 0)
+ m_Status.ROMDIS = newStatus.ROMDIS;
+
+ IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT ^ newStatus.CHIP_SELECT);
+ m_Status.CHIP_SELECT = newStatus.CHIP_SELECT;
+ if (pDevice != NULL)
+ pDevice->SetCS(m_Status.CHIP_SELECT);
+
+ CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0);
+ })
+ );
+
+ mmio->Register(base + EXI_DMAADDR,
+ MMIO::DirectRead(&m_DMAMemoryAddress),
+ MMIO::DirectWrite(&m_DMAMemoryAddress)
+ );
+ mmio->Register(base + EXI_DMALENGTH,
+ MMIO::DirectRead(&m_DMALength),
+ MMIO::DirectWrite(&m_DMALength)
+ );
+ mmio->Register(base + EXI_DMACONTROL,
+ MMIO::DirectRead(&m_Control.Hex),
+ MMIO::ComplexWrite([this](u32, u32 val) {
+ m_Control.Hex = val;
+
+ if (m_Control.TSTART)
+ {
+ IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT);
+ if (pDevice == NULL)
+ return;
+
+ if (m_Control.DMA == 0)
+ {
+ // immediate data
+ switch (m_Control.RW)
+ {
+ case EXI_READ: m_ImmData = pDevice->ImmRead(m_Control.TLEN + 1); break;
+ case EXI_WRITE: pDevice->ImmWrite(m_ImmData, m_Control.TLEN + 1); break;
+ case EXI_READWRITE: pDevice->ImmReadWrite(m_ImmData, m_Control.TLEN + 1); break;
+ default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI Imm: Unknown transfer type %i", m_Control.RW);
+ }
+ m_Control.TSTART = 0;
+ }
+ else
+ {
+ // DMA
+ switch (m_Control.RW)
+ {
+ case EXI_READ: pDevice->DMARead (m_DMAMemoryAddress, m_DMALength); break;
+ case EXI_WRITE: pDevice->DMAWrite(m_DMAMemoryAddress, m_DMALength); break;
+ default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI DMA: Unknown transfer type %i", m_Control.RW);
+ }
+ m_Control.TSTART = 0;
+ }
+
+ if(!m_Control.TSTART) // completed !
+ {
+ m_Status.TCINT = 1;
+ CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0);
+ }
+ }
+ })
+ );
+
+ mmio->Register(base + EXI_IMMDATA,
+ MMIO::DirectRead(&m_ImmData),
+ MMIO::DirectWrite(&m_ImmData)
+ );
+}
+
void CEXIChannel::RemoveDevices()
{
for (auto& device : m_pDevices)
@@ -115,152 +227,6 @@ void CEXIChannel::Update()
device->Update();
}
-void CEXIChannel::Read32(u32& _uReturnValue, const u32 _iRegister)
-{
- switch (_iRegister)
- {
- case EXI_STATUS:
- {
- // check if external device is present
- // pretty sure it is memcard only, not entirely sure
- if (m_ChannelId == 2)
- {
- m_Status.EXT = 0;
- }
- else
- {
- m_Status.EXT = GetDevice(1)->IsPresent() ? 1 : 0;
- }
-
- _uReturnValue = m_Status.Hex;
- break;
- }
-
- case EXI_DMAADDR:
- _uReturnValue = m_DMAMemoryAddress;
- break;
-
- case EXI_DMALENGTH:
- _uReturnValue = m_DMALength;
- break;
-
- case EXI_DMACONTROL:
- _uReturnValue = m_Control.Hex;
- break;
-
- case EXI_IMMDATA:
- _uReturnValue = m_ImmData;
- break;
-
- default:
- _dbg_assert_(EXPANSIONINTERFACE, 0);
- _uReturnValue = 0xDEADBEEF;
- }
-
- DEBUG_LOG(EXPANSIONINTERFACE, "(r32) 0x%08x channel: %i register: %s",
- _uReturnValue, m_ChannelId, Debug_GetRegisterName(_iRegister));
-}
-
-void CEXIChannel::Write32(const u32 _iValue, const u32 _iRegister)
-{
- DEBUG_LOG(EXPANSIONINTERFACE, "(w32) 0x%08x channel: %i register: %s",
- _iValue, m_ChannelId, Debug_GetRegisterName(_iRegister));
-
- switch (_iRegister)
- {
- case EXI_STATUS:
- {
- UEXI_STATUS newStatus(_iValue);
-
- m_Status.EXIINTMASK = newStatus.EXIINTMASK;
- if (newStatus.EXIINT)
- m_Status.EXIINT = 0;
-
- m_Status.TCINTMASK = newStatus.TCINTMASK;
- if (newStatus.TCINT)
- m_Status.TCINT = 0;
-
- m_Status.CLK = newStatus.CLK;
-
- if (m_ChannelId == 0 || m_ChannelId == 1)
- {
- m_Status.EXTINTMASK = newStatus.EXTINTMASK;
-
- if (newStatus.EXTINT)
- m_Status.EXTINT = 0;
- }
-
- if (m_ChannelId == 0)
- m_Status.ROMDIS = newStatus.ROMDIS;
-
- IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT ^ newStatus.CHIP_SELECT);
- m_Status.CHIP_SELECT = newStatus.CHIP_SELECT;
- if (pDevice != NULL)
- pDevice->SetCS(m_Status.CHIP_SELECT);
-
- CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0);
- }
- break;
-
- case EXI_DMAADDR:
- INFO_LOG(EXPANSIONINTERFACE, "Wrote DMAAddr, channel %i", m_ChannelId);
- m_DMAMemoryAddress = _iValue;
- break;
-
- case EXI_DMALENGTH:
- INFO_LOG(EXPANSIONINTERFACE, "Wrote DMALength, channel %i", m_ChannelId);
- m_DMALength = _iValue;
- break;
-
- case EXI_DMACONTROL:
- INFO_LOG(EXPANSIONINTERFACE, "Wrote DMAControl, channel %i", m_ChannelId);
- m_Control.Hex = _iValue;
-
- if (m_Control.TSTART)
- {
- IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT);
- if (pDevice == NULL)
- return;
-
- if (m_Control.DMA == 0)
- {
- // immediate data
- switch (m_Control.RW)
- {
- case EXI_READ: m_ImmData = pDevice->ImmRead(m_Control.TLEN + 1); break;
- case EXI_WRITE: pDevice->ImmWrite(m_ImmData, m_Control.TLEN + 1); break;
- case EXI_READWRITE: pDevice->ImmReadWrite(m_ImmData, m_Control.TLEN + 1); break;
- default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI Imm: Unknown transfer type %i", m_Control.RW);
- }
- m_Control.TSTART = 0;
- }
- else
- {
- // DMA
- switch (m_Control.RW)
- {
- case EXI_READ: pDevice->DMARead (m_DMAMemoryAddress, m_DMALength); break;
- case EXI_WRITE: pDevice->DMAWrite(m_DMAMemoryAddress, m_DMALength); break;
- default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI DMA: Unknown transfer type %i", m_Control.RW);
- }
- m_Control.TSTART = 0;
- }
-
- if(!m_Control.TSTART) // completed !
- {
- m_Status.TCINT = 1;
- CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0);
- }
- }
- break;
-
- case EXI_IMMDATA:
- INFO_LOG(EXPANSIONINTERFACE, "Wrote IMMData, channel %i", m_ChannelId);
- m_ImmData = _iValue;
- break;
- }
-}
-
void CEXIChannel::DoState(PointerWrap &p)
{
p.DoPOD(m_Status);
diff --git a/Source/Core/Core/HW/EXI_Channel.h b/Source/Core/Core/HW/EXI_Channel.h
index 582ea48513..aedc09c3ea 100644
--- a/Source/Core/Core/HW/EXI_Channel.h
+++ b/Source/Core/Core/HW/EXI_Channel.h
@@ -9,30 +9,20 @@
#include "EXI_Device.h"
#include
+namespace MMIO { class Mapping; }
+
class CEXIChannel
{
private:
enum
{
- EXI_STATUS = 0,
- EXI_DMAADDR = 1,
- EXI_DMALENGTH = 2,
- EXI_DMACONTROL = 3,
- EXI_IMMDATA = 4
+ EXI_STATUS = 0x00,
+ EXI_DMAADDR = 0x04,
+ EXI_DMALENGTH = 0x08,
+ EXI_DMACONTROL = 0x0C,
+ EXI_IMMDATA = 0x10
};
- const char* Debug_GetRegisterName(u32 _register)
- {
- switch (_register)
- {
- case EXI_STATUS: return "STATUS";
- case EXI_DMAADDR: return "DMAADDR";
- case EXI_DMALENGTH: return "DMALENGTH";
- case EXI_DMACONTROL: return "DMACONTROL";
- case EXI_IMMDATA: return "IMMDATA";
- default: return "!!! Unknown EXI Register !!!";
- }
- }
// EXI Status Register - "Channel Parameter Register"
union UEXI_STATUS
@@ -104,15 +94,14 @@ public:
CEXIChannel(u32 ChannelId);
~CEXIChannel();
+ void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
+
void AddDevice(const TEXIDevices device_type, const int device_num);
void AddDevice(IEXIDevice* pDevice, const int device_num, bool notifyPresenceChanged=true);
// Remove all devices
void RemoveDevices();
- void Read32(u32& _uReturnValue, const u32 _iRegister);
- void Write32(const u32 _iValue, const u32 _iRegister);
-
void Update();
bool IsCausingInterrupt();
void DoState(PointerWrap &p);
diff --git a/Source/Core/Core/HW/HW.cpp b/Source/Core/Core/HW/HW.cpp
index 586344ecc6..63f934588a 100644
--- a/Source/Core/Core/HW/HW.cpp
+++ b/Source/Core/Core/HW/HW.cpp
@@ -38,11 +38,11 @@ namespace HW
VideoInterface::Init();
SerialInterface::Init();
ProcessorInterface::Init();
+ ExpansionInterface::Init(); // Needs to be initialized before Memory
Memory::Init();
DSP::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPHLE);
DVDInterface::Init();
GPFifo::Init();
- ExpansionInterface::Init();
CCPU::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.iCPUCore);
SystemTimers::Init();
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..35f83d8b99
--- /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