diff --git a/Core/CallstackManager.cpp b/Core/CallstackManager.cpp new file mode 100644 index 0000000..df2da50 --- /dev/null +++ b/Core/CallstackManager.cpp @@ -0,0 +1,68 @@ +#include "stdafx.h" +#include "CallstackManager.h" +#include "Debugger.h" +#include "DebugBreakHelper.h" + +CallstackManager::CallstackManager(Debugger* debugger) +{ + _debugger = debugger; +} + +void CallstackManager::Push(uint32_t srcAddr, uint32_t destAddr, uint32_t returnAddress, StackFrameFlags flags) +{ + if(_callstack.size() >= 511) { + //Ensure callstack stays below 512 entries - games can use various tricks that could keep making the callstack grow + _callstack.pop_front(); + } + + StackFrameInfo stackFrame; + stackFrame.Source = srcAddr; + stackFrame.Target = destAddr; + + stackFrame.Return = returnAddress; + + stackFrame.Flags = flags; + + _callstack.push_back(stackFrame); +} + +void CallstackManager::Pop(uint32_t destAddress) +{ + if(_callstack.empty()) { + return; + } + + uint32_t returnAddr = _callstack.back().Return; + _callstack.pop_back(); + + if(!_callstack.empty() && destAddress != returnAddr) { + //Mismatch, pop that stack frame and add the new one + bool foundMatch = false; + for(int i = (int)_callstack.size() - 1; i >= 0; i--) { + if(destAddress == _callstack[i].Return) { + //Found a matching stack frame, unstack until that point + foundMatch = true; + for(int j = (int)_callstack.size() - i - 1; j >= 0; j--) { + _callstack.pop_back(); + } + break; + } + } + + if(!foundMatch) { + //Couldn't find a matching frame, replace the current one + Push(returnAddr, destAddress, returnAddr, StackFrameFlags::None); + } + } +} + +void CallstackManager::GetCallstack(StackFrameInfo* callstackArray, uint32_t &callstackSize) +{ + DebugBreakHelper helper(_debugger); + int i = 0; + for(StackFrameInfo &info : _callstack) { + callstackArray[i] = info; + i++; + } + callstackSize = i; +} \ No newline at end of file diff --git a/Core/CallstackManager.h b/Core/CallstackManager.h new file mode 100644 index 0000000..9afc6e3 --- /dev/null +++ b/Core/CallstackManager.h @@ -0,0 +1,20 @@ +#pragma once +#include "stdafx.h" +#include "DebugTypes.h" + +class Debugger; + +class CallstackManager +{ +private: + Debugger* _debugger; + deque _callstack; + +public: + CallstackManager(Debugger* debugger); + + void Push(uint32_t srcAddr, uint32_t destAddr, uint32_t returnAddress, StackFrameFlags flags); + void Pop(uint32_t destAddr); + + void GetCallstack(StackFrameInfo* callstackArray, uint32_t &callstackSize); +}; \ No newline at end of file diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index f2f3d0b..a975d63 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -55,6 +55,7 @@ + @@ -137,6 +138,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index b324d4c..f89aa4f 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -251,6 +251,9 @@ SNES + + Debugger + @@ -399,6 +402,9 @@ SNES + + Debugger + diff --git a/Core/DebugTypes.h b/Core/DebugTypes.h index b22fd5d..24c94fa 100644 --- a/Core/DebugTypes.h +++ b/Core/DebugTypes.h @@ -189,4 +189,19 @@ struct GetTileViewOptions int32_t AddressOffset; bool ShowTileGrid; +}; + +enum class StackFrameFlags +{ + None = 0, + Nmi = 1, + Irq = 2 +}; + +struct StackFrameInfo +{ + uint32_t Source; + uint32_t Target; + uint32_t Return; + StackFrameFlags Flags; }; \ No newline at end of file diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index fab08d3..6758670 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -18,6 +18,7 @@ #include "PpuTools.h" #include "EventManager.h" #include "EventType.h" +#include "CallstackManager.h" #include "ExpressionEvaluator.h" #include "../Utilities/HexUtilities.h" #include "../Utilities/FolderUtilities.h" @@ -37,6 +38,7 @@ Debugger::Debugger(shared_ptr console) _breakpointManager.reset(new BreakpointManager(this)); _ppuTools.reset(new PpuTools(_console.get(), _ppu.get())); _eventManager.reset(new EventManager(this, _cpu.get(), _ppu.get())); + _callstackManager.reset(new CallstackManager(this)); _cpuStepCount = -1; _executionStopped = false; @@ -96,7 +98,19 @@ void Debugger::ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo); _traceLogger->Log(debugState, disInfo); + uint32_t pc = (state.K << 16) | state.PC; + if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC) { + //JSR, JSL + uint8_t opSize = DisassemblyInfo::GetOperandSize(_prevOpCode, 0) + 1; + uint32_t returnPc = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + opSize) & 0xFFFF); + _callstackManager->Push(_prevProgramCounter, pc, returnPc, StackFrameFlags::None); + } else if(_prevOpCode == 0x60 || _prevOpCode == 0x6B || _prevOpCode == 0x40) { + //RTS, RTL, RTI + _callstackManager->Pop(pc); + } + _prevOpCode = value; + _prevProgramCounter = pc; if(_cpuStepCount > 0) { _cpuStepCount--; @@ -194,11 +208,19 @@ void Debugger::ProcessBreakConditions(MemoryOperationInfo &operation, AddressInf } } +void Debugger::ProcessInterrupt(bool forNmi) +{ + CpuState state = _cpu->GetState(); + _callstackManager->Push(_prevProgramCounter, (state.K << 16) | state.PC, _prevProgramCounter, forNmi ? StackFrameFlags::Nmi : StackFrameFlags::Irq); + _eventManager->AddEvent(forNmi ? DebugEventType::Nmi : DebugEventType::Irq); +} + void Debugger::ProcessEvent(EventType type) { switch(type) { - case EventType::Nmi: _eventManager->AddEvent(DebugEventType::Nmi); break; - case EventType::Irq: _eventManager->AddEvent(DebugEventType::Irq); break; + case EventType::Nmi: ProcessInterrupt(true); break; + case EventType::Irq: ProcessInterrupt(false); break; + case EventType::StartFrame: _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::EventViewerRefresh); _eventManager->ClearFrameEvents(); @@ -279,6 +301,11 @@ shared_ptr Debugger::GetEventManager() return _eventManager; } +shared_ptr Debugger::GetCallstackManager() +{ + return _callstackManager; +} + shared_ptr Debugger::GetConsole() { return _console; diff --git a/Core/Debugger.h b/Core/Debugger.h index a66f6ea..3516ad8 100644 --- a/Core/Debugger.h +++ b/Core/Debugger.h @@ -17,6 +17,7 @@ class BreakpointManager; class PpuTools; class CodeDataLogger; class EventManager; +class CallstackManager; enum class EventType; enum class SnesMemoryType; @@ -44,6 +45,7 @@ private: shared_ptr _breakpointManager; shared_ptr _ppuTools; shared_ptr _eventManager; + shared_ptr _callstackManager; unique_ptr _watchExpEval; @@ -51,7 +53,12 @@ private: atomic _breakRequestCount; atomic _cpuStepCount; + uint8_t _prevOpCode = 0; + uint32_t _prevProgramCounter = 0; + + void ProcessBreakConditions(MemoryOperationInfo &operation, AddressInfo &addressInfo); + void ProcessInterrupt(bool forNmi); public: Debugger(shared_ptr console); @@ -68,7 +75,6 @@ public: void ProcessPpuWrite(uint16_t addr, uint8_t value, SnesMemoryType memoryType); void ProcessPpuCycle(); - void ProcessBreakConditions(MemoryOperationInfo &operation, AddressInfo &addressInfo); void ProcessEvent(EventType type); int32_t EvaluateExpression(string expression, EvalResultType &resultType, bool useCache); @@ -87,5 +93,6 @@ public: shared_ptr GetBreakpointManager(); shared_ptr GetPpuTools(); shared_ptr GetEventManager(); + shared_ptr GetCallstackManager(); shared_ptr GetConsole(); }; \ No newline at end of file diff --git a/InteropDLL/DebugApiWrapper.cpp b/InteropDLL/DebugApiWrapper.cpp index 5e20fde..0be0591 100644 --- a/InteropDLL/DebugApiWrapper.cpp +++ b/InteropDLL/DebugApiWrapper.cpp @@ -9,6 +9,7 @@ #include "../Core/BreakpointManager.h" #include "../Core/PpuTools.h" #include "../Core/EventManager.h" +#include "../Core/CallstackManager.h" extern shared_ptr _console; @@ -51,6 +52,7 @@ extern "C" DllExport void __stdcall SetBreakpoints(Breakpoint breakpoints[], uint32_t length) { GetDebugger()->GetBreakpointManager()->SetBreakpoints(breakpoints, length); } DllExport int32_t __stdcall EvaluateExpression(char* expression, EvalResultType *resultType, bool useCache) { return GetDebugger()->EvaluateExpression(expression, *resultType, useCache); } + DllExport void __stdcall GetCallstack(StackFrameInfo *callstackArray, uint32_t &callstackSize) { GetDebugger()->GetCallstackManager()->GetCallstack(callstackArray, callstackSize); } DllExport void __stdcall GetState(DebugState &state) { GetDebugger()->GetState(state); } diff --git a/UI/Debugger/Controls/ctrlCallstack.cs b/UI/Debugger/Controls/ctrlCallstack.cs new file mode 100644 index 0000000..15eea8b --- /dev/null +++ b/UI/Debugger/Controls/ctrlCallstack.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using Mesen.GUI.Controls; + +namespace Mesen.GUI.Debugger.Controls +{ + public partial class ctrlCallstack : BaseControl + { + public delegate void NavigateToAddressHandler(UInt32 address); + public event NavigateToAddressHandler FunctionSelected; + + private StackFrameInfo[] _stackFrames; + private UInt32 _programCounter; + + public ctrlCallstack() + { + InitializeComponent(); + } + + public void UpdateCallstack() + { + List stack = GetStackInfo(); + this.UpdateList(stack); + } + + private List GetStackInfo() + { + _stackFrames = DebugApi.GetCallstack(); + DebugState state = DebugApi.GetState(); + _programCounter = (uint)(state.Cpu.K << 16) | state.Cpu.PC; + + int relDestinationAddr = -1; + + List stack = new List(); + for(int i = 0, len = _stackFrames.Length; i < len; i++) { + int relSubEntryAddr = i == 0 ? -1 : (int)_stackFrames[i-1].Target; + + stack.Add(new StackInfo() { + SubName = this.GetFunctionName(relSubEntryAddr, i == 0 ? StackFrameFlags.None : _stackFrames[i - 1].Flags), + Address = _stackFrames[i].Source, + }); + + relDestinationAddr = (int)_stackFrames[i].Target; + } + + //Add current location + stack.Add(new StackInfo() { + SubName = this.GetFunctionName(relDestinationAddr, _stackFrames.Length == 0 ? StackFrameFlags.None : _stackFrames[_stackFrames.Length - 1].Flags), + Address = _programCounter, + }); + + return stack; + } + + private void UpdateList(List stack) + { + this.lstCallstack.BeginUpdate(); + while(this.lstCallstack.Items.Count > stack.Count) { + this.lstCallstack.Items.RemoveAt(this.lstCallstack.Items.Count - 1); + } + while(this.lstCallstack.Items.Count < stack.Count) { + this.lstCallstack.Items.Add("").SubItems.AddRange(new string[] { "", "" }); + } + + for(int i = 0, len = stack.Count; i < len; i++) { + StackInfo stackInfo = stack[i]; + ListViewItem item = this.lstCallstack.Items[len - i - 1]; + + item.Text = stackInfo.SubName; + item.SubItems[1].Text = "@ $" + stackInfo.Address.ToString("X6"); + item.Font = new Font(item.Font, FontStyle.Regular); + } + this.lstCallstack.EndUpdate(); + } + + private string GetFunctionName(int relSubEntryAddr, StackFrameFlags flags) + { + if(relSubEntryAddr < 0) { + return "[bottom of stack]"; + } + + string funcName = ""; + + //TODO LABELS + /*CodeLabel label = absSubEntryAddr >= 0 ? LabelManager.GetLabel((UInt32)absSubEntryAddr, AddressType.PrgRom) : null; + if(label != null) { + funcName = label.Label + (relSubEntryAddr >= 0 ? (" ($" + relSubEntryAddr.ToString("X6") + ")") : ""); + } else { + funcName = (relSubEntryAddr >= 0 ? ("$" + relSubEntryAddr.ToString("X6")) : "n/a"); + }*/ + + if(flags == StackFrameFlags.Nmi) { + funcName = "[nmi] "; + } else if(flags == StackFrameFlags.Irq) { + funcName = "[irq] "; + } + funcName += "$" + relSubEntryAddr.ToString("X6"); + + return funcName; + } + + private void lstCallstack_DoubleClick(object sender, EventArgs e) + { + if(this.lstCallstack.SelectedIndices.Count > 0) { + if(this.lstCallstack.SelectedIndices[0] == 0) { + this.FunctionSelected(_programCounter); + } else { + StackFrameInfo stackFrameInfo = _stackFrames[(this.lstCallstack.Items.Count - 1 - this.lstCallstack.SelectedIndices[0])]; + this.FunctionSelected?.Invoke((UInt32)stackFrameInfo.Source); + } + } + } + + private class StackInfo + { + public string SubName; + public UInt32 Address; + } + } +} diff --git a/UI/Debugger/Controls/ctrlCallstack.designer.cs b/UI/Debugger/Controls/ctrlCallstack.designer.cs new file mode 100644 index 0000000..9f1ef3e --- /dev/null +++ b/UI/Debugger/Controls/ctrlCallstack.designer.cs @@ -0,0 +1,82 @@ +namespace Mesen.GUI.Debugger.Controls +{ + partial class ctrlCallstack + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.lstCallstack = new Mesen.GUI.Controls.DoubleBufferedListView(); + this.colFunctionAddress = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colStackAddr = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.SuspendLayout(); + // + // lstCallstack + // + this.lstCallstack.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.colFunctionAddress, + this.colStackAddr}); + this.lstCallstack.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstCallstack.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lstCallstack.FullRowSelect = true; + this.lstCallstack.GridLines = true; + this.lstCallstack.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.lstCallstack.HideSelection = false; + this.lstCallstack.Location = new System.Drawing.Point(0, 0); + this.lstCallstack.MultiSelect = false; + this.lstCallstack.Name = "lstCallstack"; + this.lstCallstack.Size = new System.Drawing.Size(330, 125); + this.lstCallstack.TabIndex = 1; + this.lstCallstack.UseCompatibleStateImageBehavior = false; + this.lstCallstack.View = System.Windows.Forms.View.Details; + this.lstCallstack.DoubleClick += new System.EventHandler(this.lstCallstack_DoubleClick); + // + // colFunctionAddress + // + this.colFunctionAddress.Text = "Function (Entry Address)"; + this.colFunctionAddress.Width = 174; + // + // colStackAddr + // + this.colStackAddr.Text = "PC Address"; + this.colStackAddr.Width = 88; + // + // ctrlCallstack + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.lstCallstack); + this.Name = "ctrlCallstack"; + this.Size = new System.Drawing.Size(330, 125); + this.ResumeLayout(false); + + } + + #endregion + + private Mesen.GUI.Controls.DoubleBufferedListView lstCallstack; + private System.Windows.Forms.ColumnHeader colFunctionAddress; + private System.Windows.Forms.ColumnHeader colStackAddr; + } +} diff --git a/UI/Debugger/Controls/ctrlCallstack.resx b/UI/Debugger/Controls/ctrlCallstack.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/UI/Debugger/Controls/ctrlCallstack.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/UI/Debugger/Controls/ctrlDisassemblyView.cs b/UI/Debugger/Controls/ctrlDisassemblyView.cs index 74198b7..61a6c57 100644 --- a/UI/Debugger/Controls/ctrlDisassemblyView.cs +++ b/UI/Debugger/Controls/ctrlDisassemblyView.cs @@ -65,6 +65,11 @@ namespace Mesen.GUI.Debugger.Controls } } + public void ScrollToAddress(uint address) + { + ctrlCode.ScrollToAddress((int)address); + } + private void UpdateCode() { int centerLineIndex = ctrlCode.GetLineIndexAtPosition(0) + ctrlCode.GetNumberVisibleLines() / 2; diff --git a/UI/Debugger/frmDebugger.Designer.cs b/UI/Debugger/frmDebugger.Designer.cs index 405a63d..caadf7a 100644 --- a/UI/Debugger/frmDebugger.Designer.cs +++ b/UI/Debugger/frmDebugger.Designer.cs @@ -63,6 +63,8 @@ this.ctrlWatch = new Mesen.GUI.Debugger.ctrlWatch(); this.grpBreakpoints = new System.Windows.Forms.GroupBox(); this.ctrlBreakpoints = new Mesen.GUI.Debugger.Controls.ctrlBreakpoints(); + this.grpCallstack = new System.Windows.Forms.GroupBox(); + this.ctrlCallstack = new Mesen.GUI.Debugger.Controls.ctrlCallstack(); this.tsToolbar = new Mesen.GUI.Controls.ctrlMesenToolStrip(); this.ctrlMesenMenuStrip1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.ctrlSplitContainer)).BeginInit(); @@ -72,6 +74,7 @@ this.tableLayoutPanel1.SuspendLayout(); this.grpWatch.SuspendLayout(); this.grpBreakpoints.SuspendLayout(); + this.grpCallstack.SuspendLayout(); this.SuspendLayout(); // // ctrlDisassemblyView @@ -298,11 +301,13 @@ // // tableLayoutPanel1 // - this.tableLayoutPanel1.ColumnCount = 2; - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.ColumnCount = 3; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); this.tableLayoutPanel1.Controls.Add(this.grpWatch, 0, 0); this.tableLayoutPanel1.Controls.Add(this.grpBreakpoints, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.grpCallstack, 2, 0); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; @@ -317,7 +322,7 @@ this.grpWatch.Dock = System.Windows.Forms.DockStyle.Fill; this.grpWatch.Location = new System.Drawing.Point(3, 3); this.grpWatch.Name = "grpWatch"; - this.grpWatch.Size = new System.Drawing.Size(410, 164); + this.grpWatch.Size = new System.Drawing.Size(271, 164); this.grpWatch.TabIndex = 1; this.grpWatch.TabStop = false; this.grpWatch.Text = "Watch"; @@ -327,16 +332,16 @@ this.ctrlWatch.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlWatch.Location = new System.Drawing.Point(3, 16); this.ctrlWatch.Name = "ctrlWatch"; - this.ctrlWatch.Size = new System.Drawing.Size(404, 145); + this.ctrlWatch.Size = new System.Drawing.Size(265, 145); this.ctrlWatch.TabIndex = 0; // // grpBreakpoints // this.grpBreakpoints.Controls.Add(this.ctrlBreakpoints); this.grpBreakpoints.Dock = System.Windows.Forms.DockStyle.Fill; - this.grpBreakpoints.Location = new System.Drawing.Point(419, 3); + this.grpBreakpoints.Location = new System.Drawing.Point(280, 3); this.grpBreakpoints.Name = "grpBreakpoints"; - this.grpBreakpoints.Size = new System.Drawing.Size(410, 164); + this.grpBreakpoints.Size = new System.Drawing.Size(271, 164); this.grpBreakpoints.TabIndex = 2; this.grpBreakpoints.TabStop = false; this.grpBreakpoints.Text = "Breakpoints"; @@ -346,9 +351,29 @@ this.ctrlBreakpoints.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlBreakpoints.Location = new System.Drawing.Point(3, 16); this.ctrlBreakpoints.Name = "ctrlBreakpoints"; - this.ctrlBreakpoints.Size = new System.Drawing.Size(404, 145); + this.ctrlBreakpoints.Size = new System.Drawing.Size(265, 145); this.ctrlBreakpoints.TabIndex = 0; // + // grpCallstack + // + this.grpCallstack.Controls.Add(this.ctrlCallstack); + this.grpCallstack.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpCallstack.Location = new System.Drawing.Point(557, 3); + this.grpCallstack.Name = "grpCallstack"; + this.grpCallstack.Size = new System.Drawing.Size(272, 164); + this.grpCallstack.TabIndex = 3; + this.grpCallstack.TabStop = false; + this.grpCallstack.Text = "Call Stack"; + // + // ctrlCallstack + // + this.ctrlCallstack.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlCallstack.Location = new System.Drawing.Point(3, 16); + this.ctrlCallstack.Name = "ctrlCallstack"; + this.ctrlCallstack.Size = new System.Drawing.Size(266, 145); + this.ctrlCallstack.TabIndex = 0; + this.ctrlCallstack.FunctionSelected += new Mesen.GUI.Debugger.Controls.ctrlCallstack.NavigateToAddressHandler(this.ctrlCallstack_FunctionSelected); + // // tsToolbar // this.tsToolbar.Location = new System.Drawing.Point(0, 24); @@ -376,6 +401,7 @@ this.tableLayoutPanel1.ResumeLayout(false); this.grpWatch.ResumeLayout(false); this.grpBreakpoints.ResumeLayout(false); + this.grpCallstack.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -416,5 +442,7 @@ private Controls.ctrlConsoleStatus ctrlStatus; private System.Windows.Forms.ToolStripMenuItem mnuRun1000Cycles; private GUI.Controls.ctrlMesenToolStrip tsToolbar; + private System.Windows.Forms.GroupBox grpCallstack; + private Controls.ctrlCallstack ctrlCallstack; } } \ No newline at end of file diff --git a/UI/Debugger/frmDebugger.cs b/UI/Debugger/frmDebugger.cs index 71663ce..274a9d6 100644 --- a/UI/Debugger/frmDebugger.cs +++ b/UI/Debugger/frmDebugger.cs @@ -91,6 +91,7 @@ namespace Mesen.GUI.Debugger ctrlStatus.UpdateStatus(state); ctrlDisassemblyView.SetActiveAddress(activeAddress); ctrlWatch.UpdateWatch(true); + ctrlCallstack.UpdateCallstack(); })); break; } @@ -110,5 +111,10 @@ namespace Mesen.GUI.Debugger { DebugApi.Step(1000); } + + private void ctrlCallstack_FunctionSelected(uint address) + { + ctrlDisassemblyView.ScrollToAddress(address); + } } } diff --git a/UI/Interop/DebugApi.cs b/UI/Interop/DebugApi.cs index 071add2..07a48d5 100644 --- a/UI/Interop/DebugApi.cs +++ b/UI/Interop/DebugApi.cs @@ -101,7 +101,19 @@ namespace Mesen.GUI byte[] buffer = new byte[340*2 * 262*2 * 4]; DebugApi.GetEventViewerOutputWrapper(buffer, options); return buffer; - } + } + + [DllImport(DllPath, EntryPoint = "GetCallstack")] private static extern void DebugGetCallstackWrapper([In, Out]StackFrameInfo[] callstackArray, ref UInt32 callstackSize); + public static StackFrameInfo[] GetCallstack() + { + StackFrameInfo[] callstack = new StackFrameInfo[512]; + UInt32 callstackSize = 0; + + DebugApi.DebugGetCallstackWrapper(callstack, ref callstackSize); + Array.Resize(ref callstack, (int)callstackSize); + + return callstack; + } } public enum SnesMemoryType @@ -348,4 +360,19 @@ namespace Mesen.GUI DivideBy0 = 3, OutOfScope = 4 } + + public struct StackFrameInfo + { + public UInt32 Source; + public UInt32 Target; + public UInt32 Return; + public StackFrameFlags Flags; + }; + + public enum StackFrameFlags + { + None = 0, + Nmi = 1, + Irq = 2 + } } diff --git a/UI/UI.csproj b/UI/UI.csproj index cd177ea..97edeee 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -320,6 +320,12 @@ Component + + UserControl + + + ctrlCallstack.cs + Component @@ -686,6 +692,9 @@ frmBreakpoint.cs + + ctrlCallstack.cs + ctrlConsoleStatus.cs