diff --git a/PlasmaNew/README.md b/PlasmaNew/README.md index 9b17d47..bf987d0 100644 --- a/PlasmaNew/README.md +++ b/PlasmaNew/README.md @@ -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 1–2 = the datasheet's baud "JUMPER 1 / JUMPER 2." Positions 3–6 -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: PD0–PD5 = pins 10–15, PA0–PA7 = pins 24–17 (descending). diff --git a/src/VPlasma.App/MainForm.cs b/src/VPlasma.App/MainForm.cs index ea63b60..9f1b290 100644 --- a/src/VPlasma.App/MainForm.cs +++ b/src/VPlasma.App/MainForm.cs @@ -4,16 +4,15 @@ namespace VPlasma.App; /// /// vPLASMA main window: the plasma glass on the left, a config panel on the -/// right. The device end is hardwired to (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 (\\.\pipe\vplasma) is -/// served permanently alongside it. +/// right. The serial device end is (the plasma side of +/// the second com0com pair); it is opened manually from the panel — no +/// auto-open. The DOSBox-X fork's named-pipe transport +/// (\\.\pipe\vplasma) is served permanently regardless. /// /// The right panel mirrors the real PD01D221's JP1 configuration /// jumpers (recovered in PlasmaNew/FIRMWARE.md): 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. @@ -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 -------------------------------------------------- /// Baud from the two straps (installed = logic 0), per the datasheet. @@ -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;