mirror of
https://github.com/SourMesen/Mesen.git
synced 2025-04-02 10:52:48 -04:00
Debugger: Improved watch window (now allows expressions like conditional breakpoints)
This commit is contained in:
parent
7d0b9e8dd5
commit
9658597e9f
11 changed files with 224 additions and 96 deletions
|
@ -153,7 +153,7 @@ bool Debugger::HasMatchingBreakpoint(BreakpointType type, uint32_t addr, int16_t
|
|||
return true;
|
||||
} else {
|
||||
ExpressionEvaluator expEval;
|
||||
if(expEval.Evaluate(condition, state, value)) {
|
||||
if(expEval.Evaluate(condition, state, value) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -163,6 +163,16 @@ bool Debugger::HasMatchingBreakpoint(BreakpointType type, uint32_t addr, int16_t
|
|||
return false;
|
||||
}
|
||||
|
||||
int32_t Debugger::EvaluateExpression(string expression, EvalResultType &resultType)
|
||||
{
|
||||
ExpressionEvaluator expEval;
|
||||
|
||||
DebugState state;
|
||||
GetState(&state);
|
||||
|
||||
return expEval.Evaluate(expression, state, resultType);
|
||||
}
|
||||
|
||||
void Debugger::UpdateCallstack(uint32_t addr)
|
||||
{
|
||||
if(_lastInstruction == 0x60 && !_callstackRelative.empty()) {
|
||||
|
|
|
@ -111,9 +111,11 @@ public:
|
|||
bool IsCodeChanged();
|
||||
string GenerateOutput();
|
||||
string* GetCode();
|
||||
|
||||
|
||||
uint8_t GetMemoryValue(uint32_t addr);
|
||||
uint32_t GetRelativeAddress(uint32_t addr);
|
||||
|
||||
int32_t EvaluateExpression(string expression, EvalResultType &resultType);
|
||||
|
||||
static void ProcessRamOperation(MemoryOperationType type, uint16_t &addr, uint8_t value);
|
||||
static void ProcessVramOperation(MemoryOperationType type, uint16_t addr, uint8_t value);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "stdafx.h"
|
||||
#include <climits>
|
||||
#include "ExpressionEvaluator.h"
|
||||
#include "Console.h"
|
||||
#include "Debugger.h"
|
||||
|
@ -51,7 +52,7 @@ bool ExpressionEvaluator::CheckSpecialTokens(string expression, size_t &pos, str
|
|||
do {
|
||||
char c = std::tolower(expression[pos]);
|
||||
if(c >= 'a' && c <= 'z') {
|
||||
token += expression[pos];
|
||||
token += c;
|
||||
pos++;
|
||||
} else {
|
||||
break;
|
||||
|
@ -91,22 +92,37 @@ string ExpressionEvaluator::GetNextToken(string expression, size_t &pos)
|
|||
bool isOperator = false;
|
||||
bool isNumber = false;
|
||||
bool isHex = false;
|
||||
bool isMemoryAddress = false;
|
||||
bool validMemoryAddress = false;
|
||||
size_t initialPos = pos;
|
||||
for(size_t len = expression.size(); pos < len; pos++) {
|
||||
char c = std::tolower(expression[pos]);
|
||||
|
||||
if(c == '$' && pos == initialPos) {
|
||||
isHex = true;
|
||||
if((c == '$' || c == '[') && pos == initialPos) {
|
||||
if(c == '$') {
|
||||
isHex = true;
|
||||
} else if(c == '[') {
|
||||
isMemoryAddress = true;
|
||||
|
||||
//Pretend the token starts after this
|
||||
initialPos++;
|
||||
}
|
||||
} else if((c >= '0' && c <= '9') || (isHex && c >= 'a' && c <= 'f')) {
|
||||
if(isNumber || output.empty()) {
|
||||
output += c;
|
||||
isNumber = true;
|
||||
} else {
|
||||
//Done reading number
|
||||
//Just hit the start of a number, done reading current token
|
||||
break;
|
||||
}
|
||||
} else if(isNumber) {
|
||||
//First non-numeric character, done
|
||||
if(c == ']') {
|
||||
if(isMemoryAddress) {
|
||||
validMemoryAddress = true;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
break;
|
||||
} else if(c == '(' || c == ')' || c == '-' || c == '+' || c == '~') {
|
||||
if(output.empty()) {
|
||||
|
@ -118,10 +134,11 @@ string ExpressionEvaluator::GetNextToken(string expression, size_t &pos)
|
|||
//Figure out if it's ! or !=
|
||||
if(pos < len - 1) {
|
||||
if(expression[pos + 1] == '=') {
|
||||
pos++;
|
||||
output += "!=";
|
||||
pos+=2;
|
||||
} else {
|
||||
output += "!";
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -155,6 +172,12 @@ string ExpressionEvaluator::GetNextToken(string expression, size_t &pos)
|
|||
output = std::to_string(x);
|
||||
}
|
||||
|
||||
if(isMemoryAddress && validMemoryAddress) {
|
||||
uint32_t address = std::stoi(output) % 0xFFFF;
|
||||
address += EvalValues::MemoryAddress;
|
||||
output = std::to_string(address);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@ -209,12 +232,14 @@ void ExpressionEvaluator::ToRpn(string expression, vector<int> &outputQueue)
|
|||
}
|
||||
}
|
||||
|
||||
bool ExpressionEvaluator::EvaluateExpression(vector<int> *outputQueue, DebugState &state, int16_t memoryValue)
|
||||
int32_t ExpressionEvaluator::EvaluateExpression(vector<int> *outputQueue, DebugState &state, EvalResultType &resultType, int16_t memoryValue)
|
||||
{
|
||||
int pos = 0;
|
||||
int right = 0;
|
||||
int left = 0;
|
||||
int operandStack[1000];
|
||||
resultType = EvalResultType::Numeric;
|
||||
shared_ptr<Debugger> debugger = Console::GetInstance()->GetDebugger().lock();
|
||||
|
||||
for(size_t i = 0, len = outputQueue->size(); i < len; i++) {
|
||||
int token = (*outputQueue)[i];
|
||||
|
@ -233,12 +258,18 @@ bool ExpressionEvaluator::EvaluateExpression(vector<int> *outputQueue, DebugStat
|
|||
case EvalValues::PpuScanline: token = state.PPU.Scanline; break;
|
||||
case EvalValues::Value: token = memoryValue; break;
|
||||
}
|
||||
|
||||
if(token >= EvalValues::MemoryAddress) {
|
||||
uint16_t memoryAddress = token - EvalValues::MemoryAddress;
|
||||
token = debugger->GetMemoryValue(memoryAddress);
|
||||
}
|
||||
} else if(token >= EvalOperators::Multiplication) {
|
||||
right = operandStack[--pos];
|
||||
if(pos > 0) {
|
||||
left = operandStack[--pos];
|
||||
}
|
||||
|
||||
resultType = EvalResultType::Numeric;
|
||||
switch(token) {
|
||||
case EvalOperators::Multiplication: token = left * right; break;
|
||||
case EvalOperators::Division: token = left / right; break;
|
||||
|
@ -247,17 +278,17 @@ bool ExpressionEvaluator::EvaluateExpression(vector<int> *outputQueue, DebugStat
|
|||
case EvalOperators::Substration: token = left - right; break;
|
||||
case EvalOperators::ShiftLeft: token = left << right; break;
|
||||
case EvalOperators::ShiftRight: token = left >> right; break;
|
||||
case EvalOperators::SmallerThan: token = left < right; break;
|
||||
case EvalOperators::SmallerOrEqual: token = left <= right; break;
|
||||
case EvalOperators::GreaterThan: token = left > right; break;
|
||||
case EvalOperators::GreaterOrEqual: token = left >= right; break;
|
||||
case EvalOperators::Equal: token = left == right; break;
|
||||
case EvalOperators::NotEqual: token = left != right; break;
|
||||
case EvalOperators::SmallerThan: token = left < right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::SmallerOrEqual: token = left <= right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::GreaterThan: token = left > right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::GreaterOrEqual: token = left >= right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::Equal: token = left == right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::NotEqual: token = left != right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::BinaryAnd: token = left & right; break;
|
||||
case EvalOperators::BinaryXor: token = left | right; break;
|
||||
case EvalOperators::BinaryOr: token = left ^ right; break;
|
||||
case EvalOperators::LogicalAnd: token = left && right; break;
|
||||
case EvalOperators::LogicalOr: token = left || right; break;
|
||||
case EvalOperators::LogicalAnd: token = left && right; resultType = EvalResultType::Boolean; break;
|
||||
case EvalOperators::LogicalOr: token = left || right; resultType = EvalResultType::Boolean; break;
|
||||
|
||||
//Unary operators
|
||||
case EvalOperators::Plus: token = right; break;
|
||||
|
@ -269,14 +300,14 @@ bool ExpressionEvaluator::EvaluateExpression(vector<int> *outputQueue, DebugStat
|
|||
}
|
||||
operandStack[pos++] = token;
|
||||
}
|
||||
return operandStack[0] != 0;
|
||||
return operandStack[0];
|
||||
}
|
||||
|
||||
ExpressionEvaluator::ExpressionEvaluator()
|
||||
{
|
||||
}
|
||||
|
||||
bool ExpressionEvaluator::PrivateEvaluate(string expression, DebugState &state, int16_t memoryValue)
|
||||
int32_t ExpressionEvaluator::PrivateEvaluate(string expression, DebugState &state, EvalResultType &resultType, int16_t memoryValue)
|
||||
{
|
||||
vector<int> *outputQueue = nullptr;
|
||||
|
||||
|
@ -301,15 +332,22 @@ bool ExpressionEvaluator::PrivateEvaluate(string expression, DebugState &state,
|
|||
outputQueue = &_outputCache[expression];
|
||||
}
|
||||
|
||||
return EvaluateExpression(outputQueue, state, memoryValue);
|
||||
return EvaluateExpression(outputQueue, state, resultType, memoryValue);
|
||||
}
|
||||
|
||||
bool ExpressionEvaluator::Evaluate(string expression, DebugState &state, int16_t memoryValue)
|
||||
int32_t ExpressionEvaluator::Evaluate(string expression, DebugState &state, int16_t memoryValue)
|
||||
{
|
||||
EvalResultType resultType;
|
||||
return Evaluate(expression, state, resultType, memoryValue);
|
||||
}
|
||||
|
||||
int32_t ExpressionEvaluator::Evaluate(string expression, DebugState &state, EvalResultType &resultType, int16_t memoryValue)
|
||||
{
|
||||
try {
|
||||
return PrivateEvaluate(expression, state, memoryValue);
|
||||
return PrivateEvaluate(expression, state, resultType, memoryValue);
|
||||
} catch(std::exception) {
|
||||
return false;
|
||||
resultType = EvalResultType::Invalid;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,7 +355,8 @@ bool ExpressionEvaluator::Validate(string expression)
|
|||
{
|
||||
try {
|
||||
DebugState state;
|
||||
PrivateEvaluate(expression, state, 0);
|
||||
EvalResultType type;
|
||||
PrivateEvaluate(expression, state, type, 0);
|
||||
return true;
|
||||
} catch(std::exception e) {
|
||||
return false;
|
||||
|
|
|
@ -51,6 +51,15 @@ enum EvalValues
|
|||
Nmi = 2000000107,
|
||||
Irq = 2000000108,
|
||||
Value = 2000000109,
|
||||
|
||||
MemoryAddress = 2000100000
|
||||
};
|
||||
|
||||
enum EvalResultType
|
||||
{
|
||||
Numeric = 0,
|
||||
Boolean = 1,
|
||||
Invalid = 2
|
||||
};
|
||||
|
||||
class StringHasher
|
||||
|
@ -80,12 +89,13 @@ private:
|
|||
bool CheckSpecialTokens(string expression, size_t &pos, string &output);
|
||||
string GetNextToken(string expression, size_t &pos);
|
||||
void ToRpn(string expression, vector<int> &outputQueue);
|
||||
bool EvaluateExpression(vector<int> *outputQueue, DebugState &state, int16_t memoryValue);
|
||||
bool PrivateEvaluate(string expression, DebugState &state, int16_t memoryValue);
|
||||
int32_t EvaluateExpression(vector<int> *outputQueue, DebugState &state, EvalResultType &resultType, int16_t memoryValue);
|
||||
int32_t PrivateEvaluate(string expression, DebugState &state, EvalResultType &resultType, int16_t memoryValue);
|
||||
|
||||
public:
|
||||
ExpressionEvaluator();
|
||||
|
||||
bool Evaluate(string expression, DebugState &state, int16_t memoryValue = 0);
|
||||
int32_t Evaluate(string expression, DebugState &state, int16_t memoryValue = 0);
|
||||
int32_t Evaluate(string expression, DebugState &state, EvalResultType &resultType, int16_t memoryValue = 0);
|
||||
bool Validate(string expression);
|
||||
};
|
|
@ -11,13 +11,20 @@ namespace Mesen.GUI.Controls
|
|||
{
|
||||
class MyListView : ListView
|
||||
{
|
||||
private bool preventCheck = false;
|
||||
private bool _preventCheck = false;
|
||||
private int _editItemIndex = -1;
|
||||
private string _originalText;
|
||||
|
||||
public bool IsEditing
|
||||
{
|
||||
get { return _editItemIndex >= 0; }
|
||||
}
|
||||
|
||||
protected override void OnItemCheck(ItemCheckEventArgs e)
|
||||
{
|
||||
if(this.preventCheck) {
|
||||
if(this._preventCheck) {
|
||||
e.NewValue = e.CurrentValue;
|
||||
this.preventCheck = false;
|
||||
this._preventCheck = false;
|
||||
} else
|
||||
base.OnItemCheck(e);
|
||||
}
|
||||
|
@ -25,17 +32,64 @@ namespace Mesen.GUI.Controls
|
|||
protected override void OnMouseDown(MouseEventArgs e)
|
||||
{
|
||||
if(e.Button == MouseButtons.Left && e.Clicks > 1) {
|
||||
this.preventCheck = true;
|
||||
this._preventCheck = true;
|
||||
} else {
|
||||
this.preventCheck = false;
|
||||
this._preventCheck = false;
|
||||
}
|
||||
base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
protected override void OnBeforeLabelEdit(LabelEditEventArgs e)
|
||||
{
|
||||
_editItemIndex = e.Item;
|
||||
base.OnBeforeLabelEdit(e);
|
||||
}
|
||||
|
||||
protected override void OnAfterLabelEdit(LabelEditEventArgs e)
|
||||
{
|
||||
base.OnAfterLabelEdit(e);
|
||||
_editItemIndex = -1;
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
{
|
||||
this.preventCheck = false;
|
||||
if(!this.IsEditing && e.KeyData == Keys.Delete) {
|
||||
if(this.SelectedItems.Count >= 1) {
|
||||
var itemsToRemove = new List<ListViewItem>();
|
||||
foreach(ListViewItem item in this.SelectedItems) {
|
||||
itemsToRemove.Add(item);
|
||||
}
|
||||
foreach(ListViewItem item in itemsToRemove) {
|
||||
this.Items.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
this._preventCheck = false;
|
||||
base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
|
||||
{
|
||||
if(this.SelectedItems.Count > _editItemIndex && _editItemIndex >= 0) {
|
||||
if(keyData == Keys.Escape) {
|
||||
this.SelectedItems[_editItemIndex].Text = _originalText;
|
||||
}
|
||||
}
|
||||
return base.ProcessCmdKey(ref msg, keyData);
|
||||
}
|
||||
|
||||
protected override void OnKeyPress(KeyPressEventArgs e)
|
||||
{
|
||||
if(this.LabelEdit && _editItemIndex < 0 && this.SelectedItems.Count > 0) {
|
||||
if(e.KeyChar >= 32 && e.KeyChar <= 127) {
|
||||
_originalText = this.SelectedItems[0].Text;
|
||||
this.SelectedItems[0].Text = e.KeyChar.ToString();
|
||||
this.SelectedItems[0].BeginEdit();
|
||||
SendKeys.Send("{RIGHT}");
|
||||
}
|
||||
}
|
||||
|
||||
base.OnKeyPress(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,13 +31,13 @@
|
|||
this.contextMenuBreakpoints = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||
this.mnuAddBreakpoint = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuRemoveBreakpoint = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuGoToLocation = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.lstBreakpoints = new Mesen.GUI.Controls.MyListView();
|
||||
this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.columnHeader4 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.colLastColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.mnuGoToLocation = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.contextMenuBreakpoints.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
|
@ -54,6 +54,7 @@
|
|||
// mnuAddBreakpoint
|
||||
//
|
||||
this.mnuAddBreakpoint.Name = "mnuAddBreakpoint";
|
||||
this.mnuAddBreakpoint.ShortcutKeys = System.Windows.Forms.Keys.Insert;
|
||||
this.mnuAddBreakpoint.Size = new System.Drawing.Size(152, 22);
|
||||
this.mnuAddBreakpoint.Text = "Add...";
|
||||
this.mnuAddBreakpoint.Click += new System.EventHandler(this.mnuAddBreakpoint_Click);
|
||||
|
@ -66,6 +67,13 @@
|
|||
this.mnuRemoveBreakpoint.Text = "Remove";
|
||||
this.mnuRemoveBreakpoint.Click += new System.EventHandler(this.mnuRemoveBreakpoint_Click);
|
||||
//
|
||||
// mnuGoToLocation
|
||||
//
|
||||
this.mnuGoToLocation.Name = "mnuGoToLocation";
|
||||
this.mnuGoToLocation.Size = new System.Drawing.Size(152, 22);
|
||||
this.mnuGoToLocation.Text = "Go to location";
|
||||
this.mnuGoToLocation.Click += new System.EventHandler(this.mnuGoToLocation_Click);
|
||||
//
|
||||
// lstBreakpoints
|
||||
//
|
||||
this.lstBreakpoints.CheckBoxes = true;
|
||||
|
@ -114,13 +122,6 @@
|
|||
//
|
||||
this.colLastColumn.Text = "";
|
||||
//
|
||||
// mnuGoToLocation
|
||||
//
|
||||
this.mnuGoToLocation.Name = "mnuGoToLocation";
|
||||
this.mnuGoToLocation.Size = new System.Drawing.Size(152, 22);
|
||||
this.mnuGoToLocation.Text = "Go to location";
|
||||
this.mnuGoToLocation.Click += new System.EventHandler(this.mnuGoToLocation_Click);
|
||||
//
|
||||
// ctrlBreakpoints
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
|
|
22
GUI.NET/Debugger/Controls/ctrlWatch.Designer.cs
generated
22
GUI.NET/Debugger/Controls/ctrlWatch.Designer.cs
generated
|
@ -29,13 +29,13 @@
|
|||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.Windows.Forms.ListViewItem listViewItem1 = new System.Windows.Forms.ListViewItem("");
|
||||
this.lstWatch = new System.Windows.Forms.ListView();
|
||||
this.lstWatch = new Mesen.GUI.Controls.MyListView();
|
||||
this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.colLastColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.contextMenuWatch = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||
this.mnuRemoveWatch = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.mnuHexDisplay = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.colLastColumn = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.contextMenuWatch.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
|
@ -61,15 +61,22 @@
|
|||
this.lstWatch.View = System.Windows.Forms.View.Details;
|
||||
this.lstWatch.AfterLabelEdit += new System.Windows.Forms.LabelEditEventHandler(this.lstWatch_AfterLabelEdit);
|
||||
this.lstWatch.SelectedIndexChanged += new System.EventHandler(this.lstWatch_SelectedIndexChanged);
|
||||
this.lstWatch.Click += new System.EventHandler(this.lstWatch_Click);
|
||||
this.lstWatch.DoubleClick += new System.EventHandler(this.lstWatch_DoubleClick);
|
||||
//
|
||||
// columnHeader1
|
||||
//
|
||||
this.columnHeader1.Text = "Address";
|
||||
this.columnHeader1.Text = "Name";
|
||||
this.columnHeader1.Width = 180;
|
||||
//
|
||||
// columnHeader2
|
||||
//
|
||||
this.columnHeader2.Text = "Value";
|
||||
this.columnHeader2.Width = 100;
|
||||
this.columnHeader2.Width = 110;
|
||||
//
|
||||
// colLastColumn
|
||||
//
|
||||
this.colLastColumn.Text = "";
|
||||
//
|
||||
// contextMenuWatch
|
||||
//
|
||||
|
@ -85,7 +92,6 @@
|
|||
this.mnuRemoveWatch.ShortcutKeys = System.Windows.Forms.Keys.Delete;
|
||||
this.mnuRemoveWatch.Size = new System.Drawing.Size(183, 22);
|
||||
this.mnuRemoveWatch.Text = "Remove";
|
||||
this.mnuRemoveWatch.Click += new System.EventHandler(this.mnuRemoveWatch_Click);
|
||||
//
|
||||
// mnuHexDisplay
|
||||
//
|
||||
|
@ -97,10 +103,6 @@
|
|||
this.mnuHexDisplay.Text = "Hexadecimal Display";
|
||||
this.mnuHexDisplay.Click += new System.EventHandler(this.mnuHexDisplay_Click);
|
||||
//
|
||||
// colLastColumn
|
||||
//
|
||||
this.colLastColumn.Text = "";
|
||||
//
|
||||
// ctrlWatch
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
|
@ -115,7 +117,7 @@
|
|||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.ListView lstWatch;
|
||||
private Mesen.GUI.Controls.MyListView lstWatch;
|
||||
private System.Windows.Forms.ColumnHeader columnHeader1;
|
||||
private System.Windows.Forms.ColumnHeader columnHeader2;
|
||||
private System.Windows.Forms.ContextMenuStrip contextMenuWatch;
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace Mesen.GUI.Debugger
|
|||
lstWatch.ColumnWidthChanged += lstWatch_ColumnWidthChanged;
|
||||
}
|
||||
|
||||
public void UpdateWatch()
|
||||
public void UpdateWatch(int currentSelection = -1)
|
||||
{
|
||||
lstWatch.SelectedIndices.Clear();
|
||||
|
||||
|
@ -70,20 +70,38 @@ namespace Mesen.GUI.Debugger
|
|||
previousValue = item.SubItems[1].Text;
|
||||
item.SubItems.RemoveAt(1);
|
||||
}
|
||||
item.SubItems[0].Text = item.SubItems[0].Text.ToUpper();
|
||||
|
||||
string newValue;
|
||||
Byte memoryValue = InteropEmu.DebugGetMemoryValue((UInt32)item.Tag);
|
||||
if(mnuHexDisplay.Checked) {
|
||||
newValue = "$" + memoryValue.ToString("X");
|
||||
} else {
|
||||
newValue = memoryValue.ToString();
|
||||
string newValue = "";
|
||||
EvalResultType resultType;
|
||||
Int32 result = InteropEmu.DebugEvaluateExpression(item.Text, out resultType);
|
||||
|
||||
switch(resultType) {
|
||||
case EvalResultType.Numeric:
|
||||
if(mnuHexDisplay.Checked) {
|
||||
newValue = "$" + result.ToString("X");
|
||||
} else {
|
||||
newValue = result.ToString();
|
||||
}
|
||||
break;
|
||||
case EvalResultType.Boolean:
|
||||
newValue = result == 0 ? "false" : "true";
|
||||
break;
|
||||
|
||||
case EvalResultType.Invalid:
|
||||
newValue = "<invalid expression>";
|
||||
break;
|
||||
}
|
||||
|
||||
item.SubItems.Add(newValue);
|
||||
item.SubItems[1].ForeColor = newValue != previousValue ? Color.Red : Color.Black;
|
||||
}
|
||||
}
|
||||
AdjustColumnWidth();
|
||||
|
||||
if(currentSelection >= 0 && lstWatch.Items.Count > currentSelection) {
|
||||
lstWatch.FocusedItem = lstWatch.Items[currentSelection];
|
||||
lstWatch.Items[currentSelection].Selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddWatch(UInt32 address)
|
||||
|
@ -95,39 +113,7 @@ namespace Mesen.GUI.Debugger
|
|||
|
||||
private void lstWatch_AfterLabelEdit(object sender, LabelEditEventArgs e)
|
||||
{
|
||||
e.CancelEdit = true;
|
||||
|
||||
if(e.Label != null) {
|
||||
ListViewItem item = lstWatch.Items[e.Item];
|
||||
item.Text = e.Label;
|
||||
if(!string.IsNullOrWhiteSpace(item.Text)) {
|
||||
if(!item.Text.StartsWith("$")) {
|
||||
item.Text = "$" + item.Text;
|
||||
}
|
||||
|
||||
UInt32 address;
|
||||
if(UInt32.TryParse(item.Text.Substring(1), System.Globalization.NumberStyles.AllowHexSpecifier, null, out address)) {
|
||||
lstWatch.Items[e.Item].Tag = address;
|
||||
} else {
|
||||
item.Text = string.Empty;
|
||||
}
|
||||
}
|
||||
UpdateWatch();
|
||||
}
|
||||
}
|
||||
|
||||
private void mnuRemoveWatch_Click(object sender, EventArgs e)
|
||||
{
|
||||
if(lstWatch.SelectedItems.Count >= 1) {
|
||||
var itemsToRemove = new List<ListViewItem>();
|
||||
foreach(ListViewItem item in lstWatch.SelectedItems) {
|
||||
itemsToRemove.Add(item);
|
||||
}
|
||||
foreach(ListViewItem item in itemsToRemove) {
|
||||
lstWatch.Items.Remove(item);
|
||||
}
|
||||
UpdateWatch();
|
||||
}
|
||||
this.BeginInvoke((MethodInvoker)(() => { this.UpdateWatch(e.Item); }));
|
||||
}
|
||||
|
||||
private void mnuHexDisplay_Click(object sender, EventArgs e)
|
||||
|
@ -139,5 +125,19 @@ namespace Mesen.GUI.Debugger
|
|||
{
|
||||
mnuRemoveWatch.Enabled = lstWatch.SelectedItems.Count >= 1;
|
||||
}
|
||||
|
||||
private void lstWatch_Click(object sender, EventArgs e)
|
||||
{
|
||||
if(lstWatch.SelectedItems.Count == 1 && string.IsNullOrWhiteSpace(lstWatch.SelectedItems[0].Text)) {
|
||||
lstWatch.SelectedItems[0].BeginEdit();
|
||||
}
|
||||
}
|
||||
|
||||
private void lstWatch_DoubleClick(object sender, EventArgs e)
|
||||
{
|
||||
if(lstWatch.SelectedItems.Count == 1) {
|
||||
lstWatch.SelectedItems[0].BeginEdit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace Mesen.GUI.Debugger
|
|||
AddBinding("Condition", txtCondition);
|
||||
|
||||
this.toolTip.SetToolTip(this.chkAbsolute, "Check to set an absolute address based on the exact address in PRG/CHR ROM (not CPU/PPU memory)");
|
||||
this.toolTip.SetToolTip(this.picHelp, "Most expressions/operators are accepted (C++ syntax)." + Environment.NewLine + "Note: Use the $ prefix to denote hexadecimal values." + Environment.NewLine + Environment.NewLine + "A/X/Y/PS/SP: Value of registers" + Environment.NewLine +"Irq/Nmi: True if the Irq/Nmi flags are set" + Environment.NewLine + "Cycle/Scanline: Current cycle (0-340)/scanline(-1 to 240) of the PPU" + Environment.NewLine + "Value: Current value being read/written from/to memory" + Environment.NewLine + Environment.NewLine + "Examples:" + Environment.NewLine + "a == 10 || x == $23" + Environment.NewLine + "scanline == 10 && (cycle >= 55 && cycle <= 100)");
|
||||
this.toolTip.SetToolTip(this.picHelp, "Most expressions/operators are accepted (C++ syntax)." + Environment.NewLine + "Note: Use the $ prefix to denote hexadecimal values." + Environment.NewLine + Environment.NewLine + "A/X/Y/PS/SP: Value of registers" + Environment.NewLine +"Irq/Nmi: True if the Irq/Nmi flags are set" + Environment.NewLine + "Cycle/Scanline: Current cycle (0-340)/scanline(-1 to 240) of the PPU" + Environment.NewLine + "Value: Current value being read/written from/to memory" + Environment.NewLine + "[<address>]: Value at address (CPU)" + Environment.NewLine + Environment.NewLine + "Examples:" + Environment.NewLine + "a == 10 || x == $23" + Environment.NewLine + "scanline == 10 && (cycle >= 55 && cycle <= 100)" + Environment.NewLine + "x == [$150] || y == [10]");
|
||||
}
|
||||
|
||||
protected override bool ValidateInput()
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace Mesen.GUI
|
|||
[DllImport(DLLPath)] public static extern void StartServer(UInt16 port);
|
||||
[DllImport(DLLPath)] public static extern void StopServer();
|
||||
[DllImport(DLLPath)] public static extern bool IsServerRunning();
|
||||
[DllImport(DLLPath)] public static extern void Connect(string host, UInt16 port, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string playerName, byte[] avatarData, UInt32 avatarSize);
|
||||
[DllImport(DLLPath)] public static extern void Connect([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string host, UInt16 port, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string playerName, byte[] avatarData, UInt32 avatarSize);
|
||||
[DllImport(DLLPath)] public static extern void Disconnect();
|
||||
[DllImport(DLLPath)] public static extern bool IsConnected();
|
||||
|
||||
|
@ -64,7 +64,7 @@ namespace Mesen.GUI
|
|||
[DllImport(DLLPath)] public static extern Int64 GetStateInfo(UInt32 stateIndex);
|
||||
|
||||
[DllImport(DLLPath)] public static extern void CheatAddCustom(UInt32 address, Byte value, Int32 compareValue, [MarshalAs(UnmanagedType.I1)]bool isRelativeAddress);
|
||||
[DllImport(DLLPath)] public static extern void CheatAddGameGenie(string code);
|
||||
[DllImport(DLLPath)] public static extern void CheatAddGameGenie([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string code);
|
||||
[DllImport(DLLPath)] public static extern void CheatAddProActionRocky(UInt32 code);
|
||||
[DllImport(DLLPath)] public static extern void CheatClear();
|
||||
|
||||
|
@ -94,6 +94,7 @@ namespace Mesen.GUI
|
|||
[DllImport(DLLPath)] public static extern Byte DebugGetMemoryValue(UInt32 addr);
|
||||
[DllImport(DLLPath)] public static extern UInt32 DebugGetRelativeAddress(UInt32 addr);
|
||||
[DllImport(DLLPath)] public static extern void DebugSetNextStatement(UInt16 addr);
|
||||
[DllImport(DLLPath)] public static extern Int32 DebugEvaluateExpression([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string expression, out EvalResultType resultType);
|
||||
|
||||
|
||||
[DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugLoadCdlFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string cdlFilepath);
|
||||
|
@ -445,7 +446,14 @@ namespace Mesen.GUI
|
|||
Write = 4,
|
||||
ReadVram = 8,
|
||||
WriteVram = 16
|
||||
};
|
||||
}
|
||||
|
||||
public enum EvalResultType
|
||||
{
|
||||
Numeric = 0,
|
||||
Boolean = 1,
|
||||
Invalid = 2
|
||||
}
|
||||
|
||||
public enum NesModel
|
||||
{
|
||||
|
|
|
@ -35,7 +35,7 @@ extern "C"
|
|||
DllExport void __stdcall DebugStepCycles(uint32_t count) { GetDebugger()->StepCycles(count); }
|
||||
DllExport void __stdcall DebugStepOver() { GetDebugger()->StepOver(); }
|
||||
DllExport void __stdcall DebugStepOut() { GetDebugger()->StepOut(); }
|
||||
DllExport int __stdcall DebugIsCodeChanged() { return GetDebugger()->IsCodeChanged(); }
|
||||
DllExport uint32_t __stdcall DebugIsCodeChanged() { return GetDebugger()->IsCodeChanged(); }
|
||||
DllExport const char* __stdcall DebugGetCode() { return GetDebugger()->GetCode()->c_str(); }
|
||||
|
||||
DllExport void __stdcall DebugSetNextStatement(uint16_t addr) { GetDebugger()->SetNextStatement(addr); }
|
||||
|
@ -55,4 +55,6 @@ extern "C"
|
|||
DllExport bool __stdcall DebugSaveCdlFile(char* cdlFilepath) { return GetDebugger()->SaveCdlFile(cdlFilepath); }
|
||||
DllExport void __stdcall DebugGetCdlRatios(CdlRatios* cdlRatios) { *cdlRatios = GetDebugger()->GetCdlRatios(); }
|
||||
DllExport void __stdcall DebugResetCdlLog() { GetDebugger()->ResetCdlLog(); }
|
||||
|
||||
DllExport int32_t __stdcall DebugEvaluateExpression(char* expression, EvalResultType *resultType) { return GetDebugger()->EvaluateExpression(expression, *resultType); }
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue