diff --git a/Core/ExpressionEvaluator.cpp b/Core/ExpressionEvaluator.cpp index c202dd7b..d730f8a0 100644 --- a/Core/ExpressionEvaluator.cpp +++ b/Core/ExpressionEvaluator.cpp @@ -292,7 +292,7 @@ bool ExpressionEvaluator::ToRpn(string expression, ExpressionData &data) if(!ProcessSpecialOperator(EvalOperators::Parenthesis, opStack, precedenceStack, data.RpnQueue)) { return false; } - operatorExpected = true; + operatorOrEndTokenExpected = true; } else if(token[0] == '[') { bracketCount++; opStack.push(EvalOperators::Bracket); @@ -302,7 +302,7 @@ bool ExpressionEvaluator::ToRpn(string expression, ExpressionData &data) if(!ProcessSpecialOperator(EvalOperators::Bracket, opStack, precedenceStack, data.RpnQueue)) { return false; } - operatorExpected = true; + operatorOrEndTokenExpected = true; } else if(token[0] == '{') { braceCount++; opStack.push(EvalOperators::Braces); @@ -312,7 +312,7 @@ bool ExpressionEvaluator::ToRpn(string expression, ExpressionData &data) if(!ProcessSpecialOperator(EvalOperators::Braces, opStack, precedenceStack, data.RpnQueue)){ return false; } - operatorExpected = true; + operatorOrEndTokenExpected = true; } else { if(token[0] < '0' || token[0] > '9') { return false; @@ -605,5 +605,8 @@ void ExpressionEvaluator::RunTests() test(":($1FFF+1)", EvalResultType::Numeric, -1); test(":$1FFF+1", EvalResultType::Numeric, 0x800); test("1+:$100", EvalResultType::Numeric, 0x101); + + test("[$4100+[$4100]]", EvalResultType::Numeric, 0x41); + test("-($10+[$4100])", EvalResultType::Numeric, -0x51); } #endif \ No newline at end of file diff --git a/GUI.NET/Debugger/BreakpointManager.cs b/GUI.NET/Debugger/BreakpointManager.cs index ca9b8d12..ad8aef66 100644 --- a/GUI.NET/Debugger/BreakpointManager.cs +++ b/GUI.NET/Debugger/BreakpointManager.cs @@ -22,6 +22,15 @@ namespace Mesen.GUI.Debugger } } + public static List Asserts { internal get; set; } = new List(); + + public static List GetAllBreakpoints() + { + List breakpoints = new List(BreakpointManager.Breakpoints); + breakpoints.AddRange(BreakpointManager.Asserts); + return breakpoints; + } + public static void RefreshBreakpoints(Breakpoint bp = null) { if(BreakpointsChanged != null) { @@ -124,8 +133,15 @@ namespace Mesen.GUI.Debugger public static void SetBreakpoints() { List breakpoints = new List(); - for(int i = 0; i < Breakpoints.Count; i++) { - breakpoints.Add(Breakpoints[i].ToInteropBreakpoint(i)); + + ReadOnlyCollection userBreakpoints = BreakpointManager.Breakpoints; + for(int i = 0; i < userBreakpoints.Count; i++) { + breakpoints.Add(userBreakpoints[i].ToInteropBreakpoint(breakpoints.Count)); + } + + List 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); } diff --git a/GUI.NET/Debugger/DbgImporter.cs b/GUI.NET/Debugger/DbgImporter.cs index 7474729a..52c7248e 100644 --- a/GUI.NET/Debugger/DbgImporter.cs +++ b/GUI.NET/Debugger/DbgImporter.cs @@ -410,7 +410,7 @@ namespace Mesen.GUI.Debugger _scopes.Add(scope.ID, scope); return true; } 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; @@ -559,8 +559,12 @@ namespace Mesen.GUI.Debugger } } + private string[] _splitOnNewLine = { Environment.NewLine }; + private void LoadComments() { + SortedDictionary constants = GetConstants(); + foreach(KeyValuePair kvp in _lines) { try { LineInfo line = kvp.Value; @@ -622,7 +626,18 @@ namespace Mesen.GUI.Debugger if(address >= 0 && addressType != null) { CodeLabel label = this.CreateLabel(address, addressType.Value, 1); 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 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 GetConstants() + { + SortedDictionary constants = new SortedDictionary(Comparer.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) { Dictionary maxLineCountByFile = new Dictionary(); diff --git a/GUI.NET/Debugger/LabelManager.cs b/GUI.NET/Debugger/LabelManager.cs index 831db51f..aaab7f0a 100644 --- a/GUI.NET/Debugger/LabelManager.cs +++ b/GUI.NET/Debugger/LabelManager.cs @@ -55,6 +55,7 @@ namespace Mesen.GUI.Debugger public class LabelManager { 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 _labelsByKey = new Dictionary(); private static HashSet _labels = new HashSet(); @@ -98,10 +99,37 @@ namespace Mesen.GUI.Debugger SetLabel(label.Address, label.AddressType, label.Label, label.Comment, false, label.Flags, label.Length); } if(raiseEvents) { - OnLabelUpdated?.Invoke(null, null); + ProcessLabelUpdate(); } } + private static void ProcessLabelUpdate() + { + OnLabelUpdated?.Invoke(null, null); + UpdateAssertBreakpoints(); + } + + private static void UpdateAssertBreakpoints() + { + List asserts = new List(); + 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 GetLabels() { return _labels.ToList(); @@ -154,7 +182,7 @@ namespace Mesen.GUI.Debugger } if(raiseEvent) { - OnLabelUpdated?.Invoke(null, null); + ProcessLabelUpdate(); } return true; @@ -180,7 +208,7 @@ namespace Mesen.GUI.Debugger } if(needEvent) { - OnLabelUpdated?.Invoke(null, null); + ProcessLabelUpdate(); } } diff --git a/GUI.NET/Debugger/frmDebugger.cs b/GUI.NET/Debugger/frmDebugger.cs index 3aec9950..a02d5980 100644 --- a/GUI.NET/Debugger/frmDebugger.cs +++ b/GUI.NET/Debugger/frmDebugger.cs @@ -451,19 +451,24 @@ namespace Mesen.GUI.Debugger BreakpointType bpType = (BreakpointType)(byte)((param >> 8) & 0x0F); UInt16 bpAddress = (UInt16)(param >> 16); - ReadOnlyCollection breakpoints = BreakpointManager.Breakpoints; + int regularBpCount = BreakpointManager.Breakpoints.Count; + List breakpoints = BreakpointManager.GetAllBreakpoints(); if(breakpointId >= 0 && breakpointId < breakpoints.Count) { Breakpoint bp = breakpoints[breakpointId]; - if(bpType != BreakpointType.Global) { - message += ": " + ResourceHelper.GetEnumText(bpType) + " ($" + bpAddress.ToString("X4") + ":$" + bpValue.ToString("X2") + ")"; - } - if(!string.IsNullOrWhiteSpace(bp.Condition)) { - string cond = bp.Condition.Trim(); - if(cond.Length > 27) { - message += Environment.NewLine + cond.Substring(0, 24) + "..."; - } else { - message += Environment.NewLine + cond; + if(breakpointId < regularBpCount) { + if(bpType != BreakpointType.Global) { + message += ": " + ResourceHelper.GetEnumText(bpType) + " ($" + bpAddress.ToString("X4") + ":$" + bpValue.ToString("X2") + ")"; } + if(!string.IsNullOrWhiteSpace(bp.Condition)) { + string cond = bp.Condition.Trim(); + if(cond.Length > 27) { + message += Environment.NewLine + cond.Substring(0, 24) + "..."; + } else { + message += Environment.NewLine + cond; + } + } + } else { + message = "Assert failed: " + bp.Condition.Substring(2, bp.Condition.Length - 3); } } } else if(source == BreakSource.BreakOnUninitMemoryRead) {