implement savestate serialization

This commit is contained in:
Jaklyy 2024-12-31 12:30:49 -05:00
parent 4071099a1f
commit 9e60f45481
5 changed files with 340 additions and 4 deletions

View File

@ -275,6 +275,22 @@ void ARM::DoSavestate(Savestate* file)
#endif
file->VarArray(NextInstr, 2*sizeof(u64));
file->VarArray(&MRTrack, sizeof(MRTrack));
file->Var32(&BranchAddr);
file->VarArray(QueueMode, sizeof(QueueMode));
file->Var8(&ExtReg);
file->Var8(&ExtROROffs);
file->Var64(&RetVal);
file->Var16(&LDRRegs);
file->Var16(&LDRFailedRegs);
file->VarArray(FetchAddr, sizeof(FetchAddr));
file->VarArray(STRVal, sizeof(STRVal));
file->Var64(&IRQTimestamp);
file->Var8(&FuncQueueFill);
file->Var8(&FuncQueueEnd);
file->Var8(&ExecuteCycles);
file->Bool32(&FuncQueueActive);
file->Bool32(&CheckInterlock);
file->Var32(&ExceptionBase);
if (!file->Saving)
@ -306,8 +322,105 @@ void ARM::DoSavestate(Savestate* file)
void ARMv5::DoSavestate(Savestate* file)
{
file->Var64(&ITCMTimestamp);
file->Var64(&TimestampMemory);
file->Bool32(&Store);
file->Var8((u8*)&ITCMDelay);
file->Var32(&QueuedDCacheLine);
file->Var32(&CP15Queue);
file->Var8(&ILCurrReg);
file->Var8(&ILPrevReg);
file->Var64(&ILCurrTime);
file->Var64(&ILPrevTime);
file->Var8(&ILQueueReg);
file->Var8((u8*)&ILQueueDelay);
file->Var8(&ILQueueMemReg);
file->VarArray(ILQueueTimes, sizeof(ILQueueTimes));
file->Var16(&ILQueueMask);
file->Var8(&ICacheStreamPtr);
file->Var8(&DCacheStreamPtr);
file->VarArray(ICacheStreamTimes, sizeof(ICacheStreamTimes));
file->VarArray(DCacheStreamTimes, sizeof(DCacheStreamTimes));
file->Var8((u8*)&ILForceDelay);
file->Var8(&WBWritePointer);
file->Var8(&WBFillPointer);
file->Var8(&WBWriting);
file->Var32(&WBCurAddr);
file->Var64(&WBCurVal);
file->VarArray(WBAddrQueued, sizeof(WBAddrQueued));
file->VarArray(storeaddr, sizeof(storeaddr));
file->VarArray(WBValQueued, sizeof(WBValQueued));
file->VarArray(WriteBufferFifo, sizeof(WriteBufferFifo));
file->Var64(&WBTimestamp);
file->Var64(&WBDelay);
file->Var32(&WBLastRegion);
file->Var64(&WBReleaseTS);
file->Var64(&WBInitialTS);
ARM::DoSavestate(file);
CP15DoSavestate(file);
if (!file->Saving)
{
int id;
file->Var32((u32*)&id);
DelayedQueue = GetQueueFuncFromID(id);
file->Var32((u32*)&id);
StartExec = GetQueueFuncFromID(id);
for (int i = 0; i <= FuncQueueEnd; i++)
{
file->Var32((u32*)&id);
FuncQueue[i] = GetQueueFuncFromID(id);
}
}
else
{
int id = GetIDFromQueueFunc(DelayedQueue);
file->Var32((u32*)&id);
id = GetIDFromQueueFunc(StartExec);
file->Var32((u32*)&id);
for (int i = 0; i <= FuncQueueEnd; i++)
{
id = GetIDFromQueueFunc(FuncQueue[i]);
file->Var32((u32*)&id);
}
}
}
void ARMv4::DoSavestate(Savestate* file)
{
file->Bool32(&Nonseq);
ARM::DoSavestate(file);
if (!file->Saving)
{
int id;
file->Var32((u32*)&id);
StartExec = GetQueueFuncFromID(id);
for (int i = 0; i <= FuncQueueEnd; i++)
{
file->Var32((u32*)&id);
FuncQueue[i] = GetQueueFuncFromID(id);
}
}
else
{
int id = GetIDFromQueueFunc(StartExec);
file->Var32((u32*)&id);
for (int i = 0; i <= FuncQueueEnd; i++)
{
id = GetIDFromQueueFunc(FuncQueue[i]);
file->Var32((u32*)&id);
}
}
}

