diff --git a/PlasmaNew/replica/MatrixPortalPlasma/MatrixPortalPlasma.ino b/PlasmaNew/replica/MatrixPortalPlasma/MatrixPortalPlasma.ino
index 8cece91..9d4b718 100644
--- a/PlasmaNew/replica/MatrixPortalPlasma/MatrixPortalPlasma.ino
+++ b/PlasmaNew/replica/MatrixPortalPlasma/MatrixPortalPlasma.ino
@@ -47,6 +47,12 @@ static int demoScreen = 0;
static uint32_t lastDemoStep = 0;
static const uint32_t DEMO_MS = 2800;
+// Panel test (DOWN button): cycle the diagnostic patterns.
+static bool testRunning = false;
+static int testPattern = 0;
+static uint32_t lastTestStep = 0;
+static const uint32_t TEST_MS = 1200;
+
// ---- helpers -------------------------------------------------------------
static bool frameHasFlash = false;
@@ -120,6 +126,7 @@ void loop() {
bool up = digitalRead(BUTTON_UP);
if (upPrev == HIGH && up == LOW) { // pressed
demoRunning = !demoRunning;
+ testRunning = false;
if (demoRunning) { demoScreen = 0; feedDemoScreen(0); demoScreen = 1; lastDemoStep = millis(); }
else { display.reset(); }
}
@@ -129,19 +136,26 @@ void loop() {
static bool downPrev = HIGH;
bool down = digitalRead(BUTTON_DOWN);
if (downPrev == HIGH && down == LOW) { // pressed
+ testRunning = !testRunning;
demoRunning = false;
- display.showTestPattern();
+ if (testRunning) { testPattern = 0; display.showTestPattern(0); lastTestStep = millis(); }
+ else { display.reset(); }
}
downPrev = down;
#endif
- // 3) Advance the demo on its timer.
+ // 3) Advance the demo / test on their timers.
uint32_t now = millis();
if (demoRunning && now - lastDemoStep >= DEMO_MS) {
feedDemoScreen(demoScreen);
demoScreen = (demoScreen + 1) % plasmaDemoCount;
lastDemoStep = now;
}
+ if (testRunning && now - lastTestStep >= TEST_MS) {
+ testPattern = (testPattern + 1) % PlasmaDisplay::TEST_PATTERN_COUNT;
+ display.showTestPattern(testPattern);
+ lastTestStep = now;
+ }
// 4) Blink phase for flashing pixels.
if (now - lastBlink >= BLINK_MS) {
diff --git a/PlasmaNew/replica/MatrixPortalPlasma/PlasmaDisplay.cpp b/PlasmaNew/replica/MatrixPortalPlasma/PlasmaDisplay.cpp
index db762f7..e2c0a31 100644
--- a/PlasmaNew/replica/MatrixPortalPlasma/PlasmaDisplay.cpp
+++ b/PlasmaNew/replica/MatrixPortalPlasma/PlasmaDisplay.cpp
@@ -32,8 +32,25 @@ void PlasmaDisplay::setOrientation(Orientation o) {
dirty_ = true;
}
-void PlasmaDisplay::showTestPattern() {
- for (int i = 0; i < WIDTH * HEIGHT; ++i) pixels_[i] = PIX_LIT;
+// The real display's power-on test (JP1 jumper 5, firmware $B888) runs through
+// several drawing sequences — solid, border+grid, and more — to expose dead
+// dots and addressing faults. These stand in for that; the sketch cycles them.
+static bool testDot(int index, int x, int y) {
+ switch (index) {
+ case 0: return true; // all on
+ case 1: return x == 0 || x == PlasmaDisplay::WIDTH - 1 || // frame
+ y == 0 || y == PlasmaDisplay::HEIGHT - 1 ||
+ (x % 16) == 0 || (y % 8) == 0; // + grid
+ case 2: return ((y / 2) % 2) == 0; // horizontal stripes
+ case 3: return ((x / 4) % 2) == 0; // vertical stripes
+ default: return (((x / 8) + (y / 8)) % 2) == 0; // checkerboard
+ }
+}
+
+void PlasmaDisplay::showTestPattern(int index) {
+ for (int y = 0; y < HEIGHT; ++y)
+ for (int x = 0; x < WIDTH; ++x)
+ pixels_[y * WIDTH + x] = testDot(index, x, y) ? PIX_LIT : 0;
dirty_ = true;
}
diff --git a/PlasmaNew/replica/MatrixPortalPlasma/PlasmaDisplay.h b/PlasmaNew/replica/MatrixPortalPlasma/PlasmaDisplay.h
index 208e143..7b96d39 100644
--- a/PlasmaNew/replica/MatrixPortalPlasma/PlasmaDisplay.h
+++ b/PlasmaNew/replica/MatrixPortalPlasma/PlasmaDisplay.h
@@ -25,10 +25,13 @@ class PlasmaDisplay {
PlasmaDisplay();
+ static const int TEST_PATTERN_COUNT = 5; // JP1 jumper-5 diagnostic patterns
+
void reset(); // power-on: dark glass, home cursor
void feed(uint8_t b); // one received wire byte
void feed(const uint8_t *buf, size_t n);
- void showTestPattern(); // all dots lit (JP1 jumper 5)
+ void showTestPattern() { showTestPattern(0); } // all dots lit
+ void showTestPattern(int index); // JP1 jumper 5: solid/grid/stripes/checker
void setOrientation(Orientation o); // JP1 jumper 4
const uint8_t *frame() const { return pixels_; }
diff --git a/PlasmaNew/replica/README.md b/PlasmaNew/replica/README.md
index 9c477d5..3b9eeb3 100644
--- a/PlasmaNew/replica/README.md
+++ b/PlasmaNew/replica/README.md
@@ -66,7 +66,9 @@ display listens), exactly as the cockpit drove the real panel.
- **UP** — toggle the built-in **firmware demonstration** (the real 10-screen
PLASMADOT demo, `demo_screens.h`, extracted from the ROM).
-- **DOWN** — the **panel test pattern** (all dots lit; a dead-dot check).
+- **DOWN** — toggle the **panel test**: cycles diagnostic patterns (solid,
+ border+grid, horizontal/vertical stripes, checkerboard) to expose dead dots
+ and addressing faults — the multi-pattern sequence the real firmware runs.
- **Power-on** briefly lights every dot (confirms both panels), then clears.
## Files
diff --git a/src/VPlasma.App/MainForm.cs b/src/VPlasma.App/MainForm.cs
index 63c7334..73ea1cd 100644
--- a/src/VPlasma.App/MainForm.cs
+++ b/src/VPlasma.App/MainForm.cs
@@ -30,8 +30,10 @@ internal sealed class MainForm : Form
private readonly System.Windows.Forms.Timer _uiTimer = new() { Interval = 500 };
private readonly System.Windows.Forms.Timer _demoTimer = new() { Interval = 2800 };
+ private readonly System.Windows.Forms.Timer _testTimer = new() { Interval = 1200 };
private int _selfTestPage;
private int _demoScreen;
+ private int _testPattern;
// ---- config panel controls -------------------------------------------
private readonly ComboBox _portBox = new() { Width = 168, DropDownStyle = ComboBoxStyle.DropDownList };
@@ -118,6 +120,7 @@ internal sealed class MainForm : Form
_jp5.CheckedChanged += (_, _) => ApplyTestPattern();
_jp6.CheckedChanged += (_, _) => ApplyDemo();
_demoTimer.Tick += (_, _) => StepDemo();
+ _testTimer.Tick += (_, _) => StepTestPattern();
// Default straps: jumper 2 installed = 9600 (the game's rate). Jumper 4
// unstrapped = horizontal (the normal cockpit orientation).
@@ -130,6 +133,7 @@ internal sealed class MainForm : Form
{
_uiTimer.Dispose();
_demoTimer.Dispose();
+ _testTimer.Dispose();
_service.Dispose();
_pipeService.Dispose();
};
@@ -288,16 +292,25 @@ internal sealed class MainForm : Form
{
if (_jp5.Checked)
{
- _device.ShowTestPattern();
- PrependLog("Jumper 5 installed — panel test pattern (all dots lit)");
+ PrependLog("Jumper 5 installed — panel test (cycling diagnostic patterns)");
+ _testPattern = 0;
+ _device.ShowTestPattern(0);
+ _testTimer.Start();
}
else
{
+ _testTimer.Stop();
_device.Reset();
PrependLog("Jumper 5 removed — test pattern cleared");
}
}
+ private void StepTestPattern()
+ {
+ _testPattern = (_testPattern + 1) % VPlasmaDevice.TestPatternCount;
+ _device.ShowTestPattern(_testPattern);
+ }
+
private void ApplyDemo()
{
if (_jp6.Checked)
diff --git a/src/VPlasma.Core/Device/VPlasmaDevice.cs b/src/VPlasma.Core/Device/VPlasmaDevice.cs
index 6a7620d..1eafc98 100644
--- a/src/VPlasma.Core/Device/VPlasmaDevice.cs
+++ b/src/VPlasma.Core/Device/VPlasmaDevice.cs
@@ -197,21 +197,41 @@ public sealed class VPlasmaDevice
FlushEvents();
}
+ /// Number of diagnostic patterns the JP1 jumper-5 test cycles.
+ public const int TestPatternCount = 5;
+
+ /// Show the first test pattern (all dots lit).
+ public void ShowTestPattern() => ShowTestPattern(0);
+
///
- /// Light every dot — the panel test pattern JP1 jumper 5 runs at power-on
- /// (the firmware's dead-dot check). Clear it with .
+ /// Paint diagnostic pattern (0..-1). The real display's power-on test (JP1
+ /// jumper 5, firmware $B888) runs through several drawing
+ /// sequences — a solid fill, a border+grid, and more, each held briefly —
+ /// to expose dead dots and addressing faults; these stand in for that.
///
- public void ShowTestPattern()
+ public void ShowTestPattern(int index)
{
lock (_sync)
{
- for (int i = 0; i < _pixels.Length; ++i)
- _pixels[i] = PixelLit;
+ for (int y = 0; y < Height; ++y)
+ for (int x = 0; x < Width; ++x)
+ _pixels[y * Width + x] = TestDot(index, x, y) ? PixelLit : (byte)0;
_dirty = true;
}
FlushEvents();
}
+ private static bool TestDot(int index, int x, int y) => index switch
+ {
+ 0 => true, // all on
+ 1 => x == 0 || x == Width - 1 || y == 0 || y == Height - 1 // frame
+ || x % 16 == 0 || y % 8 == 0, // + grid
+ 2 => (y / 2) % 2 == 0, // horizontal stripes
+ 3 => (x / 4) % 2 == 0, // vertical stripes
+ _ => ((x / 8) + (y / 8)) % 2 == 0, // checkerboard
+ };
+
/// Feed received wire bytes.
public void OnReceived(byte[] buffer, int count)
{