Files
VRIO/PlasmaNew/replica/MatrixPortalPlasma/PlasmaDisplay.h
T
CydandClaude Opus 4.8 ebad6ee5fe PD4 display test: cycle diagnostic patterns, not just all-on
The real display's power-on test (JP1 jumper 5, firmware $B888) runs through
several drawing sequences — a solid fill, a border+grid, more — each held
briefly, to expose dead dots and addressing faults; it isn't just full on/off.

Both vPLASMA and the replica firmware now cycle 5 representative patterns:
solid, border+grid, horizontal stripes, vertical stripes, checkerboard.
- VPlasmaDevice.ShowTestPattern(int) + TestPatternCount; the app cycles them
  on a 1.2s timer while jumper 5 is installed.
- PlasmaDisplay::showTestPattern(int) + TEST_PATTERN_COUNT (shared logic); the
  Matrix Portal sketch's DOWN button toggles the cycling test.

(The firmware's exact byte patterns live in the display's native scan-buffer
layout, which we haven't mapped, so these stand in for that sequence rather
than reproducing it byte-for-byte.)

Verified: 29 C# tests pass; the C++ patterns render distinctly on the host.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 20:34:33 -05:00

91 lines
3.1 KiB
C++

// PlasmaDisplay — the Babcock PD01D221 command parser + 128x32 framebuffer,
// ported line-for-line from vRIO's C# VPlasmaDevice (src/VPlasma.Core/Device/
// VPlasmaDevice.cs). Keep the two in sync: vPLASMA is the reference oracle.
//
// Feed received wire bytes to feed(); read the framebuffer with frame() and
// render it however the panel wants. Each pixel is a flag byte (PIX_LIT /
// PIX_HALF / PIX_FLASH), so the renderer can dim half-intensity dots and blink
// flashing ones. See PlasmaNew/FIRMWARE.md for the recovered command set.
#pragma once
#include <Arduino.h>
#include "plasma_fonts.h"
class PlasmaDisplay {
public:
static const int WIDTH = 128;
static const int HEIGHT = 32;
// Per-pixel flag bits in the framebuffer.
static const uint8_t PIX_LIT = 0x01;
static const uint8_t PIX_HALF = 0x02;
static const uint8_t PIX_FLASH = 0x04;
enum Orientation { HORIZONTAL, VERTICAL };
enum CursorMode { CURSOR_HIDDEN, CURSOR_STEADY, CURSOR_FLASHING };
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() { 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_; }
bool takeDirty() { // true (and clears) if the frame changed
bool d = dirty_;
dirty_ = false;
return d;
}
CursorMode cursorMode() const { return cursorMode_; }
int cursorX() const { return cx_; }
int cursorY() const { return cy_; }
int fontWidth() const { return face_->width; }
int fontHeight() const { return face_->height; }
Orientation orientation() const { return orient_; }
private:
uint8_t pixels_[WIDTH * HEIGHT]; // always physical 128x32
// Text-mode state.
int font_;
const PlasmaFace *face_;
uint8_t attrs_; // low 4 bits: 1=half 2=underline 4=reverse 8=flash
int cx_, cy_; // cursor, logical pixels
CursorMode cursorMode_;
Orientation orient_;
// Parser state.
enum State { TEXT, ESCAPE, OPERAND, GHEADER, GDATA };
State state_;
uint8_t pendingCmd_;
uint8_t header_[5]; // screen, y, x, w, h
int headerFill_;
int dataIndex_, dataLength_;
bool dirty_;
int logicalW() const { return orient_ == HORIZONTAL ? WIDTH : HEIGHT; }
int logicalH() const { return orient_ == HORIZONTAL ? HEIGHT : WIDTH; }
void plot(int lx, int ly, uint8_t flags);
void stepText(uint8_t b);
void stepEscape(uint8_t b);
void applyOperand(uint8_t cmd, uint8_t op);
void beginGraphics();
void stepGraphics(uint8_t b);
void drawChar(uint8_t code);
void advanceCursor();
void nextLine();
bool faceHas(uint8_t code) const { return code >= face_->first && code <= face_->last; }
uint16_t faceRow(uint8_t code, int row) const {
if (!faceHas(code) || (unsigned)row >= (unsigned)face_->height) return 0;
return pgm_read_word(&face_->rows[(code - face_->first) * face_->height + row]);
}
};