Revised channel access from EXI_DeviceMemoryCard

This commit is contained in:
TotalNerd 2014-07-15 01:55:22 -05:00
parent f602372885
commit b2ac65bb21
8 changed files with 55 additions and 27 deletions

View File

@ -97,6 +97,11 @@ void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 devi
CoreTiming::ScheduleEvent_Threadsafe(500000000, changeDevice, ((u64)channel << 32) | ((u64)device_type << 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) IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex)
{ {
for (auto& channel : g_Channels) for (auto& channel : g_Channels)

View File

@ -31,6 +31,9 @@ void UpdateInterrupts();
void ChangeDeviceCallback(u64 userdata, int cyclesLate); void ChangeDeviceCallback(u64 userdata, int cyclesLate);
void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num); void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num);
CEXIChannel* GetChannel(u32 index);
IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex=-1); IEXIDevice* FindDevice(TEXIDevices device_type, int customIndex=-1);
} // end of namespace ExpansionInterface } // end of namespace ExpansionInterface

View File

@ -31,7 +31,7 @@ CEXIChannel::CEXIChannel(u32 ChannelId) :
m_Status.CHIP_SELECT = 1; m_Status.CHIP_SELECT = 1;
for (auto& device : m_pDevices) for (auto& device : m_pDevices)
device.reset(EXIDevice_Create(EXIDEVICE_NONE, this)); device.reset(EXIDevice_Create(EXIDEVICE_NONE, m_ChannelId));
updateInterrupts = CoreTiming::RegisterEvent("EXIInterrupt", UpdateInterrupts); updateInterrupts = CoreTiming::RegisterEvent("EXIInterrupt", UpdateInterrupts);
} }
@ -137,7 +137,8 @@ void CEXIChannel::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
m_Control.TSTART = 0; m_Control.TSTART = 0;
if (pDevice->m_deviceType != EXIDEVICE_MEMORYCARD) // Check if device needs specific timing, otherwise just complete transfer immediately
if (!pDevice->UseDelayedTransferCompletion())
SendTransferComplete(); SendTransferComplete();
} }
}) })
@ -163,7 +164,7 @@ void CEXIChannel::RemoveDevices()
void CEXIChannel::AddDevice(const TEXIDevices device_type, const int device_num) void CEXIChannel::AddDevice(const TEXIDevices device_type, const int device_num)
{ {
IEXIDevice* pNewDevice = EXIDevice_Create(device_type, this); IEXIDevice* pNewDevice = EXIDevice_Create(device_type, m_ChannelId);
AddDevice(pNewDevice, device_num); AddDevice(pNewDevice, device_num);
} }
@ -235,7 +236,7 @@ void CEXIChannel::DoState(PointerWrap &p)
IEXIDevice* pDevice = m_pDevices[d].get(); IEXIDevice* pDevice = m_pDevices[d].get();
TEXIDevices type = pDevice->m_deviceType; TEXIDevices type = pDevice->m_deviceType;
p.Do(type); p.Do(type);
IEXIDevice* pSaveDevice = (type == pDevice->m_deviceType) ? pDevice : EXIDevice_Create(type, this); IEXIDevice* pSaveDevice = (type == pDevice->m_deviceType) ? pDevice : EXIDevice_Create(type, m_ChannelId);
pSaveDevice->DoState(p); pSaveDevice->DoState(p);
if (pSaveDevice != pDevice) if (pSaveDevice != pDevice)
{ {

View File

@ -80,9 +80,13 @@ private:
std::unique_ptr<IEXIDevice> m_pDevices[NUM_DEVICES]; std::unique_ptr<IEXIDevice> m_pDevices[NUM_DEVICES];
// Since channels operate a bit differently from each other
u32 m_ChannelId;
int updateInterrupts; int updateInterrupts;
static void UpdateInterrupts(u64 userdata, int cyclesLate); static void UpdateInterrupts(u64 userdata, int cyclesLate);
public: public:
// get device // get device
IEXIDevice* GetDevice(const u8 _CHIP_SELECT); IEXIDevice* GetDevice(const u8 _CHIP_SELECT);
@ -107,7 +111,4 @@ public:
// This should only be used to transition interrupts from SP1 to Channel 2 // This should only be used to transition interrupts from SP1 to Channel 2
void SetEXIINT(bool exiint) { m_Status.EXIINT = !!exiint; } void SetEXIINT(bool exiint) { m_Status.EXIINT = !!exiint; }
// Since channels operate a bit differently from each other
u32 m_ChannelId;
}; };

View File

