Skip to content

Commit

Permalink
Debugger: Step Back - Allow stepping back 1 scanline/frame at a time …
Browse files Browse the repository at this point in the history
…+ remove restriction that caused step back to only work for up to 30 frames
  • Loading branch information
SourMesen committed Jan 26, 2024
1 parent 71ecaff commit aa9c459
Show file tree
Hide file tree
Showing 27 changed files with 210 additions and 54 deletions.
2 changes: 1 addition & 1 deletion Core/Debugger/Debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ void Debugger::Step(CpuType cpuType, int32_t stepCount, StepType type, BreakSour
if(type != StepType::StepBack) {
debugger->ResetStepBackCache();
} else {
debugger->StepBack();
debugger->StepBack(stepCount);
}

debugger->Step(stepCount, type);
Expand Down
3 changes: 2 additions & 1 deletion Core/Debugger/IDebugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class IDebugger
bool CheckStepBack() { return _stepBackManager->CheckStepBack(); }
bool IsStepBack() { return _stepBackManager->IsRewinding(); }
void ResetStepBackCache() { return _stepBackManager->ResetCache(); }
void StepBack() { return _stepBackManager->StepBack(); }
void StepBack(int32_t stepCount) { return _stepBackManager->StepBack((StepBackType)stepCount); }
virtual StepBackConfig GetStepBackConfig() { return { GetCpuCycleCount(), 0, 0 }; }

FrozenAddressManager& GetFrozenAddressManager() { return _frozenAddressManager; }

Expand Down
36 changes: 21 additions & 15 deletions Core/Debugger/StepBackManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,20 @@ StepBackManager::StepBackManager(Emulator* emu, IDebugger* debugger)
_debugger = debugger;
}

