diff --git a/.vscode/launch.json b/.vscode/launch.json
index f34fa11a5..7e9bbff30 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -50,12 +50,31 @@
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "externalTerminal",
"stopAtEntry": false
- },
+ },
+ {
+ "name": "MCP server console app - .NET Core Launch",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build mcp server",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${workspaceFolder}/src/utils/Highbyte.DotNet6502.Util.MCPServer/bin/Debug/net9.0/Highbyte.DotNet6502.Util.MCPServer.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/src/utils/Highbyte.DotNet6502.Util.MCPServer/bin/Debug/net9.0/",
+ // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
+ "console": "externalTerminal",
+ "stopAtEntry": false
+ },
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
+ },
+ {
+ "name": ".NET Core Attach MCPServer (Windows)",
+ "type": "coreclr",
+ "request": "attach",
+ "processName": "Highbyte.DotNet6502.Util.MCPServer.exe"
}
]
}
\ No newline at end of file
diff --git a/.vscode/mcp.json b/.vscode/mcp.json
new file mode 100644
index 000000000..41ca54bfd
--- /dev/null
+++ b/.vscode/mcp.json
@@ -0,0 +1,50 @@
+{
+ "inputs": [],
+ "servers": {
+ // "DotNet6502-Embedded-Windows": {
+ // "type": "stdio",
+ // "command": "dotnet",
+ // "args": [
+ // "run",
+ // "--project",
+ // "C:\\Users\\highb\\source\\repos\\dotnet-6502\\src\\utils\\Highbyte.DotNet6502.Util.MCPServer\\Highbyte.DotNet6502.Util.MCPServer.csproj"
+ // ],
+ // "cwd": "C:\\Users\\highb\\source\\repos\\dotnet-6502\\src\\utils\\Highbyte.DotNet6502.Util.MCPServer"
+ // }
+
+ // "DotNet6502-Embedded-Mac": {
+ // "type": "stdio",
+ // "command": "dotnet",
+ // "args": [
+ // "run",
+ // "--project",
+ // "/Users/highbyte/source/repos/dotnet-6502/src/utils/Highbyte.DotNet6502.Util.MCPServer/Highbyte.DotNet6502.Util.MCPServer.csproj"
+ // ],
+ // "cwd": "/Users/highbyte/source/repos/dotnet-6502/src/utils/Highbyte.DotNet6502.Util.MCPServer"
+ // }
+
+ "DotNet6502-SadConsole-Windows": {
+ "type": "stdio",
+ "command": "C:\\Users\\highb\\source\\repos\\dotnet-6502\\src\\apps\\Highbyte.DotNet6502.App.SadConsole\\bin\\Debug\\net9.0\\Highbyte.DotNet6502.App.SadConsole.exe",
+ "cwd": "C:\\Users\\highb\\source\\repos\\dotnet-6502\\src\\apps\\Highbyte.DotNet6502.App.SadConsole\\bin\\Debug\\net9.0"
+ }
+
+ // "DotNet6502-SadConsole-Mac": {
+ // "type": "stdio",
+ // "command": "/Users/highbyte/source/repos/dotnet-6502/src/apps/Highbyte.DotNet6502.App.SadConsole/bin/Debug/net9.0/Highbyte.DotNet6502.App.SadConsole",
+ // "cwd": "/Users/highbyte/source/repos/dotnet-6502/src/apps/Highbyte.DotNet6502.App.SadConsole/bin/Debug/net9.0"
+ // }
+
+ //"DotNet6502-SilkNet-Windows": {
+ // "type": "stdio",
+ // "command": "C:\\Users\\highb\\source\\repos\\dotnet-6502\\src\\apps\\Highbyte.DotNet6502.App.SilkNetNative\\bin\\Debug\\net9.0\\Highbyte.DotNet6502.App.SilkNetNative.exe",
+ // "cwd": "C:\\Users\\highb\\source\\repos\\dotnet-6502\\src\\apps\\Highbyte.DotNet6502.App.SilkNetNative\\bin\\Debug\\net9.0"
+ //}
+
+ // "DotNet6502-SilkNet-Mac": {
+ // "type": "stdio",
+ // "command": "/Users/highbyte/source/repos/dotnet-6502/src/apps/Highbyte.DotNet6502.App.SilkNetNative/bin/Debug/net9.0/Highbyte.DotNet6502.App.SilkNetNative",
+ // "cwd": "/Users/highbyte/source/repos/dotnet-6502/src/apps/Highbyte.DotNet6502.App.SilkNetNative/bin/Debug/net9.0"
+ // }
+ }
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 357d7e327..7c3de8f57 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -116,6 +116,22 @@
"kind": "test",
"isDefault": true
}
+ },
+ {
+ "label": "build mcp server",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/src/utils/Highbyte.DotNet6502.Util.MCPServer/Highbyte.DotNet6502.Util.MCPServer.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile",
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
}
]
}
\ No newline at end of file
diff --git a/dotnet-6502.sln b/dotnet-6502.sln
index d60ed7718..eddfdf377 100644
--- a/dotnet-6502.sln
+++ b/dotnet-6502.sln
@@ -47,6 +47,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Highbyte.DotNet6502.Systems
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Highbyte.DotNet6502.AI", "src\libraries\Highbyte.DotNet6502.AI\Highbyte.DotNet6502.AI.csproj", "{B04E0DA2-F0EB-4E71-BFF9-3D3B17E30688}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Highbyte.DotNet6502.Util.MCPServer", "src\utils\Highbyte.DotNet6502.Util.MCPServer\Highbyte.DotNet6502.Util.MCPServer.csproj", "{1AB6BB50-42BF-44ED-8B5B-40266C9ED016}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -273,6 +277,18 @@ Global
{B04E0DA2-F0EB-4E71-BFF9-3D3B17E30688}.Release|x64.Build.0 = Release|Any CPU
{B04E0DA2-F0EB-4E71-BFF9-3D3B17E30688}.Release|x86.ActiveCfg = Release|Any CPU
{B04E0DA2-F0EB-4E71-BFF9-3D3B17E30688}.Release|x86.Build.0 = Release|Any CPU
+ {1AB6BB50-42BF-44ED-8B5B-40266C9ED016}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1AB6BB50-42BF-44ED-8B5B-40266C9ED016}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1AB6BB50-42BF-44ED-8B5B-40266C9ED016}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1AB6BB50-42BF-44ED-8B5B-40266C9ED016}.Debug|x64.Build.0 = Debug|Any CPU
+ {1AB6BB50-42BF-44ED-8B5B-40266C9ED016}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1AB6BB50-42BF-44ED-8B5B-40266C9ED016}.Debug|x86.Build.0 = Debug|Any CPU
+ {1AB6BB50-42BF-44ED-8B5B-40266C9ED016}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1AB6BB50-42BF-44ED-8B5B-40266C9ED016}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1AB6BB50-42BF-44ED-8B5B-40266C9ED016}.Release|x64.ActiveCfg = Release|Any CPU
+ {1AB6BB50-42BF-44ED-8B5B-40266C9ED016}.Release|x64.Build.0 = Release|Any CPU
+ {1AB6BB50-42BF-44ED-8B5B-40266C9ED016}.Release|x86.ActiveCfg = Release|Any CPU
+ {1AB6BB50-42BF-44ED-8B5B-40266C9ED016}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -296,6 +312,7 @@ Global
{4E078F4C-9C1B-4C17-A2AE-6BF2345DC791} = {B406AD5D-CB8D-45F2-A5A2-4C0AD82A410A}
{A52EFEC2-FD60-453E-AC55-703AD2F2A895} = {B406AD5D-CB8D-45F2-A5A2-4C0AD82A410A}
{B04E0DA2-F0EB-4E71-BFF9-3D3B17E30688} = {B406AD5D-CB8D-45F2-A5A2-4C0AD82A410A}
+ {1AB6BB50-42BF-44ED-8B5B-40266C9ED016} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0F55B6C2-E4B4-4F2C-9D2A-D63A17F3B5C4}
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/EmulatorConfig.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/EmulatorConfig.cs
index f7c6c87a3..2c57de4b6 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/EmulatorConfig.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/EmulatorConfig.cs
@@ -49,6 +49,8 @@ public class EmulatorConfig
///
public GenericComputerHostConfig GenericComputerHostConfig { get; set; }
+ public bool MCPServerEnabled { get; set; }
+
public EmulatorConfig()
{
UIFont = null;
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 fc43ba5f6..557e2fe08 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
@@ -29,12 +29,14 @@
+
-
-
-
+
+
+
+
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/MCP/C64SadConsoleTools.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/MCP/C64SadConsoleTools.cs
new file mode 100644
index 000000000..3abe840bf
--- /dev/null
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/MCP/C64SadConsoleTools.cs
@@ -0,0 +1,33 @@
+using System.ComponentModel;
+using Highbyte.DotNet6502.Systems;
+using Highbyte.DotNet6502.Util.MCPServer;
+using ModelContextProtocol.Protocol;
+using ModelContextProtocol.Server;
+
+namespace Highbyte.DotNet6502.App.SadConsole.MCP;
+
+[McpServerToolType]
+public static class C64SadConsoleTools
+{
+ [McpServerTool, Description("Get C64 emulator log messages")]
+ public static async Task GetLogMessages(IHostApp hostApp, int numberOfLogMessages)
+ {
+ try
+ {
+ List logs = null!;
+ await hostApp.ExternalControlInvokeOnUIThread(async () =>
+ {
+ C64ToolHelper.AssertC64EmulatorIsRunningOrPaused(hostApp);
+ var logStore = ((SadConsoleHostApp)hostApp).LogStore;
+ logs = logStore.GetLogMessages().Take(numberOfLogMessages).ToList();
+ });
+
+ return C64ToolHelper.BuildCallToolDataResult(logs);
+
+ }
+ catch (Exception ex)
+ {
+ return C64ToolHelper.BuildCallToolErrorResult(ex);
+ }
+ }
+}
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/Program.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/Program.cs
index 53d47eb35..6590487bb 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/Program.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/Program.cs
@@ -5,7 +5,10 @@
using Highbyte.DotNet6502.Systems;
using Highbyte.DotNet6502.Systems.Logging.InMem;
using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Hosting;
+using Highbyte.DotNet6502.Util.MCPServer;
// ----------
// Get config file
@@ -22,7 +25,6 @@
builder.AddUserSecrets();
}
-
IConfiguration Configuration = builder.Build();
// ----------
@@ -30,9 +32,9 @@
// ----------
DotNet6502InMemLogStore logStore = new() { WriteDebugMessage = true };
var logConfig = new DotNet6502InMemLoggerConfiguration(logStore);
+logConfig.LogLevel = LogLevel.Information; // LogLevel.Debug, LogLevel.Information
var loggerFactory = LoggerFactory.Create(builder =>
{
- logConfig.LogLevel = LogLevel.Information; // LogLevel.Debug, LogLevel.Information,
builder.AddInMem(logConfig);
builder.SetMinimumLevel(LogLevel.Trace);
});
@@ -54,10 +56,28 @@
var genericComputerSetup = new GenericComputerSetup(loggerFactory, Configuration);
systemList.AddSystem(genericComputerSetup);
+
// ----------
-// Start SadConsoleHostApp
+// Init SadConsoleHostApp
// ----------
emulatorConfig.Validate(systemList);
+var sadConsoleHostApp = new SadConsoleHostApp(systemList, loggerFactory, emulatorConfig, logStore, logConfig, Configuration);
-var silkNetHostApp = new SadConsoleHostApp(systemList, loggerFactory, emulatorConfig, logStore, logConfig, Configuration);
-silkNetHostApp.Run();
+// ----------
+// Start MCP server as a background host if enabled
+// ----------
+if (emulatorConfig.MCPServerEnabled)
+{
+ Task.Run(async () =>
+ {
+ var mcpBuilder = Host.CreateApplicationBuilder();
+ mcpBuilder.ConfigureDotNet6502McpServerTools(sadConsoleHostApp,
+ additionalToolsAssembly: typeof(Highbyte.DotNet6502.App.SadConsole.MCP.C64SadConsoleTools).Assembly);
+ await mcpBuilder.Build().RunAsync();
+ });
+}
+
+// ----------
+// Start SadConsoleHostApp
+// ----------
+sadConsoleHostApp.Run();
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleHostApp.cs b/src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleHostApp.cs
index 5d96f078c..2124feee3 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleHostApp.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleHostApp.cs
@@ -28,6 +28,7 @@ public class SadConsoleHostApp : HostApp _emulatorConfig;
private readonly DotNet6502InMemLogStore _logStore;
+ public DotNet6502InMemLogStore LogStore => _logStore;
private readonly DotNet6502InMemLoggerConfiguration _logConfig;
private readonly IConfiguration _configuration;
private readonly ILoggerFactory _loggerFactory;
@@ -75,7 +76,6 @@ protected virtual void OnMonitorStateChange(bool monitorEnabled)
private int _logsFrameCount = 0;
private DrawImage _logoDrawImage;
-
///
/// Constructor
///
@@ -610,4 +610,22 @@ private async Task HandleUIKeyboardInput()
}
}
}
+
+ public override void ExternalControlRefreshUI()
+ {
+ // Refresh UI controls
+ if (_menuConsole != null)
+ _menuConsole.IsDirty = true;
+ if (_systemMenuConsole != null)
+ _systemMenuConsole.IsDirty = true;
+ if (_sadConsoleEmulatorConsole != null)
+ _sadConsoleEmulatorConsole.IsDirty = true;
+
+ //if (_monitorConsole != null)
+ // _monitorConsole.IsDirty = true;
+ //if (_monitorStatusConsole != null)
+ // _monitorStatusConsole.IsDirty = true;
+ //if (_infoConsole != null)
+ // _infoConsole.IsDirty = true;
+ }
}
diff --git a/src/apps/Highbyte.DotNet6502.App.SadConsole/appsettings.json b/src/apps/Highbyte.DotNet6502.App.SadConsole/appsettings.json
index ef2b47fad..e2a743223 100644
--- a/src/apps/Highbyte.DotNet6502.App.SadConsole/appsettings.json
+++ b/src/apps/Highbyte.DotNet6502.App.SadConsole/appsettings.json
@@ -4,7 +4,9 @@
"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
+ "DefaultAudioVolumePercent": 20,
+
+ "MCPServerEnabled": true
},
"Highbyte.DotNet6502.C64.SadConsole": {
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/EmulatorConfig.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/EmulatorConfig.cs
index a28f25362..c2692111e 100644
--- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/EmulatorConfig.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/EmulatorConfig.cs
@@ -13,6 +13,7 @@ public class EmulatorConfig
public float DefaultDrawScale { get; set; }
public float CurrentDrawScale { get; set; }
public MonitorConfig Monitor { get; set; }
+ public bool MCPServerEnabled { get; set; }
public EmulatorConfig()
{
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 b2ff293e4..411ae28e2 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
@@ -24,11 +24,12 @@
+
-
-
+
+
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/MCP/C64SadConsoleTools.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/MCP/C64SadConsoleTools.cs
new file mode 100644
index 000000000..c37ec797d
--- /dev/null
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/MCP/C64SadConsoleTools.cs
@@ -0,0 +1,33 @@
+using System.ComponentModel;
+using Highbyte.DotNet6502.Systems;
+using Highbyte.DotNet6502.Util.MCPServer;
+using ModelContextProtocol.Protocol;
+using ModelContextProtocol.Server;
+
+namespace Highbyte.DotNet6502.App.SilkNetNative.MCP;
+
+[McpServerToolType]
+public static class C64SilkNetNativeTools
+{
+ [McpServerTool, Description("Get C64 emulator log messages")]
+ public static async Task GetLogMessages(IHostApp hostApp, int numberOfLogMessages)
+ {
+ try
+ {
+ List logs = null!;
+ await hostApp.ExternalControlInvokeOnUIThread(async () =>
+ {
+ C64ToolHelper.AssertC64EmulatorIsRunningOrPaused(hostApp);
+ var logStore = ((SilkNetHostApp)hostApp).LogStore;
+ logs = logStore.GetLogMessages().Take(numberOfLogMessages).ToList();
+ });
+
+ return C64ToolHelper.BuildCallToolDataResult(logs);
+
+ }
+ catch (Exception ex)
+ {
+ return C64ToolHelper.BuildCallToolErrorResult(ex);
+ }
+ }
+}
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Program.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Program.cs
index 7741930d1..dfe1824c3 100644
--- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Program.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/Program.cs
@@ -4,7 +4,9 @@
using Highbyte.DotNet6502.Impl.SilkNet;
using Highbyte.DotNet6502.Systems;
using Highbyte.DotNet6502.Systems.Logging.InMem;
+using Highbyte.DotNet6502.Util.MCPServer;
using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
// Fix for starting in debug mode from VS Code. By default the OS current directory is set to the project folder, not the folder containing the built .exe file...
@@ -72,6 +74,27 @@
var window = Window.Create(windowOptions);
+// ----------
+// Init SilkNetHostApp
+// ----------
var silkNetHostApp = new SilkNetHostApp(systemList, loggerFactory, emulatorConfig, window, logStore, logConfig);
silkNetHostApp.SelectSystem(emulatorConfig.DefaultEmulator).Wait();
+
+// ----------
+// Start MCP server as a background host if enabled
+// ----------
+if (emulatorConfig.MCPServerEnabled)
+{
+ Task.Run(async () =>
+ {
+ var mcpBuilder = Host.CreateApplicationBuilder();
+ mcpBuilder.ConfigureDotNet6502McpServerTools(silkNetHostApp,
+ typeof(Highbyte.DotNet6502.App.SilkNetNative.MCP.C64SilkNetNativeTools).Assembly);
+ await mcpBuilder.Build().RunAsync();
+ });
+}
+
+// ----------
+// Start SilkNetHostApp
+// ----------
silkNetHostApp.Run();
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetHostApp.cs b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetHostApp.cs
index 057909f9b..73d1b926a 100644
--- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetHostApp.cs
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/SilkNetHostApp.cs
@@ -22,6 +22,7 @@ public class SilkNetHostApp : HostApp _emulatorConfig;
private readonly DotNet6502InMemLogStore _logStore;
+ public DotNet6502InMemLogStore LogStore => _logStore;
private readonly DotNet6502InMemLoggerConfiguration _logConfig;
private readonly bool _defaultAudioEnabled;
private float _defaultAudioVolumePercent;
diff --git a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/appsettings.json b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/appsettings.json
index 3205d8e89..b9fd45127 100644
--- a/src/apps/Highbyte.DotNet6502.App.SilkNetNative/appsettings.json
+++ b/src/apps/Highbyte.DotNet6502.App.SilkNetNative/appsettings.json
@@ -6,7 +6,9 @@
"Monitor": {
"MaxLineLength": 100
//"DefaultDirectory": "../../../../../../samples/Assembler/C64/Build"
- }
+ },
+
+ "MCPServerEnabled": true
},
"Highbyte.DotNet6502.C64.SilkNetNative": {
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 fd7f29277..99a4af9e2 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
@@ -32,12 +32,12 @@
-
-
-
+
+
+
-
+
diff --git a/src/libraries/Highbyte.DotNet6502.AI/Highbyte.DotNet6502.AI.csproj b/src/libraries/Highbyte.DotNet6502.AI/Highbyte.DotNet6502.AI.csproj
index 8c9954edd..07a5f7e03 100644
--- a/src/libraries/Highbyte.DotNet6502.AI/Highbyte.DotNet6502.AI.csproj
+++ b/src/libraries/Highbyte.DotNet6502.AI/Highbyte.DotNet6502.AI.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Highbyte.DotNet6502.Impl.AspNet.csproj b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Highbyte.DotNet6502.Impl.AspNet.csproj
index 8961be646..244468053 100644
--- a/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Highbyte.DotNet6502.Impl.AspNet.csproj
+++ b/src/libraries/Highbyte.DotNet6502.Impl.AspNet/Highbyte.DotNet6502.Impl.AspNet.csproj
@@ -29,7 +29,7 @@
-
+
diff --git a/src/libraries/Highbyte.DotNet6502.Systems/HostApp.cs b/src/libraries/Highbyte.DotNet6502.Systems/HostApp.cs
index ec6562ea4..6b0246da9 100644
--- a/src/libraries/Highbyte.DotNet6502.Systems/HostApp.cs
+++ b/src/libraries/Highbyte.DotNet6502.Systems/HostApp.cs
@@ -1,6 +1,7 @@
-using Microsoft.Extensions.Logging;
+using System.Collections.Concurrent;
using Highbyte.DotNet6502.Systems.Instrumentation;
using Highbyte.DotNet6502.Systems.Instrumentation.Stats;
+using Microsoft.Extensions.Logging;
namespace Highbyte.DotNet6502.Systems;
@@ -15,7 +16,7 @@ public enum EmulatorState { Uninitialized, Running, Paused }
///
///
///
-public class HostApp
+public class HostApp : IHostApp
where TRenderContext : IRenderContext
where TInputHandlerContext : IInputHandlerContext
where TAudioHandlerContext : IAudioHandlerContext
@@ -52,13 +53,13 @@ public IHostSystemConfig CurrentHostSystemConfig
throw new DotNet6502Exception("Internal error. No system selected yet. Call SelectSystem() first.");
return _currentHostSystemConfig;
}
- private set
+ set
{
_currentHostSystemConfig = value;
}
}
- protected List GetHostSystemConfigs()
+ public List GetHostSystemConfigs()
{
var list = new List();
foreach (var system in AvailableSystemNames)
@@ -77,12 +78,18 @@ protected List GetHostSystemConfigs()
private ElapsedMillisecondsTimedStatSystem? _systemTime;
private ElapsedMillisecondsTimedStatSystem? _renderTime;
private ElapsedMillisecondsTimedStatSystem? _inputTime;
+
//private ElapsedMillisecondsTimedStatSystem _audioTime;
private readonly Instrumentations _instrumentations = new();
private readonly PerSecondTimedStat _updateFps;
private readonly PerSecondTimedStat _renderFps;
+ private bool _externalControlEnabled = false;
+ public bool ExternalControlEnabled => _externalControlEnabled;
+ private Func<(bool shouldRun, bool shouldReceiveInput)>? _externalOnBeforeRunEmulatorOneFrame;
+ private Action? _externalOnAfterRunEmulatorOneFrame;
+
public HostApp(
string hostName,
SystemList systemList,
@@ -260,15 +267,29 @@ public virtual void OnBeforeRunEmulatorOneFrame(out bool shouldRun, out bool sho
shouldRun = true;
shouldReceiveInput = true;
}
- public void RunEmulatorOneFrame()
+ public ExecEvaluatorTriggerResult RunEmulatorOneFrame()
{
+ // Process any UI actions that have been queued up from other threads
+ ExternalControlProcessUIActions();
+
// Safety check to avoid running emulator if it's not in a running state.
if (EmulatorState != EmulatorState.Running)
- return;
+ return ExecEvaluatorTriggerResult.NotTriggered;
+
+ bool shouldRun = false;
+ bool shouldReceiveInput = false;
+ if (_externalControlEnabled)
+ {
+ if (_externalOnBeforeRunEmulatorOneFrame != null)
+ (shouldRun, shouldReceiveInput) = _externalOnBeforeRunEmulatorOneFrame();
+ }
+ else
+ {
+ OnBeforeRunEmulatorOneFrame(out shouldRun, out shouldReceiveInput);
+ }
- OnBeforeRunEmulatorOneFrame(out bool shouldRun, out bool shouldReceiveInput);
if (!shouldRun)
- return;
+ return ExecEvaluatorTriggerResult.NotTriggered;
_updateFps.Update();
@@ -281,10 +302,20 @@ public void RunEmulatorOneFrame()
_systemTime!.Start();
var execEvaluatorTriggerResult = _systemRunner!.RunEmulatorOneFrame();
- OnAfterRunEmulatorOneFrame(execEvaluatorTriggerResult);
+
+ if (_externalControlEnabled)
+ {
+ _externalOnAfterRunEmulatorOneFrame?.Invoke(execEvaluatorTriggerResult);
+ }
+ else
+ {
+ OnAfterRunEmulatorOneFrame(execEvaluatorTriggerResult);
+ }
_systemTime!.Stop();
+ return execEvaluatorTriggerResult;
}
+
public virtual void OnAfterRunEmulatorOneFrame(ExecEvaluatorTriggerResult execEvaluatorTriggerResult) { }
public virtual void OnBeforeDrawFrame(bool emulatorWillBeRendered) { }
@@ -374,4 +405,80 @@ private void InitInstrumentation(ISystem system)
.Union(_systemRunner.InputHandler.Instrumentations.Stats.Select(x => (Name: $"{_hostName}-{InputTimeStatName}-{x.Name}", x.Stat)))
.ToList();
}
+
+
+ private readonly ConcurrentQueue _externalControlUIActions = new();
+ public virtual bool ExternalControlDirectInvoke { get; } = false;
+ public Task ExternalControlInvokeOnUIThread(Func action)
+ {
+ var tcs = new TaskCompletionSource