2013-04-17 22:43:11 -04:00
|
|
|
// Copyright 2013 Dolphin Emulator Project
|
|
|
|
|
// Licensed under GPLv2
|
|
|
|
|
// Refer to the license.txt file included.
|
2008-12-08 05:30:24 +00:00
|
|
|
|
|
|
|
|
#include "Common.h"
|
|
|
|
|
#include "ChunkFile.h"
|
2009-02-02 22:29:33 +00:00
|
|
|
#include "../ConfigManager.h"
|
2009-02-28 21:30:43 +00:00
|
|
|
#include "../CoreTiming.h"
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2009-10-06 15:49:20 +00:00
|
|
|
#include "ProcessorInterface.h"
|
2008-12-08 05:30:24 +00:00
|
|
|
#include "../PowerPC/PowerPC.h"
|
|
|
|
|
|
2009-02-28 21:30:43 +00:00
|
|
|
#include "EXI.h"
|
2010-12-14 02:44:27 +00:00
|
|
|
#include "Sram.h"
|
2013-01-19 15:02:02 -05:00
|
|
|
#include "../Movie.h"
|
2010-12-14 01:25:50 +00:00
|
|
|
SRAM g_SRAM;
|
2008-12-08 05:30:24 +00:00
|
|
|
|
|
|
|
|
namespace ExpansionInterface
|
|
|
|
|
{
|
|
|
|
|
|
2009-02-28 21:30:43 +00:00
|
|
|
static int changeDevice;
|
|
|
|
|
|
2014-01-08 20:34:14 -05:00
|
|
|
CEXIChannel *g_Channels[MAX_EXI_CHANNELS];
|
2008-12-08 05:30:24 +00:00
|
|
|
void Init()
|
|
|
|
|
{
|
2010-12-14 01:25:50 +00:00
|
|
|
initSRAM();
|
2014-01-08 20:34:14 -05:00
|
|
|
for (u32 i = 0; i < MAX_EXI_CHANNELS; i++)
|
2009-10-19 15:14:48 +00:00
|
|
|
g_Channels[i] = new CEXIChannel(i);
|
|
|
|
|
|
2013-01-19 15:02:02 -05:00
|
|
|
if (Movie::IsPlayingInput() && Movie::IsUsingMemcard() && Movie::IsConfigSaved())
|
|
|
|
|
g_Channels[0]->AddDevice(EXIDEVICE_MEMORYCARD, 0); // SlotA
|
|
|
|
|
else if(Movie::IsPlayingInput() && !Movie::IsUsingMemcard() && Movie::IsConfigSaved())
|
|
|
|
|
g_Channels[0]->AddDevice(EXIDEVICE_NONE, 0); // SlotA
|
|
|
|
|
else
|
|
|
|
|
g_Channels[0]->AddDevice(SConfig::GetInstance().m_EXIDevice[0], 0); // SlotA
|
2009-10-19 15:14:48 +00:00
|
|
|
g_Channels[0]->AddDevice(EXIDEVICE_MASKROM, 1);
|
|
|
|
|
g_Channels[0]->AddDevice(SConfig::GetInstance().m_EXIDevice[2], 2); // Serial Port 1
|
|
|
|
|
g_Channels[1]->AddDevice(SConfig::GetInstance().m_EXIDevice[1], 0); // SlotB
|
|
|
|
|
g_Channels[2]->AddDevice(EXIDEVICE_AD16, 0);
|
2009-02-28 21:30:43 +00:00
|
|
|
|
|
|
|
|
changeDevice = CoreTiming::RegisterEvent("ChangeEXIDevice", ChangeDeviceCallback);
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Shutdown()
|
|
|
|
|
{
|
2013-10-29 01:09:01 -04:00
|
|
|
for (auto& channel : g_Channels)
|
2009-10-19 15:14:48 +00:00
|
|
|
{
|
2013-10-29 01:09:01 -04:00
|
|
|
delete channel;
|
|
|
|
|
channel = NULL;
|
2009-10-19 15:14:48 +00:00
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DoState(PointerWrap &p)
|
|
|
|
|
{
|
2013-10-29 01:09:01 -04:00
|
|
|
for (auto& channel : g_Channels)
|
|
|
|
|
channel->DoState(p);
|
2011-12-17 22:33:50 -08:00
|
|
|
}
|
|
|
|
|
|
2011-12-30 20:16:12 -08:00
|
|
|
void PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
|
|
|
|
{
|
2013-10-29 01:09:01 -04:00
|
|
|
for (auto& channel : g_Channels)
|
|
|
|
|
channel->PauseAndLock(doLock, unpauseOnUnlock);
|
2011-12-30 20:16:12 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-02-28 21:30:43 +00:00
|
|
|
void ChangeDeviceCallback(u64 userdata, int cyclesLate)
|
|
|
|
|
{
|
|
|
|
|
u8 channel = (u8)(userdata >> 32);
|
2011-10-09 03:18:15 -07:00
|
|
|
u8 type = (u8)(userdata >> 16);
|
|
|
|
|
u8 num = (u8)userdata;
|
2009-02-28 21:30:43 +00:00
|
|
|
|
2011-10-09 03:18:15 -07:00
|
|
|
g_Channels[channel]->AddDevice((TEXIDevices)type, num);
|
2009-02-28 21:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
2011-10-09 03:18:15 -07:00
|
|
|
void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num)
|
2009-02-28 21:30:43 +00:00
|
|
|
{
|
|
|
|
|
// Called from GUI, so we need to make it thread safe.
|
|
|
|
|
// Let the hardware see no device for .5b cycles
|
2011-10-09 03:18:15 -07:00
|
|
|
CoreTiming::ScheduleEvent_Threadsafe(0, changeDevice, ((u64)channel << 32) | ((u64)EXIDEVICE_NONE << 16) | device_num);
|
|
|
|
|
CoreTiming::ScheduleEvent_Threadsafe(500000000, changeDevice, ((u64)channel << 32) | ((u64)device_type << 16) | device_num);
|
2009-02-28 21:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
2011-12-30 20:16:12 -08:00
|
|
|
IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex)
|
|
|
|
|
{
|
2013-10-29 01:09:01 -04:00
|
|
|
for (auto& channel : g_Channels)
|
2011-12-30 20:16:12 -08:00
|
|
|
{
|
2013-10-29 01:09:01 -04:00
|
|
|
IEXIDevice* device = channel->FindDevice(device_type, customIndex);
|
2011-12-30 20:16:12 -08:00
|
|
|
if (device)
|
|
|
|
|
return device;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-19 15:14:48 +00:00
|
|
|
// Unused (?!)
|
2008-12-08 05:30:24 +00:00
|
|
|
void Update()
|
|
|
|
|
{
|
2009-10-19 15:14:48 +00:00
|
|
|
g_Channels[0]->Update();
|
|
|
|
|
g_Channels[1]->Update();
|
|
|
|
|
g_Channels[2]->Update();
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Read32(u32& _uReturnValue, const u32 _iAddress)
|
|
|
|
|
{
|
2009-10-19 15:14:48 +00:00
|
|
|
// 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;
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2014-01-08 20:34:14 -05:00
|
|
|
_dbg_assert_(EXPANSIONINTERFACE, iChannel < MAX_EXI_CHANNELS);
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2014-01-08 20:34:14 -05:00
|
|
|
if (iChannel < MAX_EXI_CHANNELS)
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
2009-10-19 15:14:48 +00:00
|
|
|
g_Channels[iChannel]->Read32(_uReturnValue, iRegister);
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_uReturnValue = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Write32(const u32 _iValue, const u32 _iAddress)
|
|
|
|
|
{
|
2009-10-19 15:14:48 +00:00
|
|
|
// 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;
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2014-01-08 20:34:14 -05:00
|
|
|
_dbg_assert_(EXPANSIONINTERFACE, iChannel < MAX_EXI_CHANNELS);
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2014-01-08 20:34:14 -05:00
|
|
|
if (iChannel < MAX_EXI_CHANNELS)
|
2009-10-19 15:14:48 +00:00
|
|
|
g_Channels[iChannel]->Write32(_iValue, iRegister);
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UpdateInterrupts()
|
|
|
|
|
{
|
2009-10-19 15:14:48 +00:00
|
|
|
// Interrupts are mapped a bit strangely:
|
|
|
|
|
// Channel 0 Device 0 generates interrupt on channel 0
|
|
|
|
|
// Channel 0 Device 2 generates interrupt on channel 2
|
|
|
|
|
// Channel 1 Device 0 generates interrupt on channel 1
|
|
|
|
|
g_Channels[2]->SetEXIINT(g_Channels[0]->GetDevice(4)->IsInterruptSet());
|
|
|
|
|
|
|
|
|
|
bool causeInt = false;
|
2013-10-29 01:09:01 -04:00
|
|
|
for (auto& channel : g_Channels)
|
|
|
|
|
causeInt |= channel->IsCausingInterrupt();
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2009-10-19 15:14:48 +00:00
|
|
|
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_EXI, causeInt);
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // end of namespace ExpansionInterface
|