Debugger: Watch - Added format specifiers
+ Added help icon to watch window
This commit is contained in:
parent
b6e7ce4a9b
commit
fac849315f
10 changed files with 449 additions and 54 deletions
|
@ -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 ##
|
||||
|
||||
<div class="imgBox"><div>
|
||||
<img src="/images/WatchList.png" />
|
||||
<img src="/images/WatchWindow.png" />
|
||||
<span>Watch Window</span>
|
||||
</div></div>
|
||||
|
||||
The watch window allows you to evaluate expression and see their value. Mesen supports complex expressions in C/C++ style syntax.
|
||||
<div class="imgBox" style="margin-top: 10px"><div>
|
||||
<img src="/images/WatchList.png" />
|
||||
<span>Watch List</span>
|
||||
</div></div>
|
||||
|
||||
**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**
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 7 KiB After Width: | Height: | Size: 4.7 KiB |
BIN
Docs/static/images/WatchWindow.png
Normal file
BIN
Docs/static/images/WatchWindow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -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);
|
||||
|
|
184
GUI.NET/Debugger/Controls/ctrlWatch.Designer.cs
generated
184
GUI.NET/Debugger/Controls/ctrlWatch.Designer.cs
generated
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<WatchValueInfo> watchContent = WatchManager.GetWatchContent(mnuHexDisplay.Checked, _previousValues);
|
||||
List<WatchValueInfo> 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<string> 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("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<string> _watchEntries = new List<string>();
|
||||
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<string> WatchEntries
|
||||
{
|
||||
|
@ -24,28 +26,39 @@ namespace Mesen.GUI.Debugger
|
|||
}
|
||||
}
|
||||
|
||||
public static List<WatchValueInfo> GetWatchContent(bool useHex, List<WatchValueInfo> previousValues)
|
||||
public static List<WatchValueInfo> GetWatchContent(List<WatchValueInfo> previousValues)
|
||||
{
|
||||
WatchFormatStyle defaultStyle = ConfigManager.Config.DebugInfo.WatchFormat;
|
||||
int defaultByteLength = 1;
|
||||
if(defaultStyle == WatchFormatStyle.Signed) {
|
||||
defaultByteLength = 4;
|
||||
}
|
||||
|
||||
var list = new List<WatchValueInfo>();
|
||||
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 = "<invalid expression>"; forceHasChanged = true; break;
|
||||
case EvalResultType.DivideBy0: newValue = "<division by zero>"; 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<string> values = new List<string>(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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
16
GUI.NET/Debugger/frmWatchWindow.Designer.cs
generated
16
GUI.NET/Debugger/frmWatchWindow.Designer.cs
generated
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue