From cfad00d6e03941283d34dd8ec4731ed6c9daebe3 Mon Sep 17 00:00:00 2001 From: nitsuja Date: Sat, 17 Dec 2011 22:33:50 -0800 Subject: [PATCH] added missing ExpansionInterface data to savestates, and related fixes (for savestate robustness) --- Source/Core/Core/Src/HW/EXI.cpp | 14 ++-- Source/Core/Core/Src/HW/EXI.h | 1 + Source/Core/Core/Src/HW/EXI_Channel.cpp | 69 +++++++++++++++++-- Source/Core/Core/Src/HW/EXI_Channel.h | 3 + Source/Core/Core/Src/HW/EXI_Device.cpp | 25 ++++--- Source/Core/Core/Src/HW/EXI_Device.h | 32 +++++---- Source/Core/Core/Src/HW/EXI_DeviceAD16.cpp | 7 ++ Source/Core/Core/Src/HW/EXI_DeviceAD16.h | 1 + .../Core/Src/HW/EXI_DeviceAMBaseboard.cpp | 7 ++ .../Core/Core/Src/HW/EXI_DeviceAMBaseboard.h | 1 + .../Core/Core/Src/HW/EXI_DeviceEthernet.cpp | 22 ++++++ Source/Core/Core/Src/HW/EXI_DeviceEthernet.h | 1 + Source/Core/Core/Src/HW/EXI_DeviceIPL.cpp | 8 ++- .../Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp | 46 +++++++++++-- .../Core/Core/Src/HW/EXI_DeviceMemoryCard.h | 2 +- Source/Core/Core/Src/HW/HW.cpp | 5 ++ Source/Core/Core/Src/HW/HW.h | 1 + Source/Core/Core/Src/State.cpp | 2 + 18 files changed, 202 insertions(+), 45 deletions(-) diff --git a/Source/Core/Core/Src/HW/EXI.cpp b/Source/Core/Core/Src/HW/EXI.cpp index 109e955e8c..bfc5c28819 100644 --- a/Source/Core/Core/Src/HW/EXI.cpp +++ b/Source/Core/Core/Src/HW/EXI.cpp @@ -64,12 +64,14 @@ void Shutdown() void DoState(PointerWrap &p) { - // TODO: Complete DoState for each IEXIDevice - g_Channels[0]->GetDevice(1)->DoState(p); - g_Channels[0]->GetDevice(2)->DoState(p); - g_Channels[0]->GetDevice(4)->DoState(p); - g_Channels[1]->GetDevice(1)->DoState(p); - g_Channels[2]->GetDevice(1)->DoState(p); + for (int c = 0; c < NUM_CHANNELS; ++c) + g_Channels[c]->DoState(p); +} + +void OnAfterLoad() +{ + for (int c = 0; c < NUM_CHANNELS; ++c) + g_Channels[c]->OnAfterLoad(); } void ChangeDeviceCallback(u64 userdata, int cyclesLate) diff --git a/Source/Core/Core/Src/HW/EXI.h b/Source/Core/Core/Src/HW/EXI.h index fbd5b2e788..1b913b7da1 100644 --- a/Source/Core/Core/Src/HW/EXI.h +++ b/Source/Core/Core/Src/HW/EXI.h @@ -28,6 +28,7 @@ namespace ExpansionInterface void Init(); void Shutdown(); void DoState(PointerWrap &p); +void OnAfterLoad(); void Update(); void UpdateInterrupts(); diff --git a/Source/Core/Core/Src/HW/EXI_Channel.cpp b/Source/Core/Core/Src/HW/EXI_Channel.cpp index cb729d21ff..c29a6f2bb2 100644 --- a/Source/Core/Core/Src/HW/EXI_Channel.cpp +++ b/Source/Core/Core/Src/HW/EXI_Channel.cpp @@ -18,6 +18,8 @@ #include "EXI_Channel.h" #include "EXI_Device.h" #include "EXI.h" +#include "../ConfigManager.h" +#include "../Movie.h" #define EXI_READ 0 #define EXI_WRITE 1 @@ -60,6 +62,12 @@ void CEXIChannel::RemoveDevices() } void CEXIChannel::AddDevice(const TEXIDevices device_type, const int device_num) +{ + IEXIDevice* pNewDevice = EXIDevice_Create(device_type, m_ChannelId); + AddDevice(pNewDevice, device_num); +} + +void CEXIChannel::AddDevice(IEXIDevice* pDevice, const int device_num, bool notifyPresenceChanged) { _dbg_assert_(EXPANSIONINTERFACE, device_num < NUM_DEVICES); @@ -70,15 +78,18 @@ void CEXIChannel::AddDevice(const TEXIDevices device_type, const int device_num) m_pDevices[device_num] = NULL; } - // create the new one - m_pDevices[device_num] = EXIDevice_Create(device_type, m_ChannelId); + // replace it with the new one + m_pDevices[device_num] = pDevice; - // This means "device presence changed", software has to check - // m_Status.EXT to see if it is now present or not - if (m_ChannelId != 2) + if(notifyPresenceChanged) { - m_Status.EXTINT = 1; - UpdateInterrupts(); + // This means "device presence changed", software has to check + // m_Status.EXT to see if it is now present or not + if (m_ChannelId != 2) + { + m_Status.EXTINT = 1; + UpdateInterrupts(); + } } } @@ -264,3 +275,47 @@ void CEXIChannel::Write32(const u32 _iValue, const u32 _iRegister) break; } } + +void CEXIChannel::DoState(PointerWrap &p) +{ + p.Do(m_Status); + p.Do(m_DMAMemoryAddress); + p.Do(m_DMALength); + p.Do(m_Control); + p.Do(m_ImmData); + + bool reloadOnState = SConfig::GetInstance().b_reloadMCOnState; + + for (int d = 0; d < NUM_DEVICES; ++d) + { + IEXIDevice* pDevice = m_pDevices[d]; + TEXIDevices type = pDevice->m_deviceType; + p.Do(type); + IEXIDevice* pSaveDevice = (type == pDevice->m_deviceType) ? pDevice : EXIDevice_Create(type, m_ChannelId); + pSaveDevice->DoState(p); + if(pSaveDevice != pDevice) + { + // if we had to create a temporary device, discard it if we're not loading. + // also, if no movie is active, we'll assume the user wants to keep their current devices + // instead of the ones they had when the savestate was created, + // unless the device is NONE (since ChangeDevice sets that temporarily). + if(p.GetMode() != PointerWrap::MODE_READ || + (pDevice->m_deviceType != EXIDEVICE_NONE && + reloadOnState && !Movie::IsRecordingInput() && !Movie::IsPlayingInput())) + { + delete pSaveDevice; + } + else + { + AddDevice(pSaveDevice, d, false); + } + } + } +} + +void CEXIChannel::OnAfterLoad() +{ + for (int d = 0; d < NUM_DEVICES; ++d) + m_pDevices[d]->OnAfterLoad(); +} + diff --git a/Source/Core/Core/Src/HW/EXI_Channel.h b/Source/Core/Core/Src/HW/EXI_Channel.h index 8109de6fc1..21925f52e5 100644 --- a/Source/Core/Core/Src/HW/EXI_Channel.h +++ b/Source/Core/Core/Src/HW/EXI_Channel.h @@ -118,6 +118,7 @@ public: ~CEXIChannel(); void AddDevice(const TEXIDevices device_type, const int device_num); + void AddDevice(IEXIDevice* pDevice, const int device_num, bool notifyPresenceChanged=true); // Remove all devices void RemoveDevices(); @@ -128,6 +129,8 @@ public: void Update(); bool IsCausingInterrupt(); void UpdateInterrupts(); + void DoState(PointerWrap &p); + void OnAfterLoad(); // This should only be used to transition interrupts from SP1 to Channel 2 void SetEXIINT(bool exiint) { m_Status.EXIINT = !!exiint; } diff --git a/Source/Core/Core/Src/HW/EXI_Device.cpp b/Source/Core/Core/Src/HW/EXI_Device.cpp index aa24148f59..9c40a8f4d1 100644 --- a/Source/Core/Core/Src/HW/EXI_Device.cpp +++ b/Source/Core/Core/Src/HW/EXI_Device.cpp @@ -104,43 +104,50 @@ public: // F A C T O R Y IEXIDevice* EXIDevice_Create(TEXIDevices device_type, const int channel_num) { + IEXIDevice* result = NULL; + switch (device_type) { case EXIDEVICE_DUMMY: - return new CEXIDummy("Dummy"); + result = new CEXIDummy("Dummy"); break; case EXIDEVICE_MEMORYCARD: - return new CEXIMemoryCard(channel_num); + result = new CEXIMemoryCard(channel_num); break; case EXIDEVICE_MASKROM: - return new CEXIIPL(); + result = new CEXIIPL(); break; case EXIDEVICE_AD16: - return new CEXIAD16(); + result = new CEXIAD16(); break; case EXIDEVICE_MIC: - return new CEXIMic(channel_num); + result = new CEXIMic(channel_num); break; case EXIDEVICE_ETH: - return new CEXIETHERNET(SConfig::GetInstance().m_bba_mac); + result = new CEXIETHERNET(SConfig::GetInstance().m_bba_mac); break; case EXIDEVICE_AM_BASEBOARD: - return new CEXIAMBaseboard(); + result = new CEXIAMBaseboard(); break; case EXIDEVICE_GECKO: - return new CEXIGecko(); + result = new CEXIGecko(); break; case EXIDEVICE_NONE: default: - return new IEXIDevice(); + result = new IEXIDevice(); break; } + + if (result != NULL) + result->m_deviceType = device_type; + + return result; } diff --git a/Source/Core/Core/Src/HW/EXI_Device.h b/Source/Core/Core/Src/HW/EXI_Device.h index 15cd79d77c..8cb2f99a64 100644 --- a/Source/Core/Core/Src/HW/EXI_Device.h +++ b/Source/Core/Core/Src/HW/EXI_Device.h @@ -21,6 +21,19 @@ #include "Common.h" #include "ChunkFile.h" +enum TEXIDevices +{ + EXIDEVICE_DUMMY, + EXIDEVICE_MEMORYCARD, + EXIDEVICE_MASKROM, + EXIDEVICE_AD16, + EXIDEVICE_MIC, + EXIDEVICE_ETH, + EXIDEVICE_AM_BASEBOARD, + EXIDEVICE_GECKO, + EXIDEVICE_NONE = (u8)-1 +}; + class IEXIDevice { private: @@ -40,27 +53,18 @@ public: virtual bool IsPresent() {return false;} virtual void SetCS(int) {} virtual void DoState(PointerWrap&) {} + virtual void OnAfterLoad() {} // Update virtual void Update() {} // Is generating interrupt ? virtual bool IsInterruptSet() {return false;} - virtual ~IEXIDevice() {}; + virtual ~IEXIDevice() {} -}; - -enum TEXIDevices -{ - EXIDEVICE_DUMMY, - EXIDEVICE_MEMORYCARD, - EXIDEVICE_MASKROM, - EXIDEVICE_AD16, - EXIDEVICE_MIC, - EXIDEVICE_ETH, - EXIDEVICE_AM_BASEBOARD, - EXIDEVICE_GECKO, - EXIDEVICE_NONE = (u8)-1 + // 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. + TEXIDevices m_deviceType; }; extern IEXIDevice* EXIDevice_Create(const TEXIDevices device_type, const int channel_num); diff --git a/Source/Core/Core/Src/HW/EXI_DeviceAD16.cpp b/Source/Core/Core/Src/HW/EXI_DeviceAD16.cpp index 3b1b234d41..f65749865b 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceAD16.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceAD16.cpp @@ -90,3 +90,10 @@ void CEXIAD16::TransferByte(u8& _byte) m_uPosition++; } + +void CEXIAD16::DoState(PointerWrap &p) +{ + p.Do(m_uPosition); + p.Do(m_uCommand); + p.Do(m_uAD16Register); +} diff --git a/Source/Core/Core/Src/HW/EXI_DeviceAD16.h b/Source/Core/Core/Src/HW/EXI_DeviceAD16.h index 094b26974a..62b7da0592 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceAD16.h +++ b/Source/Core/Core/Src/HW/EXI_DeviceAD16.h @@ -24,6 +24,7 @@ public: CEXIAD16(); virtual void SetCS(int _iCS); virtual bool IsPresent(); + virtual void DoState(PointerWrap &p); private: enum diff --git a/Source/Core/Core/Src/HW/EXI_DeviceAMBaseboard.cpp b/Source/Core/Core/Src/HW/EXI_DeviceAMBaseboard.cpp index a1b8be2eee..762c87a7d4 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceAMBaseboard.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceAMBaseboard.cpp @@ -128,3 +128,10 @@ bool CEXIAMBaseboard::IsInterruptSet() ERROR_LOG(SP1, "AM-BB IRQ"); return m_have_irq; } + +void CEXIAMBaseboard::DoState(PointerWrap &p) +{ + p.Do(m_position); + p.Do(m_have_irq); + p.Do(m_command); +} diff --git a/Source/Core/Core/Src/HW/EXI_DeviceAMBaseboard.h b/Source/Core/Core/Src/HW/EXI_DeviceAMBaseboard.h index 091b478688..92a2ac4a8f 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceAMBaseboard.h +++ b/Source/Core/Core/Src/HW/EXI_DeviceAMBaseboard.h @@ -26,6 +26,7 @@ public: virtual void SetCS(int _iCS); virtual bool IsPresent(); virtual bool IsInterruptSet(); + virtual void DoState(PointerWrap &p); private: virtual void TransferByte(u8& _uByte); diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp index 72a84d12b2..6572233b20 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp @@ -415,4 +415,26 @@ void CEXIETHERNET::DMARead(u32 addr, u32 size) ERROR_LOG(SP1, "Unhandled BBA DMA read: %i, %08x", size, addr); } + +void CEXIETHERNET::DoState(PointerWrap &p) +{ + p.Do(m_uPosition); + p.Do(m_uCommand); + p.Do(m_bInterruptSet); + p.Do(mWriteP); + p.Do(mReadP); + p.Do(mExpectSpecialImmRead); + p.Do(mSpecialImmData); + p.Do(Activated); + p.Do(mRBRPP); + p.Do(mRBEmpty); + p.Do(mBbaMem); + p.Do(mExpectVariableLengthImmWrite); + p.Do(mReadyToSend); + p.Do(RegisterBlock); + // TODO? + //mWriteBuffer.DoState(p); + //mCbw.DoState(p); +} + //#pragma optimize("",on) diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h index ad70f9462b..3fa3a42d34 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h @@ -246,6 +246,7 @@ public: u32 ImmRead(u32 size); void DMAWrite(u32 addr, u32 size); void DMARead(u32 addr, u32 size); + void DoState(PointerWrap &p); //private: // STATE_TO_SAVE diff --git a/Source/Core/Core/Src/HW/EXI_DeviceIPL.cpp b/Source/Core/Core/Src/HW/EXI_DeviceIPL.cpp index 620b7e79b8..85bfd2428e 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceIPL.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceIPL.cpp @@ -152,7 +152,13 @@ CEXIIPL::~CEXIIPL() } void CEXIIPL::DoState(PointerWrap &p) { - p.DoArray(m_RTC, 4); + p.Do(m_RTC); + p.Do(m_uPosition); + p.Do(m_uAddress); + p.Do(m_uRWOffset); + p.Do(m_szBuffer); + p.Do(m_count); + p.Do(m_FontsLoaded); } void CEXIIPL::LoadFileToIPL(std::string filename, u32 offset) diff --git a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp index a1b2301caf..88116e9c7c 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp @@ -22,6 +22,7 @@ #include "../CoreTiming.h" #include "../ConfigManager.h" +#include "../Movie.h" #include "EXI.h" #include "EXI_Device.h" #include "EXI_DeviceMemoryCard.h" @@ -37,11 +38,10 @@ #define SIZE_TO_Mb (1024 * 8 * 16) #define MC_HDR_SIZE 0xA000 -static CEXIMemoryCard *cards[2]; - void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate) { - CEXIMemoryCard *ptr = cards[userdata]; + // casting userdata seems less error-prone than indexing a static (creation order issues, etc.) + CEXIMemoryCard *ptr = (CEXIMemoryCard*)userdata; ptr->Flush(); } @@ -50,7 +50,7 @@ CEXIMemoryCard::CEXIMemoryCard(const int index) , m_bDirty(false) { m_strFilename = (card_index == 0) ? SConfig::GetInstance().m_strMemoryCardA : SConfig::GetInstance().m_strMemoryCardB; - cards[card_index] = this; + // 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((card_index == 0) ? "memcardA" : "memcardB", FlushCallback); reloadOnState = SConfig::GetInstance().b_reloadMCOnState; @@ -158,6 +158,7 @@ void CEXIMemoryCard::Flush(bool exiting) CEXIMemoryCard::~CEXIMemoryCard() { + CoreTiming::RemoveEvent(et_this_card); Flush(true); delete[] memory_card_content; memory_card_content = NULL; @@ -237,7 +238,7 @@ void CEXIMemoryCard::SetCS(int cs) // 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, card_index); + CoreTiming::ScheduleEvent(500000000, et_this_card, (u64)this); break; } } @@ -423,10 +424,41 @@ void CEXIMemoryCard::TransferByte(u8 &byte) DEBUG_LOG(EXPANSIONINTERFACE, "EXI MEMCARD: < %02x", byte); } -void CEXIMemoryCard::DoState(PointerWrap &p) +void CEXIMemoryCard::OnAfterLoad() { - if (reloadOnState) + // hack for memory card switching, so you can load an old savestate and expect your newer memcard data to show up. + // it breaks movie sync, so we disable it if a movie is active. + // this was moved out of DoState because other things that got loaded later conflicted with it. + // note: the reloadOnState flag is almost always true. maybe only a few TASers have it off. + if (reloadOnState && !Movie::IsRecordingInput() && !Movie::IsPlayingInput()) { ExpansionInterface::ChangeDevice(card_index, EXIDEVICE_MEMORYCARD, 0); } } + +void CEXIMemoryCard::DoState(PointerWrap &p) +{ + // for movie sync, we need to save/load memory card contents (and other data) in savestates. + // otherwise, we'll assume the user wants to keep their memcards and saves separate, + // unless we're loading (in which case we let the savestate contents decide, in order to stay aligned with them). + bool storeContents = (!reloadOnState || Movie::IsRecordingInput() || Movie::IsPlayingInput()); + p.Do(storeContents); + + if (storeContents) + { + p.Do(interruptSwitch); + p.Do(m_bInterruptSet); + p.Do(command); + p.Do(status); + p.Do(m_uPosition); + p.Do(programming_buffer); + p.Do(formatDelay); + p.Do(m_bDirty); + p.Do(address); + + p.Do(nintendo_card_id); + p.Do(card_id); + p.Do(memory_card_size); + p.DoArray(memory_card_content, memory_card_size); + } +} diff --git a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h index e61a3d99a7..ac2cdd69f8 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h +++ b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h @@ -39,6 +39,7 @@ public: bool IsInterruptSet(); bool IsPresent(); void DoState(PointerWrap &p); + void OnAfterLoad(); private: // This is scheduled whenever a page write is issued. The this pointer is passed @@ -82,7 +83,6 @@ private: u8 programming_buffer[128]; u32 formatDelay; bool m_bDirty; - //! memory card parameters unsigned int nintendo_card_id, card_id; unsigned int address; diff --git a/Source/Core/Core/Src/HW/HW.cpp b/Source/Core/Core/Src/HW/HW.cpp index a078955815..ad5ecc404a 100644 --- a/Source/Core/Core/Src/HW/HW.cpp +++ b/Source/Core/Core/Src/HW/HW.cpp @@ -115,4 +115,9 @@ namespace HW } p.DoMarker("WIIHW"); } + + void OnAfterLoad() + { + ExpansionInterface::OnAfterLoad(); + } } diff --git a/Source/Core/Core/Src/HW/HW.h b/Source/Core/Core/Src/HW/HW.h index cd3375d3ac..3c4478ac2b 100644 --- a/Source/Core/Core/Src/HW/HW.h +++ b/Source/Core/Core/Src/HW/HW.h @@ -26,6 +26,7 @@ namespace HW void Init(); void Shutdown(); void DoState(PointerWrap &p); + void OnAfterLoad(); } #endif diff --git a/Source/Core/Core/Src/State.cpp b/Source/Core/Core/Src/State.cpp index e8dcddcb62..2643530264 100644 --- a/Source/Core/Core/Src/State.cpp +++ b/Source/Core/Core/Src/State.cpp @@ -402,6 +402,8 @@ void LoadFileStateCallback(u64 userdata, int cyclesLate) ResetCounters(); + HW::OnAfterLoad(); + g_op_in_progress = false; g_loadDepth--;