Skip to content

Commit

Permalink
Updates docs, fixes code
Browse files Browse the repository at this point in the history
  • Loading branch information
MihaMarkic committed Apr 5, 2021
1 parent 1958ef6 commit 2b8aa89
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 27 deletions.
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
# VICE Binary Monitor Bridge for .NET

Implements a bridge for communication with VICE binary monitor in .NET 5.
Implements a bridge for communication with [VICE](https://vice-emu.sourceforge.io/) [binary monitor](https://vice-emu.sourceforge.io/vice_13.html#SEC281) in .NET 5.

Tested(limited) and build against VICE 3.5.

## Quick start

Start VICE with *-binarymonitor* argument so it listens to default port 6502.

First step is to register types with .NET's standard `Microsoft.Extensions.DependencyInjection.IServiceCollection` by calling extension method `Righthand.ViceMonitor.Bridge.AddEngineServices`, like:

```csharp
var collection = new ServiceCollection();
collection.AddEngineServices();
```

After IoC is setup, `IViceBridge` has to be resolved through IoC. Optionally `ConnectionChanged` even handler can be used to track connection status (also available through `IViceBridge.IsConnected` property) and `ViceResponse` event handler can be used to receive unbound responses from VICE.

```csharp
bridge.ConnectedChanged += Bridge_ConnectedChanged;
bridge.Start();
```

After `IViceBridge.IsConnected` property becomes `true`, command can be sent to VICE and responses will flow back. Here is a ping command:

```csharp
var ping = bridge.EnqueueCommand(new PingCommand());
var response = await ping.Response;
```

## Playground sample

Playground sample is a console application that is used for testing and for sample purposes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
using Righthand.ViceMonitor.Bridge.Services.Abstract;
using Spectre.Console;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading;
Expand Down Expand Up @@ -58,11 +60,12 @@ public async Task RunAsync(CancellationToken ct)

async Task ShowMenuAsync(CancellationToken ct)
{
var options = ImmutableDictionary<string, string>.Empty
.Add("dg", "Display get")
.Add("vi", "VICE info")
.Add("cl", "Checkpoint list")
.Add("cs", "Checkpoint set");
var options = ImmutableArray<KeyValuePair<string, string>>.Empty
.Add(new KeyValuePair<string, string>("dg", "Display get"))
//.Add("vi", "VICE info")
.Add(new KeyValuePair<string, string>("cl", "Checkpoint list"))
.Add(new KeyValuePair<string, string>("cs", "Checkpoint set"))
.Add(new KeyValuePair<string, string>("p", "Ping"));
bool quit = false;
while (!quit)
{
Expand All @@ -77,21 +80,31 @@ async Task ShowMenuAsync(CancellationToken ct)
case "dg":
await GetDisplayAsync(ct);
break;
case "vi":
await ViceInfoAsync(ct);
break;
//case "vi":
// await ViceInfoAsync(ct);
// break;
case "cl":
await CheckpointListAsync(ct);
break;
case "cs":
await CheckpointSetAsync(ct);
break;
case "p":
await PingAsync(ct);
break;
case "q":
quit = true;
break;
}
}
}
async Task PingAsync(CancellationToken ct)
{
var sw = Stopwatch.StartNew();
var ping = bridge.EnqueueCommand(new PingCommand());
var response = await ping.Response;
AnsiConsole.MarkupLine($"Ping response: {response.ErrorCode} in {sw.ElapsedMilliseconds:#,##0}ms");
}
async Task CheckpointSetAsync(CancellationToken ct)
{
var setCommand = bridge.EnqueueCommand(new CheckpointSetCommand(0x1000, 0x2000, StopWhenHit: true, Enabled: true,
Expand All @@ -113,14 +126,15 @@ async Task CheckpointListAsync(CancellationToken ct)
index++;
}
}
async Task ViceInfoAsync(CancellationToken ct)
{
var command = new InfoCommand();
bridge.EnqueueCommand(command);
var response = await command.Response;
AnsiConsole.MarkupLine($"VICE version RC number is [bold]{response.VersionRCNumber}[/]");
// not yet implemented in VICE stable
//async Task ViceInfoAsync(CancellationToken ct)
//{
// var command = new InfoCommand();
// bridge.EnqueueCommand(command);
// var response = await command.Response;
// AnsiConsole.MarkupLine($"VICE version RC number is [bold]{response.VersionRCNumber}[/]");

}
//}
async Task GetDisplayAsync(CancellationToken ct)
{
var command = new DisplayGetCommand(UseVic: true, ImageFormat.Rgb);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

namespace Righthand.ViceMonitor.Bridge.Commands
{
/// <summary>
/// Retrieves VICE version.
/// </summary>
/// <remarks>This command might not yet be implemented in VICE stable version 3.5</remarks>
public record InfoCommand(): ParameterlessCommand<InfoResponse>(CommandType.Info);
///// <summary>
///// Retrieves VICE version.
///// </summary>
///// <remarks>This command might not yet be implemented in VICE stable version 3.5</remarks>
//public record InfoCommand(): ParameterlessCommand<InfoResponse>(CommandType.Info);
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ void IViceCommand.SetResult(ViceResponse response)
BitConverter.TryWriteBytes(bufferSpan.Slice(2,4), commandLength);
BitConverter.TryWriteBytes(bufferSpan.Slice(6, 4), requestId);
bufferSpan[10] = (byte)CommandType;
WriteContent(bufferSpan[11..]);
if (contentLength > 0)
{
WriteContent(bufferSpan[11..]);
}
return (buffer, totalLength);
}
/// <inheritdoc cref="IViceCommand.ApiVersion"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,21 @@ async Task LoopAsync(Socket socket, CancellationToken ct)
await ReadByteArrayAsync(socket, headerBuffer, ct).ConfigureAwait(false);
uint responseBodyLength = responseBuilder.GetResponseBodyLength(headerBuffer.Data.AsSpan());
logger.LogDebug($"Response body length is {responseBodyLength:#,##0}B");
using (var bodyBuffer = byteArrayPool.GetBuffer(responseBodyLength))
(ViceResponse Response, uint RequestId) result;
if (responseBodyLength > 0)
{
await ReadByteArrayAsync(socket, bodyBuffer, ct);
var result = responseBuilder.Build(headerBuffer.Data.AsSpan(), bodyBuffer.Data.AsSpan());
logger.LogDebug($"Response is {result.Response?.GetType().Name} with RequestId {result.RequestId}");
return result;
using (var bodyBuffer = byteArrayPool.GetBuffer(responseBodyLength))
{
await ReadByteArrayAsync(socket, bodyBuffer, ct);
result = responseBuilder.Build(headerBuffer.Data.AsSpan(), bodyBuffer.Data.AsSpan());
}
}
else
{
result = responseBuilder.Build(headerBuffer.Data.AsSpan(), Array.Empty<byte>());
}
logger.LogDebug($"Response is {result.Response?.GetType().Name} with RequestId {result.RequestId}");
return result;
}
}
async Task SendCommandAsync(Socket socket, uint requestId, IViceCommand command, CancellationToken ct)
Expand Down

0 comments on commit 2b8aa89

Please sign in to comment.