Debugger: Ability to define assert statements in comments
This commit is contained in:
parent
77e04a679e
commit
1ff7ed7b8c
5 changed files with 107 additions and 20 deletions
|
@ -292,7 +292,7 @@ bool ExpressionEvaluator::ToRpn(string expression, ExpressionData &data)
|
||||||
if(!ProcessSpecialOperator(EvalOperators::Parenthesis, opStack, precedenceStack, data.RpnQueue)) {
|
if(!ProcessSpecialOperator(EvalOperators::Parenthesis, opStack, precedenceStack, data.RpnQueue)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
operatorExpected = true;
|
operatorOrEndTokenExpected = true;
|
||||||
} else if(token[0] == '[') {
|
} else if(token[0] == '[') {
|
||||||
bracketCount++;
|
bracketCount++;
|
||||||
opStack.push(EvalOperators::Bracket);
|
opStack.push(EvalOperators::Bracket);
|
||||||
|
@ -302,7 +302,7 @@ bool ExpressionEvaluator::ToRpn(string expression, ExpressionData &data)
|
||||||
if(!ProcessSpecialOperator(EvalOperators::Bracket, opStack, precedenceStack, data.RpnQueue)) {
|
if(!ProcessSpecialOperator(EvalOperators::Bracket, opStack, precedenceStack, data.RpnQueue)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
operatorExpected = true;
|
operatorOrEndTokenExpected = true;
|
||||||
} else if(token[0] == '{') {
|
} else if(token[0] == '{') {
|
||||||
braceCount++;
|
braceCount++;
|
||||||
opStack.push(EvalOperators::Braces);
|
opStack.push(EvalOperators::Braces);
|
||||||
|
@ -312,7 +312,7 @@ bool ExpressionEvaluator::ToRpn(string expression, ExpressionData &data)
|
||||||
if(!ProcessSpecialOperator(EvalOperators::Braces, opStack, precedenceStack, data.RpnQueue)){
|
if(!ProcessSpecialOperator(EvalOperators::Braces, opStack, precedenceStack, data.RpnQueue)){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
operatorExpected = true;
|
operatorOrEndTokenExpected = true;
|
||||||
} else {
|
} else {
|
||||||
if(token[0] < '0' || token[0] > '9') {
|
if(token[0] < '0' || token[0] > '9') {
|
||||||
return false;
|
return false;
|
||||||
|
@ -605,5 +605,8 @@ void ExpressionEvaluator::RunTests()
|
||||||
test(":($1FFF+1)", EvalResultType::Numeric, -1);
|
test(":($1FFF+1)", EvalResultType::Numeric, -1);
|
||||||
test(":$1FFF+1", EvalResultType::Numeric, 0x800);
|
test(":$1FFF+1", EvalResultType::Numeric, 0x800);
|
||||||
test("1+:$100", EvalResultType::Numeric, 0x101);
|
test("1+:$100", EvalResultType::Numeric, 0x101);
|
||||||
|
|
||||||
|
test("[$4100+[$4100]]", EvalResultType::Numeric, 0x41);
|
||||||
|
test("-($10+[$4100])", EvalResultType::Numeric, -0x51);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
|
@ -22,6 +22,15 @@ namespace Mesen.GUI.Debugger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<Breakpoint> Asserts { internal get; set; } = new List<Breakpoint>();
|
||||||
|
|
||||||
|
public static List<Breakpoint> GetAllBreakpoints()
|
||||||
|
{
|
||||||
|
List<Breakpoint> breakpoints = new List<Breakpoint>(BreakpointManager.Breakpoints);
|
||||||
|
breakpoints.AddRange(BreakpointManager.Asserts);
|
||||||
|
return breakpoints;
|
||||||
|
}
|
||||||
|
|
||||||
public static void RefreshBreakpoints(Breakpoint bp = null)
|
public static void RefreshBreakpoints(Breakpoint bp = null)
|
||||||
{
|
{
|
||||||
if(BreakpointsChanged != null) {
|
if(BreakpointsChanged != null) {
|
||||||
|
@ -124,8 +133,15 @@ namespace Mesen.GUI.Debugger
|
||||||
public static void SetBreakpoints()
|
public static void SetBreakpoints()
|
||||||
{
|
{
|
||||||
List<InteropBreakpoint> breakpoints = new List<InteropBreakpoint>();
|
List<InteropBreakpoint> breakpoints = new List<InteropBreakpoint>();
|
||||||
for(int i = 0; i < Breakpoints.Count; i++) {
|
|
||||||
breakpoints.Add(Breakpoints[i].ToInteropBreakpoint(i));
|
ReadOnlyCollection<Breakpoint> userBreakpoints = BreakpointManager.Breakpoints;
|
||||||
|
for(int i = 0; i < userBreakpoints.Count; i++) {
|
||||||
|
breakpoints.Add(userBreakpoints[i].ToInteropBreakpoint(breakpoints.Count));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Breakpoint> assertBreakpoints = BreakpointManager.Asserts;
|
||||||
|
for(int i = 0; i < assertBreakpoints.Count; i++) {
|
||||||
|
breakpoints.Add(assertBreakpoints[i].ToInteropBreakpoint(breakpoints.Count));
|
||||||
}
|
}
|
||||||
InteropEmu.DebugSetBreakpoints(breakpoints.ToArray(), (UInt32)breakpoints.Count);
|
InteropEmu.DebugSetBreakpoints(breakpoints.ToArray(), (UInt32)breakpoints.Count);
|
||||||
}
|
}
|
||||||
|
|
|
@ -410,7 +410,7 @@ namespace Mesen.GUI.Debugger
|
||||||
_scopes.Add(scope.ID, scope);
|
_scopes.Add(scope.ID, scope);
|
||||||
return true;
|
return true;
|
||||||
} else if(row.StartsWith("scope")) {
|
} else if(row.StartsWith("scope")) {
|
||||||
System.Diagnostics.Debug.Fail("Regex doesn't match scope");
|
//System.Diagnostics.Debug.Fail("Regex doesn't match scope");
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -559,8 +559,12 @@ namespace Mesen.GUI.Debugger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string[] _splitOnNewLine = { Environment.NewLine };
|
||||||
|
|
||||||
private void LoadComments()
|
private void LoadComments()
|
||||||
{
|
{
|
||||||
|
SortedDictionary<string, int> constants = GetConstants();
|
||||||
|
|
||||||
foreach(KeyValuePair<int, LineInfo> kvp in _lines) {
|
foreach(KeyValuePair<int, LineInfo> kvp in _lines) {
|
||||||
try {
|
try {
|
||||||
LineInfo line = kvp.Value;
|
LineInfo line = kvp.Value;
|
||||||
|
@ -622,7 +626,18 @@ namespace Mesen.GUI.Debugger
|
||||||
if(address >= 0 && addressType != null) {
|
if(address >= 0 && addressType != null) {
|
||||||
CodeLabel label = this.CreateLabel(address, addressType.Value, 1);
|
CodeLabel label = this.CreateLabel(address, addressType.Value, 1);
|
||||||
if(label != null) {
|
if(label != null) {
|
||||||
label.Comment = comment;
|
//Parse and replace content of asserts as needed
|
||||||
|
string[] commentLines = comment.Split(_splitOnNewLine, StringSplitOptions.None);
|
||||||
|
for(int i = 0; i < commentLines.Length; i++) {
|
||||||
|
Match m = LabelManager.AssertRegex.Match(commentLines[i]);
|
||||||
|
if(m.Success) {
|
||||||
|
foreach(KeyValuePair<string, int> entry in constants) {
|
||||||
|
commentLines[i] = commentLines[i].Replace(entry.Key, entry.Value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label.Comment = string.Join(Environment.NewLine, commentLines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -632,6 +647,26 @@ namespace Mesen.GUI.Debugger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SortedDictionary<string, int> GetConstants()
|
||||||
|
{
|
||||||
|
SortedDictionary<string, int> constants = new SortedDictionary<string, int>(Comparer<string>.Create((string a, string b) => {
|
||||||
|
if(a.Length == b.Length) {
|
||||||
|
return a.CompareTo(b);
|
||||||
|
}
|
||||||
|
return b.Length - a.Length;
|
||||||
|
}));
|
||||||
|
|
||||||
|
foreach(Ld65DbgImporter.SymbolInfo symbol in GetSymbols()) {
|
||||||
|
AddressTypeInfo addressInfo = GetSymbolAddressInfo(symbol);
|
||||||
|
bool isConstant = addressInfo == null;
|
||||||
|
if(isConstant && symbol.Address.HasValue) {
|
||||||
|
constants[symbol.Name] = symbol.Address.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return constants;
|
||||||
|
}
|
||||||
|
|
||||||
private void LoadFileData(string path)
|
private void LoadFileData(string path)
|
||||||
{
|
{
|
||||||
Dictionary<int, int> maxLineCountByFile = new Dictionary<int, int>();
|
Dictionary<int, int> maxLineCountByFile = new Dictionary<int, int>();
|
||||||
|
|
|
@ -55,6 +55,7 @@ namespace Mesen.GUI.Debugger
|
||||||
public class LabelManager
|
public class LabelManager
|
||||||
{
|
{
|
||||||
public static Regex LabelRegex { get; } = new Regex("^[@_a-zA-Z]+[@_a-zA-Z0-9]*$", RegexOptions.Compiled);
|
public static Regex LabelRegex { get; } = new Regex("^[@_a-zA-Z]+[@_a-zA-Z0-9]*$", RegexOptions.Compiled);
|
||||||
|
public static Regex AssertRegex { get; } = new Regex(@"assert\((.*)\)", RegexOptions.Compiled);
|
||||||
|
|
||||||
private static Dictionary<UInt32, CodeLabel> _labelsByKey = new Dictionary<UInt32, CodeLabel>();
|
private static Dictionary<UInt32, CodeLabel> _labelsByKey = new Dictionary<UInt32, CodeLabel>();
|
||||||
private static HashSet<CodeLabel> _labels = new HashSet<CodeLabel>();
|
private static HashSet<CodeLabel> _labels = new HashSet<CodeLabel>();
|
||||||
|
@ -98,10 +99,37 @@ namespace Mesen.GUI.Debugger
|
||||||
SetLabel(label.Address, label.AddressType, label.Label, label.Comment, false, label.Flags, label.Length);
|
SetLabel(label.Address, label.AddressType, label.Label, label.Comment, false, label.Flags, label.Length);
|
||||||
}
|
}
|
||||||
if(raiseEvents) {
|
if(raiseEvents) {
|
||||||
OnLabelUpdated?.Invoke(null, null);
|
ProcessLabelUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ProcessLabelUpdate()
|
||||||
|
{
|
||||||
|
OnLabelUpdated?.Invoke(null, null);
|
||||||
|
UpdateAssertBreakpoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateAssertBreakpoints()
|
||||||
|
{
|
||||||
|
List<Breakpoint> asserts = new List<Breakpoint>();
|
||||||
|
foreach(CodeLabel label in LabelManager.GetLabels()) {
|
||||||
|
foreach(string commentLine in label.Comment.Split('\n')) {
|
||||||
|
Match m = LabelManager.AssertRegex.Match(commentLine);
|
||||||
|
if(m.Success) {
|
||||||
|
asserts.Add(new Breakpoint() {
|
||||||
|
BreakOnExec = true,
|
||||||
|
MemoryType = label.AddressType.ToMemoryType(),
|
||||||
|
Address = label.Address,
|
||||||
|
Condition = "!(" + m.Groups[1].Value + ")"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BreakpointManager.Asserts = asserts;
|
||||||
|
BreakpointManager.SetBreakpoints();
|
||||||
|
}
|
||||||
|
|
||||||
public static List<CodeLabel> GetLabels()
|
public static List<CodeLabel> GetLabels()
|
||||||
{
|
{
|
||||||
return _labels.ToList<CodeLabel>();
|
return _labels.ToList<CodeLabel>();
|
||||||
|
@ -154,7 +182,7 @@ namespace Mesen.GUI.Debugger
|
||||||
}
|
}
|
||||||
|
|
||||||
if(raiseEvent) {
|
if(raiseEvent) {
|
||||||
OnLabelUpdated?.Invoke(null, null);
|
ProcessLabelUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -180,7 +208,7 @@ namespace Mesen.GUI.Debugger
|
||||||
}
|
}
|
||||||
|
|
||||||
if(needEvent) {
|
if(needEvent) {
|
||||||
OnLabelUpdated?.Invoke(null, null);
|
ProcessLabelUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -451,9 +451,11 @@ namespace Mesen.GUI.Debugger
|
||||||
BreakpointType bpType = (BreakpointType)(byte)((param >> 8) & 0x0F);
|
BreakpointType bpType = (BreakpointType)(byte)((param >> 8) & 0x0F);
|
||||||
UInt16 bpAddress = (UInt16)(param >> 16);
|
UInt16 bpAddress = (UInt16)(param >> 16);
|
||||||
|
|
||||||
ReadOnlyCollection<Breakpoint> breakpoints = BreakpointManager.Breakpoints;
|
int regularBpCount = BreakpointManager.Breakpoints.Count;
|
||||||
|
List<Breakpoint> breakpoints = BreakpointManager.GetAllBreakpoints();
|
||||||
if(breakpointId >= 0 && breakpointId < breakpoints.Count) {
|
if(breakpointId >= 0 && breakpointId < breakpoints.Count) {
|
||||||
Breakpoint bp = breakpoints[breakpointId];
|
Breakpoint bp = breakpoints[breakpointId];
|
||||||
|
if(breakpointId < regularBpCount) {
|
||||||
if(bpType != BreakpointType.Global) {
|
if(bpType != BreakpointType.Global) {
|
||||||
message += ": " + ResourceHelper.GetEnumText(bpType) + " ($" + bpAddress.ToString("X4") + ":$" + bpValue.ToString("X2") + ")";
|
message += ": " + ResourceHelper.GetEnumText(bpType) + " ($" + bpAddress.ToString("X4") + ":$" + bpValue.ToString("X2") + ")";
|
||||||
}
|
}
|
||||||
|
@ -465,6 +467,9 @@ namespace Mesen.GUI.Debugger
|
||||||
message += Environment.NewLine + cond;
|
message += Environment.NewLine + cond;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
message = "Assert failed: " + bp.Condition.Substring(2, bp.Condition.Length - 3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if(source == BreakSource.BreakOnUninitMemoryRead) {
|
} else if(source == BreakSource.BreakOnUninitMemoryRead) {
|
||||||
UInt16 address = (UInt16)(param >> 16);
|
UInt16 address = (UInt16)(param >> 16);
|
||||||
|
|
Loading…
Add table
Reference in a new issue