void StepBackManager::StepBack()
void StepBackManager::StepBack(StepBackType type)
{
if(!_active) {
_targetClock = _debugger->GetCpuCycleCount();
StepBackConfig cfg = _debugger->GetStepBackConfig();

int64_t target = 0;
switch(type) {
default: case StepBackType::Instruction: target = cfg.CurrentCycle; break;
case StepBackType::Scanline: target = (int64_t)cfg.CurrentCycle - cfg.CyclesPerScanline; break;
case StepBackType::Frame: target = (int64_t)cfg.CurrentCycle - cfg.CyclesPerFrame; break;
}

_targetClock = (uint64_t)std::max<int64_t>(0, target);

_active = true;
_allowRetry = true;
_stateClockLimit = StepBackManager::DefaultClockLimit;
Expand All @@ -29,7 +39,7 @@ bool StepBackManager::CheckStepBack()
return false;
}

uint64_t clock = _debugger->GetCpuCycleCount();
uint64_t clock = _debugger->GetStepBackConfig().CurrentCycle;

if(!_rewindManager->IsStepBack()) {
if(_cache.size() > 1) {
Expand All @@ -39,11 +49,9 @@ bool StepBackManager::CheckStepBack()
_cache.pop_back();
if(_cache.size()) {
//If cache isn't empty, load the last state
_rewindManager->SetIgnoreLoadState(true);
_emu->Deserialize(_cache.back().SaveState, SaveStateManager::FileFormatVersion, true);
_rewindManager->SetIgnoreLoadState(false);
_emu->Deserialize(_cache.back().SaveState, SaveStateManager::FileFormatVersion, true, std::nullopt, false);

_emu->GetRewindManager()->StopRewinding(true);
_emu->GetRewindManager()->StopRewinding(true, true);
_active = false;
_prevClock = clock;
return true;
Expand All @@ -57,7 +65,7 @@ bool StepBackManager::CheckStepBack()
//Start rewinding on next instruction after StepBack() is called
_cache.clear();
_rewindManager->StartRewinding(true);
clock = _debugger->GetCpuCycleCount();
clock = _debugger->GetStepBackConfig().CurrentCycle;
}

if(clock < _targetClock && _targetClock - clock < _stateClockLimit) {
Expand All @@ -70,10 +78,8 @@ bool StepBackManager::CheckStepBack()
if(clock >= _targetClock) {
//If the CPU is back to where it was before step back, check if the cache contains data
if(_cache.size() > 0) {
//If it does, load the last state
_rewindManager->SetIgnoreLoadState(true);
_emu->Deserialize(_cache.back().SaveState, SaveStateManager::FileFormatVersion, true);
_rewindManager->SetIgnoreLoadState(false);
_emu->Deserialize(_cache.back().SaveState, SaveStateManager::FileFormatVersion, true, std::nullopt, false);
_rewindManager->StopRewinding(true, true);
} else if(_allowRetry && clock > _prevClock && (clock - _prevClock) > StepBackManager::DefaultClockLimit) {
//Cache is empty, this can happen when a single instruction takes more than X clocks (e.g block transfers, dma)
//In this case, re-run the step back process again but start recordings state earlier
Expand All @@ -82,10 +88,10 @@ bool StepBackManager::CheckStepBack()
_stateClockLimit = (clock - _prevClock) + StepBackManager::DefaultClockLimit;
_allowRetry = false;
return false;
} else {
//Stop rewinding, even if the target was not found
_rewindManager->StopRewinding(true);
}

//Stop rewinding, even if the target was not found
_rewindManager->StopRewinding(true);
_active = false;
_prevClock = clock;
return true;
Expand Down
16 changes: 15 additions & 1 deletion Core/Debugger/StepBackManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ struct StepBackCacheEntry
uint64_t Clock;
};

struct StepBackConfig
{
uint64_t CurrentCycle;
uint32_t CyclesPerScanline;
uint32_t CyclesPerFrame;
};

enum class StepBackType
{
Instruction,
Scanline,
Frame
};

class StepBackManager
{
private:
Expand All @@ -30,7 +44,7 @@ class StepBackManager
public:
StepBackManager(Emulator* emu, IDebugger* debugger);

void StepBack();
void StepBack(StepBackType type);
bool CheckStepBack();

void ResetCache() { _cache.clear(); }
Expand Down
9 changes: 9 additions & 0 deletions Core/Gameboy/Debugger/GbDebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,15 @@ void GbDebugger::Step(int32_t stepCount, StepType type)
_step.reset(new StepRequest(step));
}

StepBackConfig GbDebugger::GetStepBackConfig()
{
return {
GetCpuCycleCount(),
456,
456 * 154
};
}

void GbDebugger::DrawPartialFrame()
{
_ppu->DebugSendFrame();
Expand Down
1 change: 1 addition & 0 deletions Core/Gameboy/Debugger/GbDebugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class GbDebugger final : public IDebugger

void Run() override;
void Step(int32_t stepCount, StepType type) override;
StepBackConfig GetStepBackConfig() override;

void DrawPartialFrame() override;

Expand Down
9 changes: 9 additions & 0 deletions Core/NES/Debugger/NesDebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,15 @@ void NesDebugger::Step(int32_t stepCount, StepType type)
_step.reset(new StepRequest(step));
}

StepBackConfig NesDebugger::GetStepBackConfig()
{
return {
GetCpuCycleCount(),
341 / 3,
341u * _console->GetPpu()->GetScanlineCount() / 3
};
}

void NesDebugger::DrawPartialFrame()
{
_console->GetPpu()->DebugSendFrame();
Expand Down
1 change: 1 addition & 0 deletions Core/NES/Debugger/NesDebugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class NesDebugger final : public IDebugger

void Run() override;
void Step(int32_t stepCount, StepType type) override;
StepBackConfig GetStepBackConfig() override;

void DrawPartialFrame() override;

Expand Down
9 changes: 9 additions & 0 deletions Core/PCE/Debugger/PceDebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,15 @@ void PceDebugger::Step(int32_t stepCount, StepType type)
_step.reset(new StepRequest(step));
}

StepBackConfig PceDebugger::GetStepBackConfig()
{
return {
_memoryManager->GetState().CycleCount,
PceConstants::ClockPerScanline,
PceConstants::ClockPerScanline * _vce->GetScanlineCount()
};
}

void PceDebugger::DrawPartialFrame()
{
_vpc->DebugSendFrame();
Expand Down
1 change: 1 addition & 0 deletions Core/PCE/Debugger/PceDebugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class PceDebugger final : public IDebugger

void Run() override;
void Step(int32_t stepCount, StepType type) override;
StepBackConfig GetStepBackConfig() override;

void DrawPartialFrame() override;

Expand Down
11 changes: 10 additions & 1 deletion Core/SMS/Debugger/SmsDebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,22 @@ void SmsDebugger::Step(int32_t stepCount, StepType type)
case StepType::CpuCycleStep: step.CpuCycleStepCount = stepCount; break;
case StepType::PpuStep: step.PpuStepCount = stepCount; break;
case StepType::PpuScanline: step.PpuStepCount = 342 * stepCount; break;
case StepType::PpuFrame: step.PpuStepCount = 342 * (_console->GetModel() == SmsModel::Sms && _console->GetRegion() == ConsoleRegion::Pal ? 313 : 262) * stepCount; break;
case StepType::PpuFrame: step.PpuStepCount = 342 * _vdp->GetScanlineCount() * stepCount; break;
case StepType::SpecificScanline: step.BreakScanline = stepCount; break;
}

_step.reset(new StepRequest(step));
}

StepBackConfig SmsDebugger::GetStepBackConfig()
{
return {
_cpu->GetCycleCount() * 3,
342 * 2,
342u * 2 * _vdp->GetScanlineCount()
};
}

void SmsDebugger::DrawPartialFrame()
{
_vdp->DebugSendFrame();
Expand Down
1 change: 1 addition & 0 deletions Core/SMS/Debugger/SmsDebugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class SmsDebugger final : public IDebugger

void Run() override;
void Step(int32_t stepCount, StepType type) override;
StepBackConfig GetStepBackConfig() override;

void DrawPartialFrame() override;

Expand Down
13 changes: 13 additions & 0 deletions Core/SNES/Debugger/SnesDebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,19 @@ void SnesDebugger::Step(int32_t stepCount, StepType type)
_step.reset(new StepRequest(step));
}

StepBackConfig SnesDebugger::GetStepBackConfig()
{
if(_cpuType == CpuType::Snes) {
return {
_memoryManager->GetMasterClock(),
1364,
1364u * (_ppu->GetVblankEndScanline() + 1)
};
} else {
return IDebugger::GetStepBackConfig();
}
}

void SnesDebugger::DrawPartialFrame()
{
_ppu->DebugSendFrame();
Expand Down
1 change: 1 addition & 0 deletions Core/SNES/Debugger/SnesDebugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class SnesDebugger final : public IDebugger

void Run() override;
void Step(int32_t stepCount, StepType type) override;
StepBackConfig GetStepBackConfig() override;

void DrawPartialFrame() override;

Expand Down
6 changes: 4 additions & 2 deletions Core/Shared/Emulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,7 @@ void Emulator::Serialize(ostream& out, bool includeSettings, int compressionLeve
s.SaveTo(out, compressionLevel);
}

bool Emulator::Deserialize(istream& in, uint32_t fileFormatVersion, bool includeSettings, optional<ConsoleType> srcConsoleType)
bool Emulator::Deserialize(istream& in, uint32_t fileFormatVersion, bool includeSettings, optional<ConsoleType> srcConsoleType, bool sendNotification)
{
Serializer s(fileFormatVersion, false);
if(!s.LoadFrom(in)) {
Expand Down Expand Up @@ -931,7 +931,9 @@ bool Emulator::Deserialize(istream& in, uint32_t fileFormatVersion, bool include

s.Stream(_console, "");

_notificationManager->SendNotification(ConsoleNotificationType::StateLoaded);
if(sendNotification) {
_notificationManager->SendNotification(ConsoleNotificationType::StateLoaded);
}
return true;
}

Expand Down
2 changes: 1 addition & 1 deletion Core/Shared/Emulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class Emulator
void SuspendDebugger(bool release);

void Serialize(ostream& out, bool includeSettings, int compressionLevel = 1);
bool Deserialize(istream& in, uint32_t fileFormatVersion, bool includeSettings, optional<ConsoleType> consoleType = std::nullopt);
bool Deserialize(istream& in, uint32_t fileFormatVersion, bool includeSettings, optional<ConsoleType> consoleType = std::nullopt, bool sendNotification = true);

SoundMixer* GetSoundMixer() { return _soundMixer.get(); }
VideoRenderer* GetVideoRenderer() { return _videoRenderer.get(); }
Expand Down
4 changes: 2 additions & 2 deletions Core/Shared/RewindData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ void RewindData::ProcessXorState(T& data, deque<RewindData>& prevStates, int32_t
}
}

void RewindData::LoadState(Emulator* emu, deque<RewindData>& prevStates, int32_t position)
void RewindData::LoadState(Emulator* emu, deque<RewindData>& prevStates, int32_t position, bool sendNotification)
{
if(_saveStateData.size() == 0) {
return;
Expand All @@ -54,7 +54,7 @@ void RewindData::LoadState(Emulator* emu, deque<RewindData>& prevStates, int32_t
stream.write((char*)data.data(), data.size());
stream.seekg(0, ios::beg);

emu->Deserialize(stream, SaveStateManager::FileFormatVersion, true);
emu->Deserialize(stream, SaveStateManager::FileFormatVersion, true, std::nullopt, sendNotification);
}

void RewindData::SaveState(Emulator* emu, deque<RewindData>& prevStates, int32_t position)
Expand Down
2 changes: 1 addition & 1 deletion Core/Shared/RewindData.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ class RewindData
void GetStateData(stringstream& stateData, deque<RewindData>& prevStates, int32_t position);
uint32_t GetStateSize() { return (uint32_t)_saveStateData.size(); }

void LoadState(Emulator* emu, deque<RewindData>& prevStates, int32_t position = -1);
void LoadState(Emulator* emu, deque<RewindData>& prevStates, int32_t position = -1, bool sendNotification = true);
void SaveState(Emulator* emu, deque<RewindData>& prevStates, int32_t position = -1);
};
Loading

0 comments on commit aa9c459

Please sign in to comment.