Skip to content

Commit

Permalink
feat: Go To Function (Internal Debugger)
Browse files Browse the repository at this point in the history
Signed-off-by: Maximilien Noal <[email protected]>
  • Loading branch information
maximilien-noal committed Nov 11, 2024
1 parent 1e19339 commit 6095a47
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 11 deletions.
14 changes: 14 additions & 0 deletions src/Spice86/Models/Debugging/FunctionInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Spice86.Models.Debugging;

using CommunityToolkit.Mvvm.ComponentModel;

using Spice86.Shared.Emulator.Memory;

public partial class FunctionInfo : ObservableObject {
[ObservableProperty] private string? _name;
[ObservableProperty] private uint _address;

public override string ToString() {
return $"{Address}: {Name}";
}
}
4 changes: 3 additions & 1 deletion src/Spice86/Spice86DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,9 @@ public Spice86DependencyInjection(ILoggerService loggerService, Configuration co
dmaController, soundBlaster.Opl3Fm, softwareMixer, mouse, mouseDriver,
vgaFunctionality, pauseHandler);

IDictionary<SegmentedAddress, FunctionInformation> functionsInformation = reader.ReadGhidraSymbolsFromFileOrCreate();
InitializeFunctionHandlers(configuration, machine, loggerService,
reader.ReadGhidraSymbolsFromFileOrCreate(), functionHandler, functionHandlerInExternalInterrupt);
functionsInformation, functionHandler, functionHandlerInExternalInterrupt);

ProgramExecutor programExecutor = new(configuration, emulatorBreakpointsManager,
emulatorStateSerializer, memory, cpu, cfgCpu, state,
Expand Down Expand Up @@ -245,6 +246,7 @@ public Spice86DependencyInjection(ILoggerService loggerService, Configuration co
midiDevice, videoState.DacRegisters.ArgbPalette, softwareMixer, vgaRenderer, videoState,
cfgCpu.ExecutionContextManager, messenger, uiThreadDispatcher, textClipboard, hostStorageProvider,
emulatorBreakpointsManager,
functionsInformation,
new StructureViewModelFactory(configuration, loggerService, pauseHandler),
pauseHandler);
}
Expand Down
5 changes: 4 additions & 1 deletion src/Spice86/ViewModels/DebugWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ namespace Spice86.ViewModels;
using Spice86.Core.Emulator.Devices.Sound;
using Spice86.Core.Emulator.Devices.Sound.Midi;
using Spice86.Core.Emulator.Devices.Video;
using Spice86.Core.Emulator.Function;
using Spice86.Core.Emulator.Memory;
using Spice86.Core.Emulator.VM;
using Spice86.Infrastructure;
using Spice86.Messages;
using Spice86.Shared.Diagnostics;
using Spice86.Shared.Emulator.Memory;

