Debugger: Step into, step over, run ppu cycle/scanline/frame (+ call stack fixes)

This commit is contained in:
Sour 2019-03-24 16:42:52 -04:00
parent 75361b4bce
commit 0e26e5317d
13 changed files with 165 additions and 79 deletions

View file

@ -32,9 +32,11 @@ void CallstackManager::Pop(uint32_t destAddress)
return;
}
uint32_t returnAddr = _callstack.back().Return;
StackFrameInfo prevFrame = _callstack.back();
_callstack.pop_back();
uint32_t returnAddr = prevFrame.Return;
if(!_callstack.empty() && destAddress != returnAddr) {
//Mismatch, pop that stack frame and add the new one
bool foundMatch = false;
@ -65,4 +67,13 @@ void CallstackManager::GetCallstack(StackFrameInfo* callstackArray, uint32_t &ca
i++;
}
callstackSize = i;
}
}
int32_t CallstackManager::GetReturnAddress()
{
DebugBreakHelper helper(_debugger);
if(_callstack.empty()) {
return -1;
}
return _callstack.back().Return;
}

View file

@ -17,4 +17,5 @@ public:
void Pop(uint32_t destAddr);
void GetCallstack(StackFrameInfo* callstackArray, uint32_t &callstackSize);
int32_t GetReturnAddress();
};

View file

@ -593,6 +593,13 @@ void Console::ProcessPpuCycle()
}
}
void Console::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi)
{
if(_debugger) {
_debugger->ProcessInterrupt(originalPc, currentPc, forNmi);
}
}
void Console::ProcessEvent(EventType type)
{
if(_debugger) {

View file

@ -127,5 +127,6 @@ public:
void ProcessWorkRamRead(uint32_t addr, uint8_t value);
void ProcessWorkRamWrite(uint32_t addr, uint8_t value);
void ProcessPpuCycle();
void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi);
void ProcessEvent(EventType type);
};

View file

@ -73,12 +73,14 @@ void Cpu::Exec()
//Use the state of the IRQ/NMI flags on the previous cycle to determine if an IRQ is processed or not
if(_state.PrevNmiFlag) {
uint32_t originalPc = GetProgramAddress(_state.PC);
ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyNmiVector : Cpu::NmiVector);
_console->ProcessEvent(EventType::Nmi);
_console->ProcessInterrupt(originalPc, GetProgramAddress(_state.PC), true);
_state.NmiFlag = false;
} else if(_state.PrevIrqSource && !CheckFlag(ProcFlags::IrqDisable)) {
uint32_t originalPc = GetProgramAddress(_state.PC);
ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyIrqVector : Cpu::IrqVector);
_console->ProcessEvent(EventType::Irq);
_console->ProcessInterrupt(originalPc, GetProgramAddress(_state.PC), false);
}
}

View file

@ -204,4 +204,12 @@ struct StackFrameInfo
uint32_t Target;
uint32_t Return;
StackFrameFlags Flags;
};
};
enum class StepType
{
CpuStep,
CpuStepOut,
CpuStepOver,
PpuStep,
};

View file

