Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions .github/workflows/release-desktop-apps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ jobs:
chmod u+x src/apps/Highbyte.DotNet6502.App.Headless/publish.sh
src/apps/Highbyte.DotNet6502.App.Headless/publish.sh ${{ matrix.runtime }}

- name: Publish RemoteClient App
run: |
chmod u+x src/apps/Highbyte.DotNet6502.App.RemoteClient/publish.sh
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
src/apps/Highbyte.DotNet6502.App.RemoteClient/publish.sh ${{ matrix.runtime }}

- name: Zip Apps
run: |
cd src/apps/Highbyte.DotNet6502.App.SilkNetNative/publish/${{ matrix.runtime }}
Expand All @@ -64,13 +69,17 @@ jobs:
cd $GITHUB_WORKSPACE/src/apps/Highbyte.DotNet6502.App.Headless/publish/${{ matrix.runtime }}
zip -r $GITHUB_WORKSPACE/DotNet6502-Headless-${{ matrix.runtime }}.zip .

cd $GITHUB_WORKSPACE/src/apps/Highbyte.DotNet6502.App.RemoteClient/publish/${{ matrix.runtime }}
zip -r $GITHUB_WORKSPACE/DotNet6502-RemoteClient-${{ matrix.runtime }}.zip .

- name: Generate SHA256 checksums
run: |
cd $GITHUB_WORKSPACE
sha256sum DotNet6502-SilkNetNative-${{ matrix.runtime }}.zip >> checksums-${{ matrix.runtime }}.sha256
sha256sum DotNet6502-Avalonia-${{ matrix.runtime }}.zip >> checksums-${{ matrix.runtime }}.sha256
sha256sum DotNet6502-SadConsole-${{ matrix.runtime }}.zip >> checksums-${{ matrix.runtime }}.sha256
sha256sum DotNet6502-Headless-${{ matrix.runtime }}.zip >> checksums-${{ matrix.runtime }}.sha256
sha256sum DotNet6502-RemoteClient-${{ matrix.runtime }}.zip >> checksums-${{ matrix.runtime }}.sha256

- name: Upload Release Assets
if: github.event_name == 'release'
Expand All @@ -82,6 +91,7 @@ jobs:
DotNet6502-Avalonia-${{ matrix.runtime }}.zip \
DotNet6502-SadConsole-${{ matrix.runtime }}.zip \
DotNet6502-Headless-${{ matrix.runtime }}.zip \
DotNet6502-RemoteClient-${{ matrix.runtime }}.zip \
checksums-${{ matrix.runtime }}.sha256

- name: Upload Workflow Artifacts (non-release)
Expand All @@ -94,6 +104,7 @@ jobs:
DotNet6502-Avalonia-${{ matrix.runtime }}.zip
DotNet6502-SadConsole-${{ matrix.runtime }}.zip
DotNet6502-Headless-${{ matrix.runtime }}.zip
DotNet6502-RemoteClient-${{ matrix.runtime }}.zip
checksums-${{ matrix.runtime }}.sha256

update-package-managers:
Expand Down Expand Up @@ -138,6 +149,11 @@ jobs:
echo "headless_linux_arm64=$(extract_hash Headless linux-arm64)" >> "$GITHUB_OUTPUT"
echo "headless_win_x64=$(extract_hash Headless win-x64)" >> "$GITHUB_OUTPUT"
echo "headless_win_arm64=$(extract_hash Headless win-arm64)" >> "$GITHUB_OUTPUT"
echo "remote_osx_arm64=$(extract_hash RemoteClient osx-arm64)" >> "$GITHUB_OUTPUT"
echo "remote_linux_x64=$(extract_hash RemoteClient linux-x64)" >> "$GITHUB_OUTPUT"
echo "remote_linux_arm64=$(extract_hash RemoteClient linux-arm64)" >> "$GITHUB_OUTPUT"
echo "remote_win_x64=$(extract_hash RemoteClient win-x64)" >> "$GITHUB_OUTPUT"
echo "remote_win_arm64=$(extract_hash RemoteClient win-arm64)" >> "$GITHUB_OUTPUT"

