Files
VRIO/src/VPlasma.Core/Device/PlasmaSelfTest.cs
T
CydandClaude Fable 5 a1b7dae3da vPLASMA: companion app emulating the cockpit plasma display
The cockpit's second serial device joins vRIO: the 128x32 dot-matrix
plasma on COM2 (9600 8N1) that the game draws mission text and status
graphics on. VPlasma.App opens the device end of a COM port, decodes
the display's command stream, and renders the matrix in plasma orange.

The command set is recovered from two Tesla 4.10 artifacts: the game's
driver (L4PLASMA.CPP — ESC P packed-bitmap row writes, ESC G 0 cursor
hide at boot) and the factory test tool PLASMA.EXE, whose data segment
pairs each command literal with its printf description (BS/HT/LF/VT/CR
motion, ESC @ clear, ESC L home, ESC G cursor, ESC K fonts, ESC H
attributes: intensity/underline/reverse/flash). Text renders through
the classic public-domain 5x7 set standing in for the lost ROM glyphs;
fonts 0-3 give 21x4 cells, fonts 4-7 the doubled 10x2. A Self test
cycles banner/charset/graphics pages through the same parser the wire
feeds, and the wire log shows every decoded command.

Verified end-to-end over the second com0com pair (host writing COM2,
vPLASMA listening on COM12). The verify skill gains the cross-process
combo-box lesson: string-carrying CB_* messages hang across processes,
so select ports by locally computed index via CB_SETCURSEL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:22:00 -05:00

102 lines
3.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using VPlasma.Core.Protocol;
namespace VPlasma.Core.Device;
/// <summary>
/// Canned wire streams for the UI's <i>Self test</i> button: each page is a
/// byte sequence exactly as a host would send it over COM2, fed through the
/// same parser as live traffic — so the button doubles as an end-to-end
/// exercise of the command set without a host attached.
/// </summary>
public static class PlasmaSelfTest
{
public const int PageCount = 3;
/// <summary>The wire bytes for self-test page <paramref name="page"/> (0-based).</summary>
public static byte[] BuildPage(int page) => page switch
{
0 => BuildBanner(),
1 => BuildCharset(),
2 => BuildGraphics(),
_ => throw new ArgumentOutOfRangeException(nameof(page)),
};
private static void Esc(List<byte> b, byte cmd, params byte[] operands)
{
b.Add(PlasmaProtocol.Esc);
b.Add(cmd);
b.AddRange(operands);
}
private static void Text(List<byte> b, string s)
{
foreach (char c in s)
b.Add((byte)c);
}
/// <summary>Big-font banner over small-font attribute samples.</summary>
private static byte[] BuildBanner()
{
var b = new List<byte>();
Esc(b, PlasmaProtocol.CmdClearScreen);
Esc(b, PlasmaProtocol.CmdCursorMode, 0x00);
Esc(b, PlasmaProtocol.CmdFontSelect, 0x04); // 12×16 cells: 10 × 2
Text(b, " vPLASMA");
Esc(b, PlasmaProtocol.CmdFontSelect, 0x00); // 6×8 cells: 21 × 4
Esc(b, PlasmaProtocol.CmdHomeCursor);
b.Add(PlasmaProtocol.LineFeed);
b.Add(PlasmaProtocol.LineFeed);
Text(b, "128x32 PLASMA DISPLAY"); // exactly one 21-cell row
Esc(b, PlasmaProtocol.CmdAttributes, 1); Text(b, "HALF ");
Esc(b, PlasmaProtocol.CmdAttributes, 2); Text(b, "UNDER ");
Esc(b, PlasmaProtocol.CmdAttributes, 3); Text(b, "REV");
Esc(b, PlasmaProtocol.CmdAttributes, 4); Text(b, " FLASH");
Esc(b, PlasmaProtocol.CmdAttributes, PlasmaProtocol.OperandDefault);
return b.ToArray();
}
/// <summary>The printable set 0x20..0x6F — a full 21×4 grid, minus a cell.</summary>
private static byte[] BuildCharset()
{
var b = new List<byte>();
Esc(b, PlasmaProtocol.CmdClearScreen);
Esc(b, PlasmaProtocol.CmdCursorMode, 0x00);
for (byte c = 0x20; c <= 0x6F; ++c)
b.Add(c);
return b.ToArray();
}
/// <summary>The rest of the charset plus an ESC P pattern block.</summary>
private static byte[] BuildGraphics()
{
var b = new List<byte>();
Esc(b, PlasmaProtocol.CmdClearScreen);
Esc(b, PlasmaProtocol.CmdCursorMode, 0x00);
for (byte c = 0x70; c <= 0x7E; ++c)
b.Add(c);
// A framed checkerboard sent the way the game sends everything:
// full-width ESC P rows (screen 0, xbyte 0, 16 bytes, 1 row each).
for (int y = 10; y < VPlasmaDevice.Height; ++y)
{
Esc(b, PlasmaProtocol.CmdGraphicsWrite,
0, (byte)y, 0, VPlasmaDevice.WidthBytes, 1);
bool edge = y is 10 or VPlasmaDevice.Height - 1;
for (int x = 0; x < VPlasmaDevice.WidthBytes; ++x)
{
byte fill = edge ? (byte)0xFF : (y & 2) == 0 ? (byte)0xAA : (byte)0x55;
if (!edge)
{
if (x == 0) fill |= 0x80; // left frame edge
if (x == VPlasmaDevice.WidthBytes - 1) fill |= 0x01; // right frame edge
}
b.Add(fill);
}
}
return b.ToArray();
}
}