@ -109,6 +109,8 @@ void Debugger::ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType
_callstackManager->Pop(pc);
}
ProcessStepConditions(_prevOpCode, pc);
_prevOpCode = value;
_prevProgramCounter = pc;
@ -187,6 +189,35 @@ void Debugger::ProcessPpuCycle()
uint16_t scanline = _ppu->GetState().Scanline;
uint16_t cycle = _ppu->GetState().Cycle;
_ppuTools->UpdateViewers(scanline, cycle);
if(_ppuStepCount > 0) {
_ppuStepCount--;
if(_ppuStepCount == 0) {
_cpuStepCount = 0;
SleepUntilResume();
}
}
}
void Debugger::SleepUntilResume()
{
_console->GetSoundMixer()->StopAudio();
_disassembler->Disassemble();
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak);
_executionStopped = true;
while(_cpuStepCount == 0 || _breakRequestCount) {
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(10));
}
_executionStopped = false;
}
void Debugger::ProcessStepConditions(uint8_t opCode, uint32_t currentPc)
{
if(_breakAddress == currentPc && (opCode == 0x60 || opCode == 0x40 || opCode == 0x6B || opCode == 0x44 || opCode == 0x54)) {
//RTS/RTL/RTI found, if we're on the expected return address, break immediately (for step over/step out)
_cpuStepCount = 0;
}
}
void Debugger::ProcessBreakConditions(MemoryOperationInfo &operation, AddressInfo &addressInfo)
@ -196,31 +227,19 @@ void Debugger::ProcessBreakConditions(MemoryOperationInfo &operation, AddressInf
}
if(_cpuStepCount == 0 || _breakRequestCount) {
_console->GetSoundMixer()->StopAudio();
_disassembler->Disassemble();
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak);
_executionStopped = true;
while(_cpuStepCount == 0 || _breakRequestCount) {
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(10));
}
_executionStopped = false;
SleepUntilResume();
}
}
void Debugger::ProcessInterrupt(bool forNmi)
void Debugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi)
{
CpuState state = _cpu->GetState();
_callstackManager->Push(_prevProgramCounter, (state.K << 16) | state.PC, _prevProgramCounter, forNmi ? StackFrameFlags::Nmi : StackFrameFlags::Irq);
_callstackManager->Push(_prevProgramCounter, currentPc, originalPc, forNmi ? StackFrameFlags::Nmi : StackFrameFlags::Irq);
_eventManager->AddEvent(forNmi ? DebugEventType::Nmi : DebugEventType::Irq);
}
void Debugger::ProcessEvent(EventType type)
{
switch(type) {
case EventType::Nmi: ProcessInterrupt(true); break;
case EventType::Irq: ProcessInterrupt(false); break;
case EventType::StartFrame:
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::EventViewerRefresh);
_eventManager->ClearFrameEvents();
@ -244,11 +263,45 @@ int32_t Debugger::EvaluateExpression(string expression, EvalResultType &resultTy
void Debugger::Run()
{
_cpuStepCount = -1;
_breakAddress = -1;
_ppuStepCount = -1;
}
void Debugger::Step(int32_t stepCount)
void Debugger::Step(int32_t stepCount, StepType type)
{
_cpuStepCount = stepCount;
switch(type) {
case StepType::CpuStep:
_cpuStepCount = stepCount;
_breakAddress = -1;
_ppuStepCount = -1;
break;
case StepType::CpuStepOut:
_breakAddress = _callstackManager->GetReturnAddress();
_cpuStepCount = -1;
_ppuStepCount = -1;
break;
case StepType::CpuStepOver:
if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC || _prevOpCode == 0x00 || _prevOpCode == 0x02 || _prevOpCode == 0x44 || _prevOpCode == 0x54) {
//JSR, JSL, BRK, COP, MVP, MVN
_breakAddress = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + DisassemblyInfo::GetOperandSize(_prevOpCode, 0) + 1) & 0xFFFF);
_cpuStepCount = -1;
_ppuStepCount = -1;
} else {
//For any other instruction, step over is the same as step into
_cpuStepCount = 1;
_breakAddress = -1;
_ppuStepCount = -1;
}
break;
case StepType::PpuStep:
_ppuStepCount = stepCount;
_cpuStepCount = -1;
_breakAddress = -1;
break;
}
}
bool Debugger::IsExecutionStopped()

View file

@ -2,6 +2,7 @@
#include "stdafx.h"
#include "CpuTypes.h"
#include "PpuTypes.h"
#include "DebugTypes.h"
class Console;
class Cpu;
@ -20,15 +21,8 @@ class EventManager;
class CallstackManager;
enum class EventType;
enum class SnesMemoryType;
enum class MemoryOperationType;
enum class BreakpointCategory;
enum class EvalResultType : int32_t;
struct DebugState;
struct MemoryOperationInfo;
struct AddressInfo;
class Debugger
{
private:
@ -53,12 +47,15 @@ private:
atomic<uint32_t> _breakRequestCount;
atomic<int32_t> _cpuStepCount;
atomic<int32_t> _ppuStepCount;
atomic<int32_t> _breakAddress;
uint8_t _prevOpCode = 0;
uint32_t _prevProgramCounter = 0;
void SleepUntilResume();
void ProcessStepConditions(uint8_t opCode, uint32_t currentPc);
void ProcessBreakConditions(MemoryOperationInfo &operation, AddressInfo &addressInfo);
void ProcessInterrupt(bool forNmi);
public:
Debugger(shared_ptr<Console> console);
@ -75,12 +72,13 @@ public:
void ProcessPpuWrite(uint16_t addr, uint8_t value, SnesMemoryType memoryType);
void ProcessPpuCycle();
void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi);
void ProcessEvent(EventType type);
int32_t EvaluateExpression(string expression, EvalResultType &resultType, bool useCache);
void Run();
void Step(int32_t stepCount);
void Step(int32_t stepCount, StepType type = StepType::CpuStep);
bool IsExecutionStopped();
void BreakRequest(bool release);

View file

