Redo timing in DMAWrite/DMARead
This commit is contained in:
parent
8a0093de23
commit
f602372885
|
@ -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, m_ChannelId));
|
device.reset(EXIDevice_Create(EXIDEVICE_NONE, this));
|
||||||
|
|
||||||
updateInterrupts = CoreTiming::RegisterEvent("EXIInterrupt", UpdateInterrupts);
|
updateInterrupts = CoreTiming::RegisterEvent("EXIInterrupt", UpdateInterrupts);
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,6 @@ void CEXIChannel::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
case EXI_READWRITE: pDevice->ImmReadWrite(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);
|
default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI Imm: Unknown transfer type %i", m_Control.RW);
|
||||||
}
|
}
|
||||||
m_Control.TSTART = 0;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -134,14 +133,12 @@ void CEXIChannel::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
case EXI_WRITE: pDevice->DMAWrite(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);
|
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_Control.TSTART = 0;
|
||||||
{
|
|
||||||
m_Status.TCINT = 1;
|
if (pDevice->m_deviceType != EXIDEVICE_MEMORYCARD)
|
||||||
CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0);
|
SendTransferComplete();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -152,6 +149,12 @@ void CEXIChannel::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CEXIChannel::SendTransferComplete()
|
||||||
|
{
|
||||||
|
m_Status.TCINT = 1;
|
||||||
|
CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0);
|
||||||
|
}
|
||||||
|
|
||||||
void CEXIChannel::RemoveDevices()
|
void CEXIChannel::RemoveDevices()
|
||||||
{
|
{
|
||||||
for (auto& device : m_pDevices)
|
for (auto& device : m_pDevices)
|
||||||
|
@ -160,7 +163,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, m_ChannelId);
|
IEXIDevice* pNewDevice = EXIDevice_Create(device_type, this);
|
||||||
AddDevice(pNewDevice, device_num);
|
AddDevice(pNewDevice, device_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +235,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, m_ChannelId);
|
IEXIDevice* pSaveDevice = (type == pDevice->m_deviceType) ? pDevice : EXIDevice_Create(type, this);
|
||||||
pSaveDevice->DoState(p);
|
pSaveDevice->DoState(p);
|
||||||
if (pSaveDevice != pDevice)
|
if (pSaveDevice != pDevice)
|
||||||
{
|
{
|
||||||
|
|
|
@ -80,9 +80,6 @@ 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);
|
||||||
|
@ -96,6 +93,8 @@ public:
|
||||||
|
|
||||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||||
|
|
||||||
|
void SendTransferComplete();
|
||||||
|
|
||||||
void AddDevice(const TEXIDevices device_type, const int device_num);
|
void AddDevice(const TEXIDevices device_type, const int device_num);
|
||||||
void AddDevice(IEXIDevice* pDevice, const int device_num, bool notifyPresenceChanged=true);
|
void AddDevice(IEXIDevice* pDevice, const int device_num, bool notifyPresenceChanged=true);
|
||||||
|
|
||||||
|
@ -108,4 +107,7 @@ 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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#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"
|
||||||
|
@ -88,7 +89,7 @@ public:
|
||||||
|
|
||||||
|
|
||||||
// F A C T O R Y
|
// F A C T O R Y
|
||||||
IEXIDevice* EXIDevice_Create(TEXIDevices device_type, const int channel_num)
|
IEXIDevice* EXIDevice_Create(TEXIDevices device_type, CEXIChannel* channel)
|
||||||
{
|
{
|
||||||
IEXIDevice* result = nullptr;
|
IEXIDevice* result = nullptr;
|
||||||
|
|
||||||
|
@ -102,7 +103,8 @@ IEXIDevice* EXIDevice_Create(TEXIDevices device_type, const int channel_num)
|
||||||
case EXIDEVICE_MEMORYCARDFOLDER:
|
case EXIDEVICE_MEMORYCARDFOLDER:
|
||||||
{
|
{
|
||||||
bool gci_folder = (device_type == EXIDEVICE_MEMORYCARDFOLDER);
|
bool gci_folder = (device_type == EXIDEVICE_MEMORYCARDFOLDER);
|
||||||
result = new CEXIMemoryCard(channel_num, gci_folder);
|
device_type = EXIDEVICE_MEMORYCARD;
|
||||||
|
result = new CEXIMemoryCard(channel->m_ChannelId, gci_folder);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EXIDEVICE_MASKROM:
|
case EXIDEVICE_MASKROM:
|
||||||
|
@ -114,7 +116,7 @@ IEXIDevice* EXIDevice_Create(TEXIDevices device_type, const int channel_num)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EXIDEVICE_MIC:
|
case EXIDEVICE_MIC:
|
||||||
result = new CEXIMic(channel_num);
|
result = new CEXIMic(channel->m_ChannelId);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EXIDEVICE_ETH:
|
case EXIDEVICE_ETH:
|
||||||
|
@ -138,5 +140,7 @@ IEXIDevice* EXIDevice_Create(TEXIDevices device_type, const int channel_num)
|
||||||
if (result != nullptr)
|
if (result != nullptr)
|
||||||
result->m_deviceType = device_type;
|
result->m_deviceType = device_type;
|
||||||
|
|
||||||
|
result->m_channel = channel;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#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,
|
||||||
|
@ -48,9 +50,12 @@ 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, const int channel_num);
|
IEXIDevice* EXIDevice_Create(const TEXIDevices device_type, CEXIChannel* channel);
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "Core/CoreTiming.h"
|
#include "Core/CoreTiming.h"
|
||||||
#include "Core/Movie.h"
|
#include "Core/Movie.h"
|
||||||
#include "Core/HW/EXI.h"
|
#include "Core/HW/EXI.h"
|
||||||
|
#include "Core/HW/EXI_Channel.h"
|
||||||
#include "Core/HW/EXI_Device.h"
|
#include "Core/HW/EXI_Device.h"
|
||||||
#include "Core/HW/EXI_DeviceMemoryCard.h"
|
#include "Core/HW/EXI_DeviceMemoryCard.h"
|
||||||
#include "Core/HW/GCMemcard.h"
|
#include "Core/HW/GCMemcard.h"
|
||||||
|
@ -29,11 +30,8 @@
|
||||||
#define MC_STATUS_READY 0x01
|
#define MC_STATUS_READY 0x01
|
||||||
#define SIZE_TO_Mb (1024 * 8 * 16)
|
#define SIZE_TO_Mb (1024 * 8 * 16)
|
||||||
|
|
||||||
// These are rough estimates based on GameCube video evidence of memory card "speeds"
|
static const u32 MC_TRANSFER_RATE_READ = 512 * 1024;
|
||||||
// This will need to be refined, and perhaps the idea behind the speed thing is wrong
|
static const u32 MC_TRANSFER_RATE_WRITE = (u32)(96.125f * 1024.0f);
|
||||||
// But it works with (near?) perfect speed as far as saving goes
|
|
||||||
static const float MC_TRANSFER_RATE_WRITE = 22.0f * 1024.0f;
|
|
||||||
static const float MC_TRANSFER_RATE_READ = 73.84f * 1024.0f;
|
|
||||||
|
|
||||||
void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate)
|
void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate)
|
||||||
{
|
{
|
||||||
|
@ -56,6 +54,16 @@ void CEXIMemoryCard::CmdDoneCallback(u64 userdata, int cyclesLate)
|
||||||
pThis->CmdDone();
|
pThis->CmdDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CEXIMemoryCard::TransferCompleteCallback(u64 userdata, int cyclesLate)
|
||||||
|
{
|
||||||
|
int card_index = (int)userdata;
|
||||||
|
CEXIMemoryCard* pThis = (CEXIMemoryCard*)ExpansionInterface::FindDevice(EXIDEVICE_MEMORYCARD, card_index);
|
||||||
|
if (pThis == nullptr)
|
||||||
|
pThis = (CEXIMemoryCard*)ExpansionInterface::FindDevice(EXIDEVICE_MEMORYCARDFOLDER, card_index);
|
||||||
|
if (pThis)
|
||||||
|
pThis->TransferComplete();
|
||||||
|
}
|
||||||
|
|
||||||
CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder)
|
CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder)
|
||||||
: card_index(index)
|
: card_index(index)
|
||||||
, m_bDirty(false)
|
, m_bDirty(false)
|
||||||
|
@ -63,6 +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);
|
||||||
|
|
||||||
interruptSwitch = 0;
|
interruptSwitch = 0;
|
||||||
m_bInterruptSet = 0;
|
m_bInterruptSet = 0;
|
||||||
|
@ -206,6 +215,17 @@ void CEXIMemoryCard::CmdDone()
|
||||||
m_bDirty = true;
|
m_bDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CEXIMemoryCard::TransferComplete()
|
||||||
|
{
|
||||||
|
// Transfer complete, send interrupt
|
||||||
|
m_channel->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)
|
||||||
{
|
{
|
||||||
CoreTiming::RemoveEvent(et_cmd_done);
|
CoreTiming::RemoveEvent(et_cmd_done);
|
||||||
|
@ -234,9 +254,6 @@ void CEXIMemoryCard::SetCS(int cs)
|
||||||
|
|
||||||
//???
|
//???
|
||||||
|
|
||||||
if (address >= MC_DATA_FILES)
|
|
||||||
CmdDoneLater(BLOCK_SIZE * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_WRITE));
|
|
||||||
else
|
|
||||||
CmdDoneLater(5000);
|
CmdDoneLater(5000);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -244,37 +261,19 @@ void CEXIMemoryCard::SetCS(int cs)
|
||||||
case cmdChipErase:
|
case cmdChipErase:
|
||||||
if (m_uPosition > 2)
|
if (m_uPosition > 2)
|
||||||
{
|
{
|
||||||
// Clear only the system blocks
|
// TODO: Investigate on HW, I (LPFaint99) believe that this only erases the system area (Blocks 0-4)
|
||||||
for (int i = 0; i < 5; i++)
|
memorycard->ClearAll();
|
||||||
memorycard->ClearBlock(BLOCK_SIZE * i);
|
|
||||||
|
|
||||||
status &= ~MC_STATUS_BUSY;
|
status &= ~MC_STATUS_BUSY;
|
||||||
m_bDirty = true;
|
m_bDirty = true;
|
||||||
|
|
||||||
if (address >= MC_DATA_FILES)
|
|
||||||
CmdDoneLater((BLOCK_SIZE * 5) * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_WRITE));
|
|
||||||
else
|
|
||||||
CmdDoneLater(5000);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case cmdReadArray:
|
|
||||||
if (m_uPosition > 8)
|
|
||||||
{
|
|
||||||
// Each read is 512 bytes
|
|
||||||
if (address >= MC_DATA_FILES)
|
|
||||||
CmdDoneLater(512 * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_READ));
|
|
||||||
else
|
|
||||||
CmdDoneLater(5000);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case cmdPageProgram:
|
case cmdPageProgram:
|
||||||
if (m_uPosition > 4)
|
if (m_uPosition >= 5)
|
||||||
{
|
{
|
||||||
int count = m_uPosition - 5;
|
int count = m_uPosition - 5;
|
||||||
int i=0;
|
int i=0;
|
||||||
status &= ~MC_STATUS_BUSY;
|
status &= ~0x80;
|
||||||
|
|
||||||
while (count--)
|
while (count--)
|
||||||
{
|
{
|
||||||
|
@ -283,10 +282,6 @@ void CEXIMemoryCard::SetCS(int cs)
|
||||||
address = (address & ~0x1FF) | ((address+1) & 0x1FF);
|
address = (address & ~0x1FF) | ((address+1) & 0x1FF);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each write is 128 bytes
|
|
||||||
if (address >= MC_DATA_FILES)
|
|
||||||
CmdDoneLater(128 * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_WRITE));
|
|
||||||
else
|
|
||||||
CmdDoneLater(5000);
|
CmdDoneLater(5000);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -505,10 +500,7 @@ 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
|
CoreTiming::ScheduleEvent(_uSize * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_READ), et_transfer_complete, (u64)card_index);
|
||||||
if ((address + _uSize) % BLOCK_SIZE == 0)
|
|
||||||
INFO_LOG(EXPANSIONINTERFACE, "reading from block: %x", address / BLOCK_SIZE);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DMA write are preceded by all of the necessary setup via IMMWrite
|
// DMA write are preceded by all of the necessary setup via IMMWrite
|
||||||
|
@ -516,19 +508,5 @@ 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));
|
||||||
|
CoreTiming::ScheduleEvent(_uSize * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_WRITE), et_transfer_complete, (u64)card_index);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,26 +30,21 @@ private:
|
||||||
// Scheduled when a command that required delayed end signaling is done.
|
// Scheduled when a command that required delayed end signaling is done.
|
||||||
static void CmdDoneCallback(u64 userdata, int cyclesLate);
|
static void CmdDoneCallback(u64 userdata, int cyclesLate);
|
||||||
|
|
||||||
|
// Scheduled when memory card is done transferring data
|
||||||
|
static void TransferCompleteCallback(u64 userdata, int cyclesLate);
|
||||||
|
|
||||||
// Flushes the memory card contents to disk.
|
// Flushes the memory card contents to disk.
|
||||||
void Flush(bool exiting = false);
|
void Flush(bool exiting = false);
|
||||||
|
|
||||||
// Signals that the command that was previously executed is now done.
|
// Signals that the command that was previously executed is now done.
|
||||||
void CmdDone();
|
void CmdDone();
|
||||||
|
|
||||||
|
// Signals that the transfer that was previously executed is now done.
|
||||||
|
void TransferComplete();
|
||||||
|
|
||||||
// Variant of CmdDone which schedules an event later in the future to complete the command.
|
// Variant of CmdDone which schedules an event later in the future to complete the command.
|
||||||
void CmdDoneLater(u64 cycles);
|
void CmdDoneLater(u64 cycles);
|
||||||
|
|
||||||
// Memory card data layout
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
MC_DATA_HEADER = 0x0000,
|
|
||||||
MC_DATA_DIRECTORY = 0x2000,
|
|
||||||
MC_DATA_DIRECTORYBACKUP = 0x4000,
|
|
||||||
MC_DATA_BLOCKALLOCMAP = 0x6000,
|
|
||||||
MC_DATA_BLOCKALLOCMAPBACKUP = 0x8000,
|
|
||||||
MC_DATA_FILES = 0xA000,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
cmdNintendoID = 0x00,
|
cmdNintendoID = 0x00,
|
||||||
|
@ -70,7 +65,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
int card_index;
|
int card_index;
|
||||||
int et_this_card, et_cmd_done;
|
int et_this_card, et_cmd_done, et_transfer_complete;
|
||||||
//! memory card state
|
//! memory card state
|
||||||
|
|
||||||
// STATE_TO_SAVE
|
// STATE_TO_SAVE
|
||||||
|
|
Loading…
Reference in New Issue