389 changes: 113 additions & 276 deletions Source/Core/Core/HW/DSP.cpp

Large diffs are not rendered by default.

11 changes: 3 additions & 8 deletions Source/Core/Core/HW/DSP.h
Expand Up @@ -7,6 +7,7 @@
#include "Common.h"
class PointerWrap;
class DSPEmulator;
namespace MMIO { class Mapping; }

namespace DSP
{
Expand All @@ -28,21 +29,15 @@ enum
void Init(bool hle);
void Shutdown();

void RegisterMMIO(MMIO::Mapping* mmio, u32 base);

DSPEmulator *GetDSPEmulator();

void DoState(PointerWrap &p);

void GenerateDSPInterrupt(DSPInterruptType _DSPInterruptType, bool _bSet = true);
void GenerateDSPInterruptFromDSPEmu(DSPInterruptType _DSPInterruptType, bool _bSet = true);

// Read32
void Read16(u16& _uReturnValue, const u32 _uAddress);
void Read32(u32& _uReturnValue, const u32 _uAddress);

// Write
void Write16(const u16 _uValue, const u32 _uAddress);
void Write32(const u32 _uValue, const u32 _uAddress);

// Audio/DSP Helper
u8 ReadARAM(const u32 _uAddress);
void WriteARAM(u8 value, u32 _uAddress);
Expand Down
122 changes: 51 additions & 71 deletions Source/Core/Core/HW/DVDInterface.cpp
Expand Up @@ -17,6 +17,7 @@
#include "../VolumeHandler.h"
#include "AudioInterface.h"
#include "../Movie.h"
#include "MMIO.h"

// Disc transfer rate measured in bytes per second
static const u32 DISC_TRANSFER_RATE_GC = 5 * 1024 * 1024;
Expand Down Expand Up @@ -404,38 +405,12 @@ bool DVDReadADPCM(u8* _pDestBuffer, u32 _iNumSamples)
}
}

void Read32(u32& _uReturnValue, const u32 _iAddress)
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
{
switch (_iAddress & 0xFF)
{
case DI_STATUS_REGISTER: _uReturnValue = m_DISR.Hex; break;
case DI_COVER_REGISTER: _uReturnValue = m_DICVR.Hex; break;
case DI_COMMAND_0: _uReturnValue = m_DICMDBUF[0].Hex; break;
case DI_COMMAND_1: _uReturnValue = m_DICMDBUF[1].Hex; break;
case DI_COMMAND_2: _uReturnValue = m_DICMDBUF[2].Hex; break;
case DI_DMA_ADDRESS_REGISTER: _uReturnValue = m_DIMAR.Hex; break;
case DI_DMA_LENGTH_REGISTER: _uReturnValue = m_DILENGTH.Hex; break;
case DI_DMA_CONTROL_REGISTER: _uReturnValue = m_DICR.Hex; break;
case DI_IMMEDIATE_DATA_BUFFER: _uReturnValue = m_DIIMMBUF.Hex; break;
case DI_CONFIG_REGISTER: _uReturnValue = m_DICFG.Hex; break;

default:
_dbg_assert_(DVDINTERFACE, 0);
_uReturnValue = 0;
break;
}
DEBUG_LOG(DVDINTERFACE, "(r32): 0x%08x - 0x%08x", _iAddress, _uReturnValue);
}

void Write32(const u32 _iValue, const u32 _iAddress)
{
DEBUG_LOG(DVDINTERFACE, "(w32): 0x%08x @ 0x%08x", _iValue, _iAddress);

switch (_iAddress & 0xFF)
{
case DI_STATUS_REGISTER:
{
UDISR tmpStatusReg(_iValue);
mmio->Register(base | DI_STATUS_REGISTER,
MMIO::DirectRead<u32>(&m_DISR.Hex),
MMIO::ComplexWrite<u32>([](u32, u32 val) {
UDISR tmpStatusReg(val);

m_DISR.DEINITMASK = tmpStatusReg.DEINITMASK;
m_DISR.TCINTMASK = tmpStatusReg.TCINTMASK;
Expand All @@ -457,39 +432,46 @@ void Write32(const u32 _iValue, const u32 _iAddress)
}

UpdateInterrupts();
}
break;
})
);

case DI_COVER_REGISTER:
{
UDICVR tmpCoverReg(_iValue);
mmio->Register(base | DI_COVER_REGISTER,
MMIO::DirectRead<u32>(&m_DICVR.Hex),
MMIO::ComplexWrite<u32>([](u32, u32 val) {
UDICVR tmpCoverReg(val);

m_DICVR.CVRINTMASK = tmpCoverReg.CVRINTMASK;

if (tmpCoverReg.CVRINT)
m_DICVR.CVRINT = 0;

UpdateInterrupts();
}
break;

case DI_COMMAND_0: m_DICMDBUF[0].Hex = _iValue; break;
case DI_COMMAND_1: m_DICMDBUF[1].Hex = _iValue; break;
case DI_COMMAND_2: m_DICMDBUF[2].Hex = _iValue; break;

case DI_DMA_ADDRESS_REGISTER:
{
m_DIMAR.Hex = _iValue & ~0xfc00001f;
}
break;
case DI_DMA_LENGTH_REGISTER:
{
m_DILENGTH.Hex = _iValue & ~0x1f;
}
break;
case DI_DMA_CONTROL_REGISTER:
{
m_DICR.Hex = _iValue & 7;
})
);

// Command registers are very similar and we can register them with a
// simple loop.
for (int i = 0; i < 3; ++i)
mmio->Register(base | (DI_COMMAND_0 + 4 * i),
MMIO::DirectRead<u32>(&m_DICMDBUF[i].Hex),
MMIO::DirectWrite<u32>(&m_DICMDBUF[i].Hex)
);

// DMA related registers. Mostly direct accesses (+ masking for writes to
// handle things like address alignment) and complex write on the DMA
// control register that will trigger the DMA.
mmio->Register(base | DI_DMA_ADDRESS_REGISTER,
MMIO::DirectRead<u32>(&m_DIMAR.Hex),
MMIO::DirectWrite<u32>(&m_DIMAR.Hex, ~0xFC00001F)
);
mmio->Register(base | DI_DMA_LENGTH_REGISTER,
MMIO::DirectRead<u32>(&m_DILENGTH.Hex),
MMIO::DirectWrite<u32>(&m_DILENGTH.Hex, ~0x1F)
);
mmio->Register(base | DI_DMA_CONTROL_REGISTER,
MMIO::DirectRead<u32>(&m_DICR.Hex),
MMIO::ComplexWrite<u32>([](u32, u32 val) {
m_DICR.Hex = val & 7;
if (m_DICR.TSTART)
{
if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed)
Expand All @@ -504,21 +486,19 @@ void Write32(const u32 _iValue, const u32 _iAddress)
ExecuteCommand(m_DICR);
}
}
}
break;

case DI_IMMEDIATE_DATA_BUFFER: m_DIIMMBUF.Hex = _iValue; break;

case DI_CONFIG_REGISTER:
{
WARN_LOG(DVDINTERFACE, "Write to DICFG, ignored as it's read-only");
}
break;

default:
_dbg_assert_msg_(DVDINTERFACE, 0, "Write to unknown DI address 0x%08x", _iAddress);
break;
}
})
);

mmio->Register(base | DI_IMMEDIATE_DATA_BUFFER,
MMIO::DirectRead<u32>(&m_DIIMMBUF.Hex),
MMIO::DirectWrite<u32>(&m_DIIMMBUF.Hex)
);

// DI config register is read only.
mmio->Register(base | DI_CONFIG_REGISTER,
MMIO::DirectRead<u32>(&m_DICFG.Hex),
MMIO::InvalidWrite<u32>()
);
}

void UpdateInterrupts()
Expand Down
9 changes: 3 additions & 6 deletions Source/Core/Core/HW/DVDInterface.h
Expand Up @@ -6,6 +6,7 @@

#include "CommonTypes.h"
class PointerWrap;
namespace MMIO { class Mapping; }

namespace DVDInterface
{
Expand All @@ -14,6 +15,8 @@ void Init();
void Shutdown();
void DoState(PointerWrap &p);

void RegisterMMIO(MMIO::Mapping* mmio, u32 base);

// Disc detection and swapping
void SetDiscInside(bool _DiscInside);
bool IsDiscInside();
Expand All @@ -32,12 +35,6 @@ bool DVDRead(u32 _iDVDOffset, u32 _iRamAddress, u32 _iLength);
bool DVDReadADPCM(u8* _pDestBuffer, u32 _iNumSamples);
extern bool g_bStream;

// Read32
void Read32(u32& _uReturnValue, const u32 _iAddress);

// Write32
void Write32(const u32 _iValue, const u32 _iAddress);


// Not sure about endianness here. I'll just name them like this...
enum DIErrorLow
Expand Down
46 changes: 14 additions & 32 deletions Source/Core/Core/HW/EXI.cpp
Expand Up @@ -9,6 +9,7 @@

#include "ProcessorInterface.h"
#include "../PowerPC/PowerPC.h"
#include "MMIO.h"

#include "EXI.h"
#include "Sram.h"
Expand Down Expand Up @@ -62,6 +63,19 @@ void PauseAndLock(bool doLock, bool unpauseOnUnlock)
channel->PauseAndLock(doLock, unpauseOnUnlock);
}

void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
{
for (int i = 0; i < MAX_EXI_CHANNELS; ++i)
{
_dbg_assert_(EXPANSIONINTERFACE, g_Channels[i] != nullptr);
// Each channel has 5 32 bit registers assigned to it. We offset the
// base that we give to each channel for registration.
//
// Be careful: this means the base is no longer aligned on a page
// boundary and using "base | FOO" is not valid!
g_Channels[i]->RegisterMMIO(mmio, base + 5 * 4 * i);
}
}

void ChangeDeviceCallback(u64 userdata, int cyclesLate)
{
Expand Down Expand Up @@ -99,38 +113,6 @@ void Update()
g_Channels[2]->Update();
}

void Read32(u32& _uReturnValue, const u32 _iAddress)
{
// TODO 0xfff00000 is mapped to EXI -> mapped to first MB of maskrom
u32 iAddr = _iAddress & 0x3FF;
u32 iRegister = (iAddr >> 2) % 5;
u32 iChannel = (iAddr >> 2) / 5;

_dbg_assert_(EXPANSIONINTERFACE, iChannel < MAX_EXI_CHANNELS);

if (iChannel < MAX_EXI_CHANNELS)
{
g_Channels[iChannel]->Read32(_uReturnValue, iRegister);
}
else
{
_uReturnValue = 0;
}
}

void Write32(const u32 _iValue, const u32 _iAddress)
{
// TODO 0xfff00000 is mapped to EXI -> mapped to first MB of maskrom
u32 iAddr = _iAddress & 0x3FF;
u32 iRegister = (iAddr >> 2) % 5;
u32 iChannel = (iAddr >> 2) / 5;

_dbg_assert_(EXPANSIONINTERFACE, iChannel < MAX_EXI_CHANNELS);

if (iChannel < MAX_EXI_CHANNELS)
g_Channels[iChannel]->Write32(_iValue, iRegister);
}

void UpdateInterrupts()
{
// Interrupts are mapped a bit strangely:
Expand Down
6 changes: 3 additions & 3 deletions Source/Core/Core/HW/EXI.h
Expand Up @@ -8,6 +8,7 @@
#include "EXI_Channel.h"
#include "Thread.h"
class PointerWrap;
namespace MMIO { class Mapping; }

enum
{
Expand All @@ -22,14 +23,13 @@ void Shutdown();
void DoState(PointerWrap &p);
void PauseAndLock(bool doLock, bool unpauseOnUnlock);

void RegisterMMIO(MMIO::Mapping* mmio, u32 base);

void Update();
void UpdateInterrupts();

void ChangeDeviceCallback(u64 userdata, int cyclesLate);
void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num);
IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex=-1);

void Read32(u32& _uReturnValue, const u32 _iAddress);
void Write32(const u32 _iValue, const u32 _iAddress);

} // end of namespace ExpansionInterface
258 changes: 112 additions & 146 deletions Source/Core/Core/HW/EXI_Channel.cpp
Expand Up @@ -7,6 +7,7 @@
#include "EXI.h"
#include "../ConfigManager.h"
#include "../Movie.h"
#include "MMIO.h"

#define EXI_READ 0
#define EXI_WRITE 1
Expand Down Expand Up @@ -41,6 +42,117 @@ CEXIChannel::~CEXIChannel()
RemoveDevices();
}

void CEXIChannel::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
{
// Warning: the base is not aligned on a page boundary here. We can't use |
// to select a register address, instead we need to use +.

mmio->Register(base + EXI_STATUS,
MMIO::ComplexRead<u32>([this](u32) {
// check if external device is present
// pretty sure it is memcard only, not entirely sure
if (m_ChannelId == 2)
{
m_Status.EXT = 0;
}
else
{
m_Status.EXT = GetDevice(1)->IsPresent() ? 1 : 0;
}

return m_Status.Hex;
}),
MMIO::ComplexWrite<u32>([this](u32, u32 val) {
UEXI_STATUS newStatus(val);

m_Status.EXIINTMASK = newStatus.EXIINTMASK;
if (newStatus.EXIINT)
m_Status.EXIINT = 0;

m_Status.TCINTMASK = newStatus.TCINTMASK;
if (newStatus.TCINT)
m_Status.TCINT = 0;

m_Status.CLK = newStatus.CLK;

if (m_ChannelId == 0 || m_ChannelId == 1)
{
m_Status.EXTINTMASK = newStatus.EXTINTMASK;

if (newStatus.EXTINT)
m_Status.EXTINT = 0;
}

if (m_ChannelId == 0)
m_Status.ROMDIS = newStatus.ROMDIS;

IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT ^ newStatus.CHIP_SELECT);
m_Status.CHIP_SELECT = newStatus.CHIP_SELECT;
if (pDevice != NULL)
pDevice->SetCS(m_Status.CHIP_SELECT);

CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0);
})
);

mmio->Register(base + EXI_DMAADDR,
MMIO::DirectRead<u32>(&m_DMAMemoryAddress),
MMIO::DirectWrite<u32>(&m_DMAMemoryAddress)
);
mmio->Register(base + EXI_DMALENGTH,
MMIO::DirectRead<u32>(&m_DMALength),
MMIO::DirectWrite<u32>(&m_DMALength)
);
mmio->Register(base + EXI_DMACONTROL,
MMIO::DirectRead<u32>(&m_Control.Hex),
MMIO::ComplexWrite<u32>([this](u32, u32 val) {
m_Control.Hex = val;

if (m_Control.TSTART)
{
IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT);
if (pDevice == NULL)
return;

if (m_Control.DMA == 0)
{
// immediate data
switch (m_Control.RW)
{
case EXI_READ: m_ImmData = pDevice->ImmRead(m_Control.TLEN + 1); break;
case EXI_WRITE: pDevice->ImmWrite(m_ImmData, m_Control.TLEN + 1); break;
case EXI_READWRITE: pDevice->ImmReadWrite(m_ImmData, m_Control.TLEN + 1); break;
default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI Imm: Unknown transfer type %i", m_Control.RW);
}
m_Control.TSTART = 0;
}
else
{
// DMA
switch (m_Control.RW)
{
case EXI_READ: pDevice->DMARead (m_DMAMemoryAddress, m_DMALength); break;
case EXI_WRITE: pDevice->DMAWrite(m_DMAMemoryAddress, m_DMALength); break;
default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI DMA: Unknown transfer type %i", m_Control.RW);
}
m_Control.TSTART = 0;
}

if(!m_Control.TSTART) // completed !
{
m_Status.TCINT = 1;
CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0);
}
}
})
);

mmio->Register(base + EXI_IMMDATA,
MMIO::DirectRead<u32>(&m_ImmData),
MMIO::DirectWrite<u32>(&m_ImmData)
);
}

void CEXIChannel::RemoveDevices()
{
for (auto& device : m_pDevices)
Expand Down Expand Up @@ -115,152 +227,6 @@ void CEXIChannel::Update()
device->Update();
}

void CEXIChannel::Read32(u32& _uReturnValue, const u32 _iRegister)
{
switch (_iRegister)
{
case EXI_STATUS:
{
// check if external device is present
// pretty sure it is memcard only, not entirely sure
if (m_ChannelId == 2)
{
m_Status.EXT = 0;
}
else
{
m_Status.EXT = GetDevice(1)->IsPresent() ? 1 : 0;
}

_uReturnValue = m_Status.Hex;
break;
}

case EXI_DMAADDR:
_uReturnValue = m_DMAMemoryAddress;
break;

case EXI_DMALENGTH:
_uReturnValue = m_DMALength;
break;

case EXI_DMACONTROL:
_uReturnValue = m_Control.Hex;
break;

case EXI_IMMDATA:
_uReturnValue = m_ImmData;
break;

default:
_dbg_assert_(EXPANSIONINTERFACE, 0);
_uReturnValue = 0xDEADBEEF;
}

DEBUG_LOG(EXPANSIONINTERFACE, "(r32) 0x%08x channel: %i register: %s",
_uReturnValue, m_ChannelId, Debug_GetRegisterName(_iRegister));
}

void CEXIChannel::Write32(const u32 _iValue, const u32 _iRegister)
{
DEBUG_LOG(EXPANSIONINTERFACE, "(w32) 0x%08x channel: %i register: %s",
_iValue, m_ChannelId, Debug_GetRegisterName(_iRegister));

switch (_iRegister)
{
case EXI_STATUS:
{
UEXI_STATUS newStatus(_iValue);

m_Status.EXIINTMASK = newStatus.EXIINTMASK;
if (newStatus.EXIINT)
m_Status.EXIINT = 0;

m_Status.TCINTMASK = newStatus.TCINTMASK;
if (newStatus.TCINT)
m_Status.TCINT = 0;

m_Status.CLK = newStatus.CLK;

if (m_ChannelId == 0 || m_ChannelId == 1)
{
m_Status.EXTINTMASK = newStatus.EXTINTMASK;

if (newStatus.EXTINT)
m_Status.EXTINT = 0;
}

if (m_ChannelId == 0)
m_Status.ROMDIS = newStatus.ROMDIS;

IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT ^ newStatus.CHIP_SELECT);
m_Status.CHIP_SELECT = newStatus.CHIP_SELECT;
if (pDevice != NULL)
pDevice->SetCS(m_Status.CHIP_SELECT);

CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0);
}
break;

case EXI_DMAADDR:
INFO_LOG(EXPANSIONINTERFACE, "Wrote DMAAddr, channel %i", m_ChannelId);
m_DMAMemoryAddress = _iValue;
break;

case EXI_DMALENGTH:
INFO_LOG(EXPANSIONINTERFACE, "Wrote DMALength, channel %i", m_ChannelId);
m_DMALength = _iValue;
break;

case EXI_DMACONTROL:
INFO_LOG(EXPANSIONINTERFACE, "Wrote DMAControl, channel %i", m_ChannelId);
m_Control.Hex = _iValue;

if (m_Control.TSTART)
{
IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT);
if (pDevice == NULL)
return;

if (m_Control.DMA == 0)
{
// immediate data
switch (m_Control.RW)
{
case EXI_READ: m_ImmData = pDevice->ImmRead(m_Control.TLEN + 1); break;
case EXI_WRITE: pDevice->ImmWrite(m_ImmData, m_Control.TLEN + 1); break;
case EXI_READWRITE: pDevice->ImmReadWrite(m_ImmData, m_Control.TLEN + 1); break;
default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI Imm: Unknown transfer type %i", m_Control.RW);
}
m_Control.TSTART = 0;
}
else
{
// DMA
switch (m_Control.RW)
{
case EXI_READ: pDevice->DMARead (m_DMAMemoryAddress, m_DMALength); break;
case EXI_WRITE: pDevice->DMAWrite(m_DMAMemoryAddress, m_DMALength); break;
default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI DMA: Unknown transfer type %i", m_Control.RW);
}
m_Control.TSTART = 0;
}

if(!m_Control.TSTART) // completed !
{
m_Status.TCINT = 1;
CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0);
}
}
break;

case EXI_IMMDATA:
INFO_LOG(EXPANSIONINTERFACE, "Wrote IMMData, channel %i", m_ChannelId);
m_ImmData = _iValue;
break;
}
}

void CEXIChannel::DoState(PointerWrap &p)
{
p.DoPOD(m_Status);
Expand Down
29 changes: 9 additions & 20 deletions Source/Core/Core/HW/EXI_Channel.h
Expand Up @@ -9,30 +9,20 @@
#include "EXI_Device.h"
#include <memory>

namespace MMIO { class Mapping; }

class CEXIChannel
{
private:

enum
{
EXI_STATUS = 0,
EXI_DMAADDR = 1,
EXI_DMALENGTH = 2,
EXI_DMACONTROL = 3,
EXI_IMMDATA = 4
EXI_STATUS = 0x00,
EXI_DMAADDR = 0x04,
EXI_DMALENGTH = 0x08,
EXI_DMACONTROL = 0x0C,
EXI_IMMDATA = 0x10
};
const char* Debug_GetRegisterName(u32 _register)
{
switch (_register)
{
case EXI_STATUS: return "STATUS";
case EXI_DMAADDR: return "DMAADDR";
case EXI_DMALENGTH: return "DMALENGTH";
case EXI_DMACONTROL: return "DMACONTROL";
case EXI_IMMDATA: return "IMMDATA";
default: return "!!! Unknown EXI Register !!!";
}
}

// EXI Status Register - "Channel Parameter Register"
union UEXI_STATUS
Expand Down Expand Up @@ -104,15 +94,14 @@ class CEXIChannel
CEXIChannel(u32 ChannelId);
~CEXIChannel();

void RegisterMMIO(MMIO::Mapping* mmio, u32 base);

void AddDevice(const TEXIDevices device_type, const int device_num);
void AddDevice(IEXIDevice* pDevice, const int device_num, bool notifyPresenceChanged=true);

// Remove all devices
void RemoveDevices();

void Read32(u32& _uReturnValue, const u32 _iRegister);
void Write32(const u32 _iValue, const u32 _iRegister);

void Update();
bool IsCausingInterrupt();
void DoState(PointerWrap &p);
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/Core/HW/HW.cpp
Expand Up @@ -38,11 +38,11 @@ namespace HW
VideoInterface::Init();
SerialInterface::Init();
ProcessorInterface::Init();
ExpansionInterface::Init(); // Needs to be initialized before Memory
Memory::Init();
DSP::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPHLE);
DVDInterface::Init();
GPFifo::Init();
ExpansionInterface::Init();
CCPU::Init(SConfig::GetInstance().m_LocalCoreStartupParameter.iCPUCore);
SystemTimers::Init();

Expand Down
383 changes: 383 additions & 0 deletions Source/Core/Core/HW/MMIO.cpp
@@ -0,0 +1,383 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.

#include "MMIO.h"
#include "MMIOHandlers.h"

#include <functional>

namespace MMIO
{

// Base classes for the two handling method hierarchies. Note that a single
// class can inherit from both.
//
// At the moment the only common element between all the handling method is
// that they should be able to accept a visitor of the appropriate type.
template <typename T>
class ReadHandlingMethod
{
public:
virtual ~ReadHandlingMethod() {}
virtual void AcceptReadVisitor(ReadHandlingMethodVisitor<T>& v) const = 0;
};
template <typename T>
class WriteHandlingMethod
{
public:
virtual ~WriteHandlingMethod() {}
virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor<T>& v) const = 0;
};

// Constant: handling method holds a single integer and passes it to the
// visitor. This is a read only handling method: storing to a constant does not
// mean anything.
template <typename T>
class ConstantHandlingMethod : public ReadHandlingMethod<T>
{
public:
explicit ConstantHandlingMethod(T value) : value_(value)
{
}

virtual ~ConstantHandlingMethod() {}

virtual void AcceptReadVisitor(ReadHandlingMethodVisitor<T>& v) const
{
v.VisitConstant(value_);
}

private:
T value_;
};
template <typename T>
ReadHandlingMethod<T>* Constant(T value)
{
return new ConstantHandlingMethod<T>(value);
}

// Nop: extremely simple write handling method that does nothing at all, only
// respond to visitors and dispatch to the correct method. This is write only
// since reads should always at least return a value.
template <typename T>
class NopHandlingMethod : public WriteHandlingMethod<T>
{
public:
NopHandlingMethod() {}
virtual ~NopHandlingMethod() {}
virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor<T>& v) const
{
v.VisitNop();
}
};
template <typename T>
WriteHandlingMethod<T>* Nop()
{
return new NopHandlingMethod<T>();
}

