added missing ExpansionInterface data to savestates, and related fixes (for savestate robustness)

This commit is contained in:
nitsuja 2011-12-17 22:33:50 -08:00
parent e5286e0406
commit cfad00d6e0
18 changed files with 202 additions and 45 deletions

View File

@ -64,12 +64,14 @@ void Shutdown()
void DoState(PointerWrap &p) void DoState(PointerWrap &p)
{ {
// TODO: Complete DoState for each IEXIDevice for (int c = 0; c < NUM_CHANNELS; ++c)
g_Channels[0]->GetDevice(1)->DoState(p); g_Channels[c]->DoState(p);
g_Channels[0]->GetDevice(2)->DoState(p); }
g_Channels[0]->GetDevice(4)->DoState(p);
g_Channels[1]->GetDevice(1)->DoState(p); void OnAfterLoad()
g_Channels[2]->GetDevice(1)->DoState(p); {
for (int c = 0; c < NUM_CHANNELS; ++c)
g_Channels[c]->OnAfterLoad();
} }
void ChangeDeviceCallback(u64 userdata, int cyclesLate) void ChangeDeviceCallback(u64 userdata, int cyclesLate)

View File

@ -28,6 +28,7 @@ namespace ExpansionInterface
void Init(); void Init();
void Shutdown(); void Shutdown();
void DoState(PointerWrap &p); void DoState(PointerWrap &p);
void OnAfterLoad();
void Update(); void Update();
void UpdateInterrupts(); void UpdateInterrupts();

View File

@ -18,6 +18,8 @@
#include "EXI_Channel.h" #include "EXI_Channel.h"
#include "EXI_Device.h" #include "EXI_Device.h"
#include "EXI.h" #include "EXI.h"
#include "../ConfigManager.h"
#include "../Movie.h"
#define EXI_READ 0 #define EXI_READ 0
#define EXI_WRITE 1 #define EXI_WRITE 1
@ -60,6 +62,12 @@ 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);
AddDevice(pNewDevice, device_num);
}
void CEXIChannel::AddDevice(IEXIDevice* pDevice, const int device_num, bool notifyPresenceChanged)
{ {
_dbg_assert_(EXPANSIONINTERFACE, device_num < NUM_DEVICES); _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; m_pDevices[device_num] = NULL;
} }
// create the new one // replace it with the new one
m_pDevices[device_num] = EXIDevice_Create(device_type, m_ChannelId); m_pDevices[device_num] = pDevice;
// This means "device presence changed", software has to check if(notifyPresenceChanged)
// m_Status.EXT to see if it is now present or not
if (m_ChannelId != 2)
{ {
m_Status.EXTINT = 1; // This means "device presence changed", software has to check
UpdateInterrupts(); // 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; 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();
}

View File

@ -118,6 +118,7 @@ public:
~CEXIChannel(); ~CEXIChannel();
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);
// Remove all devices // Remove all devices
void RemoveDevices(); void RemoveDevices();
@ -128,6 +129,8 @@ public:
void Update(); void Update();
bool IsCausingInterrupt(); bool IsCausingInterrupt();
void UpdateInterrupts(); void UpdateInterrupts();
void DoState(PointerWrap &p);
void OnAfterLoad();
// 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; }

View File

@ -104,43 +104,50 @@ 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, const int channel_num)
{ {
IEXIDevice* result = NULL;
switch (device_type) switch (device_type)
{ {
case EXIDEVICE_DUMMY: case EXIDEVICE_DUMMY:
return new CEXIDummy("Dummy"); result = new CEXIDummy("Dummy");
break; break;
case EXIDEVICE_MEMORYCARD: case EXIDEVICE_MEMORYCARD:
return new CEXIMemoryCard(channel_num); result = new CEXIMemoryCard(channel_num);
break; break;
case EXIDEVICE_MASKROM: case EXIDEVICE_MASKROM:
return new CEXIIPL(); result = new CEXIIPL();
break; break;
case EXIDEVICE_AD16: case EXIDEVICE_AD16:
return new CEXIAD16(); result = new CEXIAD16();
break; break;
case EXIDEVICE_MIC: case EXIDEVICE_MIC:
return new CEXIMic(channel_num); result = new CEXIMic(channel_num);
break; break;
case EXIDEVICE_ETH: case EXIDEVICE_ETH:
return new CEXIETHERNET(SConfig::GetInstance().m_bba_mac); result = new CEXIETHERNET(SConfig::GetInstance().m_bba_mac);
break; break;
case EXIDEVICE_AM_BASEBOARD: case EXIDEVICE_AM_BASEBOARD:
return new CEXIAMBaseboard(); result = new CEXIAMBaseboard();
break; break;
case EXIDEVICE_GECKO: case EXIDEVICE_GECKO:
return new CEXIGecko(); result = new CEXIGecko();
break; break;
case EXIDEVICE_NONE: case EXIDEVICE_NONE:
default: default:
return new IEXIDevice(); result = new IEXIDevice();
break; break;
} }
if (result != NULL)
result->m_deviceType = device_type;
return result;
} }