@ -5,7 +5,6 @@
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/HW/EXI_Channel.h"
#include "Core/HW/EXI_Device.h" #include "Core/HW/EXI_Device.h"
#include "Core/HW/EXI_DeviceAD16.h" #include "Core/HW/EXI_DeviceAD16.h"
#include "Core/HW/EXI_DeviceAMBaseboard.h" #include "Core/HW/EXI_DeviceAMBaseboard.h"
@ -89,7 +88,7 @@ public:
// F A C T O R Y // F A C T O R Y
IEXIDevice* EXIDevice_Create(TEXIDevices device_type, CEXIChannel* channel) IEXIDevice* EXIDevice_Create(TEXIDevices device_type, const int channel_num)
{ {
IEXIDevice* result = nullptr; IEXIDevice* result = nullptr;
@ -103,8 +102,7 @@ IEXIDevice* EXIDevice_Create(TEXIDevices device_type, CEXIChannel* channel)
case EXIDEVICE_MEMORYCARDFOLDER: case EXIDEVICE_MEMORYCARDFOLDER:
{ {
bool gci_folder = (device_type == EXIDEVICE_MEMORYCARDFOLDER); bool gci_folder = (device_type == EXIDEVICE_MEMORYCARDFOLDER);
device_type = EXIDEVICE_MEMORYCARD; result = new CEXIMemoryCard(channel_num, gci_folder);
result = new CEXIMemoryCard(channel->m_ChannelId, gci_folder);
break; break;
} }
case EXIDEVICE_MASKROM: case EXIDEVICE_MASKROM:
@ -116,7 +114,7 @@ IEXIDevice* EXIDevice_Create(TEXIDevices device_type, CEXIChannel* channel)
break; break;
case EXIDEVICE_MIC: case EXIDEVICE_MIC:
result = new CEXIMic(channel->m_ChannelId); result = new CEXIMic(channel_num);
break; break;
case EXIDEVICE_ETH: case EXIDEVICE_ETH:
@ -140,7 +138,5 @@ IEXIDevice* EXIDevice_Create(TEXIDevices device_type, CEXIChannel* channel)
if (result != nullptr) if (result != nullptr)
result->m_deviceType = device_type; result->m_deviceType = device_type;
result->m_channel = channel;
return result; return result;
} }

View File

@ -7,8 +7,6 @@
#include "Common/ChunkFile.h" #include "Common/ChunkFile.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
class CEXIChannel;
enum TEXIDevices enum TEXIDevices
{ {
EXIDEVICE_DUMMY, EXIDEVICE_DUMMY,
@ -40,6 +38,8 @@ public:
virtual void DMAWrite(u32 _uAddr, u32 _uSize); virtual void DMAWrite(u32 _uAddr, u32 _uSize);
virtual void DMARead (u32 _uAddr, u32 _uSize); virtual void DMARead (u32 _uAddr, u32 _uSize);
virtual bool UseDelayedTransferCompletion() {return false;}
virtual bool IsPresent() {return false;} virtual bool IsPresent() {return false;}
virtual void SetCS(int) {} virtual void SetCS(int) {}
virtual void DoState(PointerWrap&) {} virtual void DoState(PointerWrap&) {}
@ -50,12 +50,9 @@ public:
virtual bool IsInterruptSet() {return false;} virtual bool IsInterruptSet() {return false;}
virtual ~IEXIDevice() {} virtual ~IEXIDevice() {}
// Pointer to channel attached to the device
CEXIChannel* m_channel;
// for savestates. storing it here seemed cleaner than requiring each implementation to report its type. // for savestates. storing it here seemed cleaner than requiring each implementation to report its type.
// I know this class is set up like an interface, but no code requires it to be strictly such. // I know this class is set up like an interface, but no code requires it to be strictly such.
TEXIDevices m_deviceType; TEXIDevices m_deviceType;
}; };
IEXIDevice* EXIDevice_Create(const TEXIDevices device_type, CEXIChannel* channel); IEXIDevice* EXIDevice_Create(const TEXIDevices device_type, const int channel_num);

View File

@ -71,7 +71,7 @@ CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder)
// we're potentially leaking events here, since there's no UnregisterEvent until emu shutdown, but I guess it's inconsequential // we're potentially leaking events here, since there's no UnregisterEvent until emu shutdown, but I guess it's inconsequential
et_this_card = CoreTiming::RegisterEvent((index == 0) ? "memcardFlushA" : "memcardFlushB", FlushCallback); et_this_card = CoreTiming::RegisterEvent((index == 0) ? "memcardFlushA" : "memcardFlushB", FlushCallback);
et_cmd_done = CoreTiming::RegisterEvent((index == 0) ? "memcardDoneA" : "memcardDoneB", CmdDoneCallback); et_cmd_done = CoreTiming::RegisterEvent((index == 0) ? "memcardDoneA" : "memcardDoneB", CmdDoneCallback);
et_transfer_complete = CoreTiming::RegisterEvent((index == 0) ? "memcardTransferCompleteB" : "memcardTransferCompleteB", TransferCompleteCallback); et_transfer_complete = CoreTiming::RegisterEvent((index == 0) ? "memcardTransferCompleteA" : "memcardTransferCompleteB", TransferCompleteCallback);
interruptSwitch = 0; interruptSwitch = 0;
m_bInterruptSet = 0; m_bInterruptSet = 0;
@ -201,6 +201,11 @@ CEXIMemoryCard::~CEXIMemoryCard()
memorycard.reset(); memorycard.reset();
} }
bool CEXIMemoryCard::UseDelayedTransferCompletion()
{
return true;
}
bool CEXIMemoryCard::IsPresent() bool CEXIMemoryCard::IsPresent()
{ {
return true; return true;
@ -218,12 +223,7 @@ void CEXIMemoryCard::CmdDone()
void CEXIMemoryCard::TransferComplete() void CEXIMemoryCard::TransferComplete()
{ {
// Transfer complete, send interrupt // Transfer complete, send interrupt
m_channel->SendTransferComplete(); ExpansionInterface::GetChannel(card_index)->SendTransferComplete();
// Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec)
// But first we unschedule already scheduled flushes - no point in flushing once per page for a large write.
CoreTiming::RemoveEvent(et_this_card);
CoreTiming::ScheduleEvent(500000000, et_this_card, (u64)card_index);
} }
void CEXIMemoryCard::CmdDoneLater(u64 cycles) void CEXIMemoryCard::CmdDoneLater(u64 cycles)
@ -500,6 +500,13 @@ IEXIDevice* CEXIMemoryCard::FindDevice(TEXIDevices device_type, int customIndex)
void CEXIMemoryCard::DMARead(u32 _uAddr, u32 _uSize) void CEXIMemoryCard::DMARead(u32 _uAddr, u32 _uSize)
{ {
memorycard->Read(address, _uSize, Memory::GetPointer(_uAddr)); memorycard->Read(address, _uSize, Memory::GetPointer(_uAddr));
#ifdef _DEBUG
if ((address + _uSize) % BLOCK_SIZE == 0)
INFO_LOG(EXPANSIONINTERFACE, "reading from block: %x", address / BLOCK_SIZE);
#endif
// Schedule transfer complete later based on read speed
CoreTiming::ScheduleEvent(_uSize * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_READ), et_transfer_complete, (u64)card_index); CoreTiming::ScheduleEvent(_uSize * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_READ), et_transfer_complete, (u64)card_index);
} }
@ -508,5 +515,22 @@ void CEXIMemoryCard::DMARead(u32 _uAddr, u32 _uSize)
void CEXIMemoryCard::DMAWrite(u32 _uAddr, u32 _uSize) void CEXIMemoryCard::DMAWrite(u32 _uAddr, u32 _uSize)
{ {
memorycard->Write(address, _uSize, Memory::GetPointer(_uAddr)); memorycard->Write(address, _uSize, Memory::GetPointer(_uAddr));
// At the end of writing to a block flush to disk
// memory card blocks are always(?) written as a whole,
// but the dma calls are by page size (0x200) at a time
// just in case this is the last block that the game will be writing for a while
if (((address + _uSize) % BLOCK_SIZE) == 0)
{
INFO_LOG(EXPANSIONINTERFACE, "writing to block: %x", address / BLOCK_SIZE);
// Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec)
// But first we unschedule already scheduled flushes - no point in flushing once per page for a large write
// Scheduling event is mainly for raw memory cards as the flush the whole 16MB to disk
// Flushing the gci folder is free in comparison
CoreTiming::RemoveEvent(et_this_card);
CoreTiming::ScheduleEvent(500000000, et_this_card, (u64)card_index);
}
// Schedule transfer complete later based on write speed
CoreTiming::ScheduleEvent(_uSize * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_WRITE), et_transfer_complete, (u64)card_index); CoreTiming::ScheduleEvent(_uSize * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_WRITE), et_transfer_complete, (u64)card_index);
} }

View File

@ -13,6 +13,7 @@ public:
virtual ~CEXIMemoryCard(); virtual ~CEXIMemoryCard();
void SetCS(int cs) override; void SetCS(int cs) override;
bool IsInterruptSet() override; bool IsInterruptSet() override;
bool UseDelayedTransferCompletion() override;
bool IsPresent() override; bool IsPresent() override;
void DoState(PointerWrap &p) override; void DoState(PointerWrap &p) override;
void PauseAndLock(bool doLock, bool unpauseOnUnlock=true) override; void PauseAndLock(bool doLock, bool unpauseOnUnlock=true) override;