diff --git a/Docs/content/debugging/Debugger.md b/Docs/content/debugging/Debugger.md
index 62e0f5c6..d07078ea 100644
--- a/Docs/content/debugging/Debugger.md
+++ b/Docs/content/debugging/Debugger.md
@@ -105,24 +105,33 @@ While execution is paused, most fields are editable. Altering the value of any f
This section lets you force certain buttons to be held down on the NES' controller. This is often useful when trying to debug input-related code.
Clicking on a button on the mini NES controllers will toggle its state - green buttons are currently being held down.
-## Watch Window ##
+## Watch List/Window ##
-
![](/images/WatchList.png)
+
Watch Window
-The watch window allows you to evaluate expression and see their value. Mesen supports complex expressions in C/C++ style syntax.
+
+
![](/images/WatchList.png)
+
Watch List
+
-**To add a new watch expression**, click on the last empty line in the list and start typing.
-**To edit a watch expression**, double-click on it and start typing.
-**To switch between hex and decimal**, right-click in the watch and toggle the **Hexadecimal Display** option.
+The watch window and watch list allow you to evaluate expression and see their value. The `Watch Window` is a standalone window that can be resized and moved independently from everything else, whereas the `Watch List` is a part of the main debugger window for quick access to watch expressions.
+
+**To add a new watch expression**, click on the last empty line in the list to enter edit mode.
+**To edit a watch expression**, double-click on it to enter edit mode.
+
+You can use the right-click context menu to delete or move entries, as well as select formatting options.
+An import and export feature is also available to save/load watch expressions from a plain text file.
### Syntax ###
-The used syntax is identical to C/C++ syntax (e.g && for and, || for or, etc.) and have the same operator precedence as C/C++.
+The syntax is identical to C/C++ (e.g `&&` for AND, `||` for OR) and uses the same operator precedence as well.
-**Note:** Use the $ prefix to denote hexadecimal values.
+{{% notice tip %}}
+Use the $ prefix to denote hexadecimal values (e.g: `$FF`) or the % prefix for binary values (e.g: `%1101`)
+{{% /notice %}}
#### Special values ####
@@ -153,6 +162,30 @@ The following "variables" can be used in both the watch window and contional bre
* **SpriteOverflow**: true if the PPU's "Sprite Overflow" flag is set
* **VerticalBlank**: true if the PPU's "Vertical Blank" flag is set
+#### Formatting ####
+
+It is possible to customize the format of each entry by adding a suffix to the expression.
+Suffixes contain a single letter and are optionally followed by a number indicating the number of bytes expected in the return value (up to 4).
+
+The available suffixes are:
+
+* `S` - Signed decimal value
+* `U` - Unsigned decimal value
+* `H` - Hexadecimal
+* `B` - Binary
+
+For example, suffixing an expression with:
+
+* `, H2` will display the result as a 2-byte hexadecimal value (e.g: `26, H2` will display as `$001A`)
+* `, B` will display the result as a binary value (e.g: `141,B` will display as `%10001101`)
+* `, S2` will display the result as a 16-bit signed decimal value (e.g: `$FE4F, S2` will display as `-433`)
+* `, U` will display the result as an unsigned decimal value (e.g: `180, U` will display as `180`)
+
+You can select the default format to use for entries without prefixes by right-clicking and choosing between:
+
+* **Decimal Display** (equivalent to `S4` to all entries - displays the result as 32-bit signed decimal values)
+* **Hexadecimal Display** (equivalent to `H1` to all entries)
+* **Binary Display** (equivalent to `B1` to all entries)
#### Usage Examples ####
```
@@ -162,6 +195,7 @@ scanline == 10 && (cycle >= 55 && cycle <= 100)
x == [$150] || y == [10]
[[$15] + y] //Reads the value at address $15, adds Y to it and reads the value at the resulting address.
{$FFFA} //Returns the NMI handler's address.
+[$14] | ([$15] << 8), H2 //Display the value of the 2-byte variable stored at $14 in hexadecimal format.
```
**Using labels**
diff --git a/Docs/static/images/WatchList.png b/Docs/static/images/WatchList.png
index 6dbf7a07..88cd02ea 100644
Binary files a/Docs/static/images/WatchList.png and b/Docs/static/images/WatchList.png differ
diff --git a/Docs/static/images/WatchWindow.png b/Docs/static/images/WatchWindow.png
new file mode 100644
index 00000000..4dd1ceac
Binary files /dev/null and b/Docs/static/images/WatchWindow.png differ
diff --git a/GUI.NET/Config/DebugInfo.cs b/GUI.NET/Config/DebugInfo.cs
index 67f5e8b2..180b5fb4 100644
--- a/GUI.NET/Config/DebugInfo.cs
+++ b/GUI.NET/Config/DebugInfo.cs
@@ -148,7 +148,7 @@ namespace Mesen.GUI.Config
public bool AlwaysScrollToCenter = false;
public bool SplitView = false;
public bool VerticalLayout = false;
- public bool HexDisplay = true;
+ public WatchFormatStyle WatchFormat = WatchFormatStyle.Hex;
public bool ShowBreakpointLabels = true;
public Size EventViewerSize = new Size(0, 0);
diff --git a/GUI.NET/Debugger/Controls/ctrlWatch.Designer.cs b/GUI.NET/Debugger/Controls/ctrlWatch.Designer.cs
index f12a719d..a959517d 100644
--- a/GUI.NET/Debugger/Controls/ctrlWatch.Designer.cs
+++ b/GUI.NET/Debugger/Controls/ctrlWatch.Designer.cs
@@ -42,11 +42,28 @@
this.mnuMoveUp = new System.Windows.Forms.ToolStripMenuItem();
this.mnuMoveDown = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
+ this.mnuDecimalDisplay = new System.Windows.Forms.ToolStripMenuItem();
this.mnuHexDisplay = new System.Windows.Forms.ToolStripMenuItem();
- this.txtEdit = new System.Windows.Forms.TextBox();
+ this.mnuBinaryDisplay = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator();
+ this.mnuRowDisplayFormat = new System.Windows.Forms.ToolStripMenuItem();
+ this.mnuRowBinary = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripSeparator();
+ this.mnuRowHex1 = new System.Windows.Forms.ToolStripMenuItem();
+ this.mnuRowHex2 = new System.Windows.Forms.ToolStripMenuItem();
+ this.mnuRowHex3 = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator();
+ this.mnuRowSigned1 = new System.Windows.Forms.ToolStripMenuItem();
+ this.mnuRowSigned2 = new System.Windows.Forms.ToolStripMenuItem();
+ this.mnuRowSigned3 = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator();
+ this.mnuRowUnsigned = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripMenuItem9 = new System.Windows.Forms.ToolStripSeparator();
+ this.mnuRowClearFormat = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator();
this.mnuImport = new System.Windows.Forms.ToolStripMenuItem();
this.mnuExport = new System.Windows.Forms.ToolStripMenuItem();
+ this.txtEdit = new System.Windows.Forms.TextBox();
this.contextMenuWatch.SuspendLayout();
this.SuspendLayout();
//
@@ -95,12 +112,16 @@
this.mnuMoveUp,
this.mnuMoveDown,
this.toolStripMenuItem2,
- this.mnuHexDisplay,
+ this.mnuRowDisplayFormat,
this.toolStripMenuItem3,
+ this.mnuDecimalDisplay,
+ this.mnuHexDisplay,
+ this.mnuBinaryDisplay,
+ this.toolStripMenuItem5,
this.mnuImport,
this.mnuExport});
this.contextMenuWatch.Name = "contextMenuWatch";
- this.contextMenuWatch.Size = new System.Drawing.Size(194, 226);
+ this.contextMenuWatch.Size = new System.Drawing.Size(194, 298);
this.contextMenuWatch.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuWatch_Opening);
//
// mnuRemoveWatch
@@ -158,25 +179,137 @@
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
this.toolStripMenuItem2.Size = new System.Drawing.Size(190, 6);
//
+ // mnuDecimalDisplay
+ //
+ this.mnuDecimalDisplay.Name = "mnuDecimalDisplay";
+ this.mnuDecimalDisplay.Size = new System.Drawing.Size(193, 22);
+ this.mnuDecimalDisplay.Text = "Decimal Display";
+ this.mnuDecimalDisplay.Click += new System.EventHandler(this.mnuDecimalDisplay_Click);
+ //
// mnuHexDisplay
//
this.mnuHexDisplay.Checked = true;
- this.mnuHexDisplay.CheckOnClick = true;
this.mnuHexDisplay.CheckState = System.Windows.Forms.CheckState.Checked;
this.mnuHexDisplay.Name = "mnuHexDisplay";
this.mnuHexDisplay.Size = new System.Drawing.Size(193, 22);
this.mnuHexDisplay.Text = "Hexadecimal Display";
this.mnuHexDisplay.Click += new System.EventHandler(this.mnuHexDisplay_Click);
//
- // txtEdit
+ // mnuBinaryDisplay
//
- this.txtEdit.AcceptsReturn = true;
- this.txtEdit.Location = new System.Drawing.Point(3, 24);
- this.txtEdit.Name = "txtEdit";
- this.txtEdit.Size = new System.Drawing.Size(177, 20);
- this.txtEdit.TabIndex = 7;
- this.txtEdit.Visible = false;
- this.txtEdit.Leave += new System.EventHandler(this.txtEdit_Leave);
+ this.mnuBinaryDisplay.Name = "mnuBinaryDisplay";
+ this.mnuBinaryDisplay.Size = new System.Drawing.Size(193, 22);
+ this.mnuBinaryDisplay.Text = "Binary Display";
+ this.mnuBinaryDisplay.Click += new System.EventHandler(this.mnuBinaryDisplay_Click);
+ //
+ // toolStripMenuItem5
+ //
+ this.toolStripMenuItem5.Name = "toolStripMenuItem5";
+ this.toolStripMenuItem5.Size = new System.Drawing.Size(190, 6);
+ //
+ // mnuRowDisplayFormat
+ //
+ this.mnuRowDisplayFormat.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.mnuRowBinary,
+ this.toolStripMenuItem8,
+ this.mnuRowHex1,
+ this.mnuRowHex2,
+ this.mnuRowHex3,
+ this.toolStripMenuItem6,
+ this.mnuRowSigned1,
+ this.mnuRowSigned2,
+ this.mnuRowSigned3,
+ this.toolStripMenuItem7,
+ this.mnuRowUnsigned,
+ this.toolStripMenuItem9,
+ this.mnuRowClearFormat});
+ this.mnuRowDisplayFormat.Name = "mnuRowDisplayFormat";
+ this.mnuRowDisplayFormat.Size = new System.Drawing.Size(193, 22);
+ this.mnuRowDisplayFormat.Text = "Row Display Format";
+ //
+ // mnuRowBinary
+ //
+ this.mnuRowBinary.Name = "mnuRowBinary";
+ this.mnuRowBinary.Size = new System.Drawing.Size(197, 22);
+ this.mnuRowBinary.Text = "Binary";
+ this.mnuRowBinary.Click += new System.EventHandler(this.mnuRowBinary_Click);
+ //
+ // toolStripMenuItem8
+ //
+ this.toolStripMenuItem8.Name = "toolStripMenuItem8";
+ this.toolStripMenuItem8.Size = new System.Drawing.Size(194, 6);
+ //
+ // mnuRowHex1
+ //
+ this.mnuRowHex1.Name = "mnuRowHex1";
+ this.mnuRowHex1.Size = new System.Drawing.Size(197, 22);
+ this.mnuRowHex1.Text = "Hexadecimal (8-bit)";
+ this.mnuRowHex1.Click += new System.EventHandler(this.mnuRowHex1_Click);
+ //
+ // mnuRowHex2
+ //
+ this.mnuRowHex2.Name = "mnuRowHex2";
+ this.mnuRowHex2.Size = new System.Drawing.Size(197, 22);
+ this.mnuRowHex2.Text = "Hexadecimal (16-bit)";
+ this.mnuRowHex2.Click += new System.EventHandler(this.mnuRowHex2_Click);
+ //
+ // mnuRowHex3
+ //
+ this.mnuRowHex3.Name = "mnuRowHex3";
+ this.mnuRowHex3.Size = new System.Drawing.Size(197, 22);
+ this.mnuRowHex3.Text = "Hexadecimal (24-bit)";
+ this.mnuRowHex3.Click += new System.EventHandler(this.mnuRowHex3_Click);
+ //
+ // toolStripMenuItem6
+ //
+ this.toolStripMenuItem6.Name = "toolStripMenuItem6";
+ this.toolStripMenuItem6.Size = new System.Drawing.Size(194, 6);
+ //
+ // mnuRowSigned1
+ //
+ this.mnuRowSigned1.Name = "mnuRowSigned1";
+ this.mnuRowSigned1.Size = new System.Drawing.Size(197, 22);
+ this.mnuRowSigned1.Text = "Signed decimal (8-bit)";
+ this.mnuRowSigned1.Click += new System.EventHandler(this.mnuRowSigned1_Click);
+ //
+ // mnuRowSigned2
+ //
+ this.mnuRowSigned2.Name = "mnuRowSigned2";
+ this.mnuRowSigned2.Size = new System.Drawing.Size(197, 22);
+ this.mnuRowSigned2.Text = "Signed decimal (16-bit)";
+ this.mnuRowSigned2.Click += new System.EventHandler(this.mnuRowSigned2_Click);
+ //
+ // mnuRowSigned3
+ //
+ this.mnuRowSigned3.Name = "mnuRowSigned3";
+ this.mnuRowSigned3.Size = new System.Drawing.Size(197, 22);
+ this.mnuRowSigned3.Text = "Signed decimal (24-bit)";
+ this.mnuRowSigned3.Click += new System.EventHandler(this.mnuRowSigned3_Click);
+ //
+ // toolStripMenuItem7
+ //
+ this.toolStripMenuItem7.Name = "toolStripMenuItem7";
+ this.toolStripMenuItem7.Size = new System.Drawing.Size(194, 6);
+ //
+ // mnuRowUnsigned
+ //
+ this.mnuRowUnsigned.Name = "mnuRowUnsigned";
+ this.mnuRowUnsigned.Size = new System.Drawing.Size(197, 22);
+ this.mnuRowUnsigned.Text = "Unsigned decimal";
+ this.mnuRowUnsigned.Click += new System.EventHandler(this.mnuRowUnsigned_Click);
+ //
+ // toolStripMenuItem9
+ //
+ this.toolStripMenuItem9.Name = "toolStripMenuItem9";
+ this.toolStripMenuItem9.Size = new System.Drawing.Size(194, 6);
+ //
+ // mnuRowClearFormat
+ //
+ this.mnuRowClearFormat.Image = global::Mesen.GUI.Properties.Resources.Close;
+ this.mnuRowClearFormat.Name = "mnuRowClearFormat";
+ this.mnuRowClearFormat.Size = new System.Drawing.Size(197, 22);
+ this.mnuRowClearFormat.Text = "Clear";
+ this.mnuRowClearFormat.Click += new System.EventHandler(this.mnuRowClearFormat_Click);
//
// toolStripMenuItem3
//
@@ -199,6 +332,16 @@
this.mnuExport.Text = "Export...";
this.mnuExport.Click += new System.EventHandler(this.mnuExport_Click);
//
+ // txtEdit
+ //
+ this.txtEdit.AcceptsReturn = true;
+ this.txtEdit.Location = new System.Drawing.Point(3, 24);
+ this.txtEdit.Name = "txtEdit";
+ this.txtEdit.Size = new System.Drawing.Size(177, 20);
+ this.txtEdit.TabIndex = 7;
+ this.txtEdit.Visible = false;
+ this.txtEdit.Leave += new System.EventHandler(this.txtEdit_Leave);
+ //
// ctrlWatch
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -232,5 +375,22 @@
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem3;
private System.Windows.Forms.ToolStripMenuItem mnuImport;
private System.Windows.Forms.ToolStripMenuItem mnuExport;
+ private System.Windows.Forms.ToolStripMenuItem mnuDecimalDisplay;
+ private System.Windows.Forms.ToolStripMenuItem mnuBinaryDisplay;
+ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem5;
+ private System.Windows.Forms.ToolStripMenuItem mnuRowDisplayFormat;
+ private System.Windows.Forms.ToolStripMenuItem mnuRowSigned1;
+ private System.Windows.Forms.ToolStripMenuItem mnuRowSigned2;
+ private System.Windows.Forms.ToolStripMenuItem mnuRowSigned3;
+ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem6;
+ private System.Windows.Forms.ToolStripMenuItem mnuRowHex1;
+ private System.Windows.Forms.ToolStripMenuItem mnuRowHex2;
+ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem7;
+ private System.Windows.Forms.ToolStripMenuItem mnuRowUnsigned;
+ private System.Windows.Forms.ToolStripMenuItem mnuRowBinary;
+ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem8;
+ private System.Windows.Forms.ToolStripMenuItem mnuRowHex3;
+ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem9;
+ private System.Windows.Forms.ToolStripMenuItem mnuRowClearFormat;
}
}
diff --git a/GUI.NET/Debugger/Controls/ctrlWatch.cs b/GUI.NET/Debugger/Controls/ctrlWatch.cs
index 2d3dced4..9b455b9d 100644
--- a/GUI.NET/Debugger/Controls/ctrlWatch.cs
+++ b/GUI.NET/Debugger/Controls/ctrlWatch.cs
@@ -38,7 +38,6 @@ namespace Mesen.GUI.Debugger
{
base.OnLoad(e);
if(!IsDesignMode) {
- this.mnuHexDisplay.Checked = ConfigManager.Config.DebugInfo.HexDisplay;
WatchManager.WatchChanged += WatchManager_WatchChanged;
mnuRemoveWatch.InitShortcut(this, nameof(DebuggerShortcutsConfig.WatchList_Delete));
mnuEditInMemoryViewer.InitShortcut(this, nameof(DebuggerShortcutsConfig.CodeWindow_EditInMemoryViewer));
@@ -48,6 +47,16 @@ namespace Mesen.GUI.Debugger
}
}
+ public string GetTooltipText()
+ {
+ return (
+ frmBreakpoint.GetConditionTooltip(true) + Environment.NewLine + Environment.NewLine +
+ "Additionally, the watch window supports a syntax to display X bytes starting from a specific address. e.g:" + Environment.NewLine +
+ "[$10, 16]: Display 16 bytes starting from address $10" + Environment.NewLine +
+ "[MyLabel, 4]: Display 4 bytes starting from the address the specified label (MyLabel) refers to"
+ );
+ }
+
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if(lstWatch.SelectedItems.Count > 0) {
@@ -90,13 +99,12 @@ namespace Mesen.GUI.Debugger
public void UpdateWatch(bool autoResizeColumns = true)
{
- List watchContent = WatchManager.GetWatchContent(mnuHexDisplay.Checked, _previousValues);
+ List watchContent = WatchManager.GetWatchContent(_previousValues);
_previousValues = watchContent;
- int currentSelection = lstWatch.FocusedItem?.Selected == true ? (lstWatch.FocusedItem?.Index ?? -1) : -1;
-
bool updating = false;
if(watchContent.Count != lstWatch.Items.Count - 1) {
+ int currentFocus = lstWatch.FocusedItem?.Selected == true ? (lstWatch.FocusedItem?.Index ?? -1) : -1;
lstWatch.BeginUpdate();
lstWatch.Items.Clear();
@@ -111,6 +119,9 @@ namespace Mesen.GUI.Debugger
lastItem.SubItems.Add("");
itemsToAdd.Add(lastItem);
lstWatch.Items.AddRange(itemsToAdd.ToArray());
+ if(currentFocus >= 0 && currentFocus < lstWatch.Items.Count) {
+ SetSelectedItem(currentFocus);
+ }
updating = true;
} else {
for(int i = 0; i < watchContent.Count; i++) {
@@ -144,19 +155,8 @@ namespace Mesen.GUI.Debugger
}
lstWatch.EndUpdate();
}
-
- if(currentSelection >= 0 && lstWatch.Items.Count > currentSelection) {
- SetSelectedItem(currentSelection);
- }
}
- private void mnuHexDisplay_Click(object sender, EventArgs e)
- {
- ConfigManager.Config.DebugInfo.HexDisplay = this.mnuHexDisplay.Checked;
- ConfigManager.ApplyChanges();
- UpdateWatch();
- }
-
private void lstWatch_SelectedIndexChanged(object sender, EventArgs e)
{
mnuRemoveWatch.Enabled = lstWatch.SelectedItems.Count >= 1;
@@ -165,6 +165,11 @@ namespace Mesen.GUI.Debugger
private void UpdateActions()
{
+ mnuHexDisplay.Checked = ConfigManager.Config.DebugInfo.WatchFormat == WatchFormatStyle.Hex;
+ mnuDecimalDisplay.Checked = ConfigManager.Config.DebugInfo.WatchFormat == WatchFormatStyle.Signed;
+ mnuBinaryDisplay.Checked = ConfigManager.Config.DebugInfo.WatchFormat == WatchFormatStyle.Binary;
+ mnuRowDisplayFormat.Enabled = lstWatch.SelectedItems.Count > 0;
+
mnuEditInMemoryViewer.Enabled = false;
mnuViewInDisassembly.Enabled = false;
mnuMoveUp.Enabled = false;
@@ -396,5 +401,107 @@ namespace Mesen.GUI.Debugger
}
}
}
+
+ private void mnuHexDisplay_Click(object sender, EventArgs e)
+ {
+ ConfigManager.Config.DebugInfo.WatchFormat = WatchFormatStyle.Hex;
+ ConfigManager.ApplyChanges();
+ UpdateWatch();
+ }
+
+ private void mnuDecimalDisplay_Click(object sender, EventArgs e)
+ {
+ ConfigManager.Config.DebugInfo.WatchFormat = WatchFormatStyle.Signed;
+ ConfigManager.ApplyChanges();
+ UpdateWatch();
+ }
+
+ private void mnuBinaryDisplay_Click(object sender, EventArgs e)
+ {
+ ConfigManager.Config.DebugInfo.WatchFormat = WatchFormatStyle.Binary;
+ ConfigManager.ApplyChanges();
+ UpdateWatch();
+ }
+
+ private string GetFormatString(WatchFormatStyle format, int byteLength)
+ {
+ string formatString = ", ";
+ switch(format) {
+ case WatchFormatStyle.Binary: formatString += "B"; break;
+ case WatchFormatStyle.Hex: formatString += "H"; break;
+ case WatchFormatStyle.Signed: formatString += "S"; break;
+ case WatchFormatStyle.Unsigned: formatString += "U"; break;
+ default: throw new Exception("Unsupported type");
+ }
+ if(byteLength > 1) {
+ formatString += byteLength.ToString();
+ }
+ return formatString;
+ }
+
+ private void SetSelectionFormat(WatchFormatStyle format, int byteLength)
+ {
+ SetSelectionFormat(GetFormatString(format, byteLength));
+ }
+
+ private void SetSelectionFormat(string formatString)
+ {
+ List entries = WatchManager.WatchEntries;
+ foreach(int i in lstWatch.SelectedIndices) {
+ if(i < entries.Count) {
+ Match match = WatchManager.FormatSuffixRegex.Match(entries[i]);
+ if(match.Success) {
+ WatchManager.UpdateWatch(i, match.Groups[1].Value + formatString);
+ } else {
+ WatchManager.UpdateWatch(i, entries[i] + formatString);
+ }
+ }
+ }
+ }
+
+ private void mnuRowBinary_Click(object sender, EventArgs e)
+ {
+ SetSelectionFormat(WatchFormatStyle.Binary, 1);
+ }
+
+ private void mnuRowHex1_Click(object sender, EventArgs e)
+ {
+ SetSelectionFormat(WatchFormatStyle.Hex, 1);
+ }
+
+ private void mnuRowHex2_Click(object sender, EventArgs e)
+ {
+ SetSelectionFormat(WatchFormatStyle.Hex, 2);
+ }
+
+ private void mnuRowHex3_Click(object sender, EventArgs e)
+ {
+ SetSelectionFormat(WatchFormatStyle.Hex, 3);
+ }
+
+ private void mnuRowSigned1_Click(object sender, EventArgs e)
+ {
+ SetSelectionFormat(WatchFormatStyle.Signed, 1);
+ }
+
+ private void mnuRowSigned2_Click(object sender, EventArgs e)
+ {
+ SetSelectionFormat(WatchFormatStyle.Signed, 2);
+ }
+
+ private void mnuRowSigned3_Click(object sender, EventArgs e)
+ {
+ SetSelectionFormat(WatchFormatStyle.Unsigned, 1);
+ }
+
+ private void mnuRowUnsigned_Click(object sender, EventArgs e)
+ {
+ SetSelectionFormat(WatchFormatStyle.Unsigned, 1);
+ }
+
+ private void mnuRowClearFormat_Click(object sender, EventArgs e)
+ {
+ SetSelectionFormat("");
+ }
}
}
diff --git a/GUI.NET/Debugger/WatchManager.cs b/GUI.NET/Debugger/WatchManager.cs
index 37b01a71..449c55bf 100644
--- a/GUI.NET/Debugger/WatchManager.cs
+++ b/GUI.NET/Debugger/WatchManager.cs
@@ -1,4 +1,5 @@
-using System;
+using Mesen.GUI.Config;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -13,6 +14,7 @@ namespace Mesen.GUI.Debugger
public static event EventHandler WatchChanged;
private static List _watchEntries = new List();
private static Regex _arrayWatchRegex = new Regex(@"\[((\$[0-9A-Fa-f]+)|(\d+)|([@_a-zA-Z0-9]+))\s*,\s*(\d+)\]", RegexOptions.Compiled);
+ public static Regex FormatSuffixRegex = new Regex(@"^(.*),\s*([B|H|S|U])([\d]){0,1}$", RegexOptions.Compiled);
public static List WatchEntries
{
@@ -24,28 +26,39 @@ namespace Mesen.GUI.Debugger
}
}
- public static List GetWatchContent(bool useHex, List previousValues)
+ public static List GetWatchContent(List previousValues)
{
+ WatchFormatStyle defaultStyle = ConfigManager.Config.DebugInfo.WatchFormat;
+ int defaultByteLength = 1;
+ if(defaultStyle == WatchFormatStyle.Signed) {
+ defaultByteLength = 4;
+ }
+
var list = new List();
for(int i = 0; i < _watchEntries.Count; i++) {
string expression = _watchEntries[i].Trim();
string newValue = "";
EvalResultType resultType;
+ string exprToEvaluate = expression;
+ WatchFormatStyle style = defaultStyle;
+ int byteLength = defaultByteLength;
+ if(expression.StartsWith("{") && expression.EndsWith("}")) {
+ //Default to 2-byte values when using {} syntax
+ byteLength = 2;
+ }
+
+ ProcessFormatSpecifier(ref exprToEvaluate, ref style, ref byteLength);
+
bool forceHasChanged = false;
Match match = _arrayWatchRegex.Match(expression);
if(match.Success) {
//Watch expression matches the array display syntax (e.g: [$300,10] = display 10 bytes starting from $300)
- newValue = ProcessArrayDisplaySyntax(useHex, ref forceHasChanged, match);
+ newValue = ProcessArrayDisplaySyntax(style, ref forceHasChanged, match);
} else {
- Int32 result = InteropEmu.DebugEvaluateExpression(expression, out resultType, true);
+ Int32 result = InteropEmu.DebugEvaluateExpression(exprToEvaluate, out resultType, true);
switch(resultType) {
- case EvalResultType.Numeric:
- //When using {$00} syntax to show the value of a word, always display 4 hex characters.
- bool displayAsWord = expression.StartsWith("{") && expression.EndsWith("}");
- newValue = useHex ? ("$" + result.ToString(displayAsWord ? "X4" : "X2")) : result.ToString();
- break;
-
+ case EvalResultType.Numeric: newValue = FormatValue(result, style, byteLength); break;
case EvalResultType.Boolean: newValue = result == 0 ? "false" : "true"; break;
case EvalResultType.Invalid: newValue = ""; forceHasChanged = true; break;
case EvalResultType.DivideBy0: newValue = ""; forceHasChanged = true; break;
@@ -59,7 +72,67 @@ namespace Mesen.GUI.Debugger
return list;
}
- private static string ProcessArrayDisplaySyntax(bool useHex, ref bool forceHasChanged, Match match)
+ private static string FormatValue(int value, WatchFormatStyle style, int byteLength)
+ {
+ switch(style) {
+ case WatchFormatStyle.Unsigned: return ((UInt32)value).ToString();
+ case WatchFormatStyle.Hex: return "$" + value.ToString("X" + byteLength * 2);
+ case WatchFormatStyle.Binary:
+ string binary = Convert.ToString(value, 2).PadLeft(byteLength * 8, '0');
+ for(int i = binary.Length - 4; i > 0; i -= 4) {
+ binary = binary.Insert(i, ".");
+ }
+ return "%" + binary;
+ case WatchFormatStyle.Signed:
+ int bitCount = byteLength * 8;
+ if(bitCount < 32) {
+ if(((value >> (bitCount - 1)) & 0x01) == 0x01) {
+ //Negative value
+ return (value | (-(1 << bitCount))).ToString();
+ } else {
+ //Position value
+ return value.ToString();
+ }
+ } else {
+ return value.ToString();
+ }
+
+ default: throw new Exception("Unsupported format");
+ }
+ }
+
+ public static bool IsArraySyntax(string expression)
+ {
+ return _arrayWatchRegex.IsMatch(expression);
+ }
+
+ private static bool ProcessFormatSpecifier(ref string expression, ref WatchFormatStyle style, ref int byteLength)
+ {
+ Match match = WatchManager.FormatSuffixRegex.Match(expression);
+ if(!match.Success) {
+ return false;
+ }
+
+ string format = match.Groups[2].Value.ToUpperInvariant();
+ switch(format[0]) {
+ case 'S': style = WatchFormatStyle.Signed; break;
+ case 'H': style = WatchFormatStyle.Hex; break;
+ case 'B': style = WatchFormatStyle.Binary; break;
+ case 'U': style = WatchFormatStyle.Unsigned; break;
+ default: throw new Exception("Invalid format");
+ }
+
+ if(match.Groups[3].Success) {
+ byteLength = Math.Max(Math.Min(Int32.Parse(match.Groups[3].Value), 4), 1);
+ } else {
+ byteLength = 1;
+ }
+
+ expression = match.Groups[1].Value;
+ return true;
+ }
+
+ private static string ProcessArrayDisplaySyntax(WatchFormatStyle style, ref bool forceHasChanged, Match match)
{
string newValue;
int address;
@@ -81,7 +154,7 @@ namespace Mesen.GUI.Debugger
List values = new List(elemCount);
for(int j = address, end = address + elemCount; j < end; j++) {
int memValue = InteropEmu.DebugGetMemoryValue(DebugMemoryType.CpuMemory, (uint)j);
- values.Add(useHex ? memValue.ToString("X2") : memValue.ToString());
+ values.Add(FormatValue(memValue, style, 1));
}
newValue = string.Join(" ", values);
} else {
@@ -141,4 +214,12 @@ namespace Mesen.GUI.Debugger
public string Value { get; set; }
public bool HasChanged { get; set; }
}
+
+ public enum WatchFormatStyle
+ {
+ Unsigned,
+ Signed,
+ Hex,
+ Binary
+ }
}
diff --git a/GUI.NET/Debugger/frmDebugger.cs b/GUI.NET/Debugger/frmDebugger.cs
index e66d1c1a..76b5a94a 100644
--- a/GUI.NET/Debugger/frmDebugger.cs
+++ b/GUI.NET/Debugger/frmDebugger.cs
@@ -167,12 +167,7 @@ namespace Mesen.GUI.Debugger
LastCodeWindow = ctrlDebuggerCode;
- this.toolTip.SetToolTip(this.picWatchHelp,
- frmBreakpoint.GetConditionTooltip(true) + Environment.NewLine + Environment.NewLine +
- "Additionally, the watch window supports a syntax to display X bytes starting from a specific address. e.g:" + Environment.NewLine +
- "[$10, 16]: Display 16 bytes starting from address $10" + Environment.NewLine +
- "[MyLabel, 4]: Display 4 bytes starting from the address the specified label (MyLabel) refers to"
- );
+ this.toolTip.SetToolTip(this.picWatchHelp, ctrlWatch.GetTooltipText());
_notifListener = new InteropEmu.NotificationListener(ConfigManager.Config.DebugInfo.DebugConsoleId);
_notifListener.OnNotification += _notifListener_OnNotification;
diff --git a/GUI.NET/Debugger/frmWatchWindow.Designer.cs b/GUI.NET/Debugger/frmWatchWindow.Designer.cs
index 78159151..0bd59cc5 100644
--- a/GUI.NET/Debugger/frmWatchWindow.Designer.cs
+++ b/GUI.NET/Debugger/frmWatchWindow.Designer.cs
@@ -28,6 +28,8 @@
private void InitializeComponent()
{
this.ctrlWatch = new Mesen.GUI.Debugger.ctrlWatch();
+ this.picWatchHelp = new System.Windows.Forms.PictureBox();
+ ((System.ComponentModel.ISupportInitialize)(this.picWatchHelp)).BeginInit();
this.SuspendLayout();
//
// ctrlWatch
@@ -38,15 +40,28 @@
this.ctrlWatch.Size = new System.Drawing.Size(317, 322);
this.ctrlWatch.TabIndex = 0;
//
+ // picWatchHelp
+ //
+ this.picWatchHelp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.picWatchHelp.Image = global::Mesen.GUI.Properties.Resources.Help;
+ this.picWatchHelp.Location = new System.Drawing.Point(297, 4);
+ this.picWatchHelp.Name = "picWatchHelp";
+ this.picWatchHelp.Size = new System.Drawing.Size(16, 16);
+ this.picWatchHelp.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;
+ this.picWatchHelp.TabIndex = 2;
+ this.picWatchHelp.TabStop = false;
+ //
// frmWatchWindow
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(317, 322);
+ this.Controls.Add(this.picWatchHelp);
this.Controls.Add(this.ctrlWatch);
this.MinimumSize = new System.Drawing.Size(248, 137);
this.Name = "frmWatchWindow";
this.Text = "Watch Window";
+ ((System.ComponentModel.ISupportInitialize)(this.picWatchHelp)).EndInit();
this.ResumeLayout(false);
}
@@ -54,5 +69,6 @@
#endregion
private ctrlWatch ctrlWatch;
+ private System.Windows.Forms.PictureBox picWatchHelp;
}
}
\ No newline at end of file
diff --git a/GUI.NET/Debugger/frmWatchWindow.cs b/GUI.NET/Debugger/frmWatchWindow.cs
index 7d7c62a4..2a4a5a15 100644
--- a/GUI.NET/Debugger/frmWatchWindow.cs
+++ b/GUI.NET/Debugger/frmWatchWindow.cs
@@ -26,6 +26,8 @@ namespace Mesen.GUI.Debugger
this.Size = ConfigManager.Config.DebugInfo.WatchWindowSize;
this.Location = ConfigManager.Config.DebugInfo.WatchWindowLocation;
}
+
+ this.toolTip.SetToolTip(picWatchHelp, ctrlWatch.GetTooltipText());
}
}