vPLASMA: fold in the real ROM fonts and firmware-confirmed commands
Replaces the guessed font/cursor model with what the firmware dump proved: - The 8 real Babcock ROM fonts, extracted from tms27pc512.BIN into PlasmaFonts.cs (6x8, 6x10, 7x10, 12x16, 12x20). Drops the public-domain 5x7 stand-in and PlasmaFont.cs. - Pixel-addressed cursor with the real positioning commands: ESC Q (row Y, 0-31) and ESC R (column X, 0-127), matching the firmware's range checks. Cursor motion (BS/HT/LF/VT/CR) now moves by font pixels, not cells. - ESC K selects fonts 0-7 (out-of-range ignored, per firmware); ESC H attributes are the low 4 bits (half/underline/reverse/flash). Deferred (documented in FIRMWARE.md): the 10 double-buffered pages (ESC I/i) and vector-graphics primitives (ESC A-F); vPLASMA keeps a single-page model for now. Verified: 24 unit tests pass; the three self-test pages render the real glyphs (big font 4 banner, full charset in font 0, graphics). Next: begin the modern-parts hardware replica, with this as the reference firmware. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -127,10 +127,14 @@ at `$8000`). It's how the new `ESC R/I/Q/i/K` commands were first spotted, e.g.
|
||||
|
||||
## What this means
|
||||
|
||||
**For vPLASMA:** the recovered commands can replace guessed behavior. Notably
|
||||
`ESC Q`/`ESC R` are the real cursor-positioning commands (row/col), and the
|
||||
`ESC P` "screen" byte selects one of 10 double-buffered pages — vPLASMA
|
||||
currently models a single buffer.
|
||||
**For vPLASMA (folded in 2026-07-16):** the recovered spec replaced the
|
||||
guessed behavior. vPLASMA now uses the **8 real ROM fonts** (extracted to
|
||||
`src/VPlasma.Core/Device/PlasmaFonts.cs`), a **pixel-addressed cursor** with
|
||||
the real `ESC Q` (row) / `ESC R` (column) positioning, `ESC K` 0–7 font
|
||||
select, and `ESC H` attributes as the low 4 bits. Verified: 24 unit tests +
|
||||
the three self-test pages render the real glyphs. Still deferred (documented,
|
||||
single-page model retained): the 10 double-buffered pages (`ESC I`/`ESC i`)
|
||||
and the vector-graphics primitives (`ESC A`–`F`).
|
||||
|
||||
**For the replica:** this *is* the spec. The firmware confirms a clean model —
|
||||
a byte-stream command parser, a 512-byte-per-page frame buffer, 10 pages with
|
||||
|
||||
@@ -157,12 +157,15 @@ recovered grammar lives in `src/VPlasma.Core/Protocol/PlasmaProtocol.cs`.
|
||||
- **Graphics** — `ESC P screen y xbyte width rows` + packed 1bpp row data,
|
||||
MSB = leftmost pixel. This is everything the game sends, so it is the
|
||||
wire path a pod's plasma actually sees.
|
||||
- **Text** — printable ASCII renders through a 5×7 font at a cursor:
|
||||
fonts 0–3 give a 21×4 cell grid, fonts 4–7 the same glyphs doubled to
|
||||
10×2. Half-intensity draws dimmer, reverse/underline render in the
|
||||
cell, flashing text (and the flashing cursor) blink on the glass. The
|
||||
panel's own ROM glyphs are lost with the hardware, so the classic
|
||||
public-domain 5×7 set stands in.
|
||||
- **Text** — printable ASCII renders at a **pixel-addressed cursor**
|
||||
(`ESC Q` sets row Y 0–31, `ESC R` sets column X 0–127) through the
|
||||
**real ROM character generator** — the eight Babcock fonts recovered
|
||||
from the firmware dump (`ESC K` 0–7): 6×8, 6×10, 7×10, and the large
|
||||
12×16 / 12×20. Attributes (`ESC H`, low 4 bits) — half-intensity
|
||||
draws dimmer, underline/reverse render in the cell, flashing text (and
|
||||
the flashing cursor) blink on the glass. Extracted from the U3 EPROM;
|
||||
see [`PlasmaNew/FIRMWARE.md`](PlasmaNew/FIRMWARE.md). (Deferred, and
|
||||
documented there: the 10 double-buffered pages and vector graphics.)
|
||||
- **Double-click** the glass to cycle three self-test pages (banner,
|
||||
charset, graphics pattern) through the same parser the wire feeds —
|
||||
useful without a host. **Right-click** resets the display to its
|
||||
@@ -178,7 +181,7 @@ recovered grammar lives in `src/VPlasma.Core/Protocol/PlasmaProtocol.cs`.
|
||||
|------|----------|
|
||||
| `src/VRio.Core` | Protocol framing/builder/parser, the `VRioDevice` state machine, serial pump, panel layout data (class library) |
|
||||
| `src/VRio.App` | WinForms panel UI |
|
||||
| `src/VPlasma.Core` | Plasma command-stream parser, the `VPlasmaDevice` display state machine, 5×7 font, serial listener (class library) |
|
||||
| `src/VPlasma.Core` | Plasma command-stream parser, the `VPlasmaDevice` display state machine, the 8 real ROM fonts (`PlasmaFonts`), serial listener (class library) |
|
||||
| `src/VPlasma.App` | WinForms dot-matrix display UI |
|
||||
| `pkg` | Sparse-package manifest + registration script: grants VRio.App.exe the package identity Dynamic Lighting's background-control list requires |
|
||||
| `tests/VRio.Core.Tests` | xUnit tests for the protocol + device engine |
|
||||
|
||||
@@ -30,7 +30,7 @@ internal sealed class PlasmaCanvas : Control
|
||||
private bool _blinkPhase = true;
|
||||
private bool _anythingBlinks;
|
||||
|
||||
private VPlasmaDevice.Point _cursor;
|
||||
private int _cursorX, _cursorY;
|
||||
private PlasmaCursorMode _cursorMode;
|
||||
private int _cellWidth = 6, _cellHeight = 8;
|
||||
|
||||
@@ -62,10 +62,11 @@ internal sealed class PlasmaCanvas : Control
|
||||
public void UpdateFrom(VPlasmaDevice device)
|
||||
{
|
||||
device.CopyFrame(_frame);
|
||||
_cursor = device.CursorCell;
|
||||
_cursorX = device.CursorX;
|
||||
_cursorY = device.CursorY;
|
||||
_cursorMode = device.CursorMode;
|
||||
_cellWidth = device.CellWidth;
|
||||
_cellHeight = device.CellHeight;
|
||||
_cellWidth = device.FontWidth;
|
||||
_cellHeight = device.FontHeight;
|
||||
|
||||
_anythingBlinks = _cursorMode == PlasmaCursorMode.Flashing;
|
||||
if (!_anythingBlinks)
|
||||
@@ -113,8 +114,8 @@ internal sealed class PlasmaCanvas : Control
|
||||
if (_cursorMode == PlasmaCursorMode.Steady
|
||||
|| (_cursorMode == PlasmaCursorMode.Flashing && _blinkPhase))
|
||||
{
|
||||
int cx = _cursor.Col * _cellWidth;
|
||||
int cy = _cursor.Row * _cellHeight + _cellHeight - 1;
|
||||
int cx = _cursorX;
|
||||
int cy = _cursorY + _cellHeight - 1;
|
||||
if (cy < VPlasmaDevice.Height)
|
||||
for (int i = 0; i < _cellWidth && cx + i < VPlasmaDevice.Width; ++i)
|
||||
g.FillRectangle(lit, Bezel + (cx + i) * _dotPitch, Bezel + cy * _dotPitch, _dotSize, _dotSize);
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
namespace VPlasma.Core.Device;
|
||||
|
||||
/// <summary>
|
||||
/// The display's character generator: a classic 5×7 dot-matrix font for
|
||||
/// ASCII 0x20..0x7E, stored column-major (5 column bytes per glyph, bit 0 =
|
||||
/// top row) — the layout every KS0108-era controller used. The real panel's
|
||||
/// ROM glyphs are lost with the hardware; this is the standard public-domain
|
||||
/// 5×7 set, which is what such panels shipped with. Codes outside the range
|
||||
/// render as a solid block so stream corruption is visible on the glass.
|
||||
/// </summary>
|
||||
public static class PlasmaFont
|
||||
{
|
||||
public const int GlyphWidth = 5;
|
||||
public const int GlyphHeight = 7;
|
||||
public const byte First = 0x20;
|
||||
public const byte Last = 0x7E;
|
||||
|
||||
/// <summary>
|
||||
/// Column bits for <paramref name="code"/>'s glyph. Bit r of
|
||||
/// <c>result[c]</c> is the dot at column c, row r (row 0 at the top).
|
||||
/// </summary>
|
||||
public static void GetColumns(byte code, Span<byte> columns)
|
||||
{
|
||||
if (code < First || code > Last)
|
||||
{
|
||||
columns.Slice(0, GlyphWidth).Fill(0x7F); // solid block
|
||||
return;
|
||||
}
|
||||
Glyphs.AsSpan((code - First) * GlyphWidth, GlyphWidth).CopyTo(columns);
|
||||
}
|
||||
|
||||
private static readonly byte[] Glyphs =
|
||||
{
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 ' '
|
||||
0x00, 0x00, 0x5F, 0x00, 0x00, // 0x21 '!'
|
||||
0x00, 0x07, 0x00, 0x07, 0x00, // 0x22 '"'
|
||||
0x14, 0x7F, 0x14, 0x7F, 0x14, // 0x23 '#'
|
||||
0x24, 0x2A, 0x7F, 0x2A, 0x12, // 0x24 '$'
|
||||
0x23, 0x13, 0x08, 0x64, 0x62, // 0x25 '%'
|
||||
0x36, 0x49, 0x55, 0x22, 0x50, // 0x26 '&'
|
||||
0x00, 0x05, 0x03, 0x00, 0x00, // 0x27 '''
|
||||
0x00, 0x1C, 0x22, 0x41, 0x00, // 0x28 '('
|
||||
0x00, 0x41, 0x22, 0x1C, 0x00, // 0x29 ')'
|
||||
0x08, 0x2A, 0x1C, 0x2A, 0x08, // 0x2A '*'
|
||||
0x08, 0x08, 0x3E, 0x08, 0x08, // 0x2B '+'
|
||||
0x00, 0x50, 0x30, 0x00, 0x00, // 0x2C ','
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, // 0x2D '-'
|
||||
0x00, 0x60, 0x60, 0x00, 0x00, // 0x2E '.'
|
||||
0x20, 0x10, 0x08, 0x04, 0x02, // 0x2F '/'
|
||||
0x3E, 0x51, 0x49, 0x45, 0x3E, // 0x30 '0'
|
||||
0x00, 0x42, 0x7F, 0x40, 0x00, // 0x31 '1'
|
||||
0x42, 0x61, 0x51, 0x49, 0x46, // 0x32 '2'
|
||||
0x21, 0x41, 0x45, 0x4B, 0x31, // 0x33 '3'
|
||||
0x18, 0x14, 0x12, 0x7F, 0x10, // 0x34 '4'
|
||||
0x27, 0x45, 0x45, 0x45, 0x39, // 0x35 '5'
|
||||
0x3C, 0x4A, 0x49, 0x49, 0x30, // 0x36 '6'
|
||||
0x01, 0x71, 0x09, 0x05, 0x03, // 0x37 '7'
|
||||
0x36, 0x49, 0x49, 0x49, 0x36, // 0x38 '8'
|
||||
0x06, 0x49, 0x49, 0x29, 0x1E, // 0x39 '9'
|
||||
0x00, 0x36, 0x36, 0x00, 0x00, // 0x3A ':'
|
||||
0x00, 0x56, 0x36, 0x00, 0x00, // 0x3B ';'
|
||||
0x00, 0x08, 0x14, 0x22, 0x41, // 0x3C '<'
|
||||
0x14, 0x14, 0x14, 0x14, 0x14, // 0x3D '='
|
||||
0x41, 0x22, 0x14, 0x08, 0x00, // 0x3E '>'
|
||||
0x02, 0x01, 0x51, 0x09, 0x06, // 0x3F '?'
|
||||
0x32, 0x49, 0x79, 0x41, 0x3E, // 0x40 '@'
|
||||
0x7E, 0x11, 0x11, 0x11, 0x7E, // 0x41 'A'
|
||||
0x7F, 0x49, 0x49, 0x49, 0x36, // 0x42 'B'
|
||||
0x3E, 0x41, 0x41, 0x41, 0x22, // 0x43 'C'
|
||||
0x7F, 0x41, 0x41, 0x22, 0x1C, // 0x44 'D'
|
||||
0x7F, 0x49, 0x49, 0x49, 0x41, // 0x45 'E'
|
||||
0x7F, 0x09, 0x09, 0x09, 0x01, // 0x46 'F'
|
||||
0x3E, 0x41, 0x49, 0x49, 0x7A, // 0x47 'G'
|
||||
0x7F, 0x08, 0x08, 0x08, 0x7F, // 0x48 'H'
|
||||
0x00, 0x41, 0x7F, 0x41, 0x00, // 0x49 'I'
|
||||
0x20, 0x40, 0x41, 0x3F, 0x01, // 0x4A 'J'
|
||||
0x7F, 0x08, 0x14, 0x22, 0x41, // 0x4B 'K'
|
||||
0x7F, 0x40, 0x40, 0x40, 0x40, // 0x4C 'L'
|
||||
0x7F, 0x02, 0x0C, 0x02, 0x7F, // 0x4D 'M'
|
||||
0x7F, 0x04, 0x08, 0x10, 0x7F, // 0x4E 'N'
|
||||
0x3E, 0x41, 0x41, 0x41, 0x3E, // 0x4F 'O'
|
||||
0x7F, 0x09, 0x09, 0x09, 0x06, // 0x50 'P'
|
||||
0x3E, 0x41, 0x51, 0x21, 0x5E, // 0x51 'Q'
|
||||
0x7F, 0x09, 0x19, 0x29, 0x46, // 0x52 'R'
|
||||
0x46, 0x49, 0x49, 0x49, 0x31, // 0x53 'S'
|
||||
0x01, 0x01, 0x7F, 0x01, 0x01, // 0x54 'T'
|
||||
0x3F, 0x40, 0x40, 0x40, 0x3F, // 0x55 'U'
|
||||
0x1F, 0x20, 0x40, 0x20, 0x1F, // 0x56 'V'
|
||||
0x3F, 0x40, 0x38, 0x40, 0x3F, // 0x57 'W'
|
||||
0x63, 0x14, 0x08, 0x14, 0x63, // 0x58 'X'
|
||||
0x07, 0x08, 0x70, 0x08, 0x07, // 0x59 'Y'
|
||||
0x61, 0x51, 0x49, 0x45, 0x43, // 0x5A 'Z'
|
||||
0x00, 0x7F, 0x41, 0x41, 0x00, // 0x5B '['
|
||||
0x02, 0x04, 0x08, 0x10, 0x20, // 0x5C '\'
|
||||
0x00, 0x41, 0x41, 0x7F, 0x00, // 0x5D ']'
|
||||
0x04, 0x02, 0x01, 0x02, 0x04, // 0x5E '^'
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, // 0x5F '_'
|
||||
0x00, 0x01, 0x02, 0x04, 0x00, // 0x60 '`'
|
||||
0x20, 0x54, 0x54, 0x54, 0x78, // 0x61 'a'
|
||||
0x7F, 0x48, 0x44, 0x44, 0x38, // 0x62 'b'
|
||||
0x38, 0x44, 0x44, 0x44, 0x20, // 0x63 'c'
|
||||
0x38, 0x44, 0x44, 0x48, 0x7F, // 0x64 'd'
|
||||
0x38, 0x54, 0x54, 0x54, 0x18, // 0x65 'e'
|
||||
0x08, 0x7E, 0x09, 0x01, 0x02, // 0x66 'f'
|
||||
0x0C, 0x52, 0x52, 0x52, 0x3E, // 0x67 'g'
|
||||
0x7F, 0x08, 0x04, 0x04, 0x78, // 0x68 'h'
|
||||
0x00, 0x44, 0x7D, 0x40, 0x00, // 0x69 'i'
|
||||
0x20, 0x40, 0x44, 0x3D, 0x00, // 0x6A 'j'
|
||||
0x7F, 0x10, 0x28, 0x44, 0x00, // 0x6B 'k'
|
||||
0x00, 0x41, 0x7F, 0x40, 0x00, // 0x6C 'l'
|
||||
0x7C, 0x04, 0x18, 0x04, 0x78, // 0x6D 'm'
|
||||
0x7C, 0x08, 0x04, 0x04, 0x78, // 0x6E 'n'
|
||||
0x38, 0x44, 0x44, 0x44, 0x38, // 0x6F 'o'
|
||||
0x7C, 0x14, 0x14, 0x14, 0x08, // 0x70 'p'
|
||||
0x08, 0x14, 0x14, 0x14, 0x7C, // 0x71 'q'
|
||||
0x7C, 0x08, 0x04, 0x04, 0x08, // 0x72 'r'
|
||||
0x48, 0x54, 0x54, 0x54, 0x20, // 0x73 's'
|
||||
0x04, 0x3F, 0x44, 0x40, 0x20, // 0x74 't'
|
||||
0x3C, 0x40, 0x40, 0x20, 0x7C, // 0x75 'u'
|
||||
0x1C, 0x20, 0x40, 0x20, 0x1C, // 0x76 'v'
|
||||
0x3C, 0x40, 0x30, 0x40, 0x3C, // 0x77 'w'
|
||||
0x44, 0x28, 0x10, 0x28, 0x44, // 0x78 'x'
|
||||
0x0C, 0x50, 0x50, 0x50, 0x3C, // 0x79 'y'
|
||||
0x44, 0x64, 0x54, 0x4C, 0x44, // 0x7A 'z'
|
||||
0x00, 0x08, 0x36, 0x41, 0x00, // 0x7B '{'
|
||||
0x00, 0x00, 0x7F, 0x00, 0x00, // 0x7C '|'
|
||||
0x00, 0x41, 0x36, 0x08, 0x00, // 0x7D '}'
|
||||
0x08, 0x04, 0x08, 0x10, 0x08, // 0x7E '~'
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
// AUTO-GENERATED from the PD01D221 firmware (tms27pc512.BIN) by
|
||||
// PlasmaNew tooling. The real Babcock character generator, all 8 fonts.
|
||||
// Each glyph row is a ushort; bit 15 = leftmost pixel. See PlasmaNew/FIRMWARE.md.
|
||||
namespace VPlasma.Core.Device;
|
||||
|
||||
/// <summary>The display's 8 built-in fonts, extracted from ROM.</summary>
|
||||
public static class PlasmaFonts
|
||||
{
|
||||
/// <summary>One font face: character range, cell size, and packed glyph rows.</summary>
|
||||
public sealed class Face
|
||||
{
|
||||
public int Index { get; }
|
||||
public byte First { get; }
|
||||
public byte Last { get; }
|
||||
public int Width { get; }
|
||||
public int Height { get; }
|
||||
private readonly ushort[] _rows; // (c-First)*Height + row; bit15 = leftmost
|
||||
public Face(int index, byte first, byte last, int width, int height, string hex)
|
||||
{
|
||||
Index = index; First = first; Last = last; Width = width; Height = height;
|
||||
_rows = new ushort[hex.Length / 4];
|
||||
for (int i = 0; i < _rows.Length; i++)
|
||||
_rows[i] = (ushort)((HexNibble(hex[i*4]) << 12) | (HexNibble(hex[i*4+1]) << 8) | (HexNibble(hex[i*4+2]) << 4) | HexNibble(hex[i*4+3]));
|
||||
}
|
||||
private static int HexNibble(char c) => c <= '9' ? c - '0' : (c & 0x5F) - 'A' + 10;
|
||||
/// <summary>True if this face has a glyph for <paramref name="code"/>.</summary>
|
||||
public bool Has(byte code) => code >= First && code <= Last;
|
||||
/// <summary>Row <paramref name="row"/> of glyph <paramref name="code"/> (bit15 = leftmost), or 0.</summary>
|
||||
public ushort Row(byte code, int row)
|
||||
=> Has(code) && (uint)row < (uint)Height ? _rows[(code - First) * Height + row] : (ushort)0;
|
||||
}
|
||||
|
||||
// font 0: 6x8, chars 0x20-0xFF, 224 glyphs
|
||||
private static readonly Face F0 = new(0, 0x20, 0xFF, 6, 8,
|
||||
"00000000000000000000000000000000200020002000200000000000200000005000500050000000000000000000000050005000F8005000F8005000"
|
||||
+ "5000000020007800A00070002800F00020000000C000C80010002000400098001800000060009000A0004000A8009000680000006000200040000000"
|
||||
+ "0000000000000000100020004000400040002000100000004000200010001000100020004000000000002000A8007000A80020000000000000002000"
|
||||
+ "2000F800200020000000000000000000000000006000200040000000000000000000F800000000000000000000000000000000000000600060000000"
|
||||
+ "00000800100020004000800000000000700088009800A800C80088007000000020006000200020002000200070000000700088000800100020004000"
|
||||
+ "F8000000F80010002000100008000800F00000001000300050009000F800100010000000F8008000F000080008000800F0000000300040008000F000"
|
||||
+ "8800880070000000F8000800100020004000400040000000700088008800700088008800700000007000880088007800080010006000000000006000"
|
||||
+ "600000006000600000000000000060006000000060002000400000001000200040008000400020001000000000000000F8000000F800000000000000"
|
||||
+ "80004000200010002000400080000000700088000800100020000000200000007000880008006800A800A800700000007000880088008800F8008800"
|
||||
+ "88000000F00088008800F00088008800F000000070008800800080008000880070000000E00090008800880088009000E0000000F80080008000F000"
|
||||
+ "80008000F8000000F80080008000F0008000800080000000700088008000B8008800880070000000880088008800F800880088008800000070002000"
|
||||
+ "2000200020002000700000003800100010001000100090006000000088009000A000C000A000900088000000800080008000800080008000F8000000"
|
||||
+ "8800D800A800A800880088008800000088008800C800A800980088008800000070008800880088008800880070000000F00088008800F00080008000"
|
||||
+ "800000007000880088008800A800900068000000F00088008800F000A000900088000000780080008000700008000800F0000000F800200020002000"
|
||||
+ "20002000200000008800880088008800880088007000000088008800880088008800500020000000880088008800A800A800A8005000000088008800"
|
||||
+ "50002000500088008800000088008800880050002000200020000000F80008001000200040008000F800000070004000400040004000400070000000"
|
||||
+ "000080004000200010000800000000007000100010001000100010007000000020005000880000000000000000000000000000000000000000000000"
|
||||
+ "FC000000600040002000000000000000000000000000000070000800780088007800000080008000B000C80088008800F00000000000000070008000"
|
||||
+ "8000880070000000080008006800980088008800780000000000000070008800F8008000700000001000280020007000200020002000000000000000"
|
||||
+ "780088007800080070000000800080008000B000C8008800880000000000200000002000200020002000000010000000300010001000900060000000"
|
||||
+ "800080009000A000C000A000900000006000200020002000200020007000000000000000D000A800A80088008800000000000000B000C80088008800"
|
||||
+ "88000000000000007000880088008800700000000000B000C8008800F0008000800000000000680098008800780008000800000000000000B000C800"
|
||||
+ "8000800080000000000000007800800070000800F000000000002000F800200020002800100000000000000088008800880098006800000000000000"
|
||||
+ "8800880088005000200000000000000088008800A800A800500000000000000088005000200050008800000000000000880050002000200040000000"
|
||||
+ "00000000F800100020004000F80000003000400040008000400040003000000020002000200000002000200020000000600010001000080010001000"
|
||||
+ "6000000008007000800000000000000000000000FC00FC00FC00FC00FC00FC00FC00FC00000000000000F80000000000000000002000200020002000"
|
||||
+ "2000200020002000200020002000E0000000000000000000000000000000700020002000200020002000200020007000000000000000000000000000"
|
||||
+ "0000E000200020002000200020002000200070002000200020002000200020002000E0002000200020002000200020002000F8002000200020002000"
|
||||
+ "200020002000F8000000000000000000000000000000F800200020002000200000000000000008001000200020002000000000000000800040002000"
|
||||
+ "200020002000200040008000000000000000000020002000100008000000000000000000600060006000F800F800600060006000000000000000F800"
|
||||
+ "F80000000000000060006000600040006000600060007000600060006000E000E0000000000000000000000000007800780060006000700060006000"
|
||||
+ "600078007800000000000000000000000000E000E00060006000600060006000600078007800600060006000600060006000E000E000600060006000"
|
||||
+ "000020007000A80020002000200020000000000040002000F0002000400000000000000020004000F8004000200000000000000020002000E0000000"
|
||||
+ "000000000000200020002000A80070002000000000002000500088005000500070000000000020007000F800700070007000000000004000F800F800"
|
||||
+ "F800400000000000000090009000080008009000E00000000000000000000000E000A000E00000000000700040004000400000000000000000000000"
|
||||
+ "0000400040004000C000000000000000000000008000400020000000000000000000C000C0000000000000000000F8000800F8000800100020000000"
|
||||
+ "00000000F8000800300020004000000000000000100020006000A00020000000000000002000F8008800080030000000000000000000F80020002000"
|
||||
+ "F8000000000000001000F8003000500090000000000000004000F8004800500040000000000000000000700010001000F800000000000000F0001000"
|
||||
+ "F0001000F0000000000000000000A800A800080030000000000000000000F0000000000000000000F800080028003000200020004000000008001000"
|
||||
+ "20006000A0002000200000002000F800880088000800100020000000F80020002000200020002000F80000001000F800100030005000900010000000"
|
||||
+ "4000F8004800500048004800900000002000F8002000F800200020002000000000000000780050008800100020000000400078009000100010001000"
|
||||
+ "200000000000F8000800080008000800F80000005000F800500050001000200040000000C0000000C800080008001000600000000000F80008001000"
|
||||
+ "20005000880000004000F800C8005000400040003800000000008800880048000800100060000000000078005000A8001C001000600000001000E000"
|
||||
+ "2000F80020002000400000000000A800A800A800080010002000000070000000F80020002000200040000000800080008000C000A000800080000000"
|
||||
+ "20002000F80020002000400080000000000070000000000000000000F80000000000F8000800500020005000800000002000F800100020007000A800"
|
||||
+ "20000000100010001000100010002000400000000000000040002000900090009000000080008000F800800080008000780000000000F80008000800"
|
||||
+ "080010006000000000004000A000100008000800000000002000F80020002000A800A800200000000000F8000800080050002000100000000000E000"
|
||||
+ "0000E0000000E0001000000000002000400080008800F80008000000000008000800500020005000800000000000F8004000F8004000400038000000"
|
||||
+ "40004000F80048005000400040000000000070001000100010001000F80000000000F8000800F80008000800F800000070000000F800080010002000"
|
||||
+ "40000000900090009000900010002000400000002000A000A000A000A800A8003000000080008000800088009000A000C000000000000000F8008800"
|
||||
+ "88008800F80000000000F8008800080008001000200000000000C0000000080008001000E0000000000020009000400000000000000000000000E000"
|
||||
+ "A000E00000000000000000002000000020004000800090006000000010002000700088008800F8008800000080004000700088008800F80088000000"
|
||||
+ "50000000700088008800F800880000007000880080008000880070002000600010002000F0008000C0008000F000000080004000F0008000C0008000"
|
||||
+ "F00000007000880070002000200020007000000050000000700088008800880070000000500000008800880088008800700000000800100070000800"
|
||||
+ "780088007000000070008800700008007800880070000000800040007000080078008800700000005000000070000800780088007000000000007000"
|
||||
+ "8000800088007000200060001000000070008800F8008000700000007000880070008800F0008000700000008000000070008800F000800070000000"
|
||||
+ "080050000000C00040004000E00000007000A80000006000200020007000000080005000000030001000100038000000500020000000600020002000"
|
||||
+ "700000006800B8000000B000C80088008800000008001000000070008800880070000000700088000000700088008800700000008000400000007000"
|
||||
+ "880088007000000000005000000070008800880070000000F0008800B00088008800D000B00000002000400000008800880098006800000070008800"
|
||||
+ "0000880088009800680000008000400000008800880098006800000000005000000088008800980068000000");
|
||||
|
||||
// font 1: 6x8, chars 0x40-0x7F, 64 glyphs
|
||||
private static readonly Face F1 = new(1, 0x40, 0x7F, 6, 8,
|
||||
"100020004000200010000000F8000000400020001000200040000000F800000020002000F800200020000000F8000000000020000000F80000002000"
|
||||
+ "00000000200050005000500088008800F800000008001000F8002000F80040008000000020007000A800A800A800A80070002000F800400040004000"
|
||||
+ "40004000E00000000000F8000000F8000000F80000000000080070008000080070008000000000000800700080000000F80000000000000004000400"
|
||||
+ "4800A8002800100010000000200050005000500088008800880000007000880008006800980088009000600000000000880050002000500088000000"
|
||||
+ "000020007000F800700020000000000020007000A800A000A8007000200000000000F800880088008800880088000000700088008800A80088008800"
|
||||
+ "70000000000000005000A8005000000000000000F80040002000100020004000F8000000300048004800300000000000000000000000000000000000"
|
||||
+ "000000000000000000000000000000000000000000000000000070008800880088005000D8000000F80000000000700000000000F8000000A800A800"
|
||||
+ "A80070002000200020000000000010007000A800A800A8007000400000000000000000000000000000000000F4005C00540054005400000000000000"
|
||||
+ "20007000A8002000200020002000200020002000200020002000A80070002000300048004000F00040004800B0000000000000000000680090009000"
|
||||
+ "6800000070008800880070008800C800B000000000008000880050002000500088000800700088004000200050008800880070000000000070008800"
|
||||
+ "6000880070000000000020007000A800A800A800700020000000000088008800500050007000200000000000B000C800880088008800080000000000"
|
||||
+ "00006000200020003000000000001000A800A800A80070002000000000000000480050006000500048000000C0004000200010002800440044000000"
|
||||
+ "0000000088008800C800B000800080000000000000008800880050002000000000000000700088008800880070000000880050002000F8002000F800"
|
||||
+ "2000000000000000F80050005000500048000000700088008800F80088008800700000000000000070008800C800B000800080000000000078009000"
|
||||
+ "8800880070000000000008007000A00020002000100000000000000000009000480048003000000000000000F80050008800A8005000000000000000"
|
||||
+ "000050008800A80050000000400030004000300040004000380008002000A800A8007000200020002000000040003000400040004000400038000800"
|
||||
+ "000010000800FC000800100000000000000020004000FC0040002000000000000000000000000000000000000000000020007000A80020002000A800"
|
||||
+ "70002000");
|
||||
|
||||
// font 2: 6x10, chars 0x20-0xFF, 224 glyphs
|
||||
private static readonly Face F2 = new(2, 0x20, 0xFF, 6, 10,
|
||||
"000000000000000000000000000000000000000000002000200020002000000000002000000000000000500050005000000000000000000000000000"
|
||||
+ "000050005000F8005000F8005000500000000000000020007800A00070002800F0002000000000000000C000C8001000200040009800180000000000"
|
||||
+ "000060009000A0004000A800900068000000000000006000200040000000000000000000000000000000100020004000400040002000100000000000"
|
||||
+ "0000400020001000100010002000400000000000000000002000A8007000A80020000000000000000000000020002000F80020002000000000000000"
|
||||
+ "00000000000000000000000060002000400000000000000000000000F800000000000000000000000000000000000000000000006000600000000000"
|
||||
+ "00000000080010002000400080000000000000000000700088009800A800C80088007000000000000000200060002000200020002000700000000000"
|
||||
+ "0000700088000800100020004000F800000000000000F80010002000100008000800F0000000000000001000300050009000F8001000100000000000"
|
||||
+ "0000F8008000F000080008000800F000000000000000300040008000F000880088007000000000000000F80008001000200040004000400000000000"
|
||||
+ "000070008800880070008800880070000000000000007000880088007800080010006000000000000000000060006000000060006000000000000000"
|
||||
+ "00000000600060000000600020004000000000000000100020004000800040002000100000000000000000000000F8000000F8000000000000000000"
|
||||
+ "0000800040002000100020004000800000000000000070008800080010002000000020000000000000007000880008006800A800A800700000000000"
|
||||
+ "00007000880088008800F80088008800000000000000F00088008800F00088008800F000000000000000700088008000800080008800700000000000"
|
||||
+ "0000E00090008800880088009000E000000000000000F80080008000F00080008000F800000000000000F80080008000F00080008000800000000000"
|
||||
+ "0000700088008000B800880088007000000000000000880088008800F800880088008800000000000000700020002000200020002000700000000000"
|
||||
+ "0000380010001000100010009000600000000000000088009000A000C000A00090008800000000000000800080008000800080008000F80000000000"
|
||||
+ "00008800D800A800A80088008800880000000000000088008800C800A800980088008800000000000000700088008800880088008800700000000000"
|
||||
+ "0000F00088008800F0008000800080000000000000007000880088008800A80090006800000000000000F00088008800F000A0009000880000000000"
|
||||
+ "0000780080008000700008000800F000000000000000F800200020002000200020002000000000000000880088008800880088008800700000000000"
|
||||
+ "00008800880088008800880050002000000000000000880088008800A800A800A8005000000000000000880088005000200050008800880000000000"
|
||||
+ "00008800880088005000200020002000000000000000F80008001000200040008000F800000000000000700040004000400040004000700000000000"
|
||||
+ "000080004000200010000800000000000000000000007000100010001000100010007000000000000000200050008800000000000000000000000000"
|
||||
+ "0000000000000000000000000000FC000000000000006000400020000000000000000000000000000000000000007000080078008800780000000000"
|
||||
+ "000080008000B000C80088008800F0000000000000000000000070008000800088007000000000000000080008003400980088008800780000000000"
|
||||
+ "00000000000070008800F800800070000000000000001000280020007000200020002000000000000000000000007800880088007800080008007000"
|
||||
+ "0000800080008000B000C800880088000000000000000000200000002000200020002000000000000000100000003000100010001000100090006000"
|
||||
+ "0000800080009000A000C000A0009000000000000000600020002000200020002000700000000000000000000000D000A800A8008800880000000000"
|
||||
+ "000000000000B000C800880088008800000000000000000000007000880088008800700000000000000000000000B000C8008800F000800080008000"
|
||||
+ "0000000000006800980088007800080008000800000000000000B000C800800080008000000000000000000000007800800070000800F00000000000"
|
||||
+ "000000002000F80020002000280010000000000000000000000088008800880098006800000000000000000000008800880088005000200000000000"
|
||||
+ "00000000000088008800A800A80050000000000000000000000088005000200050008800000000000000000000008800880088007800080008007000"
|
||||
+ "000000000000F800100020004000F8000000000000003000400040008000400040003000000000000000200020002000000020002000200000000000"
|
||||
+ "00006000100010000800100010006000000000000000080070008000000000000000000000000000FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00"
|
||||
+ "0000000000000000FC000000000000000000000010001000100010001000100010001000100010001000100010001000F00000000000000000000000"
|
||||
+ "00000000000000001C001000100010001000100010001000100010001C00000000000000000000000000000000000000F00010001000100010001000"
|
||||
+ "10001000100010001C00100010001000100010001000100010001000F000100010001000100010001000100010001000FC0010001000100010001000"
|
||||
+ "1000100010001000FC00000000000000000000000000000000000000FC00100010001000100010000000000000000000040008001000100010001000"
|
||||
+ "0000000000000000C000200010001000100010001000100010002000C000000000000000000000001000100010000800040000000000000000000000"
|
||||
+ "380038003800FC00FC00FC003800380038003800000000000000FC00FC00FC0000000000000000003800380038003800380038003800380038003800"
|
||||
+ "380038003800F800F800F80000000000000000000000000000003C003C003C0038003800380038003800380038003C003C003C000000000000000000"
|
||||
+ "000000000000F800F800F80038003800380038003800380038003C003C003C003800380038003800380038003800F800F800F8003800380038003800"
|
||||
+ "000000001000380054001000100010001000000000000000000020001000F8001000200000000000000000000000100020007C002000100000000000"
|
||||
+ "00000000000010001000F000000000000000000000000000100010001000540038001000000000000000000010002800440028002800380000000000"
|
||||
+ "00000000100038007C00380038003800000000000000000020007C00FC007C0020000000000000000000000048004800840084004800300000000000"
|
||||
+ "000000000000000000007000500070000000000000000000380020002000200000000000000000000000000000000000200020002000E00000000000"
|
||||
+ "00000000000000000000400020001000000000000000000000000000600060000000000000000000000000007C0004007C0004000800100000000000"
|
||||
+ "0000000000007C00040018001000200000000000000000000000080010003000500010000000000000000000000010007C0044000400180000000000"
|
||||
+ "00000000000000007C00100010007C000000000000000000000008007C001800280048000000000000000000000020007C0024002800200000000000"
|
||||
+ "00000000000000003800080008007C000000000000000000000078000800780008007800000000000000000000000000540054000400180000000000"
|
||||
+ "000000000000000078000000000000000000000000007C00040014001800100010002000000000000000040008001000300050001000100000000000"
|
||||
+ "000010007C00440044000400080010000000000000007C00100010001000100010007C0000000000000008007C000800180028004800080000000000"
|
||||
+ "000020007C002400240024002400480000000000000010007C0010007C00100010001000000000000000000000003C00240044000800100000000000"
|
||||
+ "000020003C004800080008000800100000000000000000007C0004000400040004007C0000000000000028007C002800280008001000200000000000"
|
||||
+ "0000600000006400040004000800300000000000000000007C000400080010002800440000000000000020007C0024002800200020001C0000000000"
|
||||
+ "0000000044004400240004000800300000000000000000003C00240054000C00080030000000000000000800700010007C0010001000200000000000"
|
||||
+ "00000000540054005400040008001000000000000000380000007C001000100010002000000000000000400040004000600050004000400000000000"
|
||||
+ "0000100010007C0010001000200040000000000000000000380000000000000000007C0000000000000000007C000400280010002800400000000000"
|
||||
+ "000010007C00080010003800540010000000000000000800080008000800080010002000000000000000000000002000100048004800480000000000"
|
||||
+ "0000400040007C004000400040003C0000000000000000007C0004000400040008003000000000000000000020005000080004000400000000000000"
|
||||
+ "000010007C001000100054005400100000000000000000007C0004000400280010000800000000000000000070000000700000007000080000000000"
|
||||
+ "0000000010002000400044007C000400000000000000000004000400280010002800400000000000000000007C0020007C00200020001C0000000000"
|
||||
+ "0000200020007C0024002800200020000000000000000000380008000800080008007C0000000000000000007C0004007C00040004007C0000000000"
|
||||
+ "0000380000007C0004000800100020000000000000004800480048004800080010002000000000000000100050005000500054005400180000000000"
|
||||
+ "00004000400040004400480050006000000000000000000000007C004400440044007C0000000000000000007C004400040004000800100000000000"
|
||||
+ "000000006000000004000400080070000000000000000000100048002000000000000000000000000000000070005000700000000000000000000000"
|
||||
+ "000010000000100020004000480030000000000008001000700088008800F800880088000000000080004000700088008800F8008800880000000000"
|
||||
+ "50000000700088008800F8008800880000000000000070008800800080008000880070002000600008001000F8008000F00080008000F80000000000"
|
||||
+ "80004000F8008000F00080008000F8000000000070008800700020002000200020007000000000005000000070008800880088008800700000000000"
|
||||
+ "500000008800880088008800880070000000000008001000000070000800780088007800000000007000880000007000080078008800780000000000"
|
||||
+ "800040000000700008007800880078000000000000005000000070000800780088007800000000000000000000007000800080008800700020006000"
|
||||
+ "08001000000070008800F800800070000000000070008800000070008800F000800070000000000080004000000070008800F8008000700000000000"
|
||||
+ "0800100040000000C00040004000E0000000000070008800200000006000200020007000000000008000400010000000300010001000380000000000"
|
||||
+ "28000000100000003000100010003800000000006800B80000007000C800880088008800000000000800100000007000880088008800700000000000"
|
||||
+ "700088000000700088008800880070000000000080004000000070008800880088007000000000000000500000007000880088008800700000000000"
|
||||
+ "0000780044005800440044006400D8000000000010002000000088008800880098006800000000007000880000008800880088009800680000000000"
|
||||
+ "80004000000088008800880098006800000000000000500000008800880088009800680000000000");
|
||||
|
||||
// font 3: 6x10, chars 0x40-0x7F, 64 glyphs
|
||||
private static readonly Face F3 = new(3, 0x40, 0x7F, 6, 10,
|
||||
"0000100020004000200010000000F800000000000000400020001000200040000000F80000000000000020002000F800200020000000F80000000000"
|
||||
+ "0000000020000000F800000020000000000000000000200050005000500088008800F80000000000000008001000F8002000F8004000800000000000"
|
||||
+ "20007000A800A800A800A800A8007000200000000000F80040004000400040004000E0000000000000000000F8000000F8000000F800000000000000"
|
||||
+ "000008007000800008007000800000000000000000000800700080000000F800000000000000000000000400040004004800A8002800100010000000"
|
||||
+ "0000200050005000500088008800880000000000000070008800080068008C0088009000600000000000000000008800500020005000880000000000"
|
||||
+ "0000000020007000F80070002000000000000000000020007000A800A000A80070002000000000000000F80088008800880088008800880000000000"
|
||||
+ "0000700088008800A800880088007000000000000000000000005000A800500000000000000000000000F80040002000100020004000F80000000000"
|
||||
+ "000030004800480030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
+ "0000000070008800880088005000D800000000000000F80000000000700000000000F800000000000000A800A800A800700020002000200000000000"
|
||||
+ "0000100010007000A800A800A80070004000400000000000000000000000000000000000000000000000F4005C005400540000000000000000000000"
|
||||
+ "20007000A80020002000200020002000200020002000200020002000200020002000A800700020000000300048004000F00040004800B00000000000"
|
||||
+ "00000000000000006800900090006800000000000000700088008800F0008800C800B000800000000000000080008800500020005000880008000000"
|
||||
+ "000070008800400020005000880088007000000000000000000070008800600088007000000000000000000020007000A800A800A800700020000000"
|
||||
+ "0000000000008800880050005000700020002000000000000000B000C800880088008800080000000000000000000000600020002000300000000000"
|
||||
+ "0000000000001000A800A800A80070002000000000000000000048005000600050004800000000000000C00040002000200050008800880000000000"
|
||||
+ "00000000000088008800C800B00080008000000000000000000000008800880050002000000000000000000000007000880088008800700000000000"
|
||||
+ "0000880050002000F8002000F800200000000000000000000000F8005000500050004800000000000000700088008800F80088008800700000000000"
|
||||
+ "00000000000070008800C800B00080008000000000000000000078009000880088007000000000000000000008007000A00020002000100000000000"
|
||||
+ "0000000000008000500048004800300000000000000000000000F80050008800A800500000000000000000000000000050008800A800500000000000"
|
||||
+ "0000400030004000300040004000380008000000000000002000A800A800A80070002000200000000000400030004000400040004000380008000000"
|
||||
+ "0000000010000800FC00080010000000000000000000000020004000FC00400020000000000000000000000000000000000000000000000000000000"
|
||||
+ "1000380054001000100010001000540038001000");
|
||||
|
||||
// font 4: 12x16, chars 0x20-0x7F, 96 glyphs
|
||||
private static readonly Face F4 = new(4, 0x20, 0x7F, 12, 16,
|
||||
"00000000000000000000000000000000000000000000000000000000000000000C000C000C000C000C000C000C000C000C000C00000000000C000C00"
|
||||
+ "0000000033003300330033003300330000000000000000000000000000000000000000003300330033003300FFC0FFC033003300FFC0FFC033003300"
|
||||
+ "33003300000000000C000C003FC07FC0CC00CC007F003F800CC00CC0FF80FF000C000C0000000000F000F000F0C0F1C0038007000E001C0038007000"
|
||||
+ "E3C0C3C003C003C0000000003C007E00C300C700CE00DC0078007800DCC0CFC0C700C7007EC03CC0000000001E001E00060006001C00180000000000"
|
||||
+ "0000000000000000000000000000000000C001C0038007000E000C000C000C000C000E000700038001C000C000000000300038001C000E0007000300"
|
||||
+ "03000300030007000E001C003800300000000000000000000C000C00CCC0EDC07F807F80EDC0CCC00C000C000000000000000000000000000C000C00"
|
||||
+ "0C000C00FFC0FFC00C000C000C000C000000000000000000000000000000000000000000000000000F000F00030003000E000C000000000000000000"
|
||||
+ "0000000000000000FFC0FFC00000000000000000000000000000000000000000000000000000000000000000000000000F000F000F000F0000000000"
|
||||
+ "0000000000C001C0038007000E001C0038007000E000C00000000000000000003F007F80E1C0C0C0C1C0C2C0C4C0C8C0D0C0E0C0C0C0E1C07F803F00"
|
||||
+ "000000000C001C003C000C000C000C000C000C000C000C000C000C003F003F00000000003F007F80E1C0C0C000C001C0038007000E001C0038007000"
|
||||
+ "FFC0FFC000000000FFC0FFC0038007000E000E000700038001C000C000C001C0FF80FF000000000007000F001F003B0033006300E300C300FFC0FFC0"
|
||||
+ "030003000300030000000000FFC0FFC0C000C000C000C000FF00FF8001C000C000C001C0FF80FF00000000000F001F0038007000E000C000FF00FF80"
|
||||
+ "C1C0C0C0C0C0E1C07F803F0000000000FFC0FFC000C001C0038007000E001C00380030003000300030003000000000003F007F80E1C0C0C0C0C0E1C0"
|
||||
+ "7F807F80E1C0C0C0C0C0E1C07F803F00000000003F007F80E1C0C0C0C0C0E0C07FC03FC000C001C0038007003E003C0000000000000000000F000F00"
|
||||
+ "0F000F00000000000F000F000F000F000000000000000000000000000F000F000F000F00000000000F000F00030003000E000C000000000000C001C0"
|
||||
+ "038007000E001C00380038001C000E000700038001C000C0000000000000000000000000FFC0FFC000000000FFC0FFC0000000000000000000000000"
|
||||
+ "300038001C000E000700038001C001C0038007000E001C0038003000000000003F007F80E1C0C0C000C001C0038007000E000C00000000000C000C00"
|
||||
+ "000000003F007F80E1C0C0C000C000C038C07CC0CCC0CCC0CCC0CCC07F803F00000000003F007F80E1C0C0C0C0C0C0C0C0C0C0C0FFC0FFC0C0C0C0C0"
|
||||
+ "C0C0C0C000000000FF00FF80C1C0C0C0C0C0C1C0FF80FF80C1C0C0C0C0C0C1C0FF80FF00000000003F007F80E1C0C0C0C0C0C000C000C000C000C0C0"
|
||||
+ "C0C0C1C07F803F0000000000FF00FF80C1C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C1C0FF80FF0000000000FFC0FFC0C000C000C000C000FF00FF00"
|
||||
+ "C000C000C000C000FFC0FFC000000000FFC0FFC0C000C000C000C000FF00FF00C000C000C000C000C000C000000000003F007F80E1C0C0C0C000C000"
|
||||
+ "C000C7C0C7C0C0C0C0C0E1C07F803F0000000000C0C0C0C0C0C0C0C0C0C0C0C0FFC0FFC0C0C0C0C0C0C0C0C0C0C0C0C0000000003F003F000C000C00"
|
||||
+ "0C000C000C000C000C000C000C000C003F003F00000000000FC00FC003000300030003000300030003000300C300E7007E003C0000000000C0C0C1C0"
|
||||
+ "C380C700CE00DC00F800F800DC00CE00C700C380C1C0C0C000000000C000C000C000C000C000C000C000C000C000C000C000C000FFC0FFC000000000"
|
||||
+ "C0C0E1C0F3C0F3C0DEC0CCC0CCC0C0C0C0C0C0C0C0C0C0C0C0C0C0C000000000C0C0C0C0C0C0E0C0F0C0F8C0DCC0CEC0C7C0C3C0C1C0C0C0C0C0C0C0"
|
||||
+ "000000003F007F80E1C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0E1C07F803F0000000000FF00FF80C1C0C0C0C0C0C1C0FF80FF00C000C000C000C000"
|
||||
+ "C000C000000000003F007F80E1C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C2C0E1C07F803F4000000000FF00FF80C1C0C0C0C0C0C1C0FF80FF00DC00CE00"
|
||||
+ "C700C380C1C0C0C0000000003FC07FC0E000C000C000E0007F003F8001C000C000C001C0FF80FF0000000000FFC0FFC00C000C000C000C000C000C00"
|
||||
+ "0C000C000C000C000C000C0000000000C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0E1C07F803F0000000000C0C0C0C0C0C0C0C0C0C0C0C0"
|
||||
+ "C0C0C0C0C0C0E1C0738033001E000C0000000000C0C0C0C0C0C0C0C0CCC0CCC0CCC0CCC0CCC0CCC0CCC0CCC07F80330000000000C0C0C0C0C0C0E1C0"
|
||||
+ "73803F001E001E003F007380E1C0C0C0C0C0C0C000000000C0C0C0C0C0C0C0C0C0C0E1C073803F001E000C000C000C000C000C0000000000FFC0FFC0"
|
||||
+ "00C001C0038007000E001C0038007000E000C000FFC0FFC0000000003F003F0030003000300030003000300030003000300030003F003F0000000000"
|
||||
+ "00000000C000E000700038001C000E000700038001C000C000000000000000003F003F0003000300030003000300030003000300030003003F003F00"
|
||||
+ "000000000C001E003F007380E1C0C0C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
+ "FFC0FFC0000000000C000E000700038001C000C0000000000000000000000000000000000000000000000000000000003F003F8000C000C03FC07FC0"
|
||||
+ "C0C0C0C07FC03FC000000000C000C000C000C000C000C000DF00FF80F1C0C0C0C0C0C1C0FF80FF000000000000000000000000003F007F80E1C0C0C0"
|
||||
+ "C000C000C0C0E1C07F803F000000000000C000C000C000C000C000C03EC07FC0E3C0C0C0C0C0E0C07FC03FC00000000000000000000000003F007F80"
|
||||
+ "C0C0C0C0FF80FF00C000C0007F003F00000000000F001F8039C030C030003000FC00FC00300030003000300030003000000000000000000000000000"
|
||||
+ "3FC07FC0C0C0C0C07FC03FC000C000C0FF80FF0000000000C000C000C000C000C000C000DF00FF80E1C0C0C0C0C0C0C0C0C0C0C0000000000C000C00"
|
||||
+ "000000003C003C000C000C000C000C000C000C003F003F000000000000C000C00000000000C000C000C000C000C000C030C039C01F800F0000000000"
|
||||
+ "300030003000300030C031C0338037003E003E003700338031C030C0000000003C003C000C000C000C000C000C000C000C000C000C000C003F003F00"
|
||||
+ "000000000000000000000000D300FF80FFC0CCC0CCC0CCC0CCC0CCC0CCC0CCC0000000000000000000000000CF00FF80F1C0C0C0C0C0C0C0C0C0C0C0"
|
||||
+ "C0C0C0C00000000000000000000000003F007F80E1C0C0C0C0C0C0C0C0C0E1C07F803F000000000000000000DF00FF80F1C0C0C0C0C0C1C0FF80FF00"
|
||||
+ "C000C000C000C00000000000000000003EC07FC0E3C0C0C0C0C0E0C07FC03FC000C000C000C000C0000000000000000000000000DF00FF80F1C0C0C0"
|
||||
+ "C000C000C000C000C000C0000000000000000000000000003FC07FC0C000C0007F003F8000C000C0FF80FF000000000030003000FC00FC0030003000"
|
||||
+ "300030003000300030C039C01F800F00000000000000000000000000C0C0C0C0C0C0C0C0C0C0C0C0C0C0E3C07FC03CC0000000000000000000000000"
|
||||
+ "C0C0C0C0C0C0C0C0C0C0E1C0738033001E000C00000000000000000000000000C0C0C0C0C0C0C0C0C0C0CCC0CCC0CCC07F8033000000000000000000"
|
||||
+ "00000000C0C0E1C073803F001E001E003F007380E1C0C0C0000000000000000000000000C0C0E1C073803F001E000C000C001C003800300000000000"
|
||||
+ "0000000000000000FFC0FFC0030007000E001C0038007000FFC0FFC00000000000C001C003800300030006000C000C00060003000300038001C000C0"
|
||||
+ "000000000C000C000C000C000C000C00000000000C000C000C000C000C000C0000000000300038001C000C000C0006000300030006000C000C001C00"
|
||||
+ "380030000000000000C001C00F801F00380030000000000000000000000000000000000000000000FFF0FFF0FFF0FFF0FFF0FFF0FFF0FFF0FFF0FFF0"
|
||||
+ "FFF0FFF0FFF0FFF0FFF0FFF0");
|
||||
|
||||
// font 5: 12x20, chars 0x20-0x7F, 96 glyphs
|
||||
private static readonly Face F5 = new(5, 0x20, 0x7F, 12, 20,
|
||||
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C000C000C000C000C000C000C000C00"
|
||||
+ "0C000C00000000000C000C00000000000000000000000000330033003300330033003300000000000000000000000000000000000000000000000000"
|
||||
+ "000000003300330033003300FFC0FFC033003300FFC0FFC033003300330033000000000000000000000000000C000C003FC07FC0CC00CC007F003F80"
|
||||
+ "0CC00CC0FF80FF000C000C00000000000000000000000000F000F000F0C0F1C0038007000E001C0038007000E3C0C3C003C003C00000000000000000"
|
||||
+ "000000003C007E00C300C700CE00DC0078007800DCC0CFC0C700C7007EC03CC00000000000000000000000001E001E00060006001C00180000000000"
|
||||
+ "00000000000000000000000000000000000000000000000000C001C0038007000E000C000C000C000C000E000700038001C000C00000000000000000"
|
||||
+ "00000000300038001C000E000700030003000300030007000E001C0038003000000000000000000000000000000000000C000C00CCC0EDC07F807F80"
|
||||
+ "EDC0CCC00C000C0000000000000000000000000000000000000000000C000C000C000C00FFC0FFC00C000C000C000C00000000000000000000000000"
|
||||
+ "000000000000000000000000000000000000000000000000000000000F000F00030003000E000C0000000000000000000000000000000000FFC0FFC0"
|
||||
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F000F000F000F000000000000000000"
|
||||
+ "000000000000000000C001C0038007000E001C0038007000E000C000000000000000000000000000000000003F007F80E1C0C0C0C1C0C2C0C4C0C8C0"
|
||||
+ "D0C0E0C0C0C0E1C07F803F000000000000000000000000000C001C003C000C000C000C000C000C000C000C000C000C003F003F000000000000000000"
|
||||
+ "000000003F007F80E1C0C0C000C001C0038007000E001C0038007000FFC0FFC0000000000000000000000000FFC0FFC0038007000E000E0007000380"
|
||||
+ "01C000C000C001C0FF80FF0000000000000000000000000007000F001F003B0033006300E300C300FFC0FFC003000300030003000000000000000000"
|
||||
+ "00000000FFC0FFC0C000C000C000C000FF00FF8001C000C000C001C0FF80FF000000000000000000000000000F001F0038007000E000C000FF00FF80"
|
||||
+ "C1C0C0C0C0C0E1C07F803F00000000000000000000000000FFC0FFC000C001C0038007000E001C003800300030003000300030000000000000000000"
|
||||
+ "000000003F007F80E1C0C0C0C0C0E1C07F807F80E1C0C0C0C0C0E1C07F803F000000000000000000000000003F007F80E1C0C0C0C0C0E0C07FC03FC0"
|
||||
+ "00C001C0038007003E003C00000000000000000000000000000000000F000F000F000F00000000000F000F000F000F00000000000000000000000000"
|
||||
+ "00000000000000000F000F000F000F00000000000F000F00030003000E000C0000000000000000000000000000C001C0038007000E001C0038003800"
|
||||
+ "1C000E000700038001C000C00000000000000000000000000000000000000000FFC0FFC000000000FFC0FFC000000000000000000000000000000000"
|
||||
+ "00000000300038001C000E000700038001C001C0038007000E001C00380030000000000000000000000000003F007F80E1C0C0C000C001C003800700"
|
||||
+ "0E000C00000000000C000C000000000000000000000000003F007F80E1C0C0C000C000C038C07CC0CCC0CCC0CCC0CCC07F803F000000000000000000"
|
||||
+ "000000003F007F80E1C0C0C0C0C0C0C0C0C0C0C0FFC0FFC0C0C0C0C0C0C0C0C0000000000000000000000000FF00FF80C1C0C0C0C0C0C1C0FF80FF80"
|
||||
+ "C1C0C0C0C0C0C1C0FF80FF000000000000000000000000003F007F80E1C0C0C0C0C0C000C000C000C000C0C0C0C0C1C07F803F000000000000000000"
|
||||
+ "00000000FF00FF80C1C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C1C0FF80FF00000000000000000000000000FFC0FFC0C000C000C000C000FF00FF00"
|
||||
+ "C000C000C000C000FFC0FFC0000000000000000000000000FFC0FFC0C000C000C000C000FF00FF00C000C000C000C000C000C0000000000000000000"
|
||||
+ "000000003F007F80E1C0C0C0C000C000C000C7C0C7C0C0C0C0C0E1C07F803F00000000000000000000000000C0C0C0C0C0C0C0C0C0C0C0C0FFC0FFC0"
|
||||
+ "C0C0C0C0C0C0C0C0C0C0C0C00000000000000000000000003F003F000C000C000C000C000C000C000C000C000C000C003F003F000000000000000000"
|
||||
+ "000000000FC00FC003000300030003000300030003000300C300E7007E003C00000000000000000000000000C0C0C1C0C380C700CE00DC00F800F800"
|
||||
+ "DC00CE00C700C380C1C0C0C0000000000000000000000000C000C000C000C000C000C000C000C000C000C000C000C000FFC0FFC00000000000000000"
|
||||
+ "00000000C0C0E1C0F3C0F3C0DEC0CCC0CCC0C0C0C0C0C0C0C0C0C0C0C0C0C0C0000000000000000000000000C0C0C0C0C0C0E0C0F0C0F8C0DCC0CEC0"
|
||||
+ "C7C0C3C0C1C0C0C0C0C0C0C00000000000000000000000003F007F80E1C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0E1C07F803F000000000000000000"
|
||||
+ "00000000FF00FF80C1C0C0C0C0C0C1C0FF80FF00C000C000C000C000C000C0000000000000000000000000003F007F80E1C0C0C0C0C0C0C0C0C0C0C0"
|
||||
+ "C0C0C0C0C2C0E1C07F803F40000000000000000000000000FF00FF80C1C0C0C0C0C0C1C0FF80FF00DC00CE00C700C380C1C0C0C00000000000000000"
|
||||
+ "000000003FC07FC0E000C000C000E0007F003F8001C000C000C001C0FF80FF00000000000000000000000000FFC0FFC00C000C000C000C000C000C00"
|
||||
+ "0C000C000C000C000C000C00000000000000000000000000C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0E1C07F803F000000000000000000"
|
||||
+ "00000000C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0E1C0738033001E000C00000000000000000000000000C0C0C0C0C0C0C0C0CCC0CCC0CCC0CCC0"
|
||||
+ "CCC0CCC0CCC0CCC07F803300000000000000000000000000C0C0C0C0C0C0E1C073803F001E001E003F007380E1C0C0C0C0C0C0C00000000000000000"
|
||||
+ "00000000C0C0C0C0C0C0C0C0C0C0E1C073803F001E000C000C000C000C000C00000000000000000000000000FFC0FFC000C001C0038007000E001C00"
|
||||
+ "38007000E000C000FFC0FFC00000000000000000000000003F003F0030003000300030003000300030003000300030003F003F000000000000000000"
|
||||
+ "0000000000000000C000E000700038001C000E000700038001C000C0000000000000000000000000000000003F003F00030003000300030003000300"
|
||||
+ "03000300030003003F003F000000000000000000000000000C001E003F007380E1C0C0C0000000000000000000000000000000000000000000000000"
|
||||
+ "0000000000000000000000000000000000000000000000000000000000000000FFC0FFC000000000000000000C000E000700038001C000C000000000"
|
||||
+ "00000000000000000000000000000000000000000000000000000000000000003F003F8000C000C03FC07FC0C0C0C0C07FC03FC00000000000000000"
|
||||
+ "00000000C000C000C000C000C000C000DF00FF80F1C0C0C0C0C0C1C0FF80FF0000000000000000000000000000000000000000003F007F80E1C0C0C0"
|
||||
+ "C000C000C0C0E1C07F803F0000000000000000000000000000C000C000C000C000C000C03EC07FC0E3C0C0C0C0C0E0C07FC03FC00000000000000000"
|
||||
+ "0000000000000000000000003F007F80C0C0C0C0FF80FF00C000C0007F003F000000000000000000000000000F001F8039C030C030003000FC00FC00"
|
||||
+ "30003000300030003000300000000000000000000000000000000000000000003FC07FC0E0C0C0C0C0C0E0C07FC03FC000C000C000C001C03F803F00"
|
||||
+ "00000000C000C000C000C000C000C000DF00FF80E1C0C0C0C0C0C0C0C0C0C0C00000000000000000000000000C000C00000000003C003C000C000C00"
|
||||
+ "0C000C000C000C003F003F0000000000000000000000000003000300000000000F000F00030003000300030003000300C300C300C300E7007E003C00"
|
||||
+ "00000000300030003000300030C031C0338037003E003E003700338031C030C00000000000000000000000003C003C000C000C000C000C000C000C00"
|
||||
+ "0C000C000C000C003F003F000000000000000000000000000000000000000000D300FF80FFC0CCC0CCC0CCC0CCC0CCC0CCC0CCC00000000000000000"
|
||||
+ "000000000000000000000000CF00FF80F1C0C0C0C0C0C0C0C0C0C0C0C0C0C0C000000000000000000000000000000000000000003F007F80E1C0C0C0"
|
||||
+ "C0C0C0C0C0C0E1C07F803F000000000000000000000000000000000000000000CF00DF80F9C0F0C0E0C0C1C0FF80FF00C000C000C000C000C000C000"
|
||||
+ "0000000000000000000000003CC07EC0E7C0C3C0C1C0E0C07FC03FC000C000C000C000C000C000C0000000000000000000000000DF00FF80F1C0C0C0"
|
||||
+ "C000C000C000C000C000C00000000000000000000000000000000000000000003FC07FC0C000C0007F003F8000C000C0FF80FF000000000000000000"
|
||||
+ "0000000030003000FC00FC0030003000300030003000300030C039C01F800F000000000000000000000000000000000000000000C0C0C0C0C0C0C0C0"
|
||||
+ "C0C0C0C0C0C0E3C07FC03CC00000000000000000000000000000000000000000C0C0C0C0C0C0C0C0C0C0E1C0738033001E000C000000000000000000"
|
||||
+ "000000000000000000000000C0C0C0C0C0C0C0C0C0C0CCC0CCC0CCC07F8033000000000000000000000000000000000000000000C0C0E1C073803F00"
|
||||
+ "1E001E003F007380E1C0C0C00000000000000000000000000000000000000000C0C0C0C0C0C0C0C0C0C0E0C07FC03FC000C000C000C001C03F803F00"
|
||||
+ "000000000000000000000000FFC0FFC0030007000E001C0038007000FFC0FFC000000000000000000000000000C001C003800300030006000C000C00"
|
||||
+ "060003000300038001C000C00000000000000000000000000C000C000C000C000C000C00000000000C000C000C000C000C000C000000000000000000"
|
||||
+ "00000000300038001C000C000C0006000300030006000C000C001C003800300000000000000000000000000000C001C00F801F003800300000000000"
|
||||
+ "000000000000000000000000000000000000000000000000FFF0FFF0FFF0FFF0FFF0FFF0FFF0FFF0FFF0FFF0FFF0FFF0FFF0FFF0FFF0FFF000000000");
|
||||
|
||||
// font 6: 7x10, chars 0x20-0xFF, 224 glyphs
|
||||
private static readonly Face F6 = new(6, 0x20, 0xFF, 7, 10,
|
||||
"000000000000000000000000000000000000000000001000100010001000000000001000000000000000280028002800000000000000000000000000"
|
||||
+ "0000280028007C0028007C002800280000000000000010003C0050003800140078001000000000000000600064000800100020004C000C0000000000"
|
||||
+ "000030004800500020005400480034000000000000003000100020000000000000000000000000000000080010002000200020001000080000000000"
|
||||
+ "0000200010000800080008001000200000000000000000001000540038005400100000000000000000000000100010007C0010001000000000000000"
|
||||
+ "000000000000000000000000300010002000000000000000000000007C00000000000000000000000000000000000000000000003000300000000000"
|
||||
+ "00000000040008001000200040000000000000000000380044004C005400640044003800000000000000100030001000100010001000380000000000"
|
||||
+ "00003800440004000800100020007C000000000000007C0008001000080004000400780000000000000008001800280048007C000800080000000000"
|
||||
+ "00007C0040007800040004000400780000000000000018002000400078004400440038000000000000007C0004000800100020002000200000000000"
|
||||
+ "000038004400440038004400440038000000000000003800440044003C00040008003000000000000000000030003000000030003000000000000000"
|
||||
+ "000000003000300000003000100020000000000000000800100020004000200010000800000000000000000000007C0000007C000000000000000000"
|
||||
+ "000040002000100008001000200040000000000000003800440004000800100000001000000000000000380044000400340054005400380000000000"
|
||||
+ "000038004400440044007C00440044000000000000007800440044007800440044007800000000000000380044004000400040004400380000000000"
|
||||
+ "000078004400440044004400440078000000000000007C00400040007800400040007C000000000000007C0040004000780040004000400000000000"
|
||||
+ "00003800440040005C004400440038000000000000004400440044007C00440044004400000000000000380010001000100010001000380000000000"
|
||||
+ "00001C0008000800080008004800300000000000000044004800500060005000480044000000000000004000400040004000400040007C0000000000"
|
||||
+ "000044006C005400540044004400440000000000000044004400640054004C0044004400000000000000380044004400440044004400380000000000"
|
||||
+ "000078004400440078004000400040000000000000003800440044004400540048003400000000000000780044004400780050004800440000000000"
|
||||
+ "00003C004000400038000400040078000000000000007C00100010001000100010001000000000000000440044004400440044004400380000000000"
|
||||
+ "000044004400440044004400280010000000000000004400440044005400540054002800000000000000440044002800100028004400440000000000"
|
||||
+ "000044004400440028001000100010000000000000007C00040008001000200040007C00000000000000380020002000200020002000380000000000"
|
||||
+ "000040002000100008000400000000000000000000003800080008000800080008003800000000000000100028004400000000000000000000000000"
|
||||
+ "000000000000000000000000000000000000FE000000300020001000000000000000000000000000000000000000380004003C0044003C0000000000"
|
||||
+ "0000400040005800640044004400780000000000000000000000380040004000400038000000000000000400040034004C00440044003C0000000000"
|
||||
+ "000000000000380044007C00400038000000000000001800240020007000200020002000000000000000000000003C00440044003C00040004003800"
|
||||
+ "000040004000580064004400440044000000000000001000000030001000100010003800000000000000080000001800080008000800080048003000"
|
||||
+ "000040004000480050006000500048000000000000003000100010001000100010003800000000000000000000006800540054004400440000000000"
|
||||
+ "000000000000580064004400440044000000000000000000000038004400440044003800000000000000000000005800640044007800400040004000"
|
||||
+ "00000000000034004C0044003C0004000400040000000000000058006400400040004000000000000000000000003C00400038000400780000000000"
|
||||
+ "000000002000F8002000200028001000000000000000000000004400440044004C003400000000000000000000004400440044002800100000000000"
|
||||
+ "000000000000440044005400540028000000000000000000000044002800100028004400000000000000000000004400440044003C00040004003800"
|
||||
+ "0000000000007C000800100020007C000000000000000800100010002000100010000800000000000000100010001000000010001000100000000000"
|
||||
+ "00002000100010000800100010002000000000000000040038004000000000000000000000000000FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00"
|
||||
+ "0000000000000000FE000000000000000000000008000800080008000800080008000800080008000800080008000800F80000000000000000000000"
|
||||
+ "00000000000000000E000800080008000800080008000800080008000E00000000000000000000000000000000000000F80008000800080008000800"
|
||||
+ "08000800080008000E00080008000800080008000800080008000800F800080008000800080008000800080008000800FE0008000800080008000800"
|
||||
+ "0800080008000800FE00000000000000000000000000000000000000FE00080008000800080008000000000000000000020004000800080008000800"
|
||||
+ "0000000000000000E000100008000800080008000800080008001000E000000000000000000000000800080008000400020000000000000000000000"
|
||||
+ "1C001C001C00FE00FE00FE001C001C001C001C00000000000000FE00FE00FE0000000000000000001C001C001C001C001C001C001C001C001C001C00"
|
||||
+ "1C001C001C00FC00FC00FC0000000000000000000000000000001E001E001E001C001C001C001C001C001C001C001E001E001E000000000000000000"
|
||||
+ "000000000000FC00FC00FC001C001C001C001C001C001C001C001E001E001E001C001C001C001C001C001C001C00FC00FC00FC001C001C001C001C00"
|
||||
+ "000000001000380054001000100010001000000000000000000020001000F8001000200000000000000000000000080010003E001000080000000000"
|
||||
+ "00000000000008000800F8000000000000000000000008000800080008002A001C0008000000000000000000080014002200140014001C0000000000"
|
||||
+ "0000000008001C003E001C001C001C00000000000000000010003E007E003E0010000000000000000000000054004400820082008200440038000000"
|
||||
+ "000000000000000000007000500070000000000000001C00100010001000000000000000000000000000000000000000100010001000700000000000"
|
||||
+ "00000000000000000000400020001000000000000000000000000000300030000000000000000000000000007C0004007C0004000800100000000000"
|
||||
+ "0000000000007C00040018001000200000000000000000000000080010003000500010000000000000000000000010007C0044000400180000000000"
|
||||
+ "00000000000000007C00100010007C000000000000000000000008007C001800280048000000000000000000000020007C0024002800200000000000"
|
||||
+ "00000000000000003800080008007C000000000000000000000078000800780008007800000000000000000000000000540054000400180000000000"
|
||||
+ "0000000000007C0000000000000000000000000000007C00040014001800100010002000000000000000040008001000300050001000100000000000"
|
||||
+ "000010007C004400440004000800100000000000000000007C0010001000100010007C0000000000000008007C000800180028004800080000000000"
|
||||
+ "000020007C002400240024002400480000000000000010007C0010007C0010001000100000000000000000003C002400440004000800300000000000"
|
||||
+ "000020003C004800080008000800100000000000000000007C0004000400040004007C0000000000000028007C002800280008001000200000000000"
|
||||
+ "0000000060000400640004000800700000000000000000007C000400080010002800440000000000000020007C0024002800200020001C0000000000"
|
||||
+ "0000000044004400240004000800300000000000000000003C00240054000C00080030000000000000000800700010007C0010001000200000000000"
|
||||
+ "00000000540054005400040008001000000000000000380000007C001000100010002000000000000000200020002000300028002000200000000000"
|
||||
+ "0000100010007C0010001000200040000000000000000000380000000000000000007C0000000000000000007C000400280010002800400000000000"
|
||||
+ "000010007C00080010003800540010000000000000000800080008000800080010002000000000000000000010000800440044004400440000000000"
|
||||
+ "0000400040007C004000400040003C0000000000000000007C0004000400040008003000000000000000000020005000080004000400000000000000"
|
||||
+ "000010007C001000100054005400100000000000000000007C0004000400280010000800000000000000000038000000380000003800040000000000"
|
||||
+ "0000000010002000400044007C000400000000000000000004000400280010002800400000000000000000007C0020007C00200020001C0000000000"
|
||||
+ "0000200020007C0024002800200020000000000000000000380008000800080008007C0000000000000000007C0004007C00040004007C0000000000"
|
||||
+ "0000380000007C0004000400080010000000000000004800480048004800080010002000000000000000100050005000500054005400180000000000"
|
||||
+ "0000400040004000440048005000600000000000000000007C0044004400440044007C0000000000000000007C004400440004000800100000000000"
|
||||
+ "000000006000000004000400080070000000000000001000480020000000000000000000000000000000700050007000000000000000000000000000"
|
||||
+ "0000100000001000200040004400380000000000040008003800440044007C004400440000000000400020003800440044007C004400440000000000"
|
||||
+ "280000003800440044007C0044004400000000000000380044004000400040004400380010003000040008007C0040007800400040007C0000000000"
|
||||
+ "400020007C0040007800400040007C000000000038004400380010001000100010003800000000002800000038004400440044004400380000000000"
|
||||
+ "2800000044004400440044004400380000000000040008000000380004003C0044003C0000000000380044000000380004003C0044003C0000000000"
|
||||
+ "400020000000380004003C0044003C0000000000000028000000380004003C0044003C00000000000000000000003800400040004400380010003000"
|
||||
+ "040008000000380044007C004000380000000000380044000000380044007C004000380000000000400020000000380044007C004000380000000000"
|
||||
+ "040008002000000060002000200070000000000038004400100000003000100010003800000000008000400010000000300010001000380000000000"
|
||||
+ "280000001000000030001000100038000000000034005C00000058006400440044004400000000000400080000003800440044004400380000000000"
|
||||
+ "380044000000380044004400440038000000000040002000000038004400440044003800000000000000280000003800440044004400380000000000"
|
||||
+ "0000380044005800440044006400D800000000000800100000004400440044004C003400000000003800440000004400440044004C00340000000000"
|
||||
+ "4000200000004400440044004C003400000000000000280000004400440044004C00340000000000");
|
||||
|
||||
// font 7: 7x10, chars 0x40-0x7F, 64 glyphs
|
||||
private static readonly Face F7 = new(7, 0x40, 0x7F, 7, 10,
|
||||
"00000800100020001000080000007C000000000000002000100008001000200000007C00000000000000100010007C001000100000007C0000000000"
|
||||
+ "00000000100000007C000000100000000000000000001000280028002800440044007C00000000000000040008007C0010007C002000400000000000"
|
||||
+ "100038005400540054005400540038001000000000007C0020002000200020002000700000000000000000007C0000007C0000007C00000000000000"
|
||||
+ "0000040038004000040038004000000000000000000004003800400000007C00000000000000000000000400040004004800A8002800100010000000"
|
||||
+ "0000100028002800280044004400440000000000000038004400040034004C0044004800300000000000000000004400280010002800440000000000"
|
||||
+ "00000000100038007C0038001000000000000000000010003800540050005400380010000000000000007C0044004400440044004400440000000000"
|
||||
+ "0000380044004400540044004400380000000000000000000000280054002800000000000000000000007C00200010000800100020007C0000000000"
|
||||
+ "00001800240024001800000000000000000000000000380044009200BA0092004400380000000000000038004400AA009200AA004400380000000000"
|
||||
+ "00000000380044004400440028006C000000000000007C00000000003800000000007C00000000000000540054005400380010001000100000000000"
|
||||
+ "00003A0044008A009200A2004400B800000000000000380044009A00A2009A00440038000000000000007A002E002A002A0000000000000000000000"
|
||||
+ "08001C002A00080008000800080008000800080008000800080008000800080008002A001C0008000000180024002000780020002400580000000000"
|
||||
+ "000000000000340048004800340000000000000000003800440044007800440064004C00400000000000000040004400280010002800440004000000"
|
||||
+ "000038004400200010002800440044003800000000000000000038004400300044003800000000000000000010003800540054005400380010000000"
|
||||
+ "000000000000440044002800280038001000100000000000000058006400440044004400040000000000000000000000300010001000180000000000"
|
||||
+ "000000000000080054005400540038001000000000000000000024002800300028002400000000000000600020001000100028004400440000000000"
|
||||
+ "000000000000440044006400580040004000000000000000000044004400280028001000000000000000000000003800440044004400380000000000"
|
||||
+ "00004400280010007C0010007C001000000000000000000000007C0028002800280024000000000000003800440044007C0044004400380000000000"
|
||||
+ "00000000000038004400640058004000400000000000000000003C004800440044003800000000000000000004003800500010001000080000000000"
|
||||
+ "00000000000040002800240024001800000000000000000000007C002800440054002800000000000000000000000000280044005400280000000000"
|
||||
+ "00002000180020001800200020001C0004000000000000001000540054003800100010000000000000002000180020002000200020001C0004000000"
|
||||
+ "00000000080004007E00040008000000000000000000000020004000FE00400020000000000000000000000028004400FE0044002800000000000000"
|
||||
+ "08001C002A0008000800080008002A001C000800");
|
||||
|
||||
/// <summary>All 8 faces, indexed 0-7 (matches ESC K).</summary>
|
||||
public static readonly Face[] All = { F0, F1, F2, F3, F4, F5, F6, F7 };
|
||||
|
||||
/// <summary>The default/power-on font (font 0: 6x8, full 0x20-0xFF range).</summary>
|
||||
public static Face Default => F0;
|
||||
}
|
||||
@@ -50,10 +50,11 @@ public static class PlasmaSelfTest
|
||||
b.Add(PlasmaProtocol.LineFeed);
|
||||
Text(b, "128x32 PLASMA DISPLAY"); // exactly one 21-cell row
|
||||
|
||||
// Attributes are the low 4 bits: 1=half, 2=underline, 4=reverse, 8=flash.
|
||||
Esc(b, PlasmaProtocol.CmdAttributes, 1); Text(b, "HALF ");
|
||||
Esc(b, PlasmaProtocol.CmdAttributes, 2); Text(b, "UNDER ");
|
||||
Esc(b, PlasmaProtocol.CmdAttributes, 3); Text(b, "REV");
|
||||
Esc(b, PlasmaProtocol.CmdAttributes, 4); Text(b, " FLASH");
|
||||
Esc(b, PlasmaProtocol.CmdAttributes, 4); Text(b, "REV");
|
||||
Esc(b, PlasmaProtocol.CmdAttributes, 8); Text(b, " FLASH");
|
||||
Esc(b, PlasmaProtocol.CmdAttributes, PlasmaProtocol.OperandDefault);
|
||||
return b.ToArray();
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ public enum PlasmaCursorMode
|
||||
Flashing,
|
||||
}
|
||||
|
||||
/// <summary>Text rendering attributes (set with <c>ESC H</c>).</summary>
|
||||
/// <summary>Text rendering attributes (set with <c>ESC H</c>, low 4 bits).</summary>
|
||||
[Flags]
|
||||
public enum PlasmaAttributes : byte
|
||||
{
|
||||
@@ -27,17 +27,23 @@ public enum PlasmaAttributes : byte
|
||||
/// COM2. Feed raw wire bytes to <see cref="OnReceived"/>; the parser is a
|
||||
/// state machine, so commands may arrive split across any chunk boundaries.
|
||||
///
|
||||
/// <para>Grounded in the real firmware (<c>PlasmaNew/FIRMWARE.md</c>): the
|
||||
/// cursor is a <b>pixel</b> position — <c>ESC Q</c> sets its row (Y, 0–31),
|
||||
/// <c>ESC R</c> its column (X, 0–127) — and glyphs are drawn there and advance
|
||||
/// X by the font's width. The eight fonts are the real ROM character generator
|
||||
/// (<see cref="PlasmaFonts"/>): 6×8, 6×10, 7×10, and the large 12×16 / 12×20,
|
||||
/// selected with <c>ESC K</c> (0–7). Attributes are the low 4 bits of the
|
||||
/// <c>ESC H</c> operand.</para>
|
||||
///
|
||||
/// <para>Pixels carry flags rather than a plain bit: graphics writes set
|
||||
/// full-intensity dots, while text can stamp half-intensity or flashing dots
|
||||
/// (<c>ESC H</c>); the UI renders <see cref="PixelHalf"/> dimmer and blinks
|
||||
/// <see cref="PixelFlash"/>. The command set itself is documented on
|
||||
/// full-intensity dots, while text can stamp half-intensity or flashing dots;
|
||||
/// the UI renders <see cref="PixelHalf"/> dimmer and blinks
|
||||
/// <see cref="PixelFlash"/>. The command set is documented on
|
||||
/// <see cref="PlasmaProtocol"/>.</para>
|
||||
///
|
||||
/// <para>Grid geometry: fonts 0–3 are the 5×7 set in a 6×8 cell (21 columns
|
||||
/// × 4 rows), fonts 4–7 the same glyphs doubled into a 12×16 cell
|
||||
/// (10 × 2). Which of the eight slots the real panel mapped to which face is
|
||||
/// lost with the hardware; two sizes cover what the surviving software
|
||||
/// exercises.</para>
|
||||
/// <para>Not yet folded in from the firmware (documented, deferred): the 10
|
||||
/// double-buffered pages (<c>ESC I</c>/<c>ESC i</c>) and the vector-graphics
|
||||
/// primitives (<c>ESC A</c>–<c>F</c>). vPLASMA models a single page.</para>
|
||||
///
|
||||
/// <para>Thread-safe: the serial reader feeds bytes while the UI snapshots
|
||||
/// frames. Events are raised outside the lock, on the caller's thread.</para>
|
||||
@@ -58,8 +64,9 @@ public sealed class VPlasmaDevice
|
||||
|
||||
// ---- text-mode state -------------------------------------------------
|
||||
private int _font; // 0..7
|
||||
private PlasmaFonts.Face _face = PlasmaFonts.Default;
|
||||
private PlasmaAttributes _attributes;
|
||||
private int _col, _row; // cursor, in cells of the current grid
|
||||
private int _x, _y; // cursor, in pixels (0..127, 0..31)
|
||||
private PlasmaCursorMode _cursorMode = PlasmaCursorMode.Steady; // power-on default; the game hides it
|
||||
|
||||
// ---- parser state ----------------------------------------------------
|
||||
@@ -99,16 +106,14 @@ public sealed class VPlasmaDevice
|
||||
public int Font { get { lock (_sync) return _font; } }
|
||||
public PlasmaAttributes Attributes { get { lock (_sync) return _attributes; } }
|
||||
|
||||
// Current font grid, for the UI's cursor overlay and status line.
|
||||
private int FontScale => _font >= 4 ? 2 : 1;
|
||||
public int CellWidth { get { lock (_sync) return 6 * FontScale; } }
|
||||
public int CellHeight { get { lock (_sync) return 8 * FontScale; } }
|
||||
private int Columns => Width / (6 * FontScale);
|
||||
private int Rows => Height / (8 * FontScale);
|
||||
public Point CursorCell { get { lock (_sync) return new Point(_col, _row); } }
|
||||
|
||||
/// <summary>A cursor cell position (avoids dragging in System.Drawing).</summary>
|
||||
public readonly record struct Point(int Col, int Row);
|
||||
/// <summary>Cursor column X in pixels (0–127).</summary>
|
||||
public int CursorX { get { lock (_sync) return _x; } }
|
||||
/// <summary>Cursor row Y in pixels (0–31).</summary>
|
||||
public int CursorY { get { lock (_sync) return _y; } }
|
||||
/// <summary>Current font cell width in pixels.</summary>
|
||||
public int FontWidth { get { lock (_sync) return _face.Width; } }
|
||||
/// <summary>Current font cell height in pixels.</summary>
|
||||
public int FontHeight { get { lock (_sync) return _face.Height; } }
|
||||
|
||||
/// <summary>Copy the frame into <paramref name="destination"/> (Width*Height flag bytes).</summary>
|
||||
public void CopyFrame(byte[] destination)
|
||||
@@ -125,8 +130,9 @@ public sealed class VPlasmaDevice
|
||||
lock (_sync)
|
||||
{
|
||||
Array.Clear(_pixels, 0, _pixels.Length);
|
||||
_col = _row = 0;
|
||||
_x = _y = 0;
|
||||
_font = 0;
|
||||
_face = PlasmaFonts.Default;
|
||||
_attributes = PlasmaAttributes.None;
|
||||
_cursorMode = PlasmaCursorMode.Steady;
|
||||
_state = State.Text;
|
||||
@@ -187,7 +193,7 @@ public sealed class VPlasmaDevice
|
||||
return;
|
||||
|
||||
case PlasmaProtocol.BackSpace:
|
||||
if (_col > 0) _col--;
|
||||
_x = Math.Max(0, _x - _face.Width);
|
||||
_dirty = true;
|
||||
return;
|
||||
|
||||
@@ -197,17 +203,18 @@ public sealed class VPlasmaDevice
|
||||
return;
|
||||
|
||||
case PlasmaProtocol.LineFeed:
|
||||
_row = (_row + 1) % Rows;
|
||||
NextLine();
|
||||
_dirty = true;
|
||||
return;
|
||||
|
||||
case PlasmaProtocol.VerticalTab:
|
||||
_row = (_row + Rows - 1) % Rows;
|
||||
_y -= _face.Height;
|
||||
if (_y < 0) _y = Math.Max(0, Height - _face.Height);
|
||||
_dirty = true;
|
||||
return;
|
||||
|
||||
case PlasmaProtocol.CarriageReturn:
|
||||
_col = 0;
|
||||
_x = 0;
|
||||
_dirty = true;
|
||||
return;
|
||||
}
|
||||
@@ -232,8 +239,9 @@ public sealed class VPlasmaDevice
|
||||
{
|
||||
case PlasmaProtocol.CmdClearScreen:
|
||||
Array.Clear(_pixels, 0, _pixels.Length);
|
||||
_col = _row = 0;
|
||||
_x = _y = 0;
|
||||
_font = 0;
|
||||
_face = PlasmaFonts.Default;
|
||||
_attributes = PlasmaAttributes.None;
|
||||
_dirty = true;
|
||||
Log("Clear screen (ESC @)");
|
||||
@@ -241,7 +249,7 @@ public sealed class VPlasmaDevice
|
||||
break;
|
||||
|
||||
case PlasmaProtocol.CmdHomeCursor:
|
||||
_col = _row = 0;
|
||||
_x = _y = 0;
|
||||
_dirty = true;
|
||||
Log("Home cursor (ESC L)");
|
||||
_graphicsLogArmed = true;
|
||||
@@ -250,6 +258,8 @@ public sealed class VPlasmaDevice
|
||||
case PlasmaProtocol.CmdCursorMode:
|
||||
case PlasmaProtocol.CmdFontSelect:
|
||||
case PlasmaProtocol.CmdAttributes:
|
||||
case PlasmaProtocol.CmdSetRow:
|
||||
case PlasmaProtocol.CmdSetColumn:
|
||||
_pendingCommand = b;
|
||||
_state = State.Operand;
|
||||
break;
|
||||
@@ -282,50 +292,52 @@ public sealed class VPlasmaDevice
|
||||
break;
|
||||
|
||||
case PlasmaProtocol.CmdFontSelect:
|
||||
_font = operand == PlasmaProtocol.OperandDefault ? 0 : operand & 0x07;
|
||||
// The cursor keeps its cell coordinates but the grid changed size.
|
||||
_col = Math.Min(_col, Columns - 1);
|
||||
_row = Math.Min(_row, Rows - 1);
|
||||
_dirty = true;
|
||||
Log($"Font {_font}: {Columns}×{Rows} cells (ESC K {operand:X2})");
|
||||
// Firmware: operands 0–7 select a real font (8 fonts); it range-
|
||||
// checks and ignores anything larger, so ESC K FF is a no-op.
|
||||
if (operand < PlasmaFonts.All.Length)
|
||||
{
|
||||
_font = operand;
|
||||
_face = PlasmaFonts.All[operand];
|
||||
_x = Math.Min(_x, Width - 1);
|
||||
_y = Math.Min(_y, Height - 1);
|
||||
_dirty = true;
|
||||
Log($"Font {_font}: {_face.Width}×{_face.Height} (ESC K {operand:X2})");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"Font select ignored (ESC K {operand:X2} out of range)");
|
||||
}
|
||||
break;
|
||||
|
||||
case PlasmaProtocol.CmdAttributes:
|
||||
_attributes = DecodeAttributes(operand);
|
||||
// Firmware stores the low 4 bits directly as flags.
|
||||
_attributes = (PlasmaAttributes)(operand & 0x0F);
|
||||
Log($"Attributes {(_attributes == PlasmaAttributes.None ? "default" : _attributes.ToString())} (ESC H {operand:X2})");
|
||||
break;
|
||||
|
||||
case PlasmaProtocol.CmdSetRow:
|
||||
// ESC Q: set cursor row Y in pixels; firmware range-checks 0–31.
|
||||
if (operand < Height)
|
||||
{
|
||||
_y = operand;
|
||||
_dirty = true;
|
||||
Log($"Cursor row Y={operand} (ESC Q {operand:X2})");
|
||||
}
|
||||
break;
|
||||
|
||||
case PlasmaProtocol.CmdSetColumn:
|
||||
// ESC R: set cursor column X in pixels; firmware range-checks 0–127.
|
||||
if (operand < Width)
|
||||
{
|
||||
_x = operand;
|
||||
_dirty = true;
|
||||
Log($"Cursor col X={operand} (ESC R {operand:X2})");
|
||||
}
|
||||
break;
|
||||
}
|
||||
_graphicsLogArmed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <c>ESC H</c>'s operand indexes the style list PLASMA.EXE enumerates
|
||||
/// (its <c>/s</c> option): the 17 intensity/underline/reverse/flash
|
||||
/// combos below, in the tool's own order. FF (and anything out of range)
|
||||
/// restores the defaults.
|
||||
/// </summary>
|
||||
private static PlasmaAttributes DecodeAttributes(byte operand) => operand switch
|
||||
{
|
||||
0 => PlasmaAttributes.None,
|
||||
1 => PlasmaAttributes.HalfIntensity,
|
||||
2 => PlasmaAttributes.Underline,
|
||||
3 => PlasmaAttributes.Reverse,
|
||||
4 => PlasmaAttributes.Flash,
|
||||
5 => PlasmaAttributes.Underline,
|
||||
6 => PlasmaAttributes.Reverse,
|
||||
7 => PlasmaAttributes.Flash,
|
||||
8 => PlasmaAttributes.HalfIntensity | PlasmaAttributes.Underline,
|
||||
9 => PlasmaAttributes.HalfIntensity | PlasmaAttributes.Reverse,
|
||||
10 => PlasmaAttributes.HalfIntensity | PlasmaAttributes.Flash,
|
||||
11 => PlasmaAttributes.Underline | PlasmaAttributes.Reverse,
|
||||
12 => PlasmaAttributes.Underline | PlasmaAttributes.Flash,
|
||||
13 => PlasmaAttributes.Underline | PlasmaAttributes.Reverse | PlasmaAttributes.Flash,
|
||||
14 => PlasmaAttributes.HalfIntensity | PlasmaAttributes.Underline | PlasmaAttributes.Reverse,
|
||||
15 => PlasmaAttributes.HalfIntensity | PlasmaAttributes.Underline | PlasmaAttributes.Flash,
|
||||
16 => PlasmaAttributes.HalfIntensity | PlasmaAttributes.Underline | PlasmaAttributes.Reverse | PlasmaAttributes.Flash,
|
||||
_ => PlasmaAttributes.None,
|
||||
};
|
||||
|
||||
// ---- graphics writes (ESC P) -------------------------------------------
|
||||
|
||||
private void BeginGraphicsData()
|
||||
@@ -374,34 +386,35 @@ public sealed class VPlasmaDevice
|
||||
|
||||
private void DrawChar(byte code)
|
||||
{
|
||||
int scale = FontScale;
|
||||
int cellW = 6 * scale, cellH = 8 * scale;
|
||||
int ox = _col * cellW, oy = _row * cellH;
|
||||
|
||||
Span<byte> columns = stackalloc byte[PlasmaFont.GlyphWidth];
|
||||
PlasmaFont.GetColumns(code, columns);
|
||||
// The firmware ignores a character outside the current font's range.
|
||||
if (!_face.Has(code))
|
||||
return;
|
||||
|
||||
int w = _face.Width, h = _face.Height;
|
||||
bool reverse = (_attributes & PlasmaAttributes.Reverse) != 0;
|
||||
bool underline = (_attributes & PlasmaAttributes.Underline) != 0;
|
||||
byte litFlags = PixelLit;
|
||||
if ((_attributes & PlasmaAttributes.HalfIntensity) != 0) litFlags |= PixelHalf;
|
||||
if ((_attributes & PlasmaAttributes.Flash) != 0) litFlags |= PixelFlash;
|
||||
|
||||
for (int cy = 0; cy < cellH; ++cy)
|
||||
for (int row = 0; row < h; ++row)
|
||||
{
|
||||
int glyphRow = cy / scale; // 0..7; row 7 is the gap/underline row
|
||||
int rowOffset = (oy + cy) * Width + ox;
|
||||
for (int cx = 0; cx < cellW; ++cx)
|
||||
int py = _y + row;
|
||||
if ((uint)py >= Height)
|
||||
continue;
|
||||
ushort bits = _face.Row(code, row); // bit 15 = leftmost pixel
|
||||
int rowOffset = py * Width;
|
||||
for (int col = 0; col < w; ++col)
|
||||
{
|
||||
int glyphCol = cx / scale; // 0..5; column 5 is the gap column
|
||||
bool on = glyphCol < PlasmaFont.GlyphWidth
|
||||
&& glyphRow < PlasmaFont.GlyphHeight
|
||||
&& (columns[glyphCol] >> glyphRow & 1) != 0;
|
||||
if (underline && glyphRow == 7)
|
||||
int px = _x + col;
|
||||
if ((uint)px >= Width)
|
||||
continue;
|
||||
bool on = (bits & (0x8000 >> col)) != 0;
|
||||
if (underline && row == h - 1)
|
||||
on = true;
|
||||
if (reverse)
|
||||
on = !on;
|
||||
_pixels[rowOffset + cx] = on ? litFlags : (byte)0;
|
||||
_pixels[rowOffset + px] = on ? litFlags : (byte)0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,15 +423,21 @@ public sealed class VPlasmaDevice
|
||||
AdvanceCursor();
|
||||
}
|
||||
|
||||
/// <summary>Advance the cursor one cell, wrapping at the right/bottom edges.</summary>
|
||||
private void AdvanceCursor()
|
||||
{
|
||||
if (++_col >= Columns)
|
||||
{
|
||||
_col = 0;
|
||||
// No scroll on these panels: writing past the last row wraps to the top.
|
||||
if (++_row >= Rows)
|
||||
_row = 0;
|
||||
}
|
||||
_x += _face.Width;
|
||||
if (_x > Width - _face.Width)
|
||||
NextLine();
|
||||
}
|
||||
|
||||
private void NextLine()
|
||||
{
|
||||
_x = 0;
|
||||
_y += _face.Height;
|
||||
// No scroll on these panels: past the last line wraps to the top.
|
||||
if (_y > Height - _face.Height)
|
||||
_y = 0;
|
||||
}
|
||||
|
||||
// ---- event plumbing --------------------------------------------------------
|
||||
|
||||
@@ -58,6 +58,11 @@ public static class PlasmaProtocol
|
||||
public const byte CmdFontSelect = (byte)'K';
|
||||
public const byte CmdHomeCursor = (byte)'L';
|
||||
public const byte CmdGraphicsWrite = (byte)'P';
|
||||
// Cursor positioning, confirmed from the firmware dump (FIRMWARE.md): the
|
||||
// cursor is a pixel position, ESC Q sets its row (Y, 0-31) and ESC R its
|
||||
// column (X, 0-127) — the exact range checks the firmware enforces.
|
||||
public const byte CmdSetRow = (byte)'Q';
|
||||
public const byte CmdSetColumn = (byte)'R';
|
||||
|
||||
/// <summary>Operand meaning "restore the default" for ESC K / ESC H.</summary>
|
||||
public const byte OperandDefault = 0xFF;
|
||||
|
||||
@@ -53,7 +53,6 @@ public class VPlasmaDeviceTests
|
||||
{
|
||||
byte[] wire = GraphicsRow(3, Enumerable.Repeat((byte)0xFF, 16).ToArray());
|
||||
|
||||
// Split the same command at every possible boundary.
|
||||
for (int split = 1; split < wire.Length; ++split)
|
||||
{
|
||||
var device = new VPlasmaDevice();
|
||||
@@ -71,7 +70,6 @@ public class VPlasmaDeviceTests
|
||||
public void GraphicsBlock_MultipleRowsAdvanceY()
|
||||
{
|
||||
var device = new VPlasmaDevice();
|
||||
// screen 0, y=10, xbyte=0, 1 byte/row, 3 rows.
|
||||
Feed(device, Esc, (byte)'P', 0, 10, 0, 1, 3, 0x80, 0x80, 0x80);
|
||||
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 0, 10));
|
||||
@@ -98,10 +96,8 @@ public class VPlasmaDeviceTests
|
||||
{
|
||||
var device = new VPlasmaDevice();
|
||||
Feed(device, GraphicsRow(40, Enumerable.Repeat((byte)0xFF, 16).ToArray()));
|
||||
Feed(device, (byte)'!'); // parser must be back in text mode
|
||||
Feed(device, (byte)'H'); // parser must be back in text mode
|
||||
|
||||
var frame = new byte[VPlasmaDevice.Width * VPlasmaDevice.Height];
|
||||
device.CopyFrame(frame);
|
||||
Assert.Equal(1, device.TextCharsDrawn);
|
||||
Assert.Equal(0, device.GraphicsRows);
|
||||
}
|
||||
@@ -110,64 +106,83 @@ public class VPlasmaDeviceTests
|
||||
public void GraphicsWrite_OverwritesTextAttributes()
|
||||
{
|
||||
var device = new VPlasmaDevice();
|
||||
Feed(device, Esc, (byte)'H', 4); // flashing text
|
||||
Feed(device, (byte)'H'); // glyph col 0 is a full bar at x=0
|
||||
Feed(device, Esc, (byte)'H', 8); // flashing text
|
||||
Feed(device, (byte)'H'); // font-0 'H' lights (0,0)
|
||||
Assert.Equal(VPlasmaDevice.PixelLit | VPlasmaDevice.PixelFlash, Pixel(device, 0, 0));
|
||||
|
||||
Feed(device, GraphicsRow(0, 0x80)); // repaint that row from the wire
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 0, 0));
|
||||
}
|
||||
|
||||
// ---- text mode ---------------------------------------------------------
|
||||
// ---- text mode (real font 0 = 6×8) -----------------------------------
|
||||
|
||||
[Fact]
|
||||
public void Text_DrawsGlyphAndAdvancesCursor()
|
||||
public void Text_DrawsRealGlyphAndAdvancesByFontWidth()
|
||||
{
|
||||
var device = new VPlasmaDevice();
|
||||
Feed(device, (byte)'H'); // 5×7 'H': column 0 = 0x7F (all seven rows)
|
||||
Feed(device, (byte)'H'); // font-0 'H': row0 = pixels at x0 and x4
|
||||
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 0, 0));
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 0, 6));
|
||||
Assert.Equal(0, Pixel(device, 0, 7)); // gap row
|
||||
Assert.Equal(0, Pixel(device, 5, 0)); // gap column
|
||||
Assert.Equal(new VPlasmaDevice.Point(1, 0), device.CursorCell);
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 4, 0));
|
||||
Assert.Equal(0, Pixel(device, 1, 0));
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 0, 3)); // crossbar row
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 4, 3));
|
||||
Assert.Equal(0, Pixel(device, 0, 7)); // blank bottom row
|
||||
Assert.Equal(6, device.CursorX); // advanced one 6-px cell
|
||||
Assert.Equal(0, device.CursorY);
|
||||
Assert.Equal(1, device.TextCharsDrawn);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ControlChars_MoveTheCursor()
|
||||
public void ControlChars_MoveCursorByPixels()
|
||||
{
|
||||
var device = new VPlasmaDevice();
|
||||
var device = new VPlasmaDevice(); // font 0: 6 wide, 8 tall
|
||||
|
||||
Feed(device, 0x09, 0x09, 0x09); // HT ×3
|
||||
Assert.Equal(new VPlasmaDevice.Point(3, 0), device.CursorCell);
|
||||
Feed(device, 0x09, 0x09, 0x09); // HT ×3 → x = 18
|
||||
Assert.Equal(18, device.CursorX);
|
||||
|
||||
Feed(device, 0x08); // BS
|
||||
Assert.Equal(new VPlasmaDevice.Point(2, 0), device.CursorCell);
|
||||
Feed(device, 0x08); // BS → x = 12
|
||||
Assert.Equal(12, device.CursorX);
|
||||
|
||||
Feed(device, 0x0A); // LF
|
||||
Assert.Equal(new VPlasmaDevice.Point(2, 1), device.CursorCell);
|
||||
Feed(device, 0x0A); // LF → y = 8
|
||||
Assert.Equal(8, device.CursorY);
|
||||
Assert.Equal(0, device.CursorX);
|
||||
|
||||
Feed(device, 0x0D); // CR
|
||||
Assert.Equal(new VPlasmaDevice.Point(0, 1), device.CursorCell);
|
||||
Feed(device, (byte)'H', 0x0D); // draw + CR → x back to 0
|
||||
Assert.Equal(0, device.CursorX);
|
||||
|
||||
Feed(device, 0x0B); // VT
|
||||
Assert.Equal(new VPlasmaDevice.Point(0, 0), device.CursorCell);
|
||||
|
||||
Feed(device, 0x0B); // VT off the top wraps to the bottom row
|
||||
Assert.Equal(new VPlasmaDevice.Point(0, 3), device.CursorCell);
|
||||
Feed(device, 0x0B); // VT → y back to 0
|
||||
Assert.Equal(0, device.CursorY);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Text_WrapsAtRowAndScreenEnd()
|
||||
public void EscQ_EscR_SetPixelCursor()
|
||||
{
|
||||
var device = new VPlasmaDevice();
|
||||
Feed(device, Enumerable.Repeat((byte)'X', 22)); // one full 21-cell row + 1
|
||||
|
||||
Assert.Equal(new VPlasmaDevice.Point(1, 1), device.CursorCell);
|
||||
Feed(device, Esc, (byte)'Q', 20); // set row Y=20
|
||||
Feed(device, Esc, (byte)'R', 37); // set col X=37
|
||||
Assert.Equal(37, device.CursorX);
|
||||
Assert.Equal(20, device.CursorY);
|
||||
|
||||
Feed(device, Enumerable.Repeat((byte)'X', 21 * 3 - 1)); // exactly to the end
|
||||
Assert.Equal(new VPlasmaDevice.Point(0, 0), device.CursorCell); // wrapped to top
|
||||
Feed(device, Esc, (byte)'Q', 40); // out of range (>31): ignored
|
||||
Assert.Equal(20, device.CursorY);
|
||||
Feed(device, Esc, (byte)'R', 200); // out of range (>127): ignored
|
||||
Assert.Equal(37, device.CursorX);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Text_WrapsAtRightAndBottom()
|
||||
{
|
||||
var device = new VPlasmaDevice(); // 21 cells of 6px per line, 4 lines
|
||||
|
||||
Feed(device, Enumerable.Repeat((byte)'X', 21)); // fills first line, wraps
|
||||
Assert.Equal(0, device.CursorX);
|
||||
Assert.Equal(8, device.CursorY);
|
||||
|
||||
Feed(device, Enumerable.Repeat((byte)'X', 21 * 3)); // to the end, wraps to top
|
||||
Assert.Equal(0, device.CursorX);
|
||||
Assert.Equal(0, device.CursorY);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -178,7 +193,8 @@ public class VPlasmaDeviceTests
|
||||
Feed(device, Esc, (byte)'@');
|
||||
|
||||
Assert.Equal(0, Pixel(device, 0, 0));
|
||||
Assert.Equal(new VPlasmaDevice.Point(0, 0), device.CursorCell);
|
||||
Assert.Equal(0, device.CursorX);
|
||||
Assert.Equal(0, device.CursorY);
|
||||
Assert.Equal(0, device.Font);
|
||||
Assert.Equal(PlasmaAttributes.None, device.Attributes);
|
||||
}
|
||||
@@ -189,7 +205,8 @@ public class VPlasmaDeviceTests
|
||||
var device = new VPlasmaDevice();
|
||||
Feed(device, (byte)'H', Esc, (byte)'L');
|
||||
|
||||
Assert.Equal(new VPlasmaDevice.Point(0, 0), device.CursorCell);
|
||||
Assert.Equal(0, device.CursorX);
|
||||
Assert.Equal(0, device.CursorY);
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 0, 0)); // glyph survives
|
||||
}
|
||||
|
||||
@@ -206,42 +223,42 @@ public class VPlasmaDeviceTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EscK_SelectsFontGrids()
|
||||
public void EscK_SelectsRealFonts()
|
||||
{
|
||||
var device = new VPlasmaDevice();
|
||||
|
||||
Feed(device, Esc, (byte)'K', 4); // large: 12×16 cells
|
||||
Feed(device, Esc, (byte)'K', 4); // large font: 12×16
|
||||
Assert.Equal(4, device.Font);
|
||||
Assert.Equal(12, device.CellWidth);
|
||||
Assert.Equal(16, device.CellHeight);
|
||||
Assert.Equal(12, device.FontWidth);
|
||||
Assert.Equal(16, device.FontHeight);
|
||||
|
||||
Feed(device, (byte)'A'); // large glyph: 'A' col 1 (0x11) row 0 → 2×2 dots at (2..3, 0..1)
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 2, 0));
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 3, 1));
|
||||
|
||||
Feed(device, Esc, (byte)'K', 0xFF); // FF → default font 0
|
||||
Feed(device, Esc, (byte)'K', 0); // back to font 0: 6×8
|
||||
Assert.Equal(0, device.Font);
|
||||
Assert.Equal(6, device.FontWidth);
|
||||
Assert.Equal(8, device.FontHeight);
|
||||
|
||||
Feed(device, Esc, (byte)'K', 0xFF); // out of range: firmware ignores it
|
||||
Assert.Equal(0, device.Font);
|
||||
Assert.Equal(6, device.CellWidth);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EscH_AppliesAttributes()
|
||||
public void EscH_AppliesAttributesAsLowFourBits()
|
||||
{
|
||||
var device = new VPlasmaDevice();
|
||||
|
||||
Feed(device, Esc, (byte)'H', 1, (byte)'H'); // half intensity
|
||||
Assert.Equal(VPlasmaDevice.PixelLit | VPlasmaDevice.PixelHalf, Pixel(device, 0, 0));
|
||||
|
||||
Feed(device, Esc, (byte)'H', 2, (byte)'H'); // underline: gap row lit
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 6, 7));
|
||||
Feed(device, Esc, (byte)'L', Esc, (byte)'@'); // clear
|
||||
Feed(device, Esc, (byte)'H', 2, (byte)'H'); // underline: bottom row (y=7) lit
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 0, 7));
|
||||
|
||||
Feed(device, Esc, (byte)'H', 3, (byte)' '); // reverse: a space renders solid
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 12, 0));
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 17, 7));
|
||||
Feed(device, Esc, (byte)'@');
|
||||
Feed(device, Esc, (byte)'H', 4, (byte)' '); // reverse: a space renders solid
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 0, 0));
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 5, 7));
|
||||
|
||||
Feed(device, Esc, (byte)'H', 0xFF, (byte)'H'); // defaults restored
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 18, 0));
|
||||
Assert.Equal(0, Pixel(device, 18, 7));
|
||||
Feed(device, Esc, (byte)'H', 0); // defaults restored
|
||||
Assert.Equal(PlasmaAttributes.None, device.Attributes);
|
||||
}
|
||||
|
||||
@@ -251,7 +268,7 @@ public class VPlasmaDeviceTests
|
||||
public void UnknownEscape_IsConsumedAndTextResumes()
|
||||
{
|
||||
var device = new VPlasmaDevice();
|
||||
Feed(device, Esc, (byte)'Z', (byte)'H');
|
||||
Feed(device, Esc, (byte)'\\', (byte)'H'); // ESC '\' is not a command
|
||||
|
||||
Assert.Equal(1, device.TextCharsDrawn);
|
||||
Assert.Equal(VPlasmaDevice.PixelLit, Pixel(device, 0, 0));
|
||||
@@ -260,11 +277,10 @@ public class VPlasmaDeviceTests
|
||||
[Fact]
|
||||
public void GameStartupSequence_HidesCursor()
|
||||
{
|
||||
// L4PLASMA.CPP's constructor sends exactly ESC 'G' 0x00.
|
||||
var device = new VPlasmaDevice();
|
||||
Assert.Equal(PlasmaCursorMode.Steady, device.CursorMode); // power-on default
|
||||
|
||||
Feed(device, 27, (byte)'G', 0x00);
|
||||
Feed(device, 27, (byte)'G', 0x00); // L4PLASMA.CPP's boot byte
|
||||
Assert.Equal(PlasmaCursorMode.Hidden, device.CursorMode);
|
||||
}
|
||||
|
||||
@@ -302,6 +318,7 @@ public class VPlasmaDeviceTests
|
||||
Assert.Equal(0, device.Font);
|
||||
Assert.Equal(PlasmaAttributes.None, device.Attributes);
|
||||
Assert.Equal(PlasmaCursorMode.Steady, device.CursorMode);
|
||||
Assert.Equal(new VPlasmaDevice.Point(0, 0), device.CursorCell);
|
||||
Assert.Equal(0, device.CursorX);
|
||||
Assert.Equal(0, device.CursorY);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user