- name: Extract macOS app bundle name from zip
id: app_bundle
Expand All @@ -156,6 +172,9 @@ jobs:
HEADLESS_OSX_ARM64_HASH: ${{ steps.hashes.outputs.headless_osx_arm64 }}
HEADLESS_LINUX_X64_HASH: ${{ steps.hashes.outputs.headless_linux_x64 }}
HEADLESS_LINUX_ARM64_HASH: ${{ steps.hashes.outputs.headless_linux_arm64 }}
REMOTE_OSX_ARM64_HASH: ${{ steps.hashes.outputs.remote_osx_arm64 }}
REMOTE_LINUX_X64_HASH: ${{ steps.hashes.outputs.remote_linux_x64 }}
REMOTE_LINUX_ARM64_HASH: ${{ steps.hashes.outputs.remote_linux_arm64 }}
run: |
git clone "https://x-access-token:${GH_TOKEN}@github.com/highbyte/homebrew-dotnet-6502.git"
cd homebrew-dotnet-6502
Expand Down Expand Up @@ -198,6 +217,12 @@ jobs:
os.environ["HEADLESS_LINUX_X64_HASH"],
os.environ["HEADLESS_LINUX_ARM64_HASH"],
])
# dotnet-6502-remote formula: macOS arm64, then Linux x64, then Linux arm64
update_formula("Formula/dotnet-6502-remote.rb", version, [
os.environ["REMOTE_OSX_ARM64_HASH"],
os.environ["REMOTE_LINUX_X64_HASH"],
os.environ["REMOTE_LINUX_ARM64_HASH"],
])
PYEOF

git config user.name "github-actions[bot]"
Expand All @@ -214,6 +239,8 @@ jobs:
WIN_ARM64_HASH: ${{ steps.hashes.outputs.win_arm64 }}
HEADLESS_WIN_X64_HASH: ${{ steps.hashes.outputs.headless_win_x64 }}
HEADLESS_WIN_ARM64_HASH: ${{ steps.hashes.outputs.headless_win_arm64 }}
REMOTE_WIN_X64_HASH: ${{ steps.hashes.outputs.remote_win_x64 }}
REMOTE_WIN_ARM64_HASH: ${{ steps.hashes.outputs.remote_win_arm64 }}
run: |
git clone "https://x-access-token:${GH_TOKEN}@github.com/highbyte/scoop-dotnet-6502.git"
cd scoop-dotnet-6502
Expand All @@ -236,8 +263,9 @@ jobs:
json.dump(manifest, f, indent=4)
f.write("\n")

update_manifest("bucket/dotnet-6502.json", "Avalonia", os.environ["WIN_X64_HASH"], os.environ["WIN_ARM64_HASH"])
update_manifest("bucket/dotnet-6502-headless.json", "Headless", os.environ["HEADLESS_WIN_X64_HASH"], os.environ["HEADLESS_WIN_ARM64_HASH"])
update_manifest("bucket/dotnet-6502.json", "Avalonia", os.environ["WIN_X64_HASH"], os.environ["WIN_ARM64_HASH"])
update_manifest("bucket/dotnet-6502-headless.json", "Headless", os.environ["HEADLESS_WIN_X64_HASH"], os.environ["HEADLESS_WIN_ARM64_HASH"])
update_manifest("bucket/dotnet-6502-remote.json", "RemoteClient", os.environ["REMOTE_WIN_X64_HASH"], os.environ["REMOTE_WIN_ARM64_HASH"])
PYEOF

git config user.name "github-actions[bot]"
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,6 @@ src/apps/Highbyte.DotNet6502.App.SilkNetNative/appsettings.Development.json
src/apps/Avalonia/Highbyte.DotNet6502.App.Avalonia.Browser/wwwroot/scripts/

src/apps/Avalonia/Highbyte.DotNet6502.App.Avalonia.Desktop/appsettings.Development.json

# memsearch AI memory (kept in separate private repo)
.memsearch/
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ The Avalonia desktop and browser apps support [Lua scripting](doc/SCRIPTING.md)
| ----------------------------------------------- | ------------------------------------------------- |
| <img src="doc/Screenshots/AvaloniaDesktop_C64_Scripting.png" title="VLua scripts in Desktop app"/> | <img src="doc/Screenshots/AvaloniaBrowser_C64_Scripting.png" title="Lua scripts in Browser app"/> |

## Remote control

The Avalonia desktop app and the headless app expose a **TCP remote control endpoint** that lets external processes inspect and drive a running emulator instance in real time. A persistent, newline-delimited JSON protocol is used — one client at a time. Useful for automation, AI agent integration, and tooling that needs ad-hoc access without embedding a Lua script inside the emulator process.

