Skip to content

Commit

Permalink
feat(debugger): basic Breakpoints View
Browse files Browse the repository at this point in the history
Signed-off-by: Maximilien Noal <noal.maximilien@gmail.com>
  • Loading branch information
maximilien-noal committed Oct 27, 2024
1 parent 0a8d5d4 commit 69176d0
Show file tree
Hide file tree
Showing 10 changed files with 257 additions and 150 deletions.
9 changes: 0 additions & 9 deletions src/Spice86.Core/Emulator/VM/EmulatorBreakpointsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,4 @@ private void CheckBreakPoints() {
_cycleBreakPoints.TriggerMatchingBreakPoints(cycles);
}
}

/// <summary>
/// Determines whether there is an address breakpoint at the specified address.
/// </summary>
/// <param name="instructionAddress">The memory address to check.</param>
/// <returns>Whether there is an address breakpoint at the specified address.</returns>
public bool IsAddressBreakpointAt(long instructionAddress) {
return _executionBreakPoints.IsAddressBreakpointAt(instructionAddress);
}
}
1 change: 0 additions & 1 deletion src/Spice86/Models/Debugging/CpuInstructionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ namespace Spice86.Models.Debugging;
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;
Expand Down
37 changes: 0 additions & 37 deletions src/Spice86/ViewModels/BreakpintsViewModel.cs

This file was deleted.

43 changes: 39 additions & 4 deletions src/Spice86/ViewModels/BreakpointViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,56 @@
namespace Spice86.ViewModels;

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

using Spice86.Core.Emulator.VM;
using Spice86.Core.Emulator.VM.Breakpoint;
using Spice86.Models.Debugging;

using System;

public partial class BreakpointViewModel : ViewModelBase {
private readonly BreakPoint _breakPoint;
private readonly EmulatorBreakpointsManager _emulatorBreakpointsManager;

public BreakpointViewModel(EmulatorBreakpointsManager emulatorBreakpointsManager, BreakPoint breakPoint) {
public BreakpointViewModel(EmulatorBreakpointsManager emulatorBreakpointsManager, AddressBreakPoint breakPoint) {
_breakPoint = breakPoint;
_emulatorBreakpointsManager = emulatorBreakpointsManager;
IsEnabled = true;
Address = breakPoint.Address;
}

public string Type => _breakPoint.BreakPointType.ToString();

//Can't get out of sync since GDB can't be used at the same tiem as the internal debugger
[ObservableProperty]
private bool _isEnabled;

public bool IsRemovedOnTrigger => _breakPoint.IsRemovedOnTrigger;

public long Address { get; }

public void Toggle() {
if (IsEnabled) {
Disable();
} else {
Enable();
}
}

[RelayCommand]
private void Enable() => _emulatorBreakpointsManager.ToggleBreakPoint(_breakPoint, on: true);

public void Enable() {
_emulatorBreakpointsManager.ToggleBreakPoint(_breakPoint, on: true);
IsEnabled = true;
}

[RelayCommand]
private void Disable() => _emulatorBreakpointsManager.ToggleBreakPoint(_breakPoint, on: false);
public void Disable() {
_emulatorBreakpointsManager.ToggleBreakPoint(_breakPoint, on: false);
IsEnabled = false;
}

internal bool IsFor(CpuInstructionInfo instructionInfo) {
return _breakPoint is AddressBreakPoint addressBreakPoint && addressBreakPoint.Address == instructionInfo.Address;
}
}
72 changes: 72 additions & 0 deletions src/Spice86/ViewModels/BreakpointsViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
namespace Spice86.ViewModels;

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

using Spice86.Core.Emulator.VM;
using Spice86.Core.Emulator.VM.Breakpoint;
using Spice86.Models.Debugging;

using System;
using System.Collections.ObjectModel;

public partial class BreakpointsViewModel : ViewModelBase {
private readonly EmulatorBreakpointsManager _emulatorBreakpointsManager;

public BreakpointsViewModel(EmulatorBreakpointsManager emulatorBreakpointsManager) {
_emulatorBreakpointsManager = emulatorBreakpointsManager;
}

[ObservableProperty]
private bool _showBreakpointCreationDialog = false;

[ObservableProperty]
private ObservableCollection<BreakpointViewModel> _breakpoints = new();

[RelayCommand(CanExecute = nameof(ToggleSelectedBreakpointCanExecute))]
private void ToggleSelectedBreakpoint() {
if (SelectedBreakpoint is not null) {
SelectedBreakpoint.Toggle();
}
}

private bool ToggleSelectedBreakpointCanExecute() => SelectedBreakpoint is not null;

[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(RemoveBreakpointCommand))]
[NotifyCanExecuteChangedFor(nameof(ToggleSelectedBreakpointCommand))]
private BreakpointViewModel? _selectedBreakpoint;

internal void AddAddressBreakpoint(AddressBreakPoint addressBreakPoint) {
var breakpointViewModel = new BreakpointViewModel(_emulatorBreakpointsManager, addressBreakPoint);
Breakpoints.Add(breakpointViewModel);
SelectedBreakpoint = breakpointViewModel;
}

[RelayCommand]
private void Create() {
ShowBreakpointCreationDialog = true;
}

private bool RemoveBreakpointCanExecute() => SelectedBreakpoint is not null;


[RelayCommand(CanExecute = nameof(RemoveBreakpointCanExecute))]
private void RemoveBreakpoint() {
if (SelectedBreakpoint is not null) {
Breakpoints.Remove(SelectedBreakpoint);
}
}

internal bool HasBreakpoint(CpuInstructionInfo instructionInfo) {
return Breakpoints.Any(x => x.IsFor(instructionInfo));
}

internal void RemoveBreakpoint(CpuInstructionInfo selectedInstruction) {
BreakpointViewModel? breakpoint = Breakpoints.FirstOrDefault(x => x.IsFor(selectedInstruction));
if (breakpoint is not null) {
breakpoint.Disable();
Breakpoints.Remove(breakpoint);
}
}
}
6 changes: 5 additions & 1 deletion src/Spice86/ViewModels/DebugWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ public partial class DebugWindowViewModel : ViewModelBase,
[ObservableProperty]
private StatusMessageViewModel _statusMessageViewModel;

