diff --git a/Core/Debugger/Debugger.cpp b/Core/Debugger/Debugger.cpp index 735a7ec4..6fc06121 100644 --- a/Core/Debugger/Debugger.cpp +++ b/Core/Debugger/Debugger.cpp @@ -227,6 +227,7 @@ void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo *operati _executionStopped = true; + bool notificationSent = false; if(source != BreakSource::Unspecified || _breakRequestCount == 0) { _emu->GetSoundMixer()->StopAudio(); @@ -239,12 +240,17 @@ void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo *operati } _waitForBreakResume = true; _emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak, &evt); + notificationSent = true; } while((_waitForBreakResume && !_suspendRequestCount) || _breakRequestCount) { std::this_thread::sleep_for(std::chrono::duration(_breakRequestCount ? 1 : 10)); } + if(notificationSent) { + _emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::DebuggerResumed); + } + _executionStopped = false; } diff --git a/Core/Shared/Interfaces/INotificationListener.h b/Core/Shared/Interfaces/INotificationListener.h index 7d28ba37..541b50ac 100644 --- a/Core/Shared/Interfaces/INotificationListener.h +++ b/Core/Shared/Interfaces/INotificationListener.h @@ -11,6 +11,7 @@ enum class ConsoleNotificationType GamePaused, GameResumed, CodeBreak, + DebuggerResumed, PpuFrameDone, ResolutionChanged, ConfigChanged, diff --git a/NewUI/Debugger/Controls/DisassemblyViewer.cs b/NewUI/Debugger/Controls/DisassemblyViewer.cs index ef9b98ec..daf2af2c 100644 --- a/NewUI/Debugger/Controls/DisassemblyViewer.cs +++ b/NewUI/Debugger/Controls/DisassemblyViewer.cs @@ -53,18 +53,18 @@ namespace Mesen.Debugger.Controls set { SetValue(ShowByteCodeProperty, value); } } + public delegate void RowClickedEventHandler(DisassemblyViewer sender, RowClickedEventArgs args); + public event RowClickedEventHandler? RowClicked; + + public delegate void CodePointerMovedEventHandler(DisassemblyViewer sender, CodePointerMovedEventArgs args); + public event CodePointerMovedEventHandler? CodePointerMoved; + private Typeface Font { get; set; } private Size LetterSize { get; set; } private double RowHeight => this.LetterSize.Height; - - public int GetVisibleRowCount() - { - InitFontAndLetterSize(); - return (int)Math.Ceiling(Bounds.Height / RowHeight); - } - - public delegate void RowClickedEventHandler(DisassemblyViewer sender, RowClickedEventArgs args); - public event RowClickedEventHandler? RowClicked; + private List _visibleCodeSegments = new(); + private Point _previousPointerPos; + private CodeSegmentInfo? _prevPointerOverSegment = null; static DisassemblyViewer() { @@ -89,6 +89,47 @@ namespace Mesen.Debugger.Controls InvalidateVisual(); } + protected override void OnPointerMoved(PointerEventArgs e) + { + base.OnPointerMoved(e); + + Point p = e.GetCurrentPoint(this).Position; + + if(_previousPointerPos == p) { + //Pointer didn't move, don't trigger the pointer event + return; + } + _previousPointerPos = p; + + foreach(var codeSegment in _visibleCodeSegments) { + if(codeSegment.Bounds.Contains(p)) { + //Don't trigger an event if this is the same segment + if(_prevPointerOverSegment != codeSegment) { + CodePointerMoved?.Invoke(this, new CodePointerMovedEventArgs(codeSegment)); + _prevPointerOverSegment = codeSegment; + } + return; + } + } + + _prevPointerOverSegment = null; + CodePointerMoved?.Invoke(this, new CodePointerMovedEventArgs(null)); + } + + protected override void OnPointerLeave(PointerEventArgs e) + { + base.OnPointerLeave(e); + _previousPointerPos = new Point(0, 0); + _prevPointerOverSegment = null; + CodePointerMoved?.Invoke(this, new CodePointerMovedEventArgs(null)); + } + + public int GetVisibleRowCount() + { + InitFontAndLetterSize(); + return (int)Math.Ceiling(Bounds.Height / RowHeight); + } + private void InitFontAndLetterSize() { this.Font = new Typeface(new FontFamily(this.FontFamily)); @@ -132,6 +173,8 @@ namespace Mesen.Debugger.Controls context.DrawLine(ColorHelper.GetPen(Colors.LightGray), new Point(addressMargin + byteCodeMargin, 0), new Point(addressMargin + byteCodeMargin, Bounds.Height)); } + _visibleCodeSegments.Clear(); + //Draw code foreach(CodeLineData line in lines) { LineProperties lineStyle = styleProvider.GetLineStyle(line, 0); @@ -184,8 +227,10 @@ namespace Mesen.Debugger.Controls } foreach(CodeColor part in lineParts) { + Point pos = new Point(x + codeIndent, y); text.Text = part.Text; - context.DrawText(ColorHelper.GetBrush(part.Color), new Point(x + codeIndent, y), text); + context.DrawText(ColorHelper.GetBrush(part.Color), pos, text); + _visibleCodeSegments.Add(new CodeSegmentInfo(part.Text, part.Type, text.Bounds.Translate(new Vector(pos.X, pos.Y)))); x += text.Bounds.Width; } } @@ -229,6 +274,30 @@ namespace Mesen.Debugger.Controls } } + public class CodePointerMovedEventArgs : EventArgs + { + public CodePointerMovedEventArgs(CodeSegmentInfo? codeSegment) + { + this.CodeSegment = codeSegment; + } + + public CodeSegmentInfo? CodeSegment { get; } + } + + public class CodeSegmentInfo + { + public CodeSegmentInfo(string text, CodeSegmentType type, Rect bounds) + { + this.Text = text; + this.Type = type; + this.Bounds = bounds; + } + + public string Text { get; } + public CodeSegmentType Type { get; } + public Rect Bounds { get; } + } + public class RowClickedEventArgs { public CodeLineData CodeLineData { get; private set; } @@ -296,9 +365,29 @@ namespace Mesen.Debugger.Controls Plus = 16 } + public enum CodeSegmentType + { + OpCode, + ImmediateValue, + Address, + Label, + EffectiveAddress, + MemoryValue, + None, + Syntax, + } + public class CodeColor { - public string Text = ""; + public string Text; public Color Color; + public CodeSegmentType Type; + + public CodeColor(string text, Color color, CodeSegmentType type) + { + Text = text; + Color = color; + Type = type; + } } } diff --git a/NewUI/Debugger/Controls/DynamicTooltip.axaml b/NewUI/Debugger/Controls/DynamicTooltip.axaml index 4b74e321..75e59374 100644 --- a/NewUI/Debugger/Controls/DynamicTooltip.axaml +++ b/NewUI/Debugger/Controls/DynamicTooltip.axaml @@ -21,11 +21,22 @@ - + - + diff --git a/NewUI/Debugger/Controls/DynamicTooltip.axaml.cs b/NewUI/Debugger/Controls/DynamicTooltip.axaml.cs index 9ddf79d9..9660f347 100644 --- a/NewUI/Debugger/Controls/DynamicTooltip.axaml.cs +++ b/NewUI/Debugger/Controls/DynamicTooltip.axaml.cs @@ -1,22 +1,20 @@ using Avalonia; using Avalonia.Controls; -using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using Avalonia.Media; -using Mesen.Config; using Mesen.Utilities; -using Mesen.ViewModels; -using Mesen.Windows; using ReactiveUI; using ReactiveUI.Fody.Helpers; using System; using System.Collections.Generic; +using System.Collections.Specialized; namespace Mesen.Debugger.Controls { public class DynamicTooltip : UserControl { public static readonly StyledProperty ItemsProperty = AvaloniaProperty.Register(nameof(Items)); + public static readonly StyledProperty FirstColumnWidthProperty = AvaloniaProperty.Register(nameof(FirstColumnWidth)); public TooltipEntries Items { @@ -24,6 +22,19 @@ namespace Mesen.Debugger.Controls set { SetValue(ItemsProperty, value); } } + public int FirstColumnWidth + { + get { return GetValue(FirstColumnWidthProperty); } + set { SetValue(FirstColumnWidthProperty, value); } + } + + static DynamicTooltip() + { + ItemsProperty.Changed.AddClassHandler((x, e) => { + x.ComputeColumnWidth(); + }); + } + public DynamicTooltip() { InitializeComponent(); @@ -33,24 +44,46 @@ namespace Mesen.Debugger.Controls { AvaloniaXamlLoader.Load(this); } + + private void ComputeColumnWidth() + { + //TODO, remove hardcoded font name+size+weight + int maxWidth = 0; + var text = new FormattedText("", new Typeface("Microsoft Sans Serif", FontStyle.Normal, FontWeight.Bold), 11, TextAlignment.Left, TextWrapping.NoWrap, Size.Empty); + foreach(var item in Items) { + text.Text = item.Name; + maxWidth = Math.Max(maxWidth, (int)text.Bounds.Width); + } + FirstColumnWidth = maxWidth; + } } public class TooltipEntry : ReactiveObject { [Reactive] public string Name { get; set; } = ""; [Reactive] public object Value { get; set; } = ""; + [Reactive] public FontFamily? Font { get; set; } = null; - public TooltipEntry(string name, object value) + public TooltipEntry(string name, object value, FontFamily? font = null) { Name = name; Value = value; + Font = font; } } - public class TooltipEntries : List + public class TooltipEntries : List, INotifyCollectionChanged { private Dictionary _entries = new(); + public event NotifyCollectionChangedEventHandler? CollectionChanged; + + [Obsolete("Do not use")] + public new void Add(TooltipEntry entry) + { + throw new NotImplementedException(); + } + public void AddPicture(string name, IImage source, double zoom, PixelRect? cropRect = null) { if(_entries.TryGetValue(name, out TooltipEntry? entry)) { @@ -71,19 +104,23 @@ namespace Mesen.Debugger.Controls } else { entry = new TooltipEntry(name, new TooltipPictureEntry(source, zoom, cropRect)); _entries[entry.Name] = entry; - Add(entry); + base.Add(entry); } + + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } - public void AddEntry(string name, object value) + public void AddEntry(string name, object value, FontFamily? font = null) { if(_entries.TryGetValue(name, out TooltipEntry? entry)) { entry.Value = value; } else { - entry = new TooltipEntry(name, value); + entry = new TooltipEntry(name, value, font); _entries[entry.Name] = entry; - Add(entry); + base.Add(entry); } + + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } } diff --git a/NewUI/Debugger/Disassembly/CodeHighlighting.cs b/NewUI/Debugger/Disassembly/CodeHighlighting.cs index 8720bcc3..b5bc4dc1 100644 --- a/NewUI/Debugger/Disassembly/CodeHighlighting.cs +++ b/NewUI/Debugger/Disassembly/CodeHighlighting.cs @@ -30,15 +30,32 @@ namespace Mesen.Debugger.Disassembly Match m; if(foundOpCode && (m = _operand.Match(codeString)).Success) { string operand = m.Value; - Color operandColor = operand.Length > 0 ? (operand[0] == '#' ? (Color)cfg.CodeImmediateColor : (operand[0] == '$' ? (Color)cfg.CodeAddressColor : (Color)cfg.CodeLabelDefinitionColor)) : Colors.Black; - colors.Add(new CodeColor() { Text = m.Value, Color = textColor.HasValue ? textColor.Value : operandColor }); + Color operandColor = Colors.Black; + CodeSegmentType type = CodeSegmentType.None; + if(operand.Length > 0) { + switch(operand[0]) { + case '#': + operandColor = cfg.CodeImmediateColor; + type = CodeSegmentType.ImmediateValue; + break; + case '$': + operandColor = cfg.CodeAddressColor; + type = CodeSegmentType.Address; + break; + default: + operandColor = cfg.CodeLabelDefinitionColor; + type = CodeSegmentType.Label; + break; + } + } + colors.Add(new CodeColor(m.Value, textColor ?? operandColor, type)); } else if(!foundOpCode && (m = _opCode.Match(codeString)).Success) { foundOpCode = true; - colors.Add(new CodeColor() { Text = m.Value, Color = textColor.HasValue ? textColor.Value : (Color)cfg.CodeOpcodeColor }); + colors.Add(new CodeColor(m.Value, textColor ?? cfg.CodeOpcodeColor, CodeSegmentType.OpCode)); } else if((m = _syntax.Match(codeString)).Success) { - colors.Add(new CodeColor() { Text = m.Value, Color = textColor.HasValue ? textColor.Value : defaultColor }); + colors.Add(new CodeColor(m.Value, textColor ?? defaultColor, CodeSegmentType.Syntax)); } else if((m = _space.Match(codeString)).Success) { - colors.Add(new CodeColor() { Text = m.Value, Color = textColor.HasValue ? textColor.Value : defaultColor }); + colors.Add(new CodeColor(m.Value, textColor ?? defaultColor, CodeSegmentType.None)); } if(m.Success) { @@ -49,20 +66,24 @@ namespace Mesen.Debugger.Disassembly } //Display the rest of the line (used by trace logger) - colors.Add(new CodeColor() { Text = codeString, Color = defaultColor }); + colors.Add(new CodeColor(codeString, defaultColor, CodeSegmentType.None)); if(lineData.EffectiveAddress >= 0) { - colors.Add(new CodeColor() { Text = " " + lineData.GetEffectiveAddressString(addressFormat), Color = cfg.CodeEffectiveAddressColor }); + string effAddress = lineData.GetEffectiveAddressString(addressFormat, out CodeSegmentType type); + colors.Add(new CodeColor(" " + effAddress, cfg.CodeEffectiveAddressColor, type)); } if(showMemoryValues && lineData.ValueSize > 0) { - colors.Add(new CodeColor() { Text = lineData.GetValueString(), Color = defaultColor }); + colors.Add(new CodeColor(lineData.GetValueString(), defaultColor, CodeSegmentType.MemoryValue)); } return colors; } else { - Color color = codeString.EndsWith(":") ? (Color)cfg.CodeLabelDefinitionColor : (textColor ?? defaultColor); - return new List() { new CodeColor() { Text = codeString, Color = color } }; + if(codeString.EndsWith(":")) { + return new List() { new CodeColor(codeString, cfg.CodeLabelDefinitionColor, CodeSegmentType.Label) }; + } else { + return new List() { new CodeColor(codeString, textColor ?? defaultColor, CodeSegmentType.None) }; + } } } } diff --git a/NewUI/Debugger/Disassembly/CodeLineData.cs b/NewUI/Debugger/Disassembly/CodeLineData.cs index a692ad95..428f06ad 100644 --- a/NewUI/Debugger/Disassembly/CodeLineData.cs +++ b/NewUI/Debugger/Disassembly/CodeLineData.cs @@ -1,4 +1,5 @@ -using Mesen.Debugger.Labels; +using Mesen.Debugger.Controls; +using Mesen.Debugger.Labels; using Mesen.Interop; using System; using System.Collections.Generic; @@ -30,12 +31,13 @@ namespace Mesen.Debugger public UInt16 Value = 0; public byte ValueSize = 0; - public string GetEffectiveAddressString(string format) + public string GetEffectiveAddressString(string format, out CodeSegmentType segmentType) { if(EffectiveAddress >= 0) { AddressInfo relAddress = new AddressInfo() { Address = EffectiveAddress, Type = CpuType.ToMemoryType() }; CodeLabel? label = LabelManager.GetLabel(relAddress); if(label != null) { + segmentType = CodeSegmentType.Label; if(label.Length > 1) { int gap = DebugApi.GetAbsoluteAddress(relAddress).Address - label.GetAbsoluteAddress().Address; if(gap > 0) { @@ -44,9 +46,11 @@ namespace Mesen.Debugger } return "[" + label.Label + "]"; } else { + segmentType = CodeSegmentType.EffectiveAddress; return "[$" + EffectiveAddress.ToString(format) + "]"; } } else { + segmentType = CodeSegmentType.None; return ""; } } diff --git a/NewUI/Debugger/HexEditorDataProvider.cs b/NewUI/Debugger/HexEditorDataProvider.cs index 4ca67838..5658aa8a 100644 --- a/NewUI/Debugger/HexEditorDataProvider.cs +++ b/NewUI/Debugger/HexEditorDataProvider.cs @@ -17,7 +17,7 @@ namespace Mesen.Debugger private SnesMemoryType _memoryType; private HexEditorConfig _cfg; private AddressCounters[] _counters = Array.Empty(); - private byte[]? _cdlData; + private CdlFlags[]? _cdlData; private bool[] _hasLabel = Array.Empty(); private TimingInfo _timing; private ByteInfo _byteInfo = new ByteInfo(); @@ -145,10 +145,10 @@ namespace Mesen.Debugger _byteInfo.BackColor = Colors.Transparent; if(_cdlData != null) { - if((_cdlData[index] & (byte)CdlFlags.Code) != 0 && _cfg.CodeHighlight.Highlight) { + if(_cdlData[index].HasFlag(CdlFlags.Code) && _cfg.CodeHighlight.Highlight) { //Code _byteInfo.BackColor = _cfg.CodeHighlight.Color; - } else if((_cdlData[index] & (byte)CdlFlags.Data) != 0 && _cfg.DataHighlight.Highlight) { + } else if(_cdlData[index].HasFlag(CdlFlags.Data) && _cfg.DataHighlight.Highlight) { //Data _byteInfo.BackColor = _cfg.DataHighlight.Color; } diff --git a/NewUI/Debugger/Labels/CodeLabel.cs b/NewUI/Debugger/Labels/CodeLabel.cs index e482f648..a6509153 100644 --- a/NewUI/Debugger/Labels/CodeLabel.cs +++ b/NewUI/Debugger/Labels/CodeLabel.cs @@ -142,6 +142,9 @@ namespace Mesen.Debugger.Labels public AddressInfo GetRelativeAddress(CpuType cpuType) { + if(MemoryType.IsRelativeMemory()) { + return GetAbsoluteAddress(); + } return DebugApi.GetRelativeAddress(GetAbsoluteAddress(), cpuType); } diff --git a/NewUI/Debugger/Utilities/CodeTooltipHelper.cs b/NewUI/Debugger/Utilities/CodeTooltipHelper.cs new file mode 100644 index 00000000..4137bfd1 --- /dev/null +++ b/NewUI/Debugger/Utilities/CodeTooltipHelper.cs @@ -0,0 +1,89 @@ +using System; +using Avalonia.Controls; +using Mesen.Interop; +using Mesen.Debugger.Controls; +using Mesen.Debugger.Disassembly; +using Avalonia.Media; +using Mesen.Debugger.Labels; +using Mesen.Localization; +using Mesen.Config; + +namespace Mesen.Debugger.Utilities +{ + public static class CodeTooltipHelper + { + public static DynamicTooltip? GetTooltip(CpuType cpuType, string text, CodeSegmentType segmentType) + { + FontFamily monoFont = ConfigManager.Config.Debug.Font.FontFamilyObject; + + int address = -1; + CodeLabel? label = null; + if(segmentType == CodeSegmentType.Address || segmentType == CodeSegmentType.EffectiveAddress) { + string addressText = text.Trim(' ', '[', ']', '$'); + int.TryParse(addressText, System.Globalization.NumberStyles.HexNumber, null, out address); + } else if(segmentType == CodeSegmentType.Label) { + string labelText = text.Trim(' ', ',', ':', ']', '['); + label = LabelManager.GetLabel(labelText); + if(label != null) { + address = label.GetRelativeAddress(cpuType).Address; + } + } + + if(address >= 0) { + SnesMemoryType memType = cpuType.ToMemoryType(); + int byteValue = DebugApi.GetMemoryValue(memType, (uint)address); + int wordValue = (DebugApi.GetMemoryValue(memType, (uint)address + 1) << 8) | byteValue; + + StackPanel mainPanel = new StackPanel() { Spacing = -4 }; + mainPanel.Children.Add(GetHexDecPanel(byteValue, "X2", monoFont)); + mainPanel.Children.Add(GetHexDecPanel(wordValue, "X4", monoFont)); + + TooltipEntries items = new(); + string addressField = "$" + address.ToString("X" + cpuType.GetAddressSize()) + " (CPU)"; + AddressInfo absAddr = DebugApi.GetAbsoluteAddress(new AddressInfo() { Address = address, Type = memType }); + if(absAddr.Address >= 0) { + addressField += Environment.NewLine + "$" + absAddr.Address.ToString("X" + cpuType.GetAddressSize()) + " (" + ResourceHelper.GetEnumText(absAddr.Type) + ")"; + } + + if(label != null) { + items.AddEntry("Label", label.Label, monoFont); + } + + items.AddEntry("Address", addressField, monoFont); + items.AddEntry("Value", mainPanel); + + if(label?.Comment.Length > 0) { + items.AddEntry("Comment", label.Comment, monoFont); + } + + bool showPreview = DebugApi.GetCdlData((uint)address, 1, memType)[0].HasFlag(CdlFlags.Code); + if(showPreview) { + items.AddEntry("", new Border() { + BorderBrush = Brushes.Gray, + BorderThickness = new(1), + Child = new DisassemblyViewer() { + Width = 300, + Height = 150, + Lines = new CodeDataProvider(cpuType).GetCodeLines(address, 40), + StyleProvider = new BaseStyleProvider(), + FontFamily = ConfigManager.Config.Debug.Font.FontFamily, + FontSize = ConfigManager.Config.Debug.Font.FontSize - 1 + } + }); + } + + return new DynamicTooltip() { Items = items }; + } + + return null; + } + + private static StackPanel GetHexDecPanel(int value, string format, FontFamily font) + { + StackPanel panel = new StackPanel() { Orientation = Avalonia.Layout.Orientation.Horizontal }; + panel.Children.Add(new TextBlock() { Text = "$" + value.ToString(format), FontFamily = font, FontSize = 12 }); + panel.Children.Add(new TextBlock() { Text = " (" + value.ToString() + ")", FontFamily = font, FontSize = 12, Foreground = Brushes.DimGray, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center }); + return panel; + } + } +} diff --git a/NewUI/Debugger/ViewModels/DebuggerWindowViewModel.cs b/NewUI/Debugger/ViewModels/DebuggerWindowViewModel.cs index 08d91cdb..6af3dd1a 100644 --- a/NewUI/Debugger/ViewModels/DebuggerWindowViewModel.cs +++ b/NewUI/Debugger/ViewModels/DebuggerWindowViewModel.cs @@ -44,12 +44,6 @@ namespace Mesen.Debugger.ViewModels public DebuggerWindowViewModel(CpuType? cpuType = null) { - Config = ConfigManager.Config.Debug.Debugger; - - Options = new DebuggerOptionsViewModel(Config, CpuType); - Disassembly = new DisassemblyViewModel(ConfigManager.Config.Debug); - DockFactory = new DebuggerDockFactory(this); - if(Design.IsDesignMode) { CpuType = CpuType.Cpu; } else if(cpuType != null) { @@ -59,6 +53,18 @@ namespace Mesen.Debugger.ViewModels CpuType = romInfo.ConsoleType.GetMainCpuType(); } + Config = ConfigManager.Config.Debug.Debugger; + + DefaultLabelHelper.SetDefaultLabels(); + Options = new DebuggerOptionsViewModel(Config, CpuType); + Disassembly = new DisassemblyViewModel(ConfigManager.Config.Debug, CpuType); + BreakpointList = new BreakpointListViewModel(CpuType); + LabelList = new LabelListViewModel(CpuType); + CallStack = new CallStackViewModel(CpuType); + WatchList = new WatchListViewModel(CpuType); + + DockFactory = new DebuggerDockFactory(this); + switch(CpuType) { case CpuType.Cpu: DockFactory.CpuStatusTool.StatusViewModel = new SnesCpuViewModel(); @@ -74,12 +80,6 @@ namespace Mesen.Debugger.ViewModels break; } - DefaultLabelHelper.SetDefaultLabels(); - BreakpointList = new BreakpointListViewModel(CpuType); - LabelList = new LabelListViewModel(CpuType); - CallStack = new CallStackViewModel(CpuType); - WatchList = new WatchListViewModel(CpuType); - DockLayout = DockFactory.CreateLayout(); DockFactory.InitLayout(DockLayout); @@ -91,16 +91,22 @@ namespace Mesen.Debugger.ViewModels ConfigApi.SetDebuggerFlag(CpuType.GetDebuggerFlag(), true); } - internal void Cleanup() + public void Cleanup() { BreakpointManager.RemoveCpuType(CpuType); ConfigApi.SetDebuggerFlag(CpuType.GetDebuggerFlag(), false); } - internal void UpdateDisassembly() + public void UpdateDisassembly() { - Disassembly.DataProvider = new CodeDataProvider(CpuType); Disassembly.SetActiveAddress(DebugUtilities.GetProgramCounter(CpuType)); + Disassembly.Refresh(); + } + + public void ClearActiveAddress() + { + Disassembly.StyleProvider.ActiveAddress = null; + Disassembly.Refresh(); } public void UpdateCpuPpuState() @@ -171,14 +177,6 @@ namespace Mesen.Debugger.ViewModels } } - private void ClearActiveAddress() - { - if(Disassembly.StyleProvider is BaseStyleProvider p) { - p.ActiveAddress = null; - Disassembly.DataProvider = new CodeDataProvider(CpuType); - } - } - public void InitializeMenu(Window wnd) { DebuggerConfig cfg = ConfigManager.Config.Debug.Debugger; @@ -246,7 +244,6 @@ namespace Mesen.Debugger.ViewModels IsEnabled = isPaused, OnClick = () => { DebugApi.ResumeExecution(); - ClearActiveAddress(); } }, new ContextMenuAction() { @@ -331,7 +328,6 @@ namespace Mesen.Debugger.ViewModels private void Step(int instructionCount, StepType type) { DebugApi.Step(CpuType, instructionCount, type); - ClearActiveAddress(); } } } diff --git a/NewUI/Debugger/ViewModels/DisassemblyViewModel.cs b/NewUI/Debugger/ViewModels/DisassemblyViewModel.cs index 2e7dab40..18c743b2 100644 --- a/NewUI/Debugger/ViewModels/DisassemblyViewModel.cs +++ b/NewUI/Debugger/ViewModels/DisassemblyViewModel.cs @@ -1,6 +1,7 @@ using Mesen.Config; using Mesen.Debugger.Controls; using Mesen.Debugger.Disassembly; +using Mesen.Interop; using Mesen.ViewModels; using ReactiveUI; using ReactiveUI.Fody.Helpers; @@ -24,12 +25,14 @@ namespace Mesen.Debugger.ViewModels private int _ignoreScrollUpdates = 0; [Obsolete("For designer only")] - public DisassemblyViewModel(): this(new DebugConfig()) { } + public DisassemblyViewModel(): this(new DebugConfig(), CpuType.Cpu) { } - public DisassemblyViewModel(DebugConfig config) + public DisassemblyViewModel(DebugConfig config, CpuType cpuType) { Config = config; + DataProvider = new CodeDataProvider(cpuType); + this.WhenAnyValue(x => x.DataProvider).Subscribe(x => Refresh()); this.WhenAnyValue(x => x.TopAddress).Subscribe(x => Refresh()); diff --git a/NewUI/Debugger/Views/DisassemblyView.axaml b/NewUI/Debugger/Views/DisassemblyView.axaml index fff8e17f..ec352731 100644 --- a/NewUI/Debugger/Views/DisassemblyView.axaml +++ b/NewUI/Debugger/Views/DisassemblyView.axaml @@ -37,6 +37,7 @@ ShowByteCode="{CompiledBinding Config.Debugger.ShowByteCode}" RowClicked="Diassembly_RowClicked" PointerWheelChanged="Disassembly_PointerWheelChanged" + CodePointerMoved="Disassembly_CodePointerMoved" /> diff --git a/NewUI/Debugger/Views/DisassemblyView.axaml.cs b/NewUI/Debugger/Views/DisassemblyView.axaml.cs index 39183504..41758093 100644 --- a/NewUI/Debugger/Views/DisassemblyView.axaml.cs +++ b/NewUI/Debugger/Views/DisassemblyView.axaml.cs @@ -3,6 +3,7 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Markup.Xaml; using Mesen.Debugger.Controls; +using Mesen.Debugger.Utilities; using Mesen.Debugger.ViewModels; using Mesen.Interop; @@ -45,6 +46,21 @@ namespace Mesen.Debugger.Views } } + public void Disassembly_CodePointerMoved(DisassemblyViewer sender, CodePointerMovedEventArgs e) + { + DynamicTooltip? tooltip; + ICodeDataProvider? dp = Model.DataProvider; + if(e.CodeSegment != null && dp != null && (tooltip = CodeTooltipHelper.GetTooltip(dp.CpuType, e.CodeSegment.Text, e.CodeSegment.Type)) != null) { + ToolTip.SetTip(this, tooltip); + ToolTip.SetHorizontalOffset(this, 14); + ToolTip.SetHorizontalOffset(this, 15); + ToolTip.SetIsOpen(this, true); + } else { + ToolTip.SetIsOpen(this, false); + ToolTip.SetTip(this, null); + } + } + public void Disassembly_PointerWheelChanged(object? sender, PointerWheelEventArgs e) { Model.Scroll((int)(-e.Delta.Y * 3)); diff --git a/NewUI/Debugger/Windows/DebuggerWindow.axaml.cs b/NewUI/Debugger/Windows/DebuggerWindow.axaml.cs index d2647afa..8dfef18d 100644 --- a/NewUI/Debugger/Windows/DebuggerWindow.axaml.cs +++ b/NewUI/Debugger/Windows/DebuggerWindow.axaml.cs @@ -26,7 +26,7 @@ namespace Mesen.Debugger.Windows { InitializeComponent(); #if DEBUG - this.AttachDevTools(); + this.AttachDevTools(); #endif } @@ -39,7 +39,6 @@ namespace Mesen.Debugger.Windows { if(this.DataContext is DebuggerWindowViewModel model) { _model = model; - _model.Disassembly.StyleProvider = new BaseStyleProvider(); _model.InitializeMenu(this); _model.Config.LoadWindowSettings(this); if(Design.IsDesignMode) { @@ -104,10 +103,18 @@ namespace Mesen.Debugger.Windows private void _listener_OnNotification(NotificationEventArgs e) { - if(e.NotificationType == ConsoleNotificationType.CodeBreak) { - Dispatcher.UIThread.Post(() => { - UpdateDebugger(); - }); + switch(e.NotificationType) { + case ConsoleNotificationType.CodeBreak: + Dispatcher.UIThread.Post(() => { + UpdateDebugger(); + }); + break; + + case ConsoleNotificationType.DebuggerResumed: + Dispatcher.UIThread.Post(() => { + _model.ClearActiveAddress(); + }); + break; } } diff --git a/NewUI/Interop/DebugApi.cs b/NewUI/Interop/DebugApi.cs index 87552ff0..ab4f76bb 100644 --- a/NewUI/Interop/DebugApi.cs +++ b/NewUI/Interop/DebugApi.cs @@ -295,10 +295,10 @@ namespace Mesen.Interop return counts; } - [DllImport(DllPath, EntryPoint = "GetCdlData")] private static extern void GetCdlDataWrapper(UInt32 offset, UInt32 length, SnesMemoryType memType, [In,Out] byte[] cdlData); - public static byte[] GetCdlData(UInt32 offset, UInt32 length, SnesMemoryType memType) + [DllImport(DllPath, EntryPoint = "GetCdlData")] private static extern void GetCdlDataWrapper(UInt32 offset, UInt32 length, SnesMemoryType memType, [In,Out] CdlFlags[] cdlData); + public static CdlFlags[] GetCdlData(UInt32 offset, UInt32 length, SnesMemoryType memType) { - byte[] cdlData = new byte[length]; + CdlFlags[] cdlData = new CdlFlags[length]; DebugApi.GetCdlDataWrapper(offset, length, memType, cdlData); return cdlData; } diff --git a/NewUI/Interop/NotificationListener.cs b/NewUI/Interop/NotificationListener.cs index 2af06a69..f8b8aa5c 100644 --- a/NewUI/Interop/NotificationListener.cs +++ b/NewUI/Interop/NotificationListener.cs @@ -61,6 +61,7 @@ namespace Mesen.Interop GamePaused, GameResumed, CodeBreak, + DebuggerResumed, PpuFrameDone, ResolutionChanged, ConfigChanged, diff --git a/NewUI/Styles/MesenStyles.Dark.xaml b/NewUI/Styles/MesenStyles.Dark.xaml index 1c1c7404..2c1ebb2d 100644 --- a/NewUI/Styles/MesenStyles.Dark.xaml +++ b/NewUI/Styles/MesenStyles.Dark.xaml @@ -15,7 +15,7 @@ - + @@ -76,7 +76,11 @@ #000 #000 Black - + + + #FFF + #444 + 150 150 diff --git a/NewUI/Styles/MesenStyles.xaml b/NewUI/Styles/MesenStyles.xaml index 3e155143..188e77ba 100644 --- a/NewUI/Styles/MesenStyles.xaml +++ b/NewUI/Styles/MesenStyles.xaml @@ -25,7 +25,7 @@ - + @@ -94,6 +94,9 @@ #CCE4F7 Black + + #ffffed + 150 150 @@ -109,6 +112,9 @@ 0 0 + 800 + 3,3,3,3 + 0 0