diff --git a/src/Spice86/Messages/StatusMessage.cs b/src/Spice86/Messages/StatusMessage.cs new file mode 100644 index 000000000..5d2dda993 --- /dev/null +++ b/src/Spice86/Messages/StatusMessage.cs @@ -0,0 +1,3 @@ +namespace Spice86.Messages; + +public record StatusMessage(DateTime Time, object Origin, string Message); \ No newline at end of file diff --git a/src/Spice86/Models/Debugging/CpuInstructionInfo.cs b/src/Spice86/Models/Debugging/CpuInstructionInfo.cs index 952afd521..b05b108df 100644 --- a/src/Spice86/Models/Debugging/CpuInstructionInfo.cs +++ b/src/Spice86/Models/Debugging/CpuInstructionInfo.cs @@ -4,9 +4,12 @@ namespace Spice86.Models.Debugging; using Iced.Intel; +using Spice86.Core.Emulator.VM.Breakpoint; + public partial class CpuInstructionInfo : ObservableObject { [ObservableProperty] private string? _stringRepresentation; [ObservableProperty] private bool _hasBreakpoint; + [ObservableProperty] private BreakPoint? _breakPoint; [ObservableProperty] private bool _isCsIp; [ObservableProperty] private uint _address; [ObservableProperty] private string? _segmentedAddress; diff --git a/src/Spice86/ViewModels/DebugWindowViewModel.cs b/src/Spice86/ViewModels/DebugWindowViewModel.cs index 9652366ff..3923ecee2 100644 --- a/src/Spice86/ViewModels/DebugWindowViewModel.cs +++ b/src/Spice86/ViewModels/DebugWindowViewModel.cs @@ -53,6 +53,9 @@ public partial class DebugWindowViewModel : ViewModelBase, [ObservableProperty] private CfgCpuViewModel _cfgCpuViewModel; + [ObservableProperty] + private StatusMessageViewModel _statusMessageViewModel; + private readonly IPauseHandler _pauseHandler; public DebugWindowViewModel(IInstructionExecutor cpu, State cpuState, IMemory memory, Midi externalMidiDevice, @@ -65,6 +68,7 @@ public DebugWindowViewModel(IInstructionExecutor cpu, State cpuState, IMemory me messenger.Register>(this); messenger.Register>(this); _messenger = messenger; + StatusMessageViewModel = new(_messenger); _pauseHandler = pauseHandler; IsPaused = pauseHandler.IsPaused; pauseHandler.Pausing += () => uiDispatcher.Post(() => IsPaused = true); diff --git a/src/Spice86/ViewModels/DisassemblyViewModel.cs b/src/Spice86/ViewModels/DisassemblyViewModel.cs index 5749e9b39..99fc79758 100644 --- a/src/Spice86/ViewModels/DisassemblyViewModel.cs +++ b/src/Spice86/ViewModels/DisassemblyViewModel.cs @@ -159,19 +159,30 @@ private List DecodeInstructions(State state, IMemory memory, return instructions; } - /// - /// Handles a breakpoint being hit. - /// - /// The object representing the breakpoint that was hit. - private void OnBreakPointReached(BreakPoint breakPoint) => - _pauseHandler.RequestPause($"breakpoint {breakPoint.BreakPointType} hit"); + private void OnBreakPointReached(BreakPoint breakPoint) { + string message = $"{breakPoint.BreakPointType} breakpoint was reached."; + _pauseHandler.RequestPause(message); + _messenger.Send(new StatusMessage(DateTime.Now, this, message)); + } + + [RelayCommand] + private void RemoveAddressBreakpointHere() { + if (SelectedInstruction?.BreakPoint is null) { + return; + } + _emulatorBreakpointsManager.ToggleBreakPoint(SelectedInstruction.BreakPoint, false); + SelectedInstruction.BreakPoint = null; + SelectedInstruction.HasBreakpoint = false; + } [RelayCommand] private void CreateAddressBreakpointHere() { if (SelectedInstruction is null) { return; } - AddressBreakPoint breakPoint = new(BreakPointType.EXECUTION, SelectedInstruction.Address, OnBreakPointReached, false); + AddressBreakPoint breakPoint = new(BreakPointType.EXECUTION, SelectedInstruction.Address, OnBreakPointReached, + isRemovedOnTrigger: false); + SelectedInstruction.BreakPoint = breakPoint; _emulatorBreakpointsManager.ToggleBreakPoint(breakPoint, true); SelectedInstruction.HasBreakpoint = true; } diff --git a/src/Spice86/ViewModels/StatusMessageViewModel.cs b/src/Spice86/ViewModels/StatusMessageViewModel.cs new file mode 100644 index 000000000..92584751f --- /dev/null +++ b/src/Spice86/ViewModels/StatusMessageViewModel.cs @@ -0,0 +1,25 @@ +namespace Spice86.ViewModels; + +using Avalonia.Collections; + +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; + +using Spice86.Messages; + +public partial class StatusMessageViewModel : ViewModelBase, IRecipient { + [ObservableProperty] + private AvaloniaList _previousMessages = new(); + + [ObservableProperty] + private StatusMessage? _message; + + public StatusMessageViewModel(IMessenger messenger) => messenger.Register(this); + + public void Receive(StatusMessage message) { + PreviousMessages.Add(message); + Message = message; + if (PreviousMessages.Count > 50) + PreviousMessages.RemoveAt(0); + } +} \ No newline at end of file diff --git a/src/Spice86/Views/DebugWindow.axaml b/src/Spice86/Views/DebugWindow.axaml index f2e962fb2..923145667 100644 --- a/src/Spice86/Views/DebugWindow.axaml +++ b/src/Spice86/Views/DebugWindow.axaml @@ -8,6 +8,7 @@ xmlns:local="clr-namespace:Spice86" xmlns:fluent="clr-namespace:FluentIcons.Avalonia.Fluent;assembly=FluentIcons.Avalonia.Fluent" xmlns:controls="clr-namespace:Spice86.Controls" + xmlns:views="clr-namespace:Spice86.Views" x:CompileBindings="True" x:DataType="vm:DebugWindowViewModel" WindowStartupLocation="CenterOwner" @@ -26,7 +27,7 @@ - +