added missing ExpansionInterface data to savestates, and related fixes (for savestate robustness)
This commit is contained in:
parent
e5286e0406
commit
cfad00d6e0
|
@ -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)
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace ExpansionInterface
|
|||
void Init();
|
||||
void Shutdown();
|
||||
void DoState(PointerWrap &p);
|
||||
void OnAfterLoad();
|
||||
|
||||
void Update();
|
||||
void UpdateInterrupts();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ public:
|
|||
CEXIAD16();
|
||||
virtual void SetCS(int _iCS);
|
||||
virtual bool IsPresent();
|
||||
virtual void DoState(PointerWrap &p);
|
||||
|
||||
private:
|
||||
enum
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -115,4 +115,9 @@ namespace HW
|
|||
}
|
||||
p.DoMarker("WIIHW");
|
||||
}
|
||||
|
||||
void OnAfterLoad()
|
||||
{
|
||||
ExpansionInterface::OnAfterLoad();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace HW
|
|||
void Init();
|
||||
void Shutdown();
|
||||
void DoState(PointerWrap &p);
|
||||
void OnAfterLoad();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -402,6 +402,8 @@ void LoadFileStateCallback(u64 userdata, int cyclesLate)
|
|||
|
||||
ResetCounters();
|
||||
|
||||
HW::OnAfterLoad();
|
||||
|
||||
g_op_in_progress = false;
|
||||
|
||||
g_loadDepth--;
|
||||
|
|
Loading…
Reference in New Issue