Debugger: Added call stack

This commit is contained in:
Sour 2019-03-24 12:05:51 -04:00
parent 77afd87815
commit 75361b4bce
16 changed files with 563 additions and 12 deletions

68
Core/CallstackManager.cpp Normal file
View file

@ -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;
}

20
Core/CallstackManager.h Normal file
View file

@ -0,0 +1,20 @@
#pragma once
#include "stdafx.h"
#include "DebugTypes.h"
class Debugger;
class CallstackManager
{
private:
Debugger* _debugger;
deque<StackFrameInfo> _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);
};

View file

@ -55,6 +55,7 @@
<ClInclude Include="blargg_source.h" />
<ClInclude Include="Breakpoint.h" />
<ClInclude Include="BreakpointManager.h" />
<ClInclude Include="CallstackManager.h" />
<ClInclude Include="CartTypes.h" />
<ClInclude Include="CodeDataLogger.h" />
<ClInclude Include="Console.h" />
@ -137,6 +138,7 @@
<ClCompile Include="BaseVideoFilter.cpp" />
<ClCompile Include="Breakpoint.cpp" />
<ClCompile Include="BreakpointManager.cpp" />
<ClCompile Include="CallstackManager.cpp" />
<ClCompile Include="CodeDataLogger.cpp" />
<ClCompile Include="Console.cpp" />
<ClCompile Include="ConsoleLock.cpp" />

View file

@ -251,6 +251,9 @@
<ClInclude Include="SpcTimer.h">
<Filter>SNES</Filter>
</ClInclude>
<ClInclude Include="CallstackManager.h">
<Filter>Debugger</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -399,6 +402,9 @@
<ClCompile Include="Spc.Instructions.cpp">
<Filter>SNES</Filter>
</ClCompile>
<ClCompile Include="CallstackManager.cpp">
<Filter>Debugger</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">

View file

@ -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;
};

View file

@ -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> 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<EventManager> Debugger::GetEventManager()
return _eventManager;
}
shared_ptr<CallstackManager> Debugger::GetCallstackManager()
{
return _callstackManager;
}
shared_ptr<Console> Debugger::GetConsole()
{
return _console;

View file

@ -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> _breakpointManager;
shared_ptr<PpuTools> _ppuTools;
shared_ptr<EventManager> _eventManager;
shared_ptr<CallstackManager> _callstackManager;
unique_ptr<ExpressionEvaluator> _watchExpEval;
@ -51,7 +53,12 @@ private:
atomic<uint32_t> _breakRequestCount;
atomic<int32_t> _cpuStepCount;
uint8_t _prevOpCode = 0;
uint32_t _prevProgramCounter = 0;
void ProcessBreakConditions(MemoryOperationInfo &operation, AddressInfo &addressInfo);
void ProcessInterrupt(bool forNmi);
public:
Debugger(shared_ptr<Console> 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<BreakpointManager> GetBreakpointManager();
shared_ptr<PpuTools> GetPpuTools();
shared_ptr<EventManager> GetEventManager();
shared_ptr<CallstackManager> GetCallstackManager();
shared_ptr<Console> GetConsole();
};

View file

@ -9,6 +9,7 @@
#include "../Core/BreakpointManager.h"
#include "../Core/PpuTools.h"
#include "../Core/EventManager.h"
#include "../Core/CallstackManager.h"
extern shared_ptr<Console> _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); }

View file

@ -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<StackInfo> stack = GetStackInfo();
this.UpdateList(stack);
}
private List<StackInfo> GetStackInfo()
{
_stackFrames = DebugApi.GetCallstack();
DebugState state = DebugApi.GetState();
_programCounter = (uint)(state.Cpu.K << 16) | state.Cpu.PC;
int relDestinationAddr = -1;
List<StackInfo> stack = new List<StackInfo>();
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<StackInfo> 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;
}
}
}

View file

@ -0,0 +1,82 @@
namespace Mesen.GUI.Debugger.Controls
{
partial class ctrlCallstack
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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);
}
}
}

View file

@ -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
}
}

View file

@ -320,6 +320,12 @@
<Compile Include="Debugger\Controls\ComboBoxWithSeparator.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Debugger\Controls\ctrlCallstack.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Debugger\Controls\ctrlCallstack.designer.cs">
<DependentUpon>ctrlCallstack.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\Controls\ctrlColorPicker.cs">
<SubType>Component</SubType>
</Compile>
@ -686,6 +692,9 @@
<EmbeddedResource Include="Debugger\Breakpoints\frmBreakpoint.resx">
<DependentUpon>frmBreakpoint.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\Controls\ctrlCallstack.resx">
<DependentUpon>ctrlCallstack.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\Controls\ctrlConsoleStatus.resx">
<DependentUpon>ctrlConsoleStatus.cs</DependentUpon>
</EmbeddedResource>