Compare commits

...

4 Commits

Author SHA1 Message Date
Jaklyy 9e60f45481 implement savestate serialization 2024-12-31 12:30:49 -05:00
Jaklyy 4071099a1f Revert "redo a bunch of gx handling for marginal accuracy benefits"
This reverts commit cc9a6f8ec1.
2024-12-29 07:08:14 -05:00
Jaklyy d2737f666c minor error 2024-12-29 00:04:49 -05:00
Jaklyy cc9a6f8ec1 redo a bunch of gx handling for marginal accuracy benefits 2024-12-28 23:27:42 -05:00
5 changed files with 344 additions and 8 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);
}
}
}

218
src/ARM.h
View File

@ -724,6 +724,160 @@ public:
u32 CP15Read(const u32 id) const;
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();
@ -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
@ -855,14 +1010,12 @@ public:
u8 MemTimings[0x40000][3];
bool (*GetMemRegion)(u32 addr, bool write, MemRegion* region);
alignas(64) void (ARMv5::*DelayedQueue)(void); // adding more than one new entry to the queue while it's already active does not work. so uh. we use this to work around that. it's less than ideal...
void (ARMv5::*StartExec)(void);
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;
@ -949,7 +1104,62 @@ public:
void AddCycles_CD() override;
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));
@ -1185,7 +1196,7 @@ void NDS::MainRAMHandleARM9()
if (burststart <= 1) dma->Running = 1;
else dma->Running = 2;
if ((dma->IterCount == 0) || ((ARM9Regions[dma->CurSrcAddr>>14] != Mem9_MainRAM) && (ARM9Regions[dma->CurDstAddr>>14] != Mem9_MainRAM)) || (DMA9Timestamp > ARM9Target))
if ((dma->IterCount == 0) || ((ARM9Regions[dma->CurSrcAddr>>14] != Mem9_MainRAM) && (ARM9Regions[dma->CurDstAddr>>14] != Mem9_MainRAM)) || (DMA9Timestamp >= ARM9Target))
memset(&ARM9.MRTrack, 0, sizeof(ARM9.MRTrack));
else
ARM9.MRTrack.Progress = 0;
@ -1297,7 +1308,7 @@ void NDS::MainRAMHandleARM9()
if (burststart <= 1) dma->Running = 1;
else dma->Running = 2;
if ((dma->IterCount == 0) || ((ARM9Regions[dma->CurSrcAddr>>14] != Mem9_MainRAM) && (ARM9Regions[dma->CurDstAddr>>14] != Mem9_MainRAM)) || (DMA9Timestamp > ARM9Target))
if ((dma->IterCount == 0) || ((ARM9Regions[dma->CurSrcAddr>>14] != Mem9_MainRAM) && (ARM9Regions[dma->CurDstAddr>>14] != Mem9_MainRAM)) || (DMA9Timestamp >= ARM9Target))
memset(&ARM9.MRTrack, 0, sizeof(ARM9.MRTrack));
else
ARM9.MRTrack.Progress = 0;
@ -1553,7 +1564,7 @@ void NDS::MainRAMHandleARM7()
if (burststart <= 1) dma->Running = 1;
else dma->Running = 2;
if ((dma->IterCount == 0) || ((ARM7Regions[dma->CurSrcAddr>>15] != Mem7_MainRAM) && (ARM7Regions[dma->CurDstAddr>>15] != Mem7_MainRAM)) || (ARM7Timestamp > ARM7Target))
if ((dma->IterCount == 0) || ((ARM7Regions[dma->CurSrcAddr>>15] != Mem7_MainRAM) && (ARM7Regions[dma->CurDstAddr>>15] != Mem7_MainRAM)) || (ARM7Timestamp >= ARM7Target))
memset(&ARM7.MRTrack, 0, sizeof(ARM7.MRTrack));
else
ARM7.MRTrack.Progress = 0;
@ -1659,7 +1670,7 @@ void NDS::MainRAMHandleARM7()
if (burststart <= 1) dma->Running = 1;
else dma->Running = 2;
if ((dma->IterCount == 0) || ((ARM7Regions[dma->CurSrcAddr>>15] != Mem7_MainRAM) && (ARM7Regions[dma->CurDstAddr>>15] != Mem7_MainRAM)) || (ARM7Timestamp > ARM7Target))
if ((dma->IterCount == 0) || ((ARM7Regions[dma->CurSrcAddr>>15] != Mem7_MainRAM) && (ARM7Regions[dma->CurDstAddr>>15] != Mem7_MainRAM)) || (ARM7Timestamp >= ARM7Target))
memset(&ARM7.MRTrack, 0, sizeof(ARM7.MRTrack));
else
ARM7.MRTrack.Progress = 0;

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