@ -38,7 +38,7 @@ extern "C"
DllExport bool __stdcall IsExecutionStopped() { return GetDebugger()->IsExecutionStopped(); }
DllExport void __stdcall ResumeExecution() { if(IsDebuggerRunning()) GetDebugger()->Run(); }
DllExport void __stdcall Step(uint32_t count) { GetDebugger()->Step(count); }
DllExport void __stdcall Step(uint32_t count, StepType type) { GetDebugger()->Step(count, type); }
DllExport void __stdcall GetDisassemblyLineData(uint32_t lineIndex, CodeLineData &data) { GetDebugger()->GetDisassembler()->GetLineData(lineIndex, data); }
DllExport uint32_t __stdcall GetDisassemblyLineCount() { return GetDebugger()->GetDisassembler()->GetLineCount(); }

View file

@ -304,6 +304,12 @@ namespace Mesen.GUI.Debugger.Controls
} else if(keyData == ConfigManager.Config.Debug.Shortcuts.Find) {
this.OpenSearchBox(true);
return true;
} else if(keyData == ConfigManager.Config.Debug.Shortcuts.FindNext) {
this.FindNext();
return true;
} else if(keyData == ConfigManager.Config.Debug.Shortcuts.FindPrev) {
this.FindPrevious();
return true;
}
if(keyData == (Keys.Control | Keys.F)) {

View file

@ -49,7 +49,7 @@
this.mnuDisableEnableBreakpoint = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
this.mnuRunCpuCycle = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRun1000Cycles = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRun1000Instructions = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRunPpuCycle = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRunScanline = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRunOneFrame = new System.Windows.Forms.ToolStripMenuItem();
@ -113,7 +113,7 @@
this.mnuDisableEnableBreakpoint,
this.toolStripMenuItem2,
this.mnuRunCpuCycle,
this.mnuRun1000Cycles,
this.mnuRun1000Instructions,
this.mnuRunPpuCycle,
this.mnuRunScanline,
this.mnuRunOneFrame,
@ -128,9 +128,8 @@
//
this.mnuContinue.Image = global::Mesen.GUI.Properties.Resources.MediaPlay;
this.mnuContinue.Name = "mnuContinue";
this.mnuContinue.Size = new System.Drawing.Size(212, 22);
this.mnuContinue.Size = new System.Drawing.Size(213, 22);
this.mnuContinue.Text = "Continue";
this.mnuContinue.Click += new System.EventHandler(this.mnuContinue_Click);
//
// mnuBreak
//
@ -138,136 +137,134 @@
this.mnuBreak.Image = global::Mesen.GUI.Properties.Resources.MediaPause;
this.mnuBreak.Name = "mnuBreak";
this.mnuBreak.ShortcutKeyDisplayString = "";
this.mnuBreak.Size = new System.Drawing.Size(212, 22);
this.mnuBreak.Size = new System.Drawing.Size(213, 22);
this.mnuBreak.Text = "Break";
//
// toolStripMenuItem3
//
this.toolStripMenuItem3.Name = "toolStripMenuItem3";
this.toolStripMenuItem3.Size = new System.Drawing.Size(209, 6);
this.toolStripMenuItem3.Size = new System.Drawing.Size(210, 6);
//
// mnuStepInto
//
this.mnuStepInto.Image = global::Mesen.GUI.Properties.Resources.StepInto;
this.mnuStepInto.Name = "mnuStepInto";
this.mnuStepInto.Size = new System.Drawing.Size(212, 22);
this.mnuStepInto.Size = new System.Drawing.Size(213, 22);
this.mnuStepInto.Text = "Step Into";
this.mnuStepInto.Click += new System.EventHandler(this.mnuStepInto_Click);
//
// mnuStepOver
//
this.mnuStepOver.Image = global::Mesen.GUI.Properties.Resources.StepOver;
this.mnuStepOver.Name = "mnuStepOver";
this.mnuStepOver.Size = new System.Drawing.Size(212, 22);
this.mnuStepOver.Size = new System.Drawing.Size(213, 22);
this.mnuStepOver.Text = "Step Over";
//
// mnuStepOut
//
this.mnuStepOut.Image = global::Mesen.GUI.Properties.Resources.StepOut;
this.mnuStepOut.Name = "mnuStepOut";
this.mnuStepOut.Size = new System.Drawing.Size(212, 22);
this.mnuStepOut.Size = new System.Drawing.Size(213, 22);
this.mnuStepOut.Text = "Step Out";
//
// mnuStepBack
//
this.mnuStepBack.Image = global::Mesen.GUI.Properties.Resources.StepBack;
this.mnuStepBack.Name = "mnuStepBack";
this.mnuStepBack.Size = new System.Drawing.Size(212, 22);
this.mnuStepBack.Size = new System.Drawing.Size(213, 22);
this.mnuStepBack.Text = "Step Back";
//
// toolStripMenuItem1
//
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(209, 6);
this.toolStripMenuItem1.Size = new System.Drawing.Size(210, 6);
//
// mnuReset
//
this.mnuReset.Image = global::Mesen.GUI.Properties.Resources.Refresh;
this.mnuReset.Name = "mnuReset";
this.mnuReset.Size = new System.Drawing.Size(212, 22);
this.mnuReset.Size = new System.Drawing.Size(213, 22);
this.mnuReset.Text = "Reset";
//
// mnuPowerCycle
//
this.mnuPowerCycle.Image = global::Mesen.GUI.Properties.Resources.PowerCycle;
this.mnuPowerCycle.Name = "mnuPowerCycle";
this.mnuPowerCycle.Size = new System.Drawing.Size(212, 22);
this.mnuPowerCycle.Size = new System.Drawing.Size(213, 22);
this.mnuPowerCycle.Text = "Power Cycle";
//
// toolStripMenuItem24
//
this.toolStripMenuItem24.Name = "toolStripMenuItem24";
this.toolStripMenuItem24.Size = new System.Drawing.Size(209, 6);
this.toolStripMenuItem24.Size = new System.Drawing.Size(210, 6);
//
// mnuToggleBreakpoint
//
this.mnuToggleBreakpoint.Image = global::Mesen.GUI.Properties.Resources.Breakpoint;
this.mnuToggleBreakpoint.Name = "mnuToggleBreakpoint";
this.mnuToggleBreakpoint.Size = new System.Drawing.Size(212, 22);
this.mnuToggleBreakpoint.Size = new System.Drawing.Size(213, 22);
this.mnuToggleBreakpoint.Text = "Toggle Breakpoint";
//
// mnuDisableEnableBreakpoint
//
this.mnuDisableEnableBreakpoint.Image = global::Mesen.GUI.Properties.Resources.BreakpointDisabled;
this.mnuDisableEnableBreakpoint.Name = "mnuDisableEnableBreakpoint";
this.mnuDisableEnableBreakpoint.Size = new System.Drawing.Size(212, 22);
this.mnuDisableEnableBreakpoint.Size = new System.Drawing.Size(213, 22);
this.mnuDisableEnableBreakpoint.Text = "Disable/Enable Breakpoint";
//
// toolStripMenuItem2
//
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
this.toolStripMenuItem2.Size = new System.Drawing.Size(209, 6);
this.toolStripMenuItem2.Size = new System.Drawing.Size(210, 6);
//
// mnuRunCpuCycle
//
this.mnuRunCpuCycle.Image = global::Mesen.GUI.Properties.Resources.JumpTarget;
this.mnuRunCpuCycle.Name = "mnuRunCpuCycle";
this.mnuRunCpuCycle.Size = new System.Drawing.Size(212, 22);
this.mnuRunCpuCycle.Size = new System.Drawing.Size(213, 22);
this.mnuRunCpuCycle.Text = "Run one CPU cycle";
//
// mnuRun1000Cycles
// mnuRun1000Instructions
//
this.mnuRun1000Cycles.Name = "mnuRun1000Cycles";
this.mnuRun1000Cycles.Size = new System.Drawing.Size(212, 22);
this.mnuRun1000Cycles.Text = "Run 1000 CPU cycles";
this.mnuRun1000Cycles.Click += new System.EventHandler(this.mnuRun1000Cycles_Click);
this.mnuRun1000Instructions.Name = "mnuRun1000Instructions";
this.mnuRun1000Instructions.Size = new System.Drawing.Size(213, 22);
this.mnuRun1000Instructions.Text = "Run 1000 CPU instructions";
//
// mnuRunPpuCycle
//
this.mnuRunPpuCycle.Image = global::Mesen.GUI.Properties.Resources.RunPpuCycle;
this.mnuRunPpuCycle.Name = "mnuRunPpuCycle";
this.mnuRunPpuCycle.Size = new System.Drawing.Size(212, 22);
this.mnuRunPpuCycle.Size = new System.Drawing.Size(213, 22);
this.mnuRunPpuCycle.Text = "Run one PPU cycle";
//
// mnuRunScanline
//
this.mnuRunScanline.Image = global::Mesen.GUI.Properties.Resources.RunPpuScanline;
this.mnuRunScanline.Name = "mnuRunScanline";
this.mnuRunScanline.Size = new System.Drawing.Size(212, 22);
this.mnuRunScanline.Size = new System.Drawing.Size(213, 22);
this.mnuRunScanline.Text = "Run one scanline";
//
// mnuRunOneFrame
//
this.mnuRunOneFrame.Image = global::Mesen.GUI.Properties.Resources.RunPpuFrame;
this.mnuRunOneFrame.Name = "mnuRunOneFrame";
this.mnuRunOneFrame.Size = new System.Drawing.Size(212, 22);
this.mnuRunOneFrame.Size = new System.Drawing.Size(213, 22);
this.mnuRunOneFrame.Text = "Run one frame";
//
// toolStripMenuItem8
//
this.toolStripMenuItem8.Name = "toolStripMenuItem8";
this.toolStripMenuItem8.Size = new System.Drawing.Size(209, 6);
this.toolStripMenuItem8.Size = new System.Drawing.Size(210, 6);
//
// mnuBreakIn
//
this.mnuBreakIn.Name = "mnuBreakIn";
this.mnuBreakIn.Size = new System.Drawing.Size(212, 22);
this.mnuBreakIn.Size = new System.Drawing.Size(213, 22);
this.mnuBreakIn.Text = "Break in...";
//
// mnuBreakOn
//
this.mnuBreakOn.Name = "mnuBreakOn";
this.mnuBreakOn.Size = new System.Drawing.Size(212, 22);
this.mnuBreakOn.Size = new System.Drawing.Size(213, 22);
this.mnuBreakOn.Text = "Break on...";
//
// ctrlSplitContainer
@ -440,7 +437,7 @@
private System.Windows.Forms.GroupBox grpBreakpoints;
private Controls.ctrlBreakpoints ctrlBreakpoints;
private Controls.ctrlConsoleStatus ctrlStatus;
private System.Windows.Forms.ToolStripMenuItem mnuRun1000Cycles;
private System.Windows.Forms.ToolStripMenuItem mnuRun1000Instructions;
private GUI.Controls.ctrlMesenToolStrip tsToolbar;
private System.Windows.Forms.GroupBox grpCallstack;
private Controls.ctrlCallstack ctrlCallstack;

View file

@ -66,6 +66,15 @@ namespace Mesen.GUI.Debugger
mnuRunPpuCycle.InitShortcut(this, nameof(DebuggerShortcutsConfig.RunPpuCycle));
mnuRunScanline.InitShortcut(this, nameof(DebuggerShortcutsConfig.RunPpuScanline));
mnuRunOneFrame.InitShortcut(this, nameof(DebuggerShortcutsConfig.RunPpuFrame));
mnuStepInto.Click += (s, e) => { DebugApi.Step(1); };
mnuStepOver.Click += (s, e) => { DebugApi.Step(1, StepType.CpuStepOver); };
mnuStepOut.Click += (s, e) => { DebugApi.Step(1, StepType.CpuStepOut); };
mnuRun1000Instructions.Click += (s, e) => { DebugApi.Step(1000); };
mnuRunPpuCycle.Click += (s, e) => { DebugApi.Step(1, StepType.PpuStep); };
mnuRunScanline.Click += (s, e) => { DebugApi.Step(341, StepType.PpuStep); };
mnuRunOneFrame.Click += (s, e) => { DebugApi.Step(341*262, StepType.PpuStep); }; //TODO ntsc/pal
mnuContinue.Click += (s, e) => { DebugApi.ResumeExecution(); };
}
private void InitToolbar()
@ -97,21 +106,6 @@ namespace Mesen.GUI.Debugger
}
}
private void mnuStepInto_Click(object sender, EventArgs e)
{
DebugApi.Step(1);
}
private void mnuContinue_Click(object sender, EventArgs e)
{
DebugApi.ResumeExecution();
}
private void mnuRun1000Cycles_Click(object sender, EventArgs e)
{
DebugApi.Step(1000);
}
private void ctrlCallstack_FunctionSelected(uint address)
{
ctrlDisassemblyView.ScrollToAddress(address);

View file

@ -20,7 +20,7 @@ namespace Mesen.GUI
[DllImport(DllPath)] public static extern void ReleaseDebugger();
[DllImport(DllPath)] public static extern void ResumeExecution();
[DllImport(DllPath)] public static extern void Step(Int32 instructionCount);
[DllImport(DllPath)] public static extern void Step(Int32 instructionCount, StepType type = StepType.CpuStep);
[DllImport(DllPath)] public static extern void StartTraceLogger([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filename);
[DllImport(DllPath)] public static extern void StopTraceLogger();
@ -375,4 +375,12 @@ namespace Mesen.GUI
Nmi = 1,
Irq = 2
}
public enum StepType
{
CpuStep,
CpuStepOut,
CpuStepOver,
PpuStep,
}
}