LoginSignup
2
3

More than 3 years have passed since last update.

【RunCPM】オレオレ EGR シーケンスを実装してみる

Last updated at Posted at 2021-01-07

はじめに

robo8080 氏がスタンドアロン動作する RunCPM を公開されていまして、ちょっとハマっています。
image.png

See also:

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 EmuWio_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() するだけです。

GRTEST.pas
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.

黄色い円が描けました。
image.png
エスケープシーケンスなので、どんなプログラミング言語からも使えます。MBASIC とかでも OK です。

Turbo Pascal からの使用 (その 2)

次のようなファイル (EGR.INC) を用意すると、

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;

先程のコードは次のように書けます。

GRTEST.pas
program GRTEST;
{$I EGR.INC} 
begin
  SetColor($FF, $FF, $00);   { 前景色を黄色にする              }
  FillCircle(160, 120, 100); { (160,120) に半径 100 の円を描く }
end.

終わりに

EGR-98 のエスケープシーケンス体系をご存知の方がいらっしゃったら教えてください m(_ _)m

See also:

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3