diff --git a/src/Spice86/Models/Debugging/FunctionInfo.cs b/src/Spice86/Models/Debugging/FunctionInfo.cs new file mode 100644 index 000000000..2081d3b7b --- /dev/null +++ b/src/Spice86/Models/Debugging/FunctionInfo.cs @@ -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}"; + } +} diff --git a/src/Spice86/Spice86DependencyInjection.cs b/src/Spice86/Spice86DependencyInjection.cs index 2bf56833e..3cdb89aff 100644 --- a/src/Spice86/Spice86DependencyInjection.cs +++ b/src/Spice86/Spice86DependencyInjection.cs @@ -193,8 +193,9 @@ public Spice86DependencyInjection(ILoggerService loggerService, Configuration co dmaController, soundBlaster.Opl3Fm, softwareMixer, mouse, mouseDriver, vgaFunctionality, pauseHandler); + IDictionary functionsInformation = reader.ReadGhidraSymbolsFromFileOrCreate(); InitializeFunctionHandlers(configuration, machine, loggerService, - reader.ReadGhidraSymbolsFromFileOrCreate(), functionHandler, functionHandlerInExternalInterrupt); + functionsInformation, functionHandler, functionHandlerInExternalInterrupt); ProgramExecutor programExecutor = new(configuration, emulatorBreakpointsManager, emulatorStateSerializer, memory, cpu, cfgCpu, state, @@ -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); } diff --git a/src/Spice86/ViewModels/DebugWindowViewModel.cs b/src/Spice86/ViewModels/DebugWindowViewModel.cs index e597abdc3..4177a8370 100644 --- a/src/Spice86/ViewModels/DebugWindowViewModel.cs +++ b/src/Spice86/ViewModels/DebugWindowViewModel.cs @@ -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>, IRecipient>, @@ -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 functionsInformation, IStructureViewModelFactory structureViewModelFactory, IPauseHandler pauseHandler) { messenger.Register>(this); messenger.Register>(this); @@ -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); diff --git a/src/Spice86/ViewModels/DisassemblyViewModel.cs b/src/Spice86/ViewModels/DisassemblyViewModel.cs index 08aeebe10..1fa0d156b 100644 --- a/src/Spice86/ViewModels/DisassemblyViewModel.cs +++ b/src/Spice86/ViewModels/DisassemblyViewModel.cs @@ -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; @@ -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 _functionsInformation; + + public DisassemblyViewModel( + IInstructionExecutor cpu, IMemory memory, State state, + IDictionary 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; @@ -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 _functions = new(); + private void OnPausing() { _uiDispatcher.Post(() => { IsPaused = true; @@ -96,7 +129,6 @@ private void ConfirmCreateExecutionBreakpoint() { (long)breakpointAddressValue, OnBreakPointReached, false); _breakpointsViewModel.AddAddressBreakpoint(addressBreakPoint); } - } [ObservableProperty] @@ -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 }; @@ -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() { @@ -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) { diff --git a/src/Spice86/Views/DisassemblyView.axaml b/src/Spice86/Views/DisassemblyView.axaml index 6ae6dfc84..e840b8f6c 100644 --- a/src/Spice86/Views/DisassemblyView.axaml +++ b/src/Spice86/Views/DisassemblyView.axaml @@ -55,8 +55,16 @@