Merge pull request #69 from delroth/mmio-interface
Redesign of the MMIO access interface
This commit is contained in:
commit
2a7a94387c
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -28,16 +28,7 @@
|
|||
|
||||
// Global declarations
|
||||
class PointerWrap;
|
||||
|
||||
typedef void (*writeFn8 )(const u8, const u32);
|
||||
typedef void (*writeFn16)(const u16,const u32);
|
||||
typedef void (*writeFn32)(const u32,const u32);
|
||||
typedef void (*writeFn64)(const u64,const u32);
|
||||
|
||||
typedef void (*readFn8 )(u8&, const u32);
|
||||
typedef void (*readFn16)(u16&, const u32);
|
||||
typedef void (*readFn32)(u32&, const u32);
|
||||
typedef void (*readFn64)(u64&, const u32);
|
||||
namespace MMIO { class Mapping; }
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
|
@ -83,6 +74,9 @@ enum
|
|||
#endif
|
||||
};
|
||||
|
||||
// MMIO mapping object.
|
||||
extern MMIO::Mapping* mmio_mapping;
|
||||
|
||||
// Init and Shutdown
|
||||
bool IsInitialized();
|
||||
void Init();
|
||||
|
@ -99,11 +93,7 @@ u32 ReadUnchecked_U32(const u32 _Address);
|
|||
void WriteUnchecked_U8(const u8 _Data, const u32 _Address);
|
||||
void WriteUnchecked_U32(const u32 _Data, const u32 _Address);
|
||||
|
||||
void InitHWMemFuncs();
|
||||
void InitHWMemFuncsWii();
|
||||
|
||||
bool IsRAMAddress(const u32 addr, bool allow_locked_cache = false, bool allow_fake_vmem = false);
|
||||
writeFn32 GetHWWriteFun32(const u32 _Address);
|
||||
|
||||
inline u8* GetCachePtr() {return m_pL1Cache;}
|
||||
inline u8* GetMainRAMPtr() {return m_pRAM;}
|
||||
|
|
|
@ -20,10 +20,10 @@
|
|||
|
||||
#include "GPFifo.h"
|
||||
#include "Memmap.h"
|
||||
#include "WII_IOB.h"
|
||||
#include "../Core.h"
|
||||
#include "../PowerPC/PowerPC.h"
|
||||
#include "VideoBackendBase.h"
|
||||
#include "MMIO.h"
|
||||
|
||||
#ifdef USE_GDBSTUB
|
||||
#include "../PowerPC/GDBStub.h"
|
||||
|
@ -61,28 +61,6 @@ extern u8 *m_pEFB;
|
|||
extern bool m_IsInitialized;
|
||||
extern bool bFakeVMEM;
|
||||
|
||||
// Read and write shortcuts
|
||||
|
||||
// It appears that some clever games use stfd to write 64 bits to the fifo. Hence the hwWrite64.
|
||||
|
||||
extern writeFn8 hwWrite8 [NUMHWMEMFUN];
|
||||
extern writeFn16 hwWrite16[NUMHWMEMFUN];
|
||||
extern writeFn32 hwWrite32[NUMHWMEMFUN];
|
||||
extern writeFn64 hwWrite64[NUMHWMEMFUN];
|
||||
|
||||
extern readFn8 hwRead8 [NUMHWMEMFUN];
|
||||
extern readFn16 hwRead16[NUMHWMEMFUN];
|
||||
extern readFn32 hwRead32[NUMHWMEMFUN];
|
||||
|
||||
extern writeFn8 hwWriteWii8 [NUMHWMEMFUN];
|
||||
extern writeFn16 hwWriteWii16[NUMHWMEMFUN];
|
||||
extern writeFn32 hwWriteWii32[NUMHWMEMFUN];
|
||||
extern writeFn64 hwWriteWii64[NUMHWMEMFUN];
|
||||
|
||||
extern readFn8 hwReadWii8 [NUMHWMEMFUN];
|
||||
extern readFn16 hwReadWii16[NUMHWMEMFUN];
|
||||
extern readFn32 hwReadWii32[NUMHWMEMFUN];
|
||||
|
||||
// Overloaded byteswap functions, for use within the templated functions below.
|
||||
inline u8 bswap(u8 val) {return val;}
|
||||
inline u16 bswap(u16 val) {return Common::swap16(val);}
|
||||
|
@ -91,42 +69,6 @@ inline u64 bswap(u64 val) {return Common::swap64(val);}
|
|||
// =================
|
||||
|
||||
|
||||
// Read and write
|
||||
// ----------------
|
||||
// The read and write macros that direct us to the right functions
|
||||
|
||||
// All these little inline functions are needed because we can't paste symbols together in templates
|
||||
// like we can in macros.
|
||||
inline void hwRead(u8 &var, u32 addr) {hwRead8 [(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
inline void hwRead(u16 &var, u32 addr) {hwRead16[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
inline void hwRead(u32 &var, u32 addr) {hwRead32[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
inline void hwRead(u64 &var, u32 addr) {PanicAlert("hwRead: There's no 64-bit HW read. %08x", addr);}
|
||||
|
||||
inline void hwWrite(u8 var, u32 addr) {hwWrite8[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
inline void hwWrite(u16 var, u32 addr) {hwWrite16[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
inline void hwWrite(u32 var, u32 addr) {hwWrite32[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
inline void hwWrite(u64 var, u32 addr) {hwWrite64[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
|
||||
inline void hwReadWii(u8 &var, u32 addr) {hwReadWii8 [(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
inline void hwReadWii(u16 &var, u32 addr) {hwReadWii16[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
inline void hwReadWii(u32 &var, u32 addr) {hwReadWii32[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
inline void hwReadWii(u64 &var, u32 addr) {PanicAlert("hwReadWii: There's no 64-bit HW read. %08x", addr);}
|
||||
|
||||
inline void hwWriteWii(u8 var, u32 addr) {hwWriteWii8[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
inline void hwWriteWii(u16 var, u32 addr) {hwWriteWii16[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
inline void hwWriteWii(u32 var, u32 addr) {hwWriteWii32[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
inline void hwWriteWii(u64 var, u32 addr) {hwWriteWii64[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
|
||||
|
||||
inline void hwReadIOBridge(u8 &var, u32 addr) {WII_IOBridge::Read8(var, addr);}
|
||||
inline void hwReadIOBridge(u16 &var, u32 addr) {WII_IOBridge::Read16(var, addr);}
|
||||
inline void hwReadIOBridge(u32 &var, u32 addr) {WII_IOBridge::Read32(var, addr);}
|
||||
inline void hwReadIOBridge(u64 &var, u32 addr) {PanicAlert("hwReadIOBridge: There's no 64-bit HW read. %08x", addr);}
|
||||
|
||||
inline void hwWriteIOBridge(u8 var, u32 addr) {WII_IOBridge::Write8(var, addr);}
|
||||
inline void hwWriteIOBridge(u16 var, u32 addr) {WII_IOBridge::Write16(var, addr);}
|
||||
inline void hwWriteIOBridge(u32 var, u32 addr) {WII_IOBridge::Write32(var, addr);}
|
||||
inline void hwWriteIOBridge(u64 var, u32 addr) {PanicAlert("hwWriteIOBridge: There's no 64-bit HW write. %08x", addr);}
|
||||
|
||||
// Nasty but necessary. Super Mario Galaxy pointer relies on this stuff.
|
||||
u32 EFB_Read(const u32 addr)
|
||||
{
|
||||
|
@ -155,20 +97,8 @@ inline void ReadFromHardware(T &_var, const u32 em_address, const u32 effective_
|
|||
{
|
||||
if (em_address < 0xcc000000)
|
||||
_var = EFB_Read(em_address);
|
||||
else if (em_address <= 0xcc009000)
|
||||
hwRead(_var, em_address);
|
||||
/* WIIMODE */
|
||||
else if (((em_address & 0xFF000000) == 0xCD000000) &&
|
||||
(em_address <= 0xcd009000))
|
||||
hwReadWii(_var, em_address);
|
||||
else if (((em_address & 0xFFF00000) == 0xCD800000) &&
|
||||
(em_address <= 0xCD809000))
|
||||
hwReadIOBridge(_var, em_address);
|
||||
else
|
||||
{
|
||||
/* Disabled because the debugger makes trouble with */
|
||||
/*_dbg_assert_(MEMMAP,0); */
|
||||
}
|
||||
mmio_mapping->Read(em_address, &_var);
|
||||
}
|
||||
else if (((em_address & 0xF0000000) == 0x80000000) ||
|
||||
((em_address & 0xF0000000) == 0xC0000000) ||
|
||||
|
@ -245,28 +175,10 @@ inline void WriteToHardware(u32 em_address, const T data, u32 effective_address,
|
|||
}
|
||||
return;
|
||||
}
|
||||
else if (em_address <= 0xcc009000)
|
||||
{
|
||||
hwWrite(data, em_address);
|
||||
return;
|
||||
}
|
||||
/* WIIMODE */
|
||||
else if (((em_address & 0xFF000000) == 0xCD000000) &&
|
||||
(em_address <= 0xcd009000))
|
||||
{
|
||||
hwWriteWii(data,em_address);
|
||||
return;
|
||||
}
|
||||
else if (((em_address & 0xFFF00000) == 0xCD800000) &&
|
||||
(em_address <= 0xCD809000))
|
||||
{
|
||||
hwWriteIOBridge(data,em_address);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(MEMMAP, "hwwrite [%08x] := %08x (PC: %08x)", em_address, (u32)data, PC);
|
||||
_dbg_assert_msg_(MEMMAP,0,"Memory - Unknown HW address %08x", em_address);
|
||||
mmio_mapping->Write(em_address, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (((em_address & 0xF0000000) == 0x80000000) ||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "../PowerPC/PowerPC.h"
|
||||
#include "MemoryInterface.h"
|
||||
#include "MMIO.h"
|
||||
|
||||
namespace MemoryInterface
|
||||
{
|
||||
|
@ -14,67 +15,199 @@ namespace MemoryInterface
|
|||
// internal hardware addresses
|
||||
enum
|
||||
{
|
||||
MEM_CHANNEL0_HI = 0x000,
|
||||
MEM_CHANNEL0_LO = 0x002,
|
||||
MEM_CHANNEL1_HI = 0x004,
|
||||
MEM_CHANNEL1_LO = 0x006,
|
||||
MEM_CHANNEL2_HI = 0x008,
|
||||
MEM_CHANNEL2_LO = 0x00A,
|
||||
MEM_CHANNEL3_HI = 0x00C,
|
||||
MEM_CHANNEL3_LO = 0x00E,
|
||||
MEM_CHANNEL_CTRL = 0x010
|
||||
MI_REGION0_FIRST = 0x000,
|
||||
MI_REGION0_LAST = 0x002,
|
||||
MI_REGION1_FIRST = 0x004,
|
||||
MI_REGION1_LAST = 0x006,
|
||||
MI_REGION2_FIRST = 0x008,
|
||||
MI_REGION2_LAST = 0x00A,
|
||||
MI_REGION3_FIRST = 0x00C,
|
||||
MI_REGION3_LAST = 0x00E,
|
||||
MI_PROT_TYPE = 0x010,
|
||||
MI_IRQMASK = 0x01C,
|
||||
MI_IRQFLAG = 0x01E,
|
||||
MI_UNKNOWN1 = 0x020,
|
||||
MI_PROT_ADDR_LO = 0x022,
|
||||
MI_PROT_ADDR_HI = 0x024,
|
||||
MI_TIMER0_HI = 0x032,
|
||||
MI_TIMER0_LO = 0x034,
|
||||
MI_TIMER1_HI = 0x036,
|
||||
MI_TIMER1_LO = 0x038,
|
||||
MI_TIMER2_HI = 0x03A,
|
||||
MI_TIMER2_LO = 0x03C,
|
||||
MI_TIMER3_HI = 0x03E,
|
||||
MI_TIMER3_LO = 0x040,
|
||||
MI_TIMER4_HI = 0x042,
|
||||
MI_TIMER4_LO = 0x044,
|
||||
MI_TIMER5_HI = 0x046,
|
||||
MI_TIMER5_LO = 0x048,
|
||||
MI_TIMER6_HI = 0x04A,
|
||||
MI_TIMER6_LO = 0x04C,
|
||||
MI_TIMER7_HI = 0x04E,
|
||||
MI_TIMER7_LO = 0x050,
|
||||
MI_TIMER8_HI = 0x052,
|
||||
MI_TIMER8_LO = 0x054,
|
||||
MI_TIMER9_HI = 0x056,
|
||||
MI_TIMER9_LO = 0x058,
|
||||
MI_UNKNOWN2 = 0x05A,
|
||||
};
|
||||
|
||||
union MIRegion
|
||||
{
|
||||
u32 hex;
|
||||
struct { u16 first_page; u16 last_page; };
|
||||
};
|
||||
|
||||
union MIProtType
|
||||
{
|
||||
u16 hex;
|
||||
struct
|
||||
{
|
||||
u16 reg0 : 2;
|
||||
u16 reg1 : 2;
|
||||
u16 reg2 : 2;
|
||||
u16 reg3 : 2;
|
||||
u16 : 8;
|
||||
};
|
||||
};
|
||||
|
||||
union MIIRQMask
|
||||
{
|
||||
u16 hex;
|
||||
struct
|
||||
{
|
||||
u16 reg0 : 1;
|
||||
u16 reg1 : 1;
|
||||
u16 reg2 : 1;
|
||||
u16 reg3 : 1;
|
||||
u16 all_regs : 1;
|
||||
u16 : 11;
|
||||
};
|
||||
};
|
||||
|
||||
union MIIRQFlag
|
||||
{
|
||||
u16 hex;
|
||||
struct
|
||||
{
|
||||
u16 reg0 : 1;
|
||||
u16 reg1 : 1;
|
||||
u16 reg2 : 1;
|
||||
u16 reg3 : 1;
|
||||
u16 all_regs : 1;
|
||||
u16 : 11;
|
||||
};
|
||||
};
|
||||
|
||||
union MIProtAddr
|
||||
{
|
||||
u32 hex;
|
||||
struct { u16 lo; u16 hi; };
|
||||
struct
|
||||
{
|
||||
u32 : 5;
|
||||
u32 addr : 25;
|
||||
u32 : 2;
|
||||
};
|
||||
};
|
||||
|
||||
union MITimer
|
||||
{
|
||||
u32 hex;
|
||||
struct { u16 lo; u16 hi; };
|
||||
};
|
||||
|
||||
struct MIMemStruct
|
||||
{
|
||||
u32 Channel0_Addr;
|
||||
u32 Channel1_Addr;
|
||||
u32 Channel2_Addr;
|
||||
u32 Channel3_Addr;
|
||||
u32 Channel_Ctrl;
|
||||
MIRegion regions[4];
|
||||
MIProtType prot_type;
|
||||
MIIRQMask irq_mask;
|
||||
MIIRQFlag irq_flag;
|
||||
u16 unknown1;
|
||||
MIProtAddr prot_addr;
|
||||
MITimer timers[10];
|
||||
u16 unknown2;
|
||||
};
|
||||
|
||||
// STATE_TO_SAVE
|
||||
static MIMemStruct miMem;
|
||||
static MIMemStruct g_mi_mem;
|
||||
|
||||
void DoState(PointerWrap &p)
|
||||
{
|
||||
p.Do(miMem);
|
||||
p.Do(g_mi_mem);
|
||||
}
|
||||
|
||||
void Read16(u16& _uReturnValue, const u32 _iAddress)
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
//0x30 -> 0x5a : gp memory metrics
|
||||
INFO_LOG(MEMMAP, "(r16) 0x%04x @ 0x%08x", 0, _iAddress);
|
||||
_uReturnValue = 0;
|
||||
}
|
||||
|
||||
void Read32(u32& _uReturnValue, const u32 _iAddress)
|
||||
{
|
||||
INFO_LOG(MEMMAP, "(r32) 0x%08x @ 0x%08x", 0, _iAddress);
|
||||
_uReturnValue = 0;
|
||||
}
|
||||
|
||||
void Write32(const u32 _iValue, const u32 _iAddress)
|
||||
{
|
||||
INFO_LOG(MEMMAP, "(w32) 0x%08x @ 0x%08x", _iValue, _iAddress);
|
||||
}
|
||||
|
||||
//TODO : check
|
||||
void Write16(const u16 _iValue, const u32 _iAddress)
|
||||
{
|
||||
INFO_LOG(MEMMAP, "(w16) 0x%04x @ 0x%08x", _iValue, _iAddress);
|
||||
switch(_iAddress & 0xFFF)
|
||||
for (int i = 0; i < MI_REGION0_FIRST; i += 4)
|
||||
{
|
||||
case MEM_CHANNEL0_HI: miMem.Channel0_Addr = (miMem.Channel0_Addr & 0xFFFF) | (_iValue<<16); return;
|
||||
case MEM_CHANNEL0_LO: miMem.Channel0_Addr = (miMem.Channel0_Addr & 0xFFFF0000) | (_iValue); return;
|
||||
case MEM_CHANNEL1_HI: miMem.Channel1_Addr = (miMem.Channel1_Addr & 0xFFFF) | (_iValue<<16); return;
|
||||
case MEM_CHANNEL1_LO: miMem.Channel1_Addr = (miMem.Channel1_Addr & 0xFFFF0000) | (_iValue); return;
|
||||
case MEM_CHANNEL2_HI: miMem.Channel2_Addr = (miMem.Channel2_Addr & 0xFFFF) | (_iValue<<16); return;
|
||||
case MEM_CHANNEL2_LO: miMem.Channel2_Addr = (miMem.Channel2_Addr & 0xFFFF0000) | (_iValue); return;
|
||||
case MEM_CHANNEL3_HI: miMem.Channel3_Addr = (miMem.Channel3_Addr & 0xFFFF) | (_iValue<<16); return;
|
||||
case MEM_CHANNEL3_LO: miMem.Channel3_Addr = (miMem.Channel3_Addr & 0xFFFF0000) | (_iValue); return;
|
||||
case MEM_CHANNEL_CTRL: miMem.Channel_Ctrl = _iValue; return;
|
||||
auto& region = g_mi_mem.regions[i / 4];
|
||||
mmio->Register(base | i,
|
||||
MMIO::DirectRead<u16>(®ion.first_page),
|
||||
MMIO::DirectWrite<u16>(®ion.first_page)
|
||||
);
|
||||
mmio->Register(base | (i + 2),
|
||||
MMIO::DirectRead<u16>(®ion.last_page),
|
||||
MMIO::DirectWrite<u16>(®ion.last_page)
|
||||
);
|
||||
}
|
||||
|
||||
mmio->Register(base | MI_PROT_TYPE,
|
||||
MMIO::DirectRead<u16>(&g_mi_mem.prot_type.hex),
|
||||
MMIO::DirectWrite<u16>(&g_mi_mem.prot_type.hex)
|
||||
);
|
||||
|
||||
mmio->Register(base | MI_IRQMASK,
|
||||
MMIO::DirectRead<u16>(&g_mi_mem.irq_mask.hex),
|
||||
MMIO::DirectWrite<u16>(&g_mi_mem.irq_mask.hex)
|
||||
);
|
||||
|
||||
mmio->Register(base | MI_IRQFLAG,
|
||||
MMIO::DirectRead<u16>(&g_mi_mem.irq_flag.hex),
|
||||
MMIO::DirectWrite<u16>(&g_mi_mem.irq_flag.hex)
|
||||
);
|
||||
|
||||
mmio->Register(base | MI_UNKNOWN1,
|
||||
MMIO::DirectRead<u16>(&g_mi_mem.unknown1),
|
||||
MMIO::DirectWrite<u16>(&g_mi_mem.unknown1)
|
||||
);
|
||||
|
||||
// The naming is confusing here: the registed contains the lower part of
|
||||
// the address (hence MI_..._LO but this is still the high part of the
|
||||
// overall register.
|
||||
mmio->Register(base | MI_PROT_ADDR_LO,
|
||||
MMIO::DirectRead<u16>(&g_mi_mem.prot_addr.hi),
|
||||
MMIO::DirectWrite<u16>(&g_mi_mem.prot_addr.hi)
|
||||
);
|
||||
mmio->Register(base | MI_PROT_ADDR_HI,
|
||||
MMIO::DirectRead<u16>(&g_mi_mem.prot_addr.lo),
|
||||
MMIO::DirectWrite<u16>(&g_mi_mem.prot_addr.lo)
|
||||
);
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
auto& timer = g_mi_mem.timers[i];
|
||||
mmio->Register(base | (MI_TIMER0_HI + 4 * i),
|
||||
MMIO::DirectRead<u16>(&timer.hi),
|
||||
MMIO::DirectWrite<u16>(&timer.hi)
|
||||
);
|
||||
mmio->Register(base | (MI_TIMER0_LO + 4 * i),
|
||||
MMIO::DirectRead<u16>(&timer.lo),
|
||||
MMIO::DirectWrite<u16>(&timer.lo)
|
||||
);
|
||||
}
|
||||
|
||||
mmio->Register(base | MI_UNKNOWN2,
|
||||
MMIO::DirectRead<u16>(&g_mi_mem.unknown2),
|
||||
MMIO::DirectWrite<u16>(&g_mi_mem.unknown2)
|
||||
);
|
||||
|
||||
for (int i = 0; i < 0x1000; i += 4)
|
||||
{
|
||||
mmio->Register(base | i,
|
||||
MMIO::ReadToSmaller<u32>(mmio, base | i, base | (i + 2)),
|
||||
MMIO::WriteToSmaller<u32>(mmio, base | i, base | (i + 2))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,14 +5,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
namespace MMIO { class Mapping; }
|
||||
class PointerWrap;
|
||||
|
||||
namespace MemoryInterface
|
||||
{
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
void Read16(u16& _uReturnValue, const u32 _iAddress);
|
||||
void Read32(u32& _uReturnValue, const u32 _iAddress);
|
||||
void Write32(const u32 _iValue, const u32 _iAddress);
|
||||
void Write16(const u16 _iValue, const u32 _iAddress);
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||
} // end of namespace MemoryInterface
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "ProcessorInterface.h"
|
||||
#include "GPFifo.h"
|
||||
#include "VideoBackendBase.h"
|
||||
#include "MMIO.h"
|
||||
|
||||
namespace ProcessorInterface
|
||||
{
|
||||
|
@ -90,113 +91,67 @@ void Init()
|
|||
toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback);
|
||||
}
|
||||
|
||||
void Read16(u16& _uReturnValue, const u32 _iAddress)
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
u32 word;
|
||||
Read32(word, _iAddress & ~3);
|
||||
_uReturnValue = word >> (_iAddress & 3) ? 16 : 0;
|
||||
}
|
||||
mmio->Register(base | PI_INTERRUPT_CAUSE,
|
||||
MMIO::DirectRead<u32>(&m_InterruptCause),
|
||||
MMIO::ComplexWrite<u32>([](u32, u32 val) {
|
||||
Common::AtomicAnd(m_InterruptCause, ~val);
|
||||
UpdateException();
|
||||
})
|
||||
);
|
||||
|
||||
void Read32(u32& _uReturnValue, const u32 _iAddress)
|
||||
{
|
||||
//INFO_LOG(PROCESSORINTERFACE, "(r32) 0x%08x", _iAddress);
|
||||
mmio->Register(base | PI_INTERRUPT_MASK,
|
||||
MMIO::DirectRead<u32>(&m_InterruptMask),
|
||||
MMIO::ComplexWrite<u32>([](u32, u32 val) {
|
||||
m_InterruptMask = val;
|
||||
UpdateException();
|
||||
})
|
||||
);
|
||||
|
||||
switch(_iAddress & 0xFFF)
|
||||
mmio->Register(base | PI_FIFO_BASE,
|
||||
MMIO::DirectRead<u32>(&Fifo_CPUBase),
|
||||
MMIO::DirectWrite<u32>(&Fifo_CPUBase, 0xFFFFFFE0)
|
||||
);
|
||||
|
||||
mmio->Register(base | PI_FIFO_END,
|
||||
MMIO::DirectRead<u32>(&Fifo_CPUEnd),
|
||||
MMIO::DirectWrite<u32>(&Fifo_CPUEnd, 0xFFFFFFE0)
|
||||
);
|
||||
|
||||
mmio->Register(base | PI_FIFO_WPTR,
|
||||
MMIO::DirectRead<u32>(&Fifo_CPUWritePointer),
|
||||
MMIO::DirectWrite<u32>(&Fifo_CPUWritePointer, 0xFFFFFFE0)
|
||||
);
|
||||
|
||||
mmio->Register(base | PI_FIFO_RESET,
|
||||
MMIO::InvalidRead<u32>(),
|
||||
MMIO::ComplexWrite<u32>([](u32, u32 val) {
|
||||
WARN_LOG(PROCESSORINTERFACE, "Fifo reset (%08x)", val);
|
||||
})
|
||||
);
|
||||
|
||||
mmio->Register(base | PI_RESET_CODE,
|
||||
MMIO::DirectRead<u32>(&m_ResetCode),
|
||||
MMIO::DirectWrite<u32>(&m_ResetCode)
|
||||
);
|
||||
|
||||
mmio->Register(base | PI_FLIPPER_REV,
|
||||
MMIO::DirectRead<u32>(&m_FlipperRev),
|
||||
MMIO::InvalidWrite<u32>()
|
||||
);
|
||||
|
||||
// 16 bit reads are based on 32 bit reads.
|
||||
for (int i = 0; i < 0x1000; i += 4)
|
||||
{
|
||||
case PI_INTERRUPT_CAUSE:
|
||||
_uReturnValue = m_InterruptCause;
|
||||
return;
|
||||
|
||||
case PI_INTERRUPT_MASK:
|
||||
_uReturnValue = m_InterruptMask;
|
||||
return;
|
||||
|
||||
case PI_FIFO_BASE:
|
||||
DEBUG_LOG(PROCESSORINTERFACE, "Read CPU FIFO base, value = %08x", Fifo_CPUBase);
|
||||
_uReturnValue = Fifo_CPUBase;
|
||||
return;
|
||||
|
||||
case PI_FIFO_END:
|
||||
DEBUG_LOG(PROCESSORINTERFACE, "Read CPU FIFO end, value = %08x", Fifo_CPUEnd);
|
||||
_uReturnValue = Fifo_CPUEnd;
|
||||
return;
|
||||
|
||||
case PI_FIFO_WPTR:
|
||||
DEBUG_LOG(PROCESSORINTERFACE, "Read writepointer, value = %08x", Fifo_CPUWritePointer);
|
||||
_uReturnValue = Fifo_CPUWritePointer; //really writes in 32-byte chunks
|
||||
// Monk's gcube does some crazy align trickery here.
|
||||
return;
|
||||
|
||||
case PI_RESET_CODE:
|
||||
INFO_LOG(PROCESSORINTERFACE, "Read reset code, 0x%08x", m_ResetCode);
|
||||
_uReturnValue = m_ResetCode;
|
||||
return;
|
||||
|
||||
case PI_FLIPPER_REV:
|
||||
INFO_LOG(PROCESSORINTERFACE, "Read flipper rev, 0x%08x", m_FlipperRev);
|
||||
_uReturnValue = m_FlipperRev;
|
||||
return;
|
||||
|
||||
default:
|
||||
ERROR_LOG(PROCESSORINTERFACE, "!!!!Unknown write!!!! 0x%08x", _iAddress);
|
||||
break;
|
||||
}
|
||||
|
||||
_uReturnValue = 0xAFFE0000;
|
||||
}
|
||||
|
||||
void Write32(const u32 _uValue, const u32 _iAddress)
|
||||
{
|
||||
//INFO_LOG(PROCESSORINTERFACE, "(w32) 0x%08x @ 0x%08x", _uValue, _iAddress);
|
||||
switch(_iAddress & 0xFFF)
|
||||
{
|
||||
case PI_INTERRUPT_CAUSE:
|
||||
Common::AtomicAnd(m_InterruptCause, ~_uValue); // writes turn them off
|
||||
UpdateException();
|
||||
return;
|
||||
|
||||
case PI_INTERRUPT_MASK:
|
||||
m_InterruptMask = _uValue;
|
||||
DEBUG_LOG(PROCESSORINTERFACE,"New Interrupt mask: %08x", m_InterruptMask);
|
||||
UpdateException();
|
||||
return;
|
||||
|
||||
case PI_FIFO_BASE:
|
||||
Fifo_CPUBase = _uValue & 0xFFFFFFE0;
|
||||
DEBUG_LOG(PROCESSORINTERFACE,"Fifo base = %08x", _uValue);
|
||||
break;
|
||||
|
||||
case PI_FIFO_END:
|
||||
Fifo_CPUEnd = _uValue & 0xFFFFFFE0;
|
||||
DEBUG_LOG(PROCESSORINTERFACE,"Fifo end = %08x", _uValue);
|
||||
break;
|
||||
|
||||
case PI_FIFO_WPTR:
|
||||
Fifo_CPUWritePointer = _uValue & 0xFFFFFFE0;
|
||||
DEBUG_LOG(PROCESSORINTERFACE,"Fifo writeptr = %08x", _uValue);
|
||||
break;
|
||||
|
||||
case PI_FIFO_RESET:
|
||||
//Abort the actual frame
|
||||
//g_video_backend->Video_AbortFrame();
|
||||
//Fifo_CPUWritePointer = Fifo_CPUBase; ??
|
||||
//PanicAlert("Unknown write to PI_FIFO_RESET (%08x)", _uValue);
|
||||
WARN_LOG(PROCESSORINTERFACE, "Fifo reset (%08x)", _uValue);
|
||||
break;
|
||||
|
||||
case PI_RESET_CODE:
|
||||
DEBUG_LOG(PROCESSORINTERFACE, "Write %08x to PI_RESET_CODE", _uValue);
|
||||
m_ResetCode = _uValue;
|
||||
break;
|
||||
|
||||
case PI_FLIPPER_UNK:
|
||||
DEBUG_LOG(PROCESSORINTERFACE, "Write %08x to unknown PI register %08x", _uValue, _iAddress);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(PROCESSORINTERFACE,"!!!!Unknown PI write!!!! 0x%08x", _iAddress);
|
||||
PanicAlert("Unknown write to PI: %08x", _iAddress);
|
||||
break;
|
||||
mmio->Register(base | i,
|
||||
MMIO::ReadToLarger<u16>(mmio, base | i, 0),
|
||||
MMIO::InvalidWrite<u16>()
|
||||
);
|
||||
mmio->Register(base | (i + 2),
|
||||
MMIO::ReadToLarger<u16>(mmio, base | i, 16),
|
||||
MMIO::InvalidWrite<u16>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include "CommonTypes.h"
|
||||
class PointerWrap;
|
||||
|
||||
namespace MMIO { class Mapping; }
|
||||
|
||||
// Holds statuses of things like the write gatherer used for fifos, and interrupts from various sources
|
||||
|
||||
namespace ProcessorInterface
|
||||
|
@ -43,10 +45,7 @@ extern u32 Fifo_CPUWritePointer;
|
|||
void Init();
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
void Read16(u16& _uReturnValue, const u32 _iAddress);
|
||||
|
||||
void Read32(u32& _uReturnValue, const u32 _iAddress);
|
||||
void Write32(const u32 _iValue, const u32 _iAddress);
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||
|
||||
inline u32 GetMask() { return m_InterruptMask; }
|
||||
inline u32 GetCause() { return m_InterruptCause; }
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "../CoreTiming.h"
|
||||
#include "../Movie.h"
|
||||
#include "../NetPlayProto.h"
|
||||
#include "MMIO.h"
|
||||
|
||||
#include "SystemTimers.h"
|
||||
#include "ProcessorInterface.h"
|
||||
|
@ -281,157 +282,56 @@ void Shutdown()
|
|||
GBAConnectionWaiter_Shutdown();
|
||||
}
|
||||
|
||||
void Read32(u32& _uReturnValue, const u32 _iAddress)
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
// SIBuffer
|
||||
if ((_iAddress >= 0xCC006480 && _iAddress < 0xCC006500) ||
|
||||
(_iAddress >= 0xCD006480 && _iAddress < 0xCD006500))
|
||||
// Register SI buffer direct accesses.
|
||||
for (int i = 0; i < 0x80; i += 4)
|
||||
mmio->Register(base | (0x80 + i),
|
||||
MMIO::DirectRead<u32>((u32*)&g_SIBuffer[i]),
|
||||
MMIO::DirectWrite<u32>((u32*)&g_SIBuffer[i])
|
||||
);
|
||||
|
||||
// In and out for the 4 SI channels.
|
||||
for (int i = 0; i < MAX_SI_CHANNELS; ++i)
|
||||
{
|
||||
_uReturnValue = *(u32*)&g_SIBuffer[_iAddress & 0x7F];
|
||||
return;
|
||||
// We need to clear the RDST bit for the SI channel when reading.
|
||||
// CH0 -> Bit 24 + 5
|
||||
// CH1 -> Bit 16 + 5
|
||||
// CH2 -> Bit 8 + 5
|
||||
// CH3 -> Bit 0 + 5
|
||||
int rdst_bit = 8 * (3 - i) + 5;
|
||||
|
||||
mmio->Register(base | (SI_CHANNEL_0_OUT + 0xC * i),
|
||||
MMIO::DirectRead<u32>(&g_Channel[i].m_Out.Hex),
|
||||
MMIO::DirectWrite<u32>(&g_Channel[i].m_Out.Hex)
|
||||
);
|
||||
mmio->Register(base | (SI_CHANNEL_0_IN_HI + 0xC * i),
|
||||
MMIO::ComplexRead<u32>([i, rdst_bit](u32) {
|
||||
g_StatusReg.Hex &= ~(1 << rdst_bit);
|
||||
UpdateInterrupts();
|
||||
return g_Channel[i].m_InHi.Hex;
|
||||
}),
|
||||
MMIO::DirectWrite<u32>(&g_Channel[i].m_InHi.Hex)
|
||||
);
|
||||
mmio->Register(base | (SI_CHANNEL_0_IN_LO + 0xC * i),
|
||||
MMIO::ComplexRead<u32>([i, rdst_bit](u32) {
|
||||
g_StatusReg.Hex &= ~(1 << rdst_bit);
|
||||
UpdateInterrupts();
|
||||
return g_Channel[i].m_InLo.Hex;
|
||||
}),
|
||||
MMIO::DirectWrite<u32>(&g_Channel[i].m_InLo.Hex)
|
||||
);
|
||||
}
|
||||
|
||||
// error if not changed in the switch
|
||||
_uReturnValue = 0xdeadbeef;
|
||||
mmio->Register(base | SI_POLL,
|
||||
MMIO::DirectRead<u32>(&g_Poll.Hex),
|
||||
MMIO::DirectWrite<u32>(&g_Poll.Hex)
|
||||
);
|
||||
|
||||
// registers
|
||||
switch (_iAddress & 0x3FF)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Channel 0
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
case SI_CHANNEL_0_OUT:
|
||||
_uReturnValue = g_Channel[0].m_Out.Hex;
|
||||
break;
|
||||
|
||||
case SI_CHANNEL_0_IN_HI:
|
||||
g_StatusReg.RDST0 = 0;
|
||||
UpdateInterrupts();
|
||||
_uReturnValue = g_Channel[0].m_InHi.Hex;
|
||||
break;
|
||||
|
||||
case SI_CHANNEL_0_IN_LO:
|
||||
g_StatusReg.RDST0 = 0;
|
||||
UpdateInterrupts();
|
||||
_uReturnValue = g_Channel[0].m_InLo.Hex;
|
||||
break;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Channel 1
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
case SI_CHANNEL_1_OUT:
|
||||
_uReturnValue = g_Channel[1].m_Out.Hex;
|
||||
break;
|
||||
|
||||
case SI_CHANNEL_1_IN_HI:
|
||||
g_StatusReg.RDST1 = 0;
|
||||
UpdateInterrupts();
|
||||
_uReturnValue = g_Channel[1].m_InHi.Hex;
|
||||
break;
|
||||
|
||||
case SI_CHANNEL_1_IN_LO:
|
||||
g_StatusReg.RDST1 = 0;
|
||||
UpdateInterrupts();
|
||||
_uReturnValue = g_Channel[1].m_InLo.Hex;
|
||||
break;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Channel 2
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
case SI_CHANNEL_2_OUT:
|
||||
_uReturnValue = g_Channel[2].m_Out.Hex;
|
||||
break;
|
||||
|
||||
case SI_CHANNEL_2_IN_HI:
|
||||
g_StatusReg.RDST2 = 0;
|
||||
UpdateInterrupts();
|
||||
_uReturnValue = g_Channel[2].m_InHi.Hex;
|
||||
break;
|
||||
|
||||
case SI_CHANNEL_2_IN_LO:
|
||||
g_StatusReg.RDST2 = 0;
|
||||
UpdateInterrupts();
|
||||
_uReturnValue = g_Channel[2].m_InLo.Hex;
|
||||
break;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Channel 3
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
case SI_CHANNEL_3_OUT:
|
||||
_uReturnValue = g_Channel[3].m_Out.Hex;
|
||||
break;
|
||||
|
||||
case SI_CHANNEL_3_IN_HI:
|
||||
g_StatusReg.RDST3 = 0;
|
||||
UpdateInterrupts();
|
||||
_uReturnValue = g_Channel[3].m_InHi.Hex;
|
||||
break;
|
||||
|
||||
case SI_CHANNEL_3_IN_LO:
|
||||
g_StatusReg.RDST3 = 0;
|
||||
UpdateInterrupts();
|
||||
_uReturnValue = g_Channel[3].m_InLo.Hex;
|
||||
break;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Other
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
case SI_POLL: _uReturnValue = g_Poll.Hex; break;
|
||||
case SI_COM_CSR: _uReturnValue = g_ComCSR.Hex; break;
|
||||
case SI_STATUS_REG: _uReturnValue = g_StatusReg.Hex; break;
|
||||
|
||||
case SI_EXI_CLOCK_COUNT: _uReturnValue = g_EXIClockCount.Hex; break;
|
||||
|
||||
default:
|
||||
INFO_LOG(SERIALINTERFACE, "(r32-unk): 0x%08x", _iAddress);
|
||||
_dbg_assert_(SERIALINTERFACE,0);
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG_LOG(SERIALINTERFACE, "(r32) 0x%08x - 0x%08x", _iAddress, _uReturnValue);
|
||||
}
|
||||
|
||||
void Write32(const u32 _iValue, const u32 _iAddress)
|
||||
{
|
||||
DEBUG_LOG(SERIALINTERFACE, "(w32) 0x%08x @ 0x%08x", _iValue, _iAddress);
|
||||
|
||||
// SIBuffer
|
||||
if ((_iAddress >= 0xCC006480 && _iAddress < 0xCC006500) ||
|
||||
(_iAddress >= 0xCD006480 && _iAddress < 0xCD006500))
|
||||
{
|
||||
*(u32*)&g_SIBuffer[_iAddress & 0x7F] = _iValue;
|
||||
return;
|
||||
}
|
||||
|
||||
// registers
|
||||
switch (_iAddress & 0x3FF)
|
||||
{
|
||||
case SI_CHANNEL_0_OUT: g_Channel[0].m_Out.Hex = _iValue; break;
|
||||
case SI_CHANNEL_0_IN_HI: g_Channel[0].m_InHi.Hex = _iValue; break;
|
||||
case SI_CHANNEL_0_IN_LO: g_Channel[0].m_InLo.Hex = _iValue; break;
|
||||
case SI_CHANNEL_1_OUT: g_Channel[1].m_Out.Hex = _iValue; break;
|
||||
case SI_CHANNEL_1_IN_HI: g_Channel[1].m_InHi.Hex = _iValue; break;
|
||||
case SI_CHANNEL_1_IN_LO: g_Channel[1].m_InLo.Hex = _iValue; break;
|
||||
case SI_CHANNEL_2_OUT: g_Channel[2].m_Out.Hex = _iValue; break;
|
||||
case SI_CHANNEL_2_IN_HI: g_Channel[2].m_InHi.Hex = _iValue; break;
|
||||
case SI_CHANNEL_2_IN_LO: g_Channel[2].m_InLo.Hex = _iValue; break;
|
||||
case SI_CHANNEL_3_OUT: g_Channel[3].m_Out.Hex = _iValue; break;
|
||||
case SI_CHANNEL_3_IN_HI: g_Channel[3].m_InHi.Hex = _iValue; break;
|
||||
case SI_CHANNEL_3_IN_LO: g_Channel[3].m_InLo.Hex = _iValue; break;
|
||||
|
||||
case SI_POLL:
|
||||
INFO_LOG(SERIALINTERFACE, "Wrote Poll: X=%03d Y=%03d %s%s%s%s%s%s%s%s",
|
||||
g_Poll.X, g_Poll.Y,
|
||||
g_Poll.EN0 ? "EN0 ":" ", g_Poll.EN1 ? "EN1 ":" ",
|
||||
g_Poll.EN2 ? "EN2 ":" ", g_Poll.EN3 ? "EN3 ":" ",
|
||||
g_Poll.VBCPY0 ? "VBCPY0 ":" ", g_Poll.VBCPY1 ? "VBCPY1 ":" ",
|
||||
g_Poll.VBCPY2 ? "VBCPY2 ":" ", g_Poll.VBCPY3 ? "VBCPY3 ":" ");
|
||||
g_Poll.Hex = _iValue;
|
||||
break;
|
||||
|
||||
case SI_COM_CSR:
|
||||
{
|
||||
USIComCSR tmpComCSR(_iValue);
|
||||
mmio->Register(base | SI_COM_CSR,
|
||||
MMIO::DirectRead<u32>(&g_ComCSR.Hex),
|
||||
MMIO::ComplexWrite<u32>([](u32, u32 val) {
|
||||
USIComCSR tmpComCSR(val);
|
||||
|
||||
g_ComCSR.CHANNEL = tmpComCSR.CHANNEL;
|
||||
g_ComCSR.INLNGTH = tmpComCSR.INLNGTH;
|
||||
|
@ -447,12 +347,13 @@ void Write32(const u32 _iValue, const u32 _iAddress)
|
|||
// be careful: run si-buffer after updating the INT flags
|
||||
if (tmpComCSR.TSTART) RunSIBuffer();
|
||||
UpdateInterrupts();
|
||||
}
|
||||
break;
|
||||
})
|
||||
);
|
||||
|
||||
case SI_STATUS_REG:
|
||||
{
|
||||
USIStatusReg tmpStatus(_iValue);
|
||||
mmio->Register(base | SI_STATUS_REG,
|
||||
MMIO::DirectRead<u32>(&g_StatusReg.Hex),
|
||||
MMIO::ComplexWrite<u32>([](u32, u32 val) {
|
||||
USIStatusReg tmpStatus(val);
|
||||
|
||||
// clear bits ( if(tmp.bit) SISR.bit=0 )
|
||||
if (tmpStatus.NOREP0) g_StatusReg.NOREP0 = 0;
|
||||
|
@ -489,21 +390,13 @@ void Write32(const u32 _iValue, const u32 _iAddress)
|
|||
g_StatusReg.WRST2 = 0;
|
||||
g_StatusReg.WRST3 = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
})
|
||||
);
|
||||
|
||||
case SI_EXI_CLOCK_COUNT:
|
||||
g_EXIClockCount.Hex = _iValue;
|
||||
break;
|
||||
|
||||
case 0x80: // Bogus? never seen it with ma own eyes
|
||||
INFO_LOG(SERIALINTERFACE, "WII something at 0xCD006480");
|
||||
break;
|
||||
|
||||
default:
|
||||
_dbg_assert_(SERIALINTERFACE, 0);
|
||||
break;
|
||||
}
|
||||
mmio->Register(base | SI_EXI_CLOCK_COUNT,
|
||||
MMIO::DirectRead<u32>(&g_EXIClockCount.Hex),
|
||||
MMIO::DirectWrite<u32>(&g_EXIClockCount.Hex)
|
||||
);
|
||||
}
|
||||
|
||||
void UpdateInterrupts()
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "SI_Device.h"
|
||||
class PointerWrap;
|
||||
class ISIDevice;
|
||||
namespace MMIO { class Mapping; }
|
||||
|
||||
// SI number of channels
|
||||
enum
|
||||
|
@ -22,6 +23,8 @@ void Init();
|
|||
void Shutdown();
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||
|
||||
void UpdateDevices();
|
||||
|
||||
void RemoveDevice(int _iDeviceNumber);
|
||||
|
@ -31,9 +34,6 @@ void AddDevice(ISIDevice* pDevice);
|
|||
void ChangeDeviceCallback(u64 userdata, int cyclesLate);
|
||||
void ChangeDevice(SIDevices device, int channel);
|
||||
|
||||
void Read32(u32& _uReturnValue, const u32 _iAddress);
|
||||
void Write32(const u32 _iValue, const u32 _iAddress);
|
||||
|
||||
int GetTicksToNextSIPoll();
|
||||
|
||||
}; // end of namespace SerialInterface
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "../CoreTiming.h"
|
||||
#include "SystemTimers.h"
|
||||
#include "StringUtil.h"
|
||||
#include "MMIO.h"
|
||||
|
||||
#include "VideoBackendBase.h"
|
||||
#include "State.h"
|
||||
|
@ -182,268 +183,171 @@ void Init()
|
|||
UpdateParameters();
|
||||
}
|
||||
|
||||
void SetRegionReg(char region)
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
if (!Core::g_CoreStartupParameter.bForceNTSCJ)
|
||||
m_DTVStatus.ntsc_j = region == 'J';
|
||||
}
|
||||
struct {
|
||||
u32 addr;
|
||||
u16* ptr;
|
||||
} directly_mapped_vars[] = {
|
||||
{ VI_VERTICAL_TIMING, &m_VerticalTimingRegister.Hex },
|
||||
{ VI_HORIZONTAL_TIMING_0_HI, &m_HTiming0.Hi },
|
||||
{ VI_HORIZONTAL_TIMING_0_LO, &m_HTiming0.Lo },
|
||||
{ VI_HORIZONTAL_TIMING_1_HI, &m_HTiming1.Hi },
|
||||
{ VI_HORIZONTAL_TIMING_1_LO, &m_HTiming1.Lo },
|
||||
{ VI_VBLANK_TIMING_ODD_HI, &m_VBlankTimingOdd.Hi },
|
||||
{ VI_VBLANK_TIMING_ODD_LO, &m_VBlankTimingOdd.Lo },
|
||||
{ VI_VBLANK_TIMING_EVEN_HI, &m_VBlankTimingEven.Hi },
|
||||
{ VI_VBLANK_TIMING_EVEN_LO, &m_VBlankTimingEven.Lo },
|
||||
{ VI_BURST_BLANKING_ODD_HI, &m_BurstBlankingOdd.Hi },
|
||||
{ VI_BURST_BLANKING_ODD_LO, &m_BurstBlankingOdd.Lo },
|
||||
{ VI_BURST_BLANKING_EVEN_HI, &m_BurstBlankingEven.Hi },
|
||||
{ VI_BURST_BLANKING_EVEN_LO, &m_BurstBlankingEven.Lo },
|
||||
{ VI_FB_LEFT_TOP_LO, &m_XFBInfoTop.Lo },
|
||||
{ VI_FB_RIGHT_TOP_LO, &m_3DFBInfoTop.Lo },
|
||||
{ VI_FB_LEFT_BOTTOM_LO, &m_XFBInfoBottom.Lo },
|
||||
{ VI_FB_RIGHT_BOTTOM_LO, &m_3DFBInfoBottom.Lo },
|
||||
{ VI_PRERETRACE_LO, &m_InterruptRegister[0].Lo },
|
||||
{ VI_POSTRETRACE_LO, &m_InterruptRegister[1].Lo },
|
||||
{ VI_DISPLAY_INTERRUPT_2_LO, &m_InterruptRegister[2].Lo },
|
||||
{ VI_DISPLAY_INTERRUPT_3_LO, &m_InterruptRegister[3].Lo },
|
||||
{ VI_DISPLAY_LATCH_0_HI, &m_LatchRegister[0].Hi },
|
||||
{ VI_DISPLAY_LATCH_0_LO, &m_LatchRegister[0].Lo },
|
||||
{ VI_DISPLAY_LATCH_1_HI, &m_LatchRegister[1].Hi },
|
||||
{ VI_DISPLAY_LATCH_1_LO, &m_LatchRegister[1].Lo },
|
||||
{ VI_HSCALEW, &m_HorizontalStepping.Hex },
|
||||
{ VI_HSCALER, &m_HorizontalScaling.Hex },
|
||||
{ VI_FILTER_COEF_0_HI, &m_FilterCoefTables.Tables02[0].Hi },
|
||||
{ VI_FILTER_COEF_0_LO, &m_FilterCoefTables.Tables02[0].Lo },
|
||||
{ VI_FILTER_COEF_1_HI, &m_FilterCoefTables.Tables02[1].Hi },
|
||||
{ VI_FILTER_COEF_1_LO, &m_FilterCoefTables.Tables02[1].Lo },
|
||||
{ VI_FILTER_COEF_2_HI, &m_FilterCoefTables.Tables02[2].Hi },
|
||||
{ VI_FILTER_COEF_2_LO, &m_FilterCoefTables.Tables02[2].Lo },
|
||||
{ VI_FILTER_COEF_3_HI, &m_FilterCoefTables.Tables36[0].Hi },
|
||||
{ VI_FILTER_COEF_3_LO, &m_FilterCoefTables.Tables36[0].Lo },
|
||||
{ VI_FILTER_COEF_4_HI, &m_FilterCoefTables.Tables36[1].Hi },
|
||||
{ VI_FILTER_COEF_4_LO, &m_FilterCoefTables.Tables36[1].Lo },
|
||||
{ VI_FILTER_COEF_5_HI, &m_FilterCoefTables.Tables36[2].Hi },
|
||||
{ VI_FILTER_COEF_5_LO, &m_FilterCoefTables.Tables36[2].Lo },
|
||||
{ VI_FILTER_COEF_6_HI, &m_FilterCoefTables.Tables36[3].Hi },
|
||||
{ VI_FILTER_COEF_6_LO, &m_FilterCoefTables.Tables36[3].Lo },
|
||||
{ VI_CLOCK, &m_Clock },
|
||||
{ VI_DTV_STATUS, &m_DTVStatus.Hex },
|
||||
{ VI_FBWIDTH, &m_FBWidth },
|
||||
{ VI_BORDER_BLANK_END, &m_BorderHBlank.Lo },
|
||||
{ VI_BORDER_BLANK_START, &m_BorderHBlank.Hi },
|
||||
};
|
||||
|
||||
void Read8(u8& _uReturnValue, const u32 _iAddress)
|
||||
{
|
||||
// Just like 32bit VI transfers, this is technically not allowed,
|
||||
// but the hardware accepts it. Action Replay uses this.
|
||||
u16 val = 0;
|
||||
|
||||
if ((_iAddress & 1) == 0)
|
||||
// Declare all the boilerplate direct MMIOs.
|
||||
for (auto& mapped_var : directly_mapped_vars)
|
||||
{
|
||||
Read16(val, _iAddress);
|
||||
_uReturnValue = (u8)(val >> 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
Read16(val, _iAddress - 1);
|
||||
_uReturnValue = (u8)val;
|
||||
mmio->Register(base | mapped_var.addr,
|
||||
MMIO::DirectRead<u16>(mapped_var.ptr),
|
||||
MMIO::DirectWrite<u16>(mapped_var.ptr)
|
||||
);
|
||||
}
|
||||
|
||||
INFO_LOG(VIDEOINTERFACE, "(r 8): 0x%02x, 0x%08x", _uReturnValue, _iAddress);
|
||||
}
|
||||
// XFB related MMIOs that require special handling on writes.
|
||||
mmio->Register(base | VI_FB_LEFT_TOP_HI,
|
||||
MMIO::DirectRead<u16>(&m_XFBInfoTop.Hi),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
m_XFBInfoTop.Hi = val;
|
||||
if (m_XFBInfoTop.CLRPOFF) m_XFBInfoTop.POFF = 0;
|
||||
})
|
||||
);
|
||||
mmio->Register(base | VI_FB_LEFT_BOTTOM_HI,
|
||||
MMIO::DirectRead<u16>(&m_XFBInfoBottom.Hi),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
m_XFBInfoBottom.Hi = val;
|
||||
if (m_XFBInfoBottom.CLRPOFF) m_XFBInfoBottom.POFF = 0;
|
||||
})
|
||||
);
|
||||
mmio->Register(base | VI_FB_RIGHT_TOP_HI,
|
||||
MMIO::DirectRead<u16>(&m_3DFBInfoTop.Hi),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
m_3DFBInfoTop.Hi = val;
|
||||
if (m_3DFBInfoTop.CLRPOFF) m_3DFBInfoTop.POFF = 0;
|
||||
})
|
||||
);
|
||||
mmio->Register(base | VI_FB_RIGHT_BOTTOM_HI,
|
||||
MMIO::DirectRead<u16>(&m_3DFBInfoBottom.Hi),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
m_3DFBInfoBottom.Hi = val;
|
||||
if (m_3DFBInfoBottom.CLRPOFF) m_3DFBInfoBottom.POFF = 0;
|
||||
})
|
||||
);
|
||||
|
||||
void Read16(u16& _uReturnValue, const u32 _iAddress)
|
||||
{
|
||||
switch (_iAddress & 0xFFF)
|
||||
{
|
||||
case VI_VERTICAL_TIMING:
|
||||
_uReturnValue = m_VerticalTimingRegister.Hex;
|
||||
return;
|
||||
// MMIOs with unimplemented writes that trigger warnings.
|
||||
mmio->Register(base | VI_VERTICAL_BEAM_POSITION,
|
||||
MMIO::DirectRead<u16>(&m_VBeamPos),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
WARN_LOG(VIDEOINTERFACE, "Changing vertical beam position to 0x%04x - not documented or implemented yet", val);
|
||||
})
|
||||
);
|
||||
mmio->Register(base | VI_HORIZONTAL_BEAM_POSITION,
|
||||
MMIO::DirectRead<u16>(&m_HBeamPos),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
WARN_LOG(VIDEOINTERFACE, "Changing horizontal beam position to 0x%04x - not documented or implemented yet", val);
|
||||
})
|
||||
);
|
||||
|
||||
case VI_CONTROL_REGISTER:
|
||||
_uReturnValue = m_DisplayControlRegister.Hex;
|
||||
return;
|
||||
// The following MMIOs are interrupts related and update interrupt status
|
||||
// on writes.
|
||||
mmio->Register(base | VI_PRERETRACE_HI,
|
||||
MMIO::DirectRead<u16>(&m_InterruptRegister[0].Hi),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
m_InterruptRegister[0].Hi = val;
|
||||
UpdateInterrupts();
|
||||
})
|
||||
);
|
||||
mmio->Register(base | VI_POSTRETRACE_HI,
|
||||
MMIO::DirectRead<u16>(&m_InterruptRegister[1].Hi),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
m_InterruptRegister[1].Hi = val;
|
||||
UpdateInterrupts();
|
||||
})
|
||||
);
|
||||
mmio->Register(base | VI_DISPLAY_INTERRUPT_2_HI,
|
||||
MMIO::DirectRead<u16>(&m_InterruptRegister[2].Hi),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
m_InterruptRegister[2].Hi = val;
|
||||
UpdateInterrupts();
|
||||
})
|
||||
);
|
||||
mmio->Register(base | VI_DISPLAY_INTERRUPT_3_HI,
|
||||
MMIO::DirectRead<u16>(&m_InterruptRegister[3].Hi),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
m_InterruptRegister[3].Hi = val;
|
||||
UpdateInterrupts();
|
||||
})
|
||||
);
|
||||
|
||||
case VI_HORIZONTAL_TIMING_0_HI:
|
||||
_uReturnValue = m_HTiming0.Hi;
|
||||
break;
|
||||
case VI_HORIZONTAL_TIMING_0_LO:
|
||||
_uReturnValue = m_HTiming0.Lo;
|
||||
break;
|
||||
// Unknown anti-aliasing related MMIO register: puts a warning on log and
|
||||
// needs to shift/mask when reading/writing.
|
||||
mmio->Register(base | VI_UNK_AA_REG_HI,
|
||||
MMIO::ComplexRead<u16>([](u32) {
|
||||
return m_UnkAARegister >> 16;
|
||||
}),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
m_UnkAARegister = (m_UnkAARegister & 0x0000FFFF) | ((u32)val << 16);
|
||||
WARN_LOG(VIDEOINTERFACE, "Writing to the unknown AA register (hi)");
|
||||
})
|
||||
);
|
||||
mmio->Register(base | VI_UNK_AA_REG_LO,
|
||||
MMIO::ComplexRead<u16>([](u32) {
|
||||
return m_UnkAARegister & 0xFFFF;
|
||||
}),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
m_UnkAARegister = (m_UnkAARegister & 0xFFFF0000) | val;
|
||||
WARN_LOG(VIDEOINTERFACE, "Writing to the unknown AA register (lo)");
|
||||
})
|
||||
);
|
||||
|
||||
case VI_HORIZONTAL_TIMING_1_HI:
|
||||
_uReturnValue = m_HTiming1.Hi;
|
||||
break;
|
||||
case VI_HORIZONTAL_TIMING_1_LO:
|
||||
_uReturnValue = m_HTiming1.Lo;
|
||||
break;
|
||||
|
||||
case VI_VBLANK_TIMING_ODD_HI:
|
||||
_uReturnValue = m_VBlankTimingOdd.Hi;
|
||||
break;
|
||||
case VI_VBLANK_TIMING_ODD_LO:
|
||||
_uReturnValue = m_VBlankTimingOdd.Lo;
|
||||
break;
|
||||
|
||||
case VI_VBLANK_TIMING_EVEN_HI:
|
||||
_uReturnValue = m_VBlankTimingEven.Hi;
|
||||
break;
|
||||
case VI_VBLANK_TIMING_EVEN_LO:
|
||||
_uReturnValue = m_VBlankTimingEven.Lo;
|
||||
break;
|
||||
|
||||
case VI_BURST_BLANKING_ODD_HI:
|
||||
_uReturnValue = m_BurstBlankingOdd.Hi;
|
||||
break;
|
||||
case VI_BURST_BLANKING_ODD_LO:
|
||||
_uReturnValue = m_BurstBlankingOdd.Lo;
|
||||
break;
|
||||
|
||||
case VI_BURST_BLANKING_EVEN_HI:
|
||||
_uReturnValue = m_BurstBlankingEven.Hi;
|
||||
break;
|
||||
case VI_BURST_BLANKING_EVEN_LO:
|
||||
_uReturnValue = m_BurstBlankingEven.Lo;
|
||||
break;
|
||||
|
||||
case VI_FB_LEFT_TOP_HI:
|
||||
_uReturnValue = m_XFBInfoTop.Hi;
|
||||
break;
|
||||
case VI_FB_LEFT_TOP_LO:
|
||||
_uReturnValue = m_XFBInfoTop.Lo;
|
||||
break;
|
||||
|
||||
case VI_FB_RIGHT_TOP_HI:
|
||||
_uReturnValue = m_3DFBInfoTop.Hi;
|
||||
break;
|
||||
case VI_FB_RIGHT_TOP_LO:
|
||||
_uReturnValue = m_3DFBInfoTop.Lo;
|
||||
break;
|
||||
|
||||
case VI_FB_LEFT_BOTTOM_HI:
|
||||
_uReturnValue = m_XFBInfoBottom.Hi;
|
||||
break;
|
||||
case VI_FB_LEFT_BOTTOM_LO:
|
||||
_uReturnValue = m_XFBInfoBottom.Lo;
|
||||
break;
|
||||
|
||||
case VI_FB_RIGHT_BOTTOM_HI:
|
||||
_uReturnValue = m_3DFBInfoBottom.Hi;
|
||||
break;
|
||||
case VI_FB_RIGHT_BOTTOM_LO:
|
||||
_uReturnValue = m_3DFBInfoBottom.Lo;
|
||||
break;
|
||||
|
||||
case VI_VERTICAL_BEAM_POSITION:
|
||||
_uReturnValue = m_VBeamPos;
|
||||
return;
|
||||
|
||||
case VI_HORIZONTAL_BEAM_POSITION:
|
||||
_uReturnValue = m_HBeamPos;
|
||||
return;
|
||||
|
||||
// RETRACE STUFF ...
|
||||
case VI_PRERETRACE_HI:
|
||||
_uReturnValue = m_InterruptRegister[0].Hi;
|
||||
return;
|
||||
case VI_PRERETRACE_LO:
|
||||
_uReturnValue = m_InterruptRegister[0].Lo;
|
||||
return;
|
||||
|
||||
case VI_POSTRETRACE_HI:
|
||||
_uReturnValue = m_InterruptRegister[1].Hi;
|
||||
return;
|
||||
case VI_POSTRETRACE_LO:
|
||||
_uReturnValue = m_InterruptRegister[1].Lo;
|
||||
return;
|
||||
|
||||
case VI_DISPLAY_INTERRUPT_2_HI:
|
||||
_uReturnValue = m_InterruptRegister[2].Hi;
|
||||
return;
|
||||
case VI_DISPLAY_INTERRUPT_2_LO:
|
||||
_uReturnValue = m_InterruptRegister[2].Lo;
|
||||
return;
|
||||
|
||||
case VI_DISPLAY_INTERRUPT_3_HI:
|
||||
_uReturnValue = m_InterruptRegister[3].Hi;
|
||||
return;
|
||||
case VI_DISPLAY_INTERRUPT_3_LO:
|
||||
_uReturnValue = m_InterruptRegister[3].Lo;
|
||||
return;
|
||||
|
||||
case VI_DISPLAY_LATCH_0_HI:
|
||||
_uReturnValue = m_LatchRegister[0].Hi;
|
||||
break;
|
||||
case VI_DISPLAY_LATCH_0_LO:
|
||||
_uReturnValue = m_LatchRegister[0].Lo;
|
||||
break;
|
||||
|
||||
case VI_DISPLAY_LATCH_1_HI:
|
||||
_uReturnValue = m_LatchRegister[1].Hi;
|
||||
break;
|
||||
case VI_DISPLAY_LATCH_1_LO:
|
||||
_uReturnValue = m_LatchRegister[1].Lo;
|
||||
break;
|
||||
|
||||
case VI_HSCALEW:
|
||||
_uReturnValue = m_HorizontalStepping.Hex;
|
||||
break;
|
||||
|
||||
case VI_HSCALER:
|
||||
_uReturnValue = m_HorizontalScaling.Hex;
|
||||
break;
|
||||
|
||||
case VI_FILTER_COEF_0_HI:
|
||||
_uReturnValue = m_FilterCoefTables.Tables02[0].Hi;
|
||||
break;
|
||||
case VI_FILTER_COEF_0_LO:
|
||||
_uReturnValue = m_FilterCoefTables.Tables02[0].Lo;
|
||||
break;
|
||||
case VI_FILTER_COEF_1_HI:
|
||||
_uReturnValue = m_FilterCoefTables.Tables02[1].Hi;
|
||||
break;
|
||||
case VI_FILTER_COEF_1_LO:
|
||||
_uReturnValue = m_FilterCoefTables.Tables02[1].Lo;
|
||||
break;
|
||||
case VI_FILTER_COEF_2_HI:
|
||||
_uReturnValue = m_FilterCoefTables.Tables02[2].Hi;
|
||||
break;
|
||||
case VI_FILTER_COEF_2_LO:
|
||||
_uReturnValue = m_FilterCoefTables.Tables02[2].Lo;
|
||||
break;
|
||||
case VI_FILTER_COEF_3_HI:
|
||||
_uReturnValue = m_FilterCoefTables.Tables36[0].Hi;
|
||||
break;
|
||||
case VI_FILTER_COEF_3_LO:
|
||||
_uReturnValue = m_FilterCoefTables.Tables36[0].Lo;
|
||||
break;
|
||||
case VI_FILTER_COEF_4_HI:
|
||||
_uReturnValue = m_FilterCoefTables.Tables36[1].Hi;
|
||||
break;
|
||||
case VI_FILTER_COEF_4_LO:
|
||||
_uReturnValue = m_FilterCoefTables.Tables36[1].Lo;
|
||||
break;
|
||||
case VI_FILTER_COEF_5_HI:
|
||||
_uReturnValue = m_FilterCoefTables.Tables36[2].Hi;
|
||||
break;
|
||||
case VI_FILTER_COEF_5_LO:
|
||||
_uReturnValue = m_FilterCoefTables.Tables36[2].Lo;
|
||||
break;
|
||||
case VI_FILTER_COEF_6_HI:
|
||||
_uReturnValue = m_FilterCoefTables.Tables36[3].Hi;
|
||||
break;
|
||||
case VI_FILTER_COEF_6_LO:
|
||||
_uReturnValue = m_FilterCoefTables.Tables36[3].Lo;
|
||||
break;
|
||||
|
||||
case VI_UNK_AA_REG_HI:
|
||||
_uReturnValue = (m_UnkAARegister & 0xffff0000) >> 16;
|
||||
WARN_LOG(VIDEOINTERFACE, "(r16) unknown AA register, not sure what it does :)");
|
||||
break;
|
||||
case VI_UNK_AA_REG_LO:
|
||||
_uReturnValue = m_UnkAARegister & 0x0000ffff;
|
||||
WARN_LOG(VIDEOINTERFACE, "(r16) unknown AA register, not sure what it does :)");
|
||||
break;
|
||||
|
||||
case VI_CLOCK:
|
||||
_uReturnValue = m_Clock;
|
||||
break;
|
||||
|
||||
case VI_DTV_STATUS:
|
||||
_uReturnValue = m_DTVStatus.Hex;
|
||||
break;
|
||||
|
||||
case VI_FBWIDTH:
|
||||
_uReturnValue = m_FBWidth;
|
||||
break;
|
||||
|
||||
case VI_BORDER_BLANK_END:
|
||||
_uReturnValue = m_BorderHBlank.Lo;
|
||||
break;
|
||||
case VI_BORDER_BLANK_START:
|
||||
_uReturnValue = m_BorderHBlank.Hi;
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(VIDEOINTERFACE, "(r16) unknown reg %x", _iAddress & 0xfff);
|
||||
_uReturnValue = 0x0;
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG_LOG(VIDEOINTERFACE, "(r16): 0x%04x, 0x%08x", _uReturnValue, _iAddress);
|
||||
}
|
||||
|
||||
void Write16(const u16 _iValue, const u32 _iAddress)
|
||||
{
|
||||
DEBUG_LOG(VIDEOINTERFACE, "(w16): 0x%04x, 0x%08x",_iValue,_iAddress);
|
||||
|
||||
//Somewhere it sets screen width.. we need to communicate this to the gfx backend...
|
||||
|
||||
switch (_iAddress & 0xFFF)
|
||||
{
|
||||
case VI_VERTICAL_TIMING:
|
||||
m_VerticalTimingRegister.Hex = _iValue;
|
||||
break;
|
||||
|
||||
case VI_CONTROL_REGISTER:
|
||||
{
|
||||
UVIDisplayControlRegister tmpConfig(_iValue);
|
||||
// Control register writes only updates some select bits, and additional
|
||||
// processing needs to be done if a reset is requested.
|
||||
mmio->Register(base | VI_CONTROL_REGISTER,
|
||||
MMIO::DirectRead<u16>(&m_DisplayControlRegister.Hex),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
UVIDisplayControlRegister tmpConfig(val);
|
||||
m_DisplayControlRegister.ENB = tmpConfig.ENB;
|
||||
m_DisplayControlRegister.NIN = tmpConfig.NIN;
|
||||
m_DisplayControlRegister.DLR = tmpConfig.DLR;
|
||||
|
@ -461,242 +365,36 @@ void Write16(const u16 _iValue, const u32 _iAddress)
|
|||
}
|
||||
|
||||
UpdateParameters();
|
||||
}
|
||||
break;
|
||||
})
|
||||
);
|
||||
|
||||
case VI_HORIZONTAL_TIMING_0_HI:
|
||||
m_HTiming0.Hi = _iValue;
|
||||
break;
|
||||
case VI_HORIZONTAL_TIMING_0_LO:
|
||||
m_HTiming0.Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_HORIZONTAL_TIMING_1_HI:
|
||||
m_HTiming1.Hi = _iValue;
|
||||
break;
|
||||
case VI_HORIZONTAL_TIMING_1_LO:
|
||||
m_HTiming1.Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_VBLANK_TIMING_ODD_HI:
|
||||
m_VBlankTimingOdd.Hi = _iValue;
|
||||
break;
|
||||
case VI_VBLANK_TIMING_ODD_LO:
|
||||
m_VBlankTimingOdd.Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_VBLANK_TIMING_EVEN_HI:
|
||||
m_VBlankTimingEven.Hi = _iValue;
|
||||
break;
|
||||
case VI_VBLANK_TIMING_EVEN_LO:
|
||||
m_VBlankTimingEven.Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_BURST_BLANKING_ODD_HI:
|
||||
m_BurstBlankingOdd.Hi = _iValue;
|
||||
break;
|
||||
case VI_BURST_BLANKING_ODD_LO:
|
||||
m_BurstBlankingOdd.Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_BURST_BLANKING_EVEN_HI:
|
||||
m_BurstBlankingEven.Hi = _iValue;
|
||||
break;
|
||||
case VI_BURST_BLANKING_EVEN_LO:
|
||||
m_BurstBlankingEven.Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_FB_LEFT_TOP_HI:
|
||||
m_XFBInfoTop.Hi = _iValue;
|
||||
if (m_XFBInfoTop.CLRPOFF) m_XFBInfoTop.POFF = 0;
|
||||
break;
|
||||
case VI_FB_LEFT_TOP_LO:
|
||||
m_XFBInfoTop.Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_FB_RIGHT_TOP_HI:
|
||||
m_3DFBInfoTop.Hi = _iValue;
|
||||
if (m_3DFBInfoTop.CLRPOFF) m_3DFBInfoTop.POFF = 0;
|
||||
break;
|
||||
case VI_FB_RIGHT_TOP_LO:
|
||||
m_3DFBInfoTop.Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_FB_LEFT_BOTTOM_HI:
|
||||
m_XFBInfoBottom.Hi = _iValue;
|
||||
if (m_XFBInfoBottom.CLRPOFF) m_XFBInfoBottom.POFF = 0;
|
||||
break;
|
||||
case VI_FB_LEFT_BOTTOM_LO:
|
||||
m_XFBInfoBottom.Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_FB_RIGHT_BOTTOM_HI:
|
||||
m_3DFBInfoBottom.Hi = _iValue;
|
||||
if (m_3DFBInfoBottom.CLRPOFF) m_3DFBInfoBottom.POFF = 0;
|
||||
break;
|
||||
case VI_FB_RIGHT_BOTTOM_LO:
|
||||
m_3DFBInfoBottom.Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_VERTICAL_BEAM_POSITION:
|
||||
WARN_LOG(VIDEOINTERFACE, "Change Vertical Beam Position to 0x%04x - Not documented or implemented", _iValue);
|
||||
break;
|
||||
|
||||
case VI_HORIZONTAL_BEAM_POSITION:
|
||||
WARN_LOG(VIDEOINTERFACE, "Change Horizontal Beam Position to 0x%04x - Not documented or implemented", _iValue);
|
||||
break;
|
||||
|
||||
// RETRACE STUFF ...
|
||||
case VI_PRERETRACE_HI:
|
||||
m_InterruptRegister[0].Hi = _iValue;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
case VI_PRERETRACE_LO:
|
||||
m_InterruptRegister[0].Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_POSTRETRACE_HI:
|
||||
m_InterruptRegister[1].Hi = _iValue;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
case VI_POSTRETRACE_LO:
|
||||
m_InterruptRegister[1].Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_DISPLAY_INTERRUPT_2_HI:
|
||||
m_InterruptRegister[2].Hi = _iValue;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
case VI_DISPLAY_INTERRUPT_2_LO:
|
||||
m_InterruptRegister[2].Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_DISPLAY_INTERRUPT_3_HI:
|
||||
m_InterruptRegister[3].Hi = _iValue;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
case VI_DISPLAY_INTERRUPT_3_LO:
|
||||
m_InterruptRegister[3].Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_DISPLAY_LATCH_0_HI:
|
||||
m_LatchRegister[0].Hi = _iValue;
|
||||
break;
|
||||
case VI_DISPLAY_LATCH_0_LO:
|
||||
m_LatchRegister[0].Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_DISPLAY_LATCH_1_HI:
|
||||
m_LatchRegister[1].Hi = _iValue;
|
||||
break;
|
||||
case VI_DISPLAY_LATCH_1_LO:
|
||||
m_LatchRegister[1].Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_HSCALEW:
|
||||
m_HorizontalStepping.Hex = _iValue;
|
||||
break;
|
||||
|
||||
case VI_HSCALER:
|
||||
m_HorizontalScaling.Hex = _iValue;
|
||||
break;
|
||||
|
||||
case VI_FILTER_COEF_0_HI:
|
||||
m_FilterCoefTables.Tables02[0].Hi = _iValue;
|
||||
break;
|
||||
case VI_FILTER_COEF_0_LO:
|
||||
m_FilterCoefTables.Tables02[0].Lo = _iValue;
|
||||
break;
|
||||
case VI_FILTER_COEF_1_HI:
|
||||
m_FilterCoefTables.Tables02[1].Hi = _iValue;
|
||||
break;
|
||||
case VI_FILTER_COEF_1_LO:
|
||||
m_FilterCoefTables.Tables02[1].Lo = _iValue;
|
||||
break;
|
||||
case VI_FILTER_COEF_2_HI:
|
||||
m_FilterCoefTables.Tables02[2].Hi = _iValue;
|
||||
break;
|
||||
case VI_FILTER_COEF_2_LO:
|
||||
m_FilterCoefTables.Tables02[2].Lo = _iValue;
|
||||
break;
|
||||
case VI_FILTER_COEF_3_HI:
|
||||
m_FilterCoefTables.Tables36[0].Hi = _iValue;
|
||||
break;
|
||||
case VI_FILTER_COEF_3_LO:
|
||||
m_FilterCoefTables.Tables36[0].Lo = _iValue;
|
||||
break;
|
||||
case VI_FILTER_COEF_4_HI:
|
||||
m_FilterCoefTables.Tables36[1].Hi = _iValue;
|
||||
break;
|
||||
case VI_FILTER_COEF_4_LO:
|
||||
m_FilterCoefTables.Tables36[1].Lo = _iValue;
|
||||
break;
|
||||
case VI_FILTER_COEF_5_HI:
|
||||
m_FilterCoefTables.Tables36[2].Hi = _iValue;
|
||||
break;
|
||||
case VI_FILTER_COEF_5_LO:
|
||||
m_FilterCoefTables.Tables36[2].Lo = _iValue;
|
||||
break;
|
||||
case VI_FILTER_COEF_6_HI:
|
||||
m_FilterCoefTables.Tables36[3].Hi = _iValue;
|
||||
break;
|
||||
case VI_FILTER_COEF_6_LO:
|
||||
m_FilterCoefTables.Tables36[3].Lo = _iValue;
|
||||
break;
|
||||
|
||||
case VI_UNK_AA_REG_HI:
|
||||
m_UnkAARegister = (m_UnkAARegister & 0x0000ffff) | (u32)(_iValue << 16);
|
||||
WARN_LOG(VIDEOINTERFACE, "(w16) to unknown AA register, not sure what it does :)");
|
||||
break;
|
||||
case VI_UNK_AA_REG_LO:
|
||||
m_UnkAARegister = (m_UnkAARegister & 0xffff0000) | _iValue;
|
||||
WARN_LOG(VIDEOINTERFACE, "(w16) to unknown AA register, not sure what it does :)");
|
||||
break;
|
||||
|
||||
case VI_CLOCK:
|
||||
m_Clock = _iValue;
|
||||
break;
|
||||
|
||||
case VI_DTV_STATUS:
|
||||
m_DTVStatus.Hex = _iValue;
|
||||
break;
|
||||
|
||||
case VI_FBWIDTH:
|
||||
m_FBWidth = _iValue;
|
||||
break;
|
||||
|
||||
case VI_BORDER_BLANK_END:
|
||||
m_BorderHBlank.Lo = _iValue;
|
||||
break;
|
||||
case VI_BORDER_BLANK_START:
|
||||
m_BorderHBlank.Hi = _iValue;
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(VIDEOINTERFACE, "(w16) %04x to unknown register %x", _iValue, _iAddress & 0xfff);
|
||||
break;
|
||||
// Map 8 bit reads (not writes) to 16 bit reads.
|
||||
for (int i = 0; i < 0x1000; i += 2)
|
||||
{
|
||||
mmio->Register(base | i,
|
||||
MMIO::ReadToLarger<u8>(mmio, base | i, 8),
|
||||
MMIO::InvalidWrite<u8>()
|
||||
);
|
||||
mmio->Register(base | (i + 1),
|
||||
MMIO::ReadToLarger<u8>(mmio, base | i, 0),
|
||||
MMIO::InvalidWrite<u8>()
|
||||
);
|
||||
}
|
||||
|
||||
// Map 32 bit reads and writes to 16 bit reads and writes.
|
||||
for (int i = 0; i < 0x1000; i += 4)
|
||||
{
|
||||
mmio->Register(base | i,
|
||||
MMIO::ReadToSmaller<u32>(mmio, base | i, base | (i + 2)),
|
||||
MMIO::WriteToSmaller<u32>(mmio, base | i, base | (i + 2))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void Read32(u32& _uReturnValue, const u32 _iAddress)
|
||||
void SetRegionReg(char region)
|
||||
{
|
||||
u16 Hi = 0, Lo = 0;
|
||||
Read16(Hi, _iAddress);
|
||||
Read16(Lo, _iAddress + 2);
|
||||
_uReturnValue = (Hi << 16) | Lo;
|
||||
|
||||
INFO_LOG(VIDEOINTERFACE, "(r32): 0x%08x, 0x%08x", _uReturnValue, _iAddress);
|
||||
}
|
||||
|
||||
void Write32(const u32 _iValue, const u32 _iAddress)
|
||||
{
|
||||
INFO_LOG(VIDEOINTERFACE, "(w32): 0x%08x, 0x%08x", _iValue, _iAddress);
|
||||
|
||||
// Allow 32-bit writes to the VI: although this is officially not
|
||||
// allowed, the hardware seems to accept it (for example, DesktopMan GC
|
||||
// Tetris uses it).
|
||||
Write16(_iValue >> 16, _iAddress);
|
||||
Write16(_iValue & 0xFFFF, _iAddress + 2);
|
||||
if (!Core::g_CoreStartupParameter.bForceNTSCJ)
|
||||
m_DTVStatus.ntsc_j = region == 'J';
|
||||
}
|
||||
|
||||
void UpdateInterrupts()
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "CommonTypes.h"
|
||||
|
||||
class PointerWrap;
|
||||
namespace MMIO { class Mapping; }
|
||||
|
||||
namespace VideoInterface
|
||||
{
|
||||
|
@ -324,12 +326,7 @@ union UVIDTVStatus
|
|||
void SetRegionReg(char region);
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
void Read8(u8& _uReturnValue, const u32 _uAddress);
|
||||
void Read16(u16& _uReturnValue, const u32 _uAddress);
|
||||
void Read32(u32& _uReturnValue, const u32 _uAddress);
|
||||
|
||||
void Write16(const u16 _uValue, const u32 _uAddress);
|
||||
void Write32(const u32 _uValue, const u32 _uAddress);
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||
|
||||
// returns a pointer to the current visible xfb
|
||||
u32 GetXFBAddressTop();
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Common.h"
|
||||
#include "ChunkFile.h"
|
||||
#include "WII_IOB.h"
|
||||
|
||||
namespace WII_IOBridge
|
||||
{
|
||||
|
||||
void Read8(u8& _rReturnValue, const u32 _Address)
|
||||
{
|
||||
_dbg_assert_(WII_IOB, 0);
|
||||
}
|
||||
|
||||
void Read16(u16& _rReturnValue, const u32 _Address)
|
||||
{
|
||||
_dbg_assert_(WII_IOB, 0);
|
||||
}
|
||||
|
||||
void Read32(u32& _rReturnValue, const u32 _Address)
|
||||
{
|
||||
switch (_Address & 0xFFFF)
|
||||
{
|
||||
// NAND Loader ... no idea
|
||||
case 0x018:
|
||||
ERROR_LOG(WII_IOB, "IOP: Read32 from 0x18 = 0x%08x (NANDLoader)", _Address);
|
||||
break;
|
||||
// WiiMenu... no idea
|
||||
case 0x24:
|
||||
ERROR_LOG(WII_IOB, "IOP: Read32 from 0x24 = 0x%08x (WiiMenu)", _Address);
|
||||
break;
|
||||
|
||||
|
||||
case 0xc0: // __VISendI2CData
|
||||
_rReturnValue = 0;
|
||||
INFO_LOG(WII_IOB, "IOP: Read32 from 0xc0 = 0x%08x (__VISendI2CData)", _rReturnValue);
|
||||
break;
|
||||
|
||||
case 0xc4: // __VISendI2CData
|
||||
_rReturnValue = 0;
|
||||
INFO_LOG(WII_IOB, "IOP: Read32 from 0xc4 = 0x%08x (__VISendI2CData)", _rReturnValue);
|
||||
break;
|
||||
|
||||
case 0xc8: // __VISendI2CData
|
||||
_rReturnValue = 0;
|
||||
INFO_LOG(WII_IOB, "IOP: Read32 from 0xc8 = 0x%08x (__VISendI2CData)", _rReturnValue);
|
||||
break;
|
||||
|
||||
case 0x180: // __AIClockInit
|
||||
_rReturnValue = 0;
|
||||
INFO_LOG(WII_IOB, "IOP: Read32 from 0x180 = 0x%08x (__AIClockInit)", _rReturnValue);
|
||||
return;
|
||||
|
||||
case 0x1CC: // __AIClockInit
|
||||
_rReturnValue = 0;
|
||||
INFO_LOG(WII_IOB, "IOP: Read32 from 0x1CC = 0x%08x (__AIClockInit)", _rReturnValue);
|
||||
return;
|
||||
|
||||
case 0x1D0: // __AIClockInit
|
||||
_rReturnValue = 0;
|
||||
INFO_LOG(WII_IOB, "IOP: Read32 from 0x1D0 = 0x%08x (__AIClockInit)", _rReturnValue);
|
||||
return;
|
||||
|
||||
default:
|
||||
_dbg_assert_msg_(WII_IOB, 0, "IOP: Read32 from 0x%08x", _Address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Read64(u64& _rReturnValue, const u32 _Address)
|
||||
{
|
||||
_dbg_assert_(WII_IOB, 0);
|
||||
}
|
||||
|
||||
void Write8(const u8 _Value, const u32 _Address)
|
||||
{
|
||||
_dbg_assert_(WII_IOB, 0);
|
||||
}
|
||||
|
||||
void Write16(const u16 _Value, const u32 _Address)
|
||||
{
|
||||
_dbg_assert_(WII_IOB, 0);
|
||||
}
|
||||
|
||||
void Write32(const u32 _Value, const u32 _Address)
|
||||
{
|
||||
switch(_Address & 0xFFFF)
|
||||
{
|
||||
// NANDLoader ... no idea
|
||||
case 0x18:
|
||||
ERROR_LOG(WII_IOB, "IOP: Write32 0x%08x to 0x%08x (NANDLoader)", _Value, _Address);
|
||||
break;
|
||||
// WiiMenu... no idea
|
||||
case 0x24:
|
||||
ERROR_LOG(WII_IOB, "IOP: Write32 0x%08x to 0x%08x (WiiMenu)", _Value, _Address);
|
||||
break;
|
||||
|
||||
case 0xc0: // __VISendI2CData
|
||||
INFO_LOG(WII_IOB, "IOP: Write32 to 0xc0 = 0x%08x (__VISendI2CData)", _Value);
|
||||
break;
|
||||
|
||||
case 0xc4: // __VISendI2CData
|
||||
INFO_LOG(WII_IOB, "IOP: Write32 to 0xc4 = 0x%08x (__VISendI2CData)", _Value);
|
||||
break;
|
||||
|
||||
case 0xc8: // __VISendI2CData
|
||||
INFO_LOG(WII_IOB, "IOP: Write32 to 0xc8 = 0x%08x (__VISendI2CData)", _Value);
|
||||
break;
|
||||
|
||||
case 0x180: // __AIClockInit
|
||||
INFO_LOG(WII_IOB, "IOP: Write32 to 0x180 = 0x%08x (__AIClockInit)", _Value);
|
||||
return;
|
||||
|
||||
case 0x1CC: // __AIClockInit
|
||||
INFO_LOG(WII_IOB, "IOP: Write32 to 0x1D0 = 0x%08x (__AIClockInit)", _Value);
|
||||
return;
|
||||
|
||||
case 0x1D0: // __AIClockInit
|
||||
INFO_LOG(WII_IOB, "IOP: Write32 to 0x1D0 = 0x%08x (__AIClockInit)", _Value);
|
||||
return;
|
||||
|
||||
default:
|
||||
_dbg_assert_msg_(WII_IOB, 0, "IOP: Write32 to 0x%08x", _Address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Write64(const u64 _Value, const u32 _Address)
|
||||
{
|
||||
//switch(_Address)
|
||||
//{
|
||||
//default:
|
||||
_dbg_assert_msg_(WII_IOB, 0, "IOP: Write32 to 0x%08x", _Address);
|
||||
//break;
|
||||
//}
|
||||
}
|
||||
|
||||
} // end of namespace AudioInterfac
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
class PointerWrap;
|
||||
|
||||
namespace WII_IOBridge
|
||||
{
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
void Update();
|
||||
|
||||
void Read8(u8& _rReturnValue, const u32 _Address);
|
||||
void Read16(u16& _rReturnValue, const u32 _Address);
|
||||
void Read32(u32& _rReturnValue, const u32 _Address);
|
||||
void Read64(u64& _rReturnValue, const u32 _Address);
|
||||
|
||||
void Write8(const u8 _Value, const u32 _Address);
|
||||
void Write16(const u16 _Value, const u32 _Address);
|
||||
void Write32(const u32 _Value, const u32 _Address);
|
||||
void Write64(const u64 _Value, const u32 _Address);
|
||||
|
||||
} // end of namespace AudioInterface
|
|
@ -12,6 +12,7 @@
|
|||
#include "CPU.h"
|
||||
#include "Memmap.h"
|
||||
#include "ProcessorInterface.h"
|
||||
#include "MMIO.h"
|
||||
|
||||
#include "../IPC_HLE/WII_IPC_HLE.h"
|
||||
#include "WII_IPC.h"
|
||||
|
@ -37,12 +38,21 @@ enum
|
|||
IPC_ARMMSG = 0x08,
|
||||
IPC_ARMCTRL = 0x0c,
|
||||
|
||||
PPCSPEED = 0x18,
|
||||
VISOLID = 0x24,
|
||||
|
||||
PPC_IRQFLAG = 0x30,
|
||||
PPC_IRQMASK = 0x34,
|
||||
ARM_IRQFLAG = 0x38,
|
||||
ARM_IRQMASK = 0x3c,
|
||||
|
||||
GPIOB_OUT = 0xc0 // sensor bar power flag??
|
||||
GPIOB_OUT = 0xc0,
|
||||
GPIOB_DIR = 0xc4,
|
||||
GPIOB_IN = 0xc8,
|
||||
|
||||
UNK_180 = 0x180,
|
||||
UNK_1CC = 0x1cc,
|
||||
UNK_1D0 = 0x1d0,
|
||||
};
|
||||
|
||||
struct CtrlRegister
|
||||
|
@ -135,86 +145,64 @@ void Shutdown()
|
|||
{
|
||||
}
|
||||
|
||||
void Read32(u32& _rReturnValue, const u32 _Address)
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
switch(_Address & 0xFFFF)
|
||||
{
|
||||
case IPC_PPCCTRL:
|
||||
_rReturnValue = ctrl.ppc();
|
||||
DEBUG_LOG(WII_IPC, "r32 IPC_PPCCTRL %03x [R:%i A:%i E:%i]",
|
||||
ctrl.ppc(), ctrl.Y1, ctrl.Y2, ctrl.X1);
|
||||
mmio->Register(base | IPC_PPCMSG,
|
||||
MMIO::InvalidRead<u32>(),
|
||||
MMIO::DirectWrite<u32>(&ppc_msg)
|
||||
);
|
||||
|
||||
// if ((REASON_REG & 0x14) == 0x14) CALL IPCReplayHandler
|
||||
// if ((REASON_REG & 0x22) != 0x22) Jumps to the end
|
||||
break;
|
||||
|
||||
case IPC_ARMMSG: // looks a little bit like a callback function
|
||||
_rReturnValue = arm_msg;
|
||||
DEBUG_LOG(WII_IPC, "r32 IPC_ARMMSG %08x ", _rReturnValue);
|
||||
break;
|
||||
|
||||
case GPIOB_OUT:
|
||||
_rReturnValue = sensorbar_power;
|
||||
break;
|
||||
|
||||
default:
|
||||
_dbg_assert_msg_(WII_IPC, 0, "r32 from %08x", _Address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Write32(const u32 _Value, const u32 _Address)
|
||||
{
|
||||
switch(_Address & 0xFFFF)
|
||||
{
|
||||
case IPC_PPCMSG: // __ios_Ipc2 ... a value from __responses is loaded
|
||||
{
|
||||
ppc_msg = _Value;
|
||||
DEBUG_LOG(WII_IPC, "IPC_PPCMSG = %08x", ppc_msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case IPC_PPCCTRL:
|
||||
{
|
||||
ctrl.ppc(_Value);
|
||||
DEBUG_LOG(WII_IPC, "w32 %08x IPC_PPCCTRL = %03x [R:%i A:%i E:%i]",
|
||||
_Value, ctrl.ppc(), ctrl.Y1, ctrl.Y2, ctrl.X1);
|
||||
mmio->Register(base | IPC_PPCCTRL,
|
||||
MMIO::ComplexRead<u32>([](u32) {
|
||||
return ctrl.ppc();
|
||||
}),
|
||||
MMIO::ComplexWrite<u32>([](u32, u32 val) {
|
||||
ctrl.ppc(val);
|
||||
if (ctrl.X1)
|
||||
{
|
||||
INFO_LOG(WII_IPC, "New pointer available: %08x", ppc_msg);
|
||||
// Let the HLE handle the request on it's own time
|
||||
WII_IPC_HLE_Interface::EnqRequest(ppc_msg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
WII_IPC_HLE_Interface::Update();
|
||||
CoreTiming::ScheduleEvent_Threadsafe(0, updateInterrupts, 0);
|
||||
})
|
||||
);
|
||||
|
||||
case PPC_IRQFLAG: // ACR REGISTER IT IS CALLED IN DEBUG
|
||||
{
|
||||
ppc_irq_flags &= ~_Value;
|
||||
DEBUG_LOG(WII_IPC, "w32 PPC_IRQFLAG %08x (%08x)", _Value, ppc_irq_flags);
|
||||
}
|
||||
break;
|
||||
mmio->Register(base | IPC_ARMMSG,
|
||||
MMIO::DirectRead<u32>(&arm_msg),
|
||||
MMIO::InvalidWrite<u32>()
|
||||
);
|
||||
|
||||
case PPC_IRQMASK: // __OSInterruptInit (0x40000000)
|
||||
{
|
||||
ppc_irq_masks = _Value;
|
||||
mmio->Register(base | PPC_IRQFLAG,
|
||||
MMIO::InvalidRead<u32>(),
|
||||
MMIO::ComplexWrite<u32>([](u32, u32 val) {
|
||||
ppc_irq_flags &= ~val;
|
||||
WII_IPC_HLE_Interface::Update();
|
||||
CoreTiming::ScheduleEvent_Threadsafe(0, updateInterrupts, 0);
|
||||
})
|
||||
);
|
||||
|
||||
mmio->Register(base | PPC_IRQMASK,
|
||||
MMIO::InvalidRead<u32>(),
|
||||
MMIO::ComplexWrite<u32>([](u32, u32 val) {
|
||||
ppc_irq_masks = val;
|
||||
if (ppc_irq_masks & INT_CAUSE_IPC_BROADWAY) // wtf?
|
||||
Reset();
|
||||
DEBUG_LOG(WII_IPC, "w32 PPC_IRQMASK %08x", ppc_irq_masks);
|
||||
}
|
||||
break;
|
||||
WII_IPC_HLE_Interface::Update();
|
||||
CoreTiming::ScheduleEvent_Threadsafe(0, updateInterrupts, 0);
|
||||
})
|
||||
);
|
||||
|
||||
case GPIOB_OUT:
|
||||
sensorbar_power = _Value;
|
||||
break;
|
||||
mmio->Register(base | GPIOB_OUT,
|
||||
MMIO::Constant<u32>(0),
|
||||
MMIO::DirectWrite<u32>(&sensorbar_power)
|
||||
);
|
||||
|
||||
default:
|
||||
_dbg_assert_msg_(WII_IPC, 0, "w32 %08x @ %08x", _Value, _Address);
|
||||
break;
|
||||
}
|
||||
|
||||
WII_IPC_HLE_Interface::Update();
|
||||
CoreTiming::ScheduleEvent_Threadsafe(0, updateInterrupts, 0);
|
||||
// Register some stubbed/unknown MMIOs required to make Wii games work.
|
||||
mmio->Register(base | PPCSPEED, MMIO::InvalidRead<u32>(), MMIO::Nop<u32>());
|
||||
mmio->Register(base | VISOLID, MMIO::InvalidRead<u32>(), MMIO::Nop<u32>());
|
||||
mmio->Register(base | GPIOB_DIR, MMIO::Constant<u32>(0), MMIO::Nop<u32>());
|
||||
mmio->Register(base | GPIOB_IN, MMIO::Constant<u32>(0), MMIO::Nop<u32>());
|
||||
mmio->Register(base | UNK_180, MMIO::Constant<u32>(0), MMIO::Nop<u32>());
|
||||
mmio->Register(base | UNK_1CC, MMIO::Constant<u32>(0), MMIO::Nop<u32>());
|
||||
mmio->Register(base | UNK_1D0, MMIO::Constant<u32>(0), MMIO::Nop<u32>());
|
||||
}
|
||||
|
||||
void UpdateInterrupts(u64 userdata, int cyclesLate)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "Common.h"
|
||||
class PointerWrap;
|
||||
namespace MMIO { class Mapping; }
|
||||
|
||||
namespace WII_IPCInterface
|
||||
{
|
||||
|
@ -36,8 +37,7 @@ void Reset();
|
|||
void Shutdown();
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
void Read32(u32& _rReturnValue, const u32 _Address);
|
||||
void Write32(const u32 _Value, const u32 _Address);
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||
|
||||
void UpdateInterrupts(u64 userdata = 0, int cyclesLate = 0);
|
||||
void GenerateAck(u32 _Address);
|
||||
|
|
|
@ -59,7 +59,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
|
|||
static std::thread g_save_thread;
|
||||
|
||||
// Don't forget to increase this after doing changes on the savestate system
|
||||
static const u32 STATE_VERSION = 21;
|
||||
static const u32 STATE_VERSION = 22;
|
||||
|
||||
enum
|
||||
{
|
||||
|
|
|
@ -437,8 +437,7 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data)
|
|||
D3D::context->Unmap(read_tex, 0);
|
||||
|
||||
// check what to do with the alpha channel (GX_PokeAlphaRead)
|
||||
PixelEngine::UPEAlphaReadReg alpha_read_mode;
|
||||
PixelEngine::Read16((u16&)alpha_read_mode, PE_ALPHAREAD);
|
||||
PixelEngine::UPEAlphaReadReg alpha_read_mode = PixelEngine::GetAlphaReadMode();
|
||||
|
||||
if (bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24)
|
||||
{
|
||||
|
|
|
@ -1043,8 +1043,7 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data)
|
|||
color = s_efbCache[1][cacheRectIdx][yRect * EFB_CACHE_RECT_SIZE + xRect];
|
||||
|
||||
// check what to do with the alpha channel (GX_PokeAlphaRead)
|
||||
PixelEngine::UPEAlphaReadReg alpha_read_mode;
|
||||
PixelEngine::Read16((u16&)alpha_read_mode, PE_ALPHAREAD);
|
||||
PixelEngine::UPEAlphaReadReg alpha_read_mode = PixelEngine::GetAlphaReadMode();
|
||||
|
||||
if (bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24)
|
||||
{
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "Core.h"
|
||||
#include "CoreTiming.h"
|
||||
#include "HW/Memmap.h"
|
||||
#include "HW/MMIO.h"
|
||||
#include "HW/ProcessorInterface.h"
|
||||
|
||||
#include "VideoBackend.h"
|
||||
|
@ -124,147 +125,53 @@ void RunGpu()
|
|||
}
|
||||
}
|
||||
|
||||
void Read16(u16& _rReturnValue, const u32 _Address)
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
u32 regAddr = (_Address & 0xFFF) >> 1;
|
||||
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "(r): 0x%08x : 0x%08x", _Address, ((u16*)&cpreg)[regAddr]);
|
||||
|
||||
if (regAddr < 0x20)
|
||||
_rReturnValue = ((u16*)&cpreg)[regAddr];
|
||||
else
|
||||
_rReturnValue = 0;
|
||||
}
|
||||
|
||||
void Write16(const u16 _Value, const u32 _Address)
|
||||
{
|
||||
INFO_LOG(COMMANDPROCESSOR, "(write16): 0x%04x @ 0x%08x",_Value,_Address);
|
||||
|
||||
switch (_Address & 0xFFF)
|
||||
// Directly map reads and writes to the cpreg structure.
|
||||
for (size_t i = 0; i < sizeof (cpreg) / sizeof (u16); ++i)
|
||||
{
|
||||
case STATUS_REGISTER:
|
||||
{
|
||||
ERROR_LOG(COMMANDPROCESSOR,"\t write to STATUS_REGISTER : %04x", _Value);
|
||||
}
|
||||
break;
|
||||
u16* ptr = ((u16*)&cpreg) + i;
|
||||
mmio->Register(base | (i * 2),
|
||||
MMIO::DirectRead<u16>(ptr),
|
||||
MMIO::DirectWrite<u16>(ptr)
|
||||
);
|
||||
}
|
||||
|
||||
case CTRL_REGISTER:
|
||||
{
|
||||
cpreg.ctrl.Hex = _Value;
|
||||
// Bleh. Apparently SWCommandProcessor does not know about regs 0x40 to
|
||||
// 0x64...
|
||||
for (size_t i = 0x40; i < 0x64; ++i)
|
||||
{
|
||||
mmio->Register(base | i,
|
||||
MMIO::Constant<u16>(0),
|
||||
MMIO::Nop<u16>()
|
||||
);
|
||||
}
|
||||
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to CTRL_REGISTER : %04x", _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "\t GPREAD %s | CPULINK %s | BP %s || BPIntEnable %s | OvF %s | UndF %s"
|
||||
, cpreg.ctrl.GPReadEnable ? "ON" : "OFF"
|
||||
, cpreg.ctrl.GPLinkEnable ? "ON" : "OFF"
|
||||
, cpreg.ctrl.BPEnable ? "ON" : "OFF"
|
||||
, cpreg.ctrl.BreakPointIntEnable ? "ON" : "OFF"
|
||||
, cpreg.ctrl.FifoOverflowIntEnable ? "ON" : "OFF"
|
||||
, cpreg.ctrl.FifoUnderflowIntEnable ? "ON" : "OFF"
|
||||
);
|
||||
}
|
||||
break;
|
||||
// The low part of MMIO regs for FIFO addresses needs to be aligned to 32
|
||||
// bytes.
|
||||
u32 fifo_addr_lo_regs[] = {
|
||||
FIFO_BASE_LO, FIFO_END_LO, FIFO_WRITE_POINTER_LO,
|
||||
FIFO_READ_POINTER_LO, FIFO_BP_LO, FIFO_RW_DISTANCE_LO,
|
||||
};
|
||||
for (u32 reg : fifo_addr_lo_regs)
|
||||
{
|
||||
mmio->RegisterWrite(base | reg,
|
||||
MMIO::DirectWrite<u16>(((u16*)&cpreg) + (reg / 2), 0xFFE0)
|
||||
);
|
||||
}
|
||||
|
||||
case CLEAR_REGISTER:
|
||||
{
|
||||
UCPClearReg tmpClear(_Value);
|
||||
// The clear register needs to perform some more complicated operations on
|
||||
// writes.
|
||||
mmio->RegisterWrite(base | CLEAR_REGISTER,
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
UCPClearReg tmpClear(val);
|
||||
|
||||
if (tmpClear.ClearFifoOverflow)
|
||||
cpreg.status.OverflowHiWatermark = 0;
|
||||
if (tmpClear.ClearFifoUnderflow)
|
||||
cpreg.status.UnderflowLoWatermark = 0;
|
||||
|
||||
INFO_LOG(COMMANDPROCESSOR,"\t write to CLEAR_REGISTER : %04x",_Value);
|
||||
}
|
||||
break;
|
||||
|
||||
// Fifo Registers
|
||||
case FIFO_TOKEN_REGISTER:
|
||||
cpreg.token = _Value;
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_TOKEN_REGISTER : %04x", _Value);
|
||||
break;
|
||||
|
||||
case FIFO_BASE_LO:
|
||||
WriteLow ((u32 &)cpreg.fifobase, _Value & 0xFFE0);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_BASE_LO. FIFO base is : %08x", cpreg.fifobase);
|
||||
break;
|
||||
case FIFO_BASE_HI:
|
||||
WriteHigh((u32 &)cpreg.fifobase, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_BASE_HI. FIFO base is : %08x", cpreg.fifobase);
|
||||
break;
|
||||
case FIFO_END_LO:
|
||||
WriteLow ((u32 &)cpreg.fifoend, _Value & 0xFFE0);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_END_LO. FIFO end is : %08x", cpreg.fifoend);
|
||||
break;
|
||||
case FIFO_END_HI:
|
||||
WriteHigh((u32 &)cpreg.fifoend, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_END_HI. FIFO end is : %08x", cpreg.fifoend);
|
||||
break;
|
||||
|
||||
case FIFO_WRITE_POINTER_LO:
|
||||
WriteLow ((u32 &)cpreg.writeptr, _Value & 0xFFE0);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_WRITE_POINTER_LO. write ptr is : %08x", cpreg.writeptr);
|
||||
break;
|
||||
case FIFO_WRITE_POINTER_HI:
|
||||
WriteHigh ((u32 &)cpreg.writeptr, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_WRITE_POINTER_HI. write ptr is : %08x", cpreg.writeptr);
|
||||
break;
|
||||
case FIFO_READ_POINTER_LO:
|
||||
WriteLow ((u32 &)cpreg.readptr, _Value & 0xFFE0);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_READ_POINTER_LO. read ptr is : %08x", cpreg.readptr);
|
||||
break;
|
||||
case FIFO_READ_POINTER_HI:
|
||||
WriteHigh ((u32 &)cpreg.readptr, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_READ_POINTER_HI. read ptr is : %08x", cpreg.readptr);
|
||||
break;
|
||||
|
||||
case FIFO_HI_WATERMARK_LO:
|
||||
WriteLow ((u32 &)cpreg.hiwatermark, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_HI_WATERMARK_LO. hiwatermark is : %08x", cpreg.hiwatermark);
|
||||
break;
|
||||
case FIFO_HI_WATERMARK_HI:
|
||||
WriteHigh ((u32 &)cpreg.hiwatermark, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_HI_WATERMARK_HI. hiwatermark is : %08x", cpreg.hiwatermark);
|
||||
break;
|
||||
case FIFO_LO_WATERMARK_LO:
|
||||
WriteLow ((u32 &)cpreg.lowatermark, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_LO_WATERMARK_LO. lowatermark is : %08x", cpreg.lowatermark);
|
||||
break;
|
||||
case FIFO_LO_WATERMARK_HI:
|
||||
WriteHigh ((u32 &)cpreg.lowatermark, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_LO_WATERMARK_HI. lowatermark is : %08x", cpreg.lowatermark);
|
||||
break;
|
||||
|
||||
case FIFO_BP_LO:
|
||||
WriteLow ((u32 &)cpreg.breakpt, _Value & 0xFFE0);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_BP_LO. breakpoint is : %08x", cpreg.breakpt);
|
||||
break;
|
||||
case FIFO_BP_HI:
|
||||
WriteHigh ((u32 &)cpreg.breakpt, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_BP_HI. breakpoint is : %08x", cpreg.breakpt);
|
||||
break;
|
||||
|
||||
case FIFO_RW_DISTANCE_LO:
|
||||
WriteLow ((u32 &)cpreg.rwdistance, _Value & 0xFFE0);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_RW_DISTANCE_LO. rwdistance is : %08x", cpreg.rwdistance);
|
||||
break;
|
||||
case FIFO_RW_DISTANCE_HI:
|
||||
WriteHigh ((u32 &)cpreg.rwdistance, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_RW_DISTANCE_HI. rwdistance is : %08x", cpreg.rwdistance);
|
||||
break;
|
||||
}
|
||||
|
||||
RunGpu();
|
||||
}
|
||||
|
||||
void Read32(u32& _rReturnValue, const u32 _Address)
|
||||
{
|
||||
_rReturnValue = 0;
|
||||
_dbg_assert_msg_(COMMANDPROCESSOR, 0, "Read32 from CommandProcessor at 0x%08x", _Address);
|
||||
}
|
||||
|
||||
void Write32(const u32 _Data, const u32 _Address)
|
||||
{
|
||||
_dbg_assert_msg_(COMMANDPROCESSOR, 0, "Write32 at CommandProcessor at 0x%08x", _Address);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
void STACKALIGN GatherPipeBursted()
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "Common.h"
|
||||
|
||||
class PointerWrap;
|
||||
namespace MMIO { class Mapping; }
|
||||
|
||||
extern volatile bool g_bSkipCurrentFrame;
|
||||
extern u8* g_pVideoData;
|
||||
|
@ -123,15 +124,11 @@ namespace SWCommandProcessor
|
|||
void Shutdown();
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||
|
||||
bool RunBuffer();
|
||||
void RunGpu();
|
||||
|
||||
// Read
|
||||
void Read16(u16& _rReturnValue, const u32 _Address);
|
||||
void Write16(const u16 _Data, const u32 _Address);
|
||||
void Read32(u32& _rReturnValue, const u32 _Address);
|
||||
void Write32(const u32 _Data, const u32 _Address);
|
||||
|
||||
// for CGPFIFO
|
||||
void GatherPipeBursted();
|
||||
void UpdateInterrupts(u64 userdata);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "ChunkFile.h"
|
||||
#include "CoreTiming.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "HW/MMIO.h"
|
||||
#include "HW/ProcessorInterface.h"
|
||||
|
||||
#include "SWPixelEngine.h"
|
||||
|
@ -60,30 +61,22 @@ void Init()
|
|||
et_SetFinishOnMainThread = CoreTiming::RegisterEvent("SetFinish", SetFinish_OnMainThread);
|
||||
}
|
||||
|
||||
void Read16(u16& _uReturnValue, const u32 _iAddress)
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
DEBUG_LOG(PIXELENGINE, "(r16): 0x%08x", _iAddress);
|
||||
|
||||
u16 address = _iAddress & 0xFFF;
|
||||
|
||||
if (address <= 0x2e)
|
||||
_uReturnValue = ((u16*)&pereg)[address >> 1];
|
||||
}
|
||||
|
||||
void Write32(const u32 _iValue, const u32 _iAddress)
|
||||
{
|
||||
WARN_LOG(PIXELENGINE, "(w32): 0x%08x @ 0x%08x",_iValue,_iAddress);
|
||||
}
|
||||
|
||||
void Write16(const u16 _iValue, const u32 _iAddress)
|
||||
{
|
||||
u16 address = _iAddress & 0xFFF;
|
||||
|
||||
switch (address)
|
||||
// Directly map reads and writes to the pereg structure.
|
||||
for (size_t i = 0; i < sizeof (pereg) / sizeof (u16); ++i)
|
||||
{
|
||||
case PE_CTRL_REGISTER:
|
||||
{
|
||||
UPECtrlReg tmpCtrl(_iValue);
|
||||
u16* ptr = (u16*)&pereg + i;
|
||||
mmio->Register(base | (i * 2),
|
||||
MMIO::DirectRead<u16>(ptr),
|
||||
MMIO::DirectWrite<u16>(ptr)
|
||||
);
|
||||
}
|
||||
|
||||
// The control register has some more complex logic to perform on writes.
|
||||
mmio->RegisterWrite(base | PE_CTRL_REGISTER,
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
UPECtrlReg tmpCtrl(val);
|
||||
|
||||
if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = false;
|
||||
if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = false;
|
||||
|
@ -93,15 +86,9 @@ void Write16(const u16 _iValue, const u32 _iAddress)
|
|||
pereg.ctrl.PEToken = 0; // this flag is write only
|
||||
pereg.ctrl.PEFinish = 0; // this flag is write only
|
||||
|
||||
DEBUG_LOG(PIXELENGINE, "(w16): PE_CTRL_REGISTER: 0x%04x", _iValue);
|
||||
UpdateInterrupts();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (address <= 0x2e)
|
||||
((u16*)&pereg)[address >> 1] = _iValue;
|
||||
break;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
bool AllowIdleSkipping()
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "VideoCommon.h"
|
||||
|
||||
class PointerWrap;
|
||||
namespace MMIO { class Mapping; }
|
||||
|
||||
namespace SWPixelEngine
|
||||
{
|
||||
|
@ -200,12 +201,7 @@ namespace SWPixelEngine
|
|||
void Init();
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
// Read
|
||||
void Read16(u16& _uReturnValue, const u32 _iAddress);
|
||||
|
||||
// Write
|
||||
void Write16(const u16 _iValue, const u32 _iAddress);
|
||||
void Write32(const u32 _iValue, const u32 _iAddress);
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||
|
||||
// gfx backend support
|
||||
void SetToken(const u16 _token, const int _bSetTokenAcknowledge);
|
||||
|
|
|
@ -360,28 +360,15 @@ void VideoSoftware::Video_AbortFrame(void)
|
|||
{
|
||||
}
|
||||
|
||||
readFn16 VideoSoftware::Video_CPRead16()
|
||||
void VideoSoftware::RegisterCPMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
return SWCommandProcessor::Read16;
|
||||
}
|
||||
writeFn16 VideoSoftware::Video_CPWrite16()
|
||||
{
|
||||
return SWCommandProcessor::Write16;
|
||||
SWCommandProcessor::RegisterMMIO(mmio, base);
|
||||
}
|
||||
|
||||
readFn16 VideoSoftware::Video_PERead16()
|
||||
void VideoSoftware::RegisterPEMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
return SWPixelEngine::Read16;
|
||||
SWPixelEngine::RegisterMMIO(mmio, base);
|
||||
}
|
||||
writeFn16 VideoSoftware::Video_PEWrite16()
|
||||
{
|
||||
return SWPixelEngine::Write16;
|
||||
}
|
||||
writeFn32 VideoSoftware::Video_PEWrite32()
|
||||
{
|
||||
return SWPixelEngine::Write32;
|
||||
}
|
||||
|
||||
|
||||
// Draw messages on top of the screen
|
||||
unsigned int VideoSoftware::PeekMessages()
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "VideoBackendBase.h"
|
||||
|
||||
namespace MMIO { class Mapping; }
|
||||
|
||||
namespace SW
|
||||
{
|
||||
|
||||
|
@ -44,11 +46,8 @@ class VideoSoftware : public VideoBackend
|
|||
bool Video_IsPossibleWaitingSetDrawDone() override;
|
||||
void Video_AbortFrame() override;
|
||||
|
||||
readFn16 Video_CPRead16() override;
|
||||
writeFn16 Video_CPWrite16() override;
|
||||
readFn16 Video_PERead16() override;
|
||||
writeFn16 Video_PEWrite16() override;
|
||||
writeFn32 Video_PEWrite32() override;
|
||||
void RegisterCPMMIO(MMIO::Mapping* mmio, u32 base) override;
|
||||
void RegisterPEMMIO(MMIO::Mapping* mmio, u32 base) override;
|
||||
|
||||
void UpdateFPSDisplay(const char*) override;
|
||||
unsigned int PeekMessages() override;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "HW/Memmap.h"
|
||||
#include "HW/SystemTimers.h"
|
||||
#include "Core.h"
|
||||
#include "HW/MMIO.h"
|
||||
|
||||
namespace CommandProcessor
|
||||
{
|
||||
|
@ -33,10 +34,10 @@ UCPStatusReg m_CPStatusReg;
|
|||
UCPCtrlReg m_CPCtrlReg;
|
||||
UCPClearReg m_CPClearReg;
|
||||
|
||||
int m_bboxleft;
|
||||
int m_bboxtop;
|
||||
int m_bboxright;
|
||||
int m_bboxbottom;
|
||||
u16 m_bboxleft;
|
||||
u16 m_bboxtop;
|
||||
u16 m_bboxright;
|
||||
u16 m_bboxbottom;
|
||||
u16 m_tokenReg;
|
||||
|
||||
static bool bProcessFifoToLoWatermark = false;
|
||||
|
@ -131,307 +132,168 @@ void Init()
|
|||
et_UpdateInterrupts = CoreTiming::RegisterEvent("CPInterrupt", UpdateInterrupts_Wrapper);
|
||||
}
|
||||
|
||||
void Read16(u16& _rReturnValue, const u32 _Address)
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
INFO_LOG(COMMANDPROCESSOR, "(r): 0x%08x", _Address);
|
||||
switch (_Address & 0xFFF)
|
||||
struct {
|
||||
u32 addr;
|
||||
u16* ptr;
|
||||
bool readonly;
|
||||
bool writes_align_to_32_bytes;
|
||||
} directly_mapped_vars[] = {
|
||||
{ FIFO_TOKEN_REGISTER, &m_tokenReg },
|
||||
|
||||
// Bounding box registers are read only.
|
||||
{ FIFO_BOUNDING_BOX_LEFT, &m_bboxleft, true },
|
||||
{ FIFO_BOUNDING_BOX_RIGHT, &m_bboxright, true },
|
||||
{ FIFO_BOUNDING_BOX_TOP, &m_bboxtop, true },
|
||||
{ FIFO_BOUNDING_BOX_BOTTOM, &m_bboxbottom, true },
|
||||
|
||||
// Some FIFO addresses need to be aligned on 32 bytes on write - only
|
||||
// the high part can be written directly without a mask.
|
||||
{ FIFO_BASE_LO, MMIO::Utils::LowPart(&fifo.CPBase), false, true },
|
||||
{ FIFO_BASE_HI, MMIO::Utils::HighPart(&fifo.CPBase) },
|
||||
{ FIFO_END_LO, MMIO::Utils::LowPart(&fifo.CPEnd), false, true },
|
||||
{ FIFO_END_HI, MMIO::Utils::HighPart(&fifo.CPEnd) },
|
||||
{ FIFO_HI_WATERMARK_LO, MMIO::Utils::LowPart(&fifo.CPHiWatermark) },
|
||||
{ FIFO_HI_WATERMARK_HI, MMIO::Utils::HighPart(&fifo.CPHiWatermark) },
|
||||
{ FIFO_LO_WATERMARK_LO, MMIO::Utils::LowPart(&fifo.CPLoWatermark) },
|
||||
{ FIFO_LO_WATERMARK_HI, MMIO::Utils::HighPart(&fifo.CPLoWatermark) },
|
||||
// FIFO_RW_DISTANCE has some complex read code different for
|
||||
// single/dual core.
|
||||
{ FIFO_WRITE_POINTER_LO, MMIO::Utils::LowPart(&fifo.CPWritePointer), false, true },
|
||||
{ FIFO_WRITE_POINTER_HI, MMIO::Utils::HighPart(&fifo.CPWritePointer) },
|
||||
// FIFO_READ_POINTER has different code for single/dual core.
|
||||
{ FIFO_BP_LO, MMIO::Utils::LowPart(&fifo.CPBreakpoint), false, true },
|
||||
{ FIFO_BP_HI, MMIO::Utils::HighPart(&fifo.CPBreakpoint) },
|
||||
};
|
||||
for (auto& mapped_var : directly_mapped_vars)
|
||||
{
|
||||
case STATUS_REGISTER:
|
||||
SetCpStatusRegister();
|
||||
_rReturnValue = m_CPStatusReg.Hex;
|
||||
return;
|
||||
case CTRL_REGISTER: _rReturnValue = m_CPCtrlReg.Hex; return;
|
||||
case CLEAR_REGISTER:
|
||||
_rReturnValue = m_CPClearReg.Hex;
|
||||
PanicAlert("CommandProcessor:: CPU reads from CLEAR_REGISTER!");
|
||||
ERROR_LOG(COMMANDPROCESSOR, "(r) clear: 0x%04x", _rReturnValue);
|
||||
return;
|
||||
case FIFO_TOKEN_REGISTER: _rReturnValue = m_tokenReg; return;
|
||||
case FIFO_BOUNDING_BOX_LEFT: _rReturnValue = m_bboxleft; return;
|
||||
case FIFO_BOUNDING_BOX_RIGHT: _rReturnValue = m_bboxright; return;
|
||||
case FIFO_BOUNDING_BOX_TOP: _rReturnValue = m_bboxtop; return;
|
||||
case FIFO_BOUNDING_BOX_BOTTOM: _rReturnValue = m_bboxbottom; return;
|
||||
|
||||
case FIFO_BASE_LO: _rReturnValue = ReadLow (fifo.CPBase); return;
|
||||
case FIFO_BASE_HI: _rReturnValue = ReadHigh(fifo.CPBase); return;
|
||||
case FIFO_END_LO: _rReturnValue = ReadLow (fifo.CPEnd); return;
|
||||
case FIFO_END_HI: _rReturnValue = ReadHigh(fifo.CPEnd); return;
|
||||
case FIFO_HI_WATERMARK_LO: _rReturnValue = ReadLow (fifo.CPHiWatermark); return;
|
||||
case FIFO_HI_WATERMARK_HI: _rReturnValue = ReadHigh(fifo.CPHiWatermark); return;
|
||||
case FIFO_LO_WATERMARK_LO: _rReturnValue = ReadLow (fifo.CPLoWatermark); return;
|
||||
case FIFO_LO_WATERMARK_HI: _rReturnValue = ReadHigh(fifo.CPLoWatermark); return;
|
||||
|
||||
case FIFO_RW_DISTANCE_LO:
|
||||
if (IsOnThread())
|
||||
{
|
||||
if(fifo.CPWritePointer >= fifo.SafeCPReadPointer)
|
||||
_rReturnValue = ReadLow (fifo.CPWritePointer - fifo.SafeCPReadPointer);
|
||||
else
|
||||
_rReturnValue = ReadLow (fifo.CPEnd - fifo.SafeCPReadPointer + fifo.CPWritePointer - fifo.CPBase + 32);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rReturnValue = ReadLow (fifo.CPReadWriteDistance);
|
||||
}
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read FIFO_RW_DISTANCE_LO : %04x", _rReturnValue);
|
||||
return;
|
||||
case FIFO_RW_DISTANCE_HI:
|
||||
if (IsOnThread())
|
||||
{
|
||||
if(fifo.CPWritePointer >= fifo.SafeCPReadPointer)
|
||||
_rReturnValue = ReadHigh (fifo.CPWritePointer - fifo.SafeCPReadPointer);
|
||||
else
|
||||
_rReturnValue = ReadHigh (fifo.CPEnd - fifo.SafeCPReadPointer + fifo.CPWritePointer - fifo.CPBase + 32);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rReturnValue = ReadHigh(fifo.CPReadWriteDistance);
|
||||
}
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read FIFO_RW_DISTANCE_HI : %04x", _rReturnValue);
|
||||
return;
|
||||
case FIFO_WRITE_POINTER_LO:
|
||||
_rReturnValue = ReadLow (fifo.CPWritePointer);
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read FIFO_WRITE_POINTER_LO : %04x", _rReturnValue);
|
||||
return;
|
||||
case FIFO_WRITE_POINTER_HI:
|
||||
_rReturnValue = ReadHigh(fifo.CPWritePointer);
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read FIFO_WRITE_POINTER_HI : %04x", _rReturnValue);
|
||||
return;
|
||||
case FIFO_READ_POINTER_LO:
|
||||
if (IsOnThread())
|
||||
_rReturnValue = ReadLow (fifo.SafeCPReadPointer);
|
||||
else
|
||||
_rReturnValue = ReadLow (fifo.CPReadPointer);
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read FIFO_READ_POINTER_LO : %04x", _rReturnValue);
|
||||
return;
|
||||
case FIFO_READ_POINTER_HI:
|
||||
if (IsOnThread())
|
||||
_rReturnValue = ReadHigh (fifo.SafeCPReadPointer);
|
||||
else
|
||||
_rReturnValue = ReadHigh (fifo.CPReadPointer);
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read FIFO_READ_POINTER_HI : %04x", _rReturnValue);
|
||||
return;
|
||||
|
||||
case FIFO_BP_LO: _rReturnValue = ReadLow (fifo.CPBreakpoint); return;
|
||||
case FIFO_BP_HI: _rReturnValue = ReadHigh(fifo.CPBreakpoint); return;
|
||||
|
||||
case XF_RASBUSY_L:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from XF_RASBUSY_L: %04x", _rReturnValue);
|
||||
return;
|
||||
case XF_RASBUSY_H:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from XF_RASBUSY_H: %04x", _rReturnValue);
|
||||
return;
|
||||
|
||||
case XF_CLKS_L:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from XF_CLKS_L: %04x", _rReturnValue);
|
||||
return;
|
||||
case XF_CLKS_H:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from XF_CLKS_H: %04x", _rReturnValue);
|
||||
return;
|
||||
|
||||
case XF_WAIT_IN_L:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from XF_WAIT_IN_L: %04x", _rReturnValue);
|
||||
return;
|
||||
case XF_WAIT_IN_H:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from XF_WAIT_IN_H: %04x", _rReturnValue);
|
||||
return;
|
||||
|
||||
case XF_WAIT_OUT_L:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from XF_WAIT_OUT_L: %04x", _rReturnValue);
|
||||
return;
|
||||
case XF_WAIT_OUT_H:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from XF_WAIT_OUT_H: %04x", _rReturnValue);
|
||||
return;
|
||||
|
||||
case VCACHE_METRIC_CHECK_L:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from VCACHE_METRIC_CHECK_L: %04x", _rReturnValue);
|
||||
return;
|
||||
case VCACHE_METRIC_CHECK_H:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from VCACHE_METRIC_CHECK_H: %04x", _rReturnValue);
|
||||
return;
|
||||
|
||||
case VCACHE_METRIC_MISS_L:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from VCACHE_METRIC_MISS_L: %04x", _rReturnValue);
|
||||
return;
|
||||
case VCACHE_METRIC_MISS_H:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from VCACHE_METRIC_MISS_H: %04x", _rReturnValue);
|
||||
return;
|
||||
|
||||
case VCACHE_METRIC_STALL_L:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from VCACHE_METRIC_STALL_L: %04x", _rReturnValue);
|
||||
return;
|
||||
case VCACHE_METRIC_STALL_H:
|
||||
_rReturnValue = 0; // TODO: Figure out the true value
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from VCACHE_METRIC_STALL_H: %04x", _rReturnValue);
|
||||
return;
|
||||
|
||||
case CLKS_PER_VTX_OUT:
|
||||
_rReturnValue = 4; //Number of clocks per vertex.. TODO: Calculate properly
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Read from CLKS_PER_VTX_OUT: %04x", _rReturnValue);
|
||||
return;
|
||||
default:
|
||||
_rReturnValue = 0;
|
||||
WARN_LOG(COMMANDPROCESSOR, "(r16) unknown CP reg @ %08x", _Address);
|
||||
return;
|
||||
u16 wmask = mapped_var.writes_align_to_32_bytes ? 0xFFE0 : 0xFFFF;
|
||||
mmio->Register(base | mapped_var.addr,
|
||||
MMIO::DirectRead<u16>(mapped_var.ptr),
|
||||
mapped_var.readonly
|
||||
? MMIO::InvalidWrite<u16>()
|
||||
: MMIO::DirectWrite<u16>(mapped_var.ptr, wmask)
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Write16(const u16 _Value, const u32 _Address)
|
||||
{
|
||||
INFO_LOG(COMMANDPROCESSOR, "(write16): 0x%04x @ 0x%08x",_Value,_Address);
|
||||
|
||||
switch (_Address & 0xFFF)
|
||||
// Timing and metrics MMIOs are stubbed with fixed values.
|
||||
struct {
|
||||
u32 addr;
|
||||
u16 value;
|
||||
} metrics_mmios[] = {
|
||||
{ XF_RASBUSY_L, 0 },
|
||||
{ XF_RASBUSY_H, 0 },
|
||||
{ XF_CLKS_L, 0 },
|
||||
{ XF_CLKS_H, 0 },
|
||||
{ XF_WAIT_IN_L, 0 },
|
||||
{ XF_WAIT_IN_H, 0 },
|
||||
{ XF_WAIT_OUT_L, 0 },
|
||||
{ XF_WAIT_OUT_H, 0 },
|
||||
{ VCACHE_METRIC_CHECK_L, 0 },
|
||||
{ VCACHE_METRIC_CHECK_H, 0 },
|
||||
{ VCACHE_METRIC_MISS_L, 0 },
|
||||
{ VCACHE_METRIC_MISS_H, 0 },
|
||||
{ VCACHE_METRIC_STALL_L, 0 },
|
||||
{ VCACHE_METRIC_STALL_H, 0 },
|
||||
{ CLKS_PER_VTX_OUT, 4 },
|
||||
};
|
||||
for (auto& metrics_mmio : metrics_mmios)
|
||||
{
|
||||
case STATUS_REGISTER:
|
||||
{
|
||||
// This should be Read-Only
|
||||
ERROR_LOG(COMMANDPROCESSOR,"\t write to STATUS_REGISTER : %04x", _Value);
|
||||
PanicAlert("CommandProcessor:: CPU writes to STATUS_REGISTER!");
|
||||
}
|
||||
break;
|
||||
mmio->Register(base | metrics_mmio.addr,
|
||||
MMIO::Constant<u16>(metrics_mmio.value),
|
||||
MMIO::InvalidWrite<u16>()
|
||||
);
|
||||
}
|
||||
|
||||
case CTRL_REGISTER:
|
||||
{
|
||||
UCPCtrlReg tmpCtrl(_Value);
|
||||
m_CPCtrlReg.Hex = tmpCtrl.Hex;
|
||||
INFO_LOG(COMMANDPROCESSOR,"\t Write to CTRL_REGISTER : %04x", _Value);
|
||||
mmio->Register(base | STATUS_REGISTER,
|
||||
MMIO::ComplexRead<u16>([](u32) {
|
||||
SetCpStatusRegister();
|
||||
return m_CPStatusReg.Hex;
|
||||
}),
|
||||
MMIO::InvalidWrite<u16>()
|
||||
);
|
||||
|
||||
mmio->Register(base | CTRL_REGISTER,
|
||||
MMIO::DirectRead<u16>(&m_CPCtrlReg.Hex),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
UCPCtrlReg tmp(val);
|
||||
m_CPCtrlReg.Hex = tmp.Hex;
|
||||
SetCpControlRegister();
|
||||
}
|
||||
break;
|
||||
if (!IsOnThread())
|
||||
RunGpu();
|
||||
})
|
||||
);
|
||||
|
||||
case CLEAR_REGISTER:
|
||||
{
|
||||
UCPClearReg tmpCtrl(_Value);
|
||||
m_CPClearReg.Hex = tmpCtrl.Hex;
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to CLEAR_REGISTER : %04x", _Value);
|
||||
mmio->Register(base | CLEAR_REGISTER,
|
||||
MMIO::DirectRead<u16>(&m_CPClearReg.Hex),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
UCPClearReg tmp(val);
|
||||
m_CPClearReg.Hex = tmp.Hex;
|
||||
SetCpClearRegister();
|
||||
}
|
||||
break;
|
||||
if (!IsOnThread())
|
||||
RunGpu();
|
||||
})
|
||||
);
|
||||
|
||||
case PERF_SELECT:
|
||||
// Seems to select which set of perf registers should be exposed.
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "Write to PERF_SELECT: %04x", _Value);
|
||||
break;
|
||||
mmio->Register(base | PERF_SELECT,
|
||||
MMIO::InvalidRead<u16>(),
|
||||
MMIO::Nop<u16>()
|
||||
);
|
||||
|
||||
// Fifo Registers
|
||||
case FIFO_TOKEN_REGISTER:
|
||||
m_tokenReg = _Value;
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to FIFO_TOKEN_REGISTER : %04x", _Value);
|
||||
break;
|
||||
case FIFO_BASE_LO:
|
||||
WriteLow ((u32 &)fifo.CPBase, _Value & 0xFFE0);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to FIFO_BASE_LO : %04x", _Value);
|
||||
break;
|
||||
case FIFO_BASE_HI:
|
||||
WriteHigh((u32 &)fifo.CPBase, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to FIFO_BASE_HI : %04x", _Value);
|
||||
break;
|
||||
|
||||
case FIFO_END_LO:
|
||||
WriteLow ((u32 &)fifo.CPEnd, _Value & 0xFFE0);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to FIFO_END_LO : %04x", _Value);
|
||||
break;
|
||||
case FIFO_END_HI:
|
||||
WriteHigh((u32 &)fifo.CPEnd, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to FIFO_END_HI : %04x", _Value);
|
||||
break;
|
||||
|
||||
case FIFO_WRITE_POINTER_LO:
|
||||
WriteLow ((u32 &)fifo.CPWritePointer, _Value & 0xFFE0);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to FIFO_WRITE_POINTER_LO : %04x", _Value);
|
||||
break;
|
||||
case FIFO_WRITE_POINTER_HI:
|
||||
WriteHigh((u32 &)fifo.CPWritePointer, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to FIFO_WRITE_POINTER_HI : %04x", _Value);
|
||||
break;
|
||||
|
||||
case FIFO_READ_POINTER_LO:
|
||||
WriteLow ((u32 &)fifo.CPReadPointer, _Value & 0xFFE0);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to FIFO_READ_POINTER_LO : %04x", _Value);
|
||||
break;
|
||||
case FIFO_READ_POINTER_HI:
|
||||
WriteHigh((u32 &)fifo.CPReadPointer, _Value);
|
||||
fifo.SafeCPReadPointer = fifo.CPReadPointer;
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to FIFO_READ_POINTER_HI : %04x", _Value);
|
||||
break;
|
||||
|
||||
case FIFO_HI_WATERMARK_LO:
|
||||
WriteLow ((u32 &)fifo.CPHiWatermark, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to FIFO_HI_WATERMARK_LO : %04x", _Value);
|
||||
break;
|
||||
case FIFO_HI_WATERMARK_HI:
|
||||
WriteHigh((u32 &)fifo.CPHiWatermark, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to FIFO_HI_WATERMARK_HI : %04x", _Value);
|
||||
break;
|
||||
|
||||
case FIFO_LO_WATERMARK_LO:
|
||||
WriteLow ((u32 &)fifo.CPLoWatermark, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to FIFO_LO_WATERMARK_LO : %04x", _Value);
|
||||
break;
|
||||
case FIFO_LO_WATERMARK_HI:
|
||||
WriteHigh((u32 &)fifo.CPLoWatermark, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t Write to FIFO_LO_WATERMARK_HI : %04x", _Value);
|
||||
break;
|
||||
|
||||
case FIFO_BP_LO:
|
||||
WriteLow ((u32 &)fifo.CPBreakpoint, _Value & 0xFFE0);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"Write to FIFO_BP_LO : %04x", _Value);
|
||||
break;
|
||||
case FIFO_BP_HI:
|
||||
WriteHigh((u32 &)fifo.CPBreakpoint, _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"Write to FIFO_BP_HI : %04x", _Value);
|
||||
break;
|
||||
|
||||
case FIFO_RW_DISTANCE_HI:
|
||||
WriteHigh((u32 &)fifo.CPReadWriteDistance, _Value);
|
||||
if (fifo.CPReadWriteDistance == 0)
|
||||
{
|
||||
GPFifo::ResetGatherPipe();
|
||||
ResetVideoBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetVideoBuffer();
|
||||
}
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"Try to write to FIFO_RW_DISTANCE_HI : %04x", _Value);
|
||||
break;
|
||||
case FIFO_RW_DISTANCE_LO:
|
||||
WriteLow((u32 &)fifo.CPReadWriteDistance, _Value & 0xFFE0);
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"Try to write to FIFO_RW_DISTANCE_LO : %04x", _Value);
|
||||
break;
|
||||
|
||||
default:
|
||||
WARN_LOG(COMMANDPROCESSOR, "(w16) unknown CP reg write %04x @ %08x", _Value, _Address);
|
||||
}
|
||||
|
||||
if (!IsOnThread())
|
||||
RunGpu();
|
||||
}
|
||||
|
||||
void Read32(u32& _rReturnValue, const u32 _Address)
|
||||
{
|
||||
_rReturnValue = 0;
|
||||
_dbg_assert_msg_(COMMANDPROCESSOR, 0, "Read32 from CommandProccessor at 0x%08x", _Address);
|
||||
}
|
||||
|
||||
void Write32(const u32 _Data, const u32 _Address)
|
||||
{
|
||||
_dbg_assert_msg_(COMMANDPROCESSOR, 0, "Write32 at CommandProccessor at 0x%08x", _Address);
|
||||
// Some MMIOs have different handlers for single core vs. dual core mode.
|
||||
mmio->Register(base | FIFO_RW_DISTANCE_LO,
|
||||
IsOnThread()
|
||||
? MMIO::ComplexRead<u16>([](u32) {
|
||||
if (fifo.CPWritePointer >= fifo.SafeCPReadPointer)
|
||||
return ReadLow(fifo.CPWritePointer - fifo.SafeCPReadPointer);
|
||||
else
|
||||
return ReadLow(fifo.CPEnd - fifo.SafeCPReadPointer + fifo.CPWritePointer - fifo.CPBase + 32);
|
||||
})
|
||||
: MMIO::DirectRead<u16>(MMIO::Utils::LowPart(&fifo.CPReadWriteDistance)),
|
||||
MMIO::DirectWrite<u16>(MMIO::Utils::LowPart(&fifo.CPReadWriteDistance), 0xFFE0)
|
||||
);
|
||||
mmio->Register(base | FIFO_RW_DISTANCE_HI,
|
||||
IsOnThread()
|
||||
? MMIO::ComplexRead<u16>([](u32) {
|
||||
if (fifo.CPWritePointer >= fifo.SafeCPReadPointer)
|
||||
return ReadHigh(fifo.CPWritePointer - fifo.SafeCPReadPointer);
|
||||
else
|
||||
return ReadHigh(fifo.CPEnd - fifo.SafeCPReadPointer + fifo.CPWritePointer - fifo.CPBase + 32);
|
||||
})
|
||||
: MMIO::DirectRead<u16>(MMIO::Utils::HighPart(&fifo.CPReadWriteDistance)),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
WriteHigh(fifo.CPReadWriteDistance, val);
|
||||
if (fifo.CPReadWriteDistance == 0)
|
||||
{
|
||||
GPFifo::ResetGatherPipe();
|
||||
ResetVideoBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetVideoBuffer();
|
||||
}
|
||||
if (!IsOnThread())
|
||||
RunGpu();
|
||||
})
|
||||
);
|
||||
mmio->Register(base | FIFO_READ_POINTER_LO,
|
||||
IsOnThread()
|
||||
? MMIO::DirectRead<u16>(MMIO::Utils::LowPart(&fifo.SafeCPReadPointer))
|
||||
: MMIO::DirectRead<u16>(MMIO::Utils::LowPart(&fifo.CPReadPointer)),
|
||||
MMIO::DirectWrite<u16>(MMIO::Utils::LowPart(&fifo.CPReadPointer), 0xFFE0)
|
||||
);
|
||||
mmio->Register(base | FIFO_READ_POINTER_HI,
|
||||
IsOnThread()
|
||||
? MMIO::DirectRead<u16>(MMIO::Utils::HighPart(&fifo.SafeCPReadPointer))
|
||||
: MMIO::DirectRead<u16>(MMIO::Utils::HighPart(&fifo.CPReadPointer)),
|
||||
IsOnThread()
|
||||
? MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
WriteHigh(fifo.CPReadPointer, val);
|
||||
fifo.SafeCPReadPointer = fifo.CPReadPointer;
|
||||
})
|
||||
: MMIO::DirectWrite<u16>(MMIO::Utils::HighPart(&fifo.CPReadPointer))
|
||||
);
|
||||
}
|
||||
|
||||
void STACKALIGN GatherPipeBursted()
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "VideoBackendBase.h"
|
||||
|
||||
class PointerWrap;
|
||||
namespace MMIO { class Mapping; }
|
||||
|
||||
extern bool MT;
|
||||
|
||||
|
@ -134,11 +135,7 @@ void Init();
|
|||
void Shutdown();
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
// Read
|
||||
void Read16(u16& _rReturnValue, const u32 _Address);
|
||||
void Write16(const u16 _Data, const u32 _Address);
|
||||
void Read32(u32& _rReturnValue, const u32 _Address);
|
||||
void Write32(const u32 _Data, const u32 _Address);
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||
|
||||
void SetCpStatus(bool isCPUThread = false);
|
||||
void GatherPipeBursted();
|
||||
|
|
|
@ -311,24 +311,12 @@ void VideoBackendHardware::Video_AbortFrame()
|
|||
CommandProcessor::AbortFrame();
|
||||
}
|
||||
|
||||
readFn16 VideoBackendHardware::Video_CPRead16()
|
||||
void VideoBackendHardware::RegisterCPMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
return CommandProcessor::Read16;
|
||||
}
|
||||
writeFn16 VideoBackendHardware::Video_CPWrite16()
|
||||
{
|
||||
return CommandProcessor::Write16;
|
||||
CommandProcessor::RegisterMMIO(mmio, base);
|
||||
}
|
||||
|
||||
readFn16 VideoBackendHardware::Video_PERead16()
|
||||
void VideoBackendHardware::RegisterPEMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
return PixelEngine::Read16;
|
||||
}
|
||||
writeFn16 VideoBackendHardware::Video_PEWrite16()
|
||||
{
|
||||
return PixelEngine::Write16;
|
||||
}
|
||||
writeFn32 VideoBackendHardware::Video_PEWrite32()
|
||||
{
|
||||
return PixelEngine::Write32;
|
||||
PixelEngine::RegisterMMIO(mmio, base);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "PixelEngine.h"
|
||||
#include "RenderBase.h"
|
||||
#include "CommandProcessor.h"
|
||||
#include "HW/MMIO.h"
|
||||
#include "HW/ProcessorInterface.h"
|
||||
#include "State.h"
|
||||
|
||||
|
@ -164,145 +165,61 @@ void Init()
|
|||
bbox_active = false;
|
||||
}
|
||||
|
||||
void Read16(u16& _uReturnValue, const u32 _iAddress)
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
{
|
||||
DEBUG_LOG(PIXELENGINE, "(r16) 0x%08x", _iAddress);
|
||||
switch (_iAddress & 0xFFF)
|
||||
// Directly mapped registers.
|
||||
struct {
|
||||
u32 addr;
|
||||
u16* ptr;
|
||||
} directly_mapped_vars[] = {
|
||||
{ PE_ZCONF, &m_ZConf.Hex },
|
||||
{ PE_ALPHACONF, &m_AlphaConf.Hex },
|
||||
{ PE_DSTALPHACONF, &m_DstAlphaConf.Hex },
|
||||
{ PE_ALPHAMODE, &m_AlphaModeConf.Hex },
|
||||
{ PE_ALPHAREAD, &m_AlphaRead.Hex },
|
||||
};
|
||||
for (auto& mapped_var : directly_mapped_vars)
|
||||
{
|
||||
// CPU Direct Access EFB Raster State Config
|
||||
case PE_ZCONF:
|
||||
_uReturnValue = m_ZConf.Hex;
|
||||
INFO_LOG(PIXELENGINE, "(r16) ZCONF");
|
||||
break;
|
||||
case PE_ALPHACONF:
|
||||
// Most games read this early. no idea why.
|
||||
_uReturnValue = m_AlphaConf.Hex;
|
||||
INFO_LOG(PIXELENGINE, "(r16) ALPHACONF");
|
||||
break;
|
||||
case PE_DSTALPHACONF:
|
||||
_uReturnValue = m_DstAlphaConf.Hex;
|
||||
INFO_LOG(PIXELENGINE, "(r16) DSTALPHACONF");
|
||||
break;
|
||||
case PE_ALPHAMODE:
|
||||
_uReturnValue = m_AlphaModeConf.Hex;
|
||||
INFO_LOG(PIXELENGINE, "(r16) ALPHAMODE");
|
||||
break;
|
||||
case PE_ALPHAREAD:
|
||||
_uReturnValue = m_AlphaRead.Hex;
|
||||
WARN_LOG(PIXELENGINE, "(r16) ALPHAREAD");
|
||||
break;
|
||||
|
||||
case PE_CTRL_REGISTER:
|
||||
_uReturnValue = m_Control.Hex;
|
||||
INFO_LOG(PIXELENGINE, "(r16) CTRL_REGISTER : %04x", _uReturnValue);
|
||||
break;
|
||||
|
||||
case PE_TOKEN_REG:
|
||||
_uReturnValue = Common::AtomicLoad(*(volatile u32*)&CommandProcessor::fifo.PEToken);
|
||||
INFO_LOG(PIXELENGINE, "(r16) TOKEN_REG : %04x", _uReturnValue);
|
||||
break;
|
||||
|
||||
// BBox
|
||||
case PE_BBOX_LEFT:
|
||||
case PE_BBOX_RIGHT:
|
||||
case PE_BBOX_TOP:
|
||||
case PE_BBOX_BOTTOM:
|
||||
_uReturnValue = bbox[(_iAddress >> 1) & 3];
|
||||
bbox_active = false;
|
||||
break;
|
||||
|
||||
// NOTE(neobrain): only PE_PERF_ZCOMP_OUTPUT is implemented in D3D11, but the other values shouldn't be contradictionary to the value of that register (i.e. INPUT registers should always be greater or equal to their corresponding OUTPUT registers).
|
||||
case PE_PERF_ZCOMP_INPUT_ZCOMPLOC_L:
|
||||
_uReturnValue = g_video_backend->Video_GetQueryResult(PQ_ZCOMP_INPUT_ZCOMPLOC) & 0xFFFF;
|
||||
break;
|
||||
|
||||
case PE_PERF_ZCOMP_INPUT_ZCOMPLOC_H:
|
||||
_uReturnValue = g_video_backend->Video_GetQueryResult(PQ_ZCOMP_INPUT_ZCOMPLOC) >> 16;
|
||||
break;
|
||||
|
||||
case PE_PERF_ZCOMP_OUTPUT_ZCOMPLOC_L:
|
||||
_uReturnValue = g_video_backend->Video_GetQueryResult(PQ_ZCOMP_OUTPUT_ZCOMPLOC) & 0xFFFF;
|
||||
break;
|
||||
|
||||
case PE_PERF_ZCOMP_OUTPUT_ZCOMPLOC_H:
|
||||
_uReturnValue = g_video_backend->Video_GetQueryResult(PQ_ZCOMP_OUTPUT_ZCOMPLOC) >> 16;
|
||||
break;
|
||||
|
||||
case PE_PERF_ZCOMP_INPUT_L:
|
||||
_uReturnValue = g_video_backend->Video_GetQueryResult(PQ_ZCOMP_INPUT) & 0xFFFF;
|
||||
break;
|
||||
|
||||
case PE_PERF_ZCOMP_INPUT_H:
|
||||
_uReturnValue = g_video_backend->Video_GetQueryResult(PQ_ZCOMP_INPUT) >> 16;
|
||||
break;
|
||||
|
||||
case PE_PERF_ZCOMP_OUTPUT_L:
|
||||
_uReturnValue = g_video_backend->Video_GetQueryResult(PQ_ZCOMP_OUTPUT) & 0xFFFF;
|
||||
break;
|
||||
|
||||
case PE_PERF_ZCOMP_OUTPUT_H:
|
||||
_uReturnValue = g_video_backend->Video_GetQueryResult(PQ_ZCOMP_OUTPUT) >> 16;
|
||||
break;
|
||||
|
||||
case PE_PERF_BLEND_INPUT_L:
|
||||
// Super Mario Sunshine uses this register in episode 6 of Sirena Beach:
|
||||
// The amount of remaining goop is determined by checking how many pixels reach the blending stage.
|
||||
// Once this register falls below a particular value (around 0x90), the game regards the challenge finished.
|
||||
// In very old builds, Dolphin only returned 0. That caused the challenge to be immediately finished without any goop being cleaned (the timer just didn't even start counting from 3:00:00).
|
||||
// Later builds returned 1 for the high register. That caused the timer to actually count down, but made the challenge unbeatable because the game always thought you didn't clear any goop at all.
|
||||
// Note that currently this functionality is only implemented in the D3D11 backend.
|
||||
_uReturnValue = g_video_backend->Video_GetQueryResult(PQ_BLEND_INPUT) & 0xFFFF;
|
||||
break;
|
||||
|
||||
case PE_PERF_BLEND_INPUT_H:
|
||||
_uReturnValue = g_video_backend->Video_GetQueryResult(PQ_BLEND_INPUT) >> 16;
|
||||
break;
|
||||
|
||||
case PE_PERF_EFB_COPY_CLOCKS_L:
|
||||
_uReturnValue = g_video_backend->Video_GetQueryResult(PQ_EFB_COPY_CLOCKS) & 0xFFFF;
|
||||
break;
|
||||
|
||||
case PE_PERF_EFB_COPY_CLOCKS_H:
|
||||
_uReturnValue = g_video_backend->Video_GetQueryResult(PQ_EFB_COPY_CLOCKS) >> 16;
|
||||
break;
|
||||
|
||||
default:
|
||||
INFO_LOG(PIXELENGINE, "(r16) unknown @ %08x", _iAddress);
|
||||
_uReturnValue = 1;
|
||||
break;
|
||||
mmio->Register(base | mapped_var.addr,
|
||||
MMIO::DirectRead<u16>(mapped_var.ptr),
|
||||
MMIO::DirectWrite<u16>(mapped_var.ptr)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Write16(const u16 _iValue, const u32 _iAddress)
|
||||
{
|
||||
switch (_iAddress & 0xFFF)
|
||||
// Performance queries registers: read only, need to call the video backend
|
||||
// to get the results.
|
||||
struct {
|
||||
u32 addr;
|
||||
PerfQueryType pqtype;
|
||||
} pq_regs[] = {
|
||||
{ PE_PERF_ZCOMP_INPUT_ZCOMPLOC_L, PQ_ZCOMP_INPUT_ZCOMPLOC },
|
||||
{ PE_PERF_ZCOMP_OUTPUT_ZCOMPLOC_L, PQ_ZCOMP_OUTPUT_ZCOMPLOC },
|
||||
{ PE_PERF_ZCOMP_INPUT_L, PQ_ZCOMP_INPUT },
|
||||
{ PE_PERF_ZCOMP_OUTPUT_L, PQ_ZCOMP_OUTPUT },
|
||||
{ PE_PERF_BLEND_INPUT_L, PQ_BLEND_INPUT },
|
||||
{ PE_PERF_EFB_COPY_CLOCKS_L, PQ_EFB_COPY_CLOCKS },
|
||||
};
|
||||
for (auto& pq_reg : pq_regs)
|
||||
{
|
||||
// CPU Direct Access EFB Raster State Config
|
||||
case PE_ZCONF:
|
||||
m_ZConf.Hex = _iValue;
|
||||
INFO_LOG(PIXELENGINE, "(w16) ZCONF: %02x", _iValue);
|
||||
break;
|
||||
case PE_ALPHACONF:
|
||||
m_AlphaConf.Hex = _iValue;
|
||||
INFO_LOG(PIXELENGINE, "(w16) ALPHACONF: %02x", _iValue);
|
||||
break;
|
||||
case PE_DSTALPHACONF:
|
||||
m_DstAlphaConf.Hex = _iValue;
|
||||
INFO_LOG(PIXELENGINE, "(w16) DSTALPHACONF: %02x", _iValue);
|
||||
break;
|
||||
case PE_ALPHAMODE:
|
||||
m_AlphaModeConf.Hex = _iValue;
|
||||
INFO_LOG(PIXELENGINE, "(w16) ALPHAMODE: %02x", _iValue);
|
||||
break;
|
||||
case PE_ALPHAREAD:
|
||||
m_AlphaRead.Hex = _iValue;
|
||||
INFO_LOG(PIXELENGINE, "(w16) ALPHAREAD: %02x", _iValue);
|
||||
break;
|
||||
mmio->Register(base | pq_reg.addr,
|
||||
MMIO::ComplexRead<u16>([pq_reg](u32) {
|
||||
return g_video_backend->Video_GetQueryResult(pq_reg.pqtype) & 0xFFFF;
|
||||
}),
|
||||
MMIO::InvalidWrite<u16>()
|
||||
);
|
||||
mmio->Register(base | (pq_reg.addr + 2),
|
||||
MMIO::ComplexRead<u16>([pq_reg](u32) {
|
||||
return g_video_backend->Video_GetQueryResult(pq_reg.pqtype) >> 16;
|
||||
}),
|
||||
MMIO::InvalidWrite<u16>()
|
||||
);
|
||||
}
|
||||
|
||||
case PE_CTRL_REGISTER:
|
||||
{
|
||||
UPECtrlReg tmpCtrl(_iValue);
|
||||
// Control register
|
||||
mmio->Register(base | PE_CTRL_REGISTER,
|
||||
MMIO::DirectRead<u16>(&m_Control.Hex),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
UPECtrlReg tmpCtrl(val);
|
||||
|
||||
if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = 0;
|
||||
if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = 0;
|
||||
|
@ -312,27 +229,28 @@ void Write16(const u16 _iValue, const u32 _iAddress)
|
|||
m_Control.PEToken = 0; // this flag is write only
|
||||
m_Control.PEFinish = 0; // this flag is write only
|
||||
|
||||
DEBUG_LOG(PIXELENGINE, "(w16) CTRL_REGISTER: 0x%04x", _iValue);
|
||||
DEBUG_LOG(PIXELENGINE, "(w16) CTRL_REGISTER: 0x%04x", val);
|
||||
UpdateInterrupts();
|
||||
}
|
||||
break;
|
||||
})
|
||||
);
|
||||
|
||||
case PE_TOKEN_REG:
|
||||
PanicAlert("(w16) WTF? PowerPC program wrote token: %i", _iValue);
|
||||
//only the gx pipeline is supposed to be able to write here
|
||||
//g_token = _iValue;
|
||||
break;
|
||||
// Token register, readonly.
|
||||
mmio->Register(base | PE_TOKEN_REG,
|
||||
MMIO::DirectRead<u16>(&CommandProcessor::fifo.PEToken),
|
||||
MMIO::InvalidWrite<u16>()
|
||||
);
|
||||
|
||||
default:
|
||||
WARN_LOG(PIXELENGINE, "(w16) unknown %04x @ %08x", _iValue, _iAddress);
|
||||
break;
|
||||
// BBOX registers, readonly and need to update a flag.
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
mmio->Register(base | (PE_BBOX_LEFT + 2 * i),
|
||||
MMIO::ComplexRead<u16>([i](u32) {
|
||||
bbox_active = false;
|
||||
return bbox[i];
|
||||
}),
|
||||
MMIO::InvalidWrite<u16>()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Write32(const u32 _iValue, const u32 _iAddress)
|
||||
{
|
||||
WARN_LOG(PIXELENGINE, "(w32) 0x%08x @ 0x%08x IGNORING...",_iValue,_iAddress);
|
||||
}
|
||||
|
||||
bool AllowIdleSkipping()
|
||||
|
@ -441,4 +359,10 @@ void ResetSetToken()
|
|||
}
|
||||
CommandProcessor::interruptTokenWaiting = false;
|
||||
}
|
||||
|
||||
UPEAlphaReadReg GetAlphaReadMode()
|
||||
{
|
||||
return m_AlphaRead;
|
||||
}
|
||||
|
||||
} // end of namespace PixelEngine
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "CommonTypes.h"
|
||||
class PointerWrap;
|
||||
namespace MMIO { class Mapping; }
|
||||
|
||||
// internal hardware addresses
|
||||
enum
|
||||
|
@ -55,18 +56,14 @@ union UPEAlphaReadReg
|
|||
void Init();
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
// Read
|
||||
void Read16(u16& _uReturnValue, const u32 _iAddress);
|
||||
|
||||
// Write
|
||||
void Write16(const u16 _iValue, const u32 _iAddress);
|
||||
void Write32(const u32 _iValue, const u32 _iAddress);
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||
|
||||
// gfx backend support
|
||||
void SetToken(const u16 _token, const int _bSetTokenAcknowledge);
|
||||
void SetFinish(void);
|
||||
void ResetSetFinish(void);
|
||||
void ResetSetToken(void);
|
||||
UPEAlphaReadReg GetAlphaReadMode();
|
||||
|
||||
// Bounding box functionality. Paper Mario (both) are a couple of the few games that use it.
|
||||
extern u16 bbox[4];
|
||||
|
|
|
@ -10,9 +10,7 @@
|
|||
#include "ChunkFile.h"
|
||||
#include "../VideoCommon/PerfQueryBase.h"
|
||||
|
||||
typedef void (*writeFn16)(const u16,const u32);
|
||||
typedef void (*writeFn32)(const u32,const u32);
|
||||
typedef void (*readFn16)(u16&, const u32);
|
||||
namespace MMIO { class Mapping; }
|
||||
|
||||
|
||||
enum FieldType
|
||||
|
@ -110,11 +108,9 @@ public:
|
|||
virtual bool Video_IsHiWatermarkActive() = 0;
|
||||
virtual void Video_AbortFrame() = 0;
|
||||
|
||||
virtual readFn16 Video_CPRead16() = 0;
|
||||
virtual writeFn16 Video_CPWrite16() = 0;
|
||||
virtual readFn16 Video_PERead16() = 0;
|
||||
virtual writeFn16 Video_PEWrite16() = 0;
|
||||
virtual writeFn32 Video_PEWrite32() = 0;
|
||||
// Registers MMIO handlers for the CommandProcessor registers.
|
||||
virtual void RegisterCPMMIO(MMIO::Mapping* mmio, u32 base) = 0;
|
||||
virtual void RegisterPEMMIO(MMIO::Mapping* mmio, u32 base) = 0;
|
||||
|
||||
static void PopulateList();
|
||||
static void ClearList();
|
||||
|
@ -162,11 +158,8 @@ class VideoBackendHardware : public VideoBackend
|
|||
bool Video_IsHiWatermarkActive();
|
||||
void Video_AbortFrame();
|
||||
|
||||
readFn16 Video_CPRead16();
|
||||
writeFn16 Video_CPWrite16();
|
||||
readFn16 Video_PERead16();
|
||||
writeFn16 Video_PEWrite16();
|
||||
writeFn32 Video_PEWrite32();
|
||||
void RegisterCPMMIO(MMIO::Mapping* mmio, u32 base) override;
|
||||
void RegisterPEMMIO(MMIO::Mapping* mmio, u32 base) override;
|
||||
|
||||
void PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
|
||||
void DoState(PointerWrap &p);
|
||||
|
|
Loading…
Reference in New Issue