The **Debug & Remoting tab** shows the server status. While a client is connected a blue banner appears at the bottom of the window.

See [TCP Remote Control documentation](doc/REMOTE_CONTROL.md) for the full protocol reference and command list.

A ready-made CLI client, `dotnet-6502-remote`, is distributed separately — see [Install Remote Client](doc/INSTALL_REMOTE_CLIENT.md) for download and installation instructions.

| [Remote control in desktop app](doc/REMOTE_CONTROL.md)|
| ----------------------------------------------- |
| <img src="doc/Screenshots/AvaloniaDesktop_RemoteControl.png" title="Avalonia Desktop app with active TCP remote control client" width="50%"/> |

## Other features

| [Run 6502 machine code in your own .NET apps](doc/CPU_LIBRARY.md) | [Machine code monitor](doc/MONITOR.md) | [C64 Basic AI code completion](doc/SYSTEMS_C64_AI_CODE_COMPLETION.md) |
Expand Down
52 changes: 43 additions & 9 deletions doc/APPS_AVALONIA.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,7 @@ The desktop app can be launched from the command line with arguments to control

When a Lua script is supplied the script owns all emulator setup and lifecycle — system selection, start, load, and quit.

| Argument | Description |
|---|---|
| `--script <path>` | Load and run a Lua script (can be specified multiple times) |
| `--scriptDir <path>` | Override the script directory from `appsettings.json` |
| `--console-log` / `-c` | Enable console logging |
| `--log-level <level>` / `-l <level>` | Set console log level (Trace/Debug/Information/Warning/Error) |
| `--enableExternalDebug` | Enable VS Code debug adapter (DAP) over TCP |
| `--debug-port <port>` | TCP port for the debug adapter (default: 6502) |
| `--debug-wait` | Wait for a debug client to connect before starting |
See [Parameter reference](#parameter-reference) for [`--script` and `--scriptDir`](#scripting-parameters), [logging](#logging), [debug adapter](#debug-adapter), and [remote control](#remote-control) options.

> [!IMPORTANT]
> `--script` and `--scriptDir` are **mutually exclusive** with `--system`, `--systemVariant`, `--start`, `--waitForSystemReady`, `--loadPrg`, and `--runLoadedProgram`. The script is responsible for all emulator setup and lifecycle. Combining them is an error.
Expand All @@ -78,6 +70,19 @@ When a Lua script is supplied the script owns all emulator setup and lifecycle

Used when driving the emulator from the command line without a script — primarily by the VS Code debugger extension.

See [Parameter reference](#parameter-reference) for [`--system`, `--start`, and related options](#automated-startup-parameters), [logging](#logging), [debug adapter](#debug-adapter), and [remote control](#remote-control) options.

### Parameter reference

#### Scripting parameters

| Argument | Description |
|---|---|
| `--script <path>` | Load and run a Lua script (can be specified multiple times) |
| `--scriptDir <path>` | Override the script directory from `appsettings.json` |

#### Automated startup parameters

| Argument | Description |
|---|---|
| `--system <name>` | Select a system (e.g. `C64`, `Generic`) |
Expand All @@ -86,12 +91,32 @@ Used when driving the emulator from the command line without a script — primar
| `--waitForSystemReady` | Wait until the system reports ready before continuing. Requires `--start`. |
| `--loadPrg <path>` | Load a `.prg` file into memory. Requires `--start`. |
| `--runLoadedProgram` | Run the loaded `.prg` file after loading. Requires `--start` and `--loadPrg`. |

#### Logging

| Argument | Description |
|---|---|
| `--console-log` / `-c` | Enable console logging |
| `--log-level <level>` / `-l <level>` | Set console log level (Trace/Debug/Information/Warning/Error) |

#### Debug adapter

| Argument | Description |
|---|---|
| `--enableExternalDebug` | Enable VS Code debug adapter (DAP) over TCP |
| `--debug-port <port>` | TCP port for the debug adapter (default: 6502) |
| `--debug-bind-address <ip>` | IP address to bind the debug adapter server to (default: `127.0.0.1`) |
| `--debug-wait` | Wait for a debug client to connect before starting |

#### Remote control

| Argument | Description |
|---|---|
| `--remote-port <port>` | Start the TCP remote control server on this port |
| `--remote-bind-address <ip>` | IP address to bind the remote control server to (default: `127.0.0.1`) |

See [REMOTE_CONTROL.md](REMOTE_CONTROL.md) for the remote control protocol and usage.

### Examples

```
Expand All @@ -103,6 +128,15 @@ Used when driving the emulator from the command line without a script — primar

# Start with debug adapter for VS Code, waiting for client
./Highbyte.DotNet6502.App.Avalonia.Desktop --system C64 --start --enableExternalDebug --debug-port 6502 --debug-wait

# Start with debug adapter bound to all interfaces (use only on trusted networks)
./Highbyte.DotNet6502.App.Avalonia.Desktop --system C64 --start --enableExternalDebug --debug-port 6502 --debug-bind-address 0.0.0.0

# Start with remote control server on port 6510 (loopback only)
./Highbyte.DotNet6502.App.Avalonia.Desktop --system C64 --start --remote-port 6510

# Start with remote control server accessible from the network (trusted networks only)
./Highbyte.DotNet6502.App.Avalonia.Desktop --system C64 --start --remote-port 6510 --remote-bind-address 0.0.0.0
```

## UI
Expand Down
6 changes: 3 additions & 3 deletions doc/APPS_AVALONIA_AUTOMATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ A non-exhaustive list of the most useful AutomationIds, grouped by view. All of
- **Emulator control**: `StartButton`, `PauseButton`, `ResetButton`, `StopButton`, `MonitorButton`, `StatsButton`
- **Display/audio**: `ScaleSlider`, `AudioCheckBox`, `AudioVolumeSlider`, `OptionsButton`
- **Status**: `EmulatorStateText`
- **Bottom tab control**: `InformationTabControl` with tabs `InformationTab`, `ConfigStatusTab`, `LogTab`, `ScriptsTab`, `GeneralInfoTab`, `DebugTab`
- **Bottom tab control**: `InformationTabControl` with tabs `InformationTab`, `ConfigStatusTab`, `LogTab`, `ScriptsTab`, `GeneralInfoTab`, `DebugAndRemotingTab`
- **Log tab**: `ClearLogButton`
- **Scripts tab**: `ScriptsBannerRefreshButton`, `ScriptFolderLink`, `AddScriptButton`, `LoadExamplesButton`, `ScriptsRefreshButton`; sort headers `SortByFileNameButton`, `SortByStatusButton`, `SortByYieldButton`, `SortByHooksButton`
- **Script rows (dynamic)**: `ScriptRow.ToggleEnabled.<FileName>`, `ScriptRow.Reload.<FileName>`, `ScriptRow.Edit.<FileName>`, `ScriptRow.Delete.<FileName>`
Expand Down Expand Up @@ -84,7 +84,7 @@ A non-exhaustive list of the most useful AutomationIds, grouped by view. All of

## EmulatorConfigUserControl (general options)

`DefaultEmulatorComboBox`, `DefaultScaleSlider`, `ShowErrorDialogCheckBox`, `ShowDebugTabCheckBox`, `AudioProfileComboBox`, `StopOnBrkCheckBox`, `StopOnUnknownInstructionCheckBox`, `LuaScriptDirectoryTextBox`, `LuaStorePrefixTextBox`, `CancelButton`, `OkButton`.
`DefaultEmulatorComboBox`, `DefaultScaleSlider`, `ShowErrorDialogCheckBox`, `ShowDebugToolsCheckBox`, `AudioProfileComboBox`, `StopOnBrkCheckBox`, `StopOnUnknownInstructionCheckBox`, `LuaScriptDirectoryTextBox`, `LuaStorePrefixTextBox`, `CancelButton`, `OkButton`.

## MonitorDialog / MonitorUserControl

Expand Down Expand Up @@ -192,7 +192,7 @@ peekaboo menu click --app "DotNet 6502 Emulator" --path "DotNet 6502 Emulator >

2. **Collapsed/conditional content** only appears in the AX tree when its container is rendered. Examples:
- `C64MenuView` section contents (`DiskSectionContent`, `LoadSaveSectionContent`, `ConfigSectionContent`) — only visible when the section header is expanded.
- `Debug` tab contents — hidden until `ShowDebugTab` is enabled in `EmulatorConfig`.
- Some tools in `Debug & Remoting` tab contents — hidden until `ShowDebugTools` is enabled in `EmulatorConfig`.
- Dialog controls (`C64ConfigDialog`, `MonitorDialog`, `ScriptEditorDialog`) — only exist while the dialog is open.

To automate these, expand/open the container first, then re-query the AX tree.
Expand Down
Loading
Loading