View File

@ -21,6 +21,19 @@
#include "Common.h" #include "Common.h"
#include "ChunkFile.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 class IEXIDevice
{ {
private: private:
@ -40,27 +53,18 @@ public:
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&) {}
virtual void OnAfterLoad() {}
// Update // Update
virtual void Update() {} virtual void Update() {}
// Is generating interrupt ? // Is generating interrupt ?
virtual bool IsInterruptSet() {return false;} virtual bool IsInterruptSet() {return false;}
virtual ~IEXIDevice() {}; virtual ~IEXIDevice() {}
}; // 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.
enum TEXIDevices TEXIDevices m_deviceType;
{
EXIDEVICE_DUMMY,
EXIDEVICE_MEMORYCARD,
EXIDEVICE_MASKROM,
EXIDEVICE_AD16,
EXIDEVICE_MIC,
EXIDEVICE_ETH,
EXIDEVICE_AM_BASEBOARD,
EXIDEVICE_GECKO,
EXIDEVICE_NONE = (u8)-1
}; };
extern IEXIDevice* EXIDevice_Create(const TEXIDevices device_type, const int channel_num); extern IEXIDevice* EXIDevice_Create(const TEXIDevices device_type, const int channel_num);

View File

@ -90,3 +90,10 @@ void CEXIAD16::TransferByte(u8& _byte)
m_uPosition++; m_uPosition++;
} }
void CEXIAD16::DoState(PointerWrap &p)
{
p.Do(m_uPosition);
p.Do(m_uCommand);
p.Do(m_uAD16Register);
}

View File

@ -24,6 +24,7 @@ public:
CEXIAD16(); CEXIAD16();
virtual void SetCS(int _iCS); virtual void SetCS(int _iCS);
virtual bool IsPresent(); virtual bool IsPresent();
virtual void DoState(PointerWrap &p);
private: private:
enum enum

View File

@ -128,3 +128,10 @@ bool CEXIAMBaseboard::IsInterruptSet()
ERROR_LOG(SP1, "AM-BB IRQ"); ERROR_LOG(SP1, "AM-BB IRQ");
return m_have_irq; return m_have_irq;
} }
void CEXIAMBaseboard::DoState(PointerWrap &p)
{
p.Do(m_position);
p.Do(m_have_irq);
p.Do(m_command);
}

View File

@ -26,6 +26,7 @@ public:
virtual void SetCS(int _iCS); virtual void SetCS(int _iCS);
virtual bool IsPresent(); virtual bool IsPresent();
virtual bool IsInterruptSet(); virtual bool IsInterruptSet();
virtual void DoState(PointerWrap &p);
private: private:
virtual void TransferByte(u8& _uByte); virtual void TransferByte(u8& _uByte);

View File

@ -415,4 +415,26 @@ void CEXIETHERNET::DMARead(u32 addr, u32 size)
ERROR_LOG(SP1, "Unhandled BBA DMA read: %i, %08x", size, addr); 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) //#pragma optimize("",on)

View File

@ -246,6 +246,7 @@ public:
u32 ImmRead(u32 size); u32 ImmRead(u32 size);
void DMAWrite(u32 addr, u32 size); void DMAWrite(u32 addr, u32 size);
void DMARead(u32 addr, u32 size); void DMARead(u32 addr, u32 size);
void DoState(PointerWrap &p);
//private: //private:
// STATE_TO_SAVE // STATE_TO_SAVE

