vPLASMA (standalone): manual COM12 open, and decode jumpers 3/4/5

- Remove the COM12 auto-open and its retry timer; the serial link is now
  opened manually via an "Open/Close COM12" button in the config panel.
  The named-pipe transport still serves automatically. Baud straps reopen
  only if already open.
- Decode the three previously-unknown JP1 config jumpers from the firmware
  (boot config routine at $9190) and relabel them:
    * Jumper 3 (PA3): sets flag $B7.2, driving hardware output PA5 to a
      fixed level (a board config line).
    * Jumper 4 (PD5): display orientation — installed = horizontal 128x32,
      removed = vertical 32x128 (the demo's "HORIZONTAL OR VERTICAL").
    * Jumper 5 (PD4): power-on display/pixel test pattern ($B888).
  PlasmaNew/README.md's JP1 table now carries the full firmware-backed decode.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-16 18:15:49 -05:00
co-authored by Claude Opus 4.8
parent 90a57ba2cf
commit d8ab0ffe49
2 changed files with 70 additions and 59 deletions
+14 -12
View File
@@ -114,19 +114,21 @@ Traced pin-by-pin (see the jumper photo). **JP1 is firmware-read
configuration, not CPU mode select** — each shunt ties a GP port pin the
firmware polls at boot. Shunt to GND = logic 0.
| JP1 pos | HC11 pin | Function |
|---------|----------|----------|
| 1 | pin 24 / PA0 | Baud select bit 0 |
| 2 | pin 22 / PA2 | Baud select bit 1 |
| 3 | pin 21 / PA3 | Option (unknown) |
| 4 | pin 15 / PD5 | Option (unknown) |
| 5 | pin 14 / PD4 | Option (unknown) |
| 6 | pin 13 / PD3 | Option (unknown) |
| 7 | J2 SEL → +5 V | Parallel interface select |
All decoded from the firmware's boot config routine at `$9190` (installed =
grounded = logic 0):
Positions 12 = the datasheet's baud "JUMPER 1 / JUMPER 2." Positions 36
are four unknown firmware option bits — candidates for a hidden factory
self-test / diagnostic mode.
| JP1 pos | HC11 pin | Function | Firmware |
|---------|----------|----------|----------|
| 1 | pin 24 / PA0 | Baud select bit 0 | `$9193` → SCI baud reg `$2B` |
| 2 | pin 22 / PA2 | Baud select bit 1 | `$9197` → SCI baud reg `$2B` |
| 3 | pin 21 / PA3 | HW config line | `$91CA` sets flag `$B7.2`, which drives output PA5 to a fixed level (board control line; exact effect board-dependent) |
| 4 | pin 15 / PD5 | Display orientation | `$90AE`: installed = horizontal 128×32, removed = vertical 32×128 (`$B3.0`; the demo's "HORIZONTAL OR VERTICAL ORIENTATION") |
| 5 | pin 14 / PD4 | Display/pixel test | `$91C3` installed → `$B888` writes a walking-bit test pattern (dead-dot check) |
| 6 | pin 13 / PD3 | Demonstration program | `$91DB` installed → `$BB60` runs the built-in demo |
| 7 | J2 SEL → +5 V | Parallel interface select | (board interface mux) |
Baud straps 1+2 pick SCI baud register (`$2B`) values `$13`/`$11`/`$12`/`$10`
= 4800 / 9600 / 19.2K / 38.4K (more zeros grounded ⇒ slower).
HC11 pin map cross-checked while tracing: PD0PD5 = pins 1015, PA0PA7 =
pins 2417 (descending).
+56 -47
View File
@@ -4,16 +4,15 @@ namespace VPlasma.App;
/// <summary>
/// vPLASMA main window: the plasma glass on the left, a config panel on the
/// right. The device end is hardwired to <see cref="PortName"/> (the plasma
/// side of the second com0com pair) and opened automatically — a retry timer
/// keeps trying while the port is missing or busy, and reopens it if it dies.
/// The DOSBox-X fork's named-pipe transport (<c>\\.\pipe\vplasma</c>) is
/// served permanently alongside it.
/// right. The serial device end is <see cref="PortName"/> (the plasma side of
/// the second com0com pair); it is <b>opened manually</b> from the panel — no
/// auto-open. The DOSBox-X fork's named-pipe transport
/// (<c>\\.\pipe\vplasma</c>) is served permanently regardless.
///
/// <para>The right panel mirrors the real PD01D221's <b>JP1 configuration
/// jumpers</b> (recovered in <c>PlasmaNew/FIRMWARE.md</c>): the two baud
/// straps drive the open baud, jumper 6 runs the built-in demo (exactly what
/// the firmware does when that jumper is installed), and the rest are the
/// straps drive the open baud, jumper 6 runs the built-in demo (as the
/// firmware does when that jumper is installed), and the rest are the
/// hardware's config bits. Plus display controls, live counters, and a wire
/// log. Double-click the glass to cycle the self-test pages; right-click to
/// reset to the power-on state.</para>
@@ -28,15 +27,15 @@ internal sealed class MainForm : Form
private readonly VPlasmaPipeService _pipeService;
private readonly PlasmaCanvas _canvas = new();
private readonly System.Windows.Forms.Timer _reconnectTimer = new() { Interval = 3000 };
private readonly System.Windows.Forms.Timer _uiTimer = new() { Interval = 500 };
private readonly System.Windows.Forms.Timer _demoTimer = new() { Interval = 3000 };
private int _selfTestPage;
// ---- config panel controls -------------------------------------------
private readonly Button _openClose = new() { Text = "Open COM12", Width = 100, Height = 24 };
private readonly Label _linkStatus = new()
{
Location = new Point(12, 12),
Location = new Point(12, 42),
AutoSize = true,
MaximumSize = new Size(286, 0),
ForeColor = Color.Gray,
@@ -46,9 +45,9 @@ internal sealed class MainForm : Form
// the CPU). Text/tooltips carry each strap's real function and HC11 pin.
private readonly CheckBox _jp1 = Jumper("1 — Baud select A (PA0)");
private readonly CheckBox _jp2 = Jumper("2 — Baud select B (PA2)");
private readonly CheckBox _jp3 = Jumper("3 — Config bit (PA3)");
private readonly CheckBox _jp4 = Jumper("4 — Config bit (PD5)");
private readonly CheckBox _jp5 = Jumper("5 — Config bit (PD4)");
private readonly CheckBox _jp3 = Jumper("3 — HW config line (PA3)");
private readonly CheckBox _jp4 = Jumper("4 — Orientation H/V (PD5)");
private readonly CheckBox _jp5 = Jumper("5 — Display test (PD4)");
private readonly CheckBox _jp6 = Jumper("6 — Demo program (PD3)");
private readonly CheckBox _jp7 = Jumper("7 — Parallel select (J2 SEL)");
private readonly Label _baudLabel = new() { AutoSize = true, ForeColor = Color.DimGray };
@@ -82,8 +81,8 @@ internal sealed class MainForm : Form
{
Text = $"vPLASMA v{Application.ProductVersion} — Virtual plasma display";
Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
ClientSize = new Size(_canvas.Width + 316, Math.Max(_canvas.Height + 8, 520));
MinimumSize = new Size(_canvas.Width + 332, 420);
ClientSize = new Size(_canvas.Width + 316, Math.Max(_canvas.Height + 8, 560));
MinimumSize = new Size(_canvas.Width + 332, 480);
StartPosition = FormStartPosition.CenterScreen;
_service = new VPlasmaSerialService(_device);
@@ -104,6 +103,7 @@ internal sealed class MainForm : Form
_pipeService.Logged += line => RunOnUi(() => PrependLog(line));
_pipeService.ClientChanged += _ => RunOnUi(UpdateStatus);
_openClose.Click += (_, _) => ToggleConnection();
_canvas.DoubleClick += (_, _) => RunSelfTest();
_canvas.MouseClick += (_, e) => { if (e.Button == MouseButtons.Right) ResetDisplay(); };
@@ -111,7 +111,8 @@ internal sealed class MainForm : Form
_clear.Click += (_, _) => ResetDisplay();
_clearLog.Click += (_, _) => _logBox.Clear();
// Baud straps → reopen at the selected rate. Jumper 6 → demo loop.
// Baud straps → reopen at the selected rate (only if already open).
// Jumper 6 → demo loop.
_jp1.CheckedChanged += (_, _) => ApplyBaud();
_jp2.CheckedChanged += (_, _) => ApplyBaud();
_jp6.CheckedChanged += (_, _) => ApplyDemo();
@@ -120,14 +121,11 @@ internal sealed class MainForm : Form
// Default straps: jumper 2 installed only = 9600 (the game's setting).
_jp2.Checked = true;
_reconnectTimer.Tick += (_, _) => EnsureOpen();
_reconnectTimer.Start();
_uiTimer.Tick += (_, _) => UpdateCounters();
_uiTimer.Start();
FormClosed += (_, _) =>
{
_reconnectTimer.Dispose();
_uiTimer.Dispose();
_demoTimer.Dispose();
_service.Dispose();
@@ -138,17 +136,18 @@ internal sealed class MainForm : Form
_service.Baud = SelectedBaud();
UpdateStatus();
UpdateCounters();
EnsureOpen();
_pipeService.Start();
PrependLog("vPLASMA ready. Serving COM12 + \\\\.\\pipe\\vplasma. Toggle jumper 6 for the built-in demo.");
_pipeService.Start(); // the pipe transport still serves automatically
PrependLog("vPLASMA ready. Serving \\\\.\\pipe\\vplasma. Click “Open COM12” for the serial link; jumper 6 for the demo.");
}
private Panel BuildConfigPanel()
{
var panel = new Panel { Dock = DockStyle.Right, Width = 300, BackColor = SystemColors.Control };
_openClose.Location = new Point(12, 12);
panel.Controls.Add(_openClose);
panel.Controls.Add(_linkStatus);
var jumpers = new GroupBox { Text = "JP1 configuration jumpers", Location = new Point(12, 44), Size = new Size(276, 176) };
var jumpers = new GroupBox { Text = "JP1 configuration jumpers", Location = new Point(12, 70), Size = new Size(276, 176) };
CheckBox[] jp = { _jp1, _jp2, _jp3, _jp4, _jp5, _jp6, _jp7 };
for (int i = 0; i < jp.Length; i++)
{
@@ -159,27 +158,28 @@ internal sealed class MainForm : Form
jumpers.Controls.Add(_baudLabel);
panel.Controls.Add(jumpers);
_tips.SetToolTip(_openClose, "Open or close the COM12 serial link. Not opened automatically.");
_tips.SetToolTip(_jp1, "Baud select, low bit. With jumper 2: 4800/9600/19.2K/38.4K.");
_tips.SetToolTip(_jp2, "Baud select, high bit. Installed alone = 9600 (the game's rate).");
_tips.SetToolTip(_jp3, "Firmware config bit on PA3 (function not yet decoded).");
_tips.SetToolTip(_jp4, "Firmware config bit on PD5 (function not yet decoded).");
_tips.SetToolTip(_jp5, "Firmware config bit on PD4 (function not yet decoded).");
_tips.SetToolTip(_jp3, "Sets flag B7.2, driving the PA5 hardware line to a fixed level (a board config line; exact effect board-dependent).");
_tips.SetToolTip(_jp4, "Display orientation: installed = horizontal 128×32, removed = vertical (rotated 32×128).");
_tips.SetToolTip(_jp5, "Installed at power-on runs the panel pixel test pattern (a walking-bit dead-dot check).");
_tips.SetToolTip(_jp6, "Install to run the built-in demonstration program (as the firmware does).");
_tips.SetToolTip(_jp7, "Selects the parallel interface on the real board; no effect here (serial only).");
var display = new GroupBox { Text = "Display", Location = new Point(12, 230), Size = new Size(276, 62) };
var display = new GroupBox { Text = "Display", Location = new Point(12, 256), Size = new Size(276, 62) };
_selfTest.Location = new Point(12, 22);
_clear.Location = new Point(146, 22);
display.Controls.Add(_selfTest);
display.Controls.Add(_clear);
panel.Controls.Add(display);
_counters.Location = new Point(12, 302);
_counters.Location = new Point(12, 328);
panel.Controls.Add(_counters);
var logLabel = new Label { Text = "Wire log:", Location = new Point(12, 384), AutoSize = true };
var logLabel = new Label { Text = "Wire log:", Location = new Point(12, 410), AutoSize = true };
panel.Controls.Add(logLabel);
_logBox.SetBounds(12, 402, 276, 0);
_logBox.SetBounds(12, 428, 276, 0);
panel.Controls.Add(_logBox);
panel.Controls.Add(_clearLog);
@@ -192,6 +192,26 @@ internal sealed class MainForm : Form
return panel;
}
// ---- connection --------------------------------------------------------
private void ToggleConnection()
{
if (_service.IsOpen)
{
_service.Close();
return;
}
try
{
_service.Open(PortName);
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or InvalidOperationException or ArgumentException)
{
PrependLog($"Open failed: {ex.Message}");
UpdateStatus();
}
}
// ---- jumper behaviour --------------------------------------------------
/// <summary>Baud from the two straps (installed = logic 0), per the datasheet.</summary>
@@ -207,10 +227,12 @@ internal sealed class MainForm : Form
{
int baud = SelectedBaud();
_service.Baud = baud;
PrependLog($"Baud straps → {baud} 8N1 (reopening)");
if (_service.IsOpen)
_service.Close(); // the reconnect timer reopens at the new baud
EnsureOpen();
{
PrependLog($"Baud straps → {baud} 8N1 (reopening)");
_service.Close();
ToggleConnection();
}
UpdateStatus();
}
@@ -252,28 +274,15 @@ internal sealed class MainForm : Form
PrependLog("Display reset to power-on state");
}
private void EnsureOpen()
{
if (_service.IsOpen)
return;
try
{
_service.Open(PortName);
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or InvalidOperationException or ArgumentException)
{
UpdateStatus(); // stays closed; the timer tries again
}
}
// ---- status / counters / log ------------------------------------------
private void UpdateStatus()
{
_openClose.Text = _service.IsOpen ? "Close COM12" : "Open COM12";
_baudLabel.Text = $"{SelectedBaud()} baud";
string line = _service.IsOpen
? $"Listening on {PortName} @ {_service.Baud} 8N1."
: $"{PortName} unavailable — retrying.";
: $"{PortName} closed.";
if (_pipeService.IsClientConnected)
line += " Pipe host connected.";
_linkStatus.Text = line;