214
src/ARM.h
View File

@ -725,6 +725,160 @@ public:
void QueueFunction(void (ARMv5::*QueueEntry)(void));
int GetIDFromQueueFunc(void (ARMv5::*funcptr)(void))
{
if (funcptr == &ARMv5::StartExecARM) return 0;
else if (funcptr == &ARMv5::ContExecARM) return 1;
else if (funcptr == &ARMv5::StartExecTHUMB) return 2;
else if (funcptr == &ARMv5::ContExecTHUMB) return 3;
else if (funcptr == &ARMv5::AddExecute) return 4;
else if (funcptr == &ARMv5::AddCycles_MW_2) return 5;
else if (funcptr == &ARMv5::DelayIfITCM_2) return 6;
else if (funcptr == &ARMv5::JumpTo_2) return 7;
else if (funcptr == &ARMv5::JumpTo_3A) return 8;
else if (funcptr == &ARMv5::JumpTo_3B) return 9;
else if (funcptr == &ARMv5::JumpTo_3C) return 10;
else if (funcptr == &ARMv5::JumpTo_4) return 11;
else if (funcptr == &ARMv5::CodeRead32_2) return 12;
else if (funcptr == &ARMv5::CodeRead32_3) return 13;
else if (funcptr == &ARMv5::CodeRead32_4) return 14;
else if (funcptr == &ARMv5::ICacheLookup_2) return 15;
else if (funcptr == &ARMv5::DAbortHandle) return 16;
else if (funcptr == &ARMv5::DCacheFin8) return 17;
else if (funcptr == &ARMv5::DRead8_2) return 18;
else if (funcptr == &ARMv5::DRead8_3) return 19;
else if (funcptr == &ARMv5::DRead8_4) return 20;
else if (funcptr == &ARMv5::DRead8_5) return 21;
else if (funcptr == &ARMv5::DCacheFin16) return 22;
else if (funcptr == &ARMv5::DRead16_2) return 23;
else if (funcptr == &ARMv5::DRead16_3) return 24;
else if (funcptr == &ARMv5::DRead16_4) return 25;
else if (funcptr == &ARMv5::DRead16_5) return 26;
else if (funcptr == &ARMv5::DCacheFin32) return 27;
else if (funcptr == &ARMv5::DRead32_2) return 28;
else if (funcptr == &ARMv5::DRead32_3) return 29;
else if (funcptr == &ARMv5::DRead32_4) return 30;
else if (funcptr == &ARMv5::DRead32_5) return 31;
else if (funcptr == &ARMv5::DRead32S_2) return 32;
else if (funcptr == &ARMv5::DRead32S_3) return 33;
else if (funcptr == &ARMv5::DRead32S_4) return 34;
else if (funcptr == &ARMv5::DRead32S_5A) return 35;
else if (funcptr == &ARMv5::DRead32S_5B) return 36;
else if (funcptr == &ARMv5::DWrite8_2) return 37;
else if (funcptr == &ARMv5::DWrite8_3) return 38;
else if (funcptr == &ARMv5::DWrite8_4) return 39;
else if (funcptr == &ARMv5::DWrite8_5) return 40;
else if (funcptr == &ARMv5::DWrite16_2) return 41;
else if (funcptr == &ARMv5::DWrite16_3) return 42;
else if (funcptr == &ARMv5::DWrite16_4) return 43;
else if (funcptr == &ARMv5::DWrite16_5) return 44;
else if (funcptr == &ARMv5::DWrite32_2) return 45;
else if (funcptr == &ARMv5::DWrite32_3) return 46;
else if (funcptr == &ARMv5::DWrite32_4) return 47;
else if (funcptr == &ARMv5::DWrite32_5) return 48;
else if (funcptr == &ARMv5::DWrite32S_2) return 49;
else if (funcptr == &ARMv5::DWrite32S_3) return 50;
else if (funcptr == &ARMv5::DWrite32S_4) return 51;
else if (funcptr == &ARMv5::DWrite32S_5A) return 52;
else if (funcptr == &ARMv5::DWrite32S_5B) return 53;
else if (funcptr == &ARMv5::WBCheck_2) return 54;
else if (funcptr == &ARMv5::ICachePrefetch_2) return 55;
else if (funcptr == &ARMv5::DCacheLookup_2) return 56;
else if (funcptr == &ARMv5::DCacheLookup_3) return 57;
else if (funcptr == &ARMv5::DCClearAddr_2) return 58;
else if (funcptr == &ARMv5::DCClearSetWay_2) return 59;
else if (funcptr == &ARMv5::DCClearInvalidateAddr_2) return 60;
else if (funcptr == &ARMv5::DCClearInvalidateSetWay_2) return 61;
else if (funcptr == &ARMv5::SetupInterlock_2) return 62;
else if (funcptr == &ARMv5::HandleInterlocksExecute_2) return 63;
else if (funcptr == &ARMv5::HandleInterlocksMemory_2) return 64;
else if (funcptr == &ARMv5::ForceInterlock_2) return 65;
else if (funcptr == &ARMv5::QueueUpdateMode) return 66;
else if (funcptr == &ARMv5::SignExtend8) return 67;
else if (funcptr == &ARMv5::SignExtend16) return 68;
else if (funcptr == &ARMv5::ROR32) return 69;
else { Platform::Log(Platform::LogLevel::Error, "ARM9: INVALID FUNCTION POINTER FOR SAVESTATES; DID SOMEONE FORGET TO UPDATE SERIALIZATION?\n"); return -1; }
}
typedef void (ARMv5::*funcptrA9)(void);
funcptrA9 GetQueueFuncFromID(int funcid)
{
switch(funcid)
{
case 0: return &ARMv5::StartExecARM;
case 1: return &ARMv5::ContExecARM;
case 2: return &ARMv5::StartExecTHUMB;
case 3: return &ARMv5::ContExecTHUMB;
case 4: return &ARMv5::AddExecute;
case 5: return &ARMv5::AddCycles_MW_2;
case 6: return &ARMv5::DelayIfITCM_2;
case 7: return &ARMv5::JumpTo_2;
case 8: return &ARMv5::JumpTo_3A;
case 9: return &ARMv5::JumpTo_3B;
case 10: return &ARMv5::JumpTo_3C;
case 11: return &ARMv5::JumpTo_4;
case 12: return &ARMv5::CodeRead32_2;
case 13: return &ARMv5::CodeRead32_3;
case 14: return &ARMv5::CodeRead32_4;
case 15: return &ARMv5::ICacheLookup_2;
case 16: return &ARMv5::DAbortHandle;
case 17: return &ARMv5::DCacheFin8;
case 18: return &ARMv5::DRead8_2;
case 19: return &ARMv5::DRead8_3;
case 20: return &ARMv5::DRead8_4;
case 21: return &ARMv5::DRead8_5;
case 22: return &ARMv5::DCacheFin16;
case 23: return &ARMv5::DRead16_2;
case 24: return &ARMv5::DRead16_3;
case 25: return &ARMv5::DRead16_4;
case 26: return &ARMv5::DRead16_5;
case 27: return &ARMv5::DCacheFin32;
case 28: return &ARMv5::DRead32_2;
case 29: return &ARMv5::DRead32_3;
case 30: return &ARMv5::DRead32_4;
case 31: return &ARMv5::DRead32_5;
case 32: return &ARMv5::DRead32S_2;
case 33: return &ARMv5::DRead32S_3;
case 34: return &ARMv5::DRead32S_4;
case 35: return &ARMv5::DRead32S_5A;
case 36: return &ARMv5::DRead32S_5B;
case 37: return &ARMv5::DWrite8_2;
case 38: return &ARMv5::DWrite8_3;
case 39: return &ARMv5::DWrite8_4;
case 40: return &ARMv5::DWrite8_5;
case 41: return &ARMv5::DWrite16_2;
case 42: return &ARMv5::DWrite16_3;
case 43: return &ARMv5::DWrite16_4;
case 44: return &ARMv5::DWrite16_5;
case 45: return &ARMv5::DWrite32_2;
case 46: return &ARMv5::DWrite32_3;
case 47: return &ARMv5::DWrite32_4;
case 48: return &ARMv5::DWrite32_5;
case 49: return &ARMv5::DWrite32S_2;
case 50: return &ARMv5::DWrite32S_3;
case 51: return &ARMv5::DWrite32S_4;
case 52: return &ARMv5::DWrite32S_5A;
case 53: return &ARMv5::DWrite32S_5B;
case 54: return &ARMv5::WBCheck_2;
case 55: return &ARMv5::ICachePrefetch_2;
case 56: return &ARMv5::DCacheLookup_2;
case 57: return &ARMv5::DCacheLookup_3;
case 58: return &ARMv5::DCClearAddr_2;
case 59: return &ARMv5::DCClearSetWay_2;
case 60: return &ARMv5::DCClearInvalidateAddr_2;
case 61: return &ARMv5::DCClearInvalidateSetWay_2;
case 62: return &ARMv5::SetupInterlock_2;
case 63: return &ARMv5::HandleInterlocksExecute_2;
case 64: return &ARMv5::HandleInterlocksMemory_2;
case 65: return &ARMv5::ForceInterlock_2;
case 66: return &ARMv5::QueueUpdateMode;
case 67: return &ARMv5::SignExtend8;
case 68: return &ARMv5::SignExtend16;
case 69: return &ARMv5::ROR32;
default: Platform::Log(Platform::LogLevel::Error, "ARM9: INVALID FUNCTION ID FOR LOADING SAVESTATES; EITHER THE SAVESTATE IS BORKED OR SOMEONE FORGOT TO UPDATE SERIALIZATION\n"); return nullptr;
}
}
// Queue Functions
void StartExecARM();
void ContExecARM();
@ -798,6 +952,7 @@ public:
void ROR32() { R[ExtReg] = ROR(R[ExtReg], ExtROROffs); }
u32 CP15Control; //! CP15 Register 1: Control Register
u32 RNGSeed; //! Global cache line fill seed. Used for pseudo random replacement strategy with the instruction and data cache
@ -861,8 +1016,6 @@ public:
void (ARMv5::*FuncQueue[32])(void);
u64 ITCMTimestamp;
u64 TimestampMemory;
u32 PC;
bool NullFetch;
bool Store;
s8 ITCMDelay;
u32 QueuedDCacheLine;
@ -921,6 +1074,8 @@ public:
void Reset() override;
void DoSavestate(Savestate* file) override;
void FillPipeline() override;
void JumpTo(u32 addr, bool restorecpsr = false, u8 R15 = 0) override;
@ -950,6 +1105,61 @@ public:
void QueueFunction(void (ARMv4::*QueueEntry)(void));
int GetIDFromQueueFunc(void (ARMv4::*funcptr)(void))
{
if (funcptr == &ARMv4::StartExecARM) return 0;
else if (funcptr == &ARMv4::StartExecTHUMB) return 1;
else if (funcptr == &ARMv4::UpdateNextInstr1) return 2;
else if (funcptr == &ARMv4::JumpTo_2) return 3;
else if (funcptr == &ARMv4::JumpTo_3A) return 4;
else if (funcptr == &ARMv4::JumpTo_3B) return 5;
else if (funcptr == &ARMv4::DRead8_2) return 6;
else if (funcptr == &ARMv4::DRead16_2) return 7;
else if (funcptr == &ARMv4::DRead32_2) return 8;
else if (funcptr == &ARMv4::DRead32S_2) return 9;
else if (funcptr == &ARMv4::DWrite8_2) return 10;
else if (funcptr == &ARMv4::DWrite16_2) return 11;
else if (funcptr == &ARMv4::DWrite32_2) return 12;
else if (funcptr == &ARMv4::DWrite32S_2) return 13;
else if (funcptr == &ARMv4::AddExecute) return 14;
else if (funcptr == &ARMv4::AddExtraCycle) return 15;
else if (funcptr == &ARMv4::QueueUpdateMode) return 16;
else if (funcptr == &ARMv4::SignExtend8) return 17;
else if (funcptr == &ARMv4::SignExtend16) return 18;
else if (funcptr == &ARMv4::ROR32) return 19;
else { Platform::Log(Platform::LogLevel::Error, "ARM7: INVALID FUNCTION POINTER FOR SAVESTATES; DID SOMEONE FORGET TO UPDATE SERIALIZATION?\n"); return -1; }
}
typedef void (ARMv4::*funcptrA7)(void);
funcptrA7 GetQueueFuncFromID(int funcid)
{
switch (funcid)
{
case 0: return &ARMv4::StartExecARM;
case 1: return &ARMv4::StartExecTHUMB;
case 2: return &ARMv4::UpdateNextInstr1;
case 3: return &ARMv4::JumpTo_2;
case 4: return &ARMv4::JumpTo_3A;
case 5: return &ARMv4::JumpTo_3B;
case 6: return &ARMv4::DRead8_2;
case 7: return &ARMv4::DRead16_2;
case 8: return &ARMv4::DRead32_2;
case 9: return &ARMv4::DRead32S_2;
case 10: return &ARMv4::DWrite8_2;
case 11: return &ARMv4::DWrite16_2;
case 12: return &ARMv4::DWrite32_2;
case 13: return &ARMv4::DWrite32S_2;
case 14: return &ARMv4::AddExecute;
case 15: return &ARMv4::AddExtraCycle;
case 16: return &ARMv4::QueueUpdateMode;
case 17: return &ARMv4::SignExtend8;
case 18: return &ARMv4::SignExtend16;
case 19: return &ARMv4::ROR32;
default: Platform::Log(Platform::LogLevel::Error, "ARM7: INVALID FUNCTION ID FOR LOADING SAVESTATES; EITHER THE SAVESTATE IS BORKED OR SOMEONE FORGOT TO UPDATE SERIALIZATION\n"); return nullptr;
}
}
// Queue Functions
void StartExecARM();
void StartExecTHUMB();
void UpdateNextInstr1() { NextInstr[1] = RetVal; }

