mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-16 19:19:13 +01:00
132 lines
3.6 KiB
C++
132 lines
3.6 KiB
C++
// Copyright 2013 Dolphin Emulator Project
|
|
// Licensed under GPLv2
|
|
// Refer to the license.txt file included.
|
|
|
|
#include "Common/ChunkFile.h"
|
|
#include "Common/CommonTypes.h"
|
|
|
|
#include "Core/ConfigManager.h"
|
|
#include "Core/CoreTiming.h"
|
|
#include "Core/Movie.h"
|
|
#include "Core/HW/EXI.h"
|
|
#include "Core/HW/MMIO.h"
|
|
#include "Core/HW/ProcessorInterface.h"
|
|
#include "Core/HW/Sram.h"
|
|
#include "Core/PowerPC/PowerPC.h"
|
|
|
|
SRAM g_SRAM;
|
|
|
|
namespace ExpansionInterface
|
|
{
|
|
|
|
static int changeDevice;
|
|
|
|
static CEXIChannel *g_Channels[MAX_EXI_CHANNELS];
|
|
void Init()
|
|
{
|
|
InitSRAM();
|
|
for (u32 i = 0; i < MAX_EXI_CHANNELS; i++)
|
|
g_Channels[i] = new CEXIChannel(i);
|
|
|
|
if (Movie::IsPlayingInput() && Movie::IsConfigSaved())
|
|
{
|
|
g_Channels[0]->AddDevice(Movie::IsUsingMemcard(0) ? EXIDEVICE_MEMORYCARD : EXIDEVICE_NONE, 0); // SlotA
|
|
g_Channels[1]->AddDevice(Movie::IsUsingMemcard(1) ? EXIDEVICE_MEMORYCARD : EXIDEVICE_NONE, 0); // SlotB
|
|
}
|
|
else
|
|
{
|
|
g_Channels[0]->AddDevice(SConfig::GetInstance().m_EXIDevice[0], 0); // SlotA
|
|
g_Channels[1]->AddDevice(SConfig::GetInstance().m_EXIDevice[1], 0); // SlotB
|
|
}
|
|
g_Channels[0]->AddDevice(EXIDEVICE_MASKROM, 1);
|
|
g_Channels[0]->AddDevice(SConfig::GetInstance().m_EXIDevice[2], 2); // Serial Port 1
|
|
g_Channels[2]->AddDevice(EXIDEVICE_AD16, 0);
|
|
|
|
changeDevice = CoreTiming::RegisterEvent("ChangeEXIDevice", ChangeDeviceCallback);
|
|
}
|
|
|
|
void Shutdown()
|
|
{
|
|
for (auto& channel : g_Channels)
|
|
{
|
|
delete channel;
|
|
channel = nullptr;
|
|
}
|
|
}
|
|
|
|
void DoState(PointerWrap &p)
|
|
{
|
|
for (auto& channel : g_Channels)
|
|
channel->DoState(p);
|
|
}
|
|
|
|
void PauseAndLock(bool doLock, bool unpauseOnUnlock)
|
|
{
|
|
for (auto& channel : g_Channels)
|
|
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)
|
|
{
|
|
u8 channel = (u8)(userdata >> 32);
|
|
u8 type = (u8)(userdata >> 16);
|
|
u8 num = (u8)userdata;
|
|
|
|
g_Channels[channel]->AddDevice((TEXIDevices)type, num);
|
|
}
|
|
|
|
void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num)
|
|
{
|
|
// Called from GUI, so we need to make it thread safe.
|
|
// Let the hardware see no device for .5b cycles
|
|
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);
|
|
}
|
|
|
|
CEXIChannel* GetChannel(u32 index)
|
|
{
|
|
return g_Channels[index];
|
|
}
|
|
|
|
IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex)
|
|
{
|
|
for (auto& channel : g_Channels)
|
|
{
|
|
IEXIDevice* device = channel->FindDevice(device_type, customIndex);
|
|
if (device)
|
|
return device;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void UpdateInterrupts()
|
|
{
|
|
// 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;
|
|
for (auto& channel : g_Channels)
|
|
causeInt |= channel->IsCausingInterrupt();
|
|
|
|
ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_EXI, causeInt);
|
|
}
|
|
|
|
} // end of namespace ExpansionInterface
|