Debugger: Added label/symbol/address tooltips

This commit is contained in:
Sour 2019-05-04 13:54:17 -04:00
parent e4868e3fa4
commit c000b1fcae
8 changed files with 206 additions and 8 deletions

View file

@ -1,16 +1,20 @@
using Mesen.GUI.Debugger.Controls;
using Mesen.GUI.Debugger.Integration;
using Mesen.GUI.Debugger.Labels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Mesen.GUI.Debugger.Integration.DbgImporter;
namespace Mesen.GUI.Debugger.Code
{
public class CpuDisassemblyManager : IDisassemblyManager
{
protected ICodeDataProvider _provider;
private DbgImporter _symbolProvider;
public ICodeDataProvider Provider { get { return this._provider; } }
public virtual SnesMemoryType RelativeMemoryType { get { return SnesMemoryType.CpuMemory; } }
@ -20,6 +24,7 @@ namespace Mesen.GUI.Debugger.Code
public virtual void RefreshCode(DbgImporter symbolProvider, DbgImporter.FileInfo file)
{
_symbolProvider = symbolProvider;
if(file == null) {
this._provider = new CodeDataProvider(CpuType.Cpu);
} else {
@ -55,5 +60,124 @@ namespace Mesen.GUI.Debugger.Code
});
}
}
public Dictionary<string, string> GetTooltipData(string word, int lineIndex)
{
int? arrayIndex = null;
int arraySeparatorIndex = word.IndexOf("+");
if(arraySeparatorIndex >= 0) {
int index;
if(int.TryParse(word.Substring(arraySeparatorIndex + 1), out index)) {
arrayIndex = index;
}
word = word.Substring(0, arraySeparatorIndex);
}
if(_provider is DbgCodeDataProvider && _symbolProvider != null) {
int rangeStart, rangeEnd;
GetSymbolByteRange(lineIndex, out rangeStart, out rangeEnd);
SymbolInfo symbol = _symbolProvider.GetSymbol(word, rangeStart, rangeEnd);
if(symbol != null) {
AddressInfo? symbolAddress = _symbolProvider.GetSymbolAddressInfo(symbol);
if(symbolAddress != null && symbolAddress.Value.Address >= 0) {
int relativeAddress = DebugApi.GetRelativeAddress(symbolAddress.Value).Address;
byte byteValue = relativeAddress >= 0 ? DebugApi.GetMemoryValue(this.RelativeMemoryType, (UInt32)relativeAddress) : (byte)0;
UInt16 wordValue = relativeAddress >= 0 ? (UInt16)(byteValue | (DebugApi.GetMemoryValue(this.RelativeMemoryType, (UInt32)relativeAddress + 1) << 8)) : (UInt16)0;
var values = new Dictionary<string, string>() {
{ "Symbol", symbol.Name + (arrayIndex != null ? $"+{arrayIndex.Value}" : "") }
};
if(relativeAddress >= 0) {
values["CPU Address"] = "$" + relativeAddress.ToString("X4");
} else {
values["CPU Address"] = "<out of scope>";
}
if(symbolAddress.Value.Type == SnesMemoryType.PrgRom) {
values["PRG Offset"] = "$" + (symbolAddress.Value.Address + (arrayIndex ?? 0)).ToString("X4");
}
values["Value"] = (relativeAddress >= 0 ? $"${byteValue.ToString("X2")} (byte){Environment.NewLine}${wordValue.ToString("X4")} (word)" : "n/a");
return values;
} else {
return new Dictionary<string, string>() {
{ "Symbol", symbol.Name },
{ "Constant", symbol.Address.HasValue ? ("$" + symbol.Address.Value.ToString("X2")) : "<unknown>" }
};
}
}
} else {
CodeLabel label = LabelManager.GetLabel(word);
if(label != null) {
AddressInfo absAddress = label.GetAbsoluteAddress();
int relativeAddress;
if(absAddress.Type == SnesMemoryType.Register) {
relativeAddress = absAddress.Address;
} else {
relativeAddress = label.GetRelativeAddress().Address;
}
byte byteValue = relativeAddress >= 0 ? DebugApi.GetMemoryValue(this.RelativeMemoryType, (UInt32)relativeAddress) : (byte)0;
UInt16 wordValue = relativeAddress >= 0 ? (UInt16)(byteValue | (DebugApi.GetMemoryValue(this.RelativeMemoryType, (UInt32)relativeAddress + 1) << 8)) : (UInt16)0;
var values = new Dictionary<string, string>() {
{ "Label", label.Label + (arrayIndex != null ? $"+{arrayIndex.Value}" : "") },
{ "Address", (relativeAddress >= 0 ? "$" + relativeAddress.ToString("X4") : "n/a") },
{ "Value", (relativeAddress >= 0 ? $"${byteValue.ToString("X2")} (byte){Environment.NewLine}${wordValue.ToString("X4")} (word)" : "n/a") },
};
if(!string.IsNullOrWhiteSpace(label.Comment)) {
values["Comment"] = label.Comment;
}
return values;
}
}
System.Globalization.NumberStyles style = System.Globalization.NumberStyles.None;
if(word.StartsWith("$")) {
style = System.Globalization.NumberStyles.HexNumber;
word = word.Replace("$", "");
}
uint address;
if(UInt32.TryParse(word, style, null, out address)) {
byte byteValue = DebugApi.GetMemoryValue(this.RelativeMemoryType, address);
UInt16 wordValue = (UInt16)(byteValue | (DebugApi.GetMemoryValue(this.RelativeMemoryType, address + 1) << 8));
return new Dictionary<string, string>() {
{ "Address", "$" + address.ToString("X4") },
{ "Value", $"${byteValue.ToString("X2")} (byte){Environment.NewLine}${wordValue.ToString("X4")} (word)" }
};
}
return null;
}
private void GetSymbolByteRange(int lineIndex, out int rangeStart, out int rangeEnd)
{
int lineCount = _provider.GetLineCount();
while(lineIndex < lineCount - 2 && _provider.GetCodeLineData(lineIndex).AbsoluteAddress < 0) {
//Find the address of the next line with an address
lineIndex++;
}
rangeStart = _provider.GetCodeLineData(lineIndex).AbsoluteAddress;
if(rangeStart >= 0) {
while(lineIndex < lineCount - 2 && _provider.GetCodeLineData(lineIndex + 1).AbsoluteAddress < 0) {
//Find the next line with an address
lineIndex++;
}
rangeEnd = _provider.GetCodeLineData(lineIndex + 1).AbsoluteAddress - 1;
if(rangeStart < rangeEnd && (rangeEnd - rangeStart) < 500) {
return;
}
}
rangeStart = 0;
rangeEnd = Int32.MaxValue;
}
}
}