// Direct: handling method holds a pointer to the value where to read/write the
// data from, as well as a mask that is used to restrict reading/writing only
// to a given set of bits.
template <typename T>
class DirectHandlingMethod : public ReadHandlingMethod<T>,
public WriteHandlingMethod<T>
{
public:
DirectHandlingMethod(T* addr, u32 mask) : addr_(addr), mask_(mask)
{
}

virtual ~DirectHandlingMethod() {}

virtual void AcceptReadVisitor(ReadHandlingMethodVisitor<T>& v) const
{
v.VisitDirect(addr_, mask_);
}

virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor<T>& v) const
{
v.VisitDirect(addr_, mask_);
}

private:
T* addr_;
u32 mask_;
};
template <typename T>
ReadHandlingMethod<T>* DirectRead(const T* addr, u32 mask)
{
return new DirectHandlingMethod<T>(const_cast<T*>(addr), mask);
}
template <typename T>
ReadHandlingMethod<T>* DirectRead(volatile const T* addr, u32 mask)
{
return new DirectHandlingMethod<T>((T*)addr, mask);
}
template <typename T>
WriteHandlingMethod<T>* DirectWrite(T* addr, u32 mask)
{
return new DirectHandlingMethod<T>(addr, mask);
}
template <typename T>
WriteHandlingMethod<T>* DirectWrite(volatile T* addr, u32 mask)
{
return new DirectHandlingMethod<T>((T*)addr, mask);
}

// Complex: holds a lambda that is called when a read or a write is executed.
// This gives complete control to the user as to what is going to happen during
// that read or write, but reduces the optimization potential.
template <typename T>
class ComplexHandlingMethod : public ReadHandlingMethod<T>,
public WriteHandlingMethod<T>
{
public:
explicit ComplexHandlingMethod(std::function<T(u32)> read_lambda)
: read_lambda_(read_lambda), write_lambda_(InvalidWriteLambda())
{
}

explicit ComplexHandlingMethod(std::function<void(u32, T)> write_lambda)
: read_lambda_(InvalidReadLambda()), write_lambda_(write_lambda)
{
}

virtual ~ComplexHandlingMethod() {}

virtual void AcceptReadVisitor(ReadHandlingMethodVisitor<T>& v) const
{
v.VisitComplex(read_lambda_);
}

virtual void AcceptWriteVisitor(WriteHandlingMethodVisitor<T>& v) const
{
v.VisitComplex(write_lambda_);
}

private:
std::function<T(u32)> InvalidReadLambda() const
{
return [](u32) {
_dbg_assert_msg_(MEMMAP, 0, "Called the read lambda on a write "
"complex handler.");
return 0;
};
}

std::function<void(u32, T)> InvalidWriteLambda() const
{
return [](u32, T) {
_dbg_assert_msg_(MEMMAP, 0, "Called the write lambda on a read "
"complex handler.");
};
}

std::function<T(u32)> read_lambda_;
std::function<void(u32, T)> write_lambda_;
};
template <typename T>
ReadHandlingMethod<T>* ComplexRead(std::function<T(u32)> lambda)
{
return new ComplexHandlingMethod<T>(lambda);
}
template <typename T>
WriteHandlingMethod<T>* ComplexWrite(std::function<void(u32, T)> lambda)
{
return new ComplexHandlingMethod<T>(lambda);
}

// Invalid: specialization of the complex handling type with lambdas that
// display error messages.
template <typename T>
ReadHandlingMethod<T>* InvalidRead()
{
return ComplexRead<T>([](u32 addr) {
ERROR_LOG(MEMMAP, "Trying to read from an invalid MMIO (addr=%08x)",
addr);
return -1;
});
}
template <typename T>
WriteHandlingMethod<T>* InvalidWrite()
{
return ComplexWrite<T>([](u32 addr, T val) {
ERROR_LOG(MEMMAP, "Trying to write to an invalid MMIO (addr=%08x, val=%08x)",
addr, (u32)val);
});
}

// Converters to larger and smaller size. Probably the most complex of these
// handlers to implement. They do not define new handling method types but
// instead will internally use the types defined above.
template <typename T> struct SmallerAccessSize {};
template <> struct SmallerAccessSize<u16> { typedef u8 value; };
template <> struct SmallerAccessSize<u32> { typedef u16 value; };

template <typename T> struct LargerAccessSize {};
template <> struct LargerAccessSize<u8> { typedef u16 value; };
template <> struct LargerAccessSize<u16> { typedef u32 value; };

template <typename T>
ReadHandlingMethod<T>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr)
{
typedef typename SmallerAccessSize<T>::value ST;

const ReadHandler<ST>* high_part;
const ReadHandler<ST>* low_part;
mmio->GetHandlerForRead(high_part_addr, &high_part);
mmio->GetHandlerForRead(low_part_addr, &low_part);

// TODO(delroth): optimize
return ComplexRead<T>([high_part, low_part](u32 addr) {
return ((T)high_part->Read(addr) << (8 * sizeof (ST))) | low_part->Read(addr);
});
}

template <typename T>
WriteHandlingMethod<T>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr)
{
typedef typename SmallerAccessSize<T>::value ST;

const WriteHandler<ST>* high_part;
const WriteHandler<ST>* low_part;
mmio->GetHandlerForWrite(high_part_addr, &high_part);
mmio->GetHandlerForWrite(low_part_addr, &low_part);

// TODO(delroth): optimize
return ComplexWrite<T>([high_part, low_part](u32 addr, T val) {
high_part->Write(addr, val >> (8 * sizeof (ST)));
low_part->Write(addr, (ST)val);
});
}

template <typename T>
ReadHandlingMethod<T>* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift)
{
typedef typename LargerAccessSize<T>::value LT;

const ReadHandler<LT>* large;
mmio->GetHandlerForRead(larger_addr, &large);

// TODO(delroth): optimize
return ComplexRead<T>([large, shift](u32 addr) {
return large->Read(addr & ~(sizeof (LT) - 1)) >> shift;
});
}

// Inplementation of the ReadHandler and WriteHandler class. There is a lot of
// redundant code between these two classes but trying to abstract it away
// brings more trouble than it fixes.
template <typename T>
ReadHandler<T>::ReadHandler() : m_Method(nullptr)
{
ResetMethod(InvalidRead<T>());
}

template <typename T>
ReadHandler<T>::ReadHandler(ReadHandlingMethod<T>* method)
: m_Method(nullptr)
{
ResetMethod(method);
}

template <typename T>
ReadHandler<T>::~ReadHandler()
{
}

template <typename T>
void ReadHandler<T>::Visit(ReadHandlingMethodVisitor<T>& visitor) const
{
m_Method->AcceptReadVisitor(visitor);
}

template <typename T>
void ReadHandler<T>::ResetMethod(ReadHandlingMethod<T>* method)
{
m_Method.reset(method);

struct FuncCreatorVisitor : public ReadHandlingMethodVisitor<T>
{
std::function<T(u32)> ret;

virtual void VisitConstant(T value)
{
ret = [value](u32) { return value; };
}

virtual void VisitDirect(const T* addr, u32 mask)
{
ret = [addr, mask](u32) { return *addr & mask; };
}

virtual void VisitComplex(std::function<T(u32)> lambda)
{
ret = lambda;
}
};

FuncCreatorVisitor v;
Visit(v);
m_ReadFunc = v.ret;
}

template <typename T>
WriteHandler<T>::WriteHandler() : m_Method(nullptr)
{
ResetMethod(InvalidWrite<T>());
}

template <typename T>
WriteHandler<T>::WriteHandler(WriteHandlingMethod<T>* method)
: m_Method(nullptr)
{
ResetMethod(method);
}

template <typename T>
WriteHandler<T>::~WriteHandler()
{
}

template <typename T>
void WriteHandler<T>::Visit(WriteHandlingMethodVisitor<T>& visitor) const
{
m_Method->AcceptWriteVisitor(visitor);
}

template <typename T>
void WriteHandler<T>::ResetMethod(WriteHandlingMethod<T>* method)
{
m_Method.reset(method);

struct FuncCreatorVisitor : public WriteHandlingMethodVisitor<T>
{
std::function<void(u32, T)> ret;

virtual void VisitNop()
{
ret = [](u32, T) {};
}

virtual void VisitDirect(T* ptr, u32 mask)
{
ret = [ptr, mask](u32, T val) { *ptr = val & mask; };
}

virtual void VisitComplex(std::function<void(u32, T)> lambda)
{
ret = lambda;
}
};

FuncCreatorVisitor v;
Visit(v);
m_WriteFunc = v.ret;
}

// Define all the public specializations that are exported in MMIOHandlers.h.
MMIO_PUBLIC_SPECIALIZATIONS();

}
156 changes: 156 additions & 0 deletions Source/Core/Core/HW/MMIO.h
@@ -0,0 +1,156 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.

#pragma once

#include "Common.h"
#include <array>
#include <string>
#include <type_traits>

#include "MMIOHandlers.h"

