mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
Merge pull request #69 from delroth/mmio-interface
Redesign of the MMIO access interface
This commit is contained in:
@@ -47,7 +47,6 @@ enum LOG_TYPE
|
||||
STREAMINGINTERFACE,
|
||||
VIDEO,
|
||||
VIDEOINTERFACE,
|
||||
WII_IOB,
|
||||
WII_IPC,
|
||||
WII_IPC_DVD,
|
||||
WII_IPC_ES,
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" />
|
||||
@@ -170,7 +171,6 @@
|
||||
<ClCompile Include="HW\WiimoteEmu\WiimoteEmu.cpp" />
|
||||
<ClCompile Include="HW\WiimoteReal\IOWin.cpp" />
|
||||
<ClCompile Include="HW\WiimoteReal\WiimoteReal.cpp" />
|
||||
<ClCompile Include="HW\WII_IOB.cpp" />
|
||||
<ClCompile Include="HW\WII_IPC.cpp" />
|
||||
<ClCompile Include="IPC_HLE\ICMPWin.cpp" />
|
||||
<ClCompile Include="IPC_HLE\WiiMote_HID_Attr.cpp" />
|
||||
@@ -342,6 +342,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" />
|
||||
@@ -368,7 +370,6 @@
|
||||
<ClInclude Include="HW\WiimoteEmu\WiimoteHid.h" />
|
||||
<ClInclude Include="HW\WiimoteReal\WiimoteReal.h" />
|
||||
<ClInclude Include="HW\WiimoteReal\WiimoteRealBase.h" />
|
||||
<ClInclude Include="HW\WII_IOB.h" />
|
||||
<ClInclude Include="HW\WII_IPC.h" />
|
||||
<ClInclude Include="IPC_HLE\fakepoll.h" />
|
||||
<ClInclude Include="IPC_HLE\hci.h" />
|
||||
|
||||
@@ -492,9 +492,6 @@
|
||||
<ClCompile Include="HW\WiimoteReal\WiimoteReal.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Real</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WII_IOB.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wii IO Bridge</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HW\WII_IPC.cpp">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wii IPC</Filter>
|
||||
</ClCompile>
|
||||
@@ -510,6 +507,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>
|
||||
@@ -1022,9 +1022,6 @@
|
||||
<ClInclude Include="HW\WiimoteReal\WiimoteRealBase.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Real</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WII_IOB.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wii IO Bridge</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HW\WII_IPC.h">
|
||||
<Filter>HW %28Flipper/Hollywood%29\Wii IPC</Filter>
|
||||
</ClInclude>
|
||||
@@ -1037,6 +1034,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 +1222,4 @@
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -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<u32>(&m_Control.hex),
|
||||
MMIO::ComplexWrite<u32>([](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<u32>(&m_Volume.hex),
|
||||
MMIO::DirectWrite<u32>(&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>([](u32) {
|
||||
Update(0, 0);
|
||||
return m_SampleCounter;
|
||||
}),
|
||||
MMIO::DirectWrite<u32>(&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<u32>(&m_InterruptTiming),
|
||||
MMIO::ComplexWrite<u32>([](u32, u32 val) {
|
||||
m_InterruptTiming = val;
|
||||
CoreTiming::RemoveEvent(et_AI);
|
||||
CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2), et_AI);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
static void UpdateInterrupts()
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
+108
-271
@@ -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<u16>(mapped_var.ptr),
|
||||
MMIO::DirectWrite<u16>(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<u16>([](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<u16>([](u32, u16 val) {
|
||||
dsp_emulator->DSP_WriteMailBoxHigh(true, val);
|
||||
})
|
||||
);
|
||||
mmio->Register(base | DSP_MAIL_TO_DSP_LO,
|
||||
MMIO::ComplexRead<u16>([](u32) {
|
||||
return dsp_emulator->DSP_ReadMailBoxLow(true);
|
||||
}),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
dsp_emulator->DSP_WriteMailBoxLow(true, val);
|
||||
})
|
||||
);
|
||||
mmio->Register(base | DSP_MAIL_FROM_DSP_HI,
|
||||
MMIO::ComplexRead<u16>([](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<u16>()
|
||||
);
|
||||
mmio->Register(base | DSP_MAIL_FROM_DSP_LO,
|
||||
MMIO::ComplexRead<u16>([](u32) {
|
||||
return dsp_emulator->DSP_ReadMailBoxLow(false);
|
||||
}),
|
||||
MMIO::InvalidWrite<u16>()
|
||||
);
|
||||
|
||||
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<u16>([](u32) {
|
||||
return (g_dspState.DSPControl.Hex & ~DSP_CONTROL_MASK) |
|
||||
(dsp_emulator->DSP_ReadControlRegister() & DSP_CONTROL_MASK);
|
||||
}),
|
||||
MMIO::ComplexWrite<u16>([](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<u16>(MMIO::Utils::LowPart(&g_arDMA.Cnt.Hex)),
|
||||
MMIO::ComplexWrite<u16>([](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<u16>(&g_audioDMA.AudioDMAControl.Hex),
|
||||
MMIO::ComplexWrite<u16>([](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<u16>([](u32) {
|
||||
return (g_audioDMA.BlocksLeft > 0 ? g_audioDMA.BlocksLeft - 1 : 0);
|
||||
}),
|
||||
MMIO::InvalidWrite<u16>()
|
||||
);
|
||||
|
||||
// 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<u32>(mmio, base | i, base | (i + 2)),
|
||||
MMIO::WriteToSmaller<u32>(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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<u32>(&m_DISR.Hex),
|
||||
MMIO::ComplexWrite<u32>([](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<u32>(&m_DICVR.Hex),
|
||||
MMIO::ComplexWrite<u32>([](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<u32>(&m_DICMDBUF[i].Hex),
|
||||
MMIO::DirectWrite<u32>(&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<u32>(&m_DIMAR.Hex),
|
||||
MMIO::DirectWrite<u32>(&m_DIMAR.Hex, ~0xFC00001F)
|
||||
);
|
||||
mmio->Register(base | DI_DMA_LENGTH_REGISTER,
|
||||
MMIO::DirectRead<u32>(&m_DILENGTH.Hex),
|
||||
MMIO::DirectWrite<u32>(&m_DILENGTH.Hex, ~0x1F)
|
||||
);
|
||||
mmio->Register(base | DI_DMA_CONTROL_REGISTER,
|
||||
MMIO::DirectRead<u32>(&m_DICR.Hex),
|
||||
MMIO::ComplexWrite<u32>([](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<u32>(&m_DIIMMBUF.Hex),
|
||||
MMIO::DirectWrite<u32>(&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<u32>(&m_DICFG.Hex),
|
||||
MMIO::InvalidWrite<u32>()
|
||||
);
|
||||
}
|
||||
|
||||
void UpdateInterrupts()
|
||||
|
||||
@@ -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
|
||||
|
||||
+14
-32
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
+112
-146
@@ -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<u32>([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<u32>([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<u32>(&m_DMAMemoryAddress),
|
||||
MMIO::DirectWrite<u32>(&m_DMAMemoryAddress)
|
||||
);
|
||||
mmio->Register(base + EXI_DMALENGTH,
|
||||
MMIO::DirectRead<u32>(&m_DMALength),
|
||||
MMIO::DirectWrite<u32>(&m_DMALength)
|
||||
);
|
||||
mmio->Register(base + EXI_DMACONTROL,
|
||||
MMIO::DirectRead<u32>(&m_Control.Hex),
|
||||
MMIO::ComplexWrite<u32>([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<u32>(&m_ImmData),
|
||||
MMIO::DirectWrite<u32>(&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);
|
||||
|
||||
@@ -9,30 +9,20 @@
|
||||
#include "EXI_Device.h"
|
||||
#include <memory>
|
||||
|
||||
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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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 <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);
|
||||
});
|
||||
}
|
||||
|
||||
// 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 <typename T> struct SmallerAccessSize {};
|
||||
template <> struct SmallerAccessSize<u16> { typedef u8 value; };
|
||||
template <> struct SmallerAccessSize<u32> { typedef u16 value; };
|
||||
|
||||
template <typename T> struct LargerAccessSize {};
|
||||
template <> struct LargerAccessSize<u8> { typedef u16 value; };
|
||||
template <> struct LargerAccessSize<u16> { typedef u32 value; };
|
||||
|
||||
template <typename T>
|
||||
ReadHandlingMethod<T>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr)
|
||||
{
|
||||
typedef typename SmallerAccessSize<T>::value ST;
|
||||
|
||||
const ReadHandler<ST>* high_part;
|
||||
const ReadHandler<ST>* low_part;
|
||||
mmio->GetHandlerForRead(high_part_addr, &high_part);
|
||||
mmio->GetHandlerForRead(low_part_addr, &low_part);
|
||||
|
||||
// TODO(delroth): optimize
|
||||
return ComplexRead<T>([high_part, low_part](u32 addr) {
|
||||
return ((T)high_part->Read(addr) << (8 * sizeof (ST))) | low_part->Read(addr);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
WriteHandlingMethod<T>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr)
|
||||
{
|
||||
typedef typename SmallerAccessSize<T>::value ST;
|
||||
|
||||
const WriteHandler<ST>* high_part;
|
||||
const WriteHandler<ST>* low_part;
|
||||
mmio->GetHandlerForWrite(high_part_addr, &high_part);
|
||||
mmio->GetHandlerForWrite(low_part_addr, &low_part);
|
||||
|
||||
// TODO(delroth): optimize
|
||||
return ComplexWrite<T>([high_part, low_part](u32 addr, T val) {
|
||||
high_part->Write(addr, val >> (8 * sizeof (ST)));
|
||||
low_part->Write(addr, (ST)val);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ReadHandlingMethod<T>* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift)
|
||||
{
|
||||
typedef typename LargerAccessSize<T>::value LT;
|
||||
|
||||
const ReadHandler<LT>* large;
|
||||
mmio->GetHandlerForRead(larger_addr, &large);
|
||||
|
||||
// TODO(delroth): optimize
|
||||
return ComplexRead<T>([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 <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();
|
||||
|
||||
}
|
||||
@@ -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 <array>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#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<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
|
||||
|
||||
// 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<u##Size>& GetHandlerFor##Type##Size(u32 addr) const \
|
||||
{ \
|
||||
return m_##Type##Size##Handlers[UniqueID(addr) / sizeof (u##Size)]; \
|
||||
} \
|
||||
void GetHandlerFor##Type(u32 addr, const Type##Handler<u##Size>** 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<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
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <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
|
||||
{
|
||||
|
||||
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 <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();
|
||||
|
||||
// {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<u32> 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 <typename T> ReadHandlingMethod<T>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr);
|
||||
template <typename T> WriteHandlingMethod<T>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr);
|
||||
template <typename T> ReadHandlingMethod<T>* 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 <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_GENERIC_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>
|
||||
|
||||
#define MMIO_SPECIAL_PUBLIC_SPECIALIZATIONS(MaybeExtern) \
|
||||
MaybeExtern template ReadHandlingMethod<u16>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \
|
||||
MaybeExtern template ReadHandlingMethod<u32>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \
|
||||
MaybeExtern template WriteHandlingMethod<u16>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \
|
||||
MaybeExtern template WriteHandlingMethod<u32>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \
|
||||
MaybeExtern template ReadHandlingMethod<u8>* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift); \
|
||||
MaybeExtern template ReadHandlingMethod<u16>* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift)
|
||||
|
||||
#define MMIO_PUBLIC_SPECIALIZATIONS(MaybeExtern) \
|
||||
MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u8); \
|
||||
MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u16); \
|
||||
MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u32); \
|
||||
MMIO_SPECIAL_PUBLIC_SPECIALIZATIONS(MaybeExtern);
|
||||
|
||||
MMIO_PUBLIC_SPECIALIZATIONS(extern)
|
||||
|
||||
}
|
||||
+26
-221
@@ -29,11 +29,11 @@
|
||||
#include "EXI.h"
|
||||
#include "AudioInterface.h"
|
||||
#include "MemoryInterface.h"
|
||||
#include "WII_IOB.h"
|
||||
#include "WII_IPC.h"
|
||||
#include "../ConfigManager.h"
|
||||
#include "../Debugger/Debugger_SymbolMap.h"
|
||||
#include "VideoBackendBase.h"
|
||||
#include "MMIO.h"
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
@@ -83,230 +83,32 @@ u8 *m_pVirtualUncachedEXRAM; // wii only
|
||||
u8 *m_pVirtualL1Cache;
|
||||
u8 *m_pVirtualFakeVMEM;
|
||||
|
||||
// =================================
|
||||
// Read and write shortcuts
|
||||
// ----------------
|
||||
writeFn8 hwWrite8 [NUMHWMEMFUN];
|
||||
writeFn16 hwWrite16[NUMHWMEMFUN];
|
||||
writeFn32 hwWrite32[NUMHWMEMFUN];
|
||||
writeFn64 hwWrite64[NUMHWMEMFUN];
|
||||
// MMIO mapping object.
|
||||
MMIO::Mapping* mmio_mapping;
|
||||
|
||||
readFn8 hwRead8 [NUMHWMEMFUN];
|
||||
readFn16 hwRead16[NUMHWMEMFUN];
|
||||
readFn32 hwRead32[NUMHWMEMFUN];
|
||||
readFn64 hwRead64[NUMHWMEMFUN];
|
||||
|
||||
writeFn8 hwWriteWii8 [NUMHWMEMFUN];
|
||||
writeFn16 hwWriteWii16[NUMHWMEMFUN];
|
||||
writeFn32 hwWriteWii32[NUMHWMEMFUN];
|
||||
writeFn64 hwWriteWii64[NUMHWMEMFUN];
|
||||
|
||||
readFn8 hwReadWii8 [NUMHWMEMFUN];
|
||||
readFn16 hwReadWii16[NUMHWMEMFUN];
|
||||
readFn32 hwReadWii32[NUMHWMEMFUN];
|
||||
readFn64 hwReadWii64[NUMHWMEMFUN];
|
||||
|
||||
// Default read and write functions
|
||||
template <class T>
|
||||
void HW_Default_Write(const T _Data, const u32 _Address){ ERROR_LOG(MASTER_LOG, "Illegal HW Write%lu %08x", (unsigned long)sizeof(T)*8, _Address);_dbg_assert_(MEMMAP, 0);}
|
||||
|
||||
template <class T>
|
||||
void HW_Default_Read(T _Data, const u32 _Address){ ERROR_LOG(MASTER_LOG, "Illegal HW Read%lu %08x", (unsigned long)sizeof(T)*8, _Address); _dbg_assert_(MEMMAP, 0);}
|
||||
|
||||
#define HW_PAGE_SHIFT 10
|
||||
#define HW_PAGE_SIZE (1 << HW_PAGE_SHIFT)
|
||||
#define HW_PAGE_MASK (HW_PAGE_SHIFT - 1)
|
||||
|
||||
template <class T, u8 *P> void HW_Read_Memory(T &_Data, const u32 _Address)
|
||||
void InitMMIO(MMIO::Mapping* mmio)
|
||||
{
|
||||
_Data = *(T *)&P[_Address & HW_PAGE_MASK];
|
||||
g_video_backend->RegisterCPMMIO(mmio, 0xCC000000);
|
||||
g_video_backend->RegisterPEMMIO(mmio, 0xCC001000);
|
||||
VideoInterface::RegisterMMIO(mmio, 0xCC002000);
|
||||
ProcessorInterface::RegisterMMIO(mmio, 0xCC003000);
|
||||
MemoryInterface::RegisterMMIO(mmio, 0xCC004000);
|
||||
DSP::RegisterMMIO(mmio, 0xCC005000);
|
||||
DVDInterface::RegisterMMIO(mmio, 0xCC006000);
|
||||
SerialInterface::RegisterMMIO(mmio, 0xCC006400);
|
||||
ExpansionInterface::RegisterMMIO(mmio, 0xCC006800);
|
||||
AudioInterface::RegisterMMIO(mmio, 0xCC006C00);
|
||||
}
|
||||
|
||||
template <class T, u8 *P> void HW_Write_Memory(T _Data, const u32 _Address)
|
||||
void InitMMIOWii(MMIO::Mapping* mmio)
|
||||
{
|
||||
*(T *)&P[_Address & HW_PAGE_MASK] = _Data;
|
||||
}
|
||||
InitMMIO(mmio);
|
||||
|
||||
// Create shortcuts to the hardware devices' read and write functions.
|
||||
// This can be seen as an alternative to a switch() or if() table.
|
||||
#define BLOCKSIZE 4
|
||||
#define CP_START 0x00 //0x0000 >> 10
|
||||
#define WII_IPC_START 0x00 //0x0000 >> 10
|
||||
#define PE_START 0x04 //0x1000 >> 10
|
||||
#define VI_START 0x08 //0x2000 >> 10
|
||||
#define PI_START 0x0C //0x3000 >> 10
|
||||
#define MI_START 0x10 //0x4000 >> 10
|
||||
#define DSP_START 0x14 //0x5000 >> 10
|
||||
#define DVD_START 0x18 //0x6000 >> 10
|
||||
#define SI_START 0x19 //0x6400 >> 10
|
||||
#define EI_START 0x1A //0x6800 >> 10
|
||||
#define AUDIO_START 0x1B //0x6C00 >> 10
|
||||
#define GP_START 0x20 //0x8000 >> 10
|
||||
|
||||
void InitHWMemFuncs()
|
||||
{
|
||||
for (int i = 0; i < NUMHWMEMFUN; i++)
|
||||
{
|
||||
hwWrite8 [i] = HW_Default_Write<u8>;
|
||||
hwWrite16[i] = HW_Default_Write<u16>;
|
||||
hwWrite32[i] = HW_Default_Write<u32>;
|
||||
hwWrite64[i] = HW_Default_Write<u64>;
|
||||
hwRead8 [i] = HW_Default_Read<u8&>;
|
||||
hwRead16 [i] = HW_Default_Read<u16&>;
|
||||
hwRead32 [i] = HW_Default_Read<u32&>;
|
||||
hwRead64 [i] = HW_Default_Read<u64&>;
|
||||
|
||||
// To prevent Dolphin from crashing when accidentally running Wii
|
||||
// executables in GC mode (or running malicious GC executables...)
|
||||
hwWriteWii8 [i] = HW_Default_Write<u8>;
|
||||
hwWriteWii16[i] = HW_Default_Write<u16>;
|
||||
hwWriteWii32[i] = HW_Default_Write<u32>;
|
||||
hwWriteWii64[i] = HW_Default_Write<u64>;
|
||||
hwReadWii8 [i] = HW_Default_Read<u8&>;
|
||||
hwReadWii16 [i] = HW_Default_Read<u16&>;
|
||||
hwReadWii32 [i] = HW_Default_Read<u32&>;
|
||||
hwReadWii64 [i] = HW_Default_Read<u64&>;
|
||||
}
|
||||
|
||||
for (int i = 0; i < BLOCKSIZE; i++)
|
||||
{
|
||||
hwRead16 [CP_START+i] = g_video_backend->Video_CPRead16();
|
||||
hwWrite16[CP_START+i] = g_video_backend->Video_CPWrite16();
|
||||
|
||||
hwRead16 [PE_START+i] = g_video_backend->Video_PERead16();
|
||||
hwWrite16[PE_START+i] = g_video_backend->Video_PEWrite16();
|
||||
hwWrite32[PE_START+i] = g_video_backend->Video_PEWrite32();
|
||||
|
||||
hwRead8 [VI_START+i] = VideoInterface::Read8;
|
||||
hwRead16 [VI_START+i] = VideoInterface::Read16;
|
||||
hwRead32 [VI_START+i] = VideoInterface::Read32;
|
||||
hwWrite16[VI_START+i] = VideoInterface::Write16;
|
||||
hwWrite32[VI_START+i] = VideoInterface::Write32;
|
||||
|
||||
hwRead16 [PI_START+i] = ProcessorInterface::Read16;
|
||||
hwRead32 [PI_START+i] = ProcessorInterface::Read32;
|
||||
hwWrite32[PI_START+i] = ProcessorInterface::Write32;
|
||||
|
||||
hwRead16 [MI_START+i] = MemoryInterface::Read16;
|
||||
hwRead32 [MI_START+i] = MemoryInterface::Read32;
|
||||
hwWrite32[MI_START+i] = MemoryInterface::Write32;
|
||||
hwWrite16[MI_START+i] = MemoryInterface::Write16;
|
||||
|
||||
hwRead16 [DSP_START+i] = DSP::Read16;
|
||||
hwWrite16[DSP_START+i] = DSP::Write16;
|
||||
hwRead32 [DSP_START+i] = DSP::Read32;
|
||||
hwWrite32[DSP_START+i] = DSP::Write32;
|
||||
}
|
||||
|
||||
hwRead32 [DVD_START] = DVDInterface::Read32;
|
||||
hwWrite32[DVD_START] = DVDInterface::Write32;
|
||||
|
||||
hwRead32 [SI_START] = SerialInterface::Read32;
|
||||
hwWrite32[SI_START] = SerialInterface::Write32;
|
||||
|
||||
hwRead32 [EI_START] = ExpansionInterface::Read32;
|
||||
hwWrite32[EI_START] = ExpansionInterface::Write32;
|
||||
|
||||
hwRead32 [AUDIO_START] = AudioInterface::Read32;
|
||||
hwWrite32[AUDIO_START] = AudioInterface::Write32;
|
||||
|
||||
hwWrite8 [GP_START] = GPFifo::Write8;
|
||||
hwWrite16[GP_START] = GPFifo::Write16;
|
||||
hwWrite32[GP_START] = GPFifo::Write32;
|
||||
hwWrite64[GP_START] = GPFifo::Write64;
|
||||
}
|
||||
|
||||
|
||||
void InitHWMemFuncsWii()
|
||||
{
|
||||
for (int i = 0; i < NUMHWMEMFUN; i++)
|
||||
{
|
||||
hwWrite8 [i] = HW_Default_Write<u8>;
|
||||
hwWrite16[i] = HW_Default_Write<u16>;
|
||||
hwWrite32[i] = HW_Default_Write<u32>;
|
||||
hwWrite64[i] = HW_Default_Write<u64>;
|
||||
hwRead8 [i] = HW_Default_Read<u8&>;
|
||||
hwRead16 [i] = HW_Default_Read<u16&>;
|
||||
hwRead32 [i] = HW_Default_Read<u32&>;
|
||||
hwRead64 [i] = HW_Default_Read<u64&>;
|
||||
|
||||
hwWriteWii8 [i] = HW_Default_Write<u8>;
|
||||
hwWriteWii16[i] = HW_Default_Write<u16>;
|
||||
hwWriteWii32[i] = HW_Default_Write<u32>;
|
||||
hwWriteWii64[i] = HW_Default_Write<u64>;
|
||||
hwReadWii8 [i] = HW_Default_Read<u8&>;
|
||||
hwReadWii16 [i] = HW_Default_Read<u16&>;
|
||||
hwReadWii32 [i] = HW_Default_Read<u32&>;
|
||||
hwReadWii64 [i] = HW_Default_Read<u64&>;
|
||||
}
|
||||
|
||||
// MI, PI, DSP are still mapped to 0xCCxxxxxx
|
||||
for (int i = 0; i < BLOCKSIZE; i++)
|
||||
{
|
||||
hwRead16 [CP_START+i] = g_video_backend->Video_CPRead16();
|
||||
hwWrite16[CP_START+i] = g_video_backend->Video_CPWrite16();
|
||||
|
||||
hwRead16 [PE_START+i] = g_video_backend->Video_PERead16();
|
||||
hwWrite16[PE_START+i] = g_video_backend->Video_PEWrite16();
|
||||
hwWrite32[PE_START+i] = g_video_backend->Video_PEWrite32();
|
||||
|
||||
hwRead16 [PI_START+i] = ProcessorInterface::Read16;
|
||||
hwRead32 [PI_START+i] = ProcessorInterface::Read32;
|
||||
hwWrite32[PI_START+i] = ProcessorInterface::Write32;
|
||||
|
||||
hwRead8 [VI_START+i] = VideoInterface::Read8;
|
||||
hwRead16 [VI_START+i] = VideoInterface::Read16;
|
||||
hwRead32 [VI_START+i] = VideoInterface::Read32;
|
||||
hwWrite16[VI_START+i] = VideoInterface::Write16;
|
||||
hwWrite32[VI_START+i] = VideoInterface::Write32;
|
||||
|
||||
hwRead16 [MI_START+i] = MemoryInterface::Read16;
|
||||
hwRead32 [MI_START+i] = MemoryInterface::Read32;
|
||||
hwWrite32[MI_START+i] = MemoryInterface::Write32;
|
||||
hwWrite16[MI_START+i] = MemoryInterface::Write16;
|
||||
|
||||
hwRead16 [DSP_START+i] = DSP::Read16;
|
||||
hwWrite16[DSP_START+i] = DSP::Write16;
|
||||
hwRead32 [DSP_START+i] = DSP::Read32;
|
||||
hwWrite32[DSP_START+i] = DSP::Write32;
|
||||
}
|
||||
|
||||
hwWrite8 [GP_START] = GPFifo::Write8;
|
||||
hwWrite16[GP_START] = GPFifo::Write16;
|
||||
hwWrite32[GP_START] = GPFifo::Write32;
|
||||
hwWrite64[GP_START] = GPFifo::Write64;
|
||||
|
||||
for (int i = 0; i < BLOCKSIZE; i++)
|
||||
{
|
||||
hwReadWii32[WII_IPC_START+i] = WII_IPCInterface::Read32;
|
||||
hwWriteWii32[WII_IPC_START+i] = WII_IPCInterface::Write32;
|
||||
}
|
||||
|
||||
hwRead32 [DVD_START] = DVDInterface::Read32;
|
||||
hwReadWii32 [DVD_START] = DVDInterface::Read32;
|
||||
hwWrite32 [DVD_START] = DVDInterface::Write32;
|
||||
hwWriteWii32[DVD_START] = DVDInterface::Write32;
|
||||
|
||||
hwRead32 [SI_START] = SerialInterface::Read32;
|
||||
hwReadWii32 [SI_START] = SerialInterface::Read32;
|
||||
hwWrite32 [SI_START] = SerialInterface::Write32;
|
||||
hwWriteWii32[SI_START] = SerialInterface::Write32;
|
||||
|
||||
hwRead32 [EI_START] = ExpansionInterface::Read32;
|
||||
hwReadWii32 [EI_START] = ExpansionInterface::Read32;
|
||||
hwWrite32 [EI_START] = ExpansionInterface::Write32;
|
||||
hwWriteWii32[EI_START] = ExpansionInterface::Write32;
|
||||
|
||||
// [F|RES] i thought this doesn't exist anymore
|
||||
hwRead32 [AUDIO_START] = AudioInterface::Read32;
|
||||
hwReadWii32 [AUDIO_START] = AudioInterface::Read32;
|
||||
hwWrite32 [AUDIO_START] = AudioInterface::Write32;
|
||||
hwWriteWii32[AUDIO_START] = AudioInterface::Write32;
|
||||
}
|
||||
|
||||
writeFn32 GetHWWriteFun32(const u32 _Address)
|
||||
{
|
||||
return hwWrite32[(_Address >> HWSHIFT) & (NUMHWMEMFUN-1)];
|
||||
WII_IPCInterface::RegisterMMIO(mmio, 0xCD000000);
|
||||
DVDInterface::RegisterMMIO(mmio, 0xCD006000);
|
||||
SerialInterface::RegisterMMIO(mmio, 0xCD006400);
|
||||
ExpansionInterface::RegisterMMIO(mmio, 0xCD006800);
|
||||
AudioInterface::RegisterMMIO(mmio, 0xCD006C00);
|
||||
}
|
||||
|
||||
bool IsInitialized()
|
||||
@@ -348,10 +150,12 @@ 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();
|
||||
InitMMIOWii(mmio_mapping);
|
||||
else
|
||||
InitHWMemFuncs();
|
||||
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);
|
||||
@@ -382,6 +186,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.");
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user