View File

@ -152,7 +152,13 @@ CEXIIPL::~CEXIIPL()
} }
void CEXIIPL::DoState(PointerWrap &p) 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) void CEXIIPL::LoadFileToIPL(std::string filename, u32 offset)

View File

@ -22,6 +22,7 @@
#include "../CoreTiming.h" #include "../CoreTiming.h"
#include "../ConfigManager.h" #include "../ConfigManager.h"
#include "../Movie.h"
#include "EXI.h" #include "EXI.h"
#include "EXI_Device.h" #include "EXI_Device.h"
#include "EXI_DeviceMemoryCard.h" #include "EXI_DeviceMemoryCard.h"
@ -37,11 +38,10 @@
#define SIZE_TO_Mb (1024 * 8 * 16) #define SIZE_TO_Mb (1024 * 8 * 16)
#define MC_HDR_SIZE 0xA000 #define MC_HDR_SIZE 0xA000
static CEXIMemoryCard *cards[2];
void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate) 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(); ptr->Flush();
} }
@ -50,7 +50,7 @@ CEXIMemoryCard::CEXIMemoryCard(const int index)
, m_bDirty(false) , m_bDirty(false)
{ {
m_strFilename = (card_index == 0) ? SConfig::GetInstance().m_strMemoryCardA : SConfig::GetInstance().m_strMemoryCardB; 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); et_this_card = CoreTiming::RegisterEvent((card_index == 0) ? "memcardA" : "memcardB", FlushCallback);
reloadOnState = SConfig::GetInstance().b_reloadMCOnState; reloadOnState = SConfig::GetInstance().b_reloadMCOnState;
@ -158,6 +158,7 @@ void CEXIMemoryCard::Flush(bool exiting)
CEXIMemoryCard::~CEXIMemoryCard() CEXIMemoryCard::~CEXIMemoryCard()
{ {
CoreTiming::RemoveEvent(et_this_card);
Flush(true); Flush(true);
delete[] memory_card_content; delete[] memory_card_content;
memory_card_content = NULL; 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) // 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. // But first we unschedule already scheduled flushes - no point in flushing once per page for a large write.
CoreTiming::RemoveEvent(et_this_card); CoreTiming::RemoveEvent(et_this_card);
CoreTiming::ScheduleEvent(500000000, et_this_card, card_index); CoreTiming::ScheduleEvent(500000000, et_this_card, (u64)this);
break; break;
} }
} }
@ -423,10 +424,41 @@ void CEXIMemoryCard::TransferByte(u8 &byte)
DEBUG_LOG(EXPANSIONINTERFACE, "EXI MEMCARD: < %02x", 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); 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);
}
}

View File

@ -39,6 +39,7 @@ public:
bool IsInterruptSet(); bool IsInterruptSet();
bool IsPresent(); bool IsPresent();
void DoState(PointerWrap &p); void DoState(PointerWrap &p);
void OnAfterLoad();
private: private:
// This is scheduled whenever a page write is issued. The this pointer is passed // This is scheduled whenever a page write is issued. The this pointer is passed
@ -82,7 +83,6 @@ private:
u8 programming_buffer[128]; u8 programming_buffer[128];
u32 formatDelay; u32 formatDelay;
bool m_bDirty; bool m_bDirty;
//! memory card parameters //! memory card parameters
unsigned int nintendo_card_id, card_id; unsigned int nintendo_card_id, card_id;
unsigned int address; unsigned int address;

View File

@ -115,4 +115,9 @@ namespace HW
} }
p.DoMarker("WIIHW"); p.DoMarker("WIIHW");
} }
void OnAfterLoad()
{
ExpansionInterface::OnAfterLoad();
}
} }

View File

@ -26,6 +26,7 @@ namespace HW
void Init(); void Init();
void Shutdown(); void Shutdown();
void DoState(PointerWrap &p); void DoState(PointerWrap &p);
void OnAfterLoad();
} }
#endif #endif

View File

@ -402,6 +402,8 @@ void LoadFileStateCallback(u64 userdata, int cyclesLate)
ResetCounters(); ResetCounters();
HW::OnAfterLoad();
g_op_in_progress = false; g_op_in_progress = false;
g_loadDepth--; g_loadDepth--;