View File

@ -110,6 +110,7 @@ void DMA::DoSavestate(Savestate* file)
file->Var32(&MRAMBurstCount);
file->Bool32(&Executing);
file->Bool32(&Stall);
file->Bool32(&DMAQueued);
file->VarArray(MRAMBurstTable.data(), sizeof(MRAMBurstTable));
}

View File

@ -691,15 +691,26 @@ bool NDS::DoSavestate(Savestate* file)
}
file->Var32(&SchedListMask);
file->Var64(&ARM9Timestamp);
file->Var64(&DMA9Timestamp);
file->Var64(&ARM9Target);
file->Var64(&ARM7Timestamp);
file->Var64(&ARM7Target);
file->Var64(&SysTimestamp);
file->Var64(&MainRAMTimestamp);
file->Var64(&MainRAMBurstStart);
file->Var64(&A9ContentionTS);
file->Bool32(&ConTSLock);
file->Var64(&LastSysClockCycles);
file->Var64(&FrameStartTimestamp);
file->Var32(&NumFrames);
file->Var32(&NumLagFrames);
file->Bool32(&LagFrameFlag);
file->VarArray(DMAReadHold, sizeof(DMAReadHold));
file->VarArray(DMAsQueued, sizeof(DMAsQueued));
file->Var8(&DMAQueuePtr);
file->Bool32(&MainRAMBork);
file->Bool32(&MainRAMLastAccess);
file->Bool32(&DMALastWasMainRAM);
// TODO: save KeyInput????
file->VarArray(KeyCnt, 2*sizeof(u16));

View File

@ -1485,6 +1485,7 @@ void NDSCartSlot::DoSavestate(Savestate* file) noexcept
file->Var32(&TransferLen);
file->Var32(&TransferDir);
file->VarArray(TransferCmd.data(), sizeof(TransferCmd));
file->Var64(&ROMTransferTime);
// cart inserted/len/ROM/etc should be already populated
// savestate should be loaded after the right game is loaded