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