View file

@ -65,6 +65,7 @@ namespace Mesen.GUI.Debugger.Code
data.Comment = trimmed.Substring(commentIndex);
data.Text = trimmed.Substring(0, commentIndex).TrimEnd();
} else {
data.Comment = "";
data.Text = trimmed;
}

View file

@ -19,5 +19,7 @@ namespace Mesen.GUI.Debugger.Code
void RefreshCode(DbgImporter symbolProvider, DbgImporter.FileInfo file);
void ToggleBreakpoint(int lineIndex);
void EnableDisableBreakpoint(int lineIndex);
Dictionary<string, string> GetTooltipData(string word, int lineIndex);
}
}

View file

@ -43,7 +43,7 @@ namespace Mesen.GUI.Debugger
}
return "[" + label.Label + "]";
} else {
return "[" + EffectiveAddress.ToString(format) + "]";
return "[$" + EffectiveAddress.ToString(format) + "]";
}
} else {
return "";

View file

@ -56,7 +56,7 @@
this.ctrlCode.Dock = System.Windows.Forms.DockStyle.Fill;
this.ctrlCode.HideSelection = false;
this.ctrlCode.Location = new System.Drawing.Point(0, 27);
this.ctrlCode.Margin = new System.Windows.Forms.Padding(0, 0, 0, 0);
this.ctrlCode.Margin = new System.Windows.Forms.Padding(0);
this.ctrlCode.Name = "ctrlCode";
this.ctrlCode.ShowCompactPrgAddresses = false;
this.ctrlCode.ShowContentNotes = false;
@ -67,7 +67,9 @@
this.ctrlCode.ShowSingleLineLineNumberNotes = false;
this.ctrlCode.Size = new System.Drawing.Size(465, 371);
this.ctrlCode.TabIndex = 0;
this.ctrlCode.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlCode_MouseMove);
this.ctrlCode.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ctrlCode_MouseDown);
this.ctrlCode.MouseLeave += new System.EventHandler(this.ctrlCode_MouseLeave);
this.ctrlCode.TextZoomChanged += new System.EventHandler(this.ctrlCode_TextZoomChanged);
//
// ctxMenu

