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 | 60 +++++++++++++------- Source/Core/Core/HW/EXI_DeviceMemoryCard.h | 12 ++-- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/Source/Core/Core/HW/EXI.cpp b/Source/Core/Core/HW/EXI.cpp index 32c6ce80a7..1b18c566c3 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 3ca0ddcfcf..fdef3181fb 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(); }); } +void CEXIMemoryCard::Init() +{ + static constexpr char DONE_PREFIX[] = "memcardDone"; + static constexpr char TRANSFER_COMPLETE_PREFIX[] = "memcardTransferComplete"; + + 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) + { + 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); + } +} + +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) { - struct - { - const char* done; - const char* transfer_complete; - } const event_names[] = { - {"memcardDoneA", "memcardTransferCompleteA"}, {"memcardDoneB", "memcardTransferCompleteB"}, - }; + _assert_msg_(EXPANSIONINTERFACE, static_cast(index) < s_et_cmd_done.size(), + "Trying to create invalid memory card index %d.", index); - if ((size_t)index >= ArraySize(event_names)) - { - PanicAlertT("Trying to create invalid memory card index."); - } - // 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); + // 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 5cafcfce29..95c0dd1e1f 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 @@ public: 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 @@ private: }; int card_index; - CoreTiming::EventType* et_cmd_done = nullptr; - CoreTiming::EventType* et_transfer_complete = nullptr; //! memory card state // STATE_TO_SAVE