public partial class DebugWindowViewModel : ViewModelBase,
IRecipient<AddViewModelMessage<DisassemblyViewModel>>, IRecipient<AddViewModelMessage<MemoryViewModel>>,
Expand Down Expand Up @@ -65,6 +67,7 @@ public DebugWindowViewModel(IInstructionExecutor cpu, State cpuState, Stack stac
ArgbPalette argbPalette, SoftwareMixer softwareMixer, IVgaRenderer vgaRenderer, VideoState videoState,
ExecutionContextManager executionContextManager, IMessenger messenger, IUIDispatcher uiDispatcher,
ITextClipboard textClipboard, IHostStorageProvider storageProvider, EmulatorBreakpointsManager emulatorBreakpointsManager,
IDictionary<SegmentedAddress, FunctionInformation> functionsInformation,
IStructureViewModelFactory structureViewModelFactory, IPauseHandler pauseHandler) {
messenger.Register<AddViewModelMessage<DisassemblyViewModel>>(this);
messenger.Register<AddViewModelMessage<MemoryViewModel>>(this);
Expand All @@ -78,7 +81,7 @@ public DebugWindowViewModel(IInstructionExecutor cpu, State cpuState, Stack stac
pauseHandler.Pausing += () => uiDispatcher.Post(() => IsPaused = true);
pauseHandler.Resumed += () => uiDispatcher.Post(() => IsPaused = false);
DisassemblyViewModel disassemblyVm = new(
cpu, memory, cpuState,
cpu, memory, cpuState, functionsInformation.ToDictionary(x => x.Key.ToPhysical(), x => x.Value),
BreakpointsViewModel, emulatorBreakpointsManager, pauseHandler,
uiDispatcher, messenger, textClipboard);
DisassemblyViewModels.Add(disassemblyVm);
Expand Down
68 changes: 60 additions & 8 deletions src/Spice86/ViewModels/DisassemblyViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Spice86.ViewModels;
using Iced.Intel;

using Spice86.Core.Emulator.CPU;
using Spice86.Core.Emulator.Function;
using Spice86.Core.Emulator.Memory;
using Spice86.Core.Emulator.VM;
using Spice86.Core.Emulator.VM.Breakpoint;
Expand All @@ -29,11 +30,23 @@ public partial class DisassemblyViewModel : ViewModelWithErrorDialog {
private readonly IInstructionExecutor _cpu;
private readonly BreakpointsViewModel _breakpointsViewModel;
private readonly EmulatorBreakpointsManager _emulatorBreakpointsManager;

public DisassemblyViewModel(IInstructionExecutor cpu, IMemory memory, State state,
BreakpointsViewModel breakpointsViewModel, EmulatorBreakpointsManager emulatorBreakpointsManager, IPauseHandler pauseHandler, IUIDispatcher uiDispatcher,
IMessenger messenger, ITextClipboard textClipboard, bool canCloseTab = false) : base(uiDispatcher, textClipboard) {
private readonly IDictionary<uint, FunctionInformation> _functionsInformation;

public DisassemblyViewModel(
IInstructionExecutor cpu, IMemory memory, State state,
IDictionary<uint, FunctionInformation> functionsInformation,
BreakpointsViewModel breakpointsViewModel, EmulatorBreakpointsManager emulatorBreakpointsManager,
IPauseHandler pauseHandler, IUIDispatcher uiDispatcher,
IMessenger messenger, ITextClipboard textClipboard, bool canCloseTab = false)
: base(uiDispatcher, textClipboard) {
_cpu = cpu;
_functionsInformation = functionsInformation;
Functions = new(functionsInformation
.Select(x => new FunctionInfo() {
Name = x.Value.Name,
Address = x.Key,
}).OrderBy(x => x.Address));
AreFunctionInformationProvided = functionsInformation.Count > 0;
_emulatorBreakpointsManager = emulatorBreakpointsManager;
_breakpointsViewModel = breakpointsViewModel;
_messenger = messenger;
Expand All @@ -46,6 +59,26 @@ public DisassemblyViewModel(IInstructionExecutor cpu, IMemory memory, State stat
CanCloseTab = canCloseTab;
}

[ObservableProperty]
private bool _areFunctionInformationProvided;

private FunctionInfo? _selectedFunction;

public FunctionInfo? SelectedFunction {
get => _selectedFunction;
set {
_selectedFunction = value;
OnPropertyChanged(nameof(SelectedFunction));
if (value is not null) {
uint address = value.Address;
GoToAddress(address);
}
}
}

[ObservableProperty]
private AvaloniaList<FunctionInfo> _functions = new();

private void OnPausing() {
_uiDispatcher.Post(() => {
IsPaused = true;
Expand Down Expand Up @@ -96,7 +129,6 @@ private void ConfirmCreateExecutionBreakpoint() {
(long)breakpointAddressValue, OnBreakPointReached, false);
_breakpointsViewModel.AddAddressBreakpoint(addressBreakPoint);
}

}

[ObservableProperty]
Expand Down Expand Up @@ -157,7 +189,9 @@ private void StepInto() {

[RelayCommand(CanExecute = nameof(IsPaused))]
private void NewDisassemblyView() {
DisassemblyViewModel disassemblyViewModel = new(_cpu, _memory, _state, _breakpointsViewModel, _emulatorBreakpointsManager, _pauseHandler, _uiDispatcher, _messenger,
DisassemblyViewModel disassemblyViewModel = new(
_cpu, _memory, _state, _functionsInformation,
_breakpointsViewModel, _emulatorBreakpointsManager, _pauseHandler, _uiDispatcher, _messenger,
_textClipboard, canCloseTab: true) {
IsPaused = IsPaused
};
Expand All @@ -166,9 +200,14 @@ private void NewDisassemblyView() {

[RelayCommand(CanExecute = nameof(IsPaused))]
private void GoToCsIp() {
StartAddress = _state.IpPhysicalAddress;
SegmentedStartAddress = new(_state.CS, _state.IP);
GoToAddress(_state.IpPhysicalAddress);
}

private void GoToAddress(uint address) {
StartAddress = address;
UpdateDisassembly();
SelectedInstruction = Instructions.FirstOrDefault();
}

private uint? GetStartAddress() {
Expand Down Expand Up @@ -197,9 +236,22 @@ private void UpdateDisassembly() {
UpdateHeader(SelectedInstruction?.Address);
}

[ObservableProperty]
private CpuInstructionInfo? _selectedInstruction;

public CpuInstructionInfo? SelectedInstruction {
get => _selectedInstruction;
set {
if (value is not null &&
_functionsInformation.TryGetValue(value.Address,
out FunctionInformation? functionInformation)) {
_selectedFunction = Functions.First(x => x.Address == value.Address);
OnPropertyChanged(nameof(SelectedFunction));
}
_selectedInstruction = value;
OnPropertyChanged(nameof(SelectedInstruction));
}
}

[RelayCommand(CanExecute = nameof(IsPaused))]
private async Task CopyLine() {
if (SelectedInstruction is not null) {
Expand Down
10 changes: 9 additions & 1 deletion src/Spice86/Views/DisassemblyView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,16 @@
</StackPanel>
<StackPanel Orientation="Vertical">
<Button Content="Go to CS:IP" Command="{Binding GoToCsIpCommand}" />
<Button Content="Refresh" Command="{Binding UpdateDisassemblyCommand}" />
<Label
IsVisible="{Binding AreFunctionInformationProvided}">
Go to function:
</Label>
<ComboBox
IsVisible="{Binding AreFunctionInformationProvided}"
ItemsSource="{Binding Functions}"
SelectedItem="{Binding SelectedFunction, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<Button Content="Refresh" Command="{Binding UpdateDisassemblyCommand}" />
</StackPanel>
<DataGrid
Grid.Row="1"
Expand Down

0 comments on commit 6095a47

Please sign in to comment.