vPLASMA: make jumpers 4 (orientation) and 5 (display test) functional

Both now do what the firmware does, at the device level so the shared
PlasmaCanvas (and vRIO's embedded glass) are untouched:

- Jumper 4 → display orientation. VPlasmaDevice gains a PlasmaOrientation
  property; in Vertical the logical space is 32x128 and every dot is rotated
  onto the physical 128x32 glass via a new Plot() helper that all drawing
  (text, cursor, ESC P graphics, ESC Q/R bounds) now goes through. Jumper 4
  installed = horizontal (the normal cockpit setup, default), removed =
  vertical. Changing it re-inits the panel, like the boot strap read.
- Jumper 5 → ShowTestPattern(): lights every dot (the power-on dead-dot
  check the firmware runs at $B888). Removing the jumper clears it.

The config panel wires both up and shows the live orientation in the
counters. 27 unit tests pass (3 new: vertical rotation mapping, orientation
re-init, test pattern), including the unchanged horizontal path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-16 18:26:51 -05:00
co-authored by Claude Opus 4.8
parent d8ab0ffe49
commit 95b701ae0a
4 changed files with 193 additions and 53 deletions
@@ -306,6 +306,55 @@ public class VPlasmaDeviceTests
}
}
// ---- orientation + test pattern (JP1 jumpers 4 & 5) --------------------
[Fact]
public void Orientation_VerticalRotatesLogicalOntoPhysicalGlass()
{
var device = new VPlasmaDevice { Orientation = PlasmaOrientation.Vertical };
Assert.Equal(32, device.LogicalWidth);
Assert.Equal(128, device.LogicalHeight);
// Logical (0,0) maps to physical (px=ly=0, py=Height-1-lx=31).
Feed(device, Esc, (byte)'R', 0, Esc, (byte)'Q', 0, (byte)'H');
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 0, 31)); // 'H' top-left dot
Assert.Equal(0, Pixel(device, 0, 0));
// ESC Q (row Y) now accepts up to the 128-tall logical height;
// ESC R (column X) is bounded by the 32-wide logical width.
Feed(device, Esc, (byte)'Q', 100);
Assert.Equal(100, device.CursorY);
Feed(device, Esc, (byte)'R', 40); // out of range for 32-wide vertical
Assert.NotEqual(40, device.CursorX);
}
[Fact]
public void Orientation_ChangeClearsAndHomes()
{
var device = new VPlasmaDevice();
Feed(device, (byte)'H');
Assert.NotEqual(0, Pixel(device, 0, 0));
device.Orientation = PlasmaOrientation.Vertical;
Assert.Equal(0, Pixel(device, 0, 0)); // cleared
Assert.Equal(0, device.CursorX);
Assert.Equal(0, device.CursorY);
}
[Fact]
public void ShowTestPattern_LightsEveryDot()
{
var device = new VPlasmaDevice();
device.ShowTestPattern();
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 0, 0));
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 127, 31));
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 64, 16));
device.Reset();
Assert.Equal(0, Pixel(device, 64, 16));
}
[Fact]
public void Reset_RestoresPowerOnState()
{