namespace MMIO
{

// There are three main MMIO blocks on the Wii (only one on the GameCube):
// - 0xCC00xxxx: GameCube MMIOs (CP, PE, VI, PI, MI, DSP, DVD, SI, EI, AI, GP)
// - 0xCD00xxxx: Wii MMIOs and GC mirrors (IPC, DVD, SI, EI, AI)
// - 0xCD80xxxx: Mirror of 0xCD00xxxx.
//
// In practice, since the third block is a mirror of the second one, we can
// assume internally that there are only two blocks: one for GC, one for Wii.
enum Block
{
GC_BLOCK = 0,
WII_BLOCK = 1,

NUM_BLOCKS
};
const u32 BLOCK_SIZE = 0x10000;
const u32 NUM_MMIOS = NUM_BLOCKS * BLOCK_SIZE;

// Compute the internal unique ID for a given MMIO address. This ID is computed
// from a very simple formula: (1 + block_id) * lower_16_bits(address).
//
// The block ID can easily be computed by simply checking bit 24 (CC vs. CD).
inline u32 UniqueID(u32 address)
{
_dbg_assert_msg_(MEMMAP, ((address & 0xFFFF0000) == 0xCC000000) ||
((address & 0xFFFF0000) == 0xCD000000) ||
((address & 0xFFFF0000) == 0xCD800000),
"Trying to get the ID of a non-existing MMIO address.");

return (1 + ((address >> 24) & 1)) * (address & 0xFFFF);
}

// Some utilities functions to define MMIO mappings.
namespace Utils
{
// Allow grabbing pointers to the high and low part of a 32 bits pointer.
inline u16* LowPart(u32* ptr) { return (u16*)ptr; }
inline u16* LowPart(volatile u32* ptr) { return (u16*)ptr; }
inline u16* HighPart(u32* ptr) { return LowPart(ptr) + 1; }
inline u16* HighPart(volatile u32* ptr) { return LowPart(ptr) + 1; }
}

class Mapping
{
public:
// MMIO registration interface. Use this to register new MMIO handlers.
//
// Example usages can be found in just about any HW/ module in Dolphin's
// codebase.
#define REGISTER_FUNCS(Size) \
void RegisterRead(u32 addr, ReadHandlingMethod<u##Size>* read) \
{ \
u32 id = UniqueID(addr) / sizeof (u##Size); \
m_Read##Size##Handlers[id].ResetMethod(read); \
} \
void RegisterWrite(u32 addr, WriteHandlingMethod<u##Size>* write) \
{ \
u32 id = UniqueID(addr) / sizeof (u##Size); \
m_Write##Size##Handlers[id].ResetMethod(write); \
} \
void Register(u32 addr, ReadHandlingMethod<u##Size>* read, \
WriteHandlingMethod<u##Size>* write) \
{ \
RegisterRead(addr, read); \
RegisterWrite(addr, write); \
}
REGISTER_FUNCS(8) REGISTER_FUNCS(16) REGISTER_FUNCS(32)
#undef REGISTER_FUNCS

// Direct read/write interface.
//
// These functions allow reading/writing an MMIO register at a given
// address. They are used by the Memory:: access functions, which are
// called in interpreter mode, from Dolphin's own code, or from JIT'd code
// where the access address could not be predicted.
//
// Note that for reads we cannot simply return the read value because C++
// allows overloading only with parameter types, not return types.
#define READ_FUNC(Size) \
void Read(u32 addr, u##Size* val) const \
{ \
u32 id = UniqueID(addr) / sizeof (u##Size); \
*val = m_Read##Size##Handlers[id].Read(addr); \
}
READ_FUNC(8) READ_FUNC(16) READ_FUNC(32)
#undef READ_FUNC

#define WRITE_FUNC(Size) \
void Write(u32 addr, u##Size val) const \
{ \
u32 id = UniqueID(addr) / sizeof (u##Size); \
m_Write##Size##Handlers[id].Write(addr, val); \
}
WRITE_FUNC(8) WRITE_FUNC(16) WRITE_FUNC(32)
#undef WRITE_FUNC

// Handlers access interface.
//
// Use when you care more about how to access the MMIO register for an
// address than the current value of that register. For example, this is
// what could be used to implement fast MMIO accesses in Dolphin's JIT.
//
// Two variants of each GetHandler function are provided: one that returns
// the handler directly and one that has a pointer parameter to return the
// value. This second variant is needed because C++ doesn't do overloads
// based on return type but only based on argument types.
#define GET_HANDLERS_FUNC(Type, Size) \
const Type##Handler<u##Size>& GetHandlerFor##Type##Size(u32 addr) const \
{ \
return m_##Type##Size##Handlers[UniqueID(addr) / sizeof (u##Size)]; \
} \
void GetHandlerFor##Type(u32 addr, const Type##Handler<u##Size>** h) const \
{ \
*h = &GetHandlerFor##Type##Size(addr); \
}
GET_HANDLERS_FUNC(Read, 8) GET_HANDLERS_FUNC(Read, 16) GET_HANDLERS_FUNC(Read, 32)
GET_HANDLERS_FUNC(Write, 8) GET_HANDLERS_FUNC(Write, 16) GET_HANDLERS_FUNC(Write, 32)
#undef GET_HANDLERS_FUNC

// Dummy 64 bits variants of these functions. While 64 bits MMIO access is
// not supported, we need these in order to make the code compile.
void Read(u32 addr, u64* val) const { _dbg_assert_(MEMMAP, 0); }
void Write(u32 addr, u64 val) const { _dbg_assert_(MEMMAP, 0); }

private:
// These arrays contain the handlers for each MMIO access type: read/write
// to 8/16/32 bits. They are indexed using the UniqueID(addr) function
// defined earlier, which maps an MMIO address to a unique ID by using the
// MMIO block ID.
//
// Each array contains NUM_MMIOS / sizeof (AccessType) because larger
// access types mean less possible adresses (assuming aligned only
// accesses).
#define HANDLERS(Size) \
std::array<ReadHandler<u##Size>, NUM_MMIOS / sizeof (u##Size)> m_Read##Size##Handlers; \
std::array<WriteHandler<u##Size>, NUM_MMIOS / sizeof (u##Size)> m_Write##Size##Handlers;
HANDLERS(8) HANDLERS(16) HANDLERS(32)
#undef HANDLERS
};

}
201 changes: 201 additions & 0 deletions Source/Core/Core/HW/MMIOHandlers.h
@@ -0,0 +1,201 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.

#pragma once

#include "Common.h"

#include <functional>
#include <memory>

// All the templated and very repetitive MMIO-related code is isolated in this
// file for easier reading. It mostly contains code related to handling methods
// (including the declaration of the public functions for creating handling
// method objects), visitors for these handling methods, and interface of the
// handler classes.
//
// This code is very genericized (aka. lots of templates) in order to handle
// u8/u16/u32 with the same code while providing type safety: it is impossible
// to mix code from these types, and the type system enforces it.

namespace MMIO
{

class Mapping;

// Read and write handling methods are separated for type safety. On top of
// that, some handling methods require different arguments for reads and writes
// (Complex, for example).
template <typename T> class ReadHandlingMethod;
template <typename T> class WriteHandlingMethod;

// Constant: use when the value read on this MMIO is always the same. This is
// only for reads.
template <typename T> ReadHandlingMethod<T>* Constant(T value);

// Nop: use for writes that shouldn't have any effect and shouldn't log an
// error either.
template <typename T> WriteHandlingMethod<T>* Nop();

// Direct: use when all the MMIO does is read/write the given value to/from a
// global variable, with an optional mask applied on the read/written value.
template <typename T> ReadHandlingMethod<T>* DirectRead(const T* addr, u32 mask = 0xFFFFFFFF);
template <typename T> ReadHandlingMethod<T>* DirectRead(volatile const T* addr, u32 mask = 0xFFFFFFFF);
template <typename T> WriteHandlingMethod<T>* DirectWrite(T* addr, u32 mask = 0xFFFFFFFF);
template <typename T> WriteHandlingMethod<T>* DirectWrite(volatile T* addr, u32 mask = 0xFFFFFFFF);

// Complex: use when no other handling method fits your needs. These allow you
// to directly provide a function that will be called when a read/write needs
// to be done.
template <typename T> ReadHandlingMethod<T>* ComplexRead(std::function<T(u32)>);
template <typename T> WriteHandlingMethod<T>* ComplexWrite(std::function<void(u32, T)>);

// Invalid: log an error and return -1 in case of a read. These are the default
// handlers set for all MMIO types.
template <typename T> ReadHandlingMethod<T>* InvalidRead();
template <typename T> WriteHandlingMethod<T>* InvalidWrite();

// {Read,Write}To{Smaller,Larger}: these functions are not themselves handling
// methods but will try to combine accesses to two handlers into one new
// handler object.
//
// This is used for example when 32 bit reads have the exact same handling as
// 16 bit. Handlers need to be registered for both 32 and 16, and it would be
// repetitive and unoptimal to require users to write the same handling code in
// both cases. Instead, an MMIO module can simply define all handlers in terms
// of 16 bit reads, then use ReadToSmaller<u32> to convert u32 reads to u16
// reads.
//
// Internally, these size conversion functions have some magic to make the
// combined handlers as fast as possible. For example, if the two underlying
// u16 handlers for a u32 reads are Direct to consecutive memory addresses,
// they can be transformed into a Direct u32 access.
//
// Warning: unlike the other handling methods, *ToSmaller are obviously not
// available for u8, and *ToLarger are not available for u32.
template <typename T> ReadHandlingMethod<T>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr);
template <typename T> WriteHandlingMethod<T>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr);
template <typename T> ReadHandlingMethod<T>* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift);

// Use these visitors interfaces if you need to write code that performs
// different actions based on the handling method used by a handler. Write your
// visitor implementing that interface, then use handler->VisitHandlingMethod
// to run the proper function.
template <typename T>
class ReadHandlingMethodVisitor
{
public:
virtual void VisitConstant(T value) = 0;
virtual void VisitDirect(const T* addr, u32 mask) = 0;
virtual void VisitComplex(std::function<T(u32)> lambda) = 0;
};
template <typename T>
class WriteHandlingMethodVisitor
{
public:
virtual void VisitNop() = 0;
virtual void VisitDirect(T* addr, u32 mask) = 0;
virtual void VisitComplex(std::function<void(u32, T)> lambda) = 0;
};

// These classes are INTERNAL. Do not use outside of the MMIO implementation
// code. Unfortunately, because we want to make Read() and Write() fast and
// inlinable, we need to provide some of the implementation of these two
// classes here and can't just use a forward declaration.
template <typename T>
class ReadHandler : public NonCopyable
{
public:
ReadHandler();

// Takes ownership of "method".
ReadHandler(ReadHandlingMethod<T>* method);

~ReadHandler();

// Entry point for read handling method visitors.
void Visit(ReadHandlingMethodVisitor<T>& visitor) const;

T Read(u32 addr) const
{
return m_ReadFunc(addr);
}

// Internal method called when changing the internal method object. Its
// main role is to make sure the read function is updated at the same time.
void ResetMethod(ReadHandlingMethod<T>* method);

private:
std::unique_ptr<ReadHandlingMethod<T>> m_Method;
std::function<T(u32)> m_ReadFunc;
};
template <typename T>
class WriteHandler : public NonCopyable
{
public:
WriteHandler();

// Takes ownership of "method".
WriteHandler(WriteHandlingMethod<T>* method);

~WriteHandler();

// Entry point for write handling method visitors.
void Visit(WriteHandlingMethodVisitor<T>& visitor) const;

void Write(u32 addr, T val) const
{
m_WriteFunc(addr, val);
}

// Internal method called when changing the internal method object. Its
// main role is to make sure the write function is updated at the same
// time.
void ResetMethod(WriteHandlingMethod<T>* method);

private:
std::unique_ptr<WriteHandlingMethod<T>> m_Method;
std::function<void(u32, T)> m_WriteFunc;
};

// Boilerplate boilerplate boilerplate.
//
// This is used to be able to avoid putting the templates implementation in the
// header files and slow down compilation times. Instead, we declare 3
// specializations in the header file as already implemented in another
// compilation unit: u8, u16, u32.
//
// The "MaybeExtern" is there because that same macro is used for declaration
// (where MaybeExtern = "extern") and definition (MaybeExtern = "").
#define MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, T) \
MaybeExtern template ReadHandlingMethod<T>* Constant<T>(T value); \
MaybeExtern template WriteHandlingMethod<T>* Nop<T>(); \
MaybeExtern template ReadHandlingMethod<T>* DirectRead(const T* addr, u32 mask); \
MaybeExtern template ReadHandlingMethod<T>* DirectRead(volatile const T* addr, u32 mask); \
MaybeExtern template WriteHandlingMethod<T>* DirectWrite(T* addr, u32 mask); \
MaybeExtern template WriteHandlingMethod<T>* DirectWrite(volatile T* addr, u32 mask); \
MaybeExtern template ReadHandlingMethod<T>* ComplexRead<T>(std::function<T(u32)>); \
MaybeExtern template WriteHandlingMethod<T>* ComplexWrite<T>(std::function<void(u32, T)>); \
MaybeExtern template ReadHandlingMethod<T>* InvalidRead<T>(); \
MaybeExtern template WriteHandlingMethod<T>* InvalidWrite<T>(); \
MaybeExtern template class ReadHandler<T>; \
MaybeExtern template class WriteHandler<T>

#define MMIO_SPECIAL_PUBLIC_SPECIALIZATIONS(MaybeExtern) \
MaybeExtern template ReadHandlingMethod<u16>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \
MaybeExtern template ReadHandlingMethod<u32>* ReadToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \
MaybeExtern template WriteHandlingMethod<u16>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \
MaybeExtern template WriteHandlingMethod<u32>* WriteToSmaller(Mapping* mmio, u32 high_part_addr, u32 low_part_addr); \
MaybeExtern template ReadHandlingMethod<u8>* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift); \
MaybeExtern template ReadHandlingMethod<u16>* ReadToLarger(Mapping* mmio, u32 larger_addr, u32 shift)

#define MMIO_PUBLIC_SPECIALIZATIONS(MaybeExtern) \
MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u8); \
MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u16); \
MMIO_GENERIC_PUBLIC_SPECIALIZATIONS(MaybeExtern, u32); \
MMIO_SPECIAL_PUBLIC_SPECIALIZATIONS(MaybeExtern);

MMIO_PUBLIC_SPECIALIZATIONS(extern)

}
247 changes: 26 additions & 221 deletions Source/Core/Core/HW/Memmap.cpp
Expand Up @@ -29,11 +29,11 @@
#include "EXI.h"
#include "AudioInterface.h"
#include "MemoryInterface.h"
#include "WII_IOB.h"
#include "WII_IPC.h"
#include "../ConfigManager.h"
#include "../Debugger/Debugger_SymbolMap.h"
#include "VideoBackendBase.h"
#include "MMIO.h"

namespace Memory
{
Expand Down Expand Up @@ -83,230 +83,32 @@ u8 *m_pVirtualUncachedEXRAM; // wii only
u8 *m_pVirtualL1Cache;
u8 *m_pVirtualFakeVMEM;

// =================================
// Read and write shortcuts
// ----------------
writeFn8 hwWrite8 [NUMHWMEMFUN];
writeFn16 hwWrite16[NUMHWMEMFUN];
writeFn32 hwWrite32[NUMHWMEMFUN];
writeFn64 hwWrite64[NUMHWMEMFUN];

readFn8 hwRead8 [NUMHWMEMFUN];
readFn16 hwRead16[NUMHWMEMFUN];
readFn32 hwRead32[NUMHWMEMFUN];
readFn64 hwRead64[NUMHWMEMFUN];

writeFn8 hwWriteWii8 [NUMHWMEMFUN];
writeFn16 hwWriteWii16[NUMHWMEMFUN];
writeFn32 hwWriteWii32[NUMHWMEMFUN];
writeFn64 hwWriteWii64[NUMHWMEMFUN];

readFn8 hwReadWii8 [NUMHWMEMFUN];
readFn16 hwReadWii16[NUMHWMEMFUN];
readFn32 hwReadWii32[NUMHWMEMFUN];
readFn64 hwReadWii64[NUMHWMEMFUN];

// Default read and write functions
template <class T>
void HW_Default_Write(const T _Data, const u32 _Address){ ERROR_LOG(MASTER_LOG, "Illegal HW Write%lu %08x", (unsigned long)sizeof(T)*8, _Address);_dbg_assert_(MEMMAP, 0);}

template <class T>
void HW_Default_Read(T _Data, const u32 _Address){ ERROR_LOG(MASTER_LOG, "Illegal HW Read%lu %08x", (unsigned long)sizeof(T)*8, _Address); _dbg_assert_(MEMMAP, 0);}

#define HW_PAGE_SHIFT 10
#define HW_PAGE_SIZE (1 << HW_PAGE_SHIFT)
#define HW_PAGE_MASK (HW_PAGE_SHIFT - 1)

template <class T, u8 *P> void HW_Read_Memory(T &_Data, const u32 _Address)
{
_Data = *(T *)&P[_Address & HW_PAGE_MASK];
}
// MMIO mapping object.
MMIO::Mapping* mmio_mapping;

template <class T, u8 *P> void HW_Write_Memory(T _Data, const u32 _Address)
void InitMMIO(MMIO::Mapping* mmio)
{
*(T *)&P[_Address & HW_PAGE_MASK] = _Data;
g_video_backend->RegisterCPMMIO(mmio, 0xCC000000);
g_video_backend->RegisterPEMMIO(mmio, 0xCC001000);
VideoInterface::RegisterMMIO(mmio, 0xCC002000);
ProcessorInterface::RegisterMMIO(mmio, 0xCC003000);
MemoryInterface::RegisterMMIO(mmio, 0xCC004000);
DSP::RegisterMMIO(mmio, 0xCC005000);
DVDInterface::RegisterMMIO(mmio, 0xCC006000);
SerialInterface::RegisterMMIO(mmio, 0xCC006400);
ExpansionInterface::RegisterMMIO(mmio, 0xCC006800);
AudioInterface::RegisterMMIO(mmio, 0xCC006C00);
}

// Create shortcuts to the hardware devices' read and write functions.
// This can be seen as an alternative to a switch() or if() table.
#define BLOCKSIZE 4
#define CP_START 0x00 //0x0000 >> 10
#define WII_IPC_START 0x00 //0x0000 >> 10
#define PE_START 0x04 //0x1000 >> 10
#define VI_START 0x08 //0x2000 >> 10
#define PI_START 0x0C //0x3000 >> 10
#define MI_START 0x10 //0x4000 >> 10
#define DSP_START 0x14 //0x5000 >> 10
#define DVD_START 0x18 //0x6000 >> 10
#define SI_START 0x19 //0x6400 >> 10
#define EI_START 0x1A //0x6800 >> 10
#define AUDIO_START 0x1B //0x6C00 >> 10
#define GP_START 0x20 //0x8000 >> 10

void InitHWMemFuncs()
void InitMMIOWii(MMIO::Mapping* mmio)
{
for (int i = 0; i < NUMHWMEMFUN; i++)
{
hwWrite8 [i] = HW_Default_Write<u8>;
hwWrite16[i] = HW_Default_Write<u16>;
hwWrite32[i] = HW_Default_Write<u32>;
hwWrite64[i] = HW_Default_Write<u64>;
hwRead8 [i] = HW_Default_Read<u8&>;
hwRead16 [i] = HW_Default_Read<u16&>;
hwRead32 [i] = HW_Default_Read<u32&>;
hwRead64 [i] = HW_Default_Read<u64&>;

// To prevent Dolphin from crashing when accidentally running Wii
// executables in GC mode (or running malicious GC executables...)
hwWriteWii8 [i] = HW_Default_Write<u8>;
hwWriteWii16[i] = HW_Default_Write<u16>;
hwWriteWii32[i] = HW_Default_Write<u32>;
hwWriteWii64[i] = HW_Default_Write<u64>;
hwReadWii8 [i] = HW_Default_Read<u8&>;
hwReadWii16 [i] = HW_Default_Read<u16&>;
hwReadWii32 [i] = HW_Default_Read<u32&>;
hwReadWii64 [i] = HW_Default_Read<u64&>;
}

for (int i = 0; i < BLOCKSIZE; i++)
{
hwRead16 [CP_START+i] = g_video_backend->Video_CPRead16();
hwWrite16[CP_START+i] = g_video_backend->Video_CPWrite16();

hwRead16 [PE_START+i] = g_video_backend->Video_PERead16();
hwWrite16[PE_START+i] = g_video_backend->Video_PEWrite16();
hwWrite32[PE_START+i] = g_video_backend->Video_PEWrite32();

hwRead8 [VI_START+i] = VideoInterface::Read8;
hwRead16 [VI_START+i] = VideoInterface::Read16;
hwRead32 [VI_START+i] = VideoInterface::Read32;
hwWrite16[VI_START+i] = VideoInterface::Write16;
hwWrite32[VI_START+i] = VideoInterface::Write32;

hwRead16 [PI_START+i] = ProcessorInterface::Read16;
hwRead32 [PI_START+i] = ProcessorInterface::Read32;
hwWrite32[PI_START+i] = ProcessorInterface::Write32;

hwRead16 [MI_START+i] = MemoryInterface::Read16;
hwRead32 [MI_START+i] = MemoryInterface::Read32;
hwWrite32[MI_START+i] = MemoryInterface::Write32;
hwWrite16[MI_START+i] = MemoryInterface::Write16;

hwRead16 [DSP_START+i] = DSP::Read16;
hwWrite16[DSP_START+i] = DSP::Write16;
hwRead32 [DSP_START+i] = DSP::Read32;
hwWrite32[DSP_START+i] = DSP::Write32;
}

hwRead32 [DVD_START] = DVDInterface::Read32;
hwWrite32[DVD_START] = DVDInterface::Write32;

hwRead32 [SI_START] = SerialInterface::Read32;
hwWrite32[SI_START] = SerialInterface::Write32;

hwRead32 [EI_START] = ExpansionInterface::Read32;
hwWrite32[EI_START] = ExpansionInterface::Write32;
InitMMIO(mmio);

hwRead32 [AUDIO_START] = AudioInterface::Read32;
hwWrite32[AUDIO_START] = AudioInterface::Write32;

hwWrite8 [GP_START] = GPFifo::Write8;
hwWrite16[GP_START] = GPFifo::Write16;
hwWrite32[GP_START] = GPFifo::Write32;
hwWrite64[GP_START] = GPFifo::Write64;
}


void InitHWMemFuncsWii()
{
for (int i = 0; i < NUMHWMEMFUN; i++)
{
hwWrite8 [i] = HW_Default_Write<u8>;
hwWrite16[i] = HW_Default_Write<u16>;
hwWrite32[i] = HW_Default_Write<u32>;
hwWrite64[i] = HW_Default_Write<u64>;
hwRead8 [i] = HW_Default_Read<u8&>;
hwRead16 [i] = HW_Default_Read<u16&>;
hwRead32 [i] = HW_Default_Read<u32&>;
hwRead64 [i] = HW_Default_Read<u64&>;

hwWriteWii8 [i] = HW_Default_Write<u8>;
hwWriteWii16[i] = HW_Default_Write<u16>;
hwWriteWii32[i] = HW_Default_Write<u32>;
hwWriteWii64[i] = HW_Default_Write<u64>;
hwReadWii8 [i] = HW_Default_Read<u8&>;
hwReadWii16 [i] = HW_Default_Read<u16&>;
hwReadWii32 [i] = HW_Default_Read<u32&>;
hwReadWii64 [i] = HW_Default_Read<u64&>;
}

// MI, PI, DSP are still mapped to 0xCCxxxxxx
for (int i = 0; i < BLOCKSIZE; i++)
{
hwRead16 [CP_START+i] = g_video_backend->Video_CPRead16();
hwWrite16[CP_START+i] = g_video_backend->Video_CPWrite16();

hwRead16 [PE_START+i] = g_video_backend->Video_PERead16();
hwWrite16[PE_START+i] = g_video_backend->Video_PEWrite16();
hwWrite32[PE_START+i] = g_video_backend->Video_PEWrite32();

hwRead16 [PI_START+i] = ProcessorInterface::Read16;
hwRead32 [PI_START+i] = ProcessorInterface::Read32;
hwWrite32[PI_START+i] = ProcessorInterface::Write32;

hwRead8 [VI_START+i] = VideoInterface::Read8;
hwRead16 [VI_START+i] = VideoInterface::Read16;
hwRead32 [VI_START+i] = VideoInterface::Read32;
hwWrite16[VI_START+i] = VideoInterface::Write16;
hwWrite32[VI_START+i] = VideoInterface::Write32;

hwRead16 [MI_START+i] = MemoryInterface::Read16;
hwRead32 [MI_START+i] = MemoryInterface::Read32;
hwWrite32[MI_START+i] = MemoryInterface::Write32;
hwWrite16[MI_START+i] = MemoryInterface::Write16;

hwRead16 [DSP_START+i] = DSP::Read16;
hwWrite16[DSP_START+i] = DSP::Write16;
hwRead32 [DSP_START+i] = DSP::Read32;
hwWrite32[DSP_START+i] = DSP::Write32;
}

hwWrite8 [GP_START] = GPFifo::Write8;
hwWrite16[GP_START] = GPFifo::Write16;
hwWrite32[GP_START] = GPFifo::Write32;
hwWrite64[GP_START] = GPFifo::Write64;

for (int i = 0; i < BLOCKSIZE; i++)
{
hwReadWii32[WII_IPC_START+i] = WII_IPCInterface::Read32;
hwWriteWii32[WII_IPC_START+i] = WII_IPCInterface::Write32;
}

hwRead32 [DVD_START] = DVDInterface::Read32;
hwReadWii32 [DVD_START] = DVDInterface::Read32;
hwWrite32 [DVD_START] = DVDInterface::Write32;
hwWriteWii32[DVD_START] = DVDInterface::Write32;

hwRead32 [SI_START] = SerialInterface::Read32;
hwReadWii32 [SI_START] = SerialInterface::Read32;
hwWrite32 [SI_START] = SerialInterface::Write32;
hwWriteWii32[SI_START] = SerialInterface::Write32;

hwRead32 [EI_START] = ExpansionInterface::Read32;
hwReadWii32 [EI_START] = ExpansionInterface::Read32;
hwWrite32 [EI_START] = ExpansionInterface::Write32;
hwWriteWii32[EI_START] = ExpansionInterface::Write32;

// [F|RES] i thought this doesn't exist anymore
hwRead32 [AUDIO_START] = AudioInterface::Read32;
hwReadWii32 [AUDIO_START] = AudioInterface::Read32;
hwWrite32 [AUDIO_START] = AudioInterface::Write32;
hwWriteWii32[AUDIO_START] = AudioInterface::Write32;
}

writeFn32 GetHWWriteFun32(const u32 _Address)
{
return hwWrite32[(_Address >> HWSHIFT) & (NUMHWMEMFUN-1)];
WII_IPCInterface::RegisterMMIO(mmio, 0xCD000000);
DVDInterface::RegisterMMIO(mmio, 0xCD006000);
SerialInterface::RegisterMMIO(mmio, 0xCD006400);
ExpansionInterface::RegisterMMIO(mmio, 0xCD006800);
AudioInterface::RegisterMMIO(mmio, 0xCD006C00);
}

bool IsInitialized()
Expand Down Expand Up @@ -348,10 +150,12 @@ void Init()
if (bFakeVMEM) flags |= MV_FAKE_VMEM;
base = MemoryMap_Setup(views, num_views, flags, &g_arena);

mmio_mapping = new MMIO::Mapping();

if (wii)
InitHWMemFuncsWii();
InitMMIOWii(mmio_mapping);
else
InitHWMemFuncs();
InitMMIO(mmio_mapping);

INFO_LOG(MEMMAP, "Memory system initialized. RAM at %p (mirrors at 0 @ %p, 0x80000000 @ %p , 0xC0000000 @ %p)",
m_pRAM, m_pPhysicalRAM, m_pVirtualCachedRAM, m_pVirtualUncachedRAM);
Expand Down Expand Up @@ -382,6 +186,7 @@ void Shutdown()
MemoryMap_Shutdown(views, num_views, flags, &g_arena);
g_arena.ReleaseSpace();
base = NULL;
delete mmio_mapping;
INFO_LOG(MEMMAP, "Memory system shut down.");
}

Expand Down
18 changes: 4 additions & 14 deletions Source/Core/Core/HW/Memmap.h
Expand Up @@ -28,16 +28,7 @@

// Global declarations
class PointerWrap;

typedef void (*writeFn8 )(const u8, const u32);
typedef void (*writeFn16)(const u16,const u32);
typedef void (*writeFn32)(const u32,const u32);
typedef void (*writeFn64)(const u64,const u32);

typedef void (*readFn8 )(u8&, const u32);
typedef void (*readFn16)(u16&, const u32);
typedef void (*readFn32)(u32&, const u32);
typedef void (*readFn64)(u64&, const u32);
namespace MMIO { class Mapping; }

namespace Memory
{
Expand Down Expand Up @@ -83,6 +74,9 @@ enum
#endif
};

// MMIO mapping object.
extern MMIO::Mapping* mmio_mapping;

// Init and Shutdown
bool IsInitialized();
void Init();
Expand All @@ -99,11 +93,7 @@ u32 ReadUnchecked_U32(const u32 _Address);
void WriteUnchecked_U8(const u8 _Data, const u32 _Address);
void WriteUnchecked_U32(const u32 _Data, const u32 _Address);

void InitHWMemFuncs();
void InitHWMemFuncsWii();

bool IsRAMAddress(const u32 addr, bool allow_locked_cache = false, bool allow_fake_vmem = false);
writeFn32 GetHWWriteFun32(const u32 _Address);

inline u8* GetCachePtr() {return m_pL1Cache;}
inline u8* GetMainRAMPtr() {return m_pRAM;}
Expand Down
96 changes: 4 additions & 92 deletions Source/Core/Core/HW/MemmapFunctions.cpp
Expand Up @@ -20,10 +20,10 @@

#include "GPFifo.h"
#include "Memmap.h"
#include "WII_IOB.h"
#include "../Core.h"
#include "../PowerPC/PowerPC.h"
#include "VideoBackendBase.h"
#include "MMIO.h"

#ifdef USE_GDBSTUB
#include "../PowerPC/GDBStub.h"
Expand Down Expand Up @@ -61,28 +61,6 @@ extern u8 *m_pEFB;
extern bool m_IsInitialized;
extern bool bFakeVMEM;

// Read and write shortcuts

// It appears that some clever games use stfd to write 64 bits to the fifo. Hence the hwWrite64.

extern writeFn8 hwWrite8 [NUMHWMEMFUN];
extern writeFn16 hwWrite16[NUMHWMEMFUN];
extern writeFn32 hwWrite32[NUMHWMEMFUN];
extern writeFn64 hwWrite64[NUMHWMEMFUN];

extern readFn8 hwRead8 [NUMHWMEMFUN];
extern readFn16 hwRead16[NUMHWMEMFUN];
extern readFn32 hwRead32[NUMHWMEMFUN];

extern writeFn8 hwWriteWii8 [NUMHWMEMFUN];
extern writeFn16 hwWriteWii16[NUMHWMEMFUN];
extern writeFn32 hwWriteWii32[NUMHWMEMFUN];
extern writeFn64 hwWriteWii64[NUMHWMEMFUN];

extern readFn8 hwReadWii8 [NUMHWMEMFUN];
extern readFn16 hwReadWii16[NUMHWMEMFUN];
extern readFn32 hwReadWii32[NUMHWMEMFUN];

// Overloaded byteswap functions, for use within the templated functions below.
inline u8 bswap(u8 val) {return val;}
inline u16 bswap(u16 val) {return Common::swap16(val);}
Expand All @@ -91,42 +69,6 @@ inline u64 bswap(u64 val) {return Common::swap64(val);}
// =================


// Read and write
// ----------------
// The read and write macros that direct us to the right functions

// All these little inline functions are needed because we can't paste symbols together in templates
// like we can in macros.
inline void hwRead(u8 &var, u32 addr) {hwRead8 [(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
inline void hwRead(u16 &var, u32 addr) {hwRead16[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
inline void hwRead(u32 &var, u32 addr) {hwRead32[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
inline void hwRead(u64 &var, u32 addr) {PanicAlert("hwRead: There's no 64-bit HW read. %08x", addr);}

inline void hwWrite(u8 var, u32 addr) {hwWrite8[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
inline void hwWrite(u16 var, u32 addr) {hwWrite16[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
inline void hwWrite(u32 var, u32 addr) {hwWrite32[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
inline void hwWrite(u64 var, u32 addr) {hwWrite64[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}

inline void hwReadWii(u8 &var, u32 addr) {hwReadWii8 [(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
inline void hwReadWii(u16 &var, u32 addr) {hwReadWii16[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
inline void hwReadWii(u32 &var, u32 addr) {hwReadWii32[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
inline void hwReadWii(u64 &var, u32 addr) {PanicAlert("hwReadWii: There's no 64-bit HW read. %08x", addr);}

inline void hwWriteWii(u8 var, u32 addr) {hwWriteWii8[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
inline void hwWriteWii(u16 var, u32 addr) {hwWriteWii16[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
inline void hwWriteWii(u32 var, u32 addr) {hwWriteWii32[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}
inline void hwWriteWii(u64 var, u32 addr) {hwWriteWii64[(addr>>HWSHIFT) & (NUMHWMEMFUN-1)](var, addr);}

inline void hwReadIOBridge(u8 &var, u32 addr) {WII_IOBridge::Read8(var, addr);}
inline void hwReadIOBridge(u16 &var, u32 addr) {WII_IOBridge::Read16(var, addr);}
inline void hwReadIOBridge(u32 &var, u32 addr) {WII_IOBridge::Read32(var, addr);}
inline void hwReadIOBridge(u64 &var, u32 addr) {PanicAlert("hwReadIOBridge: There's no 64-bit HW read. %08x", addr);}

inline void hwWriteIOBridge(u8 var, u32 addr) {WII_IOBridge::Write8(var, addr);}
inline void hwWriteIOBridge(u16 var, u32 addr) {WII_IOBridge::Write16(var, addr);}
inline void hwWriteIOBridge(u32 var, u32 addr) {WII_IOBridge::Write32(var, addr);}
inline void hwWriteIOBridge(u64 var, u32 addr) {PanicAlert("hwWriteIOBridge: There's no 64-bit HW write. %08x", addr);}

// Nasty but necessary. Super Mario Galaxy pointer relies on this stuff.
u32 EFB_Read(const u32 addr)
{
Expand Down Expand Up @@ -155,20 +97,8 @@ inline void ReadFromHardware(T &_var, const u32 em_address, const u32 effective_
{
if (em_address < 0xcc000000)
_var = EFB_Read(em_address);
else if (em_address <= 0xcc009000)
hwRead(_var, em_address);
/* WIIMODE */
else if (((em_address & 0xFF000000) == 0xCD000000) &&
(em_address <= 0xcd009000))
hwReadWii(_var, em_address);
else if (((em_address & 0xFFF00000) == 0xCD800000) &&
(em_address <= 0xCD809000))
hwReadIOBridge(_var, em_address);
else
{
/* Disabled because the debugger makes trouble with */
/*_dbg_assert_(MEMMAP,0); */
}
mmio_mapping->Read(em_address, &_var);
}
else if (((em_address & 0xF0000000) == 0x80000000) ||
((em_address & 0xF0000000) == 0xC0000000) ||
Expand Down Expand Up @@ -245,28 +175,10 @@ inline void WriteToHardware(u32 em_address, const T data, u32 effective_address,
}
return;
}
else if (em_address <= 0xcc009000)
{
hwWrite(data, em_address);
return;
}
/* WIIMODE */
else if (((em_address & 0xFF000000) == 0xCD000000) &&
(em_address <= 0xcd009000))
{
hwWriteWii(data,em_address);
return;
}
else if (((em_address & 0xFFF00000) == 0xCD800000) &&
(em_address <= 0xCD809000))
{
hwWriteIOBridge(data,em_address);
return;
}
else
{
ERROR_LOG(MEMMAP, "hwwrite [%08x] := %08x (PC: %08x)", em_address, (u32)data, PC);
_dbg_assert_msg_(MEMMAP,0,"Memory - Unknown HW address %08x", em_address);
mmio_mapping->Write(em_address, data);
return;
}
}
else if (((em_address & 0xF0000000) == 0x80000000) ||
Expand Down
221 changes: 177 additions & 44 deletions Source/Core/Core/HW/MemoryInterface.cpp
Expand Up @@ -7,74 +7,207 @@

#include "../PowerPC/PowerPC.h"
#include "MemoryInterface.h"
#include "MMIO.h"

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,
};

struct MIMemStruct
union MIRegion
{
u32 Channel0_Addr;
u32 Channel1_Addr;
u32 Channel2_Addr;
u32 Channel3_Addr;
u32 Channel_Ctrl;
u32 hex;
struct { u16 first_page; u16 last_page; };
};

// STATE_TO_SAVE
static MIMemStruct miMem;
union MIProtType
{
u16 hex;
struct
{
u16 reg0 : 2;
u16 reg1 : 2;
u16 reg2 : 2;
u16 reg3 : 2;
u16 : 8;
};
};

void DoState(PointerWrap &p)
union MIIRQMask
{
p.Do(miMem);
}
u16 hex;
struct
{
u16 reg0 : 1;
u16 reg1 : 1;
u16 reg2 : 1;
u16 reg3 : 1;
u16 all_regs : 1;
u16 : 11;
};
};

void Read16(u16& _uReturnValue, const u32 _iAddress)
union MIIRQFlag
{
//0x30 -> 0x5a : gp memory metrics
INFO_LOG(MEMMAP, "(r16) 0x%04x @ 0x%08x", 0, _iAddress);
_uReturnValue = 0;
}
u16 hex;
struct
{
u16 reg0 : 1;
u16 reg1 : 1;
u16 reg2 : 1;
u16 reg3 : 1;
u16 all_regs : 1;
u16 : 11;
};
};

void Read32(u32& _uReturnValue, const u32 _iAddress)
union MIProtAddr
{
INFO_LOG(MEMMAP, "(r32) 0x%08x @ 0x%08x", 0, _iAddress);
_uReturnValue = 0;
}
u32 hex;
struct { u16 lo; u16 hi; };
struct
{
u32 : 5;
u32 addr : 25;
u32 : 2;
};
};

void Write32(const u32 _iValue, const u32 _iAddress)
union MITimer
{
u32 hex;
struct { u16 lo; u16 hi; };
};

struct MIMemStruct
{
INFO_LOG(MEMMAP, "(w32) 0x%08x @ 0x%08x", _iValue, _iAddress);
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 g_mi_mem;

void DoState(PointerWrap &p)
{
p.Do(g_mi_mem);
}

//TODO : check
void Write16(const u16 _iValue, const u32 _iAddress)
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
{
INFO_LOG(MEMMAP, "(w16) 0x%04x @ 0x%08x", _iValue, _iAddress);
switch(_iAddress & 0xFFF)
for (int i = 0; i < MI_REGION0_FIRST; i += 4)
{
auto& region = g_mi_mem.regions[i / 4];
mmio->Register(base | i,
MMIO::DirectRead<u16>(&region.first_page),
MMIO::DirectWrite<u16>(&region.first_page)
);
mmio->Register(base | (i + 2),
MMIO::DirectRead<u16>(&region.last_page),
MMIO::DirectWrite<u16>(&region.last_page)
);
}

mmio->Register(base | MI_PROT_TYPE,
MMIO::DirectRead<u16>(&g_mi_mem.prot_type.hex),
MMIO::DirectWrite<u16>(&g_mi_mem.prot_type.hex)
);

mmio->Register(base | MI_IRQMASK,
MMIO::DirectRead<u16>(&g_mi_mem.irq_mask.hex),
MMIO::DirectWrite<u16>(&g_mi_mem.irq_mask.hex)
);

mmio->Register(base | MI_IRQFLAG,
MMIO::DirectRead<u16>(&g_mi_mem.irq_flag.hex),
MMIO::DirectWrite<u16>(&g_mi_mem.irq_flag.hex)
);

mmio->Register(base | MI_UNKNOWN1,
MMIO::DirectRead<u16>(&g_mi_mem.unknown1),
MMIO::DirectWrite<u16>(&g_mi_mem.unknown1)
);

// The naming is confusing here: the registed contains the lower part of
// the address (hence MI_..._LO but this is still the high part of the
// overall register.
mmio->Register(base | MI_PROT_ADDR_LO,
MMIO::DirectRead<u16>(&g_mi_mem.prot_addr.hi),
MMIO::DirectWrite<u16>(&g_mi_mem.prot_addr.hi)
);
mmio->Register(base | MI_PROT_ADDR_HI,
MMIO::DirectRead<u16>(&g_mi_mem.prot_addr.lo),
MMIO::DirectWrite<u16>(&g_mi_mem.prot_addr.lo)
);

for (int i = 0; i < 10; ++i)
{
auto& timer = g_mi_mem.timers[i];
mmio->Register(base | (MI_TIMER0_HI + 4 * i),
MMIO::DirectRead<u16>(&timer.hi),
MMIO::DirectWrite<u16>(&timer.hi)
);
mmio->Register(base | (MI_TIMER0_LO + 4 * i),
MMIO::DirectRead<u16>(&timer.lo),
MMIO::DirectWrite<u16>(&timer.lo)
);
}

mmio->Register(base | MI_UNKNOWN2,
MMIO::DirectRead<u16>(&g_mi_mem.unknown2),
MMIO::DirectWrite<u16>(&g_mi_mem.unknown2)
);

for (int i = 0; i < 0x1000; i += 4)
{
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;
mmio->Register(base | i,
MMIO::ReadToSmaller<u32>(mmio, base | i, base | (i + 2)),
MMIO::WriteToSmaller<u32>(mmio, base | i, base | (i + 2))
);
}
}

Expand Down
7 changes: 3 additions & 4 deletions Source/Core/Core/HW/MemoryInterface.h
Expand Up @@ -5,14 +5,13 @@
#pragma once

#include "Common.h"

namespace MMIO { class Mapping; }
class PointerWrap;

namespace MemoryInterface
{
void DoState(PointerWrap &p);

void Read16(u16& _uReturnValue, const u32 _iAddress);
void Read32(u32& _uReturnValue, const u32 _iAddress);
void Write32(const u32 _iValue, const u32 _iAddress);
void Write16(const u16 _iValue, const u32 _iAddress);
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
} // end of namespace MemoryInterface
165 changes: 60 additions & 105 deletions Source/Core/Core/HW/ProcessorInterface.cpp
Expand Up @@ -14,6 +14,7 @@
#include "ProcessorInterface.h"
#include "GPFifo.h"
#include "VideoBackendBase.h"
#include "MMIO.h"

namespace ProcessorInterface
{
Expand Down Expand Up @@ -90,113 +91,67 @@ void Init()
toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback);
}

void Read16(u16& _uReturnValue, const u32 _iAddress)
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
{
u32 word;
Read32(word, _iAddress & ~3);
_uReturnValue = word >> (_iAddress & 3) ? 16 : 0;
}

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;
}

void Write32(const u32 _uValue, const u32 _iAddress)
{
//INFO_LOG(PROCESSORINTERFACE, "(w32) 0x%08x @ 0x%08x", _uValue, _iAddress);
switch(_iAddress & 0xFFF)
mmio->Register(base | PI_INTERRUPT_CAUSE,
MMIO::DirectRead<u32>(&m_InterruptCause),
MMIO::ComplexWrite<u32>([](u32, u32 val) {
Common::AtomicAnd(m_InterruptCause, ~val);
UpdateException();
})
);

mmio->Register(base | PI_INTERRUPT_MASK,
MMIO::DirectRead<u32>(&m_InterruptMask),
MMIO::ComplexWrite<u32>([](u32, u32 val) {
m_InterruptMask = val;
UpdateException();
})
);

mmio->Register(base | PI_FIFO_BASE,
MMIO::DirectRead<u32>(&Fifo_CPUBase),
MMIO::DirectWrite<u32>(&Fifo_CPUBase, 0xFFFFFFE0)
);

mmio->Register(base | PI_FIFO_END,
MMIO::DirectRead<u32>(&Fifo_CPUEnd),
MMIO::DirectWrite<u32>(&Fifo_CPUEnd, 0xFFFFFFE0)
);

mmio->Register(base | PI_FIFO_WPTR,
MMIO::DirectRead<u32>(&Fifo_CPUWritePointer),
MMIO::DirectWrite<u32>(&Fifo_CPUWritePointer, 0xFFFFFFE0)
);

mmio->Register(base | PI_FIFO_RESET,
MMIO::InvalidRead<u32>(),
MMIO::ComplexWrite<u32>([](u32, u32 val) {
WARN_LOG(PROCESSORINTERFACE, "Fifo reset (%08x)", val);
})
);

mmio->Register(base | PI_RESET_CODE,
MMIO::DirectRead<u32>(&m_ResetCode),
MMIO::DirectWrite<u32>(&m_ResetCode)
);

mmio->Register(base | PI_FLIPPER_REV,
MMIO::DirectRead<u32>(&m_FlipperRev),
MMIO::InvalidWrite<u32>()
);

// 16 bit reads are based on 32 bit reads.
for (int i = 0; i < 0x1000; i += 4)
{
case PI_INTERRUPT_CAUSE:
Common::AtomicAnd(m_InterruptCause, ~_uValue); // writes turn them off
UpdateException();
return;

case PI_INTERRUPT_MASK:
m_InterruptMask = _uValue;
DEBUG_LOG(PROCESSORINTERFACE,"New Interrupt mask: %08x", m_InterruptMask);
UpdateException();
return;

case PI_FIFO_BASE:
Fifo_CPUBase = _uValue & 0xFFFFFFE0;
DEBUG_LOG(PROCESSORINTERFACE,"Fifo base = %08x", _uValue);
break;

case PI_FIFO_END:
Fifo_CPUEnd = _uValue & 0xFFFFFFE0;
DEBUG_LOG(PROCESSORINTERFACE,"Fifo end = %08x", _uValue);
break;

case PI_FIFO_WPTR:
Fifo_CPUWritePointer = _uValue & 0xFFFFFFE0;
DEBUG_LOG(PROCESSORINTERFACE,"Fifo writeptr = %08x", _uValue);
break;

case PI_FIFO_RESET:
//Abort the actual frame
//g_video_backend->Video_AbortFrame();
//Fifo_CPUWritePointer = Fifo_CPUBase; ??
//PanicAlert("Unknown write to PI_FIFO_RESET (%08x)", _uValue);
WARN_LOG(PROCESSORINTERFACE, "Fifo reset (%08x)", _uValue);
break;

case PI_RESET_CODE:
DEBUG_LOG(PROCESSORINTERFACE, "Write %08x to PI_RESET_CODE", _uValue);
m_ResetCode = _uValue;
break;

case PI_FLIPPER_UNK:
DEBUG_LOG(PROCESSORINTERFACE, "Write %08x to unknown PI register %08x", _uValue, _iAddress);
break;

default:
ERROR_LOG(PROCESSORINTERFACE,"!!!!Unknown PI write!!!! 0x%08x", _iAddress);
PanicAlert("Unknown write to PI: %08x", _iAddress);
break;
mmio->Register(base | i,
MMIO::ReadToLarger<u16>(mmio, base | i, 0),
MMIO::InvalidWrite<u16>()
);
mmio->Register(base | (i + 2),
MMIO::ReadToLarger<u16>(mmio, base | i, 16),
MMIO::InvalidWrite<u16>()
);
}
}

Expand Down
7 changes: 3 additions & 4 deletions Source/Core/Core/HW/ProcessorInterface.h
Expand Up @@ -7,6 +7,8 @@
#include "CommonTypes.h"
class PointerWrap;

namespace MMIO { class Mapping; }

// Holds statuses of things like the write gatherer used for fifos, and interrupts from various sources

namespace ProcessorInterface
Expand Down Expand Up @@ -43,10 +45,7 @@ extern u32 Fifo_CPUWritePointer;
void Init();
void DoState(PointerWrap &p);

void Read16(u16& _uReturnValue, const u32 _iAddress);

void Read32(u32& _uReturnValue, const u32 _iAddress);
void Write32(const u32 _iValue, const u32 _iAddress);
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);

inline u32 GetMask() { return m_InterruptMask; }
inline u32 GetCause() { return m_InterruptCause; }
Expand Down
223 changes: 58 additions & 165 deletions Source/Core/Core/HW/SI.cpp
Expand Up @@ -8,6 +8,7 @@
#include "../CoreTiming.h"
#include "../Movie.h"
#include "../NetPlayProto.h"
#include "MMIO.h"

#include "SystemTimers.h"
#include "ProcessorInterface.h"
Expand Down Expand Up @@ -281,157 +282,56 @@ void Shutdown()
GBAConnectionWaiter_Shutdown();
}

void Read32(u32& _uReturnValue, const u32 _iAddress)
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
{
// SIBuffer
if ((_iAddress >= 0xCC006480 && _iAddress < 0xCC006500) ||
(_iAddress >= 0xCD006480 && _iAddress < 0xCD006500))
// Register SI buffer direct accesses.
for (int i = 0; i < 0x80; i += 4)
mmio->Register(base | (0x80 + i),
MMIO::DirectRead<u32>((u32*)&g_SIBuffer[i]),
MMIO::DirectWrite<u32>((u32*)&g_SIBuffer[i])
);

// In and out for the 4 SI channels.
for (int i = 0; i < MAX_SI_CHANNELS; ++i)
{
_uReturnValue = *(u32*)&g_SIBuffer[_iAddress & 0x7F];
return;
// We need to clear the RDST bit for the SI channel when reading.
// CH0 -> Bit 24 + 5
// CH1 -> Bit 16 + 5
// CH2 -> Bit 8 + 5
// CH3 -> Bit 0 + 5
int rdst_bit = 8 * (3 - i) + 5;

mmio->Register(base | (SI_CHANNEL_0_OUT + 0xC * i),
MMIO::DirectRead<u32>(&g_Channel[i].m_Out.Hex),
MMIO::DirectWrite<u32>(&g_Channel[i].m_Out.Hex)
);
mmio->Register(base | (SI_CHANNEL_0_IN_HI + 0xC * i),
MMIO::ComplexRead<u32>([i, rdst_bit](u32) {
g_StatusReg.Hex &= ~(1 << rdst_bit);
UpdateInterrupts();
return g_Channel[i].m_InHi.Hex;
}),
MMIO::DirectWrite<u32>(&g_Channel[i].m_InHi.Hex)
);
mmio->Register(base | (SI_CHANNEL_0_IN_LO + 0xC * i),
MMIO::ComplexRead<u32>([i, rdst_bit](u32) {
g_StatusReg.Hex &= ~(1 << rdst_bit);
UpdateInterrupts();
return g_Channel[i].m_InLo.Hex;
}),
MMIO::DirectWrite<u32>(&g_Channel[i].m_InLo.Hex)
);
}

// error if not changed in the switch
_uReturnValue = 0xdeadbeef;
mmio->Register(base | SI_POLL,
MMIO::DirectRead<u32>(&g_Poll.Hex),
MMIO::DirectWrite<u32>(&g_Poll.Hex)
);

// registers
switch (_iAddress & 0x3FF)
{
//////////////////////////////////////////////////////////////////////////
// Channel 0
//////////////////////////////////////////////////////////////////////////
case SI_CHANNEL_0_OUT:
_uReturnValue = g_Channel[0].m_Out.Hex;
break;

case SI_CHANNEL_0_IN_HI:
g_StatusReg.RDST0 = 0;
UpdateInterrupts();
_uReturnValue = g_Channel[0].m_InHi.Hex;
break;

case SI_CHANNEL_0_IN_LO:
g_StatusReg.RDST0 = 0;
UpdateInterrupts();
_uReturnValue = g_Channel[0].m_InLo.Hex;
break;

//////////////////////////////////////////////////////////////////////////
// Channel 1
//////////////////////////////////////////////////////////////////////////
case SI_CHANNEL_1_OUT:
_uReturnValue = g_Channel[1].m_Out.Hex;
break;

case SI_CHANNEL_1_IN_HI:
g_StatusReg.RDST1 = 0;
UpdateInterrupts();
_uReturnValue = g_Channel[1].m_InHi.Hex;
break;

case SI_CHANNEL_1_IN_LO:
g_StatusReg.RDST1 = 0;
UpdateInterrupts();
_uReturnValue = g_Channel[1].m_InLo.Hex;
break;

//////////////////////////////////////////////////////////////////////////
// Channel 2
//////////////////////////////////////////////////////////////////////////
case SI_CHANNEL_2_OUT:
_uReturnValue = g_Channel[2].m_Out.Hex;
break;

case SI_CHANNEL_2_IN_HI:
g_StatusReg.RDST2 = 0;
UpdateInterrupts();
_uReturnValue = g_Channel[2].m_InHi.Hex;
break;

case SI_CHANNEL_2_IN_LO:
g_StatusReg.RDST2 = 0;
UpdateInterrupts();
_uReturnValue = g_Channel[2].m_InLo.Hex;
break;

//////////////////////////////////////////////////////////////////////////
// Channel 3
//////////////////////////////////////////////////////////////////////////
case SI_CHANNEL_3_OUT:
_uReturnValue = g_Channel[3].m_Out.Hex;
break;

case SI_CHANNEL_3_IN_HI:
g_StatusReg.RDST3 = 0;
UpdateInterrupts();
_uReturnValue = g_Channel[3].m_InHi.Hex;
break;

case SI_CHANNEL_3_IN_LO:
g_StatusReg.RDST3 = 0;
UpdateInterrupts();
_uReturnValue = g_Channel[3].m_InLo.Hex;
break;

//////////////////////////////////////////////////////////////////////////
// Other
//////////////////////////////////////////////////////////////////////////
case SI_POLL: _uReturnValue = g_Poll.Hex; break;
case SI_COM_CSR: _uReturnValue = g_ComCSR.Hex; break;
case SI_STATUS_REG: _uReturnValue = g_StatusReg.Hex; break;

case SI_EXI_CLOCK_COUNT: _uReturnValue = g_EXIClockCount.Hex; break;

default:
INFO_LOG(SERIALINTERFACE, "(r32-unk): 0x%08x", _iAddress);
_dbg_assert_(SERIALINTERFACE,0);
break;
}

DEBUG_LOG(SERIALINTERFACE, "(r32) 0x%08x - 0x%08x", _iAddress, _uReturnValue);
}

void Write32(const u32 _iValue, const u32 _iAddress)
{
DEBUG_LOG(SERIALINTERFACE, "(w32) 0x%08x @ 0x%08x", _iValue, _iAddress);

// SIBuffer
if ((_iAddress >= 0xCC006480 && _iAddress < 0xCC006500) ||
(_iAddress >= 0xCD006480 && _iAddress < 0xCD006500))
{
*(u32*)&g_SIBuffer[_iAddress & 0x7F] = _iValue;
return;
}

// registers
switch (_iAddress & 0x3FF)
{
case SI_CHANNEL_0_OUT: g_Channel[0].m_Out.Hex = _iValue; break;
case SI_CHANNEL_0_IN_HI: g_Channel[0].m_InHi.Hex = _iValue; break;
case SI_CHANNEL_0_IN_LO: g_Channel[0].m_InLo.Hex = _iValue; break;
case SI_CHANNEL_1_OUT: g_Channel[1].m_Out.Hex = _iValue; break;
case SI_CHANNEL_1_IN_HI: g_Channel[1].m_InHi.Hex = _iValue; break;
case SI_CHANNEL_1_IN_LO: g_Channel[1].m_InLo.Hex = _iValue; break;
case SI_CHANNEL_2_OUT: g_Channel[2].m_Out.Hex = _iValue; break;
case SI_CHANNEL_2_IN_HI: g_Channel[2].m_InHi.Hex = _iValue; break;
case SI_CHANNEL_2_IN_LO: g_Channel[2].m_InLo.Hex = _iValue; break;
case SI_CHANNEL_3_OUT: g_Channel[3].m_Out.Hex = _iValue; break;
case SI_CHANNEL_3_IN_HI: g_Channel[3].m_InHi.Hex = _iValue; break;
case SI_CHANNEL_3_IN_LO: g_Channel[3].m_InLo.Hex = _iValue; break;

case SI_POLL:
INFO_LOG(SERIALINTERFACE, "Wrote Poll: X=%03d Y=%03d %s%s%s%s%s%s%s%s",
g_Poll.X, g_Poll.Y,
g_Poll.EN0 ? "EN0 ":" ", g_Poll.EN1 ? "EN1 ":" ",
g_Poll.EN2 ? "EN2 ":" ", g_Poll.EN3 ? "EN3 ":" ",
g_Poll.VBCPY0 ? "VBCPY0 ":" ", g_Poll.VBCPY1 ? "VBCPY1 ":" ",
g_Poll.VBCPY2 ? "VBCPY2 ":" ", g_Poll.VBCPY3 ? "VBCPY3 ":" ");
g_Poll.Hex = _iValue;
break;

case SI_COM_CSR:
{
USIComCSR tmpComCSR(_iValue);
mmio->Register(base | SI_COM_CSR,
MMIO::DirectRead<u32>(&g_ComCSR.Hex),
MMIO::ComplexWrite<u32>([](u32, u32 val) {
USIComCSR tmpComCSR(val);

g_ComCSR.CHANNEL = tmpComCSR.CHANNEL;
g_ComCSR.INLNGTH = tmpComCSR.INLNGTH;
Expand All @@ -447,12 +347,13 @@ void Write32(const u32 _iValue, const u32 _iAddress)
// be careful: run si-buffer after updating the INT flags
if (tmpComCSR.TSTART) RunSIBuffer();
UpdateInterrupts();
}
break;
})
);

case SI_STATUS_REG:
{
USIStatusReg tmpStatus(_iValue);
mmio->Register(base | SI_STATUS_REG,
MMIO::DirectRead<u32>(&g_StatusReg.Hex),
MMIO::ComplexWrite<u32>([](u32, u32 val) {
USIStatusReg tmpStatus(val);

// clear bits ( if(tmp.bit) SISR.bit=0 )
if (tmpStatus.NOREP0) g_StatusReg.NOREP0 = 0;
Expand Down Expand Up @@ -489,21 +390,13 @@ void Write32(const u32 _iValue, const u32 _iAddress)
g_StatusReg.WRST2 = 0;
g_StatusReg.WRST3 = 0;
}
}
break;

case SI_EXI_CLOCK_COUNT:
g_EXIClockCount.Hex = _iValue;
break;
})
);

case 0x80: // Bogus? never seen it with ma own eyes
INFO_LOG(SERIALINTERFACE, "WII something at 0xCD006480");
break;

default:
_dbg_assert_(SERIALINTERFACE, 0);
break;
}
mmio->Register(base | SI_EXI_CLOCK_COUNT,
MMIO::DirectRead<u32>(&g_EXIClockCount.Hex),
MMIO::DirectWrite<u32>(&g_EXIClockCount.Hex)
);
}

void UpdateInterrupts()
Expand Down
6 changes: 3 additions & 3 deletions Source/Core/Core/HW/SI.h
Expand Up @@ -8,6 +8,7 @@
#include "SI_Device.h"
class PointerWrap;
class ISIDevice;
namespace MMIO { class Mapping; }

// SI number of channels
enum
Expand All @@ -22,6 +23,8 @@ void Init();
void Shutdown();
void DoState(PointerWrap &p);

void RegisterMMIO(MMIO::Mapping* mmio, u32 base);

void UpdateDevices();

void RemoveDevice(int _iDeviceNumber);
Expand All @@ -31,9 +34,6 @@ void AddDevice(ISIDevice* pDevice);
void ChangeDeviceCallback(u64 userdata, int cyclesLate);
void ChangeDevice(SIDevices device, int channel);

void Read32(u32& _uReturnValue, const u32 _iAddress);
void Write32(const u32 _iValue, const u32 _iAddress);

int GetTicksToNextSIPoll();

}; // end of namespace SerialInterface