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/DSP.cpp b/Source/Core/Core/HW/DSP.cpp index 51ca22d..70ea616 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; + 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/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..29181b5 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,22 @@ void InitHWMemFuncsWii() hwWriteWii32[AUDIO_START] = AudioInterface::Write32; } +void InitMMIO(MMIO::Mapping* mmio) +{ + VideoInterface::RegisterMMIO(mmio, 0xCC002000); + ProcessorInterface::RegisterMMIO(mmio, 0xCC003000); + MemoryInterface::RegisterMMIO(mmio, 0xCC004000); + DSP::RegisterMMIO(mmio, 0xCC005000); +} + +void InitMMIOWii(MMIO::Mapping* mmio) +{ + VideoInterface::RegisterMMIO(mmio, 0xCC002000); + ProcessorInterface::RegisterMMIO(mmio, 0xCC003000); + MemoryInterface::RegisterMMIO(mmio, 0xCC004000); + DSP::RegisterMMIO(mmio, 0xCC005000); +} + writeFn32 GetHWWriteFun32(const u32 _Address) { return hwWrite32[(_Address >> HWSHIFT) & (NUMHWMEMFUN-1)]; @@ -348,11 +368,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 +409,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/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/VideoBackends/Software/SWCommandProcessor.h b/Source/Core/VideoBackends/Software/SWCommandProcessor.h index 51cde8d..c2381a2 100644 --- a/Source/Core/VideoBackends/Software/SWCommandProcessor.h +++ b/Source/Core/VideoBackends/Software/SWCommandProcessor.h @@ -98,14 +98,15 @@ namespace SWCommandProcessor UCPStatusReg status; // 0x00 UCPCtrlReg ctrl; // 0x02 UCPClearReg clear; // 0x04 - u32 unk0; // 0x06 - u32 unk1; // 0x0a + u32 unk0; // 0x08 + u16 unk1; // 0x0c u16 token; // 0x0e u16 bboxleft; // 0x10 u16 bboxtop; // 0x12 u16 bboxright; // 0x14 u16 bboxbottom; // 0x16 - u16 unk2; // 0x18 + u32 unk2; // 0x18 + u32 unk3; // 0x1c u32 fifobase; // 0x20 u32 fifoend; // 0x24 u32 hiwatermark; // 0x28 diff --git a/Tools/buildbot-try.sh b/Tools/buildbot-try.sh index b34db15..6cc85db 100755 --- a/Tools/buildbot-try.sh +++ b/Tools/buildbot-try.sh @@ -17,7 +17,7 @@ if ! which buildbot >/dev/null 2>&1; then exit 1 fi -if ! git branch | grep -q '^* '; then +if true || ! git branch | grep -q '^* '; then echo "Unable to determine the current Git branch. Input the Git branch name:" read branchname else