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)
{
// 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)

View File

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

View File

@ -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();
}

View File

@ -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; }

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

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

View File

@ -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);
}

View File

@ -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);

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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;

View File

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

View File

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

View File

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