+
+[![.NET](https://img.shields.io/badge/.NET-8.0-512BD4)](doc/DEVELOP.md)
+[![language](https://img.shields.io/badge/language-C%23-239120)](doc/DEVELOP.md)
+[![OS](https://img.shields.io/badge/OS-windows%2C%20macOS%2C%20linux-0078D4)](doc/DEVELOP.md)
+[![WebAssembly](https://img.shields.io/badge/WebAssembly-654FF0?logo=webassembly&logoColor=fff)](#)
+[![SonarCloud Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=highbyte_dotnet-6502&metric=alert_status)](https://sonarcloud.io/dashboard?id=highbyte_dotnet-6502)
+[![SonarCloud Security Rating](https://sonarcloud.io/api/project_badges/measure?project=highbyte_dotnet-6502&metric=security_rating)](https://sonarcloud.io/dashboard?id=highbyte_dotnet-6502)
+[![SonarCloud Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=highbyte_dotnet-6502&metric=vulnerabilities)](https://sonarcloud.io/project/issues?id=highbyte_dotnet-6502&resolved=false&types=VULNERABILITY)
+[![SonarCloud Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=highbyte_dotnet-6502&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=highbyte_dotnet-6502)
+[![SonarCloud Bugs](https://sonarcloud.io/api/project_badges/measure?project=highbyte_dotnet-6502&metric=bugs)](https://sonarcloud.io/project/issues?id=highbyte_dotnet-6502&resolved=false&types=BUG)
+[![SonarCloud Coverage](https://sonarcloud.io/api/project_badges/measure?project=highbyte_dotnet-6502&metric=coverage)](https://sonarcloud.io/component_measures?id=highbyte_dotnet-6502&metric=coverage&view=list)
+[![.NET](https://github.com/highbyte/dotnet-6502/actions/workflows/dotnet.yml/badge.svg)](https://github.com/highbyte/dotnet-6502/actions/workflows/dotnet.yml)
+[![CodeQL](https://github.com/highbyte/dotnet-6502/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/highbyte/dotnet-6502/actions/workflows/codeql-analysis.yml)
+[![NuGet Version](https://img.shields.io/nuget/v/Highbyte.DotNet6502)](https://www.nuget.org/packages/Highbyte.DotNet6502/)
+[![GitHub Release](https://img.shields.io/github/v/release/highbyte/dotnet-6502?include_prereleases)](#)
+[![GitHub Release Date](https://img.shields.io/github/release-date-pre/highbyte/dotnet-6502)](#)
+[![GitHub last commit](https://img.shields.io/github/last-commit/highbyte/dotnet-6502)](#)
+[![GitHub License](https://img.shields.io/github/license/highbyte/dotnet-6502)](LICENSE)
# Overview / purpose
-- A collection of .NET cross platform libraries and applications for executing 6502 CPU machine code in different contexts. Links below for details on each library/app.
-
-- **_A programming exercise, that may or may not turn into something more_**
-
-- A main library [```Highbyte.DotNet6502```](doc/CPU_LIBRARY.md) for executing 6502 machine code, not bound to any specific computer, and does not have any UI code.
-
-- A machine code monitor library [```Highbyte.DotNet6502.Monitor```](doc/MONITOR.md) used as a base for specific UI implementations.
+.NET cross platform libraries and applications for executing 6502 CPU machine code, and emulating specific computer systems (such as Commodore 64) in different UI contexts. Links below for details on each library/app.
+
+> [!IMPORTANT]
+> This is mainly a programming exercise, that may or may not turn into something more. See [Limitations](#limitations) below.
+
+| Blazor WebAssembly app | SilkNet native app | SadConsole native app |
+| ----------------------------------- | ----------------------------------- | ----------------------------------- |
+| [](https://highbyte.se/dotnet-6502/app) | [](#highbytedotnet6502appsilknetnative) | [](#highbytedotnet6502appsadconsole) |
+
+| Run 6502 machine code in your own .NET apps | Machine code monitor |
+| ----------------------------------- | ----------------------------------- |
+| ![Code integration](doc/Screenshots/Code_integration.png 'Code integration') | ![SilkNet native app, C64 monitor](doc/Screenshots/SilkNetNative_Monitor.png 'SilkNet native app, C64 monitor') |
+
+## Common libraries
+- [`Highbyte.DotNet6502`](doc/CPU_LIBRARY.md)
+ - Core library for executing 6502 machine code, not bound to any specific emulated system/computer, and does not have any UI or I/O code.
+- [`Highbyte.DotNet6502.Monitor`](doc/MONITOR.md)
+ - Machine code monitor library used as a base for host apps using the `Highbyte.DotNet6502` library.
+- [`Highbyte.DotNet6502.Systems`](doc/SYSTEMS.md)
+ - Library for common interfaces and implementations for running computers ("systems") that uses the `Highbyte.DotNet6502` library.
+
+## System/computer-specific libraries
+Contains core system/computer emulation logic, but with no UI or I/O dependencies.
+Implements abstractions in `Highbyte.DotNet6502.Systems`.
+- [`Highbyte.DotNet6502.Systems.Commodore64`](doc/SYSTEMS_C64.md)
+ - Logic for emulating a Commodore 64 (C64).
+ - Runs C64 ROMs (Kernal, Basic, Chargen).
+ - List of apps/games listed that's been tested to work [here](doc/SYSTEMS_C64_COMPATIBLE_PRG.md) (and how to load them).
+
+- [`Highbyte.DotNet6502.Systems.Generic`](doc/SYSTEMS_GENERIC.md)
+ - Logic for emulating a generic computer based on 6502 CPU.
+
+## System-specific libraries for I/O
+Implements rendering, input handling, and audio using different technologies per emulated system/computer. Implements abstractions in `Highbyte.DotNet6502.Systems`. These libraries are used from relevant UI host apps (see below).
+- [`Highbyte.DotNet6502.Impl.AspNet`](doc/RENDER_INPUT_AUDIO.md#library-highbytedotnet6502implaspnet)
+ - System-specific input and audio code for AspNet Blazor `WASM` app.
+- [`Highbyte.DotNet6502.Impl.NAudio`](doc/RENDER_INPUT_AUDIO.md#library-highbytedotnet6502implnaudio)
+ - System-specific audio code for NAudio for `SilkNetNative` and `SadConsole` apps.
+- [`Highbyte.DotNet6502.Impl.SadConsole`](doc/RENDER_INPUT_AUDIO.md#library-highbytedotnet6502implsadconsole)
+ - System-specific rendering and input code for `SadConsole` app.
+- [`Highbyte.DotNet6502.Impl.SilkNet`](doc/RENDER_INPUT_AUDIO.md#library-highbytedotnet6502implsilknet)
+ - System-specific rendering (OpenGL shaders) and input code for `SilkNetNative` app.
+- [`Highbyte.DotNet6502.Impl.Skia`](doc/RENDER_INPUT_AUDIO.md#library-highbytedotnet6502implskia)
+ - System-specific rendering with SkiaSharp for `SilkNetNative`, and Blazor `WASM` apps.
+
+## UI host apps that runs emulators
+UI host apps for emulating the systems/computers above, using different I/O techniques (rendering, input, audio).
+
+### [`Highbyte.DotNet6502.App.WASM`](doc/APPS_WASM.md)
+
+A [`ASP.NET Blazor`](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor) WebAssembly UI.
+ - Rendering: `Highbyte.DotNet6502.Impl.Skia`
+ - Input: `Highbyte.DotNet6502.Impl.AspNet`
+ - Audio: `Highbyte.DotNet6502.Impl.AspNet`
+
+Live version: [https://highbyte.se/dotnet-6502/app](https://highbyte.se/dotnet-6502/app)
+
+[](https://highbyte.se/dotnet-6502/app) [](https://highbyte.se/dotnet-6502/app)
+
+### [`Highbyte.DotNet6502.App.SilkNetNative`](doc/APPS_SILKNET_NATIVE.md)
+A [`Silk.NET`](https://github.com/dotnet/Silk.NET) native UI.
+ - Rendering: `Highbyte.DotNet6502.Impl.Skia` or `Highbyte.DotNet6502.Impl.SilkNet`
+ - Input: `Highbyte.DotNet6502.Impl.SilkNet`
+ - Audio: `Highbyte.DotNet6502.Impl.NAudio`
+
+
+
+### [`Highbyte.DotNet6502.App.SadConsole`](doc/APPS_SADCONSOLE.md)
+A [`SadConsole`](https://github.com/Thraka/SadConsole) (a ascii/console/game engine) native UI.
+ - Rendering: `Highbyte.DotNet6502.Impl.SadConsole`
+ - Input: `Highbyte.DotNet6502.Impl.SadConsole`
+ - Audio: `Highbyte.DotNet6502.Impl.NAudio`
+
+
+
+### [`Highbyte.DotNet6502.App.ConsoleMonitor`](doc/APPS_CONSOLE_MONITOR.md)
+A console application with a only UI being a machine code monitor.
+ - Rendering: standard .NET console
+ - Input: standard .NET console
+ - Audio: none
+
+
+
+# Limitations
+> [!IMPORTANT]
+> - Correct emulation of all aspects of computers such as Commodore 64 is not likely.
+> - Not the fastest emulator.
+> - A real Commodore 64 uses the _6510_ CPU and not the 6502 CPU. But for the purpose of this emulator the 6502 CPU works fine as they are generally the same (same instruction set).
+> - Code coverage is currently limited to the core [`Highbyte.DotNet6502`](doc/CPU_LIBRARY.md) library.
-- A library [```Highbyte.DotNet6502.Systems```](doc/SYSTEMS.md) containing implementations of specific computers ("Systems") that runs on a 6502 CPU.
-
-- Several libraries [```Highbyte.DotNet6502.Impl.*```](doc/RENDER_INPUT_AUDIO.md) that implements rendering, input handling, and audio using different technologies (such as Skia, Blazor, SadConsole) per emulated System.
-
-- Several UI applications [```Highbyte.DotNet6502.Apps.*```](doc/APPS.md) that are the hosts for emulating the Systems above and their different rendering techniques.
-
-# What's currently missing
Missing features (but not limited to):
- 6502 CPU
- - Support for unofficial opcodes
+ - Support for unofficial opcodes.
- Systems
- - C64: cycle-exact rendering, bitmap graphics mode renderer in Blazor WASM, etc.
-
-# What this isn't (and probably never will be)
-- An emulation of all aspects of computers such as Apple II or Commodore 64.
-- The fastest emulator.
+ - Commodore 64: cycle-exact rendering, disk/tape drive support, accurate/stable audio, etc.
# How to develop
-For requirements and local development setup, see [here](doc/DEVELOP.md)
-
-# Inspiration, references & resources
-
-## 6502 CPU Emulator in C++ video
-- https://www.youtube.com/playlist?list=PLLwK93hM93Z13TRzPx9JqTIn33feefl37
-
-## 6502 CPU references
-- http://www.obelisk.me.uk/6502/index.html
-- https://www.atariarchives.org/alp/appendix_1.php
-- http://www.6502.org/tutorials/compare_beyond.html
-- https://www.c64-wiki.com/wiki/BRK
-- http://www.emulator101.com/6502-addressing-modes.html
-- https://www.pagetable.com/?p=410
-- http://6502.org/tutorials/decimal_mode.html
-
-## C64 specific references
-- https://www.c64-wiki.com/wiki/Reset_(Process)
-- https://www.c64-wiki.com/wiki/Bank_Switching
-- https://www.pagetable.com/c64ref/c64mem/
-- https://sta.c64.org/cbm64mem.html
-- https://github.com/mist64/c64ref/blob/master/Source/c64io/c64io_mapc64.txt
-- https://www.c64-wiki.com/wiki/PETSCII_Codes_in_Listings
-- https://dustlayer.com/c64-architecture/2013/5/7/hardware-basics-part-1-tick-tock-know-your-clock
-- https://dustlayer.com/vic-ii/2013/4/22/when-visibility-matters
-- https://dustlayer.com/vic-ii/2013/4/25/vic-ii-for-beginners-beyond-the-screen-rasters-cycle
-- https://www.zimmers.net/cbmpics/cbm/c64/vic-ii.txt
-- https://www.c64-wiki.com/wiki/Raster_interrupt
-- https://codebase64.org/doku.php?id=base:detect_pal_ntsc
-- https://www.lemon64.com/forum/viewtopic.php?p=667448#p667448
-- https://www.c64-wiki.com/wiki/ADSR
-- https://www.atarimagazines.com/compute/issue49/424_1_Programming_64_Sound.php
-- https://celso.io/retrocomputing/2019/12/23/c64-assembly
-
-
-## WebAudio
-- https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API
-- https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Advanced_techniques
-- https://github.com/pendragon-andyh/WebAudio-PulseOscillator
-- https://github.com/KristofferStrube/Blazor.WebAudio
-- https://ui.dev/web-audio-api
-- https://codepen.io/2kool2/pen/xrLeMq
-- https://dev.opera.com/articles/drum-sounds-webaudio/
-
-## Test programs
-- http://visual6502.org/wiki/index.php?title=6502TestPrograms
-- https://github.com/Klaus2m5/6502_65C02_functional_tests/blob/master/6502_functional_test.a65
-- http://www.csharp4u.com/2017/01/getting-pretty-hex-dump-of-binary-file.html?m=1
-
-## Assemblers
-Was used during development to compile actual 6502 source code to a binary, and then run it through the emulator.
-
-- https://sourceforge.net/projects/acme-crossass/
-- https://marketplace.visualstudio.com/items?itemName=rosc.vs64
-- https://nurpax.github.io/c64jasm-browser/
-- https://skilldrick.github.io/easy6502/#first-program
-
-## Monitors / Emulators
-Was used during development to test how certain instructions worked when in doubt.
-
-### VICE
-Monitor commands: https://vice-emu.sourceforge.io/vice_12.html
-
-How to load and step through a program in the VICE monitor
-```
-l "C:\Source\Repos\dotnet-6502\samples\Assembler\Generic\Build\testprogram.prg" 0 1000
-d 1000
-r PC=1000
-z
-r
-```
-
-## Credits
+See [here](doc/DEVELOP.md)
+
+# References
+See [here](doc/REFERENCES_AND_INSPIRATION.md).
+
+# Credits
- [Kristoffer Strube](https://github.com/KristofferStrube) for the original Blazor WASM async interop code for [WebAudio](https://github.com/KristofferStrube/Blazor.WebAudio), [DOM](https://github.com/KristofferStrube/Blazor.DOM), and [IDL](https://github.com/KristofferStrube/Blazor.WebIDL) that was the basis for a synchronous implementation in this repo. Copyright notice [here](src/libraries/Highbyte.DotNet6502.Impl.AspNet/JSInterop/JSInterop_OriginalLicense.MD).
diff --git a/doc/APPS.md b/doc/APPS.md
index ffc9f1a3..3b9ee9ee 100644
--- a/doc/APPS.md
+++ b/doc/APPS.md
@@ -3,7 +3,7 @@
# Overview
Emulator applications with user interfaces for running different [systems](SYSTEMS.md), using different [rendering input handling, and audio](RENDER_INPUT_AUDIO.md) techniques.
-- [Blazor Web Assembly (WASM) with SkiaSharp renderer](APPS_SKIA_WASM.md)
-- [Native cross-platform window via Silk.NET using with SkiaSharp renderer](APPS_SILKNET_NATIVE.md)
-- [Native cross-platform window via SadConsole, using SadConsole renderer](APPS_SADCONSOLE.md)
+- [Blazor Web Assembly (WASM)](APPS_WASM.md)
+- [Native cross-platform window via Silk.NET](APPS_SILKNET_NATIVE.md)
+- [Native cross-platform window via SadConsole](APPS_SADCONSOLE.md)
- [Native cross-platform console monitor app](APPS_CONSOLE_MONITOR.md)
diff --git a/doc/APPS_CONSOLE_MONITOR.md b/doc/APPS_CONSOLE_MONITOR.md
index 255f2a51..e9eebbff 100644
--- a/doc/APPS_CONSOLE_MONITOR.md
+++ b/doc/APPS_CONSOLE_MONITOR.md
@@ -1,7 +1,7 @@
Highbyte.DotNet6502.App.ConsoleMonitor
# Overview
-
+
# Features
Stand-alone console monitor app, no other UI.
diff --git a/doc/APPS_SADCONSOLE.md b/doc/APPS_SADCONSOLE.md
index efc02033..9832d18f 100644
--- a/doc/APPS_SADCONSOLE.md
+++ b/doc/APPS_SADCONSOLE.md
@@ -1,18 +1,23 @@
Highbyte.DotNet6502.App.SadConsole
# Overview
-
+
# Features
-Native cross-platform app written in .NET with a Window, renderer, and input handler via SadConsole (from ```Highbyte.DotNet6502.Impl.SadConsole```).
-
-Currently the system to emulate is configured in the appsettings.json file.
+Native cross-platform app based on the [`SadConsole`](https://github.com/Thraka/SadConsole) terminal/ascii/console/game engine.
# System: C64
-A directory containing the C64 ROM files (Kernal, Basic, Chargen) is supplied by the user, and set in the appsettings.json file.
+- A directory containing the C64 ROM files (Kernal, Basic, Chargen) is supplied by the user. Defaults are set in the appsettings.json file, and possible to change in the UI.
+- Only video mode that works in C64 character mode (not multicolor) with built-in characters set from ROM is supported.
+- Generation of sound via NAudio with custom OpenAL (Silk.NET) provider (for cross platform compatibility).
# System: Generic computer
TODO
# Monitor
+Press button or toggle with F12.
+TODO
+
+# Stats
+Press button or toggle with F11.
TODO
diff --git a/doc/APPS_SILKNET_NATIVE.md b/doc/APPS_SILKNET_NATIVE.md
index fd1489a3..fd347402 100644
--- a/doc/APPS_SILKNET_NATIVE.md
+++ b/doc/APPS_SILKNET_NATIVE.md
@@ -1,21 +1,26 @@
Highbyte.DotNet6502.App.SilkNetNative
# Overview
-
-
-
+
# Features
-Native cross-platform app written in .NET with a Window from Silk.NET, using a SkiaSharp (from ```Highbyte.DotNet6502.Impl.Skia```) or a OpenGL (from ```Highbyte.DotNet6502.Impl.SilkNet```) renderer, with a Silk.NET input handler (from ```Highbyte.DotNet6502.Impl.SilkNet```).
+Native cross-platform app written in .NET using a [Silk.NET](https://github.com/dotnet/Silk.NET) window.
Uses Silk.NET [ImGui extensions](https://www.nuget.org/packages/Silk.NET.OpenGL.Extensions.ImGui/) to render UI for interactive menu, monitor, and stats window.
# System: C64
-A directory containing the C64 ROM files (Kernal, Basic, Chargen) is supplied by the user, and set in the appsettings.json file.
+- A directory containing the C64 ROM files (Kernal, Basic, Chargen) is supplied by the user. Defaults are set in the appsettings.json file, and possible to change in the UI.
+
+- Renderers using either `SkiaSharp` or `SilkNet` (OpenGl)
+ - Character mode (normal and multi-color) with all renderers
+ - Bitmap mode (normal and bitmap mode) with the SkiaSharp2* and SilkNetOpenGL renderers.
+ - Sprites (normal and multi-color) with all renderers.
+ - Rendering of raster lines for border and background colors with all renderers.
-Rendering of raster lines for border and background colors.
+- Input using `SilkNet`
-Generation of sound via NAudio with custom OpenAL (Silk.NET) provider (for cross platform compatibility).
+- Audio via `NAudio`
+ - [NAudio](https://github.com/naudio/NAudio) + custom OpenAL (Silk.NET) provider for cross platform compatibility.
# System: Generic computer
TODO
@@ -29,7 +34,7 @@ Start and stop of selected system.
Configuration options of selected system.
-## Monitor
+ ## Monitor
A togglebale machine code monitor window by pressing F12.
## Stats
diff --git a/doc/APPS_WASM.md b/doc/APPS_WASM.md
index 017029af..bac9a231 100644
--- a/doc/APPS_WASM.md
+++ b/doc/APPS_WASM.md
@@ -1,21 +1,26 @@
Highbyte.DotNet6502.App.WASM
# Overview
-
-
-
+
A deployed version can be found here [https://highbyte.se/dotnet-6502/app](https://highbyte.se/dotnet-6502/app)
# Features
-A web app written in Blazor WASM (Web Assembly), using [```SkiaSharp.Views.Blazor```](https://www.nuget.org/packages/SkiaSharp.Views.Blazor) library to provide a Canvas for drawing on with main [```SkiaSharp```](https://www.nuget.org/packages/SkiaSharp) library.
+A web app written in Blazor WASM (Web Assembly), using [`SkiaSharp.Views.Blazor`](https://www.nuget.org/packages/SkiaSharp.Views.Blazor) library to provide a Canvas for drawing on with main [`SkiaSharp`](https://www.nuget.org/packages/SkiaSharp) library.
# System: C64
-Via C64 config UI you have to upload binaries for the ROMs that a C64 uses (Kernal, Basic, Chargen).
+- Via C64 config UI you have to upload binaries for the ROMs that a C64 uses (Kernal, Basic, Chargen).
+
+- Renderers: `SkiaSharp`, `SkiaSharp2`, `SkiaSharp2b`
+ - Character mode (normal and multi-color) with all renderers
+ - Bitmap mode (normal and bitmap mode) only with the `SkiaSharp2*` renderers.
+ - Sprites (normal and multi-color) with all renderers.
+ - Rendering of raster lines for border and background colors with all renderers.
-Rendering of raster lines for border and background colors.
+- Input using `AspNet`
-Generation of sound via WebAudio API.
+- Audio via `AspNet`
+ - Using .NET interop to WebAudio API
# System: Generic computer
The example 6502 machine code that is loaded and run by default for the _Generic_ computer is this a assembled version of [this 6502 assembly code](../samples/Assembler/Generic/hostinteraction_scroll_text_and_cycle_colors.asm)
@@ -40,12 +45,12 @@ For system requirements, see details [here](DEVELOP.md#Requirements)
## Visual Studio 2022 (Windows)
-Open solution ```dotnet-6502.sln```.
-Set project ```Highbyte.DotNet6502.App.WASM``` as startup, and start with F5.
+Open solution `dotnet-6502.sln`.
+Set project `Highbyte.DotNet6502.App.WASM` as startup, and start with F5.
## From command line (Windows, Linux, Mac)
### Run Debug build
-```
+```shell
cd ./src/apps/Highbyte.DotNet6502.App.WASM
dotnet run
```
@@ -53,10 +58,10 @@ Open browser at http://localhost:5000.
### Run optimized Publish build
Requires
-- DotNet workload "wasm-tools" , install with ```dotnet workload install wasm-tools```
-- DotNet global tool "serve", install with ```dotnet tool install --global dotnet-serve```
+- DotNet workload "wasm-tools" , install with `dotnet workload install wasm-tools`
+- DotNet global tool "serve", install with `dotnet tool install --global dotnet-serve`
-```
+```powershell
cd ./src/apps/Highbyte.DotNet6502.App.WASM
if(Test-Path $publishDir) { del ./bin/Publish/ -r -force }
dotnet publish -c Release -o ./bin/Publish/
diff --git a/doc/CPU_LIBRARY.md b/doc/CPU_LIBRARY.md
index dd8b9dd4..d1a92dc2 100644
--- a/doc/CPU_LIBRARY.md
+++ b/doc/CPU_LIBRARY.md
@@ -13,93 +13,197 @@
# Using from a .NET application
## Reference NuGet package
-```
+```shell
dotnet add package Highbyte.DotNet6502 --prerelease
```
## Or compile .dll yourself
-- Clone this repo ```git clone https://github.com/highbyte/dotnet-6502.git```
-- Change dir to library ```cd dotnet-6502/src/libraries/Highbyte.DotNet6502```
-- Build library ```dotnet build```
-- In your app, add .dll reference to ```./bin/Debug/net8.0/Highbyte.DotNet6502.dll```
+- Clone this repo `git clone https://github.com/highbyte/dotnet-6502.git`
+- Change dir to library `cd dotnet-6502/src/libraries/Highbyte.DotNet6502`
+- Build library `dotnet build`
+- In your app, add .dll reference to `./bin/Debug/net8.0/Highbyte.DotNet6502.dll`
## Example of basic usage of Highbyte.DotNet6502 library
-Example #1. Load compiled 6502 binary and execute it.
+## Example #1.
+
+### Step 1 - Write a 6502 program in assembly
+Write a 6502 assembly program to calculate average of two values (from different memory locations) and store result in a third memory location.
+
+> [!NOTE]
+> This example uses the [ACME](https://sourceforge.net/projects/acme-crossass/) cross assembler syntax (builds exists for Windows and macOS, for Linux it requires to download source code and build).
+> There exists other 6502 cross [assemblers](http://www.6502.org/tools/asm/) that can be used (but which may have different syntax requirements).
+
+Use a text editor (or IDE) to create a text file with the contents below and save it to `calc_avg.asm`.
+> [!TIP]
+> `VSCode` has an extension called [`VS64`](https://marketplace.visualstudio.com/items?itemName=rosc.vs64) that provides nice syntax highlighting for 6502 assembly code (.asm).
+
+```
+;Calculates the average of two values stored in memory locations, and store the result in another memory location.
+;Code written in 6502 assembler using ACME cross assembler syntax.
+
+;code start address
+* = $c000
+
+;!to "./calc_avg.prg"
+ lda $d000
+ clc
+ adc $d001
+ ror
+ sta $d002
+;In emulator, setup hitting brk instruction to stop
+ brk
+```
+
+### Step 2 - Assemble program to binary .prg file.
+Example assumes ACME is installed, and acme executable is in path.
+
+PowerShell/Bash
+```shell
+acme -f cbm -o calc_avg.prg calc_avg.asm
+
+# or examples if acme is not in path:
+# & "$($env:USERPROFILE)\c64\acme\acme.exe" -f cbm -o calc_avg.prg calc_avg.asm
+# ~/c64/acme/acme -f cbm -o calc_avg.prg calc_avg.asm
+```
+
+### Step 3 (optional) - Inspect binary .prg file
+
+> [!NOTE]
+> If the binary was assembled with the `-f cbm` parameter (as in the example above), the two first bytes in the .prg file would be the load address specified in the source .asm file (`* = $c000`), in _little endian_ order `00`,`C0`. This is usually the convention for Commodore computers, and convenient in other contexts also.
+> If the binary was assembled with the `-f plain` parameter, the binary file would not have the first two address bytes, and only contain the code (and data) declared in the source file.
+
+`PowerShell` (Windows, Linux, macOS)
+```powershell
+(Format-Hex ./calc_avg.prg).HexBytes
+```
+```
+00 C0 AD 00 D0 18 6D 01 D0 6A 8D 02 D0 00
+```
+
+`Bash` (Linux) and `Zsh` (macOS)
+```bash
+hexdump -ve '1/1 "%.2x "' ./calc_avg.prg
+```
+```
+00 c0 ad 00 d0 18 6d 01 d0 6a 8d 02 d0 00
+```
+
+### Step 4 - Load compiled 6502 binary and execute it.
+
+DotNet C# console program that runs the 6502 program.
+
+`Program.cs`
```c#
- var mem = BinaryLoader.Load(
- "C:\Binaries\MyCompiled6502Program.prg",
- out ushort loadedAtAddress);
-
- var computerBuilder = new ComputerBuilder();
- computerBuilder
- .WithCPU()
- .WithStartAddress(loadedAtAddress)
- .WithMemory(mem);
-
- var computer = computerBuilder.Build();
- computer.Run();
-```
+// ----------------------------------------------------------------------------------------------------
+// A minimal example of how to load and run a 6502 machine code program.
+// This does not involve a complete computer (such as Commodore 64) but only the CPU and memory.
+// ----------------------------------------------------------------------------------------------------
+
+using Highbyte.DotNet6502;
+using Highbyte.DotNet6502.Utils;
-Example #2. 6502 machine code for adding to numbers and dividing by 2
+string programFile = "calc_avg.prg";
+
+// Create memory (default 64KB) and load the machine code program into it. Assume two first bytes in the .prg file is the load address.
+var mem = BinaryLoader.Load(programFile, out ushort loadAddress);
+
+// Init variables in memory locations used by the program.
+mem[0xd000] = 64;
+mem[0xd001] = 20;
+Console.WriteLine($"Input 1 (0xd000) = {mem[0xd000]}");
+Console.WriteLine($"Input 2 (0xd001) = {mem[0xd001]}");
+
+// Create the CPU and set program counter (start address).
+var cpu = new CPU();
+cpu.PC = loadAddress;
+
+// Run program. The 6502 program will run until a BRK instruction is encountered.
+cpu.Execute(mem, LegacyExecEvaluator.UntilBRKExecEvaluator);
+
+// Inspect result of program which is stored in memory location 0xd002.
+Console.WriteLine($"Output (0xd002) = {mem[0xd002]}");
+
+```
+
+Result
+```
+Input 1 (0xd000) = 64
+Input 2 (0xd001) = 20
+Output (0xd002) = 42
+```
+
+
+## Example #2. Enter 6502 machine code directly and show processor status
+
+`Program.cs`
```c#
- // Test program
- // - adds values from two memory location
- // - divides it by 2 (rotate right one bit position)
- // - stores it in another memory location
-
- // Load input data into memory
- byte value1 = 12;
- byte value2 = 30;
- ushort value1Address = 0xd000;
- ushort value2Address = 0xd001;
- ushort resultAddress = 0xd002;
- var mem = new Memory();
- mem[value1Address] = value1;
- mem[value2Address] = value2;
-
- // Load machine code into memory
- ushort codeAddress = 0xc000;
- ushort codeInsAddress = codeAddress;
- mem[codeInsAddress++] = 0xad; // LDA (Load Accumulator)
- mem[codeInsAddress++] = 0x00; // |-Lowbyte of $d000
- mem[codeInsAddress++] = 0xd0; // |-Highbyte of $d000
- mem[codeInsAddress++] = 0x18; // CLC (Clear Carry flag)
- mem[codeInsAddress++] = 0x6d; // ADC (Add with Carry, adds memory to accumulator)
- mem[codeInsAddress++] = 0x01; // |-Lowbyte of $d001
- mem[codeInsAddress++] = 0xd0; // |-Highbyte of $d001
- mem[codeInsAddress++] = 0x6a; // ROR (Rotate Right, rotates accumulator right one bit position)
- mem[codeInsAddress++] = 0x8d; // STA (Store Accumulator, store to accumulator to memory)
- mem[codeInsAddress++] = 0x02; // |-Lowbyte of $d002
- mem[codeInsAddress++] = 0xd0; // |-Highbyte of $d002
- mem[codeInsAddress++] = 0x00; // BRK (Break/Force Interrupt) - emulator configured to stop execution when reaching this instruction
-
- // Initialize emulator with CPU, memory, and execution parameters
- var computerBuilder = new ComputerBuilder();
- computerBuilder
- .WithCPU()
- .WithStartAddress(codeAddress)
- .WithMemory(mem)
- .WithInstructionExecutedEventHandler(
- (s, e) => Console.WriteLine(OutputGen.GetLastInstructionDisassembly(e.CPU, e.Mem)))
- .WithExecOptions(options =>
- {
- options.ExecuteUntilInstruction = OpCodeId.BRK; // Emulator will stop executing when a BRK instruction is reached.
- });
- var computer = computerBuilder.Build();
-
- // Run program
- computer.Run();
- Console.WriteLine($"Execution stopped");
- Console.WriteLine($"CPU state: {OutputGen.GetProcessorState(computer.CPU)}");
- Console.WriteLine($"Stats: {computer.CPU.ExecState.InstructionsExecutionCount} instruction(s) processed, and used {computer.CPU.ExecState.CyclesConsumed} cycles.");
-
- // Print result
- byte result = mem[resultAddress];
- Console.WriteLine($"Result: ({value1} + {value2}) / 2 = {result}");
+// ----------------------------------------------------------------------------------------------------
+// An example of how to enter a machine code program directly into memory,
+// and instanciating the "Generic" computer with logging of executed instructions.
+// ----------------------------------------------------------------------------------------------------
+
+using Highbyte.DotNet6502;
+using Highbyte.DotNet6502.Systems.Generic;
+using Highbyte.DotNet6502.Utils;
+
+// Test program to calculate average of two values
+// - adds values from two memory location
+// - divides it by 2 (rotate right one bit position)
+// - stores it in another memory location
+
+// Load input data into memory
+byte value1 = 64;
+byte value2 = 20;
+ushort value1Address = 0xd000;
+ushort value2Address = 0xd001;
+ushort resultAddress = 0xd002;
+var mem = new Memory();
+mem[value1Address] = value1;
+mem[value2Address] = value2;
+
+// Load machine code into memory
+ushort codeAddress = 0xc000;
+ushort codeInsAddress = codeAddress;
+mem[codeInsAddress++] = 0xad; // LDA (Load Accumulator)
+mem[codeInsAddress++] = 0x00; // |-Lowbyte of $d000
+mem[codeInsAddress++] = 0xd0; // |-Highbyte of $d000
+mem[codeInsAddress++] = 0x18; // CLC (Clear Carry flag)
+mem[codeInsAddress++] = 0x6d; // ADC (Add with Carry, adds memory to accumulator)
+mem[codeInsAddress++] = 0x01; // |-Lowbyte of $d001
+mem[codeInsAddress++] = 0xd0; // |-Highbyte of $d001
+mem[codeInsAddress++] = 0x6a; // ROR (Rotate Right, rotates accumulator right one bit position)
+mem[codeInsAddress++] = 0x8d; // STA (Store Accumulator, store to accumulator to memory)
+mem[codeInsAddress++] = 0x02; // |-Lowbyte of $d002
+mem[codeInsAddress++] = 0xd0; // |-Highbyte of $d002
+mem[codeInsAddress++] = 0x00; // BRK (Break/Force Interrupt) - emulator configured to stop execution when reaching this instruction
+
+// Initialize a "Generic" 6502 computer emulator with CPU, memory, and execution parameters
+var computerBuilder = new GenericComputerBuilder();
+computerBuilder
+ .WithCPU()
+ .WithStartAddress(codeAddress)
+ .WithMemory(mem)
+ .WithInstructionExecutedEventHandler(
+ (s, e) => Console.WriteLine(OutputGen.GetLastInstructionDisassembly(e.CPU, e.Mem)))
+ .WithExecOptions(options =>
+ {
+ options.ExecuteUntilInstruction = OpCodeId.BRK; // Emulator will stop executing when a BRK instruction is reached.
+ });
+var computer = computerBuilder.Build();
+
+// Run program
+computer.Run();
+Console.WriteLine($"Execution stopped");
+Console.WriteLine($"CPU state: {OutputGen.GetProcessorState(computer.CPU)}");
+Console.WriteLine($"Stats: {computer.CPU.ExecState.InstructionsExecutionCount} instruction(s) processed, and used {computer.CPU.ExecState.CyclesConsumed} cycles.");
+
+// Print result
+byte result = mem[resultAddress];
+Console.WriteLine($"Result: ({value1} + {value2}) / 2 = {result}");
```
-generates this output
-``` console
+Result
+```
C000 AD 00 D0 LDA $D000
C003 18 CLC
C004 6D 01 D0 ADC $D001
@@ -109,11 +213,22 @@ C00B 00 BRK
Execution stopped
CPU state: A=15 X=00 Y=00 PS=[-----I--] SP=FD PC=0000
Stats: 6 instruction(s) processed, and used 23 cycles.
-Result: (12 + 30) / 2 = 21
+Result: (64 + 20) / 2 = 41
```
## Model for bank switching
-The 6502 CPU supports max 64KB of total memory (16 bit address space). To enable more memory to be used, a type of "bank switching" is supported in the memory implementation. X number of memory configurations can be created, and each populated with byte[] arrays for separate locations within the 64KB space. Switching between the different memory layouts is as easy as calling ```memory.SetMemoryConfiguration(x)```.
+The 6502 CPU supports max 64KB of total memory (16 bit address space). To enable more memory to be used, a type of "bank switching" is supported in the memory implementation. X number of memory configurations can be created, and each populated with byte[] arrays for separate locations within the 64KB space.
+
+The `Memory` constructor parameter `numberOfConfigurations` (default 1) specifies how many memory configuration to support:
+
+```c#
+var mem = new Memory(numberOfConfigurations: 4);
+```
+Switch between the different memory configurations by calling `SetMemoryConfiguration`:
+
+```c#
+mem.SetMemoryConfiguration(2)
+```
TODO: more details
diff --git a/doc/DEVELOP.md b/doc/DEVELOP.md
index acbb5798..33400d23 100644
--- a/doc/DEVELOP.md
+++ b/doc/DEVELOP.md
@@ -1,4 +1,4 @@
-
How to develop
+
Requirements and local development setup
# Requirements
- Windows, Linux, or Mac.
@@ -7,7 +7,7 @@
- VSCode (Windows, Linux, Mac)
- Visual Studio 2022 (Windows)
- Or other preferred editor.
-- Specifics for Blazor WASM (WebAssembly) projects
+- Specifics for Blazor WASM (`Highbyte.DotNet6502.App.WASM`) project
- Visual Studio 2022: For building the WASM projects, add the component ".NET WebAssembly Build Tools" in Visual Studio Installer.
- VSCode / command line: For building the WASM projects, install the dotnet workload "wasm-tool", see instruction [here](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-8.0&pivots=windows#net-webassembly-build-tools).
@@ -15,12 +15,14 @@
See [here](SYSYEM_DIAGRAM.md)
# Tests
-Most of the ```Highbyte.DotNet6502``` library code has been developed with a test-first approach.
-
The [XUnit](https://xunit.net/) library is used.
+Tests are currently focused on the core `Highbyte.DotNet6502` library. Most of its code has been developed with a test-first approach.
+
+Tests may expand to parts of system-specific logic code such as `Highbyte.DotNet6502.Systems.CommodoreC64`.
+
## Unit tests
-To run only unit tests (for the ```Highbyte.DotNet6502``` library)
+To run only unit tests (for the `Highbyte.DotNet6502` library)
```powershell
cd tests/Highbyte.DotNet6502.Tests
@@ -47,7 +49,7 @@ cd tests/Highbyte.DotNet6502.Tests
dotnet test --filter TestType=Integration
```
-# Code coverage report locally (```Highbyte.DotNet6502``` library)
+# Code coverage report locally (`Highbyte.DotNet6502` library)
Install report-generator global tool
```powershell
@@ -65,3 +67,21 @@ Generate and show reports (Linux)
chmod +x ./codecov-console.sh
./codecov-console.sh
```
+
+
+# Using other emulators to verify correct behavior
+When in doubt how a specific 6502 instruction actually worked, it was useful to use the monitor in the VICE emulator (that is widely known to be an accurate emulator of C64 and 6502/6510 CPU) as a reference for stepping through machine code programs.
+
+It was also useful to use VICE to see how graphics and audio is working (or not working) compared to the C64 implementation in this emulator, and use as a reference for general correctness.
+
+## VICE monitor
+Monitor commands: https://vice-emu.sourceforge.io/vice_12.html
+
+How to load and step through a program in the VICE monitor
+```
+l "C:\Source\Repos\dotnet-6502\samples\Assembler\Generic\Build\testprogram.prg" 0 1000
+d 1000
+r PC=1000
+z
+r
+```
diff --git a/doc/MONITOR.md b/doc/MONITOR.md
index afa330cc..ea91b447 100644
--- a/doc/MONITOR.md
+++ b/doc/MONITOR.md
@@ -8,7 +8,7 @@ The library need to be implemented for a specific UI technology, see the differe
Specific emulated "Systems" (computers) can also implement additional commands that are specific for that System, see [here](#system-specific-commands).
# General monitor commands
-Type ```?|help|-?|--help``` to list commands.
+Type `?|help|-?|--help` to list commands.
```
> ?
Usage: [command]
@@ -29,9 +29,9 @@ Commands:
z Single step through instructions. Optionally execute a specified number of instructions.
```
-Type ```[command] -?|--help``` to list help on specific command.
+Type `[command] -?|--help` to list help on specific command.
-Example on help for ```d``` (disassemble) command:
+Example on help for `d` (disassemble) command:
```
> d -?
Usage: d [options]
@@ -44,7 +44,7 @@ Options:
-?|-h|--help Show help information.
```
-Example how to load binary with ```l``` command:
+Example how to load binary with `l` command:
_The machine code binary simple.prg adds two number from memory, divides by 2, stores it in another memory location_
```
@@ -52,7 +52,7 @@ _The machine code binary simple.prg adds two number from memory, divides by 2, s
File loaded at 0xC000
```
-Example how to disassemble with ```d``` command:
+Example how to disassemble with `d` command:
_Shows what the code in simple.prg does_
```
@@ -70,14 +70,14 @@ c00f 00 BRK
c010 00 BRK
```
-Example how to fill bytes in memory with ```f``` command:
+Example how to fill bytes in memory with `f` command:
_Sets value A and B in memory locations (d000 and d001) that simple.prg uses_
```
> f d000 12 30
```
-Example how to set PC (Program Counter) with ```r pc``` command:
+Example how to set PC (Program Counter) with `r pc` command:
_Sets PC at load address of simple.prg_
```
@@ -85,7 +85,7 @@ _Sets PC at load address of simple.prg_
SP=00 PC=C000
```
-Example how to execute ```g``` command:
+Example how to execute `g` command:
_Executes simple.prg, stops on BRK instruction. Note that PC has been changed to IRQ vector, which in this example is at 0000._
```
@@ -94,7 +94,7 @@ BRK instruction at c00b triggered stop.
0000 00 BRK
```
-Example how to show contents of bytes in memory with ```m``` command:
+Example how to show contents of bytes in memory with `m` command:
_Inspects values A (d000), B (d001), and result (d002)_
```
diff --git a/doc/REFERENCES_AND_INSPIRATION.md b/doc/REFERENCES_AND_INSPIRATION.md
new file mode 100644
index 00000000..774c357a
--- /dev/null
+++ b/doc/REFERENCES_AND_INSPIRATION.md
@@ -0,0 +1,58 @@
+# References & inspiration
+
+## 6502 CPU references
+- http://www.obelisk.me.uk/6502/index.html
+- https://www.atariarchives.org/alp/appendix_1.php
+- http://www.6502.org/tutorials/compare_beyond.html
+- https://www.c64-wiki.com/wiki/BRK
+- http://www.emulator101.com/6502-addressing-modes.html
+- https://www.pagetable.com/?p=410
+- http://6502.org/tutorials/decimal_mode.html
+
+## C64 specific references
+- https://www.c64-wiki.com/wiki/Reset_(Process)
+- https://www.c64-wiki.com/wiki/Bank_Switching
+- https://www.pagetable.com/c64ref/c64mem/
+- https://sta.c64.org/cbm64mem.html
+- https://github.com/mist64/c64ref/blob/master/Source/c64io/c64io_mapc64.txt
+- https://www.c64-wiki.com/wiki/PETSCII_Codes_in_Listings
+- https://dustlayer.com/c64-architecture/2013/5/7/hardware-basics-part-1-tick-tock-know-your-clock
+- https://dustlayer.com/vic-ii/2013/4/22/when-visibility-matters
+- https://dustlayer.com/vic-ii/2013/4/25/vic-ii-for-beginners-beyond-the-screen-rasters-cycle
+- https://www.zimmers.net/cbmpics/cbm/c64/vic-ii.txt
+- https://www.c64-wiki.com/wiki/Raster_interrupt
+- https://codebase64.org/doku.php?id=base:detect_pal_ntsc
+- https://www.lemon64.com/forum/viewtopic.php?p=667448#p667448
+- https://www.c64-wiki.com/wiki/ADSR
+- https://www.atarimagazines.com/compute/issue49/424_1_Programming_64_Sound.php
+- https://celso.io/retrocomputing/2019/12/23/c64-assembly
+
+## WebAudio
+- https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API
+- https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Advanced_techniques
+- https://github.com/pendragon-andyh/WebAudio-PulseOscillator
+- https://github.com/KristofferStrube/Blazor.WebAudio
+- https://ui.dev/web-audio-api
+- https://codepen.io/2kool2/pen/xrLeMq
+- https://dev.opera.com/articles/drum-sounds-webaudio/
+
+## 6502 CPU Emulator in C++ video
+- https://www.youtube.com/playlist?list=PLLwK93hM93Z13TRzPx9JqTIn33feefl37
+
+## Test programs
+- http://visual6502.org/wiki/index.php?title=6502TestPrograms
+- https://github.com/Klaus2m5/6502_65C02_functional_tests/blob/master/6502_functional_test.a65
+- http://www.csharp4u.com/2017/01/getting-pretty-hex-dump-of-binary-file.html?m=1
+
+## Assemblers
+Was used during development to compile actual 6502 source code to a binary, and then run it through the emulator.
+
+- https://sourceforge.net/projects/acme-crossass/
+- https://marketplace.visualstudio.com/items?itemName=rosc.vs64
+- https://nurpax.github.io/c64jasm-browser/
+- https://skilldrick.github.io/easy6502/#first-program
+
+## Other emulators
+Was used during development to verify behavior of the 6502 CPU and C64.
+
+- https://vice-emu.sourceforge.io/
diff --git a/doc/RENDER_INPUT_AUDIO.md b/doc/RENDER_INPUT_AUDIO.md
index b5d3f392..017bccc7 100644
--- a/doc/RENDER_INPUT_AUDIO.md
+++ b/doc/RENDER_INPUT_AUDIO.md
@@ -5,24 +5,27 @@ Rendering, input handling, and audio libraries
| App | Techniques | Implementation libraries | C64 | Generic |
| ------------------------------------ | ------------------------------------------- | --------------------------------------------- | :---: | :---: |
-| `Highbyte.DotNet6502.App.WASM` | Render: `SkiaSharp.Blazor.View`,`OpenGL` | Render: `Highbyte.DotNet6502.Impl.Skia` | x | x |
+| `Highbyte.DotNet6502.App.WASM` | Render: `SkiaSharp.Blazor.View`,`OpenGL` | Render: `Highbyte.DotNet6502.Impl.Skia` | x | x |
| | Input: `Blazor`,`ASP.NET`,`JavaScript` | Input: `Highbyte.DotNet6502.Impl.AspNet` | x | x |
| | Audio: `WebAudio API`, `Blazor JS interop` | Audio: `Highbyte.DotNet6502.Impl.AspNet` | x | |
| | | | | |
-| `Highbyte.DotNet6502.App.SilkNetNative` | Render: `Silk.NET`,`OpenGL`,`SkiaSharp` | Render: `Highbyte.DotNet6502.Impl.Skia` | x | x |
+| `Highbyte.DotNet6502.App.SilkNetNative` | Render: `Silk.NET`,`OpenGL`,`SkiaSharp` | Render: `Highbyte.DotNet6502.Impl.Skia` | x | x |
| | | Render (OpenGL/shaders): `Highbyte.DotNet6502.Impl.SilkNet` | x | |
| | Input: `Silk.NET` | Input: `Highbyte.DotNet6502.Impl.SilkNet` | x | x |
| | Audio: `NAudio` | Audio: `Highbyte.DotNet6502.Impl.NAudio` | x | |
| | | | | |
| `Highbyte.DotNet6502.App.SadConsole` | Render: `SadConsole` | Render: `Highbyte.DotNet6502.Impl.SadConsole` | x | x |
| | Input: `SadConsole` | Input: `Highbyte.DotNet6502.Impl.SadConsole` | x | x |
-| | Audio: - | Audio: - | | |
+| | Audio: `NAudio` | Audio: `Highbyte.DotNet6502.Impl.NAudio` | x | |
# Library: Highbyte.DotNet6502.Impl.Skia
-[```Highbyte.DotNet6502.Impl.Skia```](#HighbyteDotNet6502ImplSkia)
-- Library with renderers implemented with the [```SkiaSharp```](https://github.com/mono/SkiaSharp) 2D drawing library. Can be used from both native and WASM applications.
-- Note: alternative renderers using only OpenGL + shaders can be found in [```Highbyte.DotNet6502.Impl.SilkNet```](#HighbyteDotNet6502ImplSilkNet) currently used by the Silk.NET native app, see below.
+[`Highbyte.DotNet6502.Impl.Skia`](#HighbyteDotNet6502ImplSkia)
+- Library with renderers implemented with the [`SkiaSharp`](https://github.com/mono/SkiaSharp) 2D drawing library.
+- For C64, multiple variants of a SkiaSharp renderer exists, with different tradeoffs (speed vs completeness).
+- It's possible to use special Skia SKSL fragment shaders with SkiaSharp, which is used by some renderer variants.
+- Can be used from both native and WASM applications.
+- Note: other alternative renderers using only OpenGL + shaders can be found in [`Highbyte.DotNet6502.Impl.SilkNet`](#HighbyteDotNet6502ImplSilkNet) currently used by the Silk.NET native app, see below.
## Renderer
### C64
@@ -30,9 +33,9 @@ Rendering, input handling, and audio libraries
# Library: Highbyte.DotNet6502.Impl.AspNet
-[```Highbyte.DotNet6502.Impl.AspNet```](#HighbyteDotNet6502ImplAspNet)
-- InputHandlers implemented with [```ASP.NET Blazor```](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor). Can be used from web applications.
-- AudioHandlers implemented with [```ASP.NET Blazor```](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor) JS Interop to WebAudio. Can be used from web applications.
+[`Highbyte.DotNet6502.Impl.AspNet`](#HighbyteDotNet6502ImplAspNet)
+- InputHandlers implemented with [`ASP.NET Blazor`](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor). Can be used from web applications.
+- AudioHandlers implemented with [`ASP.NET Blazor`](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor) JS Interop to WebAudio. Can be used from web applications.
## InputHandler
### C64
@@ -44,9 +47,9 @@ Experimental (non-complete) emulation of C64 SID audio chip.
# Library: Highbyte.DotNet6502.Impl.SilkNet
-[```Highbyte.DotNet6502.Impl.SilkNet```](#HighbyteDotNet6502ImplSilkNet)
-- InputHandlers implemented with the [```Silk.NET```](https://github.com/dotnet/Silk.NET) windowing library. Can be used from native applications.
-- Renderers implemented with Silk.NET OpenGL bindings, together with custom shaders. Can be used from native applications.
+[`Highbyte.DotNet6502.Impl.SilkNet`](#HighbyteDotNet6502ImplSilkNet)
+- InputHandlers implemented with the [`Silk.NET`](https://github.com/dotnet/Silk.NET) windowing library. Can be used from native Silk.NET application.
+- Renderers implemented with Silk.NET OpenGL bindings, together with custom shaders. Can be used from native Silk.NET application.
## Renderer
### C64
@@ -56,15 +59,15 @@ Experimental (non-complete) emulation of C64 SID audio chip.
# Library: Highbyte.DotNet6502.Impl.NAudio
-[```Highbyte.DotNet6502.Impl.Naudio```](#HighbyteDotNet6502ImplNAudio)
-- AudioHandlers implemented with [```NAudio```](https://github.com/naudio/NAudio) audio library using a custom `Silk.NET.OpenAL` provider for cross platform support. Can be used from native applications.
+[`Highbyte.DotNet6502.Impl.Naudio`](#HighbyteDotNet6502ImplNAudio)
+- AudioHandlers implemented with [`NAudio`](https://github.com/naudio/NAudio) audio library using a custom `Silk.NET.OpenAL` provider for cross platform support. Can be used from all native applications.
## AudioHandler
### C64
# Library: Highbyte.DotNet6502.Impl.SadConsole
-[```Highbyte.DotNet6502.Impl.SadConsole```](#HighbyteDotNet6502ImplSadConsole)
-- Renderers and InputHandlers implemented with the [```SadConsole```](https://github.com/Thraka/SadConsole) 2D console drawing library. Can be used from native applications.
+[`Highbyte.DotNet6502.Impl.SadConsole`](#HighbyteDotNet6502ImplSadConsole)
+- Renderers and InputHandlers implemented with the [`SadConsole`](https://github.com/Thraka/SadConsole) 2D console drawing library. Can be used from native SadConsole application.
## Renderer
### C64
diff --git a/doc/SYSTEMS.md b/doc/SYSTEMS.md
index 585006e0..ef472e02 100644
--- a/doc/SYSTEMS.md
+++ b/doc/SYSTEMS.md
@@ -1,9 +1,9 @@
Highbyte.DotNet6502 systems
# Overview
-_Systems_ are implementations of the [```ISystem```](../src/libraries/Highbyte.DotNet6502/Systems/ISystem.cs) and related interfaces, and represent a computer running on the 6502 CPU using the [```Highbyte.DotNet6502```](CPU_LIBRARY.md) library for executing code.
+_Systems_ are implementations of the [`ISystem`](../src/libraries/Highbyte.DotNet6502/Systems/ISystem.cs) and related interfaces, and represent a computer running on the 6502 CPU using the [`Highbyte.DotNet6502`](CPU_LIBRARY.md) library for executing code.
-A system implementation does not have any dependencies to specific rendering, input handling, or audio technologies. For these purposes, see the [Highbyte.DotNet6502.Impl.*](RENDER_INPUT_AUDIO.md) projects.
+A system implementation does not have any dependencies to specific rendering, input handling, or audio technologies. For these purposes, see the [`Highbyte.DotNet6502.Impl.*`](RENDER_INPUT_AUDIO.md) projects.
# Systems
- [Commodore 64](SYSTEMS_C64.md)
diff --git a/doc/SYSTEMS_C64.md b/doc/SYSTEMS_C64.md
index b6aa1a8f..51af8899 100644
--- a/doc/SYSTEMS_C64.md
+++ b/doc/SYSTEMS_C64.md
@@ -5,7 +5,7 @@
A partial implementation of a Commodore 64.
Current capabilities
-- Run Commodore Basic 2.0 from ROM (user supplied Kernal, Basic, and Chargen ROM files) in text mode.
+- Run Commodore Basic 2.0 from ROM (user supplied Kernal, Basic, and Chargen ROM files).
- Limited VIC2 video chip support
- Standard, extended and multi-color character modes
- Standard and multi-color bitmap mode _(newer SkiaRenderer 2/2b in native & WASM, and OpenGL renderer in native only)
@@ -21,8 +21,27 @@ Current capabilities
- Limited SID 6581 audio chip support
- WASM and native app UI
-# Implementation
-Class [```Highbyte.DotNet6502.Systems.Commodore64.C64```](../src/libraries/Highbyte.DotNet6502.Systems/Commodore64/C64.cs)
+# C64 programs that works and how to run them
+
+See [`SYSTEMS_C64_COMPATIBLE_PRG.md`](SYSTEMS_C64_COMPATIBLE_PRG.md)
+
+# Implementations
+
+- System logic [`Highbyte.DotNet6502.Systems.Commodore64`](../src/libraries/Highbyte.DotNet6502.Systems.Commodore64)
+
+- Rendering
+ - [`Highbyte.DotNet6502.Impl.SilkNet.Commodore64.Video`](../src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Video/)
+ - [`Highbyte.DotNet6502.Impl.Skia.Commodore64.Video`](../src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/)
+ - [`Highbyte.DotNet6502.Impl.SadConsole.Commodore64.Video`](../src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Video/)
+
+- Input
+ - [`Highbyte.DotNet6502.Impl.SilkNet.Commodore64.Input`](../src/libraries/Highbyte.DotNet6502.Impl.SilkNet/Commodore64/Input/)
+ - [`Highbyte.DotNet6502.Impl.AspNet.Commodore64.Input`](../src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Input/)
+ - [`Highbyte.DotNet6502.Impl.SadConsole.Commodore64.Input`](../src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Input/)
+
+- Audio
+ - [`Highbyte.DotNet6502.Impl.NAudio.Commodore64.Audio`](../src/libraries/Highbyte.DotNet6502.Impl.NAudio/Commodore64/Audio/)
+ - [`Highbyte.DotNet6502.Impl.AspNet.Commodore64.Audio`](../src/libraries/Highbyte.DotNet6502.Impl.AspNet/Commodore64/Audio/)
TODO
diff --git a/doc/SYSTEMS_C64_COMPATIBLE_PRG.md b/doc/SYSTEMS_C64_COMPATIBLE_PRG.md
new file mode 100644
index 00000000..6cb2fd1b
--- /dev/null
+++ b/doc/SYSTEMS_C64_COMPATIBLE_PRG.md
@@ -0,0 +1,52 @@
+
Compatible C64 programs
+
+A list of applications that seem to work decently with the C64 emulator.
+
+> **Limitations:**
+> - The C64 emulator currently lacks support for the C64 disk and tape drives. Therefore programs must be loaded from the emulator menu (or monitor) as **.prg** files from the host OS file system. Also, any loaded .prg file that tries to access the C64 disk or tape drive most likely will not work (hang).
+>
+> - The video emulation is not cycle exact, and does not cover all tricks possible with the C64 VIC2 video chip. Any advanced app/game/demo may not work as expected.
+>
+> - There are currently different video renderer implementations that can be selected in the C64 config UI. All renderers supports `Character` mode, most support `sprites`, but only some support `Bitmap` mode (which makes them a bit slower). Bitmap mode may be required by some apps, so the correct renderer must be selected before starting certain apps (see Renderer column in table below).
+>
+> - The audio emulation is currently not very accurate, so expect especially music to not sound correct.
+
+
+# Games
+
+| Game | D/L URL | .D64 -> PRG file | .prg type | Renderer (C64 Config menu) | Comment |
+|--------------------|------------------------------------------------|-----------------------------|------------|-----------------------------|-----------|
+| Digiloi | https://csdb.dk/release/download.php?id=213381 | `digiloi.d64` -> `digiloi64` | Basic | SkiaSharp*, SilkNetOpenGl | Character mode. |
+| Last Ninja | https://csdb.dk/release/download.php?id=101848 | `lncro.d64` -> `last ninja/zcs` | Basic | SkiaSharp2b, SilkNetOpenGl | Bitmap mode, sprites. |
+| Mini Zork | https://csdb.dk/release/download.php?id=42919 | `Mini-Zork(L+T).d64` -> `mini-zork /l+t` | Basic | SkiaSharp*, SilkNetOpenGl, SadConsole | Character mode (default charset). |
+| Rally Speedway | https://csdb.dk/release/download.php?id=22736 | `jolly_roger_-_rally_speedway.d64` -> `rallyspeedway` | Basic | SkiaSharp*, SilkNetOpenGl | Character mode, sprites. |
+| Montezuma's Revenge| https://csdb.dk/release/download.php?id=128101 | `Montezuma's Revenge - 1103.d64` -> `montezuma's rev.` | Basic | SkiaSharp*, SilkNetOpenGl | Character mode, sprites. |
+| Bubble Bobble | https://csdb.dk/release/download.php?id=187937 | `Bubble Bobble.d64` -> `bubble bobble` | Basic | SkiaSharp*, SilkNetOpenGl | Character mode, sprites. |
+
+# How extract .prg file
+If the download is a .zip (or other compressions) file, start with unzip:ing it to a folder.
+
+If the unzip:ed contents is a .prg file, then it is possible to be loaded directly into the emulator. No more extra steps needed.
+
+If the unzip:ed contents is a .D64 file (which is a C64 disk image file), a .prg file needs to be extracted from the .D64 file. For this purpose the `c1541` command line utility provided by [VICE](https://vice-emu.sourceforge.io/) emulator can be used.
+- Install VICE (if not already installed)
+- List contents of .D64 image (example)
+ `[VICE install path]\bin\c1541.exe -attach "lncro.d64" -list`
+- Extract .prg file from .D64 image (example). The last argument is what you want to name the extracted .prg file.
+ `[VICE install path]\bin\c1541.exe -attach "lncro.d64" -read "last ninja/zcs" "last ninja-zcs.prg"`
+- In this example, The Last Ninja was extracted from the .D64 image `lncro.d64` to the file `last ninja-zcs.prg`
+
+
+# How load .prg file
+## .prg type Basic
+- File must be loaded as a Basic program, from the menu `Load Basic PRG file`, or via the monitor.
+- Start by typing `RUN` (and press Enter).
+- _The Basic program is most likely a short stub program that starts a machine language program by invoking SYS._
+
+## .prg type Binary
+- File must be loaded as a Binary program (from the menu or monitor).
+- It's loaded into a memory address that is specified in the first two bytes of the .prg file (a C64 standard).
+- Menu: `Load & start binary PRG file`
+ - It's loaded and started automatically.
+- Monitor:
+ - See Monitor docs. Relevant commands are `ll [file]` and `g [address]`
diff --git a/doc/SYSTEMS_GENERIC.md b/doc/SYSTEMS_GENERIC.md
index bcccd233..d061689a 100644
--- a/doc/SYSTEMS_GENERIC.md
+++ b/doc/SYSTEMS_GENERIC.md
@@ -5,6 +5,6 @@
A generic computer, with configurable screen sizes and IO memory locations.
# Implementation
-Class [```Highbyte.DotNet6502.Generic.GenericComputer```](../src/libraries/Highbyte.DotNet6502.Systems/Generic/GenericComputer.cs)
+Class [`Highbyte.DotNet6502.Generic.GenericComputer`](../src/libraries/Highbyte.DotNet6502.Systems/Generic/GenericComputer.cs)
TODO
\ No newline at end of file
diff --git a/doc/Screenshots/Code_integration.png b/doc/Screenshots/Code_integration.png
new file mode 100644
index 00000000..d982d11b
Binary files /dev/null and b/doc/Screenshots/Code_integration.png differ
diff --git a/doc/Screenshots/ConsoleMonitor.png b/doc/Screenshots/ConsoleMonitor.png
new file mode 100644
index 00000000..82cf1b3f
Binary files /dev/null and b/doc/Screenshots/ConsoleMonitor.png differ
diff --git a/doc/Screenshots/NativeConsoleMonitor.png b/doc/Screenshots/NativeConsoleMonitor.png
deleted file mode 100644
index 7991dee8..00000000
Binary files a/doc/Screenshots/NativeConsoleMonitor.png and /dev/null differ
diff --git a/doc/Screenshots/SadConsole_C64_Basic.png b/doc/Screenshots/SadConsole_C64_Basic.png
new file mode 100644
index 00000000..678a7942
Binary files /dev/null and b/doc/Screenshots/SadConsole_C64_Basic.png differ
diff --git a/doc/Screenshots/SadConsole_C64_Monitor.png b/doc/Screenshots/SadConsole_C64_Monitor.png
new file mode 100644
index 00000000..15919637
Binary files /dev/null and b/doc/Screenshots/SadConsole_C64_Monitor.png differ
diff --git a/doc/Screenshots/SadConsole_Generic.png b/doc/Screenshots/SadConsole_Generic.png
deleted file mode 100644
index b8651235..00000000
Binary files a/doc/Screenshots/SadConsole_Generic.png and /dev/null differ
diff --git a/doc/Screenshots/SilkNetNative_C64.png b/doc/Screenshots/SilkNetNative_C64.png
deleted file mode 100644
index ef8fabbf..00000000
Binary files a/doc/Screenshots/SilkNetNative_C64.png and /dev/null differ
diff --git a/doc/Screenshots/SilkNetNative_C64_Basic.png b/doc/Screenshots/SilkNetNative_C64_Basic.png
new file mode 100644
index 00000000..bf2b7e36
Binary files /dev/null and b/doc/Screenshots/SilkNetNative_C64_Basic.png differ
diff --git a/doc/Screenshots/SilkNetNative_C64_BubbleBobble.png b/doc/Screenshots/SilkNetNative_C64_BubbleBobble.png
new file mode 100644
index 00000000..d0dc2088
Binary files /dev/null and b/doc/Screenshots/SilkNetNative_C64_BubbleBobble.png differ
diff --git a/doc/Screenshots/SilkNetNative_C64_raster_scroll.png b/doc/Screenshots/SilkNetNative_C64_raster_scroll.png
new file mode 100644
index 00000000..79983a2f
Binary files /dev/null and b/doc/Screenshots/SilkNetNative_C64_raster_scroll.png differ
diff --git a/doc/Screenshots/SilkNetNative_Monitor.png b/doc/Screenshots/SilkNetNative_Monitor.png
index 31bb0541..b9688c72 100644
Binary files a/doc/Screenshots/SilkNetNative_Monitor.png and b/doc/Screenshots/SilkNetNative_Monitor.png differ
diff --git a/doc/Screenshots/WASM_C64.png b/doc/Screenshots/WASM_C64.png
deleted file mode 100644
index 1087bdc5..00000000
Binary files a/doc/Screenshots/WASM_C64.png and /dev/null differ
diff --git a/doc/Screenshots/WASM_C64_Basic.png b/doc/Screenshots/WASM_C64_Basic.png
new file mode 100644
index 00000000..905c6e0b
Binary files /dev/null and b/doc/Screenshots/WASM_C64_Basic.png differ
diff --git a/doc/Screenshots/WASM_C64_LastNinja.png b/doc/Screenshots/WASM_C64_LastNinja.png
new file mode 100644
index 00000000..29fb363e
Binary files /dev/null and b/doc/Screenshots/WASM_C64_LastNinja.png differ
diff --git a/doc/Screenshots/WASM_C64_Monitor.png b/doc/Screenshots/WASM_C64_Monitor.png
new file mode 100644
index 00000000..6e01f61a
Binary files /dev/null and b/doc/Screenshots/WASM_C64_Monitor.png differ
diff --git a/doc/Screenshots/WASM_C64_raster.png b/doc/Screenshots/WASM_C64_raster.png
deleted file mode 100644
index c3b06805..00000000
Binary files a/doc/Screenshots/WASM_C64_raster.png and /dev/null differ
diff --git a/doc/Screenshots/WASM_Generic.png b/doc/Screenshots/WASM_Generic.png
deleted file mode 100644
index 90b257c0..00000000
Binary files a/doc/Screenshots/WASM_Generic.png and /dev/null differ
diff --git a/doc/Screenshots/WASM_Generic_Snake.png b/doc/Screenshots/WASM_Generic_Snake.png
new file mode 100644
index 00000000..97257c38
Binary files /dev/null and b/doc/Screenshots/WASM_Generic_Snake.png differ
diff --git a/doc/Screenshots/WASM_Monitor.png b/doc/Screenshots/WASM_Monitor.png
deleted file mode 100644
index 643ae7dd..00000000
Binary files a/doc/Screenshots/WASM_Monitor.png and /dev/null differ
diff --git a/resources/images/favicon.ico b/resources/images/favicon.ico
new file mode 100644
index 00000000..f804586d
Binary files /dev/null and b/resources/images/favicon.ico differ
diff --git a/resources/images/favicon.png b/resources/images/favicon.png
new file mode 100644
index 00000000..44bc7cf0
Binary files /dev/null and b/resources/images/favicon.png differ
diff --git a/resources/images/logo-256.png b/resources/images/logo-256.png
new file mode 100644
index 00000000..d9a58cb9
Binary files /dev/null and b/resources/images/logo-256.png differ
diff --git a/resources/images/logo.png b/resources/images/logo.png
new file mode 100644
index 00000000..daad1737
Binary files /dev/null and b/resources/images/logo.png differ
diff --git a/resources/images/logo2.png b/resources/images/logo2.png
new file mode 100644
index 00000000..fb0f3652
Binary files /dev/null and b/resources/images/logo2.png differ
diff --git a/samples/Assembler/Generic/Build/simple.labels b/samples/Assembler/Generic/Build/calc_avg.labels
similarity index 100%
rename from samples/Assembler/Generic/Build/simple.labels
rename to samples/Assembler/Generic/Build/calc_avg.labels
diff --git a/samples/Assembler/Generic/Build/simple.prg b/samples/Assembler/Generic/Build/calc_avg.prg
similarity index 100%
rename from samples/Assembler/Generic/Build/simple.prg
rename to samples/Assembler/Generic/Build/calc_avg.prg
diff --git a/samples/Assembler/Generic/Build/calc_avg.report b/samples/Assembler/Generic/Build/calc_avg.report
new file mode 100644
index 00000000..be6dcc8a
--- /dev/null
+++ b/samples/Assembler/Generic/Build/calc_avg.report
@@ -0,0 +1,18 @@
+
+; ******** Source: C:\Users\highb\source\repos\dotnet-6502\samples\Assembler\Generic\calc_avg.asm
+ 1 ;Calculate the average of two values stored in memory locations, and store the result in another memory location.
+ 2 ;Code written in 6502 assembler using ACME cross assembler syntax.
+ 3 ;Assemble with:
+ 4 ; acme -f cbm -o calc_avg.prg calc_avg.asm
+ 5
+ 6 ;code start address
+ 7 * = $c000
+ 8
+ 9 ;!to "./calc_avg.prg"
+ 10 c000 ad00d0 lda $d000
+ 11 c003 18 clc
+ 12 c004 6d01d0 adc $d001
+ 13 c007 6a ror
+ 14 c008 8d02d0 sta $d002
+ 15 ;In emulator, setup hitting brk instruction to stop
+ 16 c00b 00 brk
diff --git a/samples/Assembler/Generic/Build/hostinteraction_scroll_text.prg b/samples/Assembler/Generic/Build/hostinteraction_scroll_text.prg
index f64fcb9c..3a13cca6 100644
Binary files a/samples/Assembler/Generic/Build/hostinteraction_scroll_text.prg and b/samples/Assembler/Generic/Build/hostinteraction_scroll_text.prg differ
diff --git a/samples/Assembler/Generic/Build/hostinteraction_scroll_text.report b/samples/Assembler/Generic/Build/hostinteraction_scroll_text.report
index 173da84d..d277d485 100644
--- a/samples/Assembler/Generic/Build/hostinteraction_scroll_text.report
+++ b/samples/Assembler/Generic/Build/hostinteraction_scroll_text.report
@@ -100,7 +100,7 @@
98 !convtab raw ;Text conversion setting: pet (PetSCII), raw (none), scr (C64 screen code)
99 SCROLL_TEXT:
100 c049 2020202020202020... !text " "
- 101 c099 4869676862797465... !text "Highbyte, in 2022, proudly presents... A DotNet 6502 CPU emulator! "
+ 101 c099 4869676862797465... !text "Highbyte, in 2024, proudly presents... A DotNet 6502 CPU emulator! "
102 c0df 5468697320736372... !text "This scroller is written in 6502 machine code, updating the emulator host screen indirectly via shared memory. "
103 c150 4772656574696e67... !text "Greetings to all my demo-scene friends from back in the late 80s & early 90s in the groups Them and Virtual!"
104 c1bc 2020202020202020... !text " "
diff --git a/samples/Assembler/Generic/Build/hostinteraction_scroll_text_and_cycle_colors.prg b/samples/Assembler/Generic/Build/hostinteraction_scroll_text_and_cycle_colors.prg
index 42ced4fd..f8933b3a 100644
Binary files a/samples/Assembler/Generic/Build/hostinteraction_scroll_text_and_cycle_colors.prg and b/samples/Assembler/Generic/Build/hostinteraction_scroll_text_and_cycle_colors.prg differ
diff --git a/samples/Assembler/Generic/Build/hostinteraction_scroll_text_and_cycle_colors.report b/samples/Assembler/Generic/Build/hostinteraction_scroll_text_and_cycle_colors.report
index 0bc9eac4..e15a3f63 100644
--- a/samples/Assembler/Generic/Build/hostinteraction_scroll_text_and_cycle_colors.report
+++ b/samples/Assembler/Generic/Build/hostinteraction_scroll_text_and_cycle_colors.report
@@ -303,7 +303,7 @@
301 SCROLL_TEXT:
302 ;!text " "
303 c164 2020202020202020... !text " "
- 304 c18c 4869676862797465... !text "Highbyte, in 2022, proudly presents... A DotNet 6502 CPU emulator! "
+ 304 c18c 4869676862797465... !text "Highbyte, in 2024, proudly presents... A DotNet 6502 CPU emulator! "
305 c1d2 5468697320287261... !text "This (rather choppy) scroller and color cycler is written in 6502 machine code, updating the emulator host screen indirectly via shared memory. "
306 c264 486f6c6420535041... !text "Hold SPACE to flash border color. "
307 c288 4772656574696e67... !text "Greetings to all my demo-scene friends from back in the late 80s & early 90s in the groups Them and Virtual!"
diff --git a/samples/Assembler/Generic/Build/simple.report b/samples/Assembler/Generic/Build/simple.report
deleted file mode 100644
index f6677aa4..00000000
--- a/samples/Assembler/Generic/Build/simple.report
+++ /dev/null
@@ -1,16 +0,0 @@
-
-; ******** Source: C:\Users\highb\source\repos\dotnet-6502\samples\Assembler\Generic\simple.asm
- 1 ;ACME assembler
- 2
- 3 ;code start address
- 4 * = $c000
- 5
- 6 ;!to "./simple.prg"
- 7 ;Add values in two memory locations, rotate right, and store in another memory location.
- 8 c000 ad00d0 lda $d000
- 9 c003 18 clc
- 10 c004 6d01d0 adc $d001
- 11 c007 6a ror
- 12 c008 8d02d0 sta $d002
- 13 ;In emulator, setup hitting brk instruction to stop
- 14 c00b 00 brk
diff --git a/samples/Assembler/Generic/Build/snake6502.report b/samples/Assembler/Generic/Build/snake6502.report
index ae03ca04..910348fb 100644
--- a/samples/Assembler/Generic/Build/snake6502.report
+++ b/samples/Assembler/Generic/Build/snake6502.report
@@ -6,9 +6,9 @@
4 ; - Run in the DotNet6502 emulator.
5
6 * = $c000
- 7 blankCharachter = 32 ; 32 = C64 font space
- 8 snakeCharacter = 160 ; 160 = C64 font inverted space
- 9 appleCharacter = 64 ; 64 = C64 font @ sign
+ 7 blankCharachter = 32 ; 32 = SadConsole default font space
+ 8 snakeCharacter = 160 ; 160 = SadConsole default font inverted space
+ 9 appleCharacter = 64 ; 64 = SadConsole default font @ sign
10 screenMem = 0x0200 ; Start address of screen memory
11 screenCols = 32 ; Note: Cannot change cols without modifying code
12 screenRows = 32 ; Note: Cannot change rows without modifying code
diff --git a/samples/Assembler/Generic/calc_avg.asm b/samples/Assembler/Generic/calc_avg.asm
new file mode 100644
index 00000000..32001161
--- /dev/null
+++ b/samples/Assembler/Generic/calc_avg.asm
@@ -0,0 +1,16 @@
+;Calculate the average of two values stored in memory locations, and store the result in another memory location.
+;Code written in 6502 assembler using ACME cross assembler syntax.
+;Assemble with:
+; acme -f cbm -o calc_avg.prg calc_avg.asm
+
+;code start address
+* = $c000
+
+;!to "./calc_avg.prg"
+ lda $d000
+ clc
+ adc $d001
+ ror
+ sta $d002
+;In emulator, setup hitting brk instruction to stop
+ brk
diff --git a/samples/Assembler/Generic/hostinteraction_scroll_text.asm b/samples/Assembler/Generic/hostinteraction_scroll_text.asm
index daf811b6..0c4827ce 100644
--- a/samples/Assembler/Generic/hostinteraction_scroll_text.asm
+++ b/samples/Assembler/Generic/hostinteraction_scroll_text.asm
@@ -98,7 +98,7 @@ initscroll:
!convtab raw ;Text conversion setting: pet (PetSCII), raw (none), scr (C64 screen code)
SCROLL_TEXT:
!text " "
- !text "Highbyte, in 2022, proudly presents... A DotNet 6502 CPU emulator! "
+ !text "Highbyte, in 2024, proudly presents... A DotNet 6502 CPU emulator! "
!text "This scroller is written in 6502 machine code, updating the emulator host screen indirectly via shared memory. "
!text "Greetings to all my demo-scene friends from back in the late 80s & early 90s in the groups Them and Virtual!"
!text " "
diff --git a/samples/Assembler/Generic/hostinteraction_scroll_text_and_cycle_colors.asm b/samples/Assembler/Generic/hostinteraction_scroll_text_and_cycle_colors.asm
index 78cc49c7..b6e9580a 100644
--- a/samples/Assembler/Generic/hostinteraction_scroll_text_and_cycle_colors.asm
+++ b/samples/Assembler/Generic/hostinteraction_scroll_text_and_cycle_colors.asm
@@ -301,7 +301,7 @@ STATIC_TEXT_COLOR:
SCROLL_TEXT:
;!text " "
!text " "
- !text "Highbyte, in 2022, proudly presents... A DotNet 6502 CPU emulator! "
+ !text "Highbyte, in 2024, proudly presents... A DotNet 6502 CPU emulator! "
!text "This (rather choppy) scroller and color cycler is written in 6502 machine code, updating the emulator host screen indirectly via shared memory. "
!text "Hold SPACE to flash border color. "
!text "Greetings to all my demo-scene friends from back in the late 80s & early 90s in the groups Them and Virtual!"
diff --git a/samples/Assembler/Generic/simple.asm b/samples/Assembler/Generic/simple.asm
deleted file mode 100644
index 3c437952..00000000
--- a/samples/Assembler/Generic/simple.asm
+++ /dev/null
@@ -1,14 +0,0 @@
-;ACME assembler
-
-;code start address
-* = $c000
-
-;!to "./simple.prg"
-;Add values in two memory locations, rotate right, and store in another memory location.
- lda $d000
- clc
- adc $d001
- ror
- sta $d002
-;In emulator, setup hitting brk instruction to stop
- brk
diff --git a/samples/Assembler/Generic/snake6502.asm b/samples/Assembler/Generic/snake6502.asm
index 431fa42c..a2703e53 100644
--- a/samples/Assembler/Generic/snake6502.asm
+++ b/samples/Assembler/Generic/snake6502.asm
@@ -4,9 +4,9 @@
; - Run in the DotNet6502 emulator.
* = $c000
-blankCharachter = 32 ; 32 = C64 font space
-snakeCharacter = 160 ; 160 = C64 font inverted space
-appleCharacter = 64 ; 64 = C64 font @ sign
+blankCharachter = 32 ; 32 = SadConsole default font space
+snakeCharacter = 160 ; 160 = SadConsole default font inverted space
+appleCharacter = 64 ; 64 = SadConsole default font @ sign
screenMem = 0x0200 ; Start address of screen memory
screenCols = 32 ; Note: Cannot change cols without modifying code
screenRows = 32 ; Note: Cannot change rows without modifying code
diff --git a/samples/Assembler/README_BUILD_ASSEMBLER_EXAMPLES.md b/samples/Assembler/README_BUILD_ASSEMBLER_EXAMPLES.md
index 173d8a47..6d8a2e3f 100644
--- a/samples/Assembler/README_BUILD_ASSEMBLER_EXAMPLES.md
+++ b/samples/Assembler/README_BUILD_ASSEMBLER_EXAMPLES.md
@@ -2,29 +2,29 @@
Syntax to compile with ACME
-´´´acme -f cbm -o build\[sourcefile].prg [sourcefile].asm´´´
+`acme -f cbm -o build\[sourcefile].prg [sourcefile].asm`
Build all examples (PowerShell)
-´´´ pwsh
+``` pwsh
.\BuildAll.ps1
-´´´
+```
Other examples in PowerShell
-´´´ pwsh
+``` pwsh
cd Generic
$ACME_APP = "c:\Users\highb\Documents\C64\ACME\acme.exe"
& $ACME_APP -f cbm -o build\hostinteraction_scroll_text_and_cycle_colors.prg hostinteraction_scroll_text_and_cycle_colors.asm
-´´´
+```
-´´´ pwsh
+``` pwsh
cd Generic
$ACME_APP = "c:\Users\highb\Documents\C64\ACME\acme.exe"
& $ACME_APP -f cbm -o build\hostinteraction_scroll_text_and_cycle_colors.prg -r build\hostinteraction_scroll_text_and_cycle_colors.report --vicelabels build\hostinteraction_scroll_text_and_cycle_colors.labels hostinteraction_scroll_text_and_cycle_colors.asm
-´´´
+```
-´´´ pwsh
+``` pwsh
cd C64/Audio
$ACME_APP = "c:\Users\highb\Documents\C64\ACME\acme.exe"
& $ACME_APP -f cbm -o build\irqmusplr.prg -r build\irqmusplr.report --vicelabels build\irqmusplr.labels irqmusplr.asm
-´´´
+```
diff --git a/samples/Basic/README_BUILD_BASIC_EXAMPLES.md b/samples/Basic/README_BUILD_BASIC_EXAMPLES.md
index f2c7ac65..7d5964fa 100644
--- a/samples/Basic/README_BUILD_BASIC_EXAMPLES.md
+++ b/samples/Basic/README_BUILD_BASIC_EXAMPLES.md
@@ -1,9 +1,9 @@
# Convert C64 Basic text files to C64 .prg file
-To convert a text file containing C64 Basic to an actual .prg file that can be loaded into a C64, use the ´´´petcat´´´command from the VICE emulator.
+To convert a text file containing C64 Basic to an actual .prg file that can be loaded into a C64, use the ```petcat```command from the VICE emulator.
Example in PowerShell
-´´´ pwsh
+``` pwsh
$PETCAT_APP = "C:\Users\highb\Documents\C64\VICE\bin\petcat.exe"
cd C64/Sound
& $PETCAT_APP -w2 -o "Build\PlaySoundVoice1TriangleScale.prg" -- "PlaySoundVoice1TriangleScale.txt"
@@ -13,76 +13,76 @@ cd C64/Sound
& $PETCAT_APP -w2 -o "Build\PlaySoundVoice1PulseLab.prg" -- "PlaySoundVoice1PulseLab.txt"
& $PETCAT_APP -w2 -o "Build\PlaySoundVoice1NoiseLab.prg" -- "PlaySoundVoice1NoiseLab.txt"
-´´´
+```
-´´´ pwsh
+``` pwsh
$PETCAT_APP = "C:\Users\highb\Documents\C64\VICE\bin\petcat.exe"
cd C64/Text
& $PETCAT_APP -w2 -o "Build\HelloWorld.prg" -- "HelloWorld.txt"
-´´´
+```
-´´´ pwsh
+``` pwsh
$PETCAT_APP = "C:\Users\highb\Documents\C64\VICE\bin\petcat.exe"
cd C64/Sprites
& $PETCAT_APP -w2 -o "Build\SingleColorSprite.prg" -- "SingleColorSprite.txt"
-´´´
+```
-´´´ pwsh
+``` pwsh
$PETCAT_APP = "C:\Users\highb\Documents\C64\VICE\bin\petcat.exe"
cd C64/Sprites
& $PETCAT_APP -w2 -o "Build\MultiColorSprite.prg" -- "MultiColorSprite.txt"
-´´´
+```
-´´´ pwsh
+``` pwsh
$PETCAT_APP = "C:\Users\highb\Documents\C64\VICE\bin\petcat.exe"
cd C64/Sprites
& $PETCAT_APP -w2 -o "Build\SingleColorSpriteAndHiResGraphics.prg" -- "SingleColorSpriteAndHiResGraphics.txt"
-´´´
+```
-´´´ pwsh
+``` pwsh
$PETCAT_APP = "C:\Users\highb\Documents\C64\VICE\bin\petcat.exe"
cd C64/Sprites
& $PETCAT_APP -w2 -o "Build\SingleColorSpriteAndLowResGraphics.prg" -- "SingleColorSpriteAndLowResGraphics.txt"
-´´´
+```
-´´´ pwsh
+``` pwsh
$PETCAT_APP = "C:\Users\highb\Documents\C64\VICE\bin\petcat.exe"
cd C64/Text
& $PETCAT_APP -w2 -o "Build\ExtendedTextMode.prg" -- "ExtendedTextMode.txt"
-´´´
-´´´ pwsh
+```
+``` pwsh
$PETCAT_APP = "C:\Users\highb\Documents\C64\VICE\bin\petcat.exe"
cd C64/Text
& $PETCAT_APP -w2 -o "Build\MultiColorTextMode.prg" -- "MultiColorTextMode.txt"
-´´´
+```
-´´´ pwsh
+``` pwsh
$PETCAT_APP = "C:\Users\highb\Documents\C64\VICE\bin\petcat.exe"
cd C64/Text
& $PETCAT_APP -w2 -o "Build\CustomCharset.prg" -- "CustomCharset.txt"
-´´´
+```
-´´´ pwsh
+``` pwsh
$PETCAT_APP = "C:\Users\highb\Documents\C64\VICE\bin\petcat.exe"
cd C64/Text
& $PETCAT_APP -w2 -o "Build\RelocateScreenRAM.prg" -- "RelocateScreenRAM.txt"
-´´´
+```
-´´´ pwsh
+``` pwsh
$PETCAT_APP = "C:\Users\highb\Documents\C64\VICE\bin\petcat.exe"
cd C64/Graphics
& $PETCAT_APP -w2 -o "Build\HiResSinePlot.prg" -- "HiResSinePlot.txt"
-´´´
+```
-´´´ pwsh
+``` pwsh
$PETCAT_APP = "C:\Users\highb\Documents\C64\VICE\bin\petcat.exe"
cd C64/Graphics
& $PETCAT_APP -w2 -o "Build\HiResColor.prg" -- "HiResColor.txt"
-´´´
+```
-´´´ pwsh
+``` pwsh
$PETCAT_APP = "C:\Users\highb\Documents\C64\VICE\bin\petcat.exe"
cd C64/Graphics
& $PETCAT_APP -w2 -o "Build\LowResMultiColor.prg" -- "LowResMultiColor.txt"
-´´´
+```
diff --git a/samples/DotNet/CalcAverage/CalcAverage.csproj b/samples/DotNet/CalcAverage/CalcAverage.csproj
new file mode 100644
index 00000000..2361145a
--- /dev/null
+++ b/samples/DotNet/CalcAverage/CalcAverage.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/samples/DotNet/CalcAverage/Program.cs b/samples/DotNet/CalcAverage/Program.cs
new file mode 100644
index 00000000..d89bb97c
--- /dev/null
+++ b/samples/DotNet/CalcAverage/Program.cs
@@ -0,0 +1,35 @@
+// ----------------------------------------------------------------------------------------------------
+// A minimal example of how to load and run a 6502 machine code program.
+// This does not involve a complete computer (such as Commodore 64) but only the CPU and memory.
+// ----------------------------------------------------------------------------------------------------
+
+using Highbyte.DotNet6502;
+using Highbyte.DotNet6502.Utils;
+
+string programFile = "calc_avg.prg"; // Adjust path if necessary
+
+// Create memory (default 64KB)
+Memory mem = new();
+// Load the machine code program into it. Assume two first bytes in the .prg file is the load address.
+mem.Load(programFile, out ushort loadAddress);
+
+// ALT: Create memory (default 64KB) and load the machine code program into it. Assume the .prg file does not contain load address in first two bytes, thus the load address must be specified explicitly.
+//ushort loadAddress = 0xc000;
+//mem.Load(programFile, out _, out _, forceLoadAddress: loadAddress);
+
+// Init variables in memory locations used by the program.
+mem[0xd000] = 64;
+mem[0xd001] = 20;
+Console.WriteLine($"Input 1 (0xd000) = {mem[0xd000]}");
+Console.WriteLine($"Input 2 (0xd001) = {mem[0xd001]}");
+
+// Create the CPU and set program counter (start address).
+var cpu = new CPU();
+cpu.PC = loadAddress;
+
+// Run program. The 6502 program will run until a BRK instruction is encountered.
+cpu.ExecuteUntilBRK(mem);
+
+// Inspect result of program which is stored in memory location 0xd002.
+Console.WriteLine($"Output (0xd002) = {mem[0xd002]}");
+
diff --git a/samples/DotNet/CalcAverage/Program2.cs b/samples/DotNet/CalcAverage/Program2.cs
new file mode 100644
index 00000000..d0a7e095
--- /dev/null
+++ b/samples/DotNet/CalcAverage/Program2.cs
@@ -0,0 +1,63 @@
+//// ----------------------------------------------------------------------------------------------------
+//// An example of how to enter a machine code program directly into memory,
+//// and instanciating the "Generic" computer with logging of executed instructions.
+//// ----------------------------------------------------------------------------------------------------
+
+//using Highbyte.DotNet6502;
+//using Highbyte.DotNet6502.Systems.Generic;
+//using Highbyte.DotNet6502.Utils;
+
+//// Test program to calculate average of two values
+//// - adds values from two memory location
+//// - divides it by 2 (rotate right one bit position)
+//// - stores it in another memory location
+
+//// Load input data into memory
+//byte value1 = 64;
+//byte value2 = 20;
+//ushort value1Address = 0xd000;
+//ushort value2Address = 0xd001;
+//ushort resultAddress = 0xd002;
+//var mem = new Memory();
+//mem[value1Address] = value1;
+//mem[value2Address] = value2;
+
+//// Load machine code into memory
+//ushort codeAddress = 0xc000;
+//ushort codeInsAddress = codeAddress;
+//mem[codeInsAddress++] = 0xad; // LDA (Load Accumulator)
+//mem[codeInsAddress++] = 0x00; // |-Lowbyte of $d000
+//mem[codeInsAddress++] = 0xd0; // |-Highbyte of $d000
+//mem[codeInsAddress++] = 0x18; // CLC (Clear Carry flag)
+//mem[codeInsAddress++] = 0x6d; // ADC (Add with Carry, adds memory to accumulator)
+//mem[codeInsAddress++] = 0x01; // |-Lowbyte of $d001
+//mem[codeInsAddress++] = 0xd0; // |-Highbyte of $d001
+//mem[codeInsAddress++] = 0x6a; // ROR (Rotate Right, rotates accumulator right one bit position)
+//mem[codeInsAddress++] = 0x8d; // STA (Store Accumulator, store to accumulator to memory)
+//mem[codeInsAddress++] = 0x02; // |-Lowbyte of $d002
+//mem[codeInsAddress++] = 0xd0; // |-Highbyte of $d002
+//mem[codeInsAddress++] = 0x00; // BRK (Break/Force Interrupt) - emulator configured to stop execution when reaching this instruction
+
+//// Initialize a "Generic" 6502 computer emulator with CPU, memory, and execution parameters
+//var computerBuilder = new GenericComputerBuilder();
+//computerBuilder
+// .WithCPU()
+// .WithStartAddress(codeAddress)
+// .WithMemory(mem)
+// .WithInstructionExecutedEventHandler(
+// (s, e) => Console.WriteLine(OutputGen.GetLastInstructionDisassembly(e.CPU, e.Mem)))
+// .WithExecOptions(options =>
+// {
+// options.ExecuteUntilInstruction = OpCodeId.BRK; // Emulator will stop executing when a BRK instruction is reached.
+// });
+//var computer = computerBuilder.Build();
+
+//// Run program
+//computer.Run();
+//Console.WriteLine($"Execution stopped");
+//Console.WriteLine($"CPU state: {OutputGen.GetProcessorState(computer.CPU)}");
+//Console.WriteLine($"Stats: {computer.CPU.ExecState.InstructionsExecutionCount} instruction(s) processed, and used {computer.CPU.ExecState.CyclesConsumed} cycles.");
+
+//// Print result
+//byte result = mem[resultAddress];
+//Console.WriteLine($"Result: ({value1} + {value2}) / 2 = {result}");
diff --git a/samples/DotNet/CalcAverage/calc_avg.asm b/samples/DotNet/CalcAverage/calc_avg.asm
new file mode 100644
index 00000000..2b61aa20
--- /dev/null
+++ b/samples/DotNet/CalcAverage/calc_avg.asm
@@ -0,0 +1,16 @@
+;Calculate the average of two values stored in memory locations, and store the result in another memory location.
+;Code written in 6502 assembler using ACME cross assembler syntax.
+;Assemble with:
+; acme -f cbm -o calc_avg.prg calc_avg.asm
+
+;code load address
+* = $c000
+
+;start of code
+ lda $d000
+ clc
+ adc $d001
+ ror
+ sta $d002
+;In emulator, setup stop on hitting brk instruction.
+ brk
diff --git a/samples/DotNet/CalcAverage/calc_avg.prg b/samples/DotNet/CalcAverage/calc_avg.prg
new file mode 100644
index 00000000..536b6bd9
Binary files /dev/null and b/samples/DotNet/CalcAverage/calc_avg.prg differ
diff --git a/samples/BlazorWasmTest/App.razor b/samples/DotNet/Old/BlazorWasmTest/App.razor
similarity index 100%
rename from samples/BlazorWasmTest/App.razor
rename to samples/DotNet/Old/BlazorWasmTest/App.razor
diff --git a/samples/BlazorWasmTest/BlazorWasmTest.csproj b/samples/DotNet/Old/BlazorWasmTest/BlazorWasmTest.csproj
similarity index 100%
rename from samples/BlazorWasmTest/BlazorWasmTest.csproj
rename to samples/DotNet/Old/BlazorWasmTest/BlazorWasmTest.csproj
diff --git a/samples/BlazorWasmTest/Helpers/InputSystem.cs b/samples/DotNet/Old/BlazorWasmTest/Helpers/InputSystem.cs
similarity index 100%
rename from samples/BlazorWasmTest/Helpers/InputSystem.cs
rename to samples/DotNet/Old/BlazorWasmTest/Helpers/InputSystem.cs
diff --git a/samples/BlazorWasmTest/Helpers/ScreenStatusBitFlags.cs b/samples/DotNet/Old/BlazorWasmTest/Helpers/ScreenStatusBitFlags.cs
similarity index 100%
rename from samples/BlazorWasmTest/Helpers/ScreenStatusBitFlags.cs
rename to samples/DotNet/Old/BlazorWasmTest/Helpers/ScreenStatusBitFlags.cs
diff --git a/samples/BlazorWasmTest/Pages/DotNet6502Emulator.razor b/samples/DotNet/Old/BlazorWasmTest/Pages/DotNet6502Emulator.razor
similarity index 100%
rename from samples/BlazorWasmTest/Pages/DotNet6502Emulator.razor
rename to samples/DotNet/Old/BlazorWasmTest/Pages/DotNet6502Emulator.razor
diff --git a/samples/BlazorWasmTest/Pages/DotNet6502EmulatorComponent.cs b/samples/DotNet/Old/BlazorWasmTest/Pages/DotNet6502EmulatorComponent.cs
similarity index 100%
rename from samples/BlazorWasmTest/Pages/DotNet6502EmulatorComponent.cs
rename to samples/DotNet/Old/BlazorWasmTest/Pages/DotNet6502EmulatorComponent.cs
diff --git a/samples/BlazorWasmTest/Program.cs b/samples/DotNet/Old/BlazorWasmTest/Program.cs
similarity index 100%
rename from samples/BlazorWasmTest/Program.cs
rename to samples/DotNet/Old/BlazorWasmTest/Program.cs
diff --git a/samples/BlazorWasmTest/Properties/launchSettings.json b/samples/DotNet/Old/BlazorWasmTest/Properties/launchSettings.json
similarity index 100%
rename from samples/BlazorWasmTest/Properties/launchSettings.json
rename to samples/DotNet/Old/BlazorWasmTest/Properties/launchSettings.json
diff --git a/samples/BlazorWasmTest/Shared/MainLayout.razor b/samples/DotNet/Old/BlazorWasmTest/Shared/MainLayout.razor
similarity index 100%
rename from samples/BlazorWasmTest/Shared/MainLayout.razor
rename to samples/DotNet/Old/BlazorWasmTest/Shared/MainLayout.razor
diff --git a/samples/BlazorWasmTest/Shared/MainLayout.razor.css b/samples/DotNet/Old/BlazorWasmTest/Shared/MainLayout.razor.css
similarity index 100%
rename from samples/BlazorWasmTest/Shared/MainLayout.razor.css
rename to samples/DotNet/Old/BlazorWasmTest/Shared/MainLayout.razor.css
diff --git a/samples/BlazorWasmTest/_Imports.razor b/samples/DotNet/Old/BlazorWasmTest/_Imports.razor
similarity index 100%
rename from samples/BlazorWasmTest/_Imports.razor
rename to samples/DotNet/Old/BlazorWasmTest/_Imports.razor
diff --git a/samples/BlazorWasmTest/global.json b/samples/DotNet/Old/BlazorWasmTest/global.json
similarity index 100%
rename from samples/BlazorWasmTest/global.json
rename to samples/DotNet/Old/BlazorWasmTest/global.json
diff --git a/samples/BlazorWasmTest/screenshot.png b/samples/DotNet/Old/BlazorWasmTest/screenshot.png
similarity index 100%
rename from samples/BlazorWasmTest/screenshot.png
rename to samples/DotNet/Old/BlazorWasmTest/screenshot.png
diff --git a/samples/BlazorWasmTest/wwwroot/6502binaries/hostinteraction_scroll_text_and_cycle_colors.prg b/samples/DotNet/Old/BlazorWasmTest/wwwroot/6502binaries/hostinteraction_scroll_text_and_cycle_colors.prg
similarity index 100%
rename from samples/BlazorWasmTest/wwwroot/6502binaries/hostinteraction_scroll_text_and_cycle_colors.prg
rename to samples/DotNet/Old/BlazorWasmTest/wwwroot/6502binaries/hostinteraction_scroll_text_and_cycle_colors.prg
diff --git a/samples/BlazorWasmTest/wwwroot/6502binaries/snake6502.prg b/samples/DotNet/Old/BlazorWasmTest/wwwroot/6502binaries/snake6502.prg
similarity index 100%
rename from samples/BlazorWasmTest/wwwroot/6502binaries/snake6502.prg
rename to samples/DotNet/Old/BlazorWasmTest/wwwroot/6502binaries/snake6502.prg
diff --git a/samples/BlazorWasmTest/wwwroot/6502binaries/snake6502_qr_hosted.png b/samples/DotNet/Old/BlazorWasmTest/wwwroot/6502binaries/snake6502_qr_hosted.png
similarity index 100%
rename from samples/BlazorWasmTest/wwwroot/6502binaries/snake6502_qr_hosted.png
rename to samples/DotNet/Old/BlazorWasmTest/wwwroot/6502binaries/snake6502_qr_hosted.png
diff --git a/samples/BlazorWasmTest/wwwroot/6502binaries/snake6502_qr_large_hosted.png b/samples/DotNet/Old/BlazorWasmTest/wwwroot/6502binaries/snake6502_qr_large_hosted.png
similarity index 100%
rename from samples/BlazorWasmTest/wwwroot/6502binaries/snake6502_qr_large_hosted.png
rename to samples/DotNet/Old/BlazorWasmTest/wwwroot/6502binaries/snake6502_qr_large_hosted.png
diff --git a/samples/BlazorWasmTest/wwwroot/6502binaries/snake6502_qr_local.png b/samples/DotNet/Old/BlazorWasmTest/wwwroot/6502binaries/snake6502_qr_local.png
similarity index 100%
rename from samples/BlazorWasmTest/wwwroot/6502binaries/snake6502_qr_local.png
rename to samples/DotNet/Old/BlazorWasmTest/wwwroot/6502binaries/snake6502_qr_local.png
diff --git a/samples/BlazorWasmTest/wwwroot/css/app.css b/samples/DotNet/Old/BlazorWasmTest/wwwroot/css/app.css
similarity index 100%
rename from samples/BlazorWasmTest/wwwroot/css/app.css
rename to samples/DotNet/Old/BlazorWasmTest/wwwroot/css/app.css
diff --git a/samples/BlazorWasmTest/wwwroot/css/c64.css b/samples/DotNet/Old/BlazorWasmTest/wwwroot/css/c64.css
similarity index 100%
rename from samples/BlazorWasmTest/wwwroot/css/c64.css
rename to samples/DotNet/Old/BlazorWasmTest/wwwroot/css/c64.css
diff --git a/samples/BlazorWasmTest/wwwroot/css/c64colors.css b/samples/DotNet/Old/BlazorWasmTest/wwwroot/css/c64colors.css
similarity index 100%
rename from samples/BlazorWasmTest/wwwroot/css/c64colors.css
rename to samples/DotNet/Old/BlazorWasmTest/wwwroot/css/c64colors.css
diff --git a/samples/BlazorWasmTest/wwwroot/favicon.ico b/samples/DotNet/Old/BlazorWasmTest/wwwroot/favicon.ico
similarity index 100%
rename from samples/BlazorWasmTest/wwwroot/favicon.ico
rename to samples/DotNet/Old/BlazorWasmTest/wwwroot/favicon.ico
diff --git a/samples/BlazorWasmTest/wwwroot/fonts/C64_Pro_Mono-STYLE.woff2 b/samples/DotNet/Old/BlazorWasmTest/wwwroot/fonts/C64_Pro_Mono-STYLE.woff2
similarity index 100%
rename from samples/BlazorWasmTest/wwwroot/fonts/C64_Pro_Mono-STYLE.woff2
rename to samples/DotNet/Old/BlazorWasmTest/wwwroot/fonts/C64_Pro_Mono-STYLE.woff2
diff --git a/samples/BlazorWasmTest/wwwroot/index.html b/samples/DotNet/Old/BlazorWasmTest/wwwroot/index.html
similarity index 100%
rename from samples/BlazorWasmTest/wwwroot/index.html
rename to samples/DotNet/Old/BlazorWasmTest/wwwroot/index.html
diff --git a/samples/BlazorWasmTest/wwwroot/test_layout_32x32.html b/samples/DotNet/Old/BlazorWasmTest/wwwroot/test_layout_32x32.html
similarity index 100%
rename from samples/BlazorWasmTest/wwwroot/test_layout_32x32.html
rename to samples/DotNet/Old/BlazorWasmTest/wwwroot/test_layout_32x32.html
diff --git a/samples/BlazorWasmTest/wwwroot/test_layout_40x25.html b/samples/DotNet/Old/BlazorWasmTest/wwwroot/test_layout_40x25.html
similarity index 100%
rename from samples/BlazorWasmTest/wwwroot/test_layout_40x25.html
rename to samples/DotNet/Old/BlazorWasmTest/wwwroot/test_layout_40x25.html
diff --git a/samples/ConsoleTestPrograms/ConsoleTestPrograms.csproj b/samples/DotNet/Old/ConsoleTestPrograms/ConsoleTestPrograms.csproj
similarity index 100%
rename from samples/ConsoleTestPrograms/ConsoleTestPrograms.csproj
rename to samples/DotNet/Old/ConsoleTestPrograms/ConsoleTestPrograms.csproj
diff --git a/samples/ConsoleTestPrograms/HostInteractionLab_Scroll_Text.cs b/samples/DotNet/Old/ConsoleTestPrograms/HostInteractionLab_Scroll_Text.cs
similarity index 100%
rename from samples/ConsoleTestPrograms/HostInteractionLab_Scroll_Text.cs
rename to samples/DotNet/Old/ConsoleTestPrograms/HostInteractionLab_Scroll_Text.cs
diff --git a/samples/ConsoleTestPrograms/Program.cs b/samples/DotNet/Old/ConsoleTestPrograms/Program.cs
similarity index 100%
rename from samples/ConsoleTestPrograms/Program.cs
rename to samples/DotNet/Old/ConsoleTestPrograms/Program.cs
diff --git a/samples/ConsoleTestPrograms/Run16bitMultiplyProgram.cs b/samples/DotNet/Old/ConsoleTestPrograms/Run16bitMultiplyProgram.cs
similarity index 100%
rename from samples/ConsoleTestPrograms/Run16bitMultiplyProgram.cs
rename to samples/DotNet/Old/ConsoleTestPrograms/Run16bitMultiplyProgram.cs
diff --git a/samples/ConsoleTestPrograms/Run6502FunctionalTest.cs b/samples/DotNet/Old/ConsoleTestPrograms/Run6502FunctionalTest.cs
similarity index 100%
rename from samples/ConsoleTestPrograms/Run6502FunctionalTest.cs
rename to samples/DotNet/Old/ConsoleTestPrograms/Run6502FunctionalTest.cs
diff --git a/samples/ConsoleTestPrograms/RunSimple.cs b/samples/DotNet/Old/ConsoleTestPrograms/RunSimple.cs
similarity index 100%
rename from samples/ConsoleTestPrograms/RunSimple.cs
rename to samples/DotNet/Old/ConsoleTestPrograms/RunSimple.cs
diff --git a/samples/ConsoleTestPrograms/RunTestProgram.cs b/samples/DotNet/Old/ConsoleTestPrograms/RunTestProgram.cs
similarity index 100%
rename from samples/ConsoleTestPrograms/RunTestProgram.cs
rename to samples/DotNet/Old/ConsoleTestPrograms/RunTestProgram.cs
diff --git a/samples/ConsoleTestPrograms/RunTestProgram2.cs b/samples/DotNet/Old/ConsoleTestPrograms/RunTestProgram2.cs
similarity index 100%
rename from samples/ConsoleTestPrograms/RunTestProgram2.cs
rename to samples/DotNet/Old/ConsoleTestPrograms/RunTestProgram2.cs
diff --git a/samples/DotNet/dotnet-6502-examples.sln b/samples/DotNet/dotnet-6502-examples.sln
new file mode 100644
index 00000000..c9fcd51f
--- /dev/null
+++ b/samples/DotNet/dotnet-6502-examples.sln
@@ -0,0 +1,98 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.1.32228.430
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Highbyte.DotNet6502", "..\..\src\libraries\Highbyte.DotNet6502\Highbyte.DotNet6502.csproj", "{F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{588A47B5-B723-4B4F-A05F-D26A684B5E25}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Highbyte.DotNet6502.Systems", "..\..\src\libraries\Highbyte.DotNet6502.Systems\Highbyte.DotNet6502.Systems.csproj", "{127A9553-17C4-407D-9B9E-486FAFAACFA3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Highbyte.DotNet6502.Monitor", "..\..\src\libraries\Highbyte.DotNet6502.Monitor\Highbyte.DotNet6502.Monitor.csproj", "{A3405B42-1924-4638-AFD6-50B2425A4038}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Highbyte.DotNet6502.Systems.Generic", "..\..\src\libraries\Highbyte.DotNet6502.Systems.Generic\Highbyte.DotNet6502.Systems.Generic.csproj", "{94C4D835-8363-46DD-B6E4-BCD7F1844103}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CalcAverage", "CalcAverage\CalcAverage.csproj", "{21F96A21-5D73-4660-9A3C-24B9569B1EFA}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Debug|x64.Build.0 = Debug|Any CPU
+ {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Debug|x86.Build.0 = Debug|Any CPU
+ {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Release|x64.ActiveCfg = Release|Any CPU
+ {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Release|x64.Build.0 = Release|Any CPU
+ {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Release|x86.ActiveCfg = Release|Any CPU
+ {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Release|x86.Build.0 = Release|Any CPU
+ {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Debug|x64.Build.0 = Debug|Any CPU
+ {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Debug|x86.Build.0 = Debug|Any CPU
+ {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Release|x64.ActiveCfg = Release|Any CPU
+ {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Release|x64.Build.0 = Release|Any CPU
+ {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Release|x86.ActiveCfg = Release|Any CPU
+ {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Release|x86.Build.0 = Release|Any CPU
+ {A3405B42-1924-4638-AFD6-50B2425A4038}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A3405B42-1924-4638-AFD6-50B2425A4038}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A3405B42-1924-4638-AFD6-50B2425A4038}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A3405B42-1924-4638-AFD6-50B2425A4038}.Debug|x64.Build.0 = Debug|Any CPU
+ {A3405B42-1924-4638-AFD6-50B2425A4038}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A3405B42-1924-4638-AFD6-50B2425A4038}.Debug|x86.Build.0 = Debug|Any CPU
+ {A3405B42-1924-4638-AFD6-50B2425A4038}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A3405B42-1924-4638-AFD6-50B2425A4038}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A3405B42-1924-4638-AFD6-50B2425A4038}.Release|x64.ActiveCfg = Release|Any CPU
+ {A3405B42-1924-4638-AFD6-50B2425A4038}.Release|x64.Build.0 = Release|Any CPU
+ {A3405B42-1924-4638-AFD6-50B2425A4038}.Release|x86.ActiveCfg = Release|Any CPU
+ {A3405B42-1924-4638-AFD6-50B2425A4038}.Release|x86.Build.0 = Release|Any CPU
+ {94C4D835-8363-46DD-B6E4-BCD7F1844103}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {94C4D835-8363-46DD-B6E4-BCD7F1844103}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {94C4D835-8363-46DD-B6E4-BCD7F1844103}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {94C4D835-8363-46DD-B6E4-BCD7F1844103}.Debug|x64.Build.0 = Debug|Any CPU
+ {94C4D835-8363-46DD-B6E4-BCD7F1844103}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {94C4D835-8363-46DD-B6E4-BCD7F1844103}.Debug|x86.Build.0 = Debug|Any CPU
+ {94C4D835-8363-46DD-B6E4-BCD7F1844103}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {94C4D835-8363-46DD-B6E4-BCD7F1844103}.Release|Any CPU.Build.0 = Release|Any CPU
+ {94C4D835-8363-46DD-B6E4-BCD7F1844103}.Release|x64.ActiveCfg = Release|Any CPU
+ {94C4D835-8363-46DD-B6E4-BCD7F1844103}.Release|x64.Build.0 = Release|Any CPU
+ {94C4D835-8363-46DD-B6E4-BCD7F1844103}.Release|x86.ActiveCfg = Release|Any CPU
+ {94C4D835-8363-46DD-B6E4-BCD7F1844103}.Release|x86.Build.0 = Release|Any CPU
+ {21F96A21-5D73-4660-9A3C-24B9569B1EFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {21F96A21-5D73-4660-9A3C-24B9569B1EFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {21F96A21-5D73-4660-9A3C-24B9569B1EFA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {21F96A21-5D73-4660-9A3C-24B9569B1EFA}.Debug|x64.Build.0 = Debug|Any CPU
+ {21F96A21-5D73-4660-9A3C-24B9569B1EFA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {21F96A21-5D73-4660-9A3C-24B9569B1EFA}.Debug|x86.Build.0 = Debug|Any CPU
+ {21F96A21-5D73-4660-9A3C-24B9569B1EFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {21F96A21-5D73-4660-9A3C-24B9569B1EFA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {21F96A21-5D73-4660-9A3C-24B9569B1EFA}.Release|x64.ActiveCfg = Release|Any CPU
+ {21F96A21-5D73-4660-9A3C-24B9569B1EFA}.Release|x64.Build.0 = Release|Any CPU
+ {21F96A21-5D73-4660-9A3C-24B9569B1EFA}.Release|x86.ActiveCfg = Release|Any CPU
+ {21F96A21-5D73-4660-9A3C-24B9569B1EFA}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {21F96A21-5D73-4660-9A3C-24B9569B1EFA} = {588A47B5-B723-4B4F-A05F-D26A684B5E25}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {0F55B6C2-E4B4-4F2C-9D2A-D63A17F3B5C4}
+ EndGlobalSection
+EndGlobal
diff --git a/samples/dotnet-6502-examples.sln b/samples/dotnet-6502-examples.sln
deleted file mode 100644
index 7bce42bc..00000000
--- a/samples/dotnet-6502-examples.sln
+++ /dev/null
@@ -1,175 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.1.32228.430
-MinimumVisualStudioVersion = 15.0.26124.0
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Highbyte.DotNet6502", "..\src\libraries\Highbyte.DotNet6502\Highbyte.DotNet6502.csproj", "{F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Highbyte.DotNet6502.Tests", "..\tests\Highbyte.DotNet6502.Tests\Highbyte.DotNet6502.Tests.csproj", "{4C634415-8E21-4813-A40C-E19025C300D4}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTestPrograms", "ConsoleTestPrograms\ConsoleTestPrograms.csproj", "{B5BA78CE-B37C-4644-936C-4AAB4C5494A7}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{588A47B5-B723-4B4F-A05F-D26A684B5E25}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWasmTest", "BlazorWasmTest\BlazorWasmTest.csproj", "{DD12665D-24D3-4938-9757-B5D3ED254563}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Highbyte.DotNet6502.Systems", "..\src\libraries\Highbyte.DotNet6502.Systems\Highbyte.DotNet6502.Systems.csproj", "{127A9553-17C4-407D-9B9E-486FAFAACFA3}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Impl", "Impl", "{6430B62F-00CD-47AB-AB6E-7F65415921DB}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Highbyte.DotNet6502.Impl.Skia", "..\src\libraries\Highbyte.DotNet6502.Impl.Skia\Highbyte.DotNet6502.Impl.Skia.csproj", "{EAF6E615-5895-46EE-B396-51EF14E0461B}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Highbyte.DotNet6502.Impl.SilkNet", "..\src\libraries\Highbyte.DotNet6502.Impl.SilkNet\Highbyte.DotNet6502.Impl.SilkNet.csproj", "{D7DF99BE-B47A-493C-A5BD-D2A1B55793F2}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Highbyte.DotNet6502.Impl.SadConsole", "..\src\libraries\Highbyte.DotNet6502.Impl.SadConsole\Highbyte.DotNet6502.Impl.SadConsole.csproj", "{A34EA000-0FC9-46E7-B4B2-0F7E6CB04367}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Highbyte.DotNet6502.Impl.AspNet", "..\src\libraries\Highbyte.DotNet6502.Impl.AspNet\Highbyte.DotNet6502.Impl.AspNet.csproj", "{6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Highbyte.DotNet6502.Monitor", "..\src\libraries\Highbyte.DotNet6502.Monitor\Highbyte.DotNet6502.Monitor.csproj", "{A3405B42-1924-4638-AFD6-50B2425A4038}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
- Release|Any CPU = Release|Any CPU
- Release|x64 = Release|x64
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Debug|x64.ActiveCfg = Debug|Any CPU
- {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Debug|x64.Build.0 = Debug|Any CPU
- {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Debug|x86.ActiveCfg = Debug|Any CPU
- {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Debug|x86.Build.0 = Debug|Any CPU
- {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Release|Any CPU.Build.0 = Release|Any CPU
- {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Release|x64.ActiveCfg = Release|Any CPU
- {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Release|x64.Build.0 = Release|Any CPU
- {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Release|x86.ActiveCfg = Release|Any CPU
- {F613F6C7-F4BF-47F8-86A5-69DF8EDF12B3}.Release|x86.Build.0 = Release|Any CPU
- {4C634415-8E21-4813-A40C-E19025C300D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4C634415-8E21-4813-A40C-E19025C300D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4C634415-8E21-4813-A40C-E19025C300D4}.Debug|x64.ActiveCfg = Debug|Any CPU
- {4C634415-8E21-4813-A40C-E19025C300D4}.Debug|x64.Build.0 = Debug|Any CPU
- {4C634415-8E21-4813-A40C-E19025C300D4}.Debug|x86.ActiveCfg = Debug|Any CPU
- {4C634415-8E21-4813-A40C-E19025C300D4}.Debug|x86.Build.0 = Debug|Any CPU
- {4C634415-8E21-4813-A40C-E19025C300D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4C634415-8E21-4813-A40C-E19025C300D4}.Release|Any CPU.Build.0 = Release|Any CPU
- {4C634415-8E21-4813-A40C-E19025C300D4}.Release|x64.ActiveCfg = Release|Any CPU
- {4C634415-8E21-4813-A40C-E19025C300D4}.Release|x64.Build.0 = Release|Any CPU
- {4C634415-8E21-4813-A40C-E19025C300D4}.Release|x86.ActiveCfg = Release|Any CPU
- {4C634415-8E21-4813-A40C-E19025C300D4}.Release|x86.Build.0 = Release|Any CPU
- {B5BA78CE-B37C-4644-936C-4AAB4C5494A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B5BA78CE-B37C-4644-936C-4AAB4C5494A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B5BA78CE-B37C-4644-936C-4AAB4C5494A7}.Debug|x64.ActiveCfg = Debug|Any CPU
- {B5BA78CE-B37C-4644-936C-4AAB4C5494A7}.Debug|x64.Build.0 = Debug|Any CPU
- {B5BA78CE-B37C-4644-936C-4AAB4C5494A7}.Debug|x86.ActiveCfg = Debug|Any CPU
- {B5BA78CE-B37C-4644-936C-4AAB4C5494A7}.Debug|x86.Build.0 = Debug|Any CPU
- {B5BA78CE-B37C-4644-936C-4AAB4C5494A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B5BA78CE-B37C-4644-936C-4AAB4C5494A7}.Release|Any CPU.Build.0 = Release|Any CPU
- {B5BA78CE-B37C-4644-936C-4AAB4C5494A7}.Release|x64.ActiveCfg = Release|Any CPU
- {B5BA78CE-B37C-4644-936C-4AAB4C5494A7}.Release|x64.Build.0 = Release|Any CPU
- {B5BA78CE-B37C-4644-936C-4AAB4C5494A7}.Release|x86.ActiveCfg = Release|Any CPU
- {B5BA78CE-B37C-4644-936C-4AAB4C5494A7}.Release|x86.Build.0 = Release|Any CPU
- {DD12665D-24D3-4938-9757-B5D3ED254563}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DD12665D-24D3-4938-9757-B5D3ED254563}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DD12665D-24D3-4938-9757-B5D3ED254563}.Debug|x64.ActiveCfg = Debug|Any CPU
- {DD12665D-24D3-4938-9757-B5D3ED254563}.Debug|x64.Build.0 = Debug|Any CPU
- {DD12665D-24D3-4938-9757-B5D3ED254563}.Debug|x86.ActiveCfg = Debug|Any CPU
- {DD12665D-24D3-4938-9757-B5D3ED254563}.Debug|x86.Build.0 = Debug|Any CPU
- {DD12665D-24D3-4938-9757-B5D3ED254563}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DD12665D-24D3-4938-9757-B5D3ED254563}.Release|Any CPU.Build.0 = Release|Any CPU
- {DD12665D-24D3-4938-9757-B5D3ED254563}.Release|x64.ActiveCfg = Release|Any CPU
- {DD12665D-24D3-4938-9757-B5D3ED254563}.Release|x64.Build.0 = Release|Any CPU
- {DD12665D-24D3-4938-9757-B5D3ED254563}.Release|x86.ActiveCfg = Release|Any CPU
- {DD12665D-24D3-4938-9757-B5D3ED254563}.Release|x86.Build.0 = Release|Any CPU
- {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Debug|x64.ActiveCfg = Debug|Any CPU
- {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Debug|x64.Build.0 = Debug|Any CPU
- {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Debug|x86.ActiveCfg = Debug|Any CPU
- {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Debug|x86.Build.0 = Debug|Any CPU
- {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Release|Any CPU.Build.0 = Release|Any CPU
- {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Release|x64.ActiveCfg = Release|Any CPU
- {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Release|x64.Build.0 = Release|Any CPU
- {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Release|x86.ActiveCfg = Release|Any CPU
- {127A9553-17C4-407D-9B9E-486FAFAACFA3}.Release|x86.Build.0 = Release|Any CPU
- {EAF6E615-5895-46EE-B396-51EF14E0461B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {EAF6E615-5895-46EE-B396-51EF14E0461B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {EAF6E615-5895-46EE-B396-51EF14E0461B}.Debug|x64.ActiveCfg = Debug|Any CPU
- {EAF6E615-5895-46EE-B396-51EF14E0461B}.Debug|x64.Build.0 = Debug|Any CPU
- {EAF6E615-5895-46EE-B396-51EF14E0461B}.Debug|x86.ActiveCfg = Debug|Any CPU
- {EAF6E615-5895-46EE-B396-51EF14E0461B}.Debug|x86.Build.0 = Debug|Any CPU
- {EAF6E615-5895-46EE-B396-51EF14E0461B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {EAF6E615-5895-46EE-B396-51EF14E0461B}.Release|Any CPU.Build.0 = Release|Any CPU
- {EAF6E615-5895-46EE-B396-51EF14E0461B}.Release|x64.ActiveCfg = Release|Any CPU
- {EAF6E615-5895-46EE-B396-51EF14E0461B}.Release|x64.Build.0 = Release|Any CPU
- {EAF6E615-5895-46EE-B396-51EF14E0461B}.Release|x86.ActiveCfg = Release|Any CPU
- {EAF6E615-5895-46EE-B396-51EF14E0461B}.Release|x86.Build.0 = Release|Any CPU
- {D7DF99BE-B47A-493C-A5BD-D2A1B55793F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D7DF99BE-B47A-493C-A5BD-D2A1B55793F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D7DF99BE-B47A-493C-A5BD-D2A1B55793F2}.Debug|x64.ActiveCfg = Debug|Any CPU
- {D7DF99BE-B47A-493C-A5BD-D2A1B55793F2}.Debug|x64.Build.0 = Debug|Any CPU
- {D7DF99BE-B47A-493C-A5BD-D2A1B55793F2}.Debug|x86.ActiveCfg = Debug|Any CPU
- {D7DF99BE-B47A-493C-A5BD-D2A1B55793F2}.Debug|x86.Build.0 = Debug|Any CPU
- {D7DF99BE-B47A-493C-A5BD-D2A1B55793F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D7DF99BE-B47A-493C-A5BD-D2A1B55793F2}.Release|Any CPU.Build.0 = Release|Any CPU
- {D7DF99BE-B47A-493C-A5BD-D2A1B55793F2}.Release|x64.ActiveCfg = Release|Any CPU
- {D7DF99BE-B47A-493C-A5BD-D2A1B55793F2}.Release|x64.Build.0 = Release|Any CPU
- {D7DF99BE-B47A-493C-A5BD-D2A1B55793F2}.Release|x86.ActiveCfg = Release|Any CPU
- {D7DF99BE-B47A-493C-A5BD-D2A1B55793F2}.Release|x86.Build.0 = Release|Any CPU
- {A34EA000-0FC9-46E7-B4B2-0F7E6CB04367}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A34EA000-0FC9-46E7-B4B2-0F7E6CB04367}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A34EA000-0FC9-46E7-B4B2-0F7E6CB04367}.Debug|x64.ActiveCfg = Debug|Any CPU
- {A34EA000-0FC9-46E7-B4B2-0F7E6CB04367}.Debug|x64.Build.0 = Debug|Any CPU
- {A34EA000-0FC9-46E7-B4B2-0F7E6CB04367}.Debug|x86.ActiveCfg = Debug|Any CPU
- {A34EA000-0FC9-46E7-B4B2-0F7E6CB04367}.Debug|x86.Build.0 = Debug|Any CPU
- {A34EA000-0FC9-46E7-B4B2-0F7E6CB04367}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A34EA000-0FC9-46E7-B4B2-0F7E6CB04367}.Release|Any CPU.Build.0 = Release|Any CPU
- {A34EA000-0FC9-46E7-B4B2-0F7E6CB04367}.Release|x64.ActiveCfg = Release|Any CPU
- {A34EA000-0FC9-46E7-B4B2-0F7E6CB04367}.Release|x64.Build.0 = Release|Any CPU
- {A34EA000-0FC9-46E7-B4B2-0F7E6CB04367}.Release|x86.ActiveCfg = Release|Any CPU
- {A34EA000-0FC9-46E7-B4B2-0F7E6CB04367}.Release|x86.Build.0 = Release|Any CPU
- {6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D}.Debug|x64.ActiveCfg = Debug|Any CPU
- {6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D}.Debug|x64.Build.0 = Debug|Any CPU
- {6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D}.Debug|x86.ActiveCfg = Debug|Any CPU
- {6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D}.Debug|x86.Build.0 = Debug|Any CPU
- {6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D}.Release|Any CPU.Build.0 = Release|Any CPU
- {6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D}.Release|x64.ActiveCfg = Release|Any CPU
- {6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D}.Release|x64.Build.0 = Release|Any CPU
- {6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D}.Release|x86.ActiveCfg = Release|Any CPU
- {6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D}.Release|x86.Build.0 = Release|Any CPU
- {A3405B42-1924-4638-AFD6-50B2425A4038}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A3405B42-1924-4638-AFD6-50B2425A4038}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A3405B42-1924-4638-AFD6-50B2425A4038}.Debug|x64.ActiveCfg = Debug|Any CPU
- {A3405B42-1924-4638-AFD6-50B2425A4038}.Debug|x64.Build.0 = Debug|Any CPU
- {A3405B42-1924-4638-AFD6-50B2425A4038}.Debug|x86.ActiveCfg = Debug|Any CPU
- {A3405B42-1924-4638-AFD6-50B2425A4038}.Debug|x86.Build.0 = Debug|Any CPU
- {A3405B42-1924-4638-AFD6-50B2425A4038}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A3405B42-1924-4638-AFD6-50B2425A4038}.Release|Any CPU.Build.0 = Release|Any CPU
- {A3405B42-1924-4638-AFD6-50B2425A4038}.Release|x64.ActiveCfg = Release|Any CPU
- {A3405B42-1924-4638-AFD6-50B2425A4038}.Release|x64.Build.0 = Release|Any CPU
- {A3405B42-1924-4638-AFD6-50B2425A4038}.Release|x86.ActiveCfg = Release|Any CPU
- {A3405B42-1924-4638-AFD6-50B2425A4038}.Release|x86.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {B5BA78CE-B37C-4644-936C-4AAB4C5494A7} = {588A47B5-B723-4B4F-A05F-D26A684B5E25}
- {DD12665D-24D3-4938-9757-B5D3ED254563} = {588A47B5-B723-4B4F-A05F-D26A684B5E25}
- {EAF6E615-5895-46EE-B396-51EF14E0461B} = {6430B62F-00CD-47AB-AB6E-7F65415921DB}
- {D7DF99BE-B47A-493C-A5BD-D2A1B55793F2} = {6430B62F-00CD-47AB-AB6E-7F65415921DB}
- {A34EA000-0FC9-46E7-B4B2-0F7E6CB04367} = {6430B62F-00CD-47AB-AB6E-7F65415921DB}
- {6B45E308-63B7-44F2-9C1B-D7B9EDBC9F7D} = {6430B62F-00CD-47AB-AB6E-7F65415921DB}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {0F55B6C2-E4B4-4F2C-9D2A-D63A17F3B5C4}
- EndGlobalSection
-EndGlobal
diff --git a/src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Program.cs b/src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Program.cs
index a26f5b29..7fbfd540 100644
--- a/src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Program.cs
+++ b/src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Program.cs
@@ -10,7 +10,7 @@
var mem = new Memory();
-var computerBuilder = new GenericComputerBuilder(new NullLoggerFactory(), new GenericComputerConfig { WaitForHostToAcknowledgeFrame = false });
+var computerBuilder = new GenericComputerBuilder(new GenericComputerConfig { WaitForHostToAcknowledgeFrame = false }, new NullLoggerFactory());
computerBuilder
.WithCPU()
//.WithStartAddress()
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/ConfigUI/C64MenuConsole.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/ConfigUI/C64MenuConsole.cs
index 008a4022..47417266 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/ConfigUI/C64MenuConsole.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/ConfigUI/C64MenuConsole.cs
@@ -12,7 +12,7 @@ public class C64MenuConsole : ControlsConsole
public const int CONSOLE_WIDTH = USABLE_WIDTH + (SadConsoleUISettings.UI_USE_CONSOLE_BORDER ? 2 : 0);
public const int CONSOLE_HEIGHT = USABLE_HEIGHT + (SadConsoleUISettings.UI_USE_CONSOLE_BORDER ? 2 : 0);
private const int USABLE_WIDTH = 21;
- private const int USABLE_HEIGHT = 12;
+ private const int USABLE_HEIGHT = 10;
private readonly SadConsoleHostApp _sadConsoleHostApp;
private readonly ILogger _logger;
@@ -35,7 +35,7 @@ public C64MenuConsole(SadConsoleHostApp sadConsoleHostApp, ILoggerFactory logger
DrawUIItems();
if (SadConsoleUISettings.UI_USE_CONSOLE_BORDER)
- Surface.DrawBox(new Rectangle(0, 0, Width, Height), SadConsoleUISettings.ConsoleDrawBoxBorderParameters);
+ Surface.DrawBox(new Rectangle(0, 0, Width, Height), SadConsoleUISettings.UIConsoleDrawBoxBorderParameters);
}
@@ -59,21 +59,12 @@ private void DrawUIItems()
c64SaveBasicButton.Click += C64SaveBasicButton_Click;
Controls.Add(c64SaveBasicButton);
- // Load Binary
- var c64LoadBinaryButton = new Button("Load Binary .prg")
- {
- Name = "c64LoadBinaryButton",
- Position = (1, c64SaveBasicButton.Bounds.MaxExtentY + 1),
- };
- c64LoadBinaryButton.Click += C64LoadBinaryButton_Click;
- Controls.Add(c64LoadBinaryButton);
-
// Config
var c64ConfigButton = new Button("C64 Config")
{
Name = "c64ConfigButton",
- Position = (1, c64LoadBinaryButton.Bounds.MaxExtentY + 2),
+ Position = (1, c64SaveBasicButton.Bounds.MaxExtentY + 2),
};
c64ConfigButton.Click += C64ConfigButton_Click;
Controls.Add(c64ConfigButton);
@@ -138,12 +129,12 @@ private void C64LoadBasicButton_Click(object? sender, EventArgs e)
{
_logger.LogError($"Error loading Basic .prg: {ex.Message}");
}
-
- IsDirty = true;
}
if (wasRunning)
- _sadConsoleHostApp.Start();
+ _sadConsoleHostApp.Start().Wait();
+
+ IsDirty = true;
};
window.Show(true);
}
@@ -183,47 +174,7 @@ private void C64SaveBasicButton_Click(object? sender, EventArgs e)
}
if (wasRunning)
- _sadConsoleHostApp.Start();
- };
- window.Show(true);
- }
-
- private void C64LoadBinaryButton_Click(object? sender, EventArgs e)
- {
- bool wasRunning = false;
- if (_sadConsoleHostApp.EmulatorState == EmulatorState.Running)
- {
- wasRunning = true;
- _sadConsoleHostApp.Pause();
- }
-
- var window = new FilePickerConsole(FilePickerMode.OpenFile, Environment.CurrentDirectory, filter: "*.prg");
- window.Center();
- window.Closed += (s2, e2) =>
- {
- if (window.DialogResult)
- {
- try
- {
- var fileName = window.SelectedFile.FullName;
- BinaryLoader.Load(
- _sadConsoleHostApp.CurrentRunningSystem.Mem,
- fileName,
- out ushort loadedAtAddress,
- out ushort fileLength);
-
- _sadConsoleHostApp.CurrentRunningSystem.CPU.PC = loadedAtAddress;
- }
- catch (Exception ex)
- {
- _logger.LogError($"Error loading Binary .prg: {ex.Message}");
- }
-
- IsDirty = true;
- }
-
- if (wasRunning)
- _sadConsoleHostApp.Start();
+ _sadConsoleHostApp.Start().Wait();
};
window.Show(true);
}
@@ -264,9 +215,6 @@ private void SetControlStates()
var c64SaveBasicButton = Controls["c64SaveBasicButton"];
c64SaveBasicButton.IsEnabled = _sadConsoleHostApp.EmulatorState != Systems.EmulatorState.Uninitialized;
- var c64LoadBinaryButton = Controls["c64LoadBinaryButton"];
- c64LoadBinaryButton.IsEnabled = _sadConsoleHostApp.EmulatorState != Systems.EmulatorState.Uninitialized;
-
var systemComboBox = Controls["c64ConfigButton"];
systemComboBox.IsEnabled = _sadConsoleHostApp.EmulatorState == Systems.EmulatorState.Uninitialized;
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/ConsolePositioningHelper.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/ConsolePositioningHelper.cs
new file mode 100644
index 00000000..0c9c3aca
--- /dev/null
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/ConsolePositioningHelper.cs
@@ -0,0 +1,18 @@
+using static SadConsole.IFont;
+
+namespace Highbyte.DotNet6502.App.SadConsole;
+public static class ConsolePositioningHelper
+{
+
+ public static float GetFontSizeScaleFactor(this IFont.Sizes fontSize) =>
+ fontSize switch
+ {
+ Sizes.Quarter => 0.25f,
+ Sizes.Half => 0.5f,
+ Sizes.One => 1,
+ Sizes.Two => 2,
+ Sizes.Three => 3,
+ Sizes.Four => 4,
+ _ => 1
+ };
+}
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/EmulatorConfig.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/EmulatorConfig.cs
index 9e457913..f7c6c87a 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/EmulatorConfig.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/EmulatorConfig.cs
@@ -11,21 +11,19 @@ public class EmulatorConfig
{
public const string ConfigSectionName = "Highbyte.DotNet6502.SadConsoleConfig";
- public string WindowTitle { get; set; }
-
///
- /// Optional. If not specified, default SadConsole font is used.
+ /// Optional. Font used for the UI. If not specified, default SadConsole font is used.
/// To use a specific SadConsole Font, include it in your program output directory.
///
public string? UIFont { get; set; }
///
- /// Font size for emulator console only. UI is not affected.
+ /// Note: UI FontSize other than One is not currently implemented.
+ /// Font size for the UI.
/// Sizes.One is default.
///
///
- public Sizes FontSize { get; set; }
-
+ public Sizes UIFontSize { get; set; }
///
/// The name of the emulator to start.
@@ -53,9 +51,8 @@ public class EmulatorConfig
public EmulatorConfig()
{
- WindowTitle = "SadConsole + Highbyte.DotNet6502 emulator.";
UIFont = null;
- FontSize = Sizes.One;
+ UIFontSize = Sizes.One;
DefaultEmulator = "C64";
Monitor = new();
@@ -72,16 +69,4 @@ public void Validate(SystemList
- FontSize switch
- {
- Sizes.Quarter => 0.25f,
- Sizes.Half => 0.5f,
- Sizes.One => 1,
- Sizes.Two => 2,
- Sizes.Three => 3,
- Sizes.Four => 4,
- _ => 1
- };
}
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/C64.font b/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/C64.font
deleted file mode 100644
index 97a88a2e..00000000
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/C64.font
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "$type": "SadConsole.SadFont, SadConsole",
- "FilePath": "Yayo_c64.png",
- "GlyphHeight": 16,
- "GlyphPadding": 1,
- "GlyphWidth": 16,
- "Name": "C64",
- "SolidGlyphIndex": 219
-}
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/C64_ROM.font b/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/C64_ROM.font
new file mode 100644
index 00000000..2ba52e6f
--- /dev/null
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/C64_ROM.font
@@ -0,0 +1,9 @@
+{
+ "$type": "SadConsole.SadFont, SadConsole",
+ "FilePath": "c64_chargen_dump_combined.png",
+ "GlyphHeight": 8,
+ "GlyphPadding": 0,
+ "GlyphWidth": 8,
+ "Name": "C64_ROM",
+ "SolidGlyphIndex": 160
+}
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/Yayo_c64.png b/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/Yayo_c64.png
deleted file mode 100644
index fa405d42..00000000
Binary files a/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/Yayo_c64.png and /dev/null differ
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/c64_chargen_dump_combined.png b/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/c64_chargen_dump_combined.png
new file mode 100644
index 00000000..f46465bc
Binary files /dev/null and b/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/c64_chargen_dump_combined.png differ
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/c64_chargen_shifted_dump.png b/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/c64_chargen_shifted_dump.png
new file mode 100644
index 00000000..11a15abb
Binary files /dev/null and b/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/c64_chargen_shifted_dump.png differ
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/c64_chargen_unshifted_dump.png b/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/c64_chargen_unshifted_dump.png
new file mode 100644
index 00000000..af02c425
Binary files /dev/null and b/src/apps/Highbyte.DotNet6502.App.SadConsole/Fonts/c64_chargen_unshifted_dump.png differ
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/Highbyte.DotNet6502.App.SadConsole.csproj b/src/apps/Highbyte.DotNet6502.App.SadConsole/Highbyte.DotNet6502.App.SadConsole.csproj
index 6d35ed31..522ea780 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/Highbyte.DotNet6502.App.SadConsole.csproj
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/Highbyte.DotNet6502.App.SadConsole.csproj
@@ -5,8 +5,23 @@
net8.0enableenable
+ favicon.ico
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
@@ -34,4 +49,7 @@
Always
+
+
+
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/InfoConsole.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/InfoConsole.cs
index 26cafa2e..2c0c5c21 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/InfoConsole.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/InfoConsole.cs
@@ -171,11 +171,7 @@ Label CreateLabel(string text, int col, int row, Color? textColor = null, string
Panel genericSystemInfoPanel = new Panel(10, 10); // TODO: What does size in constructor affect?
{
int row = 0;
- CreateLabel("A generic 6502 CPU computer, with custom defined memory layout.", 0, row, Controls.ThemeColors.ControlHostForeground);
- row++;
- CreateLabel("A future update could make it possible to configure such things as total memory,", 0, row, Controls.ThemeColors.ControlHostForeground);
- row++;
- CreateLabel("screen memory addres and IO memory addresses in the UI.", 0, row, Controls.ThemeColors.ControlHostForeground);
+ CreateLabel("A generic 6502-based computer, with custom defined memory layout and IO functionallity.", 0, row, Controls.ThemeColors.ControlHostForeground);
Label CreateLabel(string text, int col, int row, Color? textColor = null, string? name = null)
{
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/MenuConsole.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/MenuConsole.cs
index 90242e69..39175b41 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/MenuConsole.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/MenuConsole.cs
@@ -1,6 +1,11 @@
+using Highbyte.DotNet6502.Systems;
+using Highbyte.DotNet6502.Utils;
using SadConsole.UI;
using SadConsole.UI.Controls;
using SadRogue.Primitives;
+using Microsoft.Extensions.Logging;
+using static SadConsole.IFont;
+using static System.Net.Mime.MediaTypeNames;
namespace Highbyte.DotNet6502.App.SadConsole;
public class MenuConsole : ControlsConsole
@@ -8,13 +13,19 @@ public class MenuConsole : ControlsConsole
public const int CONSOLE_WIDTH = USABLE_WIDTH + (SadConsoleUISettings.UI_USE_CONSOLE_BORDER ? 2 : 0);
public const int CONSOLE_HEIGHT = USABLE_HEIGHT + (SadConsoleUISettings.UI_USE_CONSOLE_BORDER ? 2 : 0);
private const int USABLE_WIDTH = 21;
- private const int USABLE_HEIGHT = 15;
+ private const int USABLE_HEIGHT = 17;
private readonly SadConsoleHostApp _sadConsoleHostApp;
+ private readonly ILogger _logger;
- public MenuConsole(SadConsoleHostApp sadConsoleHostApp) : base(CONSOLE_WIDTH, CONSOLE_HEIGHT)
+ public MenuConsole(SadConsoleHostApp sadConsoleHostApp, ILoggerFactory loggerFactory) : base(CONSOLE_WIDTH, CONSOLE_HEIGHT)
{
_sadConsoleHostApp = sadConsoleHostApp;
+ _logger = loggerFactory.CreateLogger(typeof(MenuConsole).Name);
+
+ // The UI font is set as default during program SadConsole startup.
+ // Note: Not yet implemented changing of UI font and size. Currently it leads to issues in the layout.
+ //FontSize = Font.GetFontSize(_sadConsoleHostApp.EmulatorConfig.UIFontSize);
Controls.ThemeColors = SadConsoleUISettings.ThemeColors;
Surface.DefaultBackground = Controls.ThemeColors.ControlHostBackground;
@@ -29,7 +40,7 @@ public MenuConsole(SadConsoleHostApp sadConsoleHostApp) : base(CONSOLE_WIDTH, CO
DrawUIItems();
if (SadConsoleUISettings.UI_USE_CONSOLE_BORDER)
- Surface.DrawBox(new Rectangle(0, 0, Width, Height), SadConsoleUISettings.ConsoleDrawBoxBorderParameters);
+ Surface.DrawBox(new Rectangle(0, 0, Width, Height), SadConsoleUISettings.UIConsoleDrawBoxBorderParameters);
}
private void DrawUIItems()
@@ -149,11 +160,21 @@ private void DrawUIItems()
{
Position = (fontSizeLabel.Bounds.MaxExtentX + 2, fontSizeLabel.Position.Y),
Name = "selectFontSizeComboBox",
- SelectedItem = _sadConsoleHostApp.EmulatorConfig.FontSize,
+ SelectedItem = Sizes.One, // Will be overritten by SetEmulatorFontSize when a system is selected
};
- selectFontSizeBox.SelectedItemChanged += (s, e) => { _sadConsoleHostApp.EmulatorConfig.FontSize = (IFont.Sizes)e.Item; IsDirty = true; };
+ selectFontSizeBox.SelectedItemChanged += (s, e) => { _sadConsoleHostApp.CommonHostSystemConfig.DefaultFontSize = (IFont.Sizes)e.Item; IsDirty = true; };
Controls.Add(selectFontSizeBox);
+ // Load Basic
+ var loadBinaryButton = new Button("Load Binary .prg")
+ {
+ Name = "loadBinaryButton",
+ Position = (1, fontSizeLabel.Bounds.MaxExtentY + 2),
+ };
+ loadBinaryButton.Click += LoadBinaryButton_Click;
+ Controls.Add(loadBinaryButton);
+
+
// Helper function to create a label and add it to the console
Label CreateLabel(string text, int col, int row, string? name = null)
{
@@ -172,6 +193,47 @@ Label CreateLabelValue(string text, int col, int row, string? name = null)
OnIsDirtyChanged();
}
+ private void LoadBinaryButton_Click(object? sender, EventArgs e)
+ {
+ bool wasRunning = false;
+ if (_sadConsoleHostApp.EmulatorState == EmulatorState.Running)
+ {
+ wasRunning = true;
+ _sadConsoleHostApp.Pause();
+ }
+
+ var window = new FilePickerConsole(FilePickerMode.OpenFile, Environment.CurrentDirectory, filter: "*.*");
+ window.Center();
+ window.Closed += (s2, e2) =>
+ {
+ if (window.DialogResult)
+ {
+ try
+ {
+ var fileName = window.SelectedFile.FullName;
+ BinaryLoader.Load(
+ _sadConsoleHostApp.CurrentRunningSystem.Mem,
+ fileName,
+ out ushort loadedAtAddress,
+ out ushort fileLength);
+
+ _sadConsoleHostApp.CurrentRunningSystem.CPU.PC = loadedAtAddress;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"Error loading Binary .prg: {ex.Message}");
+ }
+ }
+
+ if (wasRunning)
+ _sadConsoleHostApp.Start().Wait();
+
+ IsDirty = true;
+
+ };
+ window.Show(true);
+ }
+
protected override void OnIsDirtyChanged()
{
if (IsDirty)
@@ -227,7 +289,17 @@ private void SetControlStates()
var selectFontSizeComboBox = Controls["selectFontSizeComboBox"];
selectFontSizeComboBox.IsEnabled = _sadConsoleHostApp.EmulatorState == Systems.EmulatorState.Uninitialized;
+ var loadBinaryButton = Controls["loadBinaryButton"];
+ loadBinaryButton.IsEnabled = _sadConsoleHostApp.EmulatorState != Systems.EmulatorState.Uninitialized;
+
if (_sadConsoleHostApp.SystemMenuConsole != null)
_sadConsoleHostApp.SystemMenuConsole.IsDirty = true;
}
+
+ internal void SetEmulatorFontSize(IFont.Sizes defaultFontSize)
+ {
+ var selectFontSizeComboBox = Controls["selectFontSizeComboBox"] as ComboBox;
+ selectFontSizeComboBox.SelectedItem = defaultFontSize;
+ IsDirty = true;
+ }
}
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/MonitorStatusConsole.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/MonitorStatusConsole.cs
index f29118c9..46d37271 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/MonitorStatusConsole.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/MonitorStatusConsole.cs
@@ -1,8 +1,6 @@
-using System.Net.NetworkInformation;
using Highbyte.DotNet6502.Systems;
using Highbyte.DotNet6502.Utils;
using SadConsole.UI;
-using SadConsole.UI.Controls;
using SadRogue.Primitives;
namespace Highbyte.DotNet6502.App.SadConsole;
@@ -41,7 +39,7 @@ public MonitorStatusConsole(SadConsoleHostApp sadConsoleHostApp)
Surface.Clear();
if (SadConsoleUISettings.UI_USE_CONSOLE_BORDER)
- Surface.DrawBox(new Rectangle(0, 0, Width, Height), SadConsoleUISettings.ConsoleDrawBoxBorderParameters);
+ Surface.DrawBox(new Rectangle(0, 0, Width, Height), SadConsoleUISettings.UIConsoleDrawBoxBorderParameters);
CreateUIControls();
}
@@ -63,43 +61,39 @@ public void Refresh()
private void DisplayCPUStatus()
{
- if (_sadConsoleHostApp.EmulatorState == EmulatorState.Uninitialized)
+ // Make sure to clear existing text before writing new text
+ Surface.Print(1, 1, _emptyRow);
+ for (int i = 0; i < NUMBER_OF_SYS_INFO_ROWS; i++)
{
- Surface.Print(1, 1, _emptyRow);
- for (int i = 0; i < NUMBER_OF_SYS_INFO_ROWS; i++)
- {
- Surface.Print(1, 2 + i, _emptyRow);
- }
+ Surface.Print(1, 2 + i, _emptyRow);
}
- else
+
+ var system = _sadConsoleHostApp.CurrentRunningSystem!;
+
+ var cpuStateDictionary = OutputGen.GetProcessorStateDictionary(system.CPU, includeCycles: true);
+ int row = 1;
+ int col = 1;
+ const string separator = ": ";
+ foreach (var cpuState in cpuStateDictionary)
+ {
+ Surface.Print(col, 1, cpuState.Key, foreground: Controls.ThemeColors.ControlHostForeground, background: Controls.ThemeColors.ControlHostBackground);
+ col += cpuState.Key.Length;
+ Surface.Print(col, 1, separator, foreground: Controls.ThemeColors.ControlHostForeground, background: Controls.ThemeColors.ControlHostBackground);
+ col += separator.Length;
+ Surface.Print(col, 1, cpuState.Value, foreground: Controls.ThemeColors.White, background: Controls.ThemeColors.ControlHostBackground);
+ col += cpuState.Value.Length + 1;
+ }
+
+ row = 2;
+ col = 1;
+ for (int i = 0; i < NUMBER_OF_SYS_INFO_ROWS; ++i)
{
- var system = _sadConsoleHostApp.CurrentRunningSystem!;
-
- var cpuStateDictionary = OutputGen.GetProcessorStateDictionary(system.CPU, includeCycles: true);
- int row = 1;
- int col = 1;
- const string separator = ": ";
- foreach (var cpuState in cpuStateDictionary)
- {
- Surface.Print(col, 1, cpuState.Key, foreground: Controls.ThemeColors.ControlHostForeground, background: Controls.ThemeColors.ControlHostBackground);
- col += cpuState.Key.Length;
- Surface.Print(col, 1, separator, foreground: Controls.ThemeColors.ControlHostForeground, background: Controls.ThemeColors.ControlHostBackground);
- col += separator.Length;
- Surface.Print(col, 1, cpuState.Value, foreground: Controls.ThemeColors.White, background: Controls.ThemeColors.ControlHostBackground);
- col += cpuState.Value.Length + 1;
- }
-
- row = 2;
- col = 1;
- for (int i = 0; i < NUMBER_OF_SYS_INFO_ROWS; ++i)
- {
- if (i < system.SystemInfo.Count)
- // TODO: Is a new string every time needed here? If system.SystemInfo items does not change, then a list of pre-created string can be initialized once and then reused..
- Surface.Print(col, row, $"SYS: {system.SystemInfo[i]}", foreground: Controls.ThemeColors.ControlHostForeground, background: Controls.ThemeColors.ControlHostBackground);
- else
- Surface.Print(col, row, _emptyRow, foreground: Controls.ThemeColors.ControlHostForeground, background: Controls.ThemeColors.ControlHostBackground);
- row++;
- }
+ if (i < system.SystemInfo.Count)
+ // TODO: Is a new string every time needed here? If system.SystemInfo items does not change, then a list of pre-created string can be initialized once and then reused..
+ Surface.Print(col, row, $"SYS: {system.SystemInfo[i]}", foreground: Controls.ThemeColors.ControlHostForeground, background: Controls.ThemeColors.ControlHostBackground);
+ else
+ Surface.Print(col, row, _emptyRow, foreground: Controls.ThemeColors.ControlHostForeground, background: Controls.ThemeColors.ControlHostBackground);
+ row++;
}
}
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleHostApp.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleHostApp.cs
index 3d4cda58..b3b2a9b0 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleHostApp.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleHostApp.cs
@@ -6,12 +6,12 @@
using Highbyte.DotNet6502.Systems;
using Highbyte.DotNet6502.Systems.Logging.InMem;
using Microsoft.Extensions.Logging;
+using SadConsole.Components;
using SadConsole.Configuration;
using SadConsole.Input;
using SadRogue.Primitives;
using Console = SadConsole.Console;
-
namespace Highbyte.DotNet6502.App.SadConsole;
///
@@ -60,7 +60,7 @@ protected virtual void OnMonitorStateChange(bool monitorEnabled)
private const int MENU_POSITION_X = 0;
private const int MENU_POSITION_Y = 0;
- private int StartupScreenWidth => MenuConsole.CONSOLE_WIDTH + 40;
+ private int StartupScreenWidth => MenuConsole.CONSOLE_WIDTH + 60;
private int StartupScreenHeight => MenuConsole.CONSOLE_HEIGHT + 14;
private const int STATS_UPDATE_EVERY_X_FRAME = 60 * 1;
@@ -68,6 +68,7 @@ protected virtual void OnMonitorStateChange(bool monitorEnabled)
private const int LOGS_UPDATE_EVERY_X_FRAME = 60 * 1;
private int _logsFrameCount = 0;
+ private DrawImage _logoDrawImage;
///
@@ -129,7 +130,7 @@ public void Run()
.AddFrameRenderEvent(RenderSadConsole)
;
- Settings.WindowTitle = _emulatorConfig.WindowTitle;
+ Settings.WindowTitle = "Highbyte.DotNet6502 emulator + SadConsole (with NAudio)";
Settings.ResizeMode = Settings.WindowResizeOptions.None;
// Start SadConsole window
@@ -146,7 +147,7 @@ private IScreenObject CreateMainSadConsoleScreen(GameHost gameHost)
//return screen;
_sadConsoleScreen = new ScreenObject();
- _menuConsole = new MenuConsole(this);
+ _menuConsole = new MenuConsole(this, _loggerFactory);
_menuConsole.Position = (MENU_POSITION_X, MENU_POSITION_Y);
_sadConsoleScreen.Children.Add(_menuConsole);
@@ -174,7 +175,7 @@ private IScreenObject CreateMainSadConsoleScreen(GameHost gameHost)
// Position monitor to the right of the emulator console
_monitorConsole.UsePixelPositioning = true;
// Note: _sadConsoleEmulatorConsole has already changed to UsePixelPositioning = true, so its Position.X is in pixels (not Width though).
- var emulatorMaxX = _sadConsoleEmulatorConsole.Position.X + ((int)(_sadConsoleEmulatorConsole.Width * _sadConsoleEmulatorConsole.Font.GlyphWidth * _emulatorConfig.FontSizeScaleFactor));
+ var emulatorMaxX = _sadConsoleEmulatorConsole.Position.X + ((int)(_sadConsoleEmulatorConsole.Width * _sadConsoleEmulatorConsole.Font.GlyphWidth * CommonHostSystemConfig.DefaultFontSize.GetFontSizeScaleFactor()));
var infoConsoleMax = _infoConsole != null && _infoConsole.IsVisible ? _infoConsole.Position.X + _infoConsole.WidthPixels : 0;
_monitorConsole.Position = new Point(Math.Max(emulatorMaxX, infoConsoleMax), 0);
@@ -196,6 +197,16 @@ private IScreenObject CreateMainSadConsoleScreen(GameHost gameHost)
};
_sadConsoleScreen.Children.Add(_monitorConsole);
+ // Logo
+ int logoWidthAndHeight = 256; // Pixels
+ _logoDrawImage = new DrawImage("Resources/Images/logo-256.png");
+ _logoDrawImage.PositionMode = DrawImage.PositionModes.Pixels;
+ //var logoX = (MenuConsole.CONSOLE_WIDTH * _menuConsole.Font.GlyphWidth) + ((StartupScreenWidth - MenuConsole.CONSOLE_WIDTH) * _menuConsole.Font.GlyphWidth - logoWidthAndHeight) / 2;
+ //var logoY = ((MenuConsole.CONSOLE_HEIGHT * _menuConsole.Font.GlyphHeight) - logoWidthAndHeight) / 2;
+ var logoX = (MenuConsole.CONSOLE_WIDTH * _menuConsole.Font.GlyphWidth) + 10;
+ var logoY = 10;
+ _logoDrawImage.PositionOffset = new Point(logoX, logoY);
+ _sadConsoleScreen.SadComponents.Add(_logoDrawImage);
//_sadConsoleScreen.IsFocused = true;
_menuConsole.IsFocused = true;
@@ -208,6 +219,9 @@ private IScreenObject CreateMainSadConsoleScreen(GameHost gameHost)
public override void OnAfterSelectSystem()
{
+ // Set the default font size configured for the system
+ _menuConsole.SetEmulatorFontSize(CommonHostSystemConfig.DefaultFontSize);
+
// Clear any old system specific menu console
if (_systemMenuConsole != null)
{
@@ -246,7 +260,7 @@ public override bool OnBeforeStart(ISystem systemAboutToBeStarted)
{
font = Game.Instance.DefaultFont;
}
- _sadConsoleEmulatorConsole = EmulatorConsole.Create(systemAboutToBeStarted, font, _emulatorConfig.FontSize, SadConsoleUISettings.ConsoleDrawBoxBorderParameters);
+ _sadConsoleEmulatorConsole = EmulatorConsole.Create(systemAboutToBeStarted, font, CommonHostSystemConfig.DefaultFontSize, SadConsoleUISettings.CreateEmulatorConsoleDrawBoxBorderParameters(font.SolidGlyphIndex));
_sadConsoleEmulatorConsole.UsePixelPositioning = true;
_sadConsoleEmulatorConsole.Position = new Point((_menuConsole.Position.X * _menuConsole.Font.GlyphWidth) + (_menuConsole.Width * _menuConsole.Font.GlyphWidth), 0);
_sadConsoleEmulatorConsole.IsFocused = true;
@@ -424,23 +438,12 @@ private NAudioAudioHandlerContext CreateAudioHandlerContext()
initialVolumePercent: 20);
}
- private SadConsoleHostSystemConfigBase CommonHostSystemConfig => (SadConsoleHostSystemConfigBase)CurrentHostSystemConfig;
+ public SadConsoleHostSystemConfigBase CommonHostSystemConfig => (SadConsoleHostSystemConfigBase)CurrentHostSystemConfig;
private int CalculateWindowWidthPixels()
{
- int emulatorConsoleFontSizeAdjustment;
- // TODO: This is a bit of a hack for handling consoles with different font sizes, and positioning on main screen. Better way?
- if (_emulatorConfig.FontSizeScaleFactor > 1)
- {
- emulatorConsoleFontSizeAdjustment = (((int)_emulatorConfig.FontSizeScaleFactor - 1) * 16);
- }
- else
- {
- emulatorConsoleFontSizeAdjustment = 0;
- }
-
var menuConsoleWidthPixels = _menuConsole.WidthPixels;
- var emulatorConsoleWidthPixels = Math.Max((_sadConsoleEmulatorConsole != null ? _sadConsoleEmulatorConsole.WidthPixels + emulatorConsoleFontSizeAdjustment : 0)
+ var emulatorConsoleWidthPixels = Math.Max((_sadConsoleEmulatorConsole != null ? _sadConsoleEmulatorConsole.WidthPixels : 0)
, (_infoConsole != null && _infoConsole.IsVisible ? _infoConsole.WidthPixels : 0));
var monitorConsoleWidthPixels = (_monitorConsole != null && _monitorConsole.IsVisible ? _monitorConsole.WidthPixels : 0);
var widthPixels = menuConsoleWidthPixels + emulatorConsoleWidthPixels + monitorConsoleWidthPixels;
@@ -505,6 +508,10 @@ public void DisableInfo()
if (CurrentRunningSystem != null)
CurrentRunningSystem!.InstrumentationEnabled = false;
+ // Enable logo when info console is disabled (as it shouldn't be covered by the info console)
+ if (!_sadConsoleScreen.SadComponents.Contains(_logoDrawImage))
+ _sadConsoleScreen.SadComponents.Add(_logoDrawImage);
+
// Resize main window to fit menu, emulator, monitor and other visible consoles
Game.Instance.ResizeWindow(CalculateWindowWidthPixels(), CalculateWindowHeightPixels());
//OnStatsStateChange(statsEnabled: false);
@@ -534,6 +541,10 @@ public void EnableInfo()
// Resize main window to fit menu, emulator, monitor and other visible consoles
Game.Instance.ResizeWindow(CalculateWindowWidthPixels(), CalculateWindowHeightPixels());
+ // Remove logo when info console is enabled (as it may partially cover the logo)
+ if (_sadConsoleScreen.SadComponents.Contains(_logoDrawImage))
+ _sadConsoleScreen.SadComponents.Remove(_logoDrawImage);
+
//OnStatsStateChange(statsEnabled: true);
}
public void ClearInfoStats()
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleUISettings.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleUISettings.cs
index 2c72202e..74fc00b1 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleUISettings.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleUISettings.cs
@@ -73,17 +73,11 @@ public static Colors CreateDotNet6502Colors()
return colors;
}
-
- public readonly static ColoredGlyph ConsoleBorderGlyph = new(ThemeColors.Lines, ThemeColors.ControlHostBackground);
-
- //public static Color UIConsoleBackgroundColor = new Color(5, 15, 45);
- //public static Color UIConsoleForegroundColor = Color.White;
- //public readonly static ColoredGlyph ConsoleBorderGlyph = new(new Color(90, 90, 90), UIConsoleBackgroundColor);
-
-
- public readonly static ShapeParameters ConsoleDrawBoxBorderParameters = new ShapeParameters(
+ // Default border box drawing parameters for UI consoles.
+ private readonly static ColoredGlyph s_uiConsoleBorderGlyph = new(ThemeColors.Lines, ThemeColors.ControlHostBackground);
+ public readonly static ShapeParameters UIConsoleDrawBoxBorderParameters = new ShapeParameters(
hasBorder: true,
- borderGlyph: ConsoleBorderGlyph,
+ borderGlyph: s_uiConsoleBorderGlyph,
ignoreBorderForeground: false,
ignoreBorderBackground: false,
ignoreBorderGlyph: false,
@@ -96,4 +90,24 @@ public static Colors CreateDotNet6502Colors()
ignoreFillMirror: false,
boxBorderStyle: ICellSurface.ConnectedLineThin,
boxBorderStyleGlyphs: null);
+
+ // Solid border for emulator, as the font used there may not contain the box drawing characters.
+ public static ShapeParameters CreateEmulatorConsoleDrawBoxBorderParameters(int solidGlyph)
+ {
+ return new ShapeParameters(
+ hasBorder: true,
+ borderGlyph: new ColoredGlyph(ThemeColors.Lines, ThemeColors.ControlHostBackground, glyph: solidGlyph),
+ ignoreBorderForeground: false,
+ ignoreBorderBackground: false,
+ ignoreBorderGlyph: false,
+ ignoreBorderMirror: false,
+ hasFill: false,
+ fillGlyph: null,
+ ignoreFillForeground: false,
+ ignoreFillBackground: false,
+ ignoreFillGlyph: false,
+ ignoreFillMirror: false,
+ boxBorderStyle: null,
+ boxBorderStyleGlyphs: null);
+ }
}
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/C64HostConfig.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/C64HostConfig.cs
index bce2c53c..a2ebba87 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/C64HostConfig.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/C64HostConfig.cs
@@ -1,12 +1,14 @@
-using Highbyte.DotNet6502.Systems;
-
namespace Highbyte.DotNet6502.App.SadConsole.SystemSetup;
public class C64HostConfig : SadConsoleHostSystemConfigBase
{
public C64HostConfig()
{
- Font = "Fonts/C64.font";
+ //Font = "Fonts/C64.font";
+ //DefaultFontSize = IFont.Sizes.One;
+
+ Font = "Fonts/C64_ROM.font";
+ DefaultFontSize = IFont.Sizes.Two;
}
public new object Clone()
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/C64Setup.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/C64Setup.cs
index ba621cc7..b0629900 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/C64Setup.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/C64Setup.cs
@@ -43,6 +43,7 @@ public Task GetNewConfig(string configurationVariant)
var c64Config = new C64Config() { ROMs = new() };
_configuration.GetSection($"{C64Config.ConfigSectionName}.{configurationVariant}").Bind(c64Config);
+ c64Config.SetROMDefaultChecksums();
return Task.FromResult(c64Config);
}
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/GenericComputerHostConfig.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/GenericComputerHostConfig.cs
index 32343339..5f20b437 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/GenericComputerHostConfig.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/GenericComputerHostConfig.cs
@@ -5,6 +5,7 @@ public class GenericComputerHostConfig : SadConsoleHostSystemConfigBase
public GenericComputerHostConfig()
{
Font = null;
+ DefaultFontSize = IFont.Sizes.One;
}
public new object Clone()
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/SadConsoleHostSystemConfigBase.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/SadConsoleHostSystemConfigBase.cs
index f58f47d0..c90bc49a 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/SadConsoleHostSystemConfigBase.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/SystemSetup/SadConsoleHostSystemConfigBase.cs
@@ -1,4 +1,5 @@
using Highbyte.DotNet6502.Systems;
+using static SadConsole.IFont;
namespace Highbyte.DotNet6502.App.SadConsole.SystemSetup;
public abstract class SadConsoleHostSystemConfigBase : IHostSystemConfig, ICloneable
@@ -11,6 +12,14 @@ public abstract class SadConsoleHostSystemConfigBase : IHostSystemConfig, IClone
///
public string? Font { get; set; }
+ ///
+ /// Default font size for emulator console only. UI is not affected.
+ /// Sizes.One is default.
+ ///
+ ///
+ public Sizes DefaultFontSize { get; set; }
+
+
public SadConsoleHostSystemConfigBase()
{
Font = null;
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/appsettings.json b/src/apps/Highbyte.DotNet6502.App.SadConsole/appsettings.json
index 6ed8b646..dce0319b 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/appsettings.json
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/appsettings.json
@@ -1,18 +1,15 @@
{
"Highbyte.DotNet6502.SadConsoleConfig": {
-
- "WindowTitle": "Highbyte.DotNet6502 emulator + SadConsole",
"DefaultEmulator": "C64",
- "UIFont": "", // Leave blank for default SadConsole font.
-
- "FontSize": "One", // FontSize affects emulator console only, not UI console. Possible values: "Quarter", "Half, "One", "Two", "Three", "Four", "Five"
+ "UIFont": null, // UI Console font. Leave blank for default SadConsole font.
+ "UIFontSize": "One", // UI consoles font (not Emulator console). Possible values: "Quarter", "Half, "One", "Two", "Three", "Four", "Five"
"DefaultAudioVolumePercent": 20
},
// TODO: Read System host configs from this file
//"Highbyte.DotNet6502.SadConsoleConfig.C64HostConfig": {
- // "Font": "Fonts/C64.font" // C64 font copied from https://github.com/Thraka/SadConsole. Leave blank for default SadConsole font.
+ // "Font": "Fonts/C64_ROM.font" // C64 font dumped from C64 ROM and modified.
//},
//"Highbyte.DotNet6502.SadConsoleConfig.GenericComputerHostConfig": {
// "Font": "" // Leave blank for default SadConsole font.
@@ -41,7 +38,9 @@
"ColorMapName": "Default",
"AudioSupported": true,
- "AudioEnabled": true
+ "AudioEnabled": true,
+
+ "InstrumentationEnabled": false // Start with instrumentation off by default
},
"Highbyte.DotNet6502.C64.C64PAL": {
@@ -67,7 +66,9 @@
"ColorMapName": "Default",
"AudioSupported": true,
- "AudioEnabled": true
+ "AudioEnabled": true,
+
+ "InstrumentationEnabled": false // Start with instrumentation off by default
},
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/favicon.ico b/src/apps/Highbyte.DotNet6502.App.SadConsole/favicon.ico
new file mode 100644
index 00000000..f804586d
Binary files /dev/null and b/src/apps/Highbyte.DotNet6502.App.SadConsole/favicon.ico differ
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/ConfigUI/SilkNetImGuiC64Config.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/ConfigUI/SilkNetImGuiC64Config.cs
index d47edf44..756f45d0 100644
--- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/ConfigUI/SilkNetImGuiC64Config.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/ConfigUI/SilkNetImGuiC64Config.cs
@@ -31,21 +31,8 @@ public class SilkNetImGuiC64Config
private bool _open;
- private bool IsValidConfig
- {
- get
- {
- if (_config == null)
- {
- _validationErrors.Clear();
- return true;
- }
- else
- {
- return _config.IsValid(out _validationErrors);
- }
- }
- }
+ private bool _isValidConfig;
+
private List _validationErrors = new();
//private static Vector4 s_informationColor = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);
@@ -178,8 +165,9 @@ public void PostOnRender(string dialogLabel)
if (_config!.IsDirty)
{
_config.ClearDirty();
+ _isValidConfig = _config.IsValid(out _validationErrors);
}
- if (!IsValidConfig)
+ if (!_isValidConfig)
{
ImGui.PushStyleColor(ImGuiCol.Text, s_errorColor);
foreach (var error in _validationErrors!)
@@ -190,7 +178,7 @@ public void PostOnRender(string dialogLabel)
}
// Close buttons
- ImGui.BeginDisabled(disabled: !IsValidConfig);
+ ImGui.BeginDisabled(disabled: !_isValidConfig);
ImGui.PushStyleColor(ImGuiCol.Button, s_okButtonColor);
if (ImGui.Button("Ok"))
{
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/ConfigUI/SilkNetImGuiGenericComputerConfig.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/ConfigUI/SilkNetImGuiGenericComputerConfig.cs
index c883ec7b..f41bb835 100644
--- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/ConfigUI/SilkNetImGuiGenericComputerConfig.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/ConfigUI/SilkNetImGuiGenericComputerConfig.cs
@@ -18,21 +18,8 @@ public class SilkNetImGuiGenericComputerConfig
private string _programBinaryFile = default!;
- public bool IsValidConfig
- {
- get
- {
- if (_config == null)
- {
- _validationErrors.Clear();
- return true;
- }
- else
- {
- return _config.IsValid(out _validationErrors);
- }
- }
- }
+ public bool _isValidConfig;
+
private List _validationErrors = new();
//private static Vector4 s_informationColor = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);
private static Vector4 s_errorColor = new Vector4(1.0f, 0.0f, 0.0f, 1.0f);
@@ -152,8 +139,9 @@ public void PostOnRender(string dialogLabel)
if (_config!.IsDirty)
{
_config.ClearDirty();
+ _isValidConfig = _config.IsValid(out _validationErrors);
}
- if (!IsValidConfig)
+ if (!_isValidConfig)
{
ImGui.PushStyleColor(ImGuiCol.Text, s_errorColor);
foreach (var error in _validationErrors)
@@ -164,7 +152,7 @@ public void PostOnRender(string dialogLabel)
}
// Close buttons
- ImGui.BeginDisabled(disabled: !IsValidConfig);
+ ImGui.BeginDisabled(disabled: !_isValidConfig);
ImGui.PushStyleColor(ImGuiCol.Button, s_okButtonColor);
if (ImGui.Button("Ok"))
{
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Highbyte.DotNet6502.App.SilkNetNative.csproj b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Highbyte.DotNet6502.App.SilkNetNative.csproj
index 4d987144..020c99f1 100644
--- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Highbyte.DotNet6502.App.SilkNetNative.csproj
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Highbyte.DotNet6502.App.SilkNetNative.csproj
@@ -7,6 +7,16 @@
enable
+
+
+ Never
+
+
+
+
+
+
+
@@ -32,4 +42,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Program.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Program.cs
index 6fe761dc..f63a1bd8 100644
--- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Program.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Program.cs
@@ -62,7 +62,7 @@
windowOptions.VSync = false; // TODO: With Vsync=true Silk.NET seem to use incorrect UpdatePerSecond. The actual FPS its called is 10 lower than it should be (measured in the OnUpdate method)
windowOptions.WindowState = WindowState.Normal;
-windowOptions.Title = "Highbyte.DotNet6502 emulator in Silk.NET window (+ SkiaSharp, OpenGL, and NAudio)";
+windowOptions.Title = "Highbyte.DotNet6502 emulator + Silk.NET (with ImGui, SkiaSharp, OpenGL, NAudio)";
windowOptions.Size = new Vector2D(windowWidth, windowHeight);
windowOptions.WindowBorder = WindowBorder.Fixed;
windowOptions.API = GraphicsAPI.Default; // = Default = OpenGL 3.3 with forward compatibility
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetHostApp.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetHostApp.cs
index 077c6209..be49de2d 100644
--- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetHostApp.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetHostApp.cs
@@ -1,3 +1,4 @@
+using System.Reflection;
using Highbyte.DotNet6502.Impl.NAudio;
using Highbyte.DotNet6502.Impl.NAudio.NAudioOpenALProvider;
using Highbyte.DotNet6502.Impl.SilkNet;
@@ -6,6 +7,7 @@
using Highbyte.DotNet6502.Systems;
using Highbyte.DotNet6502.Systems.Logging.InMem;
using Microsoft.Extensions.Logging;
+using Silk.NET.Core;
namespace Highbyte.DotNet6502.App.SilkNetNative;
@@ -37,7 +39,7 @@ public float CanvasScale
get { return _emulatorConfig.CurrentDrawScale; }
set { _emulatorConfig.CurrentDrawScale = value; }
}
- public const int DEFAULT_WIDTH = 1000;
+ public const int DEFAULT_WIDTH = 1100;
public const int DEFAULT_HEIGHT = 700;
public const int DEFAULT_RENDER_HZ = 60;
@@ -66,6 +68,10 @@ public float CanvasScale
private GL _gl = default!;
private ImGuiController _imGuiController = default!;
+ private SKImage _logoImage;
+ private SKRect _logoImageDest;
+ private SkiaRenderContext _logoSkiaRenderContext;
+
///
/// Constructor
///
@@ -115,6 +121,9 @@ protected void OnLoad()
{
SetUninitializedWindow();
+ SetIcon();
+ InitLogo();
+
_renderContextContainer = CreateRenderContext();
_inputHandlerContext = CreateInputHandlerContext();
_audioHandlerContext = CreateAudioHandlerContext();
@@ -143,6 +152,13 @@ protected void OnLoad()
_imGuiWindows.Add(_logsPanel);
}
+ private void SetIcon()
+ {
+ //RawImage icon = SilkNetImageLoader.ReadFileAsRawImage("../../../../../../resources/images/favicon.ico");
+ RawImage icon = SilkNetImageLoader.ReadFileAsRawImage("Highbyte.DotNet6502.App.SilkNetNative.Resources.Images.favicon.ico", isEmbeddedResource: true);
+ _window.SetWindowIcon(ref icon);
+ }
+
protected void OnClosing()
{
base.Close();
@@ -259,6 +275,7 @@ public override void OnBeforeDrawFrame(bool emulatorWillBeRendered)
_gl.Clear((uint)(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit));
}
}
+
public override void OnAfterDrawFrame(bool emulatorRendered)
{
if (emulatorRendered)
@@ -274,21 +291,31 @@ public override void OnAfterDrawFrame(bool emulatorRendered)
if (_statsPanel.Visible)
_statsPanel.PostOnRender();
}
+ else
+ {
+ // If emulator was not rendered, draw logo
+ DrawLogo();
+
+ // Flush the SkiaSharp Context
+ _renderContextContainer.SkiaRenderContext.GetGRContext().Flush();
+ }
// Render logs if enabled, regardless of if emulator was rendered or not
if (_logsPanel.Visible)
_logsPanel.PostOnRender();
+ // Note: This check !emulatorRendered not needed if logo is drawn when emulator is not running (see above)
// If emulator was not rendered, clear Gl buffer before rendering ImGui windows
- if (!emulatorRendered)
- {
- if (_menu.Visible)
- _gl.Clear((uint)(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit));
- // Seems the canvas has to be drawn & flushed for ImGui stuff to be visible on top
- var canvas = _renderContextContainer.SkiaRenderContext.GetCanvas();
- canvas.Clear();
- _renderContextContainer.SkiaRenderContext.GetGRContext().Flush();
- }
+ //if (!emulatorRendered)
+ //{
+ // if (_menu.Visible)
+ // _gl.Clear((uint)(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit));
+
+ // //Seems the canvas has to be drawn & flushed for ImGui stuff to be visible on top
+ // var canvas = _renderContextContainer.SkiaRenderContext.GetCanvas();
+ // canvas.Clear();
+ // _renderContextContainer.SkiaRenderContext.GetGRContext().Flush();
+ //}
if (_menu.Visible)
_menu.PostOnRender();
@@ -510,4 +537,40 @@ public void ToggleLogsPanel()
}
}
+
+ private void InitLogo()
+ {
+ //string logoFile = "Resources/Images/Logo.png";
+ //_logoImage = SKImage.FromEncodedData(logoFile);
+
+ string logoResourcePath = "Highbyte.DotNet6502.App.SilkNetNative.Resources.Images.logo.png";
+ Assembly assembly = Assembly.GetExecutingAssembly();
+ using (Stream? resourceStream = assembly.GetManifestResourceStream(logoResourcePath))
+ {
+ if (resourceStream == null)
+ throw new Exception($"Cannot open stream to resource {logoResourcePath} in current assembly.");
+ _logoImage = SKImage.FromEncodedData(resourceStream);
+ }
+
+ float logo_width = 256;
+ float logo_height = 256;
+ float logo_x = DEFAULT_WIDTH / 2 - logo_width / 2;
+ float logo_y = DEFAULT_HEIGHT / 2 - logo_height / 2;
+
+ var scale = _emulatorConfig.CurrentDrawScale;
+
+ var left = logo_x / scale;
+ var top = logo_y / scale;
+ var right = left + (logo_width / scale);
+ var bottom = top + (logo_height / scale);
+
+ _logoImageDest = new SKRect(left, top, right, bottom);
+ }
+
+ private void DrawLogo()
+ {
+ var canvas = _renderContextContainer.SkiaRenderContext.GetCanvas();
+ canvas.Clear();
+ canvas.DrawImage(_logoImage, _logoImageDest);
+ }
}
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiMenu.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiMenu.cs
index 6949faa3..6dc2a573 100644
--- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiMenu.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImGuiMenu.cs
@@ -481,7 +481,7 @@ private void DrawGenericComputerConfig()
ImGui.EndDisabled();
_genericComputerConfigUI.PostOnRender("GenericComputer config");
- if (!_genericComputerConfigUI.IsValidConfig)
+ if (!SelectedSystemConfigIsValid())
{
ImGui.PushStyleColor(ImGuiCol.Text, s_errorColor);
ImGui.TextWrapped($"Config has errors. Press GenericComputerConfig button.");
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImageLoader.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImageLoader.cs
new file mode 100644
index 00000000..0f28071e
--- /dev/null
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetImageLoader.cs
@@ -0,0 +1,42 @@
+using System.Reflection;
+using Silk.NET.Core;
+
+namespace Highbyte.DotNet6502.App.SilkNetNative;
+public class SilkNetImageLoader
+{
+ public static RawImage ReadFileAsRawImage(string path, bool isEmbeddedResource = false)
+ {
+ byte[] fileBytes;
+ if (isEmbeddedResource)
+ {
+ Assembly assembly = Assembly.GetExecutingAssembly();
+ using (Stream? resourceStream = assembly.GetManifestResourceStream(path))
+ {
+ if (resourceStream == null)
+ throw new Exception($"Cannot open stream to resource {path} in current assembly.");
+ using (MemoryStream ms = new MemoryStream())
+ {
+ resourceStream.CopyTo(ms);
+ fileBytes = ms.ToArray();
+ }
+ }
+ }
+ else
+ {
+ fileBytes = File.ReadAllBytes(path);
+ }
+
+ var bitmap = SKBitmap.Decode(fileBytes);
+ byte[] bytes = new byte[bitmap.Pixels.Length * 4];
+ int index = 0;
+ foreach (var pixel in bitmap.Pixels)
+ {
+ bytes[index++] = pixel.Red;
+ bytes[index++] = pixel.Green;
+ bytes[index++] = pixel.Blue;
+ bytes[index++] = pixel.Alpha;
+ }
+ var rawImage = new RawImage(bitmap.Width, bitmap.Height, new Memory(bytes));
+ return rawImage;
+ }
+}
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SystemSetup/C64Setup.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SystemSetup/C64Setup.cs
index 9c1b5d4f..03660e47 100644
--- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SystemSetup/C64Setup.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SystemSetup/C64Setup.cs
@@ -52,46 +52,7 @@ public Task GetNewConfig(string configurationVariant)
var c64Config = new C64Config() { ROMs = new() };
_configuration.GetSection($"{C64Config.ConfigSectionName}.{configurationVariant}").Bind(c64Config);
-
- //var c64Config = new C64Config
- //{
- // C64Model = configurationVariant,
- // Vic2Model = C64ModelInventory.C64Models[configurationVariant].Vic2Models.First().Name, // NTSC, NTSC_old, PAL
-
- // //ROMDirectory = "%USERPROFILE%/Documents/C64/VICE/C64",
- // ROMDirectory = "%HOME%/Downloads/C64",
- // ROMs = new List
- // {
- // new ROM
- // {
- // Name = C64Config.BASIC_ROM_NAME,
- // File = "basic.901226-01.bin",
- // Data = null,
- // Checksum = "79015323128650c742a3694c9429aa91f355905e",
- // },
- // new ROM
- // {
- // Name = C64Config.CHARGEN_ROM_NAME,
- // File = "characters.901225-01.bin",
- // Data = null,
- // Checksum = "adc7c31e18c7c7413d54802ef2f4193da14711aa",
- // },
- // new ROM
- // {
- // Name = C64Config.KERNAL_ROM_NAME,
- // File = "kernal.901227-03.bin",
- // Data = null,
- // Checksum = "1d503e56df85a62fee696e7618dc5b4e781df1bb",
- // }
- // },
-
- // AudioSupported = true,
- // AudioEnabled = true,
-
- // InstrumentationEnabled = false, // Start with instrumentation off by default
- //};
-
- //c64Config.Validate();
+ c64Config.SetROMDefaultChecksums();
return Task.FromResult(c64Config);
}
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/appsettings.json b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/appsettings.json
index 9c5954c8..7103bb11 100644
--- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/appsettings.json
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/appsettings.json
@@ -38,7 +38,9 @@
"ColorMapName": "Default",
"AudioSupported": true,
- "AudioEnabled": true
+ "AudioEnabled": true,
+
+ "InstrumentationEnabled": false // Start with instrumentation off by default
},
"Highbyte.DotNet6502.C64.C64PAL": {
@@ -64,7 +66,10 @@
"ColorMapName": "Default",
"AudioSupported": true,
- "AudioEnabled": true
+ "AudioEnabled": true,
+
+ "InstrumentationEnabled": false // Start with instrumentation off by default
+
},
diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Emulator/SystemSetup/C64Setup.cs b/src/apps/Highbyte.DotNet6502.App.WASM/Emulator/SystemSetup/C64Setup.cs
index 9473c393..87afbb6e 100644
--- a/src/apps/Highbyte.DotNet6502.App.WASM/Emulator/SystemSetup/C64Setup.cs
+++ b/src/apps/Highbyte.DotNet6502.App.WASM/Emulator/SystemSetup/C64Setup.cs
@@ -58,6 +58,8 @@ public async Task GetNewConfig(string configurationVariant)
InstrumentationEnabled = false, // Start with instrumentation off by default
};
+ c64Config.SetROMDefaultChecksums();
+
//c64Config.Validate();
return c64Config;
@@ -124,7 +126,7 @@ private async Task> GetROMsFromLocalStorage()
roms.Add(new ROM
{
Name = name,
- Data = await _browserContext.LocalStorage.GetItemAsync($"{LOCAL_STORAGE_ROM_PREFIX}{name}")
+ Data = await _browserContext.LocalStorage.GetItemAsync($"{LOCAL_STORAGE_ROM_PREFIX}{name}"),
});
}
name = C64Config.KERNAL_ROM_NAME;
@@ -134,7 +136,7 @@ private async Task> GetROMsFromLocalStorage()
roms.Add(new ROM
{
Name = name,
- Data = await _browserContext.LocalStorage.GetItemAsync($"{LOCAL_STORAGE_ROM_PREFIX}{name}")
+ Data = await _browserContext.LocalStorage.GetItemAsync($"{LOCAL_STORAGE_ROM_PREFIX}{name}"),
});
}
name = C64Config.CHARGEN_ROM_NAME;
@@ -144,7 +146,7 @@ private async Task> GetROMsFromLocalStorage()
roms.Add(new ROM
{
Name = name,
- Data = await _browserContext.LocalStorage.GetItemAsync($"{LOCAL_STORAGE_ROM_PREFIX}{name}")
+ Data = await _browserContext.LocalStorage.GetItemAsync($"{LOCAL_STORAGE_ROM_PREFIX}{name}"),
});
}
diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Highbyte.DotNet6502.App.WASM.csproj b/src/apps/Highbyte.DotNet6502.App.WASM/Highbyte.DotNet6502.App.WASM.csproj
index 511bbfa8..1d8bb3ad 100644
--- a/src/apps/Highbyte.DotNet6502.App.WASM/Highbyte.DotNet6502.App.WASM.csproj
+++ b/src/apps/Highbyte.DotNet6502.App.WASM/Highbyte.DotNet6502.App.WASM.csproj
@@ -43,6 +43,7 @@
+
@@ -58,4 +59,8 @@
+
+
+
+
diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Pages/Commodore64/C64ConfigUI.razor b/src/apps/Highbyte.DotNet6502.App.WASM/Pages/Commodore64/C64ConfigUI.razor
index 92cbe6a8..bde77cee 100644
--- a/src/apps/Highbyte.DotNet6502.App.WASM/Pages/Commodore64/C64ConfigUI.razor
+++ b/src/apps/Highbyte.DotNet6502.App.WASM/Pages/Commodore64/C64ConfigUI.razor
@@ -253,11 +253,11 @@
var fileSize = fileBuffer.Length;
if (isKernal)
- SetROM(C64Config.KERNAL_ROM_NAME, fileBuffer);
+ C64Config.SetROM(C64Config.KERNAL_ROM_NAME, data: fileBuffer);
else if (isBasic)
- SetROM(C64Config.BASIC_ROM_NAME, fileBuffer);
+ C64Config.SetROM(C64Config.BASIC_ROM_NAME, data: fileBuffer);
else if (isChargen)
- SetROM(C64Config.CHARGEN_ROM_NAME, fileBuffer);
+ C64Config.SetROM(C64Config.CHARGEN_ROM_NAME, data: fileBuffer);
}
catch (Exception)
{
@@ -269,24 +269,6 @@
this.StateHasChanged();
}
- private void SetROM(string romName, byte[] data)
- {
- ROM rom;
- if (!C64Config.ROMs.Exists(x => x.Name == romName))
- {
- rom = new()
- {
- Name = romName
- };
- C64Config.ROMs.Add(rom);
- }
- else
- {
- rom = C64Config.ROMs.Single(x => x.Name == romName);
- }
- rom.Data = data;
- }
-
private void OnRendererChanged(ChangeEventArgs e)
=> C64HostConfig.Renderer = Enum.Parse(e.Value!.ToString()!);
diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/Pages/Generic/GenericMenuHelp.razor b/src/apps/Highbyte.DotNet6502.App.WASM/Pages/Generic/GenericMenuHelp.razor
index 85c2f615..b1d3f6a0 100644
--- a/src/apps/Highbyte.DotNet6502.App.WASM/Pages/Generic/GenericMenuHelp.razor
+++ b/src/apps/Highbyte.DotNet6502.App.WASM/Pages/Generic/GenericMenuHelp.razor
@@ -8,13 +8,7 @@
- A generic 6502 CPU computer, with custom defined memory layout.
-
-
- Currently configured in the application config and code.
- A future update could make it possible to configure such things as
- total memory, screen memory addres and IO memory addresses in
- the UI.
+ A generic 6502-based computer, with custom defined memory layout and IO functionallity.
A 6502 CPU emulator written in .NET, rendered with SkiaSharp, compiled to WebAssembly via Blazor, running in a browser.
diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/6502binaries/Generic/Assembler/hostinteraction_scroll_text_and_cycle_colors.prg b/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/6502binaries/Generic/Assembler/hostinteraction_scroll_text_and_cycle_colors.prg
index 42ced4fd..f8933b3a 100644
Binary files a/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/6502binaries/Generic/Assembler/hostinteraction_scroll_text_and_cycle_colors.prg and b/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/6502binaries/Generic/Assembler/hostinteraction_scroll_text_and_cycle_colors.prg differ
diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/favicon.ico b/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/favicon.ico
index 63e859b4..f804586d 100644
Binary files a/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/favicon.ico and b/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/favicon.ico differ
diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/images/logo.png b/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/images/logo.png
new file mode 100644
index 00000000..daad1737
Binary files /dev/null and b/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/images/logo.png differ
diff --git a/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/index.html b/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/index.html
index dcaf65a9..e437fea8 100644
--- a/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/index.html
+++ b/src/apps/Highbyte.DotNet6502.App.WASM/wwwroot/index.html
@@ -10,6 +10,7 @@
+
diff --git a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Video/C64SadConsoleRenderer.cs b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Video/C64SadConsoleRenderer.cs
index bb55cf0c..cd73726e 100644
--- a/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Video/C64SadConsoleRenderer.cs
+++ b/src/libraries/Highbyte.DotNet6502.Impl.SadConsole/Commodore64/Video/C64SadConsoleRenderer.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics;
using Highbyte.DotNet6502.Systems;
using Highbyte.DotNet6502.Systems.Commodore64;
using Highbyte.DotNet6502.Systems.Commodore64.Video;
@@ -124,35 +125,97 @@ public void DrawEmulatorCharacterOnScreen(int x, int y, byte emulatorCharacter,
}
byte sadConsoleCharacter;
- // Default to C64 screen codes as source
- sadConsoleCharacter = TranslateC64ScreenCodeToSadConsoleC64Font(emulatorCharacter);
+ sadConsoleCharacter = TranslateC64ScreenCodeToSadConsoleC64ROMFontIndex(emulatorCharacter, out bool inverted);
+
+ Color fgColorTemp = _c64SadConsoleColors.GetSadConsoleColor(ColorMaps.GetSystemColor(emulatorFgColor, c64.ColorMapName));
+ Color bgColorTemp = _c64SadConsoleColors.GetSadConsoleColor(ColorMaps.GetSystemColor(emulatorBgColor, c64.ColorMapName));
+
+ Color fgColor;
+ Color bgColor;
+ if (inverted)
+ {
+ fgColor = bgColorTemp;
+ bgColor = fgColorTemp;
+ }
+ else
+ {
+ fgColor = fgColorTemp;
+ bgColor = bgColorTemp;
+ };
DrawCharacter(
x,
y,
sadConsoleCharacter,
- _c64SadConsoleColors.GetSadConsoleColor(ColorMaps.GetSystemColor(emulatorFgColor, c64.ColorMapName)),
- _c64SadConsoleColors.GetSadConsoleColor(ColorMaps.GetSystemColor(emulatorBgColor, c64.ColorMapName))
- );
+ fgColor,
+ bgColor);
}
- private byte TranslateC64ScreenCodeToSadConsoleC64Font(byte sourceByte)
+ private byte TranslateC64ScreenCodeToSadConsoleC64ROMFontIndex(byte sourceByte, out bool inverted)
{
- switch (sourceByte & 0xff)
+ // The custom SadConsole font C64_ROM.font:
+ // - is a combination of the two built-in C64 ROM fonts, the non-inverted parts of "shifted" and "unshifted" ROM fonts.
+ // - the first 128 characters from the shifted font (non-inverted) are index 0-127.
+ // - the first 128 characters from the unshufter font (non-inverted) are index 1-255.
+ // - Other changes:
+ // - A sad console font must have index 0 as empty (only transparent background). As the C64 font has a @ sign in this position, it has been removed.
+ // - A sad console font must have one index as a solid block (only forground). As the C64 non-inverted parts of the C64 ROM fons does not contain a solid block, a duplicate of an empty block (160) has been changed to contain a solid block, one of the duplicate empty characters has been replace with a solid block.
+ // - These differences, as well as detecting inverted characters (and swapping foreground/background color when drawing) are mapped via code.
+
+
+ // ----------
+ // Mapping general C64 font -> SadConsole C64_ROM.font font translation
+ // ----------
+ byte sadConsoleGlyphIndex;
+ bool isUnshiftedC64CharRom = _c64.Vic2.CharsetManager.CharacterSetAddressInVIC2BankIsChargenROMUnshifted;
+ if (isUnshiftedC64CharRom)
{
- case 0xa0: //160, C64 inverted space
- return 219; // Inverted square in SadConsole C64 font
- case 0xe0: //224, Also C64 inverted space?
- return 219; // Inverted square in SadConsole C64 font
- default:
-
- // Convert C64 screen code to PETSCII
- var sadConsoleCharacter = Petscii.C64ScreenCodeToPetscII(sourceByte);
- // TODO: Also convert to ASCII? Would depend on the font being used?
- //sadConsoleCharacter = CharacterMaps.PETSCIICodeToASCII(sadConsoleCharacter);
-
- return sadConsoleCharacter;
+ if (sourceByte < 128)
+ sadConsoleGlyphIndex = (byte)(sourceByte + 128);
+ else
+ sadConsoleGlyphIndex = sourceByte;
}
+ else
+ {
+ if (sourceByte >= 128)
+ sadConsoleGlyphIndex = (byte)(sourceByte - 128);
+ else
+ sadConsoleGlyphIndex = sourceByte;
+ }
+
+ // ----------
+ // Mapping exceptions between C64 font -> SadConsole C64_ROM.font font
+ // ----------
+ // The first glyph in a SadConsole font must be empty (where the C64 ROM font has a @ sign).
+ // The @ sign has been removed from the C64 ROM font. The @ sign has a duplicate in position 128.
+ if (sourceByte == 0)
+ {
+ sadConsoleGlyphIndex = 128;
+ inverted = false;
+ }
+ // One glyph in SadConsole font must contain a solid block. As the combined font from C64 shifted and unshifted (with both inverted removed),
+ // a duplicate of an empty block (160) has been changed to contain a solid block.
+ // To avoid Space (32) when using an unshifted C64 Char ROM (that would add 128 to the index of the combined SadConsole font) to
+ // be the solid block (see below), always use Space (32).
+ else if (sourceByte == 32)
+ {
+ sadConsoleGlyphIndex = 32;
+ inverted = false;
+ }
+ // One glyph in SadConsole font must contain a solid block. As the combined font from C64 shifted and unshifted (with both inverted removed),
+ // a duplicate of an empty block (160) has been changed to contain a solid block.
+ // In the original C64 font (both shifted and unshifted) this postion was a solid block. So use it, and tell not to invert it.
+ else if (sourceByte == 160)
+ {
+ sadConsoleGlyphIndex = 160;
+ inverted = false;
+ }
+ else
+ {
+ inverted = sourceByte >= 128;
+ }
+
+ return sadConsoleGlyphIndex;
}
private int GetBorderCols(C64 c64)
diff --git a/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/v1/C64SkiaRenderer.cs b/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/v1/C64SkiaRenderer.cs
index 9599a702..2f05f652 100644
--- a/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/v1/C64SkiaRenderer.cs
+++ b/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/v1/C64SkiaRenderer.cs
@@ -516,7 +516,9 @@ private void RenderSprites(C64 c64, SKCanvas canvas, bool spritesWithPriorityOve
var imageDest = new SKRect(spriteCanvasX, spriteCanvasY, spriteCanvasX + spriteWidth, spriteCanvasY + spriteHeight);
if (sprite.Multicolor)
+ {
canvas.DrawImage(spriteImage, imageDest);
+ }
else
{
var paint = _c64SkiaPaint.GetDrawSpritePaint(sprite.Color);
diff --git a/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/v1/Chargen.cs b/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/v1/Chargen.cs
index d8352327..1550d58e 100644
--- a/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/v1/Chargen.cs
+++ b/src/libraries/Highbyte.DotNet6502.Impl.Skia/Commodore64/Video/v1/Chargen.cs
@@ -6,10 +6,9 @@ public class CharGen
{
// About the CharacterImageDraw* variables:
// - They are replaced with a transformation when drawn on screen based on the text mode, and it's configured C64 colors.
- // - It doesn't matter which color they are set to, just that they are different, and not a valid C64 color.
- public static SKColor CharacterImageDrawColor = SKColors.DarkKhaki;
- public static SKColor CharacterImageDrawMultiColorBG1 = SKColors.DarkOrchid;
- public static SKColor CharacterImageDrawMultiColorBG2 = SKColors.DarkGoldenrod;
+ public static SKColor CharacterImageDrawColor = SKColors.White;
+ public static SKColor CharacterImageDrawMultiColorBG1 = SKColors.DarkOrchid; // This color must not be in C64 color pallette.
+ public static SKColor CharacterImageDrawMultiColorBG2 = SKColors.DarkGoldenrod; // This color must not be a C64 color pallette.
private static readonly SKPaint s_paint;
private static readonly SKPaint s_paintMultiColorBG1;
diff --git a/src/libraries/Highbyte.DotNet6502.Systems.Commodore64/Config/C64Config.cs b/src/libraries/Highbyte.DotNet6502.Systems.Commodore64/Config/C64Config.cs
index 7fe4f040..46dbfbc1 100644
--- a/src/libraries/Highbyte.DotNet6502.Systems.Commodore64/Config/C64Config.cs
+++ b/src/libraries/Highbyte.DotNet6502.Systems.Commodore64/Config/C64Config.cs
@@ -15,15 +15,47 @@ public void ClearDirty()
_isDirty = false;
}
+ // ROM version info from: https://www.commodore.ca/manuals/funet/cbm/firmware/computers/c64/
+ // Checksums calculated with SHA1
+
public const string KERNAL_ROM_NAME = "kernal";
+ public static Dictionary DefaultKernalROMChecksums = new()
+ {
+ // Commodore 64 KERNAL ROM Revision 1. The RS-232 timing table is designed for exactly 1 MHz system clock frequency, although no C64 runs at that clock rate. Ripped from a very old American C64.
+ { "901227-01", "87cc04d61fc748b82df09856847bb5c2754a2033" },
+
+ // Commodore 64 KERNAL ROM Revision 2. Can be found on 1982 and 1983 models.
+ { "901227-02", "0e2e4ee3f2d41f00bed72f9ab588b83e306fdb13" },
+
+ // ! RECOMENDED ! Commodore 64 KERNAL ROM Revision 3. The last revision, also used in the C128's C64 mode.
+ { "901227-03", "1d503e56df85a62fee696e7618dc5b4e781df1bb" },
+
+ // Commodore 64 KERNAL ROM Revision 3, patched for Swedish/Finnish keyboard layout.
+ { "swedish", "e4f52d9b36c030eb94524eb49f6f0774c1d02e5e" },
+
+ // Commodore PET64 or 4064 KERNAL. With black&white startup colors, and with a different bootup message. Machines with color monitors used the standard Commodore 64 KERNAL ROM.
+ { "4064.901246-01", "6c4fa9465f6091b174df27dfe679499df447503c" },
+ };
public const string BASIC_ROM_NAME = "basic";
+ public static Dictionary DefaultBasicROMChecksums = new()
+ {
+ // Commodore 64 BASIC V2. The first and only revision.
+ { "901226-01", "79015323128650c742a3694c9429aa91f355905e" }
+ };
public const string CHARGEN_ROM_NAME = "chargen";
+ public static Dictionary DefaultCharGenROMChecksums = new()
+ {
+ // The character generator ROM.
+ { "901225-01", "adc7c31e18c7c7413d54802ef2f4193da14711aa" }
+ };
+
public static List RequiredROMs = new()
{
KERNAL_ROM_NAME, BASIC_ROM_NAME, CHARGEN_ROM_NAME
};
- public bool LoadROMs { get; set; } = true;
+ public bool LoadROMs { get; set; } = true; // Set to false for unit tests
+
private List _roms = default!;
public List ROMs
{
@@ -155,21 +187,18 @@ public C64Config()
Name = BASIC_ROM_NAME,
File = "basic",
Data = null,
- Checksum = "79015323128650c742a3694c9429aa91f355905e",
},
new ROM
{
Name = CHARGEN_ROM_NAME,
File = "chargen",
Data = null,
- Checksum = "adc7c31e18c7c7413d54802ef2f4193da14711aa",
},
new ROM
{
Name = KERNAL_ROM_NAME,
File = "kernal",
Data = null,
- Checksum = "1d503e56df85a62fee696e7618dc5b4e781df1bb",
},
};
_romDirectory = "%USERPROFILE%/Documents/C64/VICE/C64";
@@ -211,12 +240,44 @@ public void SetROM(string romName, string? file = null, byte[]? data = null)
File = file,
Data = data
};
+
+ SetROMDefaultCheckum(rom);
+
ROMs.Add(rom);
}
_isDirty = true;
}
+ public void SetROMDefaultChecksums()
+ {
+ foreach (var rom in ROMs)
+ {
+ SetROMDefaultCheckum(rom);
+ }
+ }
+
+ private void SetROMDefaultCheckum(ROM rom)
+ {
+ // If checksum(s) is already set (from config file), skip setting default checksum(s).
+ if (rom.ValidVersionChecksums.Count != 0)
+ return;
+
+ // Set default checksums for known ROMs.
+ if (rom.Name == BASIC_ROM_NAME)
+ {
+ rom.ValidVersionChecksums = DefaultBasicROMChecksums;
+ }
+ else if (rom.Name == KERNAL_ROM_NAME)
+ {
+ rom.ValidVersionChecksums = DefaultKernalROMChecksums;
+ }
+ else if (rom.Name == CHARGEN_ROM_NAME)
+ {
+ rom.ValidVersionChecksums = DefaultCharGenROMChecksums;
+ }
+ }
+
public object Clone()
{
var clone = (C64Config)this.MemberwiseClone();
@@ -263,11 +324,15 @@ public bool IsValid(out List validationErrors)
if (!Directory.Exists(romDir))
validationErrors.Add($"{nameof(ROMDirectory)} is not an existing directory: {romDir}");
}
- foreach (var rom in ROMs)
+ // Skip other ROM validation if the ROM directory is not valid.
+ if (validationErrors.Count == 0)
{
- var romValidationErrors = new List();
- if (!rom.Validate(out romValidationErrors, ROMDirectory))
- validationErrors.AddRange(romValidationErrors);
+ foreach (var rom in ROMs)
+ {
+ var romValidationErrors = new List();
+ if (!rom.Validate(out romValidationErrors, ROMDirectory))
+ validationErrors.AddRange(romValidationErrors);
+ }
}
return validationErrors.Count == 0;
diff --git a/src/libraries/Highbyte.DotNet6502.Systems.Generic/GenericComputerBuilder.cs b/src/libraries/Highbyte.DotNet6502.Systems.Generic/GenericComputerBuilder.cs
index efdc3ab8..1df5e2d3 100644
--- a/src/libraries/Highbyte.DotNet6502.Systems.Generic/GenericComputerBuilder.cs
+++ b/src/libraries/Highbyte.DotNet6502.Systems.Generic/GenericComputerBuilder.cs
@@ -2,6 +2,7 @@
using Highbyte.DotNet6502.Systems.Generic.Config;
using Highbyte.DotNet6502.Utils;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
namespace Highbyte.DotNet6502.Systems.Generic;
@@ -10,9 +11,11 @@ public class GenericComputerBuilder
private readonly GenericComputer _genericComputer;
private readonly ILoggerFactory _loggerFactory;
- public GenericComputerBuilder(ILoggerFactory loggerFactory) : this(loggerFactory, new GenericComputerConfig()) { }
+ public GenericComputerBuilder() : this(new GenericComputerConfig(), NullLoggerFactory.Instance) { }
- public GenericComputerBuilder(ILoggerFactory loggerFactory, GenericComputerConfig genericComputerConfig)
+ public GenericComputerBuilder(ILoggerFactory loggerFactory) : this(new GenericComputerConfig(), loggerFactory) { }
+
+ public GenericComputerBuilder(GenericComputerConfig genericComputerConfig, ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
_genericComputer = new GenericComputer(genericComputerConfig, loggerFactory);
@@ -96,8 +99,7 @@ public static GenericComputer SetupGenericComputerFromConfig(GenericComputerConf
Debug.WriteLine($"Loading 6502 prg file from binary file.");
Debug.WriteLine($"{emulatorConfig.ProgramBinaryFile}");
- BinaryLoader.Load(
- mem,
+ mem.Load(
emulatorConfig.ProgramBinaryFile,
out loadedAtAddress,
out fileLength);
@@ -117,7 +119,7 @@ public static GenericComputer SetupGenericComputerFromConfig(GenericComputerConf
}
// Initialize emulator with CPU, memory, and execution parameters
- var computerBuilder = new GenericComputerBuilder(loggerFactory, emulatorConfig);
+ var computerBuilder = new GenericComputerBuilder(emulatorConfig, loggerFactory);
computerBuilder
.WithCPU()
.WithStartAddress(loadedAtAddress)
diff --git a/src/libraries/Highbyte.DotNet6502.Systems/ROM.cs b/src/libraries/Highbyte.DotNet6502.Systems/ROM.cs
index 47d39e9c..9ba05343 100644
--- a/src/libraries/Highbyte.DotNet6502.Systems/ROM.cs
+++ b/src/libraries/Highbyte.DotNet6502.Systems/ROM.cs
@@ -19,9 +19,9 @@ public class ROM
public byte[]? Data { get; set; }
///
- /// Checksum of ROM.
+ /// Valid SHA1 checksum dictionary for ROM (version descriptor, SHA 1checksum)
///
- public string? Checksum { get; set; }
+ public Dictionary ValidVersionChecksums { get; set; } = new();
public static List Clone(List roms)
{
@@ -38,7 +38,7 @@ public ROM Clone()
Name = Name,
File = File,
Data = Data,
- Checksum = Checksum,
+ ValidVersionChecksums = new Dictionary(ValidVersionChecksums),
};
}
@@ -53,17 +53,40 @@ public bool Validate(out List validationErrors, string romDirectory)
validationErrors = new List();
if (string.IsNullOrEmpty(Name))
validationErrors.Add($"ROM {nameof(Name)} must be set.");
- if (string.IsNullOrEmpty(File) && (Data == null || Data.Length == 0))
- validationErrors.Add($"ROM {nameof(File)} and {nameof(Data)} must be set.");
if (!string.IsNullOrEmpty(File) && (Data != null && Data.Length > 0))
- validationErrors.Add($"ROM {nameof(File)} and {nameof(Data)} cannot be set.");
+ {
+ validationErrors.Add($"ROM {nameof(File)} and {nameof(Data)} cannot both be set.");
+ }
+ if (string.IsNullOrEmpty(File) && (Data == null || Data.Length == 0))
+ {
+ validationErrors.Add($"ROM {nameof(File)} or {nameof(Data)} must be set.");
+ }
+ bool fileExists = false;
if (!string.IsNullOrEmpty(File))
{
var romFilePath = GetROMFilePath(romDirectory);
if (!System.IO.File.Exists(romFilePath))
+ {
validationErrors.Add($"ROM file does not exist: {romFilePath}");
+ }
+ else
+ {
+ fileExists = true;
+ }
+ }
+
+ var romData = GetRomData(romFileAssumedToExist: fileExists, romDirectory);
+ if (romData != null)
+ {
+ var checksum = GetSHAChecksum(romData);
+ if (!ValidVersionChecksums.Values.Contains(checksum))
+ {
+ validationErrors.Add($"{Name} ROM checksum error. Expected ver(s): {string.Join(',', ValidVersionChecksums.Keys)}");
+ //validationErrors.Add($"{Name} ROM checksum error. Expected one of: {string.Join(',', ValidVersionChecksums.Values)}, Actual: {checksum}");
+ }
}
+
return validationErrors.Count == 0;
}
@@ -72,24 +95,35 @@ public static Dictionary LoadROMS(string directory, ROM[] roms)
var romsData = new Dictionary();
foreach (var rom in roms)
{
+ var romData = rom.GetRomData(romFileAssumedToExist: true, directory);
+ romsData.Add(rom.Name, romData!);
+ }
+ return romsData;
+ }
- byte[] fileData;
- if (!string.IsNullOrEmpty(rom.File))
+ private byte[]? GetRomData(bool romFileAssumedToExist, string? directory = null)
+ {
+ byte[]? romData;
+ if (!string.IsNullOrEmpty(File))
+ {
+ if (romFileAssumedToExist)
{
- var romFilePath = rom.GetROMFilePath(directory);
- fileData = System.IO.File.ReadAllBytes(romFilePath);
+ var romFilePath = GetROMFilePath(directory);
+ romData = System.IO.File.ReadAllBytes(romFilePath);
}
else
{
- fileData = rom.Data!;
+ romData = null;
}
- // TODO: Verify checksum
- romsData.Add(rom.Name, fileData);
}
- return romsData;
+ else
+ {
+ romData = Data;
+ }
+ return romData;
}
- public string GetROMFilePath(string romDirectory)
+ public string GetROMFilePath(string? romDirectory)
{
if (File == null)
throw new DotNet6502Exception($"Cannot get ROM file path if rom File is empty.");
@@ -100,4 +134,11 @@ public string GetROMFilePath(string romDirectory)
romFilePath = File;
return PathHelper.ExpandOSEnvironmentVariables(romFilePath);
}
+
+ private string GetSHAChecksum(byte[] data)
+ {
+ using var sha1 = System.Security.Cryptography.SHA1.Create();
+ var hash = sha1.ComputeHash(data);
+ return BitConverter.ToString(hash).Replace("-", "").ToLower();
+ }
}
diff --git a/src/libraries/Highbyte.DotNet6502/CPU.cs b/src/libraries/Highbyte.DotNet6502/CPU.cs
index 43c8899c..3a030ab7 100644
--- a/src/libraries/Highbyte.DotNet6502/CPU.cs
+++ b/src/libraries/Highbyte.DotNet6502/CPU.cs
@@ -188,6 +188,17 @@ public ExecState ExecuteOneInstruction(
return Execute(mem, LegacyExecEvaluator.OneInstructionExecEvaluator);
}
+ ///
+ /// Executes until BRK instruction is encountered, and will fire events and collect statistics.
+ ///
+ ///
+ ///
+ public ExecState ExecuteUntilBRK(
+ Memory mem)
+ {
+ return Execute(mem, LegacyExecEvaluator.UntilBRKExecEvaluator);
+ }
+
///
/// Executes instructions in a loop until a condition is triggered in one of the specified ExecEvaluators.
/// Events are also triggered for different stages of the execution.
diff --git a/src/libraries/Highbyte.DotNet6502/ExecEvaluator.cs b/src/libraries/Highbyte.DotNet6502/ExecEvaluator.cs
index 19504d76..1ad85d57 100644
--- a/src/libraries/Highbyte.DotNet6502/ExecEvaluator.cs
+++ b/src/libraries/Highbyte.DotNet6502/ExecEvaluator.cs
@@ -14,7 +14,8 @@ public class LegacyExecEvaluator : IExecEvaluator
private readonly ExecOptions _execOptions;
- public static LegacyExecEvaluator OneInstructionExecEvaluator = new LegacyExecEvaluator(new ExecOptions { MaxNumberOfInstructions = 1 });
+ public readonly static LegacyExecEvaluator OneInstructionExecEvaluator = new LegacyExecEvaluator(new ExecOptions { MaxNumberOfInstructions = 1 });
+ public readonly static LegacyExecEvaluator UntilBRKExecEvaluator = new LegacyExecEvaluator(new ExecOptions { BRKInstructionStopsExecution = true });
public static LegacyExecEvaluator InstructionCountExecEvaluator(ulong numberOfInstructions)
{
diff --git a/src/libraries/Highbyte.DotNet6502/Utils/BinaryLoader.cs b/src/libraries/Highbyte.DotNet6502/Utils/BinaryLoader.cs
index e35d661f..1ff873f7 100644
--- a/src/libraries/Highbyte.DotNet6502/Utils/BinaryLoader.cs
+++ b/src/libraries/Highbyte.DotNet6502/Utils/BinaryLoader.cs
@@ -23,10 +23,10 @@ public static Memory Load(
/// Otherwise it assumes first two bytes is load address.
/// Returns a new Memory instance.
///
- ///
- /// The address the program as loaded at.
- /// The file size in bytes
- /// Optional. If not specified, the two first bytes in file is assumed to be the load address
+ /// The binary file to load.
+ /// Is set to the address the binary file was loaded at.
+ /// Is set to file size in bytes.
+ /// Optional. If not specified, the two first bytes in file is assumed to be the load address.
public static Memory Load(
string binaryFilePath,
out ushort loadedAtAddress,
@@ -34,21 +34,37 @@ public static Memory Load(
ushort? forceLoadAddress = null)
{
Memory mem = new(mapToDefaultRAM: true);
- Load(mem, binaryFilePath, out loadedAtAddress, out fileLength, forceLoadAddress);
+ mem.Load(binaryFilePath, out loadedAtAddress, out fileLength, forceLoadAddress);
return mem;
}
+
+ ///
+ /// Load binary file into memory. Assume first two bytes is load address.
+ ///
+ ///
+ ///
+ /// Is set to the address the binary file was loaded to.
+ public static void Load(
+ this Memory mem,
+ string binaryFilePath,
+ out ushort loadedAtAddress)
+ {
+ mem.Load(binaryFilePath, out loadedAtAddress, out var _);
+ }
+
///
/// Load binary file into memory. If forceLoadAddress is specified, the binary is loaded at that address.
/// Otherwise it assumes first two bytes is load address.
/// Loads into the provided Memory instance.
///
- ///
- /// The address the program as loaded at.
- /// The file size in bytes
- /// Optional. If not specified, the two first bytes in file is assumed to be the load address
+ ///
+ /// The binary file to load.
+ /// Is set to the address the binary file was loaded at.
+ /// Is set to file size in bytes.
+ /// Optional. If not specified, the two first bytes in file is assumed to be the load address.
public static void Load(
- Memory mem,
+ this Memory mem,
string binaryFilePath,
out ushort loadedAtAddress,
out ushort fileLength,
@@ -68,8 +84,19 @@ out fileLength
mem.StoreData(loadedAtAddress, fileData);
}
+ ///
+ /// Load byte array into memory. If forceLoadAddress is specified, the binary is loaded at that address.
+ /// Otherwise it assumes first two bytes is load address.
+ /// Loads into the provided Memory instance.
+ ///
+ /// Byte array of the data to load
+ /// Is set to the address the byte array was loaded to
+ /// Is set to the byte array size
+ /// Optional. If not specified, the two first bytes in file is assumed to be the load address
+
+ ///
public static void Load(
- Memory mem,
+ this Memory mem,
byte[] fileData,
out ushort loadedAtAddress,
out ushort fileLength,
diff --git a/tests/Highbyte.DotNet6502.Tests/CPUTest.cs b/tests/Highbyte.DotNet6502.Tests/CPUTest.cs
index 5497eb5a..129f153b 100644
--- a/tests/Highbyte.DotNet6502.Tests/CPUTest.cs
+++ b/tests/Highbyte.DotNet6502.Tests/CPUTest.cs
@@ -170,6 +170,29 @@ public void ExecuteOneInstruction_Only_Executes_One_Instruction()
Assert.Equal((ulong)1, execState.InstructionsExecutionCount);
}
+ [Fact]
+ public void ExecuteUntilBRK_Executes_Until_BRK_Instruction()
+ {
+ // Arrange
+ var cpu = new CPU();
+ var mem = new Memory();
+
+ ushort originalPC = 0x1000;
+ ushort addr = originalPC;
+ mem[addr++] = (byte)OpCodeId.NOP;
+ mem[addr++] = (byte)OpCodeId.NOP;
+ mem[addr++] = (byte)OpCodeId.NOP;
+ mem[addr++] = (byte)OpCodeId.BRK;
+ cpu.PC = originalPC;
+
+ // Act
+ var execState = cpu.ExecuteUntilBRK(
+ mem);
+
+ // Assert
+ Assert.Equal((ulong)4, execState.InstructionsExecutionCount);
+ }
+
[Fact]
public void ExecuteOneInstructionMinimal_Only_Executes_One_Instruction()
{