Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/misc fixes #92

Merged
merged 4 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 32 additions & 14 deletions src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Monitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MonitorBase, ushort, ushort>? afterLoadCallback = null)
Expand All @@ -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)
Expand Down
101 changes: 68 additions & 33 deletions src/apps/Highbyte.DotNet6502.App.SkiaNative/SilkNetImGuiMenu.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Diagnostics;
using System.Numerics;
using AutoMapper;
using Highbyte.DotNet6502.App.SkiaNative.ConfigUI;
Expand All @@ -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;
Expand All @@ -33,7 +33,7 @@ public class SilkNetImGuiMenu : ISilkNetImGuiWindow
private bool _audioEnabled;
private float _audioVolumePercent;
private readonly IMapper _mapper;

private readonly ILogger<SilkNetImGuiMenu> _logger;
public bool C64KeyboardJoystickEnabled;
public int C64KeyboardJoystick;
public int C64SelectedJoystick;
Expand All @@ -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();
Expand All @@ -58,6 +60,8 @@ public SilkNetImGuiMenu(SilkNetWindow silkNetWindow, string defaultSystemName, b
_audioVolumePercent = defaultAudioVolumePercent;

_mapper = mapper;
_logger = loggerFactory.CreateLogger<SilkNetImGuiMenu>();


ISystemConfig systemConfig = GetSelectedSystemConfig();
if (systemConfig is C64Config c64Config)
Expand Down Expand Up @@ -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
{
Expand All @@ -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");
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -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)
Expand Down
55 changes: 40 additions & 15 deletions src/apps/Highbyte.DotNet6502.App.SkiaNative/SilkNetWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public float CanvasScale
private readonly PerSecondTimedStat _renderFps = InstrumentationBag.Add<PerSecondTimedStat>("SilkNetSkiaSharp-OnRenderFPS");

private const string CustomSystemStatNamePrefix = "Emulator-SystemTime-Custom-";
private Dictionary<string, ElapsedMillisecondsStat> _customSystemStats = new();
private const string CustomRenderStatNamePrefix = "SkiaSharp-RenderTime-Custom-";
private Dictionary<string, ElapsedMillisecondsStat> _customStats = new();

// SkipSharp context/surface/canvas
private SkiaRenderContext _skiaRenderContext;
Expand Down Expand Up @@ -150,7 +151,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();
Expand Down Expand Up @@ -236,30 +237,37 @@ public void SetCurrentSystem(string systemName)
Window.Size = new Vector2D<int>((int)(screen.VisibleWidth * CanvasScale), (int)(screen.VisibleHeight * CanvasScale));
Window.UpdatesPerSecond = screen.RefreshFrequencyHz;

InitCustomSystemStats(system);

InitRendering();
}
else
{
}
}

private void InitCustomSystemStats(ISystem system)
private void InitCustomSystemStats()
{
// Remove any existing custom system stats
foreach (var existingCustomSystemStatName in _customSystemStats.Keys)
foreach (var existingCustomStatName in _customStats.Keys)
{
if (existingCustomSystemStatName.StartsWith(CustomSystemStatNamePrefix))
if (existingCustomStatName.StartsWith(CustomSystemStatNamePrefix)
|| existingCustomStatName.StartsWith(CustomRenderStatNamePrefix))
{
InstrumentationBag.Remove(existingCustomSystemStatName);
_customSystemStats.Remove(existingCustomSystemStatName);
InstrumentationBag.Remove(existingCustomStatName);
_customStats.Remove(existingCustomStatName);
}
}
// Add any custom system stats for selected system
foreach (var customSystemStatName in system.DetailedStatNames)
var system = _systemRunner.System;
foreach (var customStatName in system.DetailedStatNames)
{
_customStats.Add($"{CustomSystemStatNamePrefix}{customStatName}", InstrumentationBag.Add<ElapsedMillisecondsStat>($"{CustomSystemStatNamePrefix}{customStatName}"));
}

// Add any custom system stats for selected renderer
var renderer = _systemRunner.Renderer;
foreach (var customStatName in renderer.DetailedStatNames)
{
_customSystemStats.Add($"{CustomSystemStatNamePrefix}{customSystemStatName}", InstrumentationBag.Add<ElapsedMillisecondsStat>($"{CustomSystemStatNamePrefix}{customSystemStatName}"));
_customStats.Add($"{CustomRenderStatNamePrefix}{customStatName}", InstrumentationBag.Add<ElapsedMillisecondsStat>($"{CustomRenderStatNamePrefix}{customStatName}"));
}
}

Expand All @@ -281,6 +289,8 @@ public void Start()
if (EmulatorState == EmulatorState.Uninitialized)
_systemRunner = _systemList.BuildSystemRunner(_currentSystemName).Result;

InitCustomSystemStats();

_monitor.Init(_systemRunner!);

_systemRunner!.AudioHandler.StartPlaying();
Expand Down Expand Up @@ -363,11 +373,11 @@ private void RunEmulator()
// TODO: Make custom system stats less messy?
foreach (var detailedStatName in detailedStats.Keys)
{
var statLookup = _customSystemStats.Keys.SingleOrDefault(x => x.EndsWith(detailedStatName));
var statLookup = _customStats.Keys.SingleOrDefault(x => x.EndsWith(detailedStatName));
if (statLookup != null)
{
_customSystemStats[$"{CustomSystemStatNamePrefix}{detailedStatName}"].Set(detailedStats[detailedStatName]);
_customSystemStats[$"{CustomSystemStatNamePrefix}{detailedStatName}"].UpdateStat();
_customStats[$"{CustomSystemStatNamePrefix}{detailedStatName}"].Set(detailedStats[detailedStatName]);
_customStats[$"{CustomSystemStatNamePrefix}{detailedStatName}"].UpdateStat();
}
}
}
Expand Down Expand Up @@ -408,13 +418,28 @@ private void RenderEmulator(double deltaTime)
using (_renderTime.Measure())
{
// Render emulator system screen
_systemRunner!.Draw();
_systemRunner!.Draw(out Dictionary<string, double> detailedStats);

// Flush the Skia Context
_skiaRenderContext.GetGRContext().Flush();

// Update custom system stats
// TODO: Make custom system stats less messy?
foreach (var detailedStatName in detailedStats.Keys)
{
var statLookup = _customStats.Keys.SingleOrDefault(x => x.EndsWith(detailedStatName));
if (statLookup != null)
{
_customStats[$"{CustomRenderStatNamePrefix}{detailedStatName}"].Set(detailedStats[detailedStatName]);
_customStats[$"{CustomRenderStatNamePrefix}{detailedStatName}"].UpdateStat();
}
}

}
emulatorRendered = true;



// SilkNet windows are what's known as "double-buffered". In essence, the window manages two buffers.
// One is rendered to while the other is currently displayed by the window.
// This avoids screen tearing, a visual artifact that can happen if the buffer is modified while being displayed.
Expand Down
Loading