diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 504b8ad..79f2491 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -107,6 +107,7 @@ set(SRCS ActionReplay.cpp HW/Memmap.cpp HW/MemmapFunctions.cpp HW/MemoryInterface.cpp + HW/MMIO.cpp HW/ProcessorInterface.cpp HW/SI.cpp HW/SI_DeviceAMBaseboard.cpp diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 69b9bda..fdf6fc4 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -145,6 +145,7 @@ + @@ -342,6 +343,8 @@ + + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 40c8903..99626fe 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -510,6 +510,9 @@ HW %28Flipper/Hollywood%29 + + HW %28Flipper/Hollywood%29 + HW %28Flipper/Hollywood%29 @@ -1037,6 +1040,12 @@ HW %28Flipper/Hollywood%29 + + HW %28Flipper/Hollywood%29 + + + HW %28Flipper/Hollywood%29 + HW %28Flipper/Hollywood%29 @@ -1219,4 +1228,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Core/HW/AudioInterface.cpp b/Source/Core/Core/HW/AudioInterface.cpp index 5998b53..e2e15c9 100644 --- a/Source/Core/Core/HW/AudioInterface.cpp +++ b/Source/Core/Core/HW/AudioInterface.cpp @@ -61,6 +61,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 { @@ -169,43 +170,12 @@ void Shutdown() { } -void Read32(u32& _rReturnValue, const u32 _Address) -{ - 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) +void RegisterMMIO(MMIO::Mapping* mmio, u32 base) { - switch (_Address & 0xFFFF) - { - case AI_CONTROL_REGISTER: - { - AICR tmpAICtrl(_Value); + mmio->Register(base | AI_CONTROL_REGISTER, + MMIO::DirectRead(&m_Control.hex), + MMIO::ComplexWrite([](u32, u32 val) { + AICR tmpAICtrl(val); m_Control.AIINTMSK = tmpAICtrl.AIINTMSK; m_Control.AIINTVLD = tmpAICtrl.AIINTVLD; @@ -259,32 +229,42 @@ 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; - - 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; - - 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_VOLUME_REGISTER, + MMIO::DirectRead(&m_Volume.hex), + MMIO::DirectWrite(&m_Volume.hex) + ); + + mmio->Register(base | AI_SAMPLE_COUNTER, + MMIO::ComplexRead([](u32) { + Update(0, 0); + return m_SampleCounter; + }), + MMIO::DirectWrite(&m_SampleCounter) + ); + + mmio->Register(base | AI_INTERRUPT_TIMING, + MMIO::DirectRead(&m_InterruptTiming), + MMIO::ComplexWrite([](u32, u32 val) { + m_InterruptTiming = val; + CoreTiming::RemoveEvent(et_AI); + CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2), et_AI); + }) + ); +} + +void Read32(u32& _rReturnValue, const u32 _Address) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_Address, _rReturnValue); +} + +void Write32(const u32 _Value, const u32 _Address) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_Address, _Value); } static void UpdateInterrupts() diff --git a/Source/Core/Core/HW/AudioInterface.h b/Source/Core/Core/HW/AudioInterface.h index 6893cff..1c82f48 100644 --- a/Source/Core/Core/HW/AudioInterface.h +++ b/Source/Core/Core/HW/AudioInterface.h @@ -10,6 +10,7 @@ #include "CommonTypes.h" class PointerWrap; +namespace MMIO { class Mapping; } namespace AudioInterface { @@ -18,6 +19,8 @@ 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 diff --git a/Source/Core/Core/HW/DSP.cpp b/Source/Core/Core/HW/DSP.cpp index 51ca22d..654ebc0 100644 --- a/Source/Core/Core/HW/DSP.cpp +++ b/Source/Core/Core/HW/DSP.cpp @@ -30,6 +30,7 @@ #include "CPU.h" #include "MemoryUtil.h" #include "Memmap.h" +#include "MMIO.h" #include "ProcessorInterface.h" #include "AudioInterface.h" #include "../PowerPC/PowerPC.h" @@ -287,124 +288,88 @@ void Shutdown() dsp_emulator = NULL; } -void Read16(u16& _uReturnValue, const u32 _iAddress) +void RegisterMMIO(MMIO::Mapping* mmio, u32 base) { - switch (_iAddress & 0xFFFF) - { - // 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; - } + struct { + u32 addr; + u16* ptr; + } 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) }, + { AR_DMA_ARADDR_H, MMIO::Utils::HighPart(&g_arDMA.ARAddr) }, + { AR_DMA_ARADDR_L, MMIO::Utils::LowPart(&g_arDMA.ARAddr) }, + { 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) }, + }; - if (_iAddress != (0xCC000000 + DSP_MAIL_FROM_DSP_HI)) + // Declare all the boilerplate direct MMIOs. + for (auto& mapped_var : directly_mapped_vars) { - DEBUG_LOG(DSPINTERFACE, "DSPInterface(r16) 0x%08x (0x%04x) (%08x)", _iAddress, _uReturnValue, PowerPC::ppcState.pc); + mmio->Register(base | mapped_var.addr, + MMIO::DirectRead(mapped_var.ptr), + MMIO::DirectWrite(mapped_var.ptr) + ); } -} - -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: - { + // DSP mail MMIOs call DSP emulator functions to get results or write data. + mmio->Register(base | DSP_MAIL_TO_DSP_HI, + MMIO::ComplexRead([](u32) { + if (dsp_slice > DSP_MAIL_SLICE && dsp_is_lle) + { + dsp_emulator->DSP_Update(DSP_MAIL_SLICE); + dsp_slice -= DSP_MAIL_SLICE; + } + return dsp_emulator->DSP_ReadMailBoxHigh(true); + }), + MMIO::ComplexWrite([](u32, u16 val) { + dsp_emulator->DSP_WriteMailBoxHigh(true, val); + }) + ); + mmio->Register(base | DSP_MAIL_TO_DSP_LO, + MMIO::ComplexRead([](u32) { + return dsp_emulator->DSP_ReadMailBoxLow(true); + }), + MMIO::ComplexWrite([](u32, u16 val) { + dsp_emulator->DSP_WriteMailBoxLow(true, val); + }) + ); + mmio->Register(base | DSP_MAIL_FROM_DSP_HI, + MMIO::ComplexRead([](u32) { + if (dsp_slice > DSP_MAIL_SLICE && dsp_is_lle) + { + dsp_emulator->DSP_Update(DSP_MAIL_SLICE); + dsp_slice -= DSP_MAIL_SLICE; + } + return dsp_emulator->DSP_ReadMailBoxHigh(false); + }), + MMIO::InvalidWrite() + ); + mmio->Register(base | DSP_MAIL_FROM_DSP_LO, + MMIO::ComplexRead([](u32) { + return dsp_emulator->DSP_ReadMailBoxLow(false); + }), + MMIO::InvalidWrite() + ); + + mmio->Register(base | DSP_CONTROL, + MMIO::ComplexRead([](u32) { + return (g_dspState.DSPControl.Hex & ~DSP_CONTROL_MASK) | + (dsp_emulator->DSP_ReadControlRegister() & DSP_CONTROL_MASK); + }), + MMIO::ComplexWrite([](u32, u16 val) { UDSPControl tmpControl; - tmpControl.Hex = (_Value & ~DSP_CONTROL_MASK) | - (dsp_emulator->DSP_WriteControlRegister(_Value) & DSP_CONTROL_MASK); + tmpControl.Hex = (val & ~DSP_CONTROL_MASK) | + (dsp_emulator->DSP_WriteControlRegister(val) & DSP_CONTROL_MASK); // Not really sure if this is correct, but it works... // Kind of a hack because DSP_CONTROL_MASK should make this bit // only viewable to dsp emulator - if (_Value & 1 /*DSPReset*/) + if (val & 1 /*DSPReset*/) { g_audioDMA.AudioDMAControl.Hex = 0; } @@ -430,177 +395,66 @@ 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; - - // 0x43 - // Monster Hunter Tri, DKCR - - // 0x43, 0x63: - // Rebel Strike, Clone Wars, WWE DOR2, Mario Golf - - // 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; - } + }) + ); + + // ARAM MMIO controlling the DMA start. + mmio->Register(base | AR_DMA_CNT_L, + MMIO::DirectRead(MMIO::Utils::LowPart(&g_arDMA.Cnt.Hex)), + MMIO::ComplexWrite([](u32, u16 val) { + g_arDMA.Cnt.Hex = (g_arDMA.Cnt.Hex & 0xFFFF0000) | (val & ~31); + Do_ARAM_DMA(); + }) + ); + + // Audio DMA MMIO controlling the DMA start. + mmio->Register(base | AUDIO_DMA_CONTROL_LEN, + MMIO::DirectRead(&g_audioDMA.AudioDMAControl.Hex), + MMIO::ComplexWrite([](u32, u16 val) { + g_audioDMA.AudioDMAControl.Hex = val; + g_audioDMA.ReadAddress = g_audioDMA.SourceAddress; + g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks; + }) + ); + + // Audio DMA blocks remaining is invalid to write to, and requires logic on + // the read side. + mmio->Register(base | AUDIO_DMA_BLOCKS_LEFT, + MMIO::ComplexRead([](u32) { + return (g_audioDMA.BlocksLeft > 0 ? g_audioDMA.BlocksLeft - 1 : 0); + }), + MMIO::InvalidWrite() + ); } -void Read32(u32& _uReturnValue, const u32 _iAddress) +void Read16(u16& _uReturnValue, const u32 _iAddress) { - INFO_LOG(DSPINTERFACE, "DSPInterface(r32) 0x%08x", _iAddress); + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_iAddress, _uReturnValue); +} - switch (_iAddress & 0xFFFF) - { - // 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; - } +void Write16(const u16 _Value, const u32 _Address) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_Address, _Value); } -void Write32(const u32 _iValue, const u32 _iAddress) +void Read32(u32& _uReturnValue, const u32 _iAddress) { - INFO_LOG(DSPINTERFACE, "DSPInterface(w32) 0x%08x 0x%08x", _iValue, _iAddress); + u16 Hi = 0, Lo = 0; + Read16(Hi, _iAddress); + Read16(Lo, _iAddress + 2); + _uReturnValue = (Hi << 16) | Lo; +} - 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; - } +void Write32(const u32 _iValue, const u32 _iAddress) +{ + Write16(_iValue >> 16, _iAddress); + Write16(_iValue & 0xFFFF, _iAddress + 2); } diff --git a/Source/Core/Core/HW/DSP.h b/Source/Core/Core/HW/DSP.h index 8feabf4..8424da0 100644 --- a/Source/Core/Core/HW/DSP.h +++ b/Source/Core/Core/HW/DSP.h @@ -8,6 +8,7 @@ #include "Common.h" class PointerWrap; class DSPEmulator; +namespace MMIO { class Mapping; } namespace DSP { @@ -29,6 +30,8 @@ enum void Init(bool hle); void Shutdown(); +void RegisterMMIO(MMIO::Mapping* mmio, u32 base); + DSPEmulator *GetDSPEmulator(); void DoState(PointerWrap &p); diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index 52696eb..19f125a 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -17,6 +17,7 @@ #include "../VolumeHandler.h" #include "AudioInterface.h" #include "../Movie.h" +#include "MMIO.h" // Disc transfer rate measured in bytes per second static const u32 DISC_TRANSFER_RATE_GC = 5 * 1024 * 1024; @@ -404,38 +405,12 @@ bool DVDReadADPCM(u8* _pDestBuffer, u32 _iNumSamples) } } -void Read32(u32& _uReturnValue, const u32 _iAddress) -{ - 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) +void RegisterMMIO(MMIO::Mapping* mmio, u32 base) { - DEBUG_LOG(DVDINTERFACE, "(w32): 0x%08x @ 0x%08x", _iValue, _iAddress); - - switch (_iAddress & 0xFF) - { - case DI_STATUS_REGISTER: - { - UDISR tmpStatusReg(_iValue); + mmio->Register(base | DI_STATUS_REGISTER, + MMIO::DirectRead(&m_DISR.Hex), + MMIO::ComplexWrite([](u32, u32 val) { + UDISR tmpStatusReg(val); m_DISR.DEINITMASK = tmpStatusReg.DEINITMASK; m_DISR.TCINTMASK = tmpStatusReg.TCINTMASK; @@ -457,12 +432,13 @@ void Write32(const u32 _iValue, const u32 _iAddress) } UpdateInterrupts(); - } - break; + }) + ); - case DI_COVER_REGISTER: - { - UDICVR tmpCoverReg(_iValue); + mmio->Register(base | DI_COVER_REGISTER, + MMIO::DirectRead(&m_DICVR.Hex), + MMIO::ComplexWrite([](u32, u32 val) { + UDICVR tmpCoverReg(val); m_DICVR.CVRINTMASK = tmpCoverReg.CVRINTMASK; @@ -470,26 +446,32 @@ void Write32(const u32 _iValue, const u32 _iAddress) m_DICVR.CVRINT = 0; UpdateInterrupts(); - } - break; - - case DI_COMMAND_0: m_DICMDBUF[0].Hex = _iValue; break; - case DI_COMMAND_1: m_DICMDBUF[1].Hex = _iValue; break; - case DI_COMMAND_2: m_DICMDBUF[2].Hex = _iValue; break; - - 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; + }) + ); + + // Command registers are very similar and we can register them with a + // simple loop. + for (int i = 0; i < 3; ++i) + mmio->Register(base | (DI_COMMAND_0 + 4 * i), + MMIO::DirectRead(&m_DICMDBUF[i].Hex), + MMIO::DirectWrite(&m_DICMDBUF[i].Hex) + ); + + // DMA related registers. Mostly direct accesses (+ masking for writes to + // handle things like address alignment) and complex write on the DMA + // control register that will trigger the DMA. + mmio->Register(base | DI_DMA_ADDRESS_REGISTER, + MMIO::DirectRead(&m_DIMAR.Hex), + MMIO::DirectWrite(&m_DIMAR.Hex, ~0xFC00001F) + ); + mmio->Register(base | DI_DMA_LENGTH_REGISTER, + MMIO::DirectRead(&m_DILENGTH.Hex), + MMIO::DirectWrite(&m_DILENGTH.Hex, ~0x1F) + ); + mmio->Register(base | DI_DMA_CONTROL_REGISTER, + MMIO::DirectRead(&m_DICR.Hex), + MMIO::ComplexWrite([](u32, u32 val) { + m_DICR.Hex = val & 7; if (m_DICR.TSTART) { if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed) @@ -504,21 +486,31 @@ void Write32(const u32 _iValue, const u32 _iAddress) ExecuteCommand(m_DICR); } } - } - break; - - case DI_IMMEDIATE_DATA_BUFFER: m_DIIMMBUF.Hex = _iValue; break; + }) + ); + + mmio->Register(base | DI_IMMEDIATE_DATA_BUFFER, + MMIO::DirectRead(&m_DIIMMBUF.Hex), + MMIO::DirectWrite(&m_DIIMMBUF.Hex) + ); + + // DI config register is read only. + mmio->Register(base | DI_CONFIG_REGISTER, + MMIO::DirectRead(&m_DICFG.Hex), + MMIO::InvalidWrite() + ); +} - case DI_CONFIG_REGISTER: - { - WARN_LOG(DVDINTERFACE, "Write to DICFG, ignored as it's read-only"); - } - break; +void Read32(u32& _uReturnValue, const u32 _iAddress) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_iAddress, _uReturnValue); +} - default: - _dbg_assert_msg_(DVDINTERFACE, 0, "Write to unknown DI address 0x%08x", _iAddress); - break; - } +void Write32(const u32 _iValue, const u32 _iAddress) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_iAddress, _iValue); } void UpdateInterrupts() diff --git a/Source/Core/Core/HW/DVDInterface.h b/Source/Core/Core/HW/DVDInterface.h index 8d96485..3e602dd 100644 --- a/Source/Core/Core/HW/DVDInterface.h +++ b/Source/Core/Core/HW/DVDInterface.h @@ -7,6 +7,7 @@ #include "CommonTypes.h" class PointerWrap; +namespace MMIO { class Mapping; } namespace DVDInterface { @@ -15,6 +16,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(); diff --git a/Source/Core/Core/HW/EXI.cpp b/Source/Core/Core/HW/EXI.cpp index 4e9c905..850d481 100644 --- a/Source/Core/Core/HW/EXI.cpp +++ b/Source/Core/Core/HW/EXI.cpp @@ -9,6 +9,7 @@ #include "ProcessorInterface.h" #include "../PowerPC/PowerPC.h" +#include "MMIO.h" #include "EXI.h" #include "Sram.h" @@ -62,6 +63,19 @@ void PauseAndLock(bool doLock, bool unpauseOnUnlock) channel->PauseAndLock(doLock, unpauseOnUnlock); } +void RegisterMMIO(MMIO::Mapping* mmio, u32 base) +{ + for (int i = 0; i < MAX_EXI_CHANNELS; ++i) + { + _dbg_assert_(EXPANSIONINTERFACE, g_Channels[i] != nullptr); + // Each channel has 5 32 bit registers assigned to it. We offset the + // base that we give to each channel for registration. + // + // Be careful: this means the base is no longer aligned on a page + // boundary and using "base | FOO" is not valid! + g_Channels[i]->RegisterMMIO(mmio, base + 5 * 4 * i); + } +} void ChangeDeviceCallback(u64 userdata, int cyclesLate) { @@ -101,34 +115,14 @@ void 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; - } + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_iAddress, _uReturnValue); } 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); + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_iAddress, _iValue); } void UpdateInterrupts() diff --git a/Source/Core/Core/HW/EXI.h b/Source/Core/Core/HW/EXI.h index f93af36..3730098 100644 --- a/Source/Core/Core/HW/EXI.h +++ b/Source/Core/Core/HW/EXI.h @@ -9,6 +9,7 @@ #include "EXI_Channel.h" #include "Thread.h" class PointerWrap; +namespace MMIO { class Mapping; } enum { @@ -23,6 +24,8 @@ void Shutdown(); void DoState(PointerWrap &p); void PauseAndLock(bool doLock, bool unpauseOnUnlock); +void RegisterMMIO(MMIO::Mapping* mmio, u32 base); + void Update(); void UpdateInterrupts(); diff --git a/Source/Core/Core/HW/EXI_Channel.cpp b/Source/Core/Core/HW/EXI_Channel.cpp index a57b069..52fd79f 100644 --- a/Source/Core/Core/HW/EXI_Channel.cpp +++ b/Source/Core/Core/HW/EXI_Channel.cpp @@ -7,6 +7,7 @@ #include "EXI.h" #include "../ConfigManager.h" #include "../Movie.h" +#include "MMIO.h" #define EXI_READ 0 #define EXI_WRITE 1 @@ -41,6 +42,117 @@ CEXIChannel::~CEXIChannel() RemoveDevices(); } +void CEXIChannel::RegisterMMIO(MMIO::Mapping* mmio, u32 base) +{ + // Warning: the base is not aligned on a page boundary here. We can't use | + // to select a register address, instead we need to use +. + + mmio->Register(base + EXI_STATUS, + MMIO::ComplexRead([this](u32) { + // check if external device is present + // pretty sure it is memcard only, not entirely sure + if (m_ChannelId == 2) + { + m_Status.EXT = 0; + } + else + { + m_Status.EXT = GetDevice(1)->IsPresent() ? 1 : 0; + } + + return m_Status.Hex; + }), + MMIO::ComplexWrite([this](u32, u32 val) { + UEXI_STATUS newStatus(val); + + m_Status.EXIINTMASK = newStatus.EXIINTMASK; + if (newStatus.EXIINT) + m_Status.EXIINT = 0; + + m_Status.TCINTMASK = newStatus.TCINTMASK; + if (newStatus.TCINT) + m_Status.TCINT = 0; + + m_Status.CLK = newStatus.CLK; + + if (m_ChannelId == 0 || m_ChannelId == 1) + { + m_Status.EXTINTMASK = newStatus.EXTINTMASK; + + if (newStatus.EXTINT) + m_Status.EXTINT = 0; + } + + if (m_ChannelId == 0) + m_Status.ROMDIS = newStatus.ROMDIS; + + IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT ^ newStatus.CHIP_SELECT); + m_Status.CHIP_SELECT = newStatus.CHIP_SELECT; + if (pDevice != NULL) + pDevice->SetCS(m_Status.CHIP_SELECT); + + CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0); + }) + ); + + mmio->Register(base + EXI_DMAADDR, + MMIO::DirectRead(&m_DMAMemoryAddress), + MMIO::DirectWrite(&m_DMAMemoryAddress) + ); + mmio->Register(base + EXI_DMALENGTH, + MMIO::DirectRead(&m_DMALength), + MMIO::DirectWrite(&m_DMALength) + ); + mmio->Register(base + EXI_DMACONTROL, + MMIO::DirectRead(&m_Control.Hex), + MMIO::ComplexWrite([this](u32, u32 val) { + m_Control.Hex = val; + + if (m_Control.TSTART) + { + IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT); + if (pDevice == NULL) + return; + + if (m_Control.DMA == 0) + { + // immediate data + switch (m_Control.RW) + { + case EXI_READ: m_ImmData = pDevice->ImmRead(m_Control.TLEN + 1); break; + case EXI_WRITE: pDevice->ImmWrite(m_ImmData, m_Control.TLEN + 1); break; + case EXI_READWRITE: pDevice->ImmReadWrite(m_ImmData, m_Control.TLEN + 1); break; + default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI Imm: Unknown transfer type %i", m_Control.RW); + } + m_Control.TSTART = 0; + } + else + { + // DMA + switch (m_Control.RW) + { + case EXI_READ: pDevice->DMARead (m_DMAMemoryAddress, m_DMALength); break; + case EXI_WRITE: pDevice->DMAWrite(m_DMAMemoryAddress, m_DMALength); break; + default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI DMA: Unknown transfer type %i", m_Control.RW); + } + m_Control.TSTART = 0; + } + + if(!m_Control.TSTART) // completed ! + { + m_Status.TCINT = 1; + CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0); + } + } + }) + ); + + mmio->Register(base + EXI_IMMDATA, + MMIO::DirectRead(&m_ImmData), + MMIO::DirectWrite(&m_ImmData) + ); +} + void CEXIChannel::RemoveDevices() { for (auto& device : m_pDevices) @@ -115,152 +227,6 @@ void CEXIChannel::Update() device->Update(); } -void CEXIChannel::Read32(u32& _uReturnValue, const u32 _iRegister) -{ - switch (_iRegister) - { - case EXI_STATUS: - { - // check if external device is present - // pretty sure it is memcard only, not entirely sure - if (m_ChannelId == 2) - { - m_Status.EXT = 0; - } - else - { - m_Status.EXT = GetDevice(1)->IsPresent() ? 1 : 0; - } - - _uReturnValue = m_Status.Hex; - break; - } - - case EXI_DMAADDR: - _uReturnValue = m_DMAMemoryAddress; - break; - - case EXI_DMALENGTH: - _uReturnValue = m_DMALength; - break; - - case EXI_DMACONTROL: - _uReturnValue = m_Control.Hex; - break; - - case EXI_IMMDATA: - _uReturnValue = m_ImmData; - break; - - default: - _dbg_assert_(EXPANSIONINTERFACE, 0); - _uReturnValue = 0xDEADBEEF; - } - - DEBUG_LOG(EXPANSIONINTERFACE, "(r32) 0x%08x channel: %i register: %s", - _uReturnValue, m_ChannelId, Debug_GetRegisterName(_iRegister)); -} - -void CEXIChannel::Write32(const u32 _iValue, const u32 _iRegister) -{ - DEBUG_LOG(EXPANSIONINTERFACE, "(w32) 0x%08x channel: %i register: %s", - _iValue, m_ChannelId, Debug_GetRegisterName(_iRegister)); - - switch (_iRegister) - { - case EXI_STATUS: - { - UEXI_STATUS newStatus(_iValue); - - m_Status.EXIINTMASK = newStatus.EXIINTMASK; - if (newStatus.EXIINT) - m_Status.EXIINT = 0; - - m_Status.TCINTMASK = newStatus.TCINTMASK; - if (newStatus.TCINT) - m_Status.TCINT = 0; - - m_Status.CLK = newStatus.CLK; - - if (m_ChannelId == 0 || m_ChannelId == 1) - { - m_Status.EXTINTMASK = newStatus.EXTINTMASK; - - if (newStatus.EXTINT) - m_Status.EXTINT = 0; - } - - if (m_ChannelId == 0) - m_Status.ROMDIS = newStatus.ROMDIS; - - IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT ^ newStatus.CHIP_SELECT); - m_Status.CHIP_SELECT = newStatus.CHIP_SELECT; - if (pDevice != NULL) - pDevice->SetCS(m_Status.CHIP_SELECT); - - CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0); - } - break; - - case EXI_DMAADDR: - INFO_LOG(EXPANSIONINTERFACE, "Wrote DMAAddr, channel %i", m_ChannelId); - m_DMAMemoryAddress = _iValue; - break; - - case EXI_DMALENGTH: - INFO_LOG(EXPANSIONINTERFACE, "Wrote DMALength, channel %i", m_ChannelId); - m_DMALength = _iValue; - break; - - case EXI_DMACONTROL: - INFO_LOG(EXPANSIONINTERFACE, "Wrote DMAControl, channel %i", m_ChannelId); - m_Control.Hex = _iValue; - - if (m_Control.TSTART) - { - IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT); - if (pDevice == NULL) - return; - - if (m_Control.DMA == 0) - { - // immediate data - switch (m_Control.RW) - { - case EXI_READ: m_ImmData = pDevice->ImmRead(m_Control.TLEN + 1); break; - case EXI_WRITE: pDevice->ImmWrite(m_ImmData, m_Control.TLEN + 1); break; - case EXI_READWRITE: pDevice->ImmReadWrite(m_ImmData, m_Control.TLEN + 1); break; - default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI Imm: Unknown transfer type %i", m_Control.RW); - } - m_Control.TSTART = 0; - } - else - { - // DMA - switch (m_Control.RW) - { - case EXI_READ: pDevice->DMARead (m_DMAMemoryAddress, m_DMALength); break; - case EXI_WRITE: pDevice->DMAWrite(m_DMAMemoryAddress, m_DMALength); break; - default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI DMA: Unknown transfer type %i", m_Control.RW); - } - m_Control.TSTART = 0; - } - - if(!m_Control.TSTART) // completed ! - { - m_Status.TCINT = 1; - CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0); - } - } - break; - - case EXI_IMMDATA: - INFO_LOG(EXPANSIONINTERFACE, "Wrote IMMData, channel %i", m_ChannelId); - m_ImmData = _iValue; - break; - } -} - void CEXIChannel::DoState(PointerWrap &p) { p.DoPOD(m_Status); diff --git a/Source/Core/Core/HW/EXI_Channel.h b/Source/Core/Core/HW/EXI_Channel.h index 1555df3..df794b5 100644 --- a/Source/Core/Core/HW/EXI_Channel.h +++ b/Source/Core/Core/HW/EXI_Channel.h @@ -10,30 +10,20 @@ #include "EXI_Device.h" #include +namespace MMIO { class Mapping; } + class CEXIChannel { private: enum { - EXI_STATUS = 0, - EXI_DMAADDR = 1, - EXI_DMALENGTH = 2, - EXI_DMACONTROL = 3, - EXI_IMMDATA = 4 + EXI_STATUS = 0x00, + EXI_DMAADDR = 0x04, + EXI_DMALENGTH = 0x08, + EXI_DMACONTROL = 0x0C, + EXI_IMMDATA = 0x10 }; - const char* Debug_GetRegisterName(u32 _register) - { - switch (_register) - { - case EXI_STATUS: return "STATUS"; - case EXI_DMAADDR: return "DMAADDR"; - case EXI_DMALENGTH: return "DMALENGTH"; - case EXI_DMACONTROL: return "DMACONTROL"; - case EXI_IMMDATA: return "IMMDATA"; - default: return "!!! Unknown EXI Register !!!"; - } - } // EXI Status Register - "Channel Parameter Register" union UEXI_STATUS @@ -105,15 +95,14 @@ public: CEXIChannel(u32 ChannelId); ~CEXIChannel(); + void RegisterMMIO(MMIO::Mapping* mmio, u32 base); + void AddDevice(const TEXIDevices device_type, const int device_num); void AddDevice(IEXIDevice* pDevice, const int device_num, bool notifyPresenceChanged=true); // Remove all devices void RemoveDevices(); - void Read32(u32& _uReturnValue, const u32 _iRegister); - void Write32(const u32 _iValue, const u32 _iRegister); - void Update(); bool IsCausingInterrupt(); void DoState(PointerWrap &p); diff --git a/Source/Core/Core/HW/HW.cpp b/Source/Core/Core/HW/HW.cpp index 586344e..63f9345 100644 --- a/Source/Core/Core/HW/HW.cpp +++ b/Source/Core/Core/HW/HW.cpp @@ -38,11 +38,11 @@ namespace HW VideoInterface::Init(); SerialInterface::Init(); ProcessorInterface::Init(); + ExpansionInterface::Init(); // Needs to be initialized before Memory Memory::Init(); DSP::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPHLE); DVDInterface::Init(); GPFifo::Init(); - ExpansionInterface::Init(); CCPU::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.iCPUCore); SystemTimers::Init(); diff --git a/Source/Core/Core/HW/MMIO.cpp b/Source/Core/Core/HW/MMIO.cpp new file mode 100644 index 0000000..894f345 --- /dev/null +++ b/Source/Core/Core/HW/MMIO.cpp @@ -0,0 +1,340 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official Git repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "MMIO.h" +#include "MMIOHandlers.h" + +#include + +namespace MMIO +{ + +// Base classes for the two handling method hierarchies. Note that a single +// class can inherit from both. +// +// At the moment the only common element between all the handling method is +// that they should be able to accept a visitor of the appropriate type. +template +class ReadHandlingMethod +{ +public: + virtual ~ReadHandlingMethod() {} + virtual void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const = 0; +}; +template +class WriteHandlingMethod +{ +public: + virtual ~WriteHandlingMethod() {} + virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const = 0; +}; + +// Constant: handling method holds a single integer and passes it to the +// visitor. This is a read only handling method: storing to a constant does not +// mean anything. +template +class ConstantHandlingMethod : public ReadHandlingMethod +{ +public: + explicit ConstantHandlingMethod(T value) : value_(value) + { + } + + virtual ~ConstantHandlingMethod() {} + + virtual void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const + { + v.VisitConstant(value_); + } + +private: + T value_; +}; +template +ReadHandlingMethod* Constant(T value) +{ + return new ConstantHandlingMethod(value); +} + +// Nop: extremely simple write handling method that does nothing at all, only +// respond to visitors and dispatch to the correct method. This is write only +// since reads should always at least return a value. +template +class NopHandlingMethod : public WriteHandlingMethod +{ +public: + NopHandlingMethod() {} + virtual ~NopHandlingMethod() {} + virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const + { + v.VisitNop(); + } +}; +template +WriteHandlingMethod* Nop() +{ + return new NopHandlingMethod(); +} + +// Direct: handling method holds a pointer to the value where to read/write the +// data from, as well as a mask that is used to restrict reading/writing only +// to a given set of bits. +template +class DirectHandlingMethod : public ReadHandlingMethod, + public WriteHandlingMethod +{ +public: + DirectHandlingMethod(T* addr, u32 mask) : addr_(addr), mask_(mask) + { + } + + virtual ~DirectHandlingMethod() {} + + virtual void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const + { + v.VisitDirect(addr_, mask_); + } + + virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const + { + v.VisitDirect(addr_, mask_); + } + +private: + T* addr_; + u32 mask_; +}; +template +ReadHandlingMethod* DirectRead(const T* addr, u32 mask) +{ + return new DirectHandlingMethod(const_cast(addr), mask); +} +template +ReadHandlingMethod* DirectRead(volatile const T* addr, u32 mask) +{ + return new DirectHandlingMethod((T*)addr, mask); +} +template +WriteHandlingMethod* DirectWrite(T* addr, u32 mask) +{ + return new DirectHandlingMethod(addr, mask); +} +template +WriteHandlingMethod* DirectWrite(volatile T* addr, u32 mask) +{ + return new DirectHandlingMethod((T*)addr, mask); +} + +// Complex: holds a lambda that is called when a read or a write is executed. +// This gives complete control to the user as to what is going to happen during +// that read or write, but reduces the optimization potential. +template +class ComplexHandlingMethod : public ReadHandlingMethod, + public WriteHandlingMethod +{ +public: + explicit ComplexHandlingMethod(std::function read_lambda) + : read_lambda_(read_lambda), write_lambda_(InvalidWriteLambda()) + { + } + + explicit ComplexHandlingMethod(std::function write_lambda) + : read_lambda_(InvalidReadLambda()), write_lambda_(write_lambda) + { + } + + virtual ~ComplexHandlingMethod() {} + + virtual void AcceptReadVisitor(ReadHandlingMethodVisitor& v) const + { + v.VisitComplex(read_lambda_); + } + + virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor& v) const + { + v.VisitComplex(write_lambda_); + } + +private: + std::function InvalidReadLambda() const + { + return [](u32) { + _dbg_assert_msg_(MEMMAP, 0, "Called the read lambda on a write " + "complex handler."); + return 0; + }; + } + + std::function InvalidWriteLambda() const + { + return [](u32, T) { + _dbg_assert_msg_(MEMMAP, 0, "Called the write lambda on a read " + "complex handler."); + }; + } + + std::function read_lambda_; + std::function write_lambda_; +}; +template +ReadHandlingMethod* ComplexRead(std::function lambda) +{ + return new ComplexHandlingMethod(lambda); +} +template +WriteHandlingMethod* ComplexWrite(std::function lambda) +{ + return new ComplexHandlingMethod(lambda); +} + +// Invalid: specialization of the complex handling type with lambdas that +// display error messages. +template +ReadHandlingMethod* InvalidRead() +{ + return ComplexRead([](u32 addr) { + ERROR_LOG(MEMMAP, "Trying to read from an invalid MMIO (addr=%08x)", + addr); + return -1; + }); +} +template +WriteHandlingMethod* InvalidWrite() +{ + return ComplexWrite([](u32 addr, T val) { + ERROR_LOG(MEMMAP, "Trying to write to an invalid MMIO (addr=%08x, val=%08x)", + addr, (u32)val); + }); +} + +// Inplementation of the ReadHandler and WriteHandler class. There is a lot of +// redundant code between these two classes but trying to abstract it away +// brings more trouble than it fixes. +template +ReadHandler::ReadHandler() : m_Method(nullptr) +{ + ResetMethod(InvalidRead()); +} + +template +ReadHandler::ReadHandler(ReadHandlingMethod* method) + : m_Method(nullptr) +{ + ResetMethod(method); +} + +template +ReadHandler::~ReadHandler() +{ +} + +template +void ReadHandler::Visit(ReadHandlingMethodVisitor& visitor) const +{ + m_Method->AcceptReadVisitor(visitor); +} + +template +void ReadHandler::ResetMethod(ReadHandlingMethod* method) +{ + m_Method.reset(method); + + struct FuncCreatorVisitor : public ReadHandlingMethodVisitor + { + std::function ret; + + virtual void VisitConstant(T value) + { + ret = [value](u32) { return value; }; + } + + virtual void VisitDirect(const T* addr, u32 mask) + { + ret = [addr, mask](u32) { return *addr & mask; }; + } + + virtual void VisitComplex(std::function lambda) + { + ret = lambda; + } + }; + + FuncCreatorVisitor v; + Visit(v); + m_ReadFunc = v.ret; +} + +template +WriteHandler::WriteHandler() : m_Method(nullptr) +{ + ResetMethod(InvalidWrite()); +} + +template +WriteHandler::WriteHandler(WriteHandlingMethod* method) + : m_Method(nullptr) +{ + ResetMethod(method); +} + +template +WriteHandler::~WriteHandler() +{ +} + +template +void WriteHandler::Visit(WriteHandlingMethodVisitor& visitor) const +{ + m_Method->AcceptWriteVisitor(visitor); +} + +template +void WriteHandler::ResetMethod(WriteHandlingMethod* method) +{ + m_Method.reset(method); + + struct FuncCreatorVisitor : public WriteHandlingMethodVisitor + { + std::function ret; + + virtual void VisitNop() + { + ret = [](u32, T) {}; + } + + virtual void VisitDirect(T* ptr, u32 mask) + { + ret = [ptr, mask](u32, T val) { *ptr = val & mask; }; + } + + virtual void VisitComplex(std::function lambda) + { + ret = lambda; + } + }; + + FuncCreatorVisitor v; + Visit(v); + m_WriteFunc = v.ret; +} + +// Define all the public specializations that are exported in MMIOHandlers.h. +MMIO_PUBLIC_SPECIALIZATIONS(, u8); +MMIO_PUBLIC_SPECIALIZATIONS(, u16); +MMIO_PUBLIC_SPECIALIZATIONS(, u32); + +} diff --git a/Source/Core/Core/HW/MMIO.h b/Source/Core/Core/HW/MMIO.h new file mode 100644 index 0000000..14440fd --- /dev/null +++ b/Source/Core/Core/HW/MMIO.h @@ -0,0 +1,149 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official Git repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _MMIO_H +#define _MMIO_H + +#include "Common.h" +#include +#include +#include + +#include "MMIOHandlers.h" + +// HACK: Remove when the new MMIO interface is used. +#include "Memmap.h" + +namespace MMIO +{ + +// There are three main MMIO blocks on the Wii (only one on the GameCube): +// - 0xCC00xxxx: GameCube MMIOs (CP, PE, VI, PI, MI, DSP, DVD, SI, EI, AI, GP) +// - 0xCD00xxxx: Wii MMIOs and GC mirrors (IPC, DVD, SI, EI, AI) +// - 0xCD80xxxx: Mirror of 0xCD00xxxx. +// +// In practice, since the third block is a mirror of the second one, we can +// assume internally that there are only two blocks: one for GC, one for Wii. +enum Block +{ + GC_BLOCK = 0, + WII_BLOCK = 1, + + NUM_BLOCKS +}; +const u32 BLOCK_SIZE = 0x10000; +const u32 NUM_MMIOS = NUM_BLOCKS * BLOCK_SIZE; + +// Compute the internal unique ID for a given MMIO address. This ID is computed +// from a very simple formula: (1 + block_id) * lower_16_bits(address). +// +// The block ID can easily be computed by simply checking bit 24 (CC vs. CD). +inline u32 UniqueID(u32 address) +{ + _dbg_assert_msg_(MEMMAP, ((address & 0xFFFF0000) == 0xCC000000) || + ((address & 0xFFFF0000) == 0xCD000000) || + ((address & 0xFFFF0000) == 0xCD800000), + "Trying to get the ID of a non-existing MMIO address."); + + return (address & 0xFFFF) * (1 + ((address >> 24) & 1)); +} + +// Some utilities functions to define MMIO mappings. +namespace Utils +{ +// Allow grabbing pointers to the high and low part of a 32 bits pointer. +inline u16* LowPart(u32* ptr) { return (u16*)ptr; } +inline u16* LowPart(volatile u32* ptr) { return (u16*)ptr; } +inline u16* HighPart(u32* ptr) { return LowPart(ptr) + 1; } +inline u16* HighPart(volatile u32* ptr) { return LowPart(ptr) + 1; } +} + +class Mapping +{ +public: + // MMIO registration interface. Use this to register new MMIO handlers. +#define REGISTER_FUNCS(Size) \ + void RegisterRead(u32 addr, ReadHandlingMethod* read) \ + { \ + u32 id = UniqueID(addr) / sizeof (u##Size); \ + m_Read##Size##Handlers[id].ResetMethod(read); \ + } \ + void RegisterWrite(u32 addr, WriteHandlingMethod* write) \ + { \ + u32 id = UniqueID(addr) / sizeof (u##Size); \ + m_Write##Size##Handlers[id].ResetMethod(write); \ + } \ + void Register(u32 addr, ReadHandlingMethod* read, \ + WriteHandlingMethod* write) \ + { \ + RegisterRead(addr, read); \ + RegisterWrite(addr, write); \ + } + REGISTER_FUNCS(8) REGISTER_FUNCS(16) REGISTER_FUNCS(32) +#undef REGISTER_FUNCS + + // Direct read/write interface. + // + // These functions allow reading/writing an MMIO register at a given + // address. They are used by the Memory:: access functions, which are + // called in interpreter mode, from Dolphin's own code, or from JIT'd code + // where the access address could not be predicted. + // + // Note that for reads we cannot simply return the read value because C++ + // allows overloading only with parameter types, not return types. +#define READ_FUNC(Size) \ + void Read(u32 addr, u##Size& val) const \ + { \ + u32 id = UniqueID(addr) / sizeof (u##Size); \ + val = m_Read##Size##Handlers[id].Read(addr); \ + } + READ_FUNC(8) READ_FUNC(16) READ_FUNC(32) +#undef READ_FUNC + +#define WRITE_FUNC(Size) \ + void Write(u32 addr, u##Size val) const \ + { \ + u32 id = UniqueID(addr) / sizeof (u##Size); \ + m_Write##Size##Handlers[id].Write(addr, val); \ + } + WRITE_FUNC(8) WRITE_FUNC(16) WRITE_FUNC(32) +#undef WRITE_FUNC + + // Dummy 64 bits variants of these functions. While 64 bits MMIO access is + // not supported, we need these in order to make the code compile. + void Read(u32 addr, u64& val) const { _dbg_assert_(MEMMAP, 0); } + void Write(u32 addr, u64 val) const { _dbg_assert_(MEMMAP, 0); } + +private: + // These arrays contain the handlers for each MMIO access type: read/write + // to 8/16/32 bits. They are indexed using the UniqueID(addr) function + // defined earlier, which maps an MMIO address to a unique ID by using the + // MMIO block ID. + // + // Each array contains NUM_MMIOS / sizeof (AccessType) because larger + // access types mean less possible adresses (assuming aligned only + // accesses). +#define HANDLERS(Size) \ + std::array, NUM_MMIOS / sizeof (u##Size)> m_Read##Size##Handlers; \ + std::array, NUM_MMIOS / sizeof (u##Size)> m_Write##Size##Handlers; + HANDLERS(8) HANDLERS(16) HANDLERS(32) +#undef HANDLERS +}; + +} + +#endif diff --git a/Source/Core/Core/HW/MMIOHandlers.h b/Source/Core/Core/HW/MMIOHandlers.h new file mode 100644 index 0000000..d0fedab --- /dev/null +++ b/Source/Core/Core/HW/MMIOHandlers.h @@ -0,0 +1,181 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official Git repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _MMIOHANDLERS_H +#define _MMIOHANDLERS_H + +#include "Common.h" + +#include +#include + +// All the templated and very repetitive MMIO-related code is isolated in this +// file for easier reading. It mostly contains code related to handling methods +// (including the declaration of the public functions for creating handling +// method objects), visitors for these handling methods, and interface of the +// handler classes. +// +// This code is very genericized (aka. lots of templates) in order to handle +// u8/u16/u32 with the same code while providing type safety: it is impossible +// to mix code from these types, and the type system enforces it. + +namespace MMIO +{ + +// Read and write handling methods are separated for type safety. On top of +// that, some handling methods require different arguments for reads and writes +// (Complex, for example). +template class ReadHandlingMethod; +template class WriteHandlingMethod; + +// Constant: use when the value read on this MMIO is always the same. This is +// only for reads. +template ReadHandlingMethod* Constant(T value); + +// Nop: use for writes that shouldn't have any effect and shouldn't log an +// error either. +template WriteHandlingMethod* Nop(); + +// Direct: use when all the MMIO does is read/write the given value to/from a +// global variable, with an optional mask applied on the read/written value. +template ReadHandlingMethod* DirectRead(const T* addr, u32 mask = 0xFFFFFFFF); +template ReadHandlingMethod* DirectRead(volatile const T* addr, u32 mask = 0xFFFFFFFF); +template WriteHandlingMethod* DirectWrite(T* addr, u32 mask = 0xFFFFFFFF); +template WriteHandlingMethod* DirectWrite(volatile T* addr, u32 mask = 0xFFFFFFFF); + +// Complex: use when no other handling method fits your needs. These allow you +// to directly provide a function that will be called when a read/write needs +// to be done. +template ReadHandlingMethod* ComplexRead(std::function); +template WriteHandlingMethod* ComplexWrite(std::function); + +// Invalid: log an error and return -1 in case of a read. These are the default +// handlers set for all MMIO types. +template ReadHandlingMethod* InvalidRead(); +template WriteHandlingMethod* InvalidWrite(); + +// Use these visitors interfaces if you need to write code that performs +// different actions based on the handling method used by a handler. Write your +// visitor implementing that interface, then use handler->VisitHandlingMethod +// to run the proper function. +template +class ReadHandlingMethodVisitor +{ +public: + virtual void VisitConstant(T value) = 0; + virtual void VisitDirect(const T* addr, u32 mask) = 0; + virtual void VisitComplex(std::function lambda) = 0; +}; +template +class WriteHandlingMethodVisitor +{ +public: + virtual void VisitNop() = 0; + virtual void VisitDirect(T* addr, u32 mask) = 0; + virtual void VisitComplex(std::function lambda) = 0; +}; + +// These classes are INTERNAL. Do not use outside of the MMIO implementation +// code. Unfortunately, because we want to make Read() and Write() fast and +// inlinable, we need to provide some of the implementation of these two +// classes here and can't just use a forward declaration. +template +class ReadHandler : public NonCopyable +{ +public: + ReadHandler(); + + // Takes ownership of "method". + ReadHandler(ReadHandlingMethod* method); + + ~ReadHandler(); + + // Entry point for read handling method visitors. + void Visit(ReadHandlingMethodVisitor& visitor) const; + + T Read(u32 addr) const + { + return m_ReadFunc(addr); + } + + // Internal method called when changing the internal method object. Its + // main role is to make sure the read function is updated at the same time. + void ResetMethod(ReadHandlingMethod* method); + +private: + std::unique_ptr> m_Method; + std::function m_ReadFunc; +}; +template +class WriteHandler : public NonCopyable +{ +public: + WriteHandler(); + + // Takes ownership of "method". + WriteHandler(WriteHandlingMethod* method); + + ~WriteHandler(); + + // Entry point for write handling method visitors. + void Visit(WriteHandlingMethodVisitor& visitor) const; + + void Write(u32 addr, T val) const + { + m_WriteFunc(addr, val); + } + + // Internal method called when changing the internal method object. Its + // main role is to make sure the write function is updated at the same + // time. + void ResetMethod(WriteHandlingMethod* method); + +private: + std::unique_ptr> m_Method; + std::function m_WriteFunc; +}; + +// Boilerplate boilerplate boilerplate. +// +// This is used to be able to avoid putting the templates implementation in the +// header files and slow down compilation times. Instead, we declare 3 +// specializations in the header file as already implemented in another +// compilation unit: u8, u16, u32. +// +// The "MaybeExtern" is there because that same macro is used for declaration +// (where MaybeExtern = "extern") and definition (MaybeExtern = ""). +#define MMIO_PUBLIC_SPECIALIZATIONS(MaybeExtern, T) \ + MaybeExtern template ReadHandlingMethod* Constant(T value); \ + MaybeExtern template WriteHandlingMethod* Nop(); \ + MaybeExtern template ReadHandlingMethod* DirectRead(const T* addr, u32 mask); \ + MaybeExtern template ReadHandlingMethod* DirectRead(volatile const T* addr, u32 mask); \ + MaybeExtern template WriteHandlingMethod* DirectWrite(T* addr, u32 mask); \ + MaybeExtern template WriteHandlingMethod* DirectWrite(volatile T* addr, u32 mask); \ + MaybeExtern template ReadHandlingMethod* ComplexRead(std::function); \ + MaybeExtern template WriteHandlingMethod* ComplexWrite(std::function); \ + MaybeExtern template ReadHandlingMethod* InvalidRead(); \ + MaybeExtern template WriteHandlingMethod* InvalidWrite(); \ + MaybeExtern template class ReadHandler; \ + MaybeExtern template class WriteHandler + +MMIO_PUBLIC_SPECIALIZATIONS(extern, u8); +MMIO_PUBLIC_SPECIALIZATIONS(extern, u16); +MMIO_PUBLIC_SPECIALIZATIONS(extern, u32); + +} + +#endif diff --git a/Source/Core/Core/HW/Memmap.cpp b/Source/Core/Core/HW/Memmap.cpp index 82f3141..64aebc5 100644 --- a/Source/Core/Core/HW/Memmap.cpp +++ b/Source/Core/Core/HW/Memmap.cpp @@ -34,6 +34,7 @@ #include "../ConfigManager.h" #include "../Debugger/Debugger_SymbolMap.h" #include "VideoBackendBase.h" +#include "MMIO.h" namespace Memory { @@ -83,6 +84,9 @@ u8 *m_pVirtualUncachedEXRAM; // wii only u8 *m_pVirtualL1Cache; u8 *m_pVirtualFakeVMEM; +// MMIO mapping object. +MMIO::Mapping* mmio_mapping; + // ================================= // Read and write shortcuts // ---------------- @@ -304,6 +308,31 @@ void InitHWMemFuncsWii() hwWriteWii32[AUDIO_START] = AudioInterface::Write32; } +void InitMMIO(MMIO::Mapping* mmio) +{ + 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); +} + +void InitMMIOWii(MMIO::Mapping* mmio) +{ + InitMMIO(mmio); + + WII_IPCInterface::RegisterMMIO(mmio, 0xCD000000); + DVDInterface::RegisterMMIO(mmio, 0xCD006000); + SerialInterface::RegisterMMIO(mmio, 0xCD006400); + ExpansionInterface::RegisterMMIO(mmio, 0xCD006800); + AudioInterface::RegisterMMIO(mmio, 0xCD006C00); +} + writeFn32 GetHWWriteFun32(const u32 _Address) { return hwWrite32[(_Address >> HWSHIFT) & (NUMHWMEMFUN-1)]; @@ -348,11 +377,18 @@ void Init() if (bFakeVMEM) flags |= MV_FAKE_VMEM; base = MemoryMap_Setup(views, num_views, flags, &g_arena); + mmio_mapping = new MMIO::Mapping(); + if (wii) InitHWMemFuncsWii(); else InitHWMemFuncs(); + if (wii) + InitMMIOWii(mmio_mapping); + else + InitMMIO(mmio_mapping); + INFO_LOG(MEMMAP, "Memory system initialized. RAM at %p (mirrors at 0 @ %p, 0x80000000 @ %p , 0xC0000000 @ %p)", m_pRAM, m_pPhysicalRAM, m_pVirtualCachedRAM, m_pVirtualUncachedRAM); m_IsInitialized = true; @@ -382,6 +418,7 @@ void Shutdown() MemoryMap_Shutdown(views, num_views, flags, &g_arena); g_arena.ReleaseSpace(); base = NULL; + delete mmio_mapping; INFO_LOG(MEMMAP, "Memory system shut down."); } diff --git a/Source/Core/Core/HW/Memmap.h b/Source/Core/Core/HW/Memmap.h index 819829c..7451045 100644 --- a/Source/Core/Core/HW/Memmap.h +++ b/Source/Core/Core/HW/Memmap.h @@ -29,6 +29,7 @@ // Global declarations class PointerWrap; +namespace MMIO { class Mapping; } typedef void (*writeFn8 )(const u8, const u32); typedef void (*writeFn16)(const u16,const u32); @@ -84,6 +85,9 @@ enum #endif }; +// MMIO mapping object. +extern MMIO::Mapping* mmio_mapping; + // Init and Shutdown bool IsInitialized(); void Init(); diff --git a/Source/Core/Core/HW/MemmapFunctions.cpp b/Source/Core/Core/HW/MemmapFunctions.cpp index 40bc5f4..eb33522 100644 --- a/Source/Core/Core/HW/MemmapFunctions.cpp +++ b/Source/Core/Core/HW/MemmapFunctions.cpp @@ -24,6 +24,7 @@ #include "../Core.h" #include "../PowerPC/PowerPC.h" #include "VideoBackendBase.h" +#include "MMIO.h" #ifdef USE_GDBSTUB #include "../PowerPC/GDBStub.h" diff --git a/Source/Core/Core/HW/MemoryInterface.cpp b/Source/Core/Core/HW/MemoryInterface.cpp index 80f4516..a803ea8 100644 --- a/Source/Core/Core/HW/MemoryInterface.cpp +++ b/Source/Core/Core/HW/MemoryInterface.cpp @@ -7,6 +7,7 @@ #include "../PowerPC/PowerPC.h" #include "MemoryInterface.h" +#include "MMIO.h" namespace MemoryInterface { @@ -14,68 +15,220 @@ namespace MemoryInterface // internal hardware addresses enum { - MEM_CHANNEL0_HI = 0x000, - MEM_CHANNEL0_LO = 0x002, - MEM_CHANNEL1_HI = 0x004, - MEM_CHANNEL1_LO = 0x006, - MEM_CHANNEL2_HI = 0x008, - MEM_CHANNEL2_LO = 0x00A, - MEM_CHANNEL3_HI = 0x00C, - MEM_CHANNEL3_LO = 0x00E, - MEM_CHANNEL_CTRL = 0x010 + MI_REGION0_FIRST = 0x000, + MI_REGION0_LAST = 0x002, + MI_REGION1_FIRST = 0x004, + MI_REGION1_LAST = 0x006, + MI_REGION2_FIRST = 0x008, + MI_REGION2_LAST = 0x00A, + MI_REGION3_FIRST = 0x00C, + MI_REGION3_LAST = 0x00E, + MI_PROT_TYPE = 0x010, + MI_IRQMASK = 0x01C, + MI_IRQFLAG = 0x01E, + MI_UNKNOWN1 = 0x020, + MI_PROT_ADDR_LO = 0x022, + MI_PROT_ADDR_HI = 0x024, + MI_TIMER0_HI = 0x032, + MI_TIMER0_LO = 0x034, + MI_TIMER1_HI = 0x036, + MI_TIMER1_LO = 0x038, + MI_TIMER2_HI = 0x03A, + MI_TIMER2_LO = 0x03C, + MI_TIMER3_HI = 0x03E, + MI_TIMER3_LO = 0x040, + MI_TIMER4_HI = 0x042, + MI_TIMER4_LO = 0x044, + MI_TIMER5_HI = 0x046, + MI_TIMER5_LO = 0x048, + MI_TIMER6_HI = 0x04A, + MI_TIMER6_LO = 0x04C, + MI_TIMER7_HI = 0x04E, + MI_TIMER7_LO = 0x050, + MI_TIMER8_HI = 0x052, + MI_TIMER8_LO = 0x054, + MI_TIMER9_HI = 0x056, + MI_TIMER9_LO = 0x058, + MI_UNKNOWN2 = 0x05A, +}; + +union MIRegion +{ + u32 hex; + struct { u16 first_page; u16 last_page; }; +}; + +union MIProtType +{ + u16 hex; + struct + { + u16 reg0 : 2; + u16 reg1 : 2; + u16 reg2 : 2; + u16 reg3 : 2; + u16 : 8; + }; +}; + +union MIIRQMask +{ + u16 hex; + struct + { + u16 reg0 : 1; + u16 reg1 : 1; + u16 reg2 : 1; + u16 reg3 : 1; + u16 all_regs : 1; + u16 : 11; + }; +}; + +union MIIRQFlag +{ + u16 hex; + struct + { + u16 reg0 : 1; + u16 reg1 : 1; + u16 reg2 : 1; + u16 reg3 : 1; + u16 all_regs : 1; + u16 : 11; + }; +}; + +union MIProtAddr +{ + u32 hex; + struct { u16 lo; u16 hi; }; + struct + { + u32 : 5; + u32 addr : 25; + u32 : 2; + }; +}; + +union MITimer +{ + u32 hex; + struct { u16 lo; u16 hi; }; }; struct MIMemStruct { - u32 Channel0_Addr; - u32 Channel1_Addr; - u32 Channel2_Addr; - u32 Channel3_Addr; - u32 Channel_Ctrl; + MIRegion regions[4]; + MIProtType prot_type; + MIIRQMask irq_mask; + MIIRQFlag irq_flag; + u16 unknown1; + MIProtAddr prot_addr; + MITimer timers[10]; + u16 unknown2; }; // STATE_TO_SAVE -static MIMemStruct miMem; +static MIMemStruct g_mi_mem; void DoState(PointerWrap &p) { - p.Do(miMem); + p.Do(g_mi_mem); +} + +void RegisterMMIO(MMIO::Mapping* mmio, u32 base) +{ + for (int i = 0; i < MI_REGION0_FIRST; i += 4) + { + auto& region = g_mi_mem.regions[i / 4]; + mmio->Register(base | i, + MMIO::DirectRead(®ion.first_page), + MMIO::DirectWrite(®ion.first_page) + ); + mmio->Register(base | (i + 2), + MMIO::DirectRead(®ion.last_page), + MMIO::DirectWrite(®ion.last_page) + ); + } + + mmio->Register(base | MI_PROT_TYPE, + MMIO::DirectRead(&g_mi_mem.prot_type.hex), + MMIO::DirectWrite(&g_mi_mem.prot_type.hex) + ); + + mmio->Register(base | MI_IRQMASK, + MMIO::DirectRead(&g_mi_mem.irq_mask.hex), + MMIO::DirectWrite(&g_mi_mem.irq_mask.hex) + ); + + mmio->Register(base | MI_IRQFLAG, + MMIO::DirectRead(&g_mi_mem.irq_flag.hex), + MMIO::DirectWrite(&g_mi_mem.irq_flag.hex) + ); + + mmio->Register(base | MI_UNKNOWN1, + MMIO::DirectRead(&g_mi_mem.unknown1), + MMIO::DirectWrite(&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(&g_mi_mem.prot_addr.hi), + MMIO::DirectWrite(&g_mi_mem.prot_addr.hi) + ); + mmio->Register(base | MI_PROT_ADDR_HI, + MMIO::DirectRead(&g_mi_mem.prot_addr.lo), + MMIO::DirectWrite(&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(&timer.hi), + MMIO::DirectWrite(&timer.hi) + ); + mmio->Register(base | (MI_TIMER0_LO + 4 * i), + MMIO::DirectRead(&timer.lo), + MMIO::DirectWrite(&timer.lo) + ); + } + + mmio->Register(base | MI_UNKNOWN2, + MMIO::DirectRead(&g_mi_mem.unknown2), + MMIO::DirectWrite(&g_mi_mem.unknown2) + ); + + // TODO: register mapping from 32 bit reads/writes to 16 bit. } void Read16(u16& _uReturnValue, const u32 _iAddress) { - //0x30 -> 0x5a : gp memory metrics - INFO_LOG(MEMMAP, "(r16) 0x%04x @ 0x%08x", 0, _iAddress); - _uReturnValue = 0; + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_iAddress, _uReturnValue); } -void Read32(u32& _uReturnValue, const u32 _iAddress) +void Write16(const u16 _iValue, const u32 _iAddress) { - INFO_LOG(MEMMAP, "(r32) 0x%08x @ 0x%08x", 0, _iAddress); - _uReturnValue = 0; + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_iAddress, _iValue); } -void Write32(const u32 _iValue, const u32 _iAddress) +void Read32(u32& _uReturnValue, const u32 _iAddress) { - INFO_LOG(MEMMAP, "(w32) 0x%08x @ 0x%08x", _iValue, _iAddress); + u16 hi, lo; + Read16(hi, _iAddress); + Read16(lo, _iAddress +2); + _uReturnValue = (hi << 16) | lo; } -//TODO : check -void Write16(const u16 _iValue, const u32 _iAddress) +void Write32(const u32 _iValue, const u32 _iAddress) { - INFO_LOG(MEMMAP, "(w16) 0x%04x @ 0x%08x", _iValue, _iAddress); - switch(_iAddress & 0xFFF) - { - case MEM_CHANNEL0_HI: miMem.Channel0_Addr = (miMem.Channel0_Addr & 0xFFFF) | (_iValue<<16); return; - case MEM_CHANNEL0_LO: miMem.Channel0_Addr = (miMem.Channel0_Addr & 0xFFFF0000) | (_iValue); return; - case MEM_CHANNEL1_HI: miMem.Channel1_Addr = (miMem.Channel1_Addr & 0xFFFF) | (_iValue<<16); return; - case MEM_CHANNEL1_LO: miMem.Channel1_Addr = (miMem.Channel1_Addr & 0xFFFF0000) | (_iValue); return; - case MEM_CHANNEL2_HI: miMem.Channel2_Addr = (miMem.Channel2_Addr & 0xFFFF) | (_iValue<<16); return; - case MEM_CHANNEL2_LO: miMem.Channel2_Addr = (miMem.Channel2_Addr & 0xFFFF0000) | (_iValue); return; - case MEM_CHANNEL3_HI: miMem.Channel3_Addr = (miMem.Channel3_Addr & 0xFFFF) | (_iValue<<16); return; - case MEM_CHANNEL3_LO: miMem.Channel3_Addr = (miMem.Channel3_Addr & 0xFFFF0000) | (_iValue); return; - case MEM_CHANNEL_CTRL: miMem.Channel_Ctrl = _iValue; return; - } + Write16(_iValue >> 16, _iAddress); + Write16(_iValue & 0xFFFF, _iAddress + 2); } } // end of namespace MemoryInterface diff --git a/Source/Core/Core/HW/MemoryInterface.h b/Source/Core/Core/HW/MemoryInterface.h index 2a4fa2a..6dc8c54 100644 --- a/Source/Core/Core/HW/MemoryInterface.h +++ b/Source/Core/Core/HW/MemoryInterface.h @@ -6,12 +6,16 @@ #define _MEMORYINTERFACE_H #include "Common.h" + +namespace MMIO { class Mapping; } class PointerWrap; namespace MemoryInterface { void DoState(PointerWrap &p); +void RegisterMMIO(MMIO::Mapping* mmio, u32 base); + void Read16(u16& _uReturnValue, const u32 _iAddress); void Read32(u32& _uReturnValue, const u32 _iAddress); void Write32(const u32 _iValue, const u32 _iAddress); diff --git a/Source/Core/Core/HW/ProcessorInterface.cpp b/Source/Core/Core/HW/ProcessorInterface.cpp index 68b0ce9..49adad2 100644 --- a/Source/Core/Core/HW/ProcessorInterface.cpp +++ b/Source/Core/Core/HW/ProcessorInterface.cpp @@ -14,6 +14,7 @@ #include "ProcessorInterface.h" #include "GPFifo.h" #include "VideoBackendBase.h" +#include "MMIO.h" namespace ProcessorInterface { @@ -90,6 +91,59 @@ void Init() toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback); } +void RegisterMMIO(MMIO::Mapping* mmio, u32 base) +{ + mmio->Register(base | PI_INTERRUPT_CAUSE, + MMIO::DirectRead(&m_InterruptCause), + MMIO::ComplexWrite([](u32, u32 val) { + Common::AtomicAnd(m_InterruptCause, ~val); + UpdateException(); + }) + ); + + mmio->Register(base | PI_INTERRUPT_MASK, + MMIO::DirectRead(&m_InterruptMask), + MMIO::ComplexWrite([](u32, u32 val) { + m_InterruptMask = val; + UpdateException(); + }) + ); + + mmio->Register(base | PI_FIFO_BASE, + MMIO::DirectRead(&Fifo_CPUBase), + MMIO::DirectWrite(&Fifo_CPUBase, 0xFFFFFFE0) + ); + + mmio->Register(base | PI_FIFO_END, + MMIO::DirectRead(&Fifo_CPUEnd), + MMIO::DirectWrite(&Fifo_CPUEnd, 0xFFFFFFE0) + ); + + mmio->Register(base | PI_FIFO_WPTR, + MMIO::DirectRead(&Fifo_CPUWritePointer), + MMIO::DirectWrite(&Fifo_CPUWritePointer, 0xFFFFFFE0) + ); + + mmio->Register(base | PI_FIFO_RESET, + MMIO::InvalidRead(), + MMIO::ComplexWrite([](u32, u32 val) { + WARN_LOG(PROCESSORINTERFACE, "Fifo reset (%08x)", val); + }) + ); + + mmio->Register(base | PI_RESET_CODE, + MMIO::DirectRead(&m_ResetCode), + MMIO::DirectWrite(&m_ResetCode) + ); + + mmio->Register(base | PI_FLIPPER_REV, + MMIO::DirectRead(&m_FlipperRev), + MMIO::InvalidWrite() + ); + + // TODO: register mapping from 16 bit reads to 32 bit + mask/shift. +} + void Read16(u16& _uReturnValue, const u32 _iAddress) { u32 word; @@ -99,105 +153,14 @@ void Read16(u16& _uReturnValue, const u32 _iAddress) void Read32(u32& _uReturnValue, const u32 _iAddress) { - //INFO_LOG(PROCESSORINTERFACE, "(r32) 0x%08x", _iAddress); - - switch(_iAddress & 0xFFF) - { - case PI_INTERRUPT_CAUSE: - _uReturnValue = m_InterruptCause; - return; - - case PI_INTERRUPT_MASK: - _uReturnValue = m_InterruptMask; - return; - - case PI_FIFO_BASE: - DEBUG_LOG(PROCESSORINTERFACE, "Read CPU FIFO base, value = %08x", Fifo_CPUBase); - _uReturnValue = Fifo_CPUBase; - return; - - case PI_FIFO_END: - DEBUG_LOG(PROCESSORINTERFACE, "Read CPU FIFO end, value = %08x", Fifo_CPUEnd); - _uReturnValue = Fifo_CPUEnd; - return; - - case PI_FIFO_WPTR: - DEBUG_LOG(PROCESSORINTERFACE, "Read writepointer, value = %08x", Fifo_CPUWritePointer); - _uReturnValue = Fifo_CPUWritePointer; //really writes in 32-byte chunks - // Monk's gcube does some crazy align trickery here. - return; - - case PI_RESET_CODE: - INFO_LOG(PROCESSORINTERFACE, "Read reset code, 0x%08x", m_ResetCode); - _uReturnValue = m_ResetCode; - return; - - case PI_FLIPPER_REV: - INFO_LOG(PROCESSORINTERFACE, "Read flipper rev, 0x%08x", m_FlipperRev); - _uReturnValue = m_FlipperRev; - return; - - default: - ERROR_LOG(PROCESSORINTERFACE, "!!!!Unknown write!!!! 0x%08x", _iAddress); - break; - } - - _uReturnValue = 0xAFFE0000; + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_iAddress, _uReturnValue); } void Write32(const u32 _uValue, const u32 _iAddress) { - //INFO_LOG(PROCESSORINTERFACE, "(w32) 0x%08x @ 0x%08x", _uValue, _iAddress); - switch(_iAddress & 0xFFF) - { - case PI_INTERRUPT_CAUSE: - Common::AtomicAnd(m_InterruptCause, ~_uValue); // writes turn them off - UpdateException(); - return; - - case PI_INTERRUPT_MASK: - m_InterruptMask = _uValue; - DEBUG_LOG(PROCESSORINTERFACE,"New Interrupt mask: %08x", m_InterruptMask); - UpdateException(); - return; - - case PI_FIFO_BASE: - Fifo_CPUBase = _uValue & 0xFFFFFFE0; - DEBUG_LOG(PROCESSORINTERFACE,"Fifo base = %08x", _uValue); - break; - - case PI_FIFO_END: - Fifo_CPUEnd = _uValue & 0xFFFFFFE0; - DEBUG_LOG(PROCESSORINTERFACE,"Fifo end = %08x", _uValue); - break; - - case PI_FIFO_WPTR: - Fifo_CPUWritePointer = _uValue & 0xFFFFFFE0; - DEBUG_LOG(PROCESSORINTERFACE,"Fifo writeptr = %08x", _uValue); - break; - - case PI_FIFO_RESET: - //Abort the actual frame - //g_video_backend->Video_AbortFrame(); - //Fifo_CPUWritePointer = Fifo_CPUBase; ?? - //PanicAlert("Unknown write to PI_FIFO_RESET (%08x)", _uValue); - WARN_LOG(PROCESSORINTERFACE, "Fifo reset (%08x)", _uValue); - break; - - case PI_RESET_CODE: - DEBUG_LOG(PROCESSORINTERFACE, "Write %08x to PI_RESET_CODE", _uValue); - m_ResetCode = _uValue; - break; - - case PI_FLIPPER_UNK: - DEBUG_LOG(PROCESSORINTERFACE, "Write %08x to unknown PI register %08x", _uValue, _iAddress); - break; - - default: - ERROR_LOG(PROCESSORINTERFACE,"!!!!Unknown PI write!!!! 0x%08x", _iAddress); - PanicAlert("Unknown write to PI: %08x", _iAddress); - break; - } + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_iAddress, _uValue); } void UpdateException() diff --git a/Source/Core/Core/HW/ProcessorInterface.h b/Source/Core/Core/HW/ProcessorInterface.h index 7feefa2..29a8750 100644 --- a/Source/Core/Core/HW/ProcessorInterface.h +++ b/Source/Core/Core/HW/ProcessorInterface.h @@ -8,6 +8,8 @@ #include "CommonTypes.h" class PointerWrap; +namespace MMIO { class Mapping; } + // Holds statuses of things like the write gatherer used for fifos, and interrupts from various sources namespace ProcessorInterface @@ -44,6 +46,8 @@ extern u32 Fifo_CPUWritePointer; void Init(); void DoState(PointerWrap &p); +void RegisterMMIO(MMIO::Mapping* mmio, u32 base); + void Read16(u16& _uReturnValue, const u32 _iAddress); void Read32(u32& _uReturnValue, const u32 _iAddress); diff --git a/Source/Core/Core/HW/SI.cpp b/Source/Core/Core/HW/SI.cpp index 39e9e21..8330172 100644 --- a/Source/Core/Core/HW/SI.cpp +++ b/Source/Core/Core/HW/SI.cpp @@ -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)) - { - _uReturnValue = *(u32*)&g_SIBuffer[_iAddress & 0x7F]; - return; - } - - // error if not changed in the switch - _uReturnValue = 0xdeadbeef; - - // registers - switch (_iAddress & 0x3FF) + // Register SI buffer direct accesses. + for (int i = 0; i < 0x80; i += 4) + mmio->Register(base | (0x80 + i), + MMIO::DirectRead((u32*)&g_SIBuffer[i]), + MMIO::DirectWrite((u32*)&g_SIBuffer[i]) + ); + + // In and out for the 4 SI channels. + for (int i = 0; i < 4; ++i) { - ////////////////////////////////////////////////////////////////////////// - // 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; + // 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(&g_Channel[i].m_Out.Hex), + MMIO::DirectWrite(&g_Channel[i].m_Out.Hex) + ); + mmio->Register(base | (SI_CHANNEL_0_IN_HI + 0xC * i), + MMIO::ComplexRead([i, rdst_bit](u32) { + g_StatusReg.Hex &= ~(1 << rdst_bit); + UpdateInterrupts(); + return g_Channel[i].m_InHi.Hex; + }), + MMIO::DirectWrite(&g_Channel[i].m_InHi.Hex) + ); + mmio->Register(base | (SI_CHANNEL_0_IN_LO + 0xC * i), + MMIO::ComplexRead([i, rdst_bit](u32) { + g_StatusReg.Hex &= ~(1 << rdst_bit); + UpdateInterrupts(); + return g_Channel[i].m_InLo.Hex; + }), + MMIO::DirectWrite(&g_Channel[i].m_InLo.Hex) + ); } - 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; - } + mmio->Register(base | SI_POLL, + MMIO::DirectRead(&g_Poll.Hex), + MMIO::DirectWrite(&g_Poll.Hex) + ); - // 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(&g_ComCSR.Hex), + MMIO::ComplexWrite([](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(&g_StatusReg.Hex), + MMIO::ComplexWrite([](u32, u32 val) { + USIStatusReg tmpStatus(val); // clear bits ( if(tmp.bit) SISR.bit=0 ) if (tmpStatus.NOREP0) g_StatusReg.NOREP0 = 0; @@ -489,21 +390,25 @@ 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; + mmio->Register(base | SI_EXI_CLOCK_COUNT, + MMIO::DirectRead(&g_EXIClockCount.Hex), + MMIO::DirectWrite(&g_EXIClockCount.Hex) + ); +} - case 0x80: // Bogus? never seen it with ma own eyes - INFO_LOG(SERIALINTERFACE, "WII something at 0xCD006480"); - break; +void Read32(u32& _uReturnValue, const u32 _iAddress) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_iAddress, _uReturnValue); +} - default: - _dbg_assert_(SERIALINTERFACE, 0); - break; - } +void Write32(const u32 _iValue, const u32 _iAddress) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_iAddress, _iValue); } void UpdateInterrupts() diff --git a/Source/Core/Core/HW/SI.h b/Source/Core/Core/HW/SI.h index 792f630..eea9814 100644 --- a/Source/Core/Core/HW/SI.h +++ b/Source/Core/Core/HW/SI.h @@ -9,6 +9,7 @@ #include "SI_Device.h" class PointerWrap; class ISIDevice; +namespace MMIO { class Mapping; } // SI number of channels enum @@ -23,6 +24,8 @@ void Init(); void Shutdown(); void DoState(PointerWrap &p); +void RegisterMMIO(MMIO::Mapping* mmio, u32 base); + void UpdateDevices(); void RemoveDevice(int _iDeviceNumber); diff --git a/Source/Core/Core/HW/VideoInterface.cpp b/Source/Core/Core/HW/VideoInterface.cpp index d01d19d..b9f5c43 100644 --- a/Source/Core/Core/HW/VideoInterface.cpp +++ b/Source/Core/Core/HW/VideoInterface.cpp @@ -13,6 +13,7 @@ #include "../CoreTiming.h" #include "SystemTimers.h" #include "StringUtil.h" +#include "MMIO.h" #include "VideoBackendBase.h" #include "State.h" @@ -182,6 +183,194 @@ void Init() UpdateParameters(); } +void RegisterMMIO(MMIO::Mapping* mmio, u32 base) +{ + struct { + u32 addr; + u16* ptr; + } directly_mapped_vars[] = { + { VI_VERTICAL_TIMING, &m_VerticalTimingRegister.Hex }, + { VI_HORIZONTAL_TIMING_0_HI, &m_HTiming0.Hi }, + { VI_HORIZONTAL_TIMING_0_LO, &m_HTiming0.Lo }, + { VI_HORIZONTAL_TIMING_1_HI, &m_HTiming1.Hi }, + { VI_HORIZONTAL_TIMING_1_LO, &m_HTiming1.Lo }, + { VI_VBLANK_TIMING_ODD_HI, &m_VBlankTimingOdd.Hi }, + { VI_VBLANK_TIMING_ODD_LO, &m_VBlankTimingOdd.Lo }, + { VI_VBLANK_TIMING_EVEN_HI, &m_VBlankTimingEven.Hi }, + { VI_VBLANK_TIMING_EVEN_LO, &m_VBlankTimingEven.Lo }, + { VI_BURST_BLANKING_ODD_HI, &m_BurstBlankingOdd.Hi }, + { VI_BURST_BLANKING_ODD_LO, &m_BurstBlankingOdd.Lo }, + { VI_BURST_BLANKING_EVEN_HI, &m_BurstBlankingEven.Hi }, + { VI_BURST_BLANKING_EVEN_LO, &m_BurstBlankingEven.Lo }, + { VI_FB_LEFT_TOP_LO, &m_XFBInfoTop.Lo }, + { VI_FB_RIGHT_TOP_LO, &m_3DFBInfoTop.Lo }, + { VI_FB_LEFT_BOTTOM_LO, &m_XFBInfoBottom.Lo }, + { VI_FB_RIGHT_BOTTOM_LO, &m_3DFBInfoBottom.Lo }, + { VI_PRERETRACE_LO, &m_InterruptRegister[0].Lo }, + { VI_POSTRETRACE_LO, &m_InterruptRegister[1].Lo }, + { VI_DISPLAY_INTERRUPT_2_LO, &m_InterruptRegister[2].Lo }, + { VI_DISPLAY_INTERRUPT_3_LO, &m_InterruptRegister[3].Lo }, + { VI_DISPLAY_LATCH_0_HI, &m_LatchRegister[0].Hi }, + { VI_DISPLAY_LATCH_0_LO, &m_LatchRegister[0].Lo }, + { VI_DISPLAY_LATCH_1_HI, &m_LatchRegister[1].Hi }, + { VI_DISPLAY_LATCH_1_LO, &m_LatchRegister[1].Lo }, + { VI_HSCALEW, &m_HorizontalStepping.Hex }, + { VI_HSCALER, &m_HorizontalScaling.Hex }, + { VI_FILTER_COEF_0_HI, &m_FilterCoefTables.Tables02[0].Hi }, + { VI_FILTER_COEF_0_LO, &m_FilterCoefTables.Tables02[0].Lo }, + { VI_FILTER_COEF_1_HI, &m_FilterCoefTables.Tables02[1].Hi }, + { VI_FILTER_COEF_1_LO, &m_FilterCoefTables.Tables02[1].Lo }, + { VI_FILTER_COEF_2_HI, &m_FilterCoefTables.Tables02[2].Hi }, + { VI_FILTER_COEF_2_LO, &m_FilterCoefTables.Tables02[2].Lo }, + { VI_FILTER_COEF_3_HI, &m_FilterCoefTables.Tables36[0].Hi }, + { VI_FILTER_COEF_3_LO, &m_FilterCoefTables.Tables36[0].Lo }, + { VI_FILTER_COEF_4_HI, &m_FilterCoefTables.Tables36[1].Hi }, + { VI_FILTER_COEF_4_LO, &m_FilterCoefTables.Tables36[1].Lo }, + { VI_FILTER_COEF_5_HI, &m_FilterCoefTables.Tables36[2].Hi }, + { VI_FILTER_COEF_5_LO, &m_FilterCoefTables.Tables36[2].Lo }, + { VI_FILTER_COEF_6_HI, &m_FilterCoefTables.Tables36[3].Hi }, + { VI_FILTER_COEF_6_LO, &m_FilterCoefTables.Tables36[3].Lo }, + { VI_CLOCK, &m_Clock }, + { VI_DTV_STATUS, &m_DTVStatus.Hex }, + { VI_FBWIDTH, &m_FBWidth }, + { VI_BORDER_BLANK_END, &m_BorderHBlank.Lo }, + { VI_BORDER_BLANK_START, &m_BorderHBlank.Hi }, + }; + + // Declare all the boilerplate direct MMIOs. + for (auto& mapped_var : directly_mapped_vars) + { + mmio->Register(base | mapped_var.addr, + MMIO::DirectRead(mapped_var.ptr), + MMIO::DirectWrite(mapped_var.ptr) + ); + } + + // XFB related MMIOs that require special handling on writes. + mmio->Register(base | VI_FB_LEFT_TOP_HI, + MMIO::DirectRead(&m_XFBInfoTop.Hi), + MMIO::ComplexWrite([](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(&m_XFBInfoBottom.Hi), + MMIO::ComplexWrite([](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(&m_3DFBInfoTop.Hi), + MMIO::ComplexWrite([](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(&m_3DFBInfoBottom.Hi), + MMIO::ComplexWrite([](u32, u16 val) { + m_3DFBInfoBottom.Hi = val; + if (m_3DFBInfoBottom.CLRPOFF) m_3DFBInfoBottom.POFF = 0; + }) + ); + + // MMIOs with unimplemented writes that trigger warnings. + mmio->Register(base | VI_VERTICAL_BEAM_POSITION, + MMIO::DirectRead(&m_VBeamPos), + MMIO::ComplexWrite([](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(&m_HBeamPos), + MMIO::ComplexWrite([](u32, u16 val) { + WARN_LOG(VIDEOINTERFACE, "Changing horizontal beam position to 0x%04x - not documented or implemented yet", val); + }) + ); + + // The following MMIOs are interrupts related and update interrupt status + // on writes. + mmio->Register(base | VI_PRERETRACE_HI, + MMIO::DirectRead(&m_InterruptRegister[0].Hi), + MMIO::ComplexWrite([](u32, u16 val) { + m_InterruptRegister[0].Hi = val; + UpdateInterrupts(); + }) + ); + mmio->Register(base | VI_POSTRETRACE_HI, + MMIO::DirectRead(&m_InterruptRegister[1].Hi), + MMIO::ComplexWrite([](u32, u16 val) { + m_InterruptRegister[1].Hi = val; + UpdateInterrupts(); + }) + ); + mmio->Register(base | VI_DISPLAY_INTERRUPT_2_HI, + MMIO::DirectRead(&m_InterruptRegister[2].Hi), + MMIO::ComplexWrite([](u32, u16 val) { + m_InterruptRegister[2].Hi = val; + UpdateInterrupts(); + }) + ); + mmio->Register(base | VI_DISPLAY_INTERRUPT_3_HI, + MMIO::DirectRead(&m_InterruptRegister[3].Hi), + MMIO::ComplexWrite([](u32, u16 val) { + m_InterruptRegister[3].Hi = val; + UpdateInterrupts(); + }) + ); + + // Unknown anti-aliasing related MMIO register: puts a warning on log and + // needs to shift/mask when reading/writing. + mmio->Register(base | VI_UNK_AA_REG_HI, + MMIO::ComplexRead([](u32) { + return m_UnkAARegister >> 16; + }), + MMIO::ComplexWrite([](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([](u32) { + return m_UnkAARegister & 0xFFFF; + }), + MMIO::ComplexWrite([](u32, u16 val) { + m_UnkAARegister = (m_UnkAARegister & 0xFFFF0000) | val; + WARN_LOG(VIDEOINTERFACE, "Writing to the unknown AA register (lo)"); + }) + ); + + // Control register writes only updates some select bits, and additional + // processing needs to be done if a reset is requested. + mmio->Register(base | VI_CONTROL_REGISTER, + MMIO::DirectRead(&m_DisplayControlRegister.Hex), + MMIO::ComplexWrite([](u32, u16 val) { + UVIDisplayControlRegister tmpConfig(val); + m_DisplayControlRegister.ENB = tmpConfig.ENB; + m_DisplayControlRegister.NIN = tmpConfig.NIN; + m_DisplayControlRegister.DLR = tmpConfig.DLR; + m_DisplayControlRegister.LE0 = tmpConfig.LE0; + m_DisplayControlRegister.LE1 = tmpConfig.LE1; + m_DisplayControlRegister.FMT = tmpConfig.FMT; + + if (tmpConfig.RST) + { + // shuffle2 clear all data, reset to default vals, and enter idle mode + m_DisplayControlRegister.RST = 0; + for (int i = 0; i < 4; i++) + m_InterruptRegister[i].Hex = 0; + UpdateInterrupts(); + } + + UpdateParameters(); + }) + ); + + // TODO: register mapping from 8/32 bit reads/writes to 16 bit. +} + void SetRegionReg(char region) { if (!Core::g_CoreStartupParameter.bForceNTSCJ) @@ -210,472 +399,14 @@ void Read8(u8& _uReturnValue, const u32 _iAddress) void Read16(u16& _uReturnValue, const u32 _iAddress) { - switch (_iAddress & 0xFFF) - { - case VI_VERTICAL_TIMING: - _uReturnValue = m_VerticalTimingRegister.Hex; - return; - - case VI_CONTROL_REGISTER: - _uReturnValue = m_DisplayControlRegister.Hex; - return; - - case VI_HORIZONTAL_TIMING_0_HI: - _uReturnValue = m_HTiming0.Hi; - break; - case VI_HORIZONTAL_TIMING_0_LO: - _uReturnValue = m_HTiming0.Lo; - break; - - case VI_HORIZONTAL_TIMING_1_HI: - _uReturnValue = m_HTiming1.Hi; - break; - case VI_HORIZONTAL_TIMING_1_LO: - _uReturnValue = m_HTiming1.Lo; - break; - - case VI_VBLANK_TIMING_ODD_HI: - _uReturnValue = m_VBlankTimingOdd.Hi; - break; - case VI_VBLANK_TIMING_ODD_LO: - _uReturnValue = m_VBlankTimingOdd.Lo; - break; - - case VI_VBLANK_TIMING_EVEN_HI: - _uReturnValue = m_VBlankTimingEven.Hi; - break; - case VI_VBLANK_TIMING_EVEN_LO: - _uReturnValue = m_VBlankTimingEven.Lo; - break; - - case VI_BURST_BLANKING_ODD_HI: - _uReturnValue = m_BurstBlankingOdd.Hi; - break; - case VI_BURST_BLANKING_ODD_LO: - _uReturnValue = m_BurstBlankingOdd.Lo; - break; - - case VI_BURST_BLANKING_EVEN_HI: - _uReturnValue = m_BurstBlankingEven.Hi; - break; - case VI_BURST_BLANKING_EVEN_LO: - _uReturnValue = m_BurstBlankingEven.Lo; - break; - - case VI_FB_LEFT_TOP_HI: - _uReturnValue = m_XFBInfoTop.Hi; - break; - case VI_FB_LEFT_TOP_LO: - _uReturnValue = m_XFBInfoTop.Lo; - break; - - case VI_FB_RIGHT_TOP_HI: - _uReturnValue = m_3DFBInfoTop.Hi; - break; - case VI_FB_RIGHT_TOP_LO: - _uReturnValue = m_3DFBInfoTop.Lo; - break; - - case VI_FB_LEFT_BOTTOM_HI: - _uReturnValue = m_XFBInfoBottom.Hi; - break; - case VI_FB_LEFT_BOTTOM_LO: - _uReturnValue = m_XFBInfoBottom.Lo; - break; - - case VI_FB_RIGHT_BOTTOM_HI: - _uReturnValue = m_3DFBInfoBottom.Hi; - break; - case VI_FB_RIGHT_BOTTOM_LO: - _uReturnValue = m_3DFBInfoBottom.Lo; - break; - - case VI_VERTICAL_BEAM_POSITION: - _uReturnValue = m_VBeamPos; - return; - - case VI_HORIZONTAL_BEAM_POSITION: - _uReturnValue = m_HBeamPos; - return; - - // RETRACE STUFF ... - case VI_PRERETRACE_HI: - _uReturnValue = m_InterruptRegister[0].Hi; - return; - case VI_PRERETRACE_LO: - _uReturnValue = m_InterruptRegister[0].Lo; - return; - - case VI_POSTRETRACE_HI: - _uReturnValue = m_InterruptRegister[1].Hi; - return; - case VI_POSTRETRACE_LO: - _uReturnValue = m_InterruptRegister[1].Lo; - return; - - case VI_DISPLAY_INTERRUPT_2_HI: - _uReturnValue = m_InterruptRegister[2].Hi; - return; - case VI_DISPLAY_INTERRUPT_2_LO: - _uReturnValue = m_InterruptRegister[2].Lo; - return; - - case VI_DISPLAY_INTERRUPT_3_HI: - _uReturnValue = m_InterruptRegister[3].Hi; - return; - case VI_DISPLAY_INTERRUPT_3_LO: - _uReturnValue = m_InterruptRegister[3].Lo; - return; - - case VI_DISPLAY_LATCH_0_HI: - _uReturnValue = m_LatchRegister[0].Hi; - break; - case VI_DISPLAY_LATCH_0_LO: - _uReturnValue = m_LatchRegister[0].Lo; - break; - - case VI_DISPLAY_LATCH_1_HI: - _uReturnValue = m_LatchRegister[1].Hi; - break; - case VI_DISPLAY_LATCH_1_LO: - _uReturnValue = m_LatchRegister[1].Lo; - break; - - case VI_HSCALEW: - _uReturnValue = m_HorizontalStepping.Hex; - break; - - case VI_HSCALER: - _uReturnValue = m_HorizontalScaling.Hex; - break; - - case VI_FILTER_COEF_0_HI: - _uReturnValue = m_FilterCoefTables.Tables02[0].Hi; - break; - case VI_FILTER_COEF_0_LO: - _uReturnValue = m_FilterCoefTables.Tables02[0].Lo; - break; - case VI_FILTER_COEF_1_HI: - _uReturnValue = m_FilterCoefTables.Tables02[1].Hi; - break; - case VI_FILTER_COEF_1_LO: - _uReturnValue = m_FilterCoefTables.Tables02[1].Lo; - break; - case VI_FILTER_COEF_2_HI: - _uReturnValue = m_FilterCoefTables.Tables02[2].Hi; - break; - case VI_FILTER_COEF_2_LO: - _uReturnValue = m_FilterCoefTables.Tables02[2].Lo; - break; - case VI_FILTER_COEF_3_HI: - _uReturnValue = m_FilterCoefTables.Tables36[0].Hi; - break; - case VI_FILTER_COEF_3_LO: - _uReturnValue = m_FilterCoefTables.Tables36[0].Lo; - break; - case VI_FILTER_COEF_4_HI: - _uReturnValue = m_FilterCoefTables.Tables36[1].Hi; - break; - case VI_FILTER_COEF_4_LO: - _uReturnValue = m_FilterCoefTables.Tables36[1].Lo; - break; - case VI_FILTER_COEF_5_HI: - _uReturnValue = m_FilterCoefTables.Tables36[2].Hi; - break; - case VI_FILTER_COEF_5_LO: - _uReturnValue = m_FilterCoefTables.Tables36[2].Lo; - break; - case VI_FILTER_COEF_6_HI: - _uReturnValue = m_FilterCoefTables.Tables36[3].Hi; - break; - case VI_FILTER_COEF_6_LO: - _uReturnValue = m_FilterCoefTables.Tables36[3].Lo; - break; - - case VI_UNK_AA_REG_HI: - _uReturnValue = (m_UnkAARegister & 0xffff0000) >> 16; - WARN_LOG(VIDEOINTERFACE, "(r16) unknown AA register, not sure what it does :)"); - break; - case VI_UNK_AA_REG_LO: - _uReturnValue = m_UnkAARegister & 0x0000ffff; - WARN_LOG(VIDEOINTERFACE, "(r16) unknown AA register, not sure what it does :)"); - break; - - case VI_CLOCK: - _uReturnValue = m_Clock; - break; - - case VI_DTV_STATUS: - _uReturnValue = m_DTVStatus.Hex; - break; - - case VI_FBWIDTH: - _uReturnValue = m_FBWidth; - break; - - case VI_BORDER_BLANK_END: - _uReturnValue = m_BorderHBlank.Lo; - break; - case VI_BORDER_BLANK_START: - _uReturnValue = m_BorderHBlank.Hi; - break; - - default: - ERROR_LOG(VIDEOINTERFACE, "(r16) unknown reg %x", _iAddress & 0xfff); - _uReturnValue = 0x0; - break; - } - - DEBUG_LOG(VIDEOINTERFACE, "(r16): 0x%04x, 0x%08x", _uReturnValue, _iAddress); + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_iAddress, _uReturnValue); } void Write16(const u16 _iValue, const u32 _iAddress) { - DEBUG_LOG(VIDEOINTERFACE, "(w16): 0x%04x, 0x%08x",_iValue,_iAddress); - - //Somewhere it sets screen width.. we need to communicate this to the gfx backend... - - switch (_iAddress & 0xFFF) - { - case VI_VERTICAL_TIMING: - m_VerticalTimingRegister.Hex = _iValue; - break; - - case VI_CONTROL_REGISTER: - { - UVIDisplayControlRegister tmpConfig(_iValue); - m_DisplayControlRegister.ENB = tmpConfig.ENB; - m_DisplayControlRegister.NIN = tmpConfig.NIN; - m_DisplayControlRegister.DLR = tmpConfig.DLR; - m_DisplayControlRegister.LE0 = tmpConfig.LE0; - m_DisplayControlRegister.LE1 = tmpConfig.LE1; - m_DisplayControlRegister.FMT = tmpConfig.FMT; - - if (tmpConfig.RST) - { - // shuffle2 clear all data, reset to default vals, and enter idle mode - m_DisplayControlRegister.RST = 0; - for (int i = 0; i < 4; i++) - m_InterruptRegister[i].Hex = 0; - UpdateInterrupts(); - } - - UpdateParameters(); - } - break; - - case VI_HORIZONTAL_TIMING_0_HI: - m_HTiming0.Hi = _iValue; - break; - case VI_HORIZONTAL_TIMING_0_LO: - m_HTiming0.Lo = _iValue; - break; - - case VI_HORIZONTAL_TIMING_1_HI: - m_HTiming1.Hi = _iValue; - break; - case VI_HORIZONTAL_TIMING_1_LO: - m_HTiming1.Lo = _iValue; - break; - - case VI_VBLANK_TIMING_ODD_HI: - m_VBlankTimingOdd.Hi = _iValue; - break; - case VI_VBLANK_TIMING_ODD_LO: - m_VBlankTimingOdd.Lo = _iValue; - break; - - case VI_VBLANK_TIMING_EVEN_HI: - m_VBlankTimingEven.Hi = _iValue; - break; - case VI_VBLANK_TIMING_EVEN_LO: - m_VBlankTimingEven.Lo = _iValue; - break; - - case VI_BURST_BLANKING_ODD_HI: - m_BurstBlankingOdd.Hi = _iValue; - break; - case VI_BURST_BLANKING_ODD_LO: - m_BurstBlankingOdd.Lo = _iValue; - break; - - case VI_BURST_BLANKING_EVEN_HI: - m_BurstBlankingEven.Hi = _iValue; - break; - case VI_BURST_BLANKING_EVEN_LO: - m_BurstBlankingEven.Lo = _iValue; - break; - - case VI_FB_LEFT_TOP_HI: - m_XFBInfoTop.Hi = _iValue; - if (m_XFBInfoTop.CLRPOFF) m_XFBInfoTop.POFF = 0; - break; - case VI_FB_LEFT_TOP_LO: - m_XFBInfoTop.Lo = _iValue; - break; - - case VI_FB_RIGHT_TOP_HI: - m_3DFBInfoTop.Hi = _iValue; - if (m_3DFBInfoTop.CLRPOFF) m_3DFBInfoTop.POFF = 0; - break; - case VI_FB_RIGHT_TOP_LO: - m_3DFBInfoTop.Lo = _iValue; - break; - - case VI_FB_LEFT_BOTTOM_HI: - m_XFBInfoBottom.Hi = _iValue; - if (m_XFBInfoBottom.CLRPOFF) m_XFBInfoBottom.POFF = 0; - break; - case VI_FB_LEFT_BOTTOM_LO: - m_XFBInfoBottom.Lo = _iValue; - break; - - case VI_FB_RIGHT_BOTTOM_HI: - m_3DFBInfoBottom.Hi = _iValue; - if (m_3DFBInfoBottom.CLRPOFF) m_3DFBInfoBottom.POFF = 0; - break; - case VI_FB_RIGHT_BOTTOM_LO: - m_3DFBInfoBottom.Lo = _iValue; - break; - - case VI_VERTICAL_BEAM_POSITION: - WARN_LOG(VIDEOINTERFACE, "Change Vertical Beam Position to 0x%04x - Not documented or implemented", _iValue); - break; - - case VI_HORIZONTAL_BEAM_POSITION: - WARN_LOG(VIDEOINTERFACE, "Change Horizontal Beam Position to 0x%04x - Not documented or implemented", _iValue); - break; - - // RETRACE STUFF ... - case VI_PRERETRACE_HI: - m_InterruptRegister[0].Hi = _iValue; - UpdateInterrupts(); - break; - case VI_PRERETRACE_LO: - m_InterruptRegister[0].Lo = _iValue; - break; - - case VI_POSTRETRACE_HI: - m_InterruptRegister[1].Hi = _iValue; - UpdateInterrupts(); - break; - case VI_POSTRETRACE_LO: - m_InterruptRegister[1].Lo = _iValue; - break; - - case VI_DISPLAY_INTERRUPT_2_HI: - m_InterruptRegister[2].Hi = _iValue; - UpdateInterrupts(); - break; - case VI_DISPLAY_INTERRUPT_2_LO: - m_InterruptRegister[2].Lo = _iValue; - break; - - case VI_DISPLAY_INTERRUPT_3_HI: - m_InterruptRegister[3].Hi = _iValue; - UpdateInterrupts(); - break; - case VI_DISPLAY_INTERRUPT_3_LO: - m_InterruptRegister[3].Lo = _iValue; - break; - - case VI_DISPLAY_LATCH_0_HI: - m_LatchRegister[0].Hi = _iValue; - break; - case VI_DISPLAY_LATCH_0_LO: - m_LatchRegister[0].Lo = _iValue; - break; - - case VI_DISPLAY_LATCH_1_HI: - m_LatchRegister[1].Hi = _iValue; - break; - case VI_DISPLAY_LATCH_1_LO: - m_LatchRegister[1].Lo = _iValue; - break; - - case VI_HSCALEW: - m_HorizontalStepping.Hex = _iValue; - break; - - case VI_HSCALER: - m_HorizontalScaling.Hex = _iValue; - break; - - case VI_FILTER_COEF_0_HI: - m_FilterCoefTables.Tables02[0].Hi = _iValue; - break; - case VI_FILTER_COEF_0_LO: - m_FilterCoefTables.Tables02[0].Lo = _iValue; - break; - case VI_FILTER_COEF_1_HI: - m_FilterCoefTables.Tables02[1].Hi = _iValue; - break; - case VI_FILTER_COEF_1_LO: - m_FilterCoefTables.Tables02[1].Lo = _iValue; - break; - case VI_FILTER_COEF_2_HI: - m_FilterCoefTables.Tables02[2].Hi = _iValue; - break; - case VI_FILTER_COEF_2_LO: - m_FilterCoefTables.Tables02[2].Lo = _iValue; - break; - case VI_FILTER_COEF_3_HI: - m_FilterCoefTables.Tables36[0].Hi = _iValue; - break; - case VI_FILTER_COEF_3_LO: - m_FilterCoefTables.Tables36[0].Lo = _iValue; - break; - case VI_FILTER_COEF_4_HI: - m_FilterCoefTables.Tables36[1].Hi = _iValue; - break; - case VI_FILTER_COEF_4_LO: - m_FilterCoefTables.Tables36[1].Lo = _iValue; - break; - case VI_FILTER_COEF_5_HI: - m_FilterCoefTables.Tables36[2].Hi = _iValue; - break; - case VI_FILTER_COEF_5_LO: - m_FilterCoefTables.Tables36[2].Lo = _iValue; - break; - case VI_FILTER_COEF_6_HI: - m_FilterCoefTables.Tables36[3].Hi = _iValue; - break; - case VI_FILTER_COEF_6_LO: - m_FilterCoefTables.Tables36[3].Lo = _iValue; - break; - - case VI_UNK_AA_REG_HI: - m_UnkAARegister = (m_UnkAARegister & 0x0000ffff) | (u32)(_iValue << 16); - WARN_LOG(VIDEOINTERFACE, "(w16) to unknown AA register, not sure what it does :)"); - break; - case VI_UNK_AA_REG_LO: - m_UnkAARegister = (m_UnkAARegister & 0xffff0000) | _iValue; - WARN_LOG(VIDEOINTERFACE, "(w16) to unknown AA register, not sure what it does :)"); - break; - - case VI_CLOCK: - m_Clock = _iValue; - break; - - case VI_DTV_STATUS: - m_DTVStatus.Hex = _iValue; - break; - - case VI_FBWIDTH: - m_FBWidth = _iValue; - break; - - case VI_BORDER_BLANK_END: - m_BorderHBlank.Lo = _iValue; - break; - case VI_BORDER_BLANK_START: - m_BorderHBlank.Hi = _iValue; - break; - - default: - ERROR_LOG(VIDEOINTERFACE, "(w16) %04x to unknown register %x", _iValue, _iAddress & 0xfff); - break; - } + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_iAddress, _iValue); } void Read32(u32& _uReturnValue, const u32 _iAddress) diff --git a/Source/Core/Core/HW/VideoInterface.h b/Source/Core/Core/HW/VideoInterface.h index aaaedf5..c9216a3 100644 --- a/Source/Core/Core/HW/VideoInterface.h +++ b/Source/Core/Core/HW/VideoInterface.h @@ -6,7 +6,9 @@ #define _VIDEOINTERFACE_H #include "CommonTypes.h" + class PointerWrap; +namespace MMIO { class Mapping; } namespace VideoInterface { @@ -325,6 +327,8 @@ union UVIDTVStatus void SetRegionReg(char region); void DoState(PointerWrap &p); + void RegisterMMIO(MMIO::Mapping* mmio, u32 base); + void Read8(u8& _uReturnValue, const u32 _uAddress); void Read16(u16& _uReturnValue, const u32 _uAddress); void Read32(u32& _uReturnValue, const u32 _uAddress); diff --git a/Source/Core/Core/HW/WII_IPC.cpp b/Source/Core/Core/HW/WII_IPC.cpp index d4535d1..cf358b3 100644 --- a/Source/Core/Core/HW/WII_IPC.cpp +++ b/Source/Core/Core/HW/WII_IPC.cpp @@ -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" @@ -135,86 +136,67 @@ 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); - - // 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_PPCMSG, + MMIO::InvalidRead(), + MMIO::DirectWrite(&ppc_msg) + ); + + mmio->Register(base | IPC_PPCCTRL, + MMIO::ComplexRead([](u32) { + return ctrl.ppc(); + }), + MMIO::ComplexWrite([](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; - - 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; - - case PPC_IRQMASK: // __OSInterruptInit (0x40000000) - { - ppc_irq_masks = _Value; + WII_IPC_HLE_Interface::Update(); + CoreTiming::ScheduleEvent_Threadsafe(0, updateInterrupts, 0); + }) + ); + + mmio->Register(base | IPC_ARMMSG, + MMIO::DirectRead(&arm_msg), + MMIO::InvalidWrite() + ); + + mmio->Register(base | PPC_IRQFLAG, + MMIO::InvalidRead(), + MMIO::ComplexWrite([](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(), + MMIO::ComplexWrite([](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; - - case GPIOB_OUT: - sensorbar_power = _Value; - break; + WII_IPC_HLE_Interface::Update(); + CoreTiming::ScheduleEvent_Threadsafe(0, updateInterrupts, 0); + }) + ); + + mmio->Register(base | GPIOB_OUT, + MMIO::InvalidRead(), + MMIO::DirectWrite(&sensorbar_power) + ); +} - default: - _dbg_assert_msg_(WII_IPC, 0, "w32 %08x @ %08x", _Value, _Address); - break; - } +void Read32(u32& _rReturnValue, const u32 _Address) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_Address, _rReturnValue); +} - WII_IPC_HLE_Interface::Update(); - CoreTiming::ScheduleEvent_Threadsafe(0, updateInterrupts, 0); +void Write32(const u32 _Value, const u32 _Address) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_Address, _Value); } void UpdateInterrupts(u64 userdata, int cyclesLate) diff --git a/Source/Core/Core/HW/WII_IPC.h b/Source/Core/Core/HW/WII_IPC.h index 3e5ec1c..20b0a58 100644 --- a/Source/Core/Core/HW/WII_IPC.h +++ b/Source/Core/Core/HW/WII_IPC.h @@ -7,6 +7,7 @@ #include "Common.h" class PointerWrap; +namespace MMIO { class Mapping; } namespace WII_IPCInterface { @@ -37,6 +38,8 @@ void Reset(); void Shutdown(); void DoState(PointerWrap &p); +void RegisterMMIO(MMIO::Mapping* mmio, u32 base); + void Read32(u32& _rReturnValue, const u32 _Address); void Write32(const u32 _Value, const u32 _Address); diff --git a/Source/Core/VideoBackends/Software/SWCommandProcessor.cpp b/Source/Core/VideoBackends/Software/SWCommandProcessor.cpp index 06876a2..8319b1e 100644 --- a/Source/Core/VideoBackends/Software/SWCommandProcessor.cpp +++ b/Source/Core/VideoBackends/Software/SWCommandProcessor.cpp @@ -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,77 @@ void RunGpu() } } -void Read16(u16& _rReturnValue, const u32 _Address) -{ - 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) +void RegisterMMIO(MMIO::Mapping* mmio, u32 base) { - INFO_LOG(COMMANDPROCESSOR, "(write16): 0x%04x @ 0x%08x",_Value,_Address); + // Directly map reads and writes to the cpreg structure. + for (size_t i = 0; i < sizeof (cpreg) / sizeof (u16); ++i) + { + u16* ptr = ((u16*)&cpreg) + i; + mmio->Register(base | (i * 2), + MMIO::DirectRead(ptr), + MMIO::DirectWrite(ptr) + ); + } - switch (_Address & 0xFFF) + // Bleh. Apparently SWCommandProcessor does not know about regs 0x40 to + // 0x64... + for (size_t i = 0x40; i < 0x64; ++i) { - case STATUS_REGISTER: - { - ERROR_LOG(COMMANDPROCESSOR,"\t write to STATUS_REGISTER : %04x", _Value); - } - break; + mmio->Register(base | i, + MMIO::Constant(0), + MMIO::Nop() + ); + } - case CTRL_REGISTER: - { - cpreg.ctrl.Hex = _Value; - - 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 (auto& reg : fifo_addr_lo_regs) + { + mmio->RegisterWrite(base | reg, + MMIO::DirectWrite(((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([](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; - } +void Read16(u16& _rReturnValue, const u32 _Address) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_Address, _rReturnValue); +} - RunGpu(); +void Write16(const u16 _Value, const u32 _Address) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_Address, _Value); } void Read32(u32& _rReturnValue, const u32 _Address) { - _rReturnValue = 0; - _dbg_assert_msg_(COMMANDPROCESSOR, 0, "Read32 from CommandProcessor at 0x%08x", _Address); + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_Address, _rReturnValue); } void Write32(const u32 _Data, const u32 _Address) { - _dbg_assert_msg_(COMMANDPROCESSOR, 0, "Write32 at CommandProcessor at 0x%08x", _Address); + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_Address, _Data); } void STACKALIGN GatherPipeBursted() diff --git a/Source/Core/VideoBackends/Software/SWCommandProcessor.h b/Source/Core/VideoBackends/Software/SWCommandProcessor.h index 51cde8d..30ca4c8 100644 --- a/Source/Core/VideoBackends/Software/SWCommandProcessor.h +++ b/Source/Core/VideoBackends/Software/SWCommandProcessor.h @@ -8,6 +8,7 @@ #include "Common.h" class PointerWrap; +namespace MMIO { class Mapping; } extern volatile bool g_bSkipCurrentFrame; extern u8* g_pVideoData; @@ -98,14 +99,15 @@ namespace SWCommandProcessor UCPStatusReg status; // 0x00 UCPCtrlReg ctrl; // 0x02 UCPClearReg clear; // 0x04 - u32 unk0; // 0x06 - u32 unk1; // 0x0a + u32 unk0; // 0x08 + u16 unk1; // 0x0c u16 token; // 0x0e u16 bboxleft; // 0x10 u16 bboxtop; // 0x12 u16 bboxright; // 0x14 u16 bboxbottom; // 0x16 - u16 unk2; // 0x18 + u32 unk2; // 0x18 + u32 unk3; // 0x1c u32 fifobase; // 0x20 u32 fifoend; // 0x24 u32 hiwatermark; // 0x28 @@ -123,6 +125,8 @@ namespace SWCommandProcessor void Shutdown(); void DoState(PointerWrap &p); + void RegisterMMIO(MMIO::Mapping* mmio, u32 base); + bool RunBuffer(); void RunGpu(); diff --git a/Source/Core/VideoBackends/Software/SWPixelEngine.cpp b/Source/Core/VideoBackends/Software/SWPixelEngine.cpp index a747a66..ae6ad59 100644 --- a/Source/Core/VideoBackends/Software/SWPixelEngine.cpp +++ b/Source/Core/VideoBackends/Software/SWPixelEngine.cpp @@ -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) -{ - 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) +void RegisterMMIO(MMIO::Mapping* mmio, u32 base) { - 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(ptr), + MMIO::DirectWrite(ptr) + ); + } + + // The control register has some more complex logic to perform on writes. + mmio->RegisterWrite(base | PE_CTRL_REGISTER, + MMIO::ComplexWrite([](u32, u16 val) { + UPECtrlReg tmpCtrl(val); if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = false; if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = false; @@ -93,15 +86,27 @@ 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; - } + }) + ); +} + +void Read16(u16& _uReturnValue, const u32 _iAddress) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_iAddress, _uReturnValue); +} + +void Write32(const u32 _iValue, const u32 _iAddress) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_iAddress, _iValue); +} + +void Write16(const u16 _iValue, const u32 _iAddress) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_iAddress, _iValue); } bool AllowIdleSkipping() diff --git a/Source/Core/VideoBackends/Software/SWPixelEngine.h b/Source/Core/VideoBackends/Software/SWPixelEngine.h index 01fb496..28d91d4 100644 --- a/Source/Core/VideoBackends/Software/SWPixelEngine.h +++ b/Source/Core/VideoBackends/Software/SWPixelEngine.h @@ -9,6 +9,7 @@ #include "VideoCommon.h" class PointerWrap; +namespace MMIO { class Mapping; } namespace SWPixelEngine { @@ -201,6 +202,8 @@ namespace SWPixelEngine void Init(); void DoState(PointerWrap &p); + void RegisterMMIO(MMIO::Mapping* mmio, u32 base); + // Read void Read16(u16& _uReturnValue, const u32 _iAddress); diff --git a/Source/Core/VideoBackends/Software/SWmain.cpp b/Source/Core/VideoBackends/Software/SWmain.cpp index 2182fdc..22a7088 100644 --- a/Source/Core/VideoBackends/Software/SWmain.cpp +++ b/Source/Core/VideoBackends/Software/SWmain.cpp @@ -360,6 +360,16 @@ void VideoSoftware::Video_AbortFrame(void) { } +void VideoSoftware::RegisterCPMMIO(MMIO::Mapping* mmio, u32 base) +{ + SWCommandProcessor::RegisterMMIO(mmio, base); +} + +void VideoSoftware::RegisterPEMMIO(MMIO::Mapping* mmio, u32 base) +{ + SWPixelEngine::RegisterMMIO(mmio, base); +} + readFn16 VideoSoftware::Video_CPRead16() { return SWCommandProcessor::Read16; diff --git a/Source/Core/VideoBackends/Software/VideoBackend.h b/Source/Core/VideoBackends/Software/VideoBackend.h index 4d85c63..02634f6 100644 --- a/Source/Core/VideoBackends/Software/VideoBackend.h +++ b/Source/Core/VideoBackends/Software/VideoBackend.h @@ -4,6 +4,8 @@ #include "VideoBackendBase.h" +namespace MMIO { class Mapping; } + namespace SW { @@ -46,6 +48,9 @@ class VideoSoftware : public VideoBackend bool Video_IsPossibleWaitingSetDrawDone() override; void Video_AbortFrame() override; + void RegisterCPMMIO(MMIO::Mapping* mmio, u32 base) override; + void RegisterPEMMIO(MMIO::Mapping* mmio, u32 base) override; + readFn16 Video_CPRead16() override; writeFn16 Video_CPWrite16() override; readFn16 Video_PERead16() override; diff --git a/Source/Core/VideoCommon/CommandProcessor.cpp b/Source/Core/VideoCommon/CommandProcessor.cpp index b52be51..bc76ea8 100644 --- a/Source/Core/VideoCommon/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/CommandProcessor.cpp @@ -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,186 @@ 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(mapped_var.ptr), + mapped_var.readonly + ? MMIO::InvalidWrite() + : MMIO::DirectWrite(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(metrics_mmio.value), + MMIO::InvalidWrite() + ); + } - 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([](u32) { + SetCpStatusRegister(); + return m_CPStatusReg.Hex; + }), + MMIO::InvalidWrite() + ); + + mmio->Register(base | CTRL_REGISTER, + MMIO::DirectRead(&m_CPCtrlReg.Hex), + MMIO::ComplexWrite([](u32, u16 val) { + UCPCtrlReg tmp(val); + m_CPCtrlReg.Hex = tmp.Hex; SetCpControlRegister(); - } - break; - - 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(&m_CPClearReg.Hex), + MMIO::ComplexWrite([](u32, u16 val) { + UCPClearReg tmp(val); + m_CPClearReg.Hex = tmp.Hex; SetCpClearRegister(); - } - break; - - case PERF_SELECT: - // Seems to select which set of perf registers should be exposed. - DEBUG_LOG(COMMANDPROCESSOR, "Write to PERF_SELECT: %04x", _Value); - break; - - // 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); - } + }) + ); + + mmio->Register(base | PERF_SELECT, + MMIO::InvalidRead(), + MMIO::Nop() + ); + + // Some MMIOs have different handlers for single core vs. dual core mode. + mmio->Register(base | FIFO_RW_DISTANCE_LO, + IsOnThread() + ? MMIO::ComplexRead([](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(MMIO::Utils::LowPart(&fifo.CPReadWriteDistance)), + MMIO::DirectWrite(MMIO::Utils::LowPart(&fifo.CPReadWriteDistance), 0xFFE0) + ); + mmio->Register(base | FIFO_RW_DISTANCE_HI, + IsOnThread() + ? MMIO::ComplexRead([](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(MMIO::Utils::HighPart(&fifo.CPReadWriteDistance)), + MMIO::ComplexWrite([](u32, u16 val) { + WriteHigh(fifo.CPReadWriteDistance, val); + if (fifo.CPReadWriteDistance == 0) + { + GPFifo::ResetGatherPipe(); + ResetVideoBuffer(); + } + else + { + ResetVideoBuffer(); + } + }) + ); + mmio->Register(base | FIFO_READ_POINTER_LO, + IsOnThread() + ? MMIO::DirectRead(MMIO::Utils::LowPart(&fifo.SafeCPReadPointer)) + : MMIO::DirectRead(MMIO::Utils::LowPart(&fifo.CPReadPointer)), + MMIO::DirectWrite(MMIO::Utils::LowPart(&fifo.CPReadPointer), 0xFFE0) + ); + mmio->Register(base | FIFO_READ_POINTER_HI, + IsOnThread() + ? MMIO::DirectRead(MMIO::Utils::HighPart(&fifo.SafeCPReadPointer)) + : MMIO::DirectRead(MMIO::Utils::HighPart(&fifo.CPReadPointer)), + IsOnThread() + ? MMIO::ComplexWrite([](u32, u16 val) { + WriteHigh(fifo.CPReadPointer, val); + fifo.SafeCPReadPointer = fifo.CPReadPointer; + }) + : MMIO::DirectWrite(MMIO::Utils::HighPart(&fifo.CPReadPointer)) + ); +} - if (!IsOnThread()) - RunGpu(); +void Read16(u16& _rReturnValue, const u32 _Address) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_Address, _rReturnValue); +} + +void Write16(const u16 _Value, const u32 _Address) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_Address, _Value); } void Read32(u32& _rReturnValue, const u32 _Address) { - _rReturnValue = 0; - _dbg_assert_msg_(COMMANDPROCESSOR, 0, "Read32 from CommandProccessor at 0x%08x", _Address); + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_Address, _rReturnValue); } void Write32(const u32 _Data, const u32 _Address) { - _dbg_assert_msg_(COMMANDPROCESSOR, 0, "Write32 at CommandProccessor at 0x%08x", _Address); + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_Address, _Data); } void STACKALIGN GatherPipeBursted() diff --git a/Source/Core/VideoCommon/CommandProcessor.h b/Source/Core/VideoCommon/CommandProcessor.h index 6d480ef..6d2ee02 100644 --- a/Source/Core/VideoCommon/CommandProcessor.h +++ b/Source/Core/VideoCommon/CommandProcessor.h @@ -9,6 +9,7 @@ #include "VideoBackendBase.h" class PointerWrap; +namespace MMIO { class Mapping; } extern bool MT; @@ -135,6 +136,8 @@ void Init(); void Shutdown(); void DoState(PointerWrap &p); +void RegisterMMIO(MMIO::Mapping* mmio, u32 base); + // Read void Read16(u16& _rReturnValue, const u32 _Address); void Write16(const u16 _Data, const u32 _Address); diff --git a/Source/Core/VideoCommon/MainBase.cpp b/Source/Core/VideoCommon/MainBase.cpp index 7d81e4a..63655a4 100644 --- a/Source/Core/VideoCommon/MainBase.cpp +++ b/Source/Core/VideoCommon/MainBase.cpp @@ -311,6 +311,16 @@ void VideoBackendHardware::Video_AbortFrame() CommandProcessor::AbortFrame(); } +void VideoBackendHardware::RegisterCPMMIO(MMIO::Mapping* mmio, u32 base) +{ + CommandProcessor::RegisterMMIO(mmio, base); +} + +void VideoBackendHardware::RegisterPEMMIO(MMIO::Mapping* mmio, u32 base) +{ + PixelEngine::RegisterMMIO(mmio, base); +} + readFn16 VideoBackendHardware::Video_CPRead16() { return CommandProcessor::Read16; diff --git a/Source/Core/VideoCommon/PixelEngine.cpp b/Source/Core/VideoCommon/PixelEngine.cpp index 32d91f4..848d66d 100644 --- a/Source/Core/VideoCommon/PixelEngine.cpp +++ b/Source/Core/VideoCommon/PixelEngine.cpp @@ -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(mapped_var.ptr), + MMIO::DirectWrite(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; - - case PE_CTRL_REGISTER: - { - UPECtrlReg tmpCtrl(_iValue); + mmio->Register(base | pq_reg.addr, + MMIO::ComplexRead([pq_reg](u32) { + return g_video_backend->Video_GetQueryResult(pq_reg.pqtype) & 0xFFFF; + }), + MMIO::InvalidWrite() + ); + mmio->Register(base | (pq_reg.addr + 2), + MMIO::ComplexRead([pq_reg](u32) { + return g_video_backend->Video_GetQueryResult(pq_reg.pqtype) >> 16; + }), + MMIO::InvalidWrite() + ); + } + + // Control register + mmio->Register(base | PE_CTRL_REGISTER, + MMIO::DirectRead(&m_Control.Hex), + MMIO::ComplexWrite([](u32, u16 val) { + UPECtrlReg tmpCtrl(val); if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = 0; if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = 0; @@ -312,27 +229,46 @@ 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; - - default: - WARN_LOG(PIXELENGINE, "(w16) unknown %04x @ %08x", _iValue, _iAddress); - break; + }) + ); + + // Token register, readonly. + mmio->Register(base | PE_TOKEN_REG, + MMIO::DirectRead(&CommandProcessor::fifo.PEToken), + MMIO::InvalidWrite() + ); + + // 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([i](u32) { + bbox_active = false; + return bbox[i]; + }), + MMIO::InvalidWrite() + ); } +} +void Read16(u16& _uReturnValue, const u32 _iAddress) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Read(_iAddress, _uReturnValue); +} + +void Write16(const u16 _iValue, const u32 _iAddress) +{ + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_iAddress, _iValue); } void Write32(const u32 _iValue, const u32 _iAddress) { - WARN_LOG(PIXELENGINE, "(w32) 0x%08x @ 0x%08x IGNORING...",_iValue,_iAddress); + // HACK: Remove this function when the new MMIO interface is used. + Memory::mmio_mapping->Write(_iAddress, _iValue); } bool AllowIdleSkipping() diff --git a/Source/Core/VideoCommon/PixelEngine.h b/Source/Core/VideoCommon/PixelEngine.h index c420dbe..19fee9a 100644 --- a/Source/Core/VideoCommon/PixelEngine.h +++ b/Source/Core/VideoCommon/PixelEngine.h @@ -7,6 +7,7 @@ #include "CommonTypes.h" class PointerWrap; +namespace MMIO { class Mapping; } // internal hardware addresses enum @@ -56,6 +57,8 @@ union UPEAlphaReadReg void Init(); void DoState(PointerWrap &p); +void RegisterMMIO(MMIO::Mapping* mmio, u32 base); + // Read void Read16(u16& _uReturnValue, const u32 _iAddress); diff --git a/Source/Core/VideoCommon/VideoBackendBase.h b/Source/Core/VideoCommon/VideoBackendBase.h index 8d88b9c..9c65d15 100644 --- a/Source/Core/VideoCommon/VideoBackendBase.h +++ b/Source/Core/VideoCommon/VideoBackendBase.h @@ -11,6 +11,8 @@ #include "ChunkFile.h" #include "../VideoCommon/PerfQueryBase.h" +namespace MMIO { class Mapping; } + typedef void (*writeFn16)(const u16,const u32); typedef void (*writeFn32)(const u32,const u32); typedef void (*readFn16)(u16&, const u32); @@ -111,6 +113,11 @@ public: virtual bool Video_IsHiWatermarkActive() = 0; virtual void Video_AbortFrame() = 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; + + // HACK: Remove these functions when the new MMIO interface is used. virtual readFn16 Video_CPRead16() = 0; virtual writeFn16 Video_CPWrite16() = 0; virtual readFn16 Video_PERead16() = 0; @@ -163,6 +170,9 @@ class VideoBackendHardware : public VideoBackend bool Video_IsHiWatermarkActive(); void Video_AbortFrame(); + void RegisterCPMMIO(MMIO::Mapping* mmio, u32 base) override; + void RegisterPEMMIO(MMIO::Mapping* mmio, u32 base) override; + readFn16 Video_CPRead16(); writeFn16 Video_CPWrite16(); readFn16 Video_PERead16(); diff --git a/Tools/buildbot-try.sh b/Tools/buildbot-try.sh index b34db15..6cc85db 100755 --- a/Tools/buildbot-try.sh +++ b/Tools/buildbot-try.sh @@ -17,7 +17,7 @@ if ! which buildbot >/dev/null 2>&1; then exit 1 fi -if ! git branch | grep -q '^* '; then +if true || ! git branch | grep -q '^* '; then echo "Unable to determine the current Git branch. Input the Git branch name:" read branchname else