View file

@ -14,6 +14,7 @@ using Mesen.GUI.Debugger.Labels;
using Mesen.GUI.Debugger.Integration;
using Mesen.GUI.Debugger.Workspace;
using static Mesen.GUI.Debugger.Integration.DbgImporter;
using Mesen.GUI.Forms;
namespace Mesen.GUI.Debugger.Controls
{
@ -230,6 +231,36 @@ namespace Mesen.GUI.Debugger.Controls
int lineIndex = ctrlCode.GetLineIndexAtPosition(e.Y);
_manager.ToggleBreakpoint(lineIndex);
}
BaseForm.GetPopupTooltip(this.FindForm()).Hide();
}
private string _lastWord = null;
private void ctrlCode_MouseMove(object sender, MouseEventArgs e)
{
string word = ctrlCode.GetWordUnderLocation(e.Location).Trim();
if(word == _lastWord) {
return;
}
if(word.Length > 0) {
Dictionary<string, string> values = _manager.GetTooltipData(word, ctrlCode.GetLineIndexAtPosition(e.Y));
if(values != null) {
Form form = this.FindForm();
Point tooltipLocation = ctrlCode.GetWordEndPosition(e.Location);
BaseForm.GetPopupTooltip(form).SetTooltip(form.PointToClient(ctrlCode.PointToScreen(tooltipLocation)), values);
} else {
BaseForm.GetPopupTooltip(this.FindForm()).Hide();
}
} else {
BaseForm.GetPopupTooltip(this.FindForm()).Hide();
}
_lastWord = word;
}
private void ctrlCode_MouseLeave(object sender, EventArgs e)
{
BaseForm.GetPopupTooltip(this.FindForm()).Hide();
}
private void ctrlCode_TextZoomChanged(object sender, EventArgs e)

View file

