From b04d63b981abb9a64d2d2f074067d81457be95cf Mon Sep 17 00:00:00 2001 From: Highbyte Date: Mon, 23 Oct 2023 16:45:38 +0200 Subject: [PATCH 1/3] Error handling on file load and save --- .../Monitor.cs | 46 ++-- .../SilkNetImGuiMenu.cs | 101 +++++--- .../SilkNetWindow.cs | 2 +- .../SkiaNativeMonitor.cs | 49 ++-- .../Pages/Commodore64/C64Menu.razor | 220 +++++++++++------- .../Skia/WasmMonitor.cs | 59 +++-- .../MemoryHelperTest.cs | 15 ++ 7 files changed, 327 insertions(+), 165 deletions(-) diff --git a/src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Monitor.cs b/src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Monitor.cs index afaa7574..025f09e5 100644 --- a/src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Monitor.cs +++ b/src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Monitor.cs @@ -25,13 +25,24 @@ public override bool LoadBinary(string fileName, out ushort loadedAtAddress, out return false; } - BinaryLoader.Load( - Mem, - fileName, - out loadedAtAddress, - out fileLength, - forceLoadAddress); - return true; + try + { + BinaryLoader.Load( + Mem, + fileName, + out loadedAtAddress, + out fileLength, + forceLoadAddress); + return true; + } + catch (Exception ex) + { + WriteOutput($"Load error: {ex.Message}", MessageSeverity.Error); + loadedAtAddress = 0; + fileLength = 0; + return false; + } + } public override bool LoadBinary(out ushort loadedAtAddress, out ushort fileLength, ushort? forceLoadAddress = null, Action? afterLoadCallback = null) @@ -44,14 +55,21 @@ public override bool LoadBinary(out ushort loadedAtAddress, out ushort fileLengt public override void SaveBinary(string fileName, ushort startAddress, ushort endAddress, bool addFileHeaderWithLoadAddress) { - BinarySaver.Save( - Mem, - fileName, - startAddress, - endAddress, - addFileHeaderWithLoadAddress: addFileHeaderWithLoadAddress); + try + { + BinarySaver.Save( + Mem, + fileName, + startAddress, + endAddress, + addFileHeaderWithLoadAddress: addFileHeaderWithLoadAddress); + WriteOutput($"Program saved to {fileName}"); - WriteOutput($"Program saved to {fileName}"); + } + catch (Exception ex) + { + WriteOutput($"Save error: {ex.Message}", MessageSeverity.Error); + } } public override void WriteOutput(string message) diff --git a/src/apps/Highbyte.DotNet6502.App.SkiaNative/SilkNetImGuiMenu.cs b/src/apps/Highbyte.DotNet6502.App.SkiaNative/SilkNetImGuiMenu.cs index 81611b3f..4cb3144c 100644 --- a/src/apps/Highbyte.DotNet6502.App.SkiaNative/SilkNetImGuiMenu.cs +++ b/src/apps/Highbyte.DotNet6502.App.SkiaNative/SilkNetImGuiMenu.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Numerics; using AutoMapper; using Highbyte.DotNet6502.App.SkiaNative.ConfigUI; @@ -7,6 +6,7 @@ using Highbyte.DotNet6502.Systems.Commodore64; using Highbyte.DotNet6502.Systems.Commodore64.Config; using Highbyte.DotNet6502.Systems.Generic.Config; +using Microsoft.Extensions.Logging; using NativeFileDialogSharp; namespace Highbyte.DotNet6502.App.SkiaNative; @@ -33,7 +33,7 @@ public class SilkNetImGuiMenu : ISilkNetImGuiWindow private bool _audioEnabled; private float _audioVolumePercent; private readonly IMapper _mapper; - + private readonly ILogger _logger; public bool C64KeyboardJoystickEnabled; public int C64KeyboardJoystick; public int C64SelectedJoystick; @@ -47,7 +47,9 @@ public class SilkNetImGuiMenu : ISilkNetImGuiWindow private SilkNetImGuiC64Config? _c64ConfigUI; private SilkNetImGuiGenericComputerConfig? _genericComputerConfigUI; - public SilkNetImGuiMenu(SilkNetWindow silkNetWindow, string defaultSystemName, bool defaultAudioEnabled, float defaultAudioVolumePercent, IMapper mapper) + private string _lastFileError = ""; + + public SilkNetImGuiMenu(SilkNetWindow silkNetWindow, string defaultSystemName, bool defaultAudioEnabled, float defaultAudioVolumePercent, IMapper mapper, ILoggerFactory loggerFactory) { _silkNetWindow = silkNetWindow; _screenScaleString = silkNetWindow.CanvasScale.ToString(); @@ -58,6 +60,8 @@ public SilkNetImGuiMenu(SilkNetWindow silkNetWindow, string defaultSystemName, b _audioVolumePercent = defaultAudioVolumePercent; _mapper = mapper; + _logger = loggerFactory.CreateLogger(); + ISystemConfig systemConfig = GetSelectedSystemConfig(); if (systemConfig is C64Config c64Config) @@ -214,19 +218,27 @@ public void PostOnRender() _silkNetWindow.Pause(); } + _lastFileError = ""; var dialogResult = Dialog.FileOpen(@"prg;*"); if (dialogResult.IsOk) { - var fileName = dialogResult.Path; - BinaryLoader.Load( - _silkNetWindow.SystemRunner.System.Mem, - fileName, - out ushort loadedAtAddress, - out ushort fileLength); + try + { + var fileName = dialogResult.Path; + BinaryLoader.Load( + _silkNetWindow.SystemRunner.System.Mem, + fileName, + out ushort loadedAtAddress, + out ushort fileLength); - _silkNetWindow.SystemRunner.System.CPU.PC = loadedAtAddress; + _silkNetWindow.SystemRunner.System.CPU.PC = loadedAtAddress; - _silkNetWindow.Start(); + _silkNetWindow.Start(); + } + catch (Exception ex) + { + _lastFileError = ex.Message; + } } else { @@ -250,6 +262,13 @@ public void PostOnRender() } } + if (!string.IsNullOrEmpty(_lastFileError)) + { + ImGui.PushStyleColor(ImGuiCol.Text, s_errorColor); + ImGui.Text($"File error: {_lastFileError}"); + ImGui.PopStyleColor(); + } + ImGui.PushStyleColor(ImGuiCol.Text, s_warningColor); ImGui.Text("Toggle menu with F6"); ImGui.Text("Toggle monitor with F12"); @@ -325,25 +344,33 @@ private void DrawC64Config() _silkNetWindow.Pause(); } _silkNetWindow.Pause(); + _lastFileError = ""; var dialogResult = Dialog.FileOpen(@"prg;*"); if (dialogResult.IsOk) { - var fileName = dialogResult.Path; - BinaryLoader.Load( - _silkNetWindow.SystemRunner.System.Mem, - fileName, - out ushort loadedAtAddress, - out ushort fileLength); - - if (loadedAtAddress != C64.BASIC_LOAD_ADDRESS) + try { - // Probably not a Basic program that was loaded. Don't init BASIC memory variables. - Debug.WriteLine($"Warning: Loaded program is not a Basic program, it's expected to load at {C64.BASIC_LOAD_ADDRESS.ToHex()} but was loaded at {loadedAtAddress.ToHex()}"); + var fileName = dialogResult.Path; + BinaryLoader.Load( + _silkNetWindow.SystemRunner.System.Mem, + fileName, + out ushort loadedAtAddress, + out ushort fileLength); + + if (loadedAtAddress != C64.BASIC_LOAD_ADDRESS) + { + // Probably not a Basic program that was loaded. Don't init BASIC memory variables. + _logger.LogWarning($"Warning: Loaded program is not a Basic program, it's expected to load at {C64.BASIC_LOAD_ADDRESS.ToHex()} but was loaded at {loadedAtAddress.ToHex()}"); + } + else + { + // Init C64 BASIC memory variables + ((C64)_silkNetWindow.SystemRunner.System).InitBasicMemoryVariables(loadedAtAddress, fileLength); + } } - else + catch (Exception ex) { - // Init C64 BASIC memory variables - ((C64)_silkNetWindow.SystemRunner.System).InitBasicMemoryVariables(loadedAtAddress, fileLength); + _lastFileError = ex.Message; } } @@ -362,18 +389,26 @@ private void DrawC64Config() _silkNetWindow.Pause(); } _silkNetWindow.Pause(); + _lastFileError = ""; var dialogResult = Dialog.FileSave(@"prg;*"); if (dialogResult.IsOk) { - var fileName = dialogResult.Path; - ushort startAddressValue = C64.BASIC_LOAD_ADDRESS; - var endAddressValue = ((C64)_silkNetWindow.SystemRunner.System).GetBasicProgramEndAddress(); - BinarySaver.Save( - _silkNetWindow.SystemRunner.System.Mem, - fileName, - startAddressValue, - endAddressValue, - addFileHeaderWithLoadAddress: true); + try + { + var fileName = dialogResult.Path; + ushort startAddressValue = C64.BASIC_LOAD_ADDRESS; + var endAddressValue = ((C64)_silkNetWindow.SystemRunner.System).GetBasicProgramEndAddress(); + BinarySaver.Save( + _silkNetWindow.SystemRunner.System.Mem, + fileName, + startAddressValue, + endAddressValue, + addFileHeaderWithLoadAddress: true); + } + catch (Exception ex) + { + _lastFileError = ex.Message; + } } if (wasRunning) diff --git a/src/apps/Highbyte.DotNet6502.App.SkiaNative/SilkNetWindow.cs b/src/apps/Highbyte.DotNet6502.App.SkiaNative/SilkNetWindow.cs index 6c54eda0..b0a077df 100644 --- a/src/apps/Highbyte.DotNet6502.App.SkiaNative/SilkNetWindow.cs +++ b/src/apps/Highbyte.DotNet6502.App.SkiaNative/SilkNetWindow.cs @@ -150,7 +150,7 @@ protected void OnLoad() InitImGui(); // Init main menu UI - _menu = new SilkNetImGuiMenu(this, _emulatorConfig.DefaultEmulator, _defaultAudioEnabled, _defaultAudioVolumePercent, _mapper); + _menu = new SilkNetImGuiMenu(this, _emulatorConfig.DefaultEmulator, _defaultAudioEnabled, _defaultAudioVolumePercent, _mapper, _loggerFactory); // Create other UI windows _statsPanel = CreateStatsUI(); diff --git a/src/apps/Highbyte.DotNet6502.App.SkiaNative/SkiaNativeMonitor.cs b/src/apps/Highbyte.DotNet6502.App.SkiaNative/SkiaNativeMonitor.cs index 4608ba52..e005c360 100644 --- a/src/apps/Highbyte.DotNet6502.App.SkiaNative/SkiaNativeMonitor.cs +++ b/src/apps/Highbyte.DotNet6502.App.SkiaNative/SkiaNativeMonitor.cs @@ -31,14 +31,24 @@ public override bool LoadBinary(string fileName, out ushort loadedAtAddress, out return false; } - BinaryLoader.Load( - Mem, - fileName, - out loadedAtAddress, - out fileLength, - forceLoadAddress); - - return true; + try + { + BinaryLoader.Load( + Mem, + fileName, + out loadedAtAddress, + out fileLength, + forceLoadAddress); + + return true; + } + catch (Exception ex) + { + WriteOutput($"Load error: {ex.Message}", MessageSeverity.Error); + loadedAtAddress = 0; + fileLength = 0; + return false; + } } public override bool LoadBinary(out ushort loadedAtAddress, out ushort fileLength, ushort? forceLoadAddress = null, Action? afterLoadCallback = null) @@ -71,14 +81,21 @@ public override void SaveBinary(string fileName, ushort startAddress, ushort end if (!Path.IsPathFullyQualified(fileName)) fileName = $"{_monitorConfig.DefaultDirectory}/{fileName}"; - BinarySaver.Save( - Mem, - fileName, - startAddress, - endAddress, - addFileHeaderWithLoadAddress: addFileHeaderWithLoadAddress); - - WriteOutput($"Program saved to {fileName}"); + try + { + BinarySaver.Save( + Mem, + fileName, + startAddress, + endAddress, + addFileHeaderWithLoadAddress: addFileHeaderWithLoadAddress); + + WriteOutput($"Program saved to {fileName}"); + } + catch (Exception ex) + { + WriteOutput($"Save error: {ex.Message}", MessageSeverity.Error); + } } public override void WriteOutput(string message) diff --git a/src/apps/Highbyte.DotNet6502.App.SkiaWASM/Pages/Commodore64/C64Menu.razor b/src/apps/Highbyte.DotNet6502.App.SkiaWASM/Pages/Commodore64/C64Menu.razor index 41647567..093b58ff 100644 --- a/src/apps/Highbyte.DotNet6502.App.SkiaWASM/Pages/Commodore64/C64Menu.razor +++ b/src/apps/Highbyte.DotNet6502.App.SkiaWASM/Pages/Commodore64/C64Menu.razor @@ -52,6 +52,11 @@ +
+ @_latestFileError +
+ +
@@ -77,13 +82,22 @@ @code { @inject IJSRuntime Js @inject HttpClient? HttpClient + @inject ILoggerFactory LoggerFactory + private ILogger _logger; + private string _latestFileError = ""; private string SYSTEM_NAME = C64.SystemName; // Note: The current config object (reference) is stored in this variable so that the UI can bind it's properties (not possible to use async call to _systemList.GetSystemConfig() in property ) private C64Config? _c64Config => Parent.SystemConfig as C64Config; private C64HostConfig? _c64HostConfig => Parent.HostSystemConfig as C64HostConfig; + protected override async Task OnInitializedAsync() + { + _logger = LoggerFactory.CreateLogger(); + _logger.LogDebug("OnInitializedAsync() was called"); + } + private bool JoystickKeyboardEnabled { get @@ -192,26 +206,37 @@ if (e.FileCount > 1) return; var file = e.File; - System.Diagnostics.Debug.WriteLine($"File picked: {file.Name} Size: {file.Size}"); + _logger.LogInformation($"File picked: {file.Name} Size: {file.Size}"); - var fileBuffer = new byte[file.Size]; - //var fileStream = e.File.OpenReadStream(file.Size); - await file.OpenReadStream().ReadAsync(fileBuffer); - var fileSize = fileBuffer.Length; - // Load file into memory, assume starting at address specified in two first bytes of .prg file - BinaryLoader.Load( - Parent.WasmHost.SystemRunner.System.Mem, - fileBuffer, - out ushort loadedAtAddress, - out ushort fileLength); + try + { + _latestFileError = ""; + var fileBuffer = new byte[file.Size]; + //var fileStream = e.File.OpenReadStream(file.Size); + await file.OpenReadStream().ReadAsync(fileBuffer); + var fileSize = fileBuffer.Length; - System.Diagnostics.Debug.WriteLine($"File loaded at {loadedAtAddress.ToHex()}, length {fileLength.ToHex()}"); + // Load file into memory, assume starting at address specified in two first bytes of .prg file + BinaryLoader.Load( + Parent.WasmHost.SystemRunner.System.Mem, + fileBuffer, + out ushort loadedAtAddress, + out ushort fileLength); - System.Diagnostics.Debug.WriteLine($"Starting loaded program by changing Program Counter to {loadedAtAddress.ToHex()}"); - Parent.WasmHost.SystemRunner.System.CPU.PC = loadedAtAddress; + _logger.LogInformation($"File loaded at {loadedAtAddress.ToHex()}, length {fileLength.ToHex()}"); - await Parent.OnStart(new()); + _logger.LogInformation($"Starting loaded program by changing Program Counter to {loadedAtAddress.ToHex()}"); + Parent.WasmHost.SystemRunner.System.CPU.PC = loadedAtAddress; + + await Parent.OnStart(new()); + + } + catch (Exception ex) + { + _latestFileError = $"Load error: {ex.Message}"; + _logger.LogError($"Load error: {ex.Message}"); + } } /// @@ -241,33 +266,42 @@ if (e.FileCount > 1) return; var file = e.File; - System.Diagnostics.Debug.WriteLine($"File picked: {file.Name} Size: {file.Size}"); - - var fileBuffer = new byte[file.Size]; - //var fileStream = e.File.OpenReadStream(file.Size); - await file.OpenReadStream().ReadAsync(fileBuffer); - var fileSize = fileBuffer.Length; + _logger.LogInformation($"File picked: {file.Name} Size: {file.Size}"); - // Load file into memory, assume starting at address specified in two first bytes of .prg file - BinaryLoader.Load( - Parent.WasmHost.SystemRunner.System.Mem, - fileBuffer, - out ushort loadedAtAddress, - out ushort fileLength); - - if (loadedAtAddress != C64.BASIC_LOAD_ADDRESS) + try { - // Probably not a Basic program that was loaded. Don't init BASIC memory variables. - System.Diagnostics.Debug.WriteLine($"Warning: Loaded program is not a Basic program, it's expected to load at {C64.BASIC_LOAD_ADDRESS.ToHex()} but was loaded at {loadedAtAddress.ToHex()}"); + _latestFileError = ""; + var fileBuffer = new byte[file.Size]; + //var fileStream = e.File.OpenReadStream(file.Size); + await file.OpenReadStream().ReadAsync(fileBuffer); + var fileSize = fileBuffer.Length; + + // Load file into memory, assume starting at address specified in two first bytes of .prg file + BinaryLoader.Load( + Parent.WasmHost.SystemRunner.System.Mem, + fileBuffer, + out ushort loadedAtAddress, + out ushort fileLength); + + if (loadedAtAddress != C64.BASIC_LOAD_ADDRESS) + { + // Probably not a Basic program that was loaded. Don't init BASIC memory variables. + _logger.LogWarning($"Warning: Loaded program is not a Basic program, it's expected to load at {C64.BASIC_LOAD_ADDRESS.ToHex()} but was loaded at {loadedAtAddress.ToHex()}"); + } + else + { + // Init C64 BASIC memory variables + ((C64)Parent.WasmHost.SystemRunner.System).InitBasicMemoryVariables(loadedAtAddress, fileLength); + } + + _logger.LogInformation($"Basic program loaded at {loadedAtAddress.ToHex()}, length {fileLength.ToHex()}"); } - else + catch (Exception ex) { - // Init C64 BASIC memory variables - ((C64)Parent.WasmHost.SystemRunner.System).InitBasicMemoryVariables(loadedAtAddress, fileLength); + _latestFileError = $"Load error: {ex.Message}"; + _logger.LogError($"Load error: {ex.Message}"); } - System.Diagnostics.Debug.WriteLine($"Basic program loaded at {loadedAtAddress.ToHex()}, length {fileLength.ToHex()}"); - if (_wasRunningBeforeFileDialog) await Parent.OnStart(new()); } @@ -291,13 +325,22 @@ if (string.IsNullOrEmpty(ext)) fileName += ".prg"; - var startAddress = C64.BASIC_LOAD_ADDRESS; - var endAddress = ((C64)Parent.WasmHost.SystemRunner.System).GetBasicProgramEndAddress(); - var saveData = BinarySaver.BuildSaveData(Parent.WasmHost.SystemRunner.System.Mem, startAddress, endAddress, addFileHeaderWithLoadAddress: true); - var fileStream = new MemoryStream(saveData); - using var streamRef = new DotNetStreamReference(stream: fileStream); - // Invoke JS helper script to trigger save dialog to users browser downloads folder - await Js.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef); + try + { + _latestFileError = ""; + var startAddress = C64.BASIC_LOAD_ADDRESS; + var endAddress = ((C64)Parent.WasmHost.SystemRunner.System).GetBasicProgramEndAddress(); + var saveData = BinarySaver.BuildSaveData(Parent.WasmHost.SystemRunner.System.Mem, startAddress, endAddress, addFileHeaderWithLoadAddress: true); + var fileStream = new MemoryStream(saveData); + using var streamRef = new DotNetStreamReference(stream: fileStream); + // Invoke JS helper script to trigger save dialog to users browser downloads folder + await Js.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef); + } + catch (Exception ex) + { + _latestFileError = $"Save error: {ex.Message}"; + _logger.LogError($"Save error: {ex.Message}"); + } } } @@ -315,18 +358,27 @@ await Parent.OnPause(new()); - var prgBytes = await HttpClient!.GetByteArrayAsync(url); - // Load file into memory, assume starting at address specified in two first bytes of .prg file - BinaryLoader.Load( - Parent.WasmHost.SystemRunner.System.Mem, - prgBytes, - out ushort loadedAtAddress, - out ushort fileLength); - - System.Diagnostics.Debug.WriteLine($"File loaded at {loadedAtAddress.ToHex()}, length {fileLength.ToHex()}"); - - System.Diagnostics.Debug.WriteLine($"Starting loaded program by changing Program Counter to {loadedAtAddress.ToHex()}"); - Parent.WasmHost.SystemRunner.System.CPU.PC = loadedAtAddress; + try + { + _latestFileError = ""; + var prgBytes = await HttpClient!.GetByteArrayAsync(url); + // Load file into memory, assume starting at address specified in two first bytes of .prg file + BinaryLoader.Load( + Parent.WasmHost.SystemRunner.System.Mem, + prgBytes, + out ushort loadedAtAddress, + out ushort fileLength); + + _logger.LogInformation($"File loaded at {loadedAtAddress.ToHex()}, length {fileLength.ToHex()}"); + + _logger.LogInformation($"Starting loaded program by changing Program Counter to {loadedAtAddress.ToHex()}"); + Parent.WasmHost.SystemRunner.System.CPU.PC = loadedAtAddress; + } + catch (Exception ex) + { + _latestFileError = $"Load error: {ex.Message}"; + _logger.LogError($"Load error: {ex.Message}"); + } await Parent.OnStart(new()); } @@ -344,36 +396,46 @@ await Parent.OnPause(new()); - var prgBytes = await HttpClient!.GetByteArrayAsync(url); + try + { + _latestFileError = ""; + var prgBytes = await HttpClient!.GetByteArrayAsync(url); + + // Load file into memory, assume starting at address specified in two first bytes of .prg file + BinaryLoader.Load( + Parent.WasmHost.SystemRunner.System.Mem, + prgBytes, + out ushort loadedAtAddress, + out ushort fileLength); + + var c64 = (C64)Parent.WasmHost.SystemRunner.System; + if (loadedAtAddress != C64.BASIC_LOAD_ADDRESS) + { + // Probably not a Basic program that was loaded. Don't init BASIC memory variables. + System.Diagnostics.Debug.WriteLine($"Warning: Loaded program is not a Basic program, it's expected to load at {C64.BASIC_LOAD_ADDRESS.ToHex()} but was loaded at {loadedAtAddress.ToHex()}"); + } + else + { + // Init C64 BASIC memory variables + c64.InitBasicMemoryVariables(loadedAtAddress, fileLength); + } - // Load file into memory, assume starting at address specified in two first bytes of .prg file - BinaryLoader.Load( - Parent.WasmHost.SystemRunner.System.Mem, - prgBytes, - out ushort loadedAtAddress, - out ushort fileLength); + // Send "list" + Enter to the keyboard buffer to immediately list the loaded program + var c64Keyboard = c64.Cia.Keyboard; + // Bypass keyboard matrix scanning and send directly to keyboard buffer? + c64Keyboard.InsertPetsciiCharIntoBuffer(Petscii.CharToPetscii['l']); + c64Keyboard.InsertPetsciiCharIntoBuffer(Petscii.CharToPetscii['i']); + c64Keyboard.InsertPetsciiCharIntoBuffer(Petscii.CharToPetscii['s']); + c64Keyboard.InsertPetsciiCharIntoBuffer(Petscii.CharToPetscii['t']); + c64Keyboard.InsertPetsciiCharIntoBuffer(Petscii.CharToPetscii[(char)13]); - var c64 = (C64)Parent.WasmHost.SystemRunner.System; - if (loadedAtAddress != C64.BASIC_LOAD_ADDRESS) - { - // Probably not a Basic program that was loaded. Don't init BASIC memory variables. - System.Diagnostics.Debug.WriteLine($"Warning: Loaded program is not a Basic program, it's expected to load at {C64.BASIC_LOAD_ADDRESS.ToHex()} but was loaded at {loadedAtAddress.ToHex()}"); } - else + catch (Exception ex) { - // Init C64 BASIC memory variables - c64.InitBasicMemoryVariables(loadedAtAddress, fileLength); + _latestFileError = $"Load error: {ex.Message}"; + _logger.LogError($"Load error: {ex.Message}"); } - // Send "list" + Enter to the keyboard buffer to immediately list the loaded program - var c64Keyboard = c64.Cia.Keyboard; - // Bypass keyboard matrix scanning and send directly to keyboard buffer? - c64Keyboard.InsertPetsciiCharIntoBuffer(Petscii.CharToPetscii['l']); - c64Keyboard.InsertPetsciiCharIntoBuffer(Petscii.CharToPetscii['i']); - c64Keyboard.InsertPetsciiCharIntoBuffer(Petscii.CharToPetscii['s']); - c64Keyboard.InsertPetsciiCharIntoBuffer(Petscii.CharToPetscii['t']); - c64Keyboard.InsertPetsciiCharIntoBuffer(Petscii.CharToPetscii[(char)13]); - await Parent.OnStart(new()); } } \ No newline at end of file diff --git a/src/apps/Highbyte.DotNet6502.App.SkiaWASM/Skia/WasmMonitor.cs b/src/apps/Highbyte.DotNet6502.App.SkiaWASM/Skia/WasmMonitor.cs index 6ad8decb..7fde53c1 100644 --- a/src/apps/Highbyte.DotNet6502.App.SkiaWASM/Skia/WasmMonitor.cs +++ b/src/apps/Highbyte.DotNet6502.App.SkiaWASM/Skia/WasmMonitor.cs @@ -180,39 +180,54 @@ public override bool LoadBinary(out ushort loadedAtAddress, out ushort fileLengt /// public async Task LoadBinaryFromUser(byte[] fileData) { - BinaryLoader.Load( - Mem, - fileData, - out ushort loadedAtAddress, - out ushort fileLength, - _lastTriggeredLoadBinaryForceLoadAddress); + try + { + BinaryLoader.Load( + Mem, + fileData, + out ushort loadedAtAddress, + out ushort fileLength, + _lastTriggeredLoadBinaryForceLoadAddress); - WriteOutput($"File loaded at {loadedAtAddress.ToHex()}, length {fileLength.ToHex()}"); + WriteOutput($"File loaded at {loadedAtAddress.ToHex()}, length {fileLength.ToHex()}"); - // Set PC to start of loaded file. - Cpu.PC = loadedAtAddress; + // Set PC to start of loaded file. + Cpu.PC = loadedAtAddress; - if (_lastTriggeredAfterLoadCallback != null) - _lastTriggeredAfterLoadCallback(this, loadedAtAddress, fileLength); + if (_lastTriggeredAfterLoadCallback != null) + _lastTriggeredAfterLoadCallback(this, loadedAtAddress, fileLength); - DisplayStatus(); + DisplayStatus(); + + } + catch (Exception ex) + { + WriteOutput($"Load error: {ex.Message}", MessageSeverity.Error); + } } public override async void SaveBinary(string fileName, ushort startAddress, ushort endAddress, bool addFileHeaderWithLoadAddress) { - // Ensure file has .prg extension if not specfied. When saving by issuing a browser file download, and saving a file with no extension, the browser will add .txt extension. - string ext = Path.GetExtension(fileName); - if (string.IsNullOrEmpty(ext)) - fileName += ".prg"; + try + { + // Ensure file has .prg extension if not specfied. When saving by issuing a browser file download, and saving a file with no extension, the browser will add .txt extension. + string ext = Path.GetExtension(fileName); + if (string.IsNullOrEmpty(ext)) + fileName += ".prg"; - var saveData = BinarySaver.BuildSaveData(Mem, startAddress, endAddress, addFileHeaderWithLoadAddress); - var fileStream = new MemoryStream(saveData); - using var streamRef = new DotNetStreamReference(stream: fileStream); + var saveData = BinarySaver.BuildSaveData(Mem, startAddress, endAddress, addFileHeaderWithLoadAddress); + var fileStream = new MemoryStream(saveData); + using var streamRef = new DotNetStreamReference(stream: fileStream); - // Invoke JS helper script to trigger save dialog to users browser downloads folder - await _jsRuntime.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef); + // Invoke JS helper script to trigger save dialog to users browser downloads folder + await _jsRuntime.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef); - WriteOutput($"Program downloaded to {fileName}"); + WriteOutput($"Program downloaded to {fileName}"); + } + catch (Exception ex) + { + WriteOutput($"Save error: {ex.Message}", MessageSeverity.Error); + } } public override void WriteOutput(string message) diff --git a/tests/Highbyte.DotNet6502.Tests/MemoryHelperTest.cs b/tests/Highbyte.DotNet6502.Tests/MemoryHelperTest.cs index 289011a7..ccb06a86 100644 --- a/tests/Highbyte.DotNet6502.Tests/MemoryHelperTest.cs +++ b/tests/Highbyte.DotNet6502.Tests/MemoryHelperTest.cs @@ -68,6 +68,21 @@ public void StoreData_Throws_Exception_If_Address_And_Data_Length_Exceeds_64K() Assert.Contains("exceeds maximum memory limit", ex.Message); } + [Fact] + public void StoreData_Throws_Exception_If_Data_Length_Exceeds_64K() + { + // Arrange + var data = new byte[(64 * 1024) + 1]; + + ushort address = 0x0; + var mem = new Memory(); + + // Act / Assert + var ex = Assert.Throws(() => mem.StoreData(address, data)); + + Assert.Contains("exceeds maximum memory limit", ex.Message); + } + [Fact] public void ReadData_Loads_Data_From_Memory() From 624681b864518ba2a139cdc37e4742e032a6d1ea Mon Sep 17 00:00:00 2001 From: Highbyte Date: Mon, 23 Oct 2023 17:07:34 +0200 Subject: [PATCH 2/3] Fix size of WASM C64Config modal. --- .../Pages/Commodore64/C64ConfigUI.razor | 103 +++++++++--------- .../wwwroot/css/app.css | 5 +- 2 files changed, 57 insertions(+), 51 deletions(-) diff --git a/src/apps/Highbyte.DotNet6502.App.SkiaWASM/Pages/Commodore64/C64ConfigUI.razor b/src/apps/Highbyte.DotNet6502.App.SkiaWASM/Pages/Commodore64/C64ConfigUI.razor index 81016a94..314d1573 100644 --- a/src/apps/Highbyte.DotNet6502.App.SkiaWASM/Pages/Commodore64/C64ConfigUI.razor +++ b/src/apps/Highbyte.DotNet6502.App.SkiaWASM/Pages/Commodore64/C64ConfigUI.razor @@ -6,63 +6,66 @@ @using static Highbyte.DotNet6502.Systems.Commodore64.TimerAndPeripheral.C64Joystick; @using static Highbyte.DotNet6502.App.SkiaWASM.Pages.Index -

ROMs

+

ROMs

The C64 system requires the following types of ROM files: Kernal, Basic, and Character generator.

-
-

1. Use existing C64 ROM files on your computer, or download them to your computer from example here

- - -
-

2. Upload the ROM files from your computer to this emulator with the button below.

- -
- public void Draw() + public void Draw(out Dictionary detailedStats) { - _renderer?.Draw(_system); + detailedStats = new() + { + }; + + _renderer?.Draw(_system, detailedStats); } /// diff --git a/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs b/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs index 9f603eb2..8c13c188 100644 --- a/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs +++ b/tests/Highbyte.DotNet6502.Tests/Systems/SystemRunnerBuilderTests.cs @@ -130,10 +130,10 @@ public ExecEvaluatorTriggerResult ExecuteOneInstruction(SystemRunner systemRunne public class TestRenderer : IRenderer { - public void Draw(TestSystem system) + public void Draw(TestSystem system, Dictionary detailedStats) { } - public void Draw(ISystem system) + public void Draw(ISystem system, Dictionary detailedStats) { } public void Init(TestSystem system, IRenderContext renderContext) @@ -142,15 +142,21 @@ public void Init(TestSystem system, IRenderContext renderContext) public void Init(ISystem system, IRenderContext renderContext) { } + + public bool HasDetailedStats => false; + public List DetailedStatNames => new List(); } public class TestRendererNonGeneric : IRenderer { - public void Draw(ISystem system) + public void Draw(ISystem system, Dictionary detailedStats) { } public void Init(ISystem system, IRenderContext renderContext) { } + + public bool HasDetailedStats => false; + public List DetailedStatNames => new List(); } public class TestInputHandler : IInputHandler