From 55d13fac961aaaea720c6d0e655e4d5b88ef16ef Mon Sep 17 00:00:00 2001 From: Maximilien Noal Date: Mon, 17 Jun 2024 20:05:28 +0200 Subject: [PATCH] Feature/multiple memory views (#716) * UI: refactored the error dialogs into a single one Signed-off-by: Maximilien Noal * feature: editable memory view range Signed-off-by: Maximilien Noal * feature: additional memory views (WIP) Signed-off-by: Maximilien Noal * Debugger: catch IndexOutOfRangeException Signed-off-by: Maximilien Noal * UI: Pause the software mixer on emulator Pause Signed-off-by: Maximilien Noal * UI:Additonnal memory and disasm views feature Signed-off-by: Maximilien Noal * fix: UI did not respond while visiting emulator Signed-off-by: Maximilien Noal * fix: MemoryView crashes, new DISASM view button Signed-off-by: Maximilien Noal * Debugger: refactored into a tab Signed-off-by: Maximilien Noal * removed disasm duplicated view datatemplate Signed-off-by: Maximilien Noal * refactor: Removed reflection bindings for commands Signed-off-by: Maximilien Noal * refactor: Configuration is a sealed class Signed-off-by: Maximilien Noal * refactor: removed ViewLocator antipattern Signed-off-by: Maximilien Noal * refactor: ViewModelBaseWithErrrorDialog Signed-off-by: Maximilien Noal * refactor: removed ctors for Designer from VMs Signed-off-by: Maximilien Noal * fix: UI reads memory without trigerring read breakpoints Signed-off-by: Maximilien Noal * fix: Edit Memory button reacts to pause status Signed-off-by: Maximilien Noal * refactor: merged DI branches in Program.cs Signed-off-by: Maximilien Noal * refactor: inverted if to reduce indentation Signed-off-by: Maximilien Noal * doc: corrected grammar Signed-off-by: Maximilien Noal * refactor: memory read/write methods without trigerring breakpoints Signed-off-by: Maximilien Noal * refactor: removed some unused ViewModel code Signed-off-by: Maximilien Noal * refactor: removed SelectedTab ObservableProperty Signed-off-by: Maximilien Noal * refactor: Debugger is its own window again Signed-off-by: Maximilien Noal * Expanded XML documentation, polished DebugWindow Signed-off-by: Maximilien Noal * feature: close Memory/Disassembly views Signed-off-by: Maximilien Noal * doc: documented the fact that IReadOnlyBitRangeUnion endAdress is not included Signed-off-by: Maximilien Noal * MemoryView: better layout (DockPanel) Signed-off-by: Maximilien Noal * fix: Close DebugWindow on new program launch Signed-off-by: Maximilien Noal * UI: Debug Window icon Signed-off-by: Maximilien Noal * refactor: removed unused usings. Co-authored-by: Joris van Eijden --------- Signed-off-by: Maximilien Noal Co-authored-by: Joris van Eijden --- src/Spice86.Core/CLI/Configuration.cs | 2 +- .../Emulator/Devices/Sound/SoftwareMixer.cs | 7 +- .../Emulator/Devices/Timer/Timer.cs | 13 +- .../Function/Dump/MemoryDataExporter.cs | 2 +- .../InternalDebugger/IInternalDebugger.cs | 17 +- .../Common/Callback/CallbackHandler.cs | 2 +- src/Spice86.Core/Emulator/Memory/IMemory.cs | 20 +- src/Spice86.Core/Emulator/Memory/Memory.cs | 43 +-- src/Spice86.Core/Emulator/VM/Machine.cs | 4 +- src/Spice86.Shared/Interfaces/IGui.cs | 5 - src/Spice86/App.axaml | 4 - src/Spice86/Assets/Debug.ico | Bin 0 -> 115262 bytes .../DependencyInjection/Composition.cs | 18 -- .../Infrastructure/DebugWindowActivator.cs | 26 -- .../Infrastructure/HostStorageProvider.cs | 15 + .../Infrastructure/IDebugWindowActivator.cs | 21 -- .../Infrastructure/IHostStorageProvider.cs | 7 + src/Spice86/Infrastructure/IWindowService.cs | 19 ++ src/Spice86/Infrastructure/WindowService.cs | 50 ++++ .../MemoryWrappers/MemoryBinaryDocument.cs | 26 +- .../MemoryReadOnlyBitRangeUnion.cs | 25 +- src/Spice86/Program.cs | 65 ++-- src/Spice86/Spice86.csproj | 5 +- .../ErrorModalDialogUserControl.axaml | 47 +++ .../ErrorModalDialogUserControl.axaml.cs | 9 + src/Spice86/ViewLocator.cs | 25 -- src/Spice86/ViewModels/CpuViewModel.cs | 29 +- .../ViewModels/DebugWindowViewModel.cs | 117 ++++---- .../ViewModels/DisassemblyViewModel.cs | 71 ++++- src/Spice86/ViewModels/MainWindowViewModel.cs | 114 +++---- src/Spice86/ViewModels/MemoryViewModel.cs | 141 ++++++++- src/Spice86/ViewModels/MidiViewModel.cs | 28 +- src/Spice86/ViewModels/PaletteViewModel.cs | 20 +- .../ViewModels/PerformanceViewModel.cs | 20 +- .../ViewModels/SoftwareMixerViewModel.cs | 21 +- src/Spice86/ViewModels/VideoCardViewModel.cs | 283 ++++++++++-------- src/Spice86/ViewModels/ViewModelBase.cs | 19 +- .../ViewModelBaseWithErrorDialog.cs | 38 +++ src/Spice86/Views/DebugWindow.axaml | 71 ++--- src/Spice86/Views/DebugWindow.axaml.cs | 2 - src/Spice86/Views/DisassemblyView.axaml | 40 +-- src/Spice86/Views/MainWindow.axaml | 33 +- src/Spice86/Views/MemoryView.axaml | 49 ++- tests/Spice86.Tests/MachineTest.cs | 2 +- 44 files changed, 940 insertions(+), 635 deletions(-) create mode 100644 src/Spice86/Assets/Debug.ico delete mode 100644 src/Spice86/DependencyInjection/Composition.cs delete mode 100644 src/Spice86/Infrastructure/DebugWindowActivator.cs delete mode 100644 src/Spice86/Infrastructure/IDebugWindowActivator.cs create mode 100644 src/Spice86/Infrastructure/IWindowService.cs create mode 100644 src/Spice86/Infrastructure/WindowService.cs create mode 100644 src/Spice86/UserControls/ErrorModalDialogUserControl.axaml create mode 100644 src/Spice86/UserControls/ErrorModalDialogUserControl.axaml.cs delete mode 100644 src/Spice86/ViewLocator.cs create mode 100644 src/Spice86/ViewModels/ViewModelBaseWithErrorDialog.cs diff --git a/src/Spice86.Core/CLI/Configuration.cs b/src/Spice86.Core/CLI/Configuration.cs index a0ccedf65..6de5132aa 100644 --- a/src/Spice86.Core/CLI/Configuration.cs +++ b/src/Spice86.Core/CLI/Configuration.cs @@ -6,7 +6,7 @@ namespace Spice86.Core.CLI; using Spice86.Core.Emulator.Function; /// Configuration for spice86, that is what to run and how. Set on startup. -public class Configuration { +public sealed class Configuration { /// /// Gets or sets whether the A20 gate is silenced. If true memory addresses will rollover above 1 MB. /// diff --git a/src/Spice86.Core/Emulator/Devices/Sound/SoftwareMixer.cs b/src/Spice86.Core/Emulator/Devices/Sound/SoftwareMixer.cs index 55144ea5b..0adff1a2b 100644 --- a/src/Spice86.Core/Emulator/Devices/Sound/SoftwareMixer.cs +++ b/src/Spice86.Core/Emulator/Devices/Sound/SoftwareMixer.cs @@ -27,13 +27,18 @@ internal void Register(SoundChannel soundChannel) { Channels = _channels.ToFrozenDictionary(); } + /// + /// Gets or sets a value indicating whether the software mixer is paused. In paused state, the mixer will not render any sound. + /// + public bool IsPaused { get; set; } + /// /// Gets the sound channels in a read-only dictionary. /// public FrozenDictionary Channels { get; private set; } = new Dictionary().ToFrozenDictionary(); internal int Render(Span data, SoundChannel channel) { - if (channel.Volume == 0 || channel.IsMuted) { + if (channel.Volume == 0 || channel.IsMuted || IsPaused) { _channels[channel].WriteSilence(); return data.Length; } diff --git a/src/Spice86.Core/Emulator/Devices/Timer/Timer.cs b/src/Spice86.Core/Emulator/Devices/Timer/Timer.cs index 78866c3c7..fdeed0571 100644 --- a/src/Spice86.Core/Emulator/Devices/Timer/Timer.cs +++ b/src/Spice86.Core/Emulator/Devices/Timer/Timer.cs @@ -6,19 +6,15 @@ using Spice86.Shared.Interfaces; using Spice86.Core.Emulator.Devices.ExternalInput; -using Spice86.Core.Emulator.Devices.Video; +using Spice86.Core.Emulator.InternalDebugger; using Spice86.Core.Emulator.IOPorts; -using Spice86.Core.Emulator.Memory; -using Spice86.Core.Emulator.VM; - -using System.ComponentModel; /// /// Emulates a PIT8254 Programmable Interval Timer.
/// Triggers interrupt 8 on the CPU via the PIC.
/// https://k.lse.epita.fr/internals/8254_controller.html ///
-public class Timer : DefaultIOPortHandler, ITimeMultiplier { +public class Timer : DefaultIOPortHandler, ITimeMultiplier, IDebuggableComponent { private const int CounterRegisterZero = 0x40; private const int CounterRegisterOne = 0x41; private const int CounterRegisterTwo = 0x42; @@ -119,4 +115,9 @@ private Counter GetCounterIndexFromPortNumber(int port) { int counter = port & 0b11; return GetCounter(counter); } + + /// + public void Accept(T emulatorDebugger) where T : IInternalDebugger { + emulatorDebugger.Visit(this); + } } diff --git a/src/Spice86.Core/Emulator/Function/Dump/MemoryDataExporter.cs b/src/Spice86.Core/Emulator/Function/Dump/MemoryDataExporter.cs index a04cfc4a3..59356704f 100644 --- a/src/Spice86.Core/Emulator/Function/Dump/MemoryDataExporter.cs +++ b/src/Spice86.Core/Emulator/Function/Dump/MemoryDataExporter.cs @@ -46,6 +46,6 @@ private byte[] GenerateToolingCompliantRamDump() { return _callbackHandler.ReplaceAllCallbacksInRamImage(_memory); } - return _memory.RamCopy; + return _memory.ReadRam(); } } \ No newline at end of file diff --git a/src/Spice86.Core/Emulator/InternalDebugger/IInternalDebugger.cs b/src/Spice86.Core/Emulator/InternalDebugger/IInternalDebugger.cs index eecbfa321..372a342db 100644 --- a/src/Spice86.Core/Emulator/InternalDebugger/IInternalDebugger.cs +++ b/src/Spice86.Core/Emulator/InternalDebugger/IInternalDebugger.cs @@ -1,5 +1,18 @@ namespace Spice86.Core.Emulator.InternalDebugger; -public interface IInternalDebugger -{ + +/// +/// Interface for the internal debuggers implemented by the UI ViewModels. +/// +public interface IInternalDebugger { + /// + /// Visit an emulator component that accepts the internal debugger. + /// + /// The emulator component that accepts the internal debugger + /// A class that implements the interface. void Visit(T component) where T : IDebuggableComponent; + + /// + /// Tells if the ViewModel for the internal debugger needs to visit the emulator. Either to get references to internal objects or refresh UI data. + /// + public bool NeedsToVisitEmulator { get; } } diff --git a/src/Spice86.Core/Emulator/InterruptHandlers/Common/Callback/CallbackHandler.cs b/src/Spice86.Core/Emulator/InterruptHandlers/Common/Callback/CallbackHandler.cs index ca178da3c..192765a4e 100644 --- a/src/Spice86.Core/Emulator/InterruptHandlers/Common/Callback/CallbackHandler.cs +++ b/src/Spice86.Core/Emulator/InterruptHandlers/Common/Callback/CallbackHandler.cs @@ -50,7 +50,7 @@ public void RunFromOverriden(int index) { /// The memory bus. /// A byte array representing the memory content with the Spice86 machine code for callbacks removed. public byte[] ReplaceAllCallbacksInRamImage(IMemory memory) { - ByteArrayBasedIndexable indexable = new ByteArrayBasedIndexable(memory.RamCopy); + ByteArrayBasedIndexable indexable = new ByteArrayBasedIndexable(memory.ReadRam()); MemoryAsmWriter memoryAsmWriter = new MemoryAsmWriter(indexable, new SegmentedAddress(0, 0), this); foreach (ICallback callback in this.AllRunnables) { memoryAsmWriter.CurrentAddress = callback.InstructionAddress; diff --git a/src/Spice86.Core/Emulator/Memory/IMemory.cs b/src/Spice86.Core/Emulator/Memory/IMemory.cs index 7905cdb82..af2430315 100644 --- a/src/Spice86.Core/Emulator/Memory/IMemory.cs +++ b/src/Spice86.Core/Emulator/Memory/IMemory.cs @@ -24,12 +24,22 @@ public interface IMemory : IIndexable, IByteReaderWriter, IDebuggableComponent { A20Gate A20Gate { get; } /// - /// Gets a copy of the current memory state. + /// Gets a copy of the current memory state, not triggering any breakpoints. /// - byte[] RamCopy { get; } + /// The length of the byte array. Default is equal to the memory length. + /// Where to start in the memory. Default is 0. + /// A copy of the current memory state. + public byte[] ReadRam(uint length = 0, uint offset = 0); /// - /// Returns a that represents the specified range of memory. + /// Writes an array of bytes to memory, not triggering any breakpoints. + /// + /// The array to copy data from. + /// Where to start in the memory. Default is 0. + public void WriteRam(byte[] array, uint offset = 0); + + /// + /// Returns a that represents the specified range of memory. Will trigger memory read breakpoints. /// /// The starting address of the memory range. /// The length of the memory range. @@ -47,12 +57,12 @@ public interface IMemory : IIndexable, IByteReaderWriter, IDebuggableComponent { uint? SearchValue(uint address, int len, IList value); /// - /// Allows write breakpoints to access the byte being written before it actually is. + /// Allows memory write breakpoints to access the byte being written before it actually is. /// byte CurrentlyWritingByte { get; } /// - /// Allow a class to register for a certain memory range. + /// Allow a memory mapped device to register for a certain memory range. /// /// The start of the frameB /// The size of the window diff --git a/src/Spice86.Core/Emulator/Memory/Memory.cs b/src/Spice86.Core/Emulator/Memory/Memory.cs index f02f9a5b0..c33855bb9 100644 --- a/src/Spice86.Core/Emulator/Memory/Memory.cs +++ b/src/Spice86.Core/Emulator/Memory/Memory.cs @@ -47,17 +47,22 @@ public Memory(IMemoryDevice baseMemory, bool is20ThAddressLineSilenced) { /// public const uint EndOfHighMemoryArea = 0x10FFEF; - /// - /// Gets a copy of the current memory state, not triggering any breakpoints. - /// - public byte[] RamCopy { - get { - byte[] copy = new byte[_memoryDevices.Length]; - for (uint address = 0; address < copy.Length; address++) { - copy[address] = _memoryDevices[address].Read(address); - } - - return copy; + /// + public byte[] ReadRam(uint length = 0, uint offset = 0) { + if (length == 0) { + length = (uint)_memoryDevices.Length; + } + byte[] copy = new byte[length]; + for (uint address = 0; address < copy.Length; address++) { + copy[address] = _memoryDevices[address + offset].Read(address + offset); + } + return copy; + } + + /// + public void WriteRam(byte[] array, uint offset = 0) { + for (uint address = 0; address < array.Length; address++) { + _memoryDevices[address + offset].Write(address + offset, array[address]); } } @@ -77,7 +82,7 @@ public byte this[uint address] { } /// - /// Allows write breakpoints to access the byte being written before it actually is. + /// Allows memory write breakpoints to access the byte being written before it actually is. /// public byte CurrentlyWritingByte { get; @@ -97,10 +102,11 @@ public byte CurrentlyWritingByte { public Span GetSpan(int address, int length) { address = A20Gate.TransformAddress(address); foreach (DeviceRegistration device in _devices) { - if (address >= device.StartAddress && address + length <= device.EndAddress) { - MemoryBreakpoints.MonitorRangeReadAccess((uint)address, (uint)(address + length)); - return device.Device.GetSpan(address, length); + if (address < device.StartAddress || address + length > device.EndAddress) { + continue; } + MemoryBreakpoints.MonitorRangeReadAccess((uint)address, (uint)(address + length)); + return device.Device.GetSpan(address, length); } throw new InvalidOperationException($"No Memory Device supports a span from {address} to {address + length}"); @@ -173,12 +179,7 @@ public override SegmentedAddressIndexer SegmentedAddress { get; } - /// - /// Allow a class to register for a certain memory range. - /// - /// The start of the frame - /// The size of the window - /// The memory device to use + /// public void RegisterMapping(uint baseAddress, uint size, IMemoryDevice memoryDevice) { uint endAddress = baseAddress + size; if (endAddress >= _memoryDevices.Length) { diff --git a/src/Spice86.Core/Emulator/VM/Machine.cs b/src/Spice86.Core/Emulator/VM/Machine.cs index 67e8f311c..6f66b252f 100644 --- a/src/Spice86.Core/Emulator/VM/Machine.cs +++ b/src/Spice86.Core/Emulator/VM/Machine.cs @@ -242,9 +242,6 @@ public Machine(IGui? gui, State cpuState, IOPortDispatcher ioPortDispatcher, ILo VgaCard = new VgaCard(gui, VgaRenderer, loggerService); Timer = new Timer(CpuState, loggerService, DualPic, counterConfigurator, configuration.FailOnUnhandledPort); - if (gui is not null) { - gui.ProgrammableIntervalTimer = Timer; - } RegisterIoPortHandler(Timer); Keyboard = new Keyboard(CpuState, Memory.A20Gate, DualPic, loggerService, gui, configuration.FailOnUnhandledPort); RegisterIoPortHandler(Keyboard); @@ -385,5 +382,6 @@ public void Accept(T emulatorDebugger) where T : IInternalDebugger { VgaRegisters.Accept(emulatorDebugger); MidiDevice.Accept(emulatorDebugger); SoftwareMixer.Accept(emulatorDebugger); + Timer.Accept(emulatorDebugger); } } \ No newline at end of file diff --git a/src/Spice86.Shared/Interfaces/IGui.cs b/src/Spice86.Shared/Interfaces/IGui.cs index 319bb96a3..9bdccc3bb 100644 --- a/src/Spice86.Shared/Interfaces/IGui.cs +++ b/src/Spice86.Shared/Interfaces/IGui.cs @@ -77,9 +77,4 @@ public interface IGui { /// Indicate that a mouse button has been released. /// event EventHandler? MouseButtonUp; - - /// - /// Used by the UI to set or reset the time multiplier. - /// - ITimeMultiplier? ProgrammableIntervalTimer { set; } } \ No newline at end of file diff --git a/src/Spice86/App.axaml b/src/Spice86/App.axaml index eb600ef39..d8b443d53 100644 --- a/src/Spice86/App.axaml +++ b/src/Spice86/App.axaml @@ -3,15 +3,11 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:dialogHostAvalonia="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia" - xmlns:spice86="clr-namespace:Spice86" RequestedThemeVariant="Default"> avares://Spice86/Assets#Roboto Mono Consolas - - - diff --git a/src/Spice86/Assets/Debug.ico b/src/Spice86/Assets/Debug.ico new file mode 100644 index 0000000000000000000000000000000000000000..f1e25597f4a49992131d72f5f07ccc29cb3c3977 GIT binary patch literal 115262 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 *pRDZ7g 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 literal 0 HcmV?d00001 diff --git a/src/Spice86/DependencyInjection/Composition.cs b/src/Spice86/DependencyInjection/Composition.cs deleted file mode 100644 index 0e768f293..000000000 --- a/src/Spice86/DependencyInjection/Composition.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Spice86.DependencyInjection; - -using Spice86.Logging; -using Spice86.Shared.Interfaces; -using Pure.DI; - -/// -/// The DI composition root for both the UI and headless mode. -/// -public partial class Composition { - private static void Setup() => - DI.Setup(nameof(Composition)) - // Infrastructure - .Bind().As(Lifetime.Singleton).To() - .Bind().As(Lifetime.Singleton).To() - // Composition Root - .Root(nameof(Root)); -} diff --git a/src/Spice86/Infrastructure/DebugWindowActivator.cs b/src/Spice86/Infrastructure/DebugWindowActivator.cs deleted file mode 100644 index 334063945..000000000 --- a/src/Spice86/Infrastructure/DebugWindowActivator.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Spice86.Infrastructure; - -using Spice86.ViewModels; -using Spice86.Views; - -/// -internal class DebugWindowActivator : IDebugWindowActivator { - private DebugWindow? _debugWindow; - - /// - public void ActivateDebugWindow(DebugWindowViewModel viewModel) { - if (_debugWindow is not null) { - _debugWindow.Activate(); - return; - } - _debugWindow = new DebugWindow { - DataContext = viewModel - }; - _debugWindow.Show(); - _debugWindow.Closed += (_, _) => _debugWindow = null; - } - - public void CloseDebugWindow() { - _debugWindow?.Close(); - } -} diff --git a/src/Spice86/Infrastructure/HostStorageProvider.cs b/src/Spice86/Infrastructure/HostStorageProvider.cs index 9d67e0bb1..c53a9dc10 100644 --- a/src/Spice86/Infrastructure/HostStorageProvider.cs +++ b/src/Spice86/Infrastructure/HostStorageProvider.cs @@ -59,6 +59,21 @@ public async Task SaveBitmapFile(WriteableBitmap bitmap) { } } } + + public async Task SaveBinaryFile(byte[] bytes) { + if (CanSave && CanPickFolder) { + FilePickerSaveOptions options = new() { + Title = "Save binary file", + SuggestedFileName = "dump.bin", + DefaultExtension = "bin", + SuggestedStartLocation = await TryGetWellKnownFolderAsync(WellKnownFolder.Documents) + }; + string? file = (await SaveFilePickerAsync(options))?.TryGetLocalPath(); + if (!string.IsNullOrWhiteSpace(file)) { + await File.WriteAllBytesAsync(file, bytes); + } + } + } public async Task DumpEmulatorStateToFile(Configuration configuration, IProgramExecutor programExecutor) { if (CanSave && CanPickFolder) { diff --git a/src/Spice86/Infrastructure/IDebugWindowActivator.cs b/src/Spice86/Infrastructure/IDebugWindowActivator.cs deleted file mode 100644 index a65037982..000000000 --- a/src/Spice86/Infrastructure/IDebugWindowActivator.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Spice86.Infrastructure; - -using Spice86.Core.Emulator; -using Spice86.Interfaces; -using Spice86.ViewModels; - -/// -/// Activates or closes the Debug window -/// -public interface IDebugWindowActivator { - /// - /// Activates the Window corresponding to the - /// - /// The viewModel for the debug view. - void ActivateDebugWindow(DebugWindowViewModel viewModel); - - /// - /// Closes the debug window - /// - void CloseDebugWindow(); -} diff --git a/src/Spice86/Infrastructure/IHostStorageProvider.cs b/src/Spice86/Infrastructure/IHostStorageProvider.cs index a8f29fe37..f204c2c92 100644 --- a/src/Spice86/Infrastructure/IHostStorageProvider.cs +++ b/src/Spice86/Infrastructure/IHostStorageProvider.cs @@ -82,4 +82,11 @@ public interface IHostStorageProvider { /// The directory of the last file picked, if any. /// The operation as an awaitable Task, containing the first picked file, or null. Task PickExecutableFile(string lastExecutableDirectory); + + /// + /// Spanws the file picker to save a binary file. + /// + /// The binary content of the file. + /// The operation as an awaitable Task. + Task SaveBinaryFile(byte[] bytes); } diff --git a/src/Spice86/Infrastructure/IWindowService.cs b/src/Spice86/Infrastructure/IWindowService.cs new file mode 100644 index 000000000..e99d1d7e6 --- /dev/null +++ b/src/Spice86/Infrastructure/IWindowService.cs @@ -0,0 +1,19 @@ +namespace Spice86.Infrastructure; + +using Spice86.ViewModels; + +/// +/// Service used for showing the debug window. +/// +public interface IWindowService { + /// + /// Shows the debug window. + /// + /// The used as DataContext in case the window needs to be created. + Task ShowDebugWindow(DebugWindowViewModel viewModel); + + /// + /// Close the debug window. + /// + void CloseDebugWindow(); +} \ No newline at end of file diff --git a/src/Spice86/Infrastructure/WindowService.cs b/src/Spice86/Infrastructure/WindowService.cs new file mode 100644 index 000000000..dde46da3f --- /dev/null +++ b/src/Spice86/Infrastructure/WindowService.cs @@ -0,0 +1,50 @@ +namespace Spice86.Infrastructure; + +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Threading; + +using Spice86.ViewModels; +using Spice86.Views; + +/// +public class WindowService : IWindowService { + public void CloseDebugWindow() { + Dispatcher.UIThread.Post(() => { + if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime lifetime) { + return; + } + DebugWindow? debugWindow = lifetime.Windows.FirstOrDefault(x => x is DebugWindow) as DebugWindow; + debugWindow?.Close(); + }); + } + + public async Task ShowDebugWindow(DebugWindowViewModel viewModel) { + await Dispatcher.UIThread.InvokeAsync(() => { + if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime lifetime) { + return; + } + + bool foundWindow = false; + IReadOnlyList windows = lifetime.Windows; + foreach (WindowBase window in windows) { + if (window.DataContext?.GetType() != viewModel?.GetType()) { + continue; + } + foundWindow = true; + window.Activate(); + break; + } + + if (foundWindow) { + return; + } + + WindowBase debugWindow = new DebugWindow { + DataContext = viewModel + }; + debugWindow.Show(); + }); + } +} \ No newline at end of file diff --git a/src/Spice86/MemoryWrappers/MemoryBinaryDocument.cs b/src/Spice86/MemoryWrappers/MemoryBinaryDocument.cs index 52300c45c..8d8b4e230 100644 --- a/src/Spice86/MemoryWrappers/MemoryBinaryDocument.cs +++ b/src/Spice86/MemoryWrappers/MemoryBinaryDocument.cs @@ -7,15 +7,22 @@ public class MemoryBinaryDocument : IBinaryDocument { private readonly IMemory _memory; - public MemoryBinaryDocument(IMemory memory) { - _memory = memory; + private readonly uint _startAddress; + private readonly uint _endAddress; + + public MemoryBinaryDocument(IMemory memory, uint startAddress, uint endAddress) { IsReadOnly = false; CanInsert = false; CanRemove = false; - ValidRanges = new MemoryReadOnlyBitRangeUnion(memory); + _startAddress = startAddress; + _endAddress = endAddress; + _memory = memory; + ValidRanges = new MemoryReadOnlyBitRangeUnion(0, _endAddress - _startAddress); } - public ulong Length => _memory.Length; + public event Action? MemoryReadInvalidOperation; + + public ulong Length => _endAddress - _startAddress; public bool IsReadOnly { get; } public bool CanInsert { get; } public bool CanRemove { get; } @@ -28,8 +35,11 @@ public void InsertBytes(ulong offset, ReadOnlySpan buffer) { } public void ReadBytes(ulong offset, Span buffer) { - for (int i = 0; i < buffer.Length; i++) { - buffer[i] = _memory[(uint)(offset + (uint)i)]; + try { + Span memRange = _memory.ReadRam((uint)buffer.Length, (uint)(_startAddress + offset)); + memRange.CopyTo(buffer); + } catch (InvalidOperationException e) { + MemoryReadInvalidOperation?.Invoke(e); } } @@ -38,8 +48,6 @@ public void RemoveBytes(ulong offset, ulong length) { } public void WriteBytes(ulong offset, ReadOnlySpan buffer) { - for (int i = 0; i < buffer.Length; i++) { - _memory[(uint)(offset + (uint)i)] = buffer[i]; - } + _memory.WriteRam(buffer.ToArray(), (uint)(_startAddress + offset)); } } \ No newline at end of file diff --git a/src/Spice86/MemoryWrappers/MemoryReadOnlyBitRangeUnion.cs b/src/Spice86/MemoryWrappers/MemoryReadOnlyBitRangeUnion.cs index 57b6ce66b..f147ad0b8 100644 --- a/src/Spice86/MemoryWrappers/MemoryReadOnlyBitRangeUnion.cs +++ b/src/Spice86/MemoryWrappers/MemoryReadOnlyBitRangeUnion.cs @@ -9,15 +9,23 @@ using System.Collections.Specialized; public class MemoryReadOnlyBitRangeUnion : IReadOnlyBitRangeUnion { - private readonly IMemory _memory; - - public MemoryReadOnlyBitRangeUnion(IMemory memory) { - _memory = memory; - EnclosingRange = new BitRange(0, _memory.Length * 8); + private readonly uint _startAddress; + private readonly uint _endAddress; + + /// + /// Initializes a new instance of the class. + /// + /// The start address of tha range of memory. + /// The end address of the range of memory. This end address is not included in the range. + public MemoryReadOnlyBitRangeUnion(uint startAddress, uint endAddress) { + _startAddress = startAddress; + _endAddress = endAddress; + // The endAddress is excluded from the range + EnclosingRange = new BitRange(startAddress, endAddress); } public BitRange EnclosingRange { get; } - public int Count => (int)_memory.Length; + public int Count => (int)(_endAddress - _startAddress); public event NotifyCollectionChangedEventHandler? CollectionChanged; @@ -27,8 +35,7 @@ public bool Contains(BitLocation location) { public BitRangeUnion.Enumerator GetEnumerator() { var bitRangeUnion = new BitRangeUnion(); - // Multiply by 8 to convert from bytes to bits - bitRangeUnion.Add(new BitRange(0, _memory.Length * 8)); + bitRangeUnion.Add(new BitRange(_startAddress, _endAddress)); return bitRangeUnion.GetEnumerator(); } @@ -41,7 +48,7 @@ public bool IsSuperSetOf(BitRange range) { } IEnumerator IEnumerable.GetEnumerator() { - for (uint i = 0; i < _memory.Length; i++) { + for (uint i = _startAddress; i < _endAddress; i++) { yield return EnclosingRange; } } diff --git a/src/Spice86/Program.cs b/src/Spice86/Program.cs index ad601e99d..8fc001efb 100644 --- a/src/Spice86/Program.cs +++ b/src/Spice86/Program.cs @@ -6,21 +6,19 @@ using Spice86.Core.CLI; using Spice86.Core.Emulator; -using Spice86.DependencyInjection; using Spice86.Shared.Interfaces; using Spice86.Infrastructure; using Avalonia.Threading; -using Spice86.Views; +using Microsoft.Extensions.DependencyInjection; + +using Spice86.Logging; using Spice86.ViewModels; /// /// Entry point for Spice86 application. /// public class Program { - private readonly ILoggerService _loggerService; - internal Program(ILoggerService loggerService) => _loggerService = loggerService; - /// /// Alternate entry point to use when injecting a class that defines C# overrides of the x86 assembly code found in the target DOS program. /// @@ -44,42 +42,39 @@ public class Program { [STAThread] public static void Main(string[] args) { Configuration configuration = CommandLineParser.ParseCommandLine(args); - Program program = new Composition().Resolve(); - program.StartApp(configuration, args); - } + + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + + ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); + ILoggerService loggerService = serviceProvider.GetRequiredService(); + Startup.SetLoggingLevel(loggerService, configuration); - private void StartApp(Configuration configuration, string[] args) { - Startup.SetLoggingLevel(_loggerService, configuration); if (!configuration.HeadlessMode) { - StartMainWindow(configuration, _loggerService, args); + AppBuilder appBuilder = BuildAvaloniaApp(); + ClassicDesktopStyleApplicationLifetime desktop = SetupWithClassicDesktopLifetime(appBuilder, args); + App? app = (App?)appBuilder.Instance; + + if (app is null) { + return; + } + + Views.MainWindow mainWindow = new(); + using var mainWindowViewModel = new MainWindowViewModel(new WindowService(), new AvaloniaKeyScanCodeConverter(), + new ProgramExecutorFactory(configuration, loggerService), + new UIDispatcher(Dispatcher.UIThread), new HostStorageProvider(mainWindow.StorageProvider), + new TextClipboard(mainWindow.Clipboard), new UIDispatcherTimerFactory(), configuration, loggerService); + mainWindow.DataContext = mainWindowViewModel; + desktop.MainWindow = mainWindow; + desktop.Start(args); } else { - StartConsole(configuration, _loggerService); - } - } - - private static void StartConsole(Configuration configuration, ILoggerService loggerService) { - ProgramExecutor programExecutor = new(configuration, loggerService, null); - programExecutor.Run(); - } - - private static void StartMainWindow(Configuration configuration, ILoggerService loggerService, string[] args) { - AppBuilder appBuilder = BuildAvaloniaApp(); - ClassicDesktopStyleApplicationLifetime desktop = SetupWithClassicDesktopLifetime(appBuilder, args); - App? app = (App?)appBuilder.Instance; - if (app is null) { - return; + ProgramExecutor programExecutor = new(configuration, loggerService, null); + programExecutor.Run(); } - MainWindow mainWindow = new(); - using var mainWindowViewModel = new MainWindowViewModel(new AvaloniaKeyScanCodeConverter(), - new ProgramExecutorFactory(configuration, loggerService), new DebugWindowActivator(), - new UIDispatcher(Dispatcher.UIThread), new HostStorageProvider(mainWindow.StorageProvider), - new TextClipboard(mainWindow.Clipboard), new UIDispatcherTimerFactory(), configuration, loggerService); - mainWindow.DataContext = mainWindowViewModel; - desktop.MainWindow = mainWindow; - desktop.Start(args); } - + /// /// Configures and builds an Avalonia application instance. /// diff --git a/src/Spice86/Spice86.csproj b/src/Spice86/Spice86.csproj index b64729d38..0242faaf8 100644 --- a/src/Spice86/Spice86.csproj +++ b/src/Spice86/Spice86.csproj @@ -44,6 +44,7 @@ + all @@ -58,10 +59,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/src/Spice86/UserControls/ErrorModalDialogUserControl.axaml b/src/Spice86/UserControls/ErrorModalDialogUserControl.axaml new file mode 100644 index 000000000..f7d157534 --- /dev/null +++ b/src/Spice86/UserControls/ErrorModalDialogUserControl.axaml @@ -0,0 +1,47 @@ + + + + + + + + + + +