@ -147,6 +147,11 @@ namespace Mesen.GUI.Debugger.Controls
return this.ctrlTextbox.GetWordUnderLocation(position);
}
public Point GetWordEndPosition(Point position)
{
return this.ctrlTextbox.GetWordEndPosition(position);
}
private void ctrlTextbox_ScrollPositionChanged(object sender, EventArgs e)
{
this.vScrollBar.Value = this.ctrlTextbox.ScrollPosition;

View file

@ -406,7 +406,15 @@ namespace Mesen.GUI.Debugger.Controls
public string GetFullWidthString(int lineIndex)
{
CodeLineData lineData = _dataProvider.GetCodeLineData(lineIndex);
string text = lineData.Text + lineData.GetEffectiveAddressString(_addressFormat);
if(lineData.Flags.HasFlag(LineFlags.BlockStart) || lineData.Flags.HasFlag(LineFlags.BlockEnd)) {
return string.Empty;
}
string text = lineData.Text;
if(!lineData.Flags.HasFlag(LineFlags.Label) && text.Length > 0) {
text += " " + lineData.GetEffectiveAddressString(_addressFormat);
}
if(lineData.Comment.Length > 0) {
return text.PadRight(text.Length > 0 ? CommentSpacingCharCount : 0) + lineData.Comment;
}
@ -424,8 +432,7 @@ namespace Mesen.GUI.Debugger.Controls
}
int positionX = position.X - marginLeft;
//TODO
//positionX -= (LineIndentations != null ? LineIndentations[lineIndex] : 0);
positionX -= _dataProvider.GetCodeLineData(lineIndex).Indentation;
if(positionX >= 0) {
float charWidth = g.MeasureString("W", this.Font, int.MaxValue, StringFormat.GenericTypographic).Width;
charIndex = (int)(positionX / charWidth);
@ -452,7 +459,7 @@ namespace Mesen.GUI.Debugger.Controls
}
int startIndex = text.LastIndexOfAny(_wordDelimiters, charIndex);
if(startIndex >= 0 && text[startIndex] == '#' && text.Length > startIndex && text[startIndex + 1] == '$') {
if(startIndex >= 0 && text[startIndex] == '#' && text.Length > startIndex && (text[startIndex + 1] == '$' || text[startIndex + 1] >= '0' && text[startIndex + 1] <= '9')) {
//Special case for immediate values. e.g: we want to show a tooltip for #MyLabel, but not for #$EF
return text.Substring(startIndex, endIndex - startIndex);
} else {
@ -464,6 +471,32 @@ namespace Mesen.GUI.Debugger.Controls
return string.Empty;
}
public Point GetWordEndPosition(Point position)
{
int charIndex;
int lineIndex;
if(this.GetCharIndex(position, out charIndex, out lineIndex)) {
using(Graphics g = Graphics.FromHwnd(this.Handle)) {
int marginLeft = this.GetMargin(g, true);
int positionX = marginLeft + _dataProvider.GetCodeLineData(lineIndex).Indentation;
string text = this.GetFullWidthString(lineIndex);
int endIndex = text.IndexOfAny(_wordDelimiters, charIndex);
if(endIndex == -1) {
endIndex = text.Length;
}
float charWidth = g.MeasureString("W", this.Font, int.MaxValue, StringFormat.GenericTypographic).Width;
positionX += (int)(endIndex * charWidth);
int positionY = (GetLineAtPosition(position.Y) + 1) * this.LineHeight;
return new Point(positionX, positionY);
}
}
return Point.Empty;
}
private int GetLineAtPosition(int yPos)
{
return Math.Max(0, yPos / this.LineHeight);
@ -967,7 +1000,7 @@ namespace Mesen.GUI.Debugger.Controls
Color defaultColor = Color.FromArgb(60, 60, 60);
if(codeString.Length > 0) {
Match match = CodeHighlightingEnabled ? _codeRegex.Match(codeString) : null;
if(match != null && match.Success && !codeString.EndsWith(":")) {
if(match != null && match.Success && !lineData.Flags.HasFlag(LineFlags.Label)) {
string padding = match.Groups[1].Value;
string opcode = match.Groups[2].Value;
string invalidStar = match.Groups[3].Value;
@ -1000,7 +1033,7 @@ namespace Mesen.GUI.Debugger.Controls
float xOffset = 0;
for(int i = 0; i < parts.Count; i++) {
using(Brush b = new SolidBrush(textColor.HasValue && (i < codePartCount || i == parts.Count - 1) ? textColor.Value : colors[i])) {
using(Brush b = new SolidBrush(textColor.HasValue && (i <= codePartCount) ? textColor.Value : colors[i])) {
g.DrawString(parts[i], this.Font, b, marginLeft + xOffset, positionY, StringFormat.GenericTypographic);
xOffset += g.MeasureString("".PadLeft(parts[i].Length, 'w'), this.Font, Point.Empty, StringFormat.GenericTypographic).Width;
characterCount += parts[i].Length;