はじめに
robo8080 氏がスタンドアロン動作する RunCPM を公開されていまして、ちょっとハマっています。
See also:
- RunCPM + VT100 Emu for M5Stack (Github)
- RunCPM + VT100 Emu for Wio Terminal (CardKB) (Github)
- RunCPM + VT100 Emu for Wio Terminal (USB KB) (Github)
EGR
その昔、カノープス社より EGR-98
というエスケープシーケンスでグラフィックを描画できるようになるグラフィクドライバが発売されていたそうです。
See also:
オレオレ EGR
実際に EGR-98 を使ったことがないので、EGR-98 のエスケープシーケンス体系を知らないのですが、オレオレ EGR シーケンスを定義し、LovyanGFX の描画メソッドをエスケープシーケンスを通して各種プログラミング言語から透過的に使えるようにしてみたいと思いました。
画像でお判り頂けると思いますが、使用している RunCPM + VT100 Emu は Wio Terminal 向けの USB キーボード版です。
文字 | 機能 | パラメータ | 説明 |
---|---|---|---|
A | drawArc | x, y, r0, r1, angle0, angle1 | 円弧の外周 |
a | fillArc | x, y, r0, r1, angle0, angle1 | 円弧の塗り |
B | drawBezier | x0, y0, x1, y1, x2, y2 | 3点間のベジエ曲線 |
drawBezier | x0, y0, x1, y1, x2, y2, x3, y3 | 4点間のベジエ曲線 | |
C | drawCircle | x, y, r | 円の外周 |
c | fillCircle | x, y, r | 円の塗り |
E | drawEllipse | x, y, rx, ry | 楕円の外周 |
e | fillEllipse | x, y, rx, ry | 楕円の塗り |
F | setColor | r, g, b | 前景色設定 |
H | drawFastHLine | x, y, w | 水平線 |
K | setBaseColor | r, g, b | 背景色設定 |
L | drawLine | x0, y0, x1, y1 | 2点間の直線 |
O | drawRoundRect | x, y, w, h, r | 角丸の矩形の外周 |
o | fillRoundRect | x, y, w, h, r | 角丸の矩形の塗り |
P | drawPixel | x, y | 点 |
R | drawRect | x, y, w, h | 矩形の外周 |
r | fillRect | x, y, w, h | 矩形の塗り |
S | clear | クリア | |
s | fillScreen | 画面全体の塗り潰し | |
T | drawTriangle | x0, y0, x1, y1, x2, y2 | 3点間の三角形の外周 |
t | fillTriangle | x0, y0, x1, y1, x2, y2 | 3点間の三角形の塗り |
V | drawFastVLine | x, y, h | 垂直線 |
※ EGR = ESC %
See also:
コード
下記コードを RunCPM + VT100 Emu の Wio_RunPCM_vt100_xxxxxx.ino
に組み込んで使います。まずは列挙型の項目を追加します。
PROGMEM enum class em {NONE, ES, CSI, CSI2, LSC, G0S, G1S, EGR}; // EGR を追加
次のコードを組み込む場所は printChar()
の中です。
...
case '%':
// EGR セット シーケンス へ
clearParams(em::EGR);
break;
...
// "*" EGR シーケンス
if (escMode == em::EGR) {
if (isdigit(c)) {
// [パラメータ]
vals[nVals] = vals[nVals] * 10 + (c - '0');
hasParam = true;
} else if (c == ';') {
// [セパレータ]
nVals++;
hasParam = false;
} else {
if (hasParam) nVals++;
switch (c) {
case 'A':
// drawArc
lcd.drawArc(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
break;
case 'a':
// fillArc
lcd.fillArc(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
break;
case 'B':
// drawBezier
if (nVals == 8)
lcd.drawBezier(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5], vals[6], vals[7]);
else
lcd.drawBezier(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
break;
case 'C':
// drawCircle
lcd.drawCircle(vals[0], vals[1], vals[2]);
break;
case 'c':
// fillCircle
lcd.fillCircle(vals[0], vals[1], vals[2]);
break;
case 'E':
// drawEllipse
lcd.drawEllipse(vals[0], vals[1], vals[2], vals[3]);
break;
case 'e':
// fillEllipse
lcd.fillEllipse(vals[0], vals[1], vals[2], vals[3]);
break;
case 'F':
// setColor
lcd.setColor(lcd.color565(vals[0], vals[1], vals[2]));
break;
case 'H':
// drawFastHLine
lcd.drawFastHLine(vals[0], vals[1], vals[2]);
break;
case 'K':
// setBaseColor
lcd.setBaseColor(lcd.color565(vals[0], vals[1], vals[2]));
break;
case 'L':
// drawLine
lcd.drawLine(vals[0], vals[1], vals[2], vals[3]);
break;
case 'O':
// drawRoundRect
lcd.drawRoundRect(vals[0], vals[1], vals[2], vals[3], vals[4]);
break;
case 'o':
// fillRoundRect
lcd.fillRoundRect(vals[0], vals[1], vals[2], vals[3], vals[4]);
break;
case 'P':
// drawPixel
lcd.drawPixel(vals[0], vals[1]);
break;
case 'R':
// drawRect
lcd.drawRect(vals[0], vals[1], vals[2], vals[3]);
break;
case 'r':
// fillRect
lcd.fillRect(vals[0], vals[1], vals[2], vals[3]);
break;
case 'S':
// clear
lcd.clear();
break;
case 's':
// fillScreen
lcd.fillScreen();
break;
case 'T':
// drawTriangle
lcd.drawTriangle(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
break;
case 't':
// fillTriangle
lcd.fillTriangle(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
break;
case 'V':
// drawFastVLine
lcd.drawFastVLine(vals[0], vals[1], vals[2]);
break;
default:
// 未確認のシーケンス
unknownSequence(escMode, c);
break;
}
clearParams(em::NONE);
}
return;
}
...
コードに何の捻りもありませんし、パラメータ数のチェックもしていません。
Turbo Pascal からの使用 (その 1)
エスケープシーケンスなので、標準出力に Write() / Writeln() するだけです。
program GRTEST;
begin
{ <ESC>*255;255;0F }
Write(#$1B, '%', '255;255;0' , 'F'); { 前景色を黄色にする }
{ <ESC>*160;120;100c }
Write(#$1B, '%', '160;120;100', 'c'); { (160,120) に半径 100 の円を描く }
end.
黄色い円が描けました。
エスケープシーケンスなので、どんなプログラミング言語からも使えます。MBASIC とかでも OK です。
Turbo Pascal からの使用 (その 2)
次のようなファイル (EGR.INC
) を用意すると、
{* EGR.INC *}
const
_EGR = #$1B'%';
procedure Clear;
begin
Write(_EGR, 'S');
end;
procedure DrawArc(x, y, r0, r1, angle0, angle1: Integer);
begin
Write(_EGR, x, ';', y, ';', r0, ';', r1, ';', angle0, ';', angle1, 'A');
end;
procedure DrawBezier(x0, y0, x1, y1, x2, y2: Integer);
begin
Write(_EGR, x0, ';', y0, ';', x1, ';', y1, ';', x2, ';', y2, 'B');
end;
procedure DrawBezier2(x0, y0, x1, y1, x2, y2, x3, y3: Integer);
begin
Write(_EGR, x0, ';', y0, ';', x1, ';', y1, ';', x2, ';', y2, ';', x3, ';', y3, 'B');
end;
procedure DrawCircle(x, y, r: Integer);
begin
Write(_EGR, x, ';', y, ';', r, 'C');
end;
procedure DrawEllipse(x, y, rx, ry: Integer);
begin
Write(_EGR, x, ';', y, ';', rx, ';', ry, 'E');
end;
procedure DrawFastHLine(x, y, w: Integer);
begin
Write(_EGR, x, ';', y, ';', w, 'H');
end;
procedure DrawFastVLine(x, y, h: Integer);
begin
Write(_EGR, x, ';', y, ';', h, 'V');
end;
procedure DrawLine(x0, y0, x1, y1: Integer);
begin
Write(_EGR, x0, ';', y0, ';', x1, ';', y1, 'L');
end;
procedure DrawPixel(x, y: Integer);
begin
Write(_EGR, x, ';', y, 'P');
end;
procedure DrawRect(x, y, w, h: Integer);
begin
Write(_EGR, x, ';', y, ';', w, ';', h, 'R');
end;
procedure DrawRoundRect(x, y, w, h, r: Integer);
begin
Write(_EGR, x, ';', y, ';', w, ';', h, ';', r, 'O');
end;
procedure DrawTriangle(x0, y0, x1, y1, x2, y2: Integer);
begin
Write(_EGR, x0, ';', y0, ';', x1, ';', x1, ';', x2, ';', y2, 'T');
end;
procedure FillArc(x, y, r0, r1, angle0, angle1: Integer);
begin
Write(_EGR, x, ';', y, ';', r0, ';', r1, ';', angle0, ';', angle1, 'a');
end;
procedure FillCircle(x, y, r: Integer);
begin
Write(_EGR, x, ';', y, ';', r, 'c');
end;
procedure FillEllipse(x, y, rx, ry: Integer);
begin
Write(_EGR, x, ';', y, ';', rx, ';', ry, 'e');
end;
procedure FillRect(x, y, w, h: Integer);
begin
Write(_EGR, x, ';', y, ';', w, ';', h, 'r');
end;
procedure FillRoundRect(x, y, w, h, r: Integer);
begin
Write(_EGR, x, ';', y, ';', w, ';', h, 'o');
end;
procedure FillScreen;
begin
Write(_EGR, 's');
end;
procedure FillTriangle(x0, y0, x1, y1, x2, y2: Integer);
begin
Write(_EGR, x0, ';', y0, ';', x1, ';', y1, ';', x2, ';', y2, 't');
end;
procedure SetBaseColor(r, g, b: Integer);
begin
Write(_EGR, r, ';', g, ';', b, 'K');
end;
procedure SetColor(r, g, b: Integer);
begin
Write(_EGR, r, ';', g, ';', b, 'F');
end;
procedure InitGraph;
begin
Write(#$1B'[H'#$1B'[2J');
SetBaseColor(0, 0, 0);
Clear;
SetColor(255, 255, 255);
end;
先程のコードは次のように書けます。
program GRTEST;
{$I EGR.INC}
begin
SetColor($FF, $FF, $00); { 前景色を黄色にする }
FillCircle(160, 120, 100); { (160,120) に半径 100 の円を描く }
end.
終わりに
EGR-98
のエスケープシーケンス体系をご存知の方がいらっしゃったら教えてください m(_ _)m
See also: