From 3a85aa9817b56d2c289a28c05790480357bb2224 Mon Sep 17 00:00:00 2001 From: EmptyChaos Date: Sat, 3 Sep 2016 02:43:20 +0000 Subject: [PATCH] EXI_DeviceMemoryCard: Use CoreTiming/DoState correctly CoreTiming gets restored before ExpansionInterface so CoreTiming events need to already be registered before the save state loading begins. This means that the callbacks must be registered unconditionally instead of on-demand. --- Source/Core/Core/HW/EXI.cpp | 4 ++ Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp | 58 +++++++++++++------- Source/Core/Core/HW/EXI_DeviceMemoryCard.h | 12 ++-- 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/Source/Core/Core/HW/EXI.cpp b/Source/Core/Core/HW/EXI.cpp index 32c6ce80a7b3..1b18c566c348 100644 --- a/Source/Core/Core/HW/EXI.cpp +++ b/Source/Core/Core/HW/EXI.cpp @@ -12,6 +12,7 @@ #include "Core/CoreTiming.h" #include "Core/HW/EXI.h" #include "Core/HW/EXI_Channel.h" +#include "Core/HW/EXI_DeviceMemoryCard.h" #include "Core/HW/MMIO.h" #include "Core/HW/ProcessorInterface.h" #include "Core/HW/Sram.h" @@ -38,6 +39,7 @@ void Init() InitSRAM(); } + CEXIMemoryCard::Init(); for (u32 i = 0; i < MAX_EXI_CHANNELS; i++) g_Channels[i] = std::make_unique(i); @@ -65,6 +67,8 @@ void Shutdown() { for (auto& channel : g_Channels) channel.reset(); + + CEXIMemoryCard::Shutdown(); } void DoState(PointerWrap& p) diff --git a/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp index 3ca0ddcfcf69..fdef3181fb82 100644 --- a/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp +++ b/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include #include #include #include @@ -41,6 +42,9 @@ static const u32 MC_TRANSFER_RATE_READ = 512 * 1024; static const u32 MC_TRANSFER_RATE_WRITE = (u32)(96.125f * 1024.0f); +static std::array s_et_cmd_done; +static std::array s_et_transfer_complete; + // Takes care of the nasty recovery of the 'this' pointer from card_index, // stored in the userdata parameter of the CoreTiming event. void CEXIMemoryCard::EventCompleteFindInstance(u64 userdata, @@ -70,25 +74,37 @@ void CEXIMemoryCard::TransferCompleteCallback(u64 userdata, s64 cyclesLate) [](CEXIMemoryCard* instance) { instance->TransferComplete(); }); } -CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder) : card_index(index) +void CEXIMemoryCard::Init() { - struct - { - const char* done; - const char* transfer_complete; - } const event_names[] = { - {"memcardDoneA", "memcardTransferCompleteA"}, {"memcardDoneB", "memcardTransferCompleteB"}, - }; + static constexpr char DONE_PREFIX[] = "memcardDone"; + static constexpr char TRANSFER_COMPLETE_PREFIX[] = "memcardTransferComplete"; - if ((size_t)index >= ArraySize(event_names)) + static_assert(s_et_cmd_done.size() == s_et_transfer_complete.size(), "Event array size differs"); + for (unsigned int i = 0; i < s_et_cmd_done.size(); ++i) { - PanicAlertT("Trying to create invalid memory card index."); + std::string name = DONE_PREFIX; + name += static_cast('A' + i); + s_et_cmd_done[i] = CoreTiming::RegisterEvent(name, CmdDoneCallback); + + name = TRANSFER_COMPLETE_PREFIX; + name += static_cast('A' + i); + s_et_transfer_complete[i] = CoreTiming::RegisterEvent(name, TransferCompleteCallback); } - // we're potentially leaking events here, since there's no RemoveEvent - // until emu shutdown, but I guess it's inconsequential - et_cmd_done = CoreTiming::RegisterEvent(event_names[index].done, CmdDoneCallback); - et_transfer_complete = - CoreTiming::RegisterEvent(event_names[index].transfer_complete, TransferCompleteCallback); +} + +void CEXIMemoryCard::Shutdown() +{ + s_et_cmd_done.fill(nullptr); + s_et_transfer_complete.fill(nullptr); +} + +CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder) : card_index(index) +{ + _assert_msg_(EXPANSIONINTERFACE, static_cast(index) < s_et_cmd_done.size(), + "Trying to create invalid memory card index %d.", index); + + // NOTE: When loading a save state, DMA completion callbacks (s_et_transfer_complete) and such + // may have been restored, we need to anticipate those arriving. interruptSwitch = 0; m_bInterruptSet = 0; @@ -248,8 +264,8 @@ void CEXIMemoryCard::SetupRawMemcard(u16 sizeMb) CEXIMemoryCard::~CEXIMemoryCard() { - CoreTiming::RemoveEvent(et_cmd_done); - CoreTiming::RemoveEvent(et_transfer_complete); + CoreTiming::RemoveEvent(s_et_cmd_done[card_index]); + CoreTiming::RemoveEvent(s_et_transfer_complete[card_index]); } bool CEXIMemoryCard::UseDelayedTransferCompletion() const @@ -279,8 +295,8 @@ void CEXIMemoryCard::TransferComplete() void CEXIMemoryCard::CmdDoneLater(u64 cycles) { - CoreTiming::RemoveEvent(et_cmd_done); - CoreTiming::ScheduleEvent((int)cycles, et_cmd_done, (u64)card_index); + CoreTiming::RemoveEvent(s_et_cmd_done[card_index]); + CoreTiming::ScheduleEvent((int)cycles, s_et_cmd_done[card_index], (u64)card_index); } void CEXIMemoryCard::SetCS(int cs) @@ -547,7 +563,7 @@ void CEXIMemoryCard::DMARead(u32 _uAddr, u32 _uSize) // Schedule transfer complete later based on read speed CoreTiming::ScheduleEvent(_uSize * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_READ), - et_transfer_complete, (u64)card_index); + s_et_transfer_complete[card_index], (u64)card_index); } // DMA write are preceded by all of the necessary setup via IMMWrite @@ -563,5 +579,5 @@ void CEXIMemoryCard::DMAWrite(u32 _uAddr, u32 _uSize) // Schedule transfer complete later based on write speed CoreTiming::ScheduleEvent(_uSize * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_WRITE), - et_transfer_complete, (u64)card_index); + s_et_transfer_complete[card_index], (u64)card_index); } diff --git a/Source/Core/Core/HW/EXI_DeviceMemoryCard.h b/Source/Core/Core/HW/EXI_DeviceMemoryCard.h index 5cafcfce2927..95c0dd1e1f4d 100644 --- a/Source/Core/Core/HW/EXI_DeviceMemoryCard.h +++ b/Source/Core/Core/HW/EXI_DeviceMemoryCard.h @@ -9,10 +9,6 @@ #include "Core/HW/EXI_Device.h" -namespace CoreTiming -{ -struct EventType; -} class MemoryCardBase; class PointerWrap; @@ -30,6 +26,12 @@ class CEXIMemoryCard : public IEXIDevice void DMARead(u32 _uAddr, u32 _uSize) override; void DMAWrite(u32 _uAddr, u32 _uSize) override; + // CoreTiming events need to be registered during boot since CoreTiming is DoState()-ed + // before ExpansionInterface so we'll lose the save stated events if the callbacks are + // not already registered first. + static void Init(); + static void Shutdown(); + private: void SetupGciFolder(u16 sizeMb); void SetupRawMemcard(u16 sizeMb); @@ -71,8 +73,6 @@ class CEXIMemoryCard : public IEXIDevice }; int card_index; - CoreTiming::EventType* et_cmd_done = nullptr; - CoreTiming::EventType* et_transfer_complete = nullptr; //! memory card state // STATE_TO_SAVE