[ObservableProperty]
private BreakpointsViewModel _breakpointsViewModel;

private readonly IPauseHandler _pauseHandler;

public DebugWindowViewModel(IInstructionExecutor cpu, State cpuState, Stack stack, IMemory memory, Midi externalMidiDevice,
Expand All @@ -68,12 +71,13 @@ public DebugWindowViewModel(IInstructionExecutor cpu, State cpuState, Stack stac
messenger.Register<RemoveViewModelMessage<DisassemblyViewModel>>(this);
messenger.Register<RemoveViewModelMessage<MemoryViewModel>>(this);
_messenger = messenger;
BreakpointsViewModel = new(emulatorBreakpointsManager);
StatusMessageViewModel = new(_messenger);
_pauseHandler = pauseHandler;
IsPaused = pauseHandler.IsPaused;
pauseHandler.Pausing += () => uiDispatcher.Post(() => IsPaused = true);
pauseHandler.Resumed += () => uiDispatcher.Post(() => IsPaused = false);
DisassemblyViewModel disassemblyVm = new(cpu, memory, cpuState, emulatorBreakpointsManager, pauseHandler, uiDispatcher, messenger, textClipboard);
DisassemblyViewModel disassemblyVm = new(cpu, memory, cpuState, BreakpointsViewModel, pauseHandler, uiDispatcher, messenger, textClipboard);
DisassemblyViewModels.Add(disassemblyVm);
PaletteViewModel = new(argbPalette, uiDispatcher);
SoftwareMixerViewModel = new(softwareMixer);
Expand Down
24 changes: 10 additions & 14 deletions src/Spice86/ViewModels/DisassemblyViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public partial class DisassemblyViewModel : ViewModelBase {
private readonly ITextClipboard _textClipboard;
private readonly IUIDispatcher _uiDispatcher;
private readonly IInstructionExecutor _cpu;
private readonly EmulatorBreakpointsManager _emulatorBreakpointsManager;
private readonly BreakpointsViewModel _breakpointsViewModel;

[ObservableProperty]
private string _header = "Disassembly View";
Expand Down Expand Up @@ -67,11 +67,11 @@ public uint? StartAddress {
[NotifyCanExecuteChangedFor(nameof(CloseTabCommand))]
private bool _canCloseTab;

public DisassemblyViewModel(IInstructionExecutor cpu, IMemory memory, State state, EmulatorBreakpointsManager emulatorBreakpointsManager, IPauseHandler pauseHandler, IUIDispatcher uiDispatcher,
IMessenger messenger, ITextClipboard textClipboard,
bool canCloseTab = false) {
public DisassemblyViewModel(IInstructionExecutor cpu, IMemory memory, State state,
BreakpointsViewModel breakpointsViewModel, IPauseHandler pauseHandler, IUIDispatcher uiDispatcher,
IMessenger messenger, ITextClipboard textClipboard, bool canCloseTab = false) {
_cpu = cpu;
_emulatorBreakpointsManager = emulatorBreakpointsManager;
_breakpointsViewModel = breakpointsViewModel;
_messenger = messenger;
_uiDispatcher = uiDispatcher;
_textClipboard = textClipboard;
Expand Down Expand Up @@ -100,7 +100,7 @@ private void StepInto() {

[RelayCommand(CanExecute = nameof(IsPaused))]
private void NewDisassemblyView() {
DisassemblyViewModel disassemblyViewModel = new(_cpu, _memory, _state, _emulatorBreakpointsManager, _pauseHandler, _uiDispatcher, _messenger,
DisassemblyViewModel disassemblyViewModel = new(_cpu, _memory, _state, _breakpointsViewModel, _pauseHandler, _uiDispatcher, _messenger,
_textClipboard, canCloseTab: true) {
IsPaused = IsPaused
};
Expand Down Expand Up @@ -162,7 +162,6 @@ private List<CpuInstructionInfo> DecodeInstructions(State state, IMemory memory,
long instructionAddress = codeMemoryStream.Position;
decoder.Decode(out Instruction instruction);
CpuInstructionInfo instructionInfo = new() {
HasBreakpoint = _emulatorBreakpointsManager.IsAddressBreakpointAt(instructionAddress),
Instruction = instruction,
Address = (uint)instructionAddress,
Length = instruction.Length,
Expand All @@ -178,6 +177,7 @@ private List<CpuInstructionInfo> DecodeInstructions(State state, IMemory memory,
FlowControl = instruction.FlowControl,
Bytes = $"{Convert.ToHexString(memory.GetData((uint)instructionAddress, (uint)instruction.Length))}"
};
instructionInfo.HasBreakpoint = _breakpointsViewModel.HasBreakpoint(instructionInfo);
instructionInfo.StringRepresentation =
$"{instructionInfo.Address:X4} ({instructionInfo.SegmentedAddress}): {instruction} ({instructionInfo.Bytes})";
if (instructionAddress == state.IpPhysicalAddress) {
Expand All @@ -199,12 +199,9 @@ private void OnBreakPointReached(BreakPoint breakPoint) {

[RelayCommand]
private void RemoveAddressBreakpointHere() {
if (SelectedInstruction?.BreakPoint is null) {
return;
if (SelectedInstruction is not null) {
_breakpointsViewModel.RemoveBreakpoint(SelectedInstruction);
}
_emulatorBreakpointsManager.ToggleBreakPoint(SelectedInstruction.BreakPoint, false);
SelectedInstruction.BreakPoint = null;
SelectedInstruction.HasBreakpoint = false;
}

[RelayCommand]
Expand All @@ -214,8 +211,7 @@ private void CreateAddressBreakpointHere() {
}
AddressBreakPoint breakPoint = new(BreakPointType.EXECUTION, SelectedInstruction.Address, OnBreakPointReached,
isRemovedOnTrigger: false);
SelectedInstruction.BreakPoint = breakPoint;
_emulatorBreakpointsManager.ToggleBreakPoint(breakPoint, true);
_breakpointsViewModel.AddAddressBreakpoint(breakPoint);
SelectedInstruction.HasBreakpoint = true;
}

Expand Down
36 changes: 36 additions & 0 deletions src/Spice86/Views/BreakpointsView.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Spice86.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Spice86.Views.BreakpointsView"
x:DataType="viewModels:BreakpointsViewModel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="Create..." Command="{Binding CreateCommand}" Margin="5"/>
<Button Content="Remove" Command="{Binding RemoveBreakpointCommand}" Margin="5"/>
<Button Content="Toggle" Command="{Binding ToggleSelectedBreakpointCommand}" Margin="5"/>
</StackPanel>
<DataGrid Grid.Row="1"
ItemsSource="{Binding Breakpoints}"
SelectedItem="{Binding SelectedBreakpoint, Mode=TwoWay}"
AutoGenerateColumns="False"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserSortColumns="False"
IsReadOnly="True"
SelectionMode="Extended">
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding IsEnabled}" Header="Is Enabled ?" />
<DataGridCheckBoxColumn Binding="{Binding IsRemovedOnTrigger}" Header="Is removed on trigger ?" />
<DataGridTextColumn Binding="{Binding Address}" Header="Address" />
<DataGridTextColumn Binding="{Binding Type}" Header="Type" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
10 changes: 10 additions & 0 deletions src/Spice86/Views/BreakpointsView.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Spice86.Views;
using Avalonia.Controls;

public partial class BreakpointsView : UserControl
{
public BreakpointsView()
{
InitializeComponent();
}
}
Loading

0 comments on commit 69176d0

Please sign in to comment.