フォーラム


ゲスト  

ようこそ ゲスト さん。このフォーラムに投稿するには 登録が必要です。

ページ: 1 2 [3]
トピック: STM32 (Blue Pill) 用の VT100 エミュレータ
DEKO
管理者
投稿数: 2690
Re: STM32 (Blue Pill) 用の VT100 エミュレータ
on: 2018/11/18 23:49 Sun

カーソル表示処理を変更しました。SGR (Select Graphic Rendition) の 39 及び 49 を実装しました。

DEKO
管理者
投稿数: 2690
Re: STM32 (Blue Pill) 用の VT100 エミュレータ
on: 2018/11/19 01:24 Mon

Blink (<ESC>[5m) を明色で表現する事にしました。本当に点滅しようとすると描画コストがかかるためです。

DEKO
管理者
投稿数: 2690
Re: STM32 (Blue Pill) 用の VT100 エミュレータ
on: 2018/11/19 10:13 Mon

CPR (Cursor Position Report) の XY が逆だったのを修正。
<ESC>[38;2;?m – selectGraphicRendition RGB、<ESC>[38;5;?m – selectGraphicRendition Color Index にも対応。
RGB は 8 色の中から最も近い色が選ばれる。Color Index の 6x6x6 も 8 色の中から最も近い色が選ばれる。

DEKO
管理者
投稿数: 2690
Re: STM32 (Blue Pill) 用の VT100 エミュレータ
on: 2018/11/19 10:24 Mon

色関係の話は setup() の setCursorToHome(); 行の上あたりで

  // TFT の初期化
oColor.Color.Foreground = clWhite;
oColor.Color.Background = clBlack;
cColor.value = oColor.value;
tft.begin();
tft.setRotation(3);
tft.fillScreen(aColors[oColor.Color.Background]);
fontTop = (uint8_t*)font6x8tt + 3;
resetToInitialState();
printString("\e[0;44m *** Terminal Init *** \e[0m\n");
displayTest(); // <- 追加
setCursorToHome();

 
表示テストコードを走らせてみるとどういった修正だったのか解ると思います。

// 表示テスト
void displayTest() {
char buf[32];
PROGMEM const uint8_t atbl[] = {0, 0, 1, 4, 5, 7};
PROGMEM const char *ctbl[] = {"black ", "red ", "green ", "yellow ", "blue ", "magenta", "cyan ", "white "};

// Attribute Test
cursorPosition(3, 1);
printString("\e[0m+-------+-------+-------+-------+-------+-------+\eE");
printString("\e[0m|Fore |Back |Bold |UnderL |Blink |Reverse|\eE");
printString("\e[0m+-------+-------+-------+-------+-------+-------+\eE");
for (uint16_t l = 0; l < 8; l++) {
for (uint16_t i = 0; i < 6; i++) {
int v = (i == 1) ? 40 : 30 ;
printString("\e[0m|\e[");
sprintf(buf, "%d;%dm%s", atbl[i], v + l, ctbl[l]);
printString(buf);
}
printString("\e[0m|\eE");
}
printString("\e[0m+-------+-------+-------+-------+-------+-------+\eE");
nextLine();

// System colors (0..15)
printString("\e[0mSystem colors (0..15):\n");
printString("\e[7m");
for (uint16_t i = 0; i <= 7; i++) {
sprintf(buf, "\e[38;5;%dm\e[48;5;%dm ", i, i);
printString(buf);
}
nextLine();
for (uint16_t i = 8; i <= 15; i++) {
sprintf(buf, "\e[38;5;%dm\e[48;5;%dm ", i, i);
printString(buf);
}
cursorDown(1);
nextLine();

// Color cube, 6x6x6 (16..231)
printString("\e[0mColor cube, 6x6x6 (16..231):\n");
printString("\e[7m");
uint16_t y_top = YP;
for (uint16_t r = 0; r <= 5; r++) {
for (uint16_t g = 0; g <= 5; g++) {
for (uint16_t b = 0; b <= 5; b++) {
cursorPosition(y_top + g + 1, (r * 7) + b + 1);
uint8_t i = r * 36 + g * 6 + b + 16;
sprintf(buf, "\e[38;5;%dm\e[48;5;%dm ", i, i);
printString(buf);
}
}
}
cursorDown(1);
nextLine();

// Grayscale ramp (232..255)
printString("\e[0mGrayscale ramp (232..255):\n");
printString("\e[7m");
for (uint16_t i = 232; i <= 255; i++) {
sprintf(buf, "\e[38;5;%dm\e[48;5;%dm ", i, i);
printString(buf);
}
printString("\e[0m");
}

 

256 色パレットに対応しようとすると色属性バッファが 2 倍必要になります。そこで 256 色には対応しないけれど 16 色には対応し、256 色の色指定をされたら 8 色の中で最も近い色を使うようにしてあります。Blink 属性と 16 色のうちの後半 8 色が区別できない事があります。

要は <ESC>[38;2;?m や <ESC>[38;5;?m なんてのが来ても破綻しないようにしてみたという事です。

DEKO
管理者
投稿数: 2690
Re: STM32 (Blue Pill) 用の VT100 エミュレータ
on: 2018/11/19 15:06 Mon

TTBASIC (豊四季 Tiny BASIC) の 6×8 フォントに依存していたので PC-E200 / PC-E500 / SC1602B 風のフォントを追加。
今まで通り TTBASIC のフォントにする事も可能ですが、デフォルトでは SC1602B になっています。

フォントファイルは GitHub から DL できます。
https://github.com/ht-deko/vt100_stm32/tree/master/vt100_stm32

DEKO
管理者
投稿数: 2690
Re: STM32 (Blue Pill) 用の VT100 エミュレータ
on: 2018/11/21 10:41 Wed

DECSCNM (Screen Mode) 対応など。コード量が増えている割には機能は増えていません。

  • シリアルは Serial3 (PB10 / PB11 / GND) に接続します (PB10 / PB11 は 5V トレラント)。
  • TFT (ILI9341) の CS / RST / DC はそれぞれ PA4 / PA2 / PA3 です。
  • PS/2 キーボードは PB6 / PB7 / 5V / GND に接続します (PB6 / PB7 は 5V トレラント)。
  • PA8 にパッシブブザーを接続可能です。
  • PA15~PA12 に LED を接続可能です。
  • 画面サイズ (文字単位) は 53 x 30 です。
  • 通信速度は 9600bps です。
  • Arduino-STM32 のライブラリを改変し、バッファサイズを 512 に変更する必要があります。
  • PS2Keyboard Library-STM32 (茶虎たま吉さん版) のライブラリを改変し、CTRL キー対応にする必要があります。
  • Turbo Pascal を使う時には TINST で画面のサイズ (ROWS / COLS) を 30 / 53 に設定するといいでしょう。
  • Turbo Pascal を使う時には TINST でカーソルキーの定義をしておくといいでしょう。
vt100_stm32.ino
/*
vt100_stm32.ino - VT100 Terminal Emulator for Arduino STM32
Copyright (c) 2018 Hideaki Tominaga. All rights reserved.
*/

#include <SPI.h>
#include <tone.h>
#include <libmaple/nvic.h>

#include <Adafruit_GFX_AS.h> // Core graphics library
#include <Adafruit_ILI9341_STM.h> // Hardware-specific library
#include <PS2Keyboard_stm32.h> // PS/2 Keyboard Library for STM32
//#include <font6x8tt.h> // 6x8 ドットフォント (TTBASIC 付属)
//#include "font6x8e200.h" // 6x8 ドットフォント (SHARP PC-E200 風)
//#include "font6x8e500.h" // 6x8 ドットフォント (SHARP PC-E500 風)
#include "font6x8sc1602b.h" // 6x8 ドットフォント (SUNLIKE SC1602B 風)

// TFT 制御用ピン
#define TFT_CS PA4
#define TFT_RST PA3
#define TFT_DC PA2

// キーボード制御用ピン (5V トレラント)
#define KBD_DAT PB7
#define KBD_CLK PB6

// スピーカー制御用ピン
#define SPK_PIN PA8

// LED 制御用ピン
#define LED_01 PB15
#define LED_02 PB14
#define LED_03 PB13
#define LED_04 PB12

// デフォルト通信速度
#define DEFAULT_BAUDRATE 9600

// TFT 制御用
Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST);

// キーボード制御用
PS2Keyboard keyboard;

// フォント管理用
uint8_t* fontTop;
#define CH_W 6 // フォント横サイズ
#define CH_H 8 // フォント縦サイズ

// スクリーン管理用
#define SC_W 53 // キャラクタスクリーン横サイズ
#define SC_H 30 // キャラクタスクリーン縦サイズ
uint16_t M_TOP = 0; // 上マージン行
uint16_t M_BOTTOM = SC_H - 1; // 下マージン行

// 座標やサイズのプレ計算
PROGMEM const uint16_t SCSIZE = SC_W * SC_H; // キャラクタスクリーンサイズ
PROGMEM const uint16_t SP_W = SC_W * CH_W; // ピクセルスクリーン横サイズ
PROGMEM const uint16_t SP_H = SC_H * CH_H; // ピクセルスクリーン縦サイズ
PROGMEM const uint16_t MAX_CH_X = CH_W - 1; // フォント最大横位置
PROGMEM const uint16_t MAX_CH_Y = CH_H - 1; // フォント最大縦位置
PROGMEM const uint16_t MAX_SC_X = SC_W - 1; // キャラクタスクリーン最大横位置
PROGMEM const uint16_t MAX_SC_Y = SC_H - 1; // キャラクタスクリーン最大縦位置
PROGMEM const uint16_t MAX_SP_X = SP_W - 1; // ピクセルスクリーン最大横位置
PROGMEM const uint16_t MAX_SP_Y = SP_H - 1; // ピクセルスクリーン最大縦位置

// 文字アトリビュート用
struct TATTR {
uint8_t Bold : 1; // 1
uint8_t Faint : 1; // 2
uint8_t Italic : 1; // 3
uint8_t Underline : 1; // 4
uint8_t Blink : 1; // 5 (Slow Blink)
uint8_t RapidBlink : 1; // 6
uint8_t Reverse : 1; // 7
uint8_t Conceal : 1; // 8
};

union ATTR {
uint8_t value;
struct TATTR Bits;
};

// カラーアトリビュート用 (RGB565)
PROGMEM const uint16_t aColors[] = {
// Normal (0..7)
0x0000, // black
0x8000, // red
0x0400, // green
0x8400, // yellow
0x0010, // blue (Dark)
0x8010, // magenta
0x0410, // cyan
0xbdf7, // black
// Bright (8..15)
0x8410, // white
0xf800, // red
0x07e0, // green
0xffe0, // yellow
0x001f, // blue
0xf81f, // magenta
0x07ff, // cyan
0xe73c // white
};

PROGMEM const uint8_t clBlack = 0;
PROGMEM const uint8_t clRed = 1;
PROGMEM const uint8_t clGreen = 2;
PROGMEM const uint8_t clYellow = 3;
PROGMEM const uint8_t clBlue = 4;
PROGMEM const uint8_t clMagenta = 5;
PROGMEM const uint8_t clCyan = 6;
PROGMEM const uint8_t clWhite = 7;

struct TCOLOR {
uint8_t Foreground : 4;
uint8_t Background : 4;
};
union COLOR {
uint8_t value;
struct TCOLOR Color;
};

// 環境設定用
struct TMODE {
bool Reserved2 : 1; // 2
bool Reserved4 : 1; // 4:
bool Reserved12 : 1; // 12:
bool CrLf : 1; // 20: LNM (Line feed new line mode)
bool Reserved33 : 1; // 33:
bool Reserved34 : 1; // 34:
uint8_t Reverse : 2;
};

union MODE {
uint8_t value;
struct TMODE Flgs;
};

struct TMODE_EX {
bool Reserved1 : 1; // 1 DECCKM (Cursor Keys Mode)
bool Reserved2 : 1; // 2 DECANM (ANSI/VT52 Mode)
bool Reserved3 : 1; // 3 DECCOLM (Column Mode)
bool Reserved4 : 1; // 4 DECSCLM (Scrolling Mode)
bool ScreenReverse : 1; // 5 DECSCNM (Screen Mode)
bool Reserved6 : 1; // 6 DECOM (Origin Mode)
bool WrapLine : 1; // 7 DECAWM (Autowrap Mode)
bool Reserved8 : 1; // 8 DECARM (Auto Repeat Mode)
bool Reserved9 : 1; // 9 DECINLM (Interlace Mode)
uint16_t Reverse : 7;
};

union MODE_EX {
uint16_t value;
struct TMODE_EX Flgs;
};

// バッファ
uint8_t screen[SCSIZE]; // スクリーンバッファ
uint8_t attrib[SCSIZE]; // 文字アトリビュートバッファ
uint8_t colors[SCSIZE]; // カラーアトリビュートバッファ
uint8_t tabs[SC_W]; // タブ位置バッファ

// 状態
PROGMEM enum class em {NONE, ES, CSI, CSI2, LSC, G0S, G1S};
PROGMEM uint8_t defaultMode = 0b00001000;
PROGMEM uint8_t defaultModeEx = 0b0000000001000000;
PROGMEM const union ATTR defaultAttr = {0b00000000};
PROGMEM const union COLOR defaultColor = {(clBlack << 4) | clWhite}; // back, fore
em escMode = em::NONE; // エスケープシーケンスモード
bool isShowCursor = false; // カーソル表示中か?
bool canShowCursor = false; // カーソル表示可能か?
bool lastShowCursor = false; // 前回のカーソル表示状態
bool hasParam = false; // <ESC> [ がパラメータを持っているか?
bool isDECPrivateMode = false; // DEC Private Mode (<ESC> [ ?)
union MODE mode = {defaultMode};
union MODE_EX mode_ex = {defaultModeEx};

// 前回位置情報
int16_t p_XP = 0;
int16_t p_YP = 0;

// カレント情報
int16_t XP = 0;
int16_t YP = 0;
union ATTR cAttr = defaultAttr;
union COLOR cColor = defaultColor;

// バックアップ情報
int16_t b_XP = 0;
int16_t b_YP = 0;
union ATTR bAttr = defaultAttr;
union COLOR bColor = defaultColor;

// CSI パラメータ
int16_t nVals = 0;
int16_t vals[10] = {0};

// 関数
// -----------------------------------------------------------------------------

// 指定位置の文字の更新表示
void sc_updateChar(uint16_t x, uint16_t y) {
uint16_t idx = SC_W * y + x;
uint8_t c = screen[idx]; // キャラクタの取得
uint8_t* ptr = &fontTop[c * CH_H]; // フォントデータ先頭アドレス
union ATTR a;
union COLOR l;
a.value = attrib[idx]; // 文字アトリビュートの取得
l.value = colors[idx]; // カラーアトリビュートの取得
uint16_t fore = aColors[l.Color.Foreground | (a.Bits.Blink << 3)];
uint16_t back = aColors[l.Color.Background | (a.Bits.Blink << 3)];
if (a.Bits.Reverse) swap(fore, back);
if (mode_ex.Flgs.ScreenReverse) swap(fore, back);
uint16_t xx = x * CH_W;
uint16_t yy = y * CH_H;
tft.setAddrWindow(xx, yy, xx + MAX_CH_X, yy + MAX_CH_Y);
for (uint8_t i = 0; i < CH_H; i++) {
bool prev = (a.Bits.Underline && (i == MAX_CH_Y));
for (uint8_t j = 0; j < CH_W; j++) {
bool pset = ((*ptr) & (0x80 >> j));
uint16_t d = (pset || prev) ? fore : back;
tft.pushColor(d);
if (a.Bits.Bold)
prev = pset;
}
ptr++;
}
}

// カーソルの描画
void drawCursor(uint16_t x, uint16_t y) {
uint16_t xx = x * CH_W;
uint16_t yy = y * CH_H;
tft.setAddrWindow(xx, yy, xx + MAX_CH_X, yy + MAX_CH_Y);
for (uint8_t i = 0; i < CH_H; i++) {
for (uint8_t j = 0; j < CH_W; j++)
tft.pushColor(ILI9341_WHITE);
}
}

// カーソルの表示
void dispCursor(bool forceupdate) {
if (escMode != em::NONE)
return;
if (!forceupdate)
isShowCursor = !isShowCursor;
if (lastShowCursor || (forceupdate && isShowCursor))
sc_updateChar(p_XP, p_YP);
if (isShowCursor) {
drawCursor(XP, YP);
p_XP = XP;
p_YP = YP;
}
if (!forceupdate)
canShowCursor = false;
lastShowCursor = isShowCursor;
}

// 指定行をTFT画面に反映
// 引数
// ln:行番号(0~29)
void sc_updateLine(uint16_t ln) {
uint8_t c;
uint8_t dt;
uint16_t buf[2][SP_W];
uint16_t cnt, idx;
union ATTR a;
union COLOR l;

for (uint16_t i = 0; i < CH_H; i++) { // 1文字高さ分ループ
cnt = 0;
for (uint16_t clm = 0; clm < SC_W; clm++) { // 横文字数分ループ
idx = ln * SC_W + clm;
c = screen[idx]; // キャラクタの取得
a.value = attrib[idx]; // 文字アトリビュートの取得
l.value = colors[idx]; // カラーアトリビュートの取得
uint16_t fore = aColors[l.Color.Foreground | (a.Bits.Blink << 3)];
uint16_t back = aColors[l.Color.Background | (a.Bits.Blink << 3)];
if (a.Bits.Reverse) swap(fore, back);
if (mode_ex.Flgs.ScreenReverse) swap(fore, back);
dt = fontTop[c * CH_H + i]; // 文字内i行データの取得
bool prev = (a.Bits.Underline && (i == MAX_CH_Y));
for (uint16_t j = 0; j < CH_W; j++) {
bool pset = dt & (0x80 >> j);
buf[i & 1][cnt] = (pset || prev) ? fore : back;
if (a.Bits.Bold)
prev = pset;
cnt++;
}
}
tft.pushColors(buf[i & 1], SP_W, true);
}

// SPI1 の DMA 転送待ち (SPI1 DMA1 CH3)
while ((dma_get_isr_bits(DMA1, DMA_CH3) & DMA_ISR_TCIF1) == 0);
}

// カーソルをホーム位置へ移動
void setCursorToHome() {
XP = 0;
YP = 0;
}

// カーソル位置と属性の初期化
void initCursorAndAttribute() {
cAttr.value = defaultAttr.value;
cColor.value = defaultColor.value;
memset(tabs, 0x00, SC_W);
for (uint8_t i = 0; i < SC_W; i += 8)
tabs[i] = 1;
setTopAndBottomMargins(1, SC_H);
mode.value = defaultMode;
mode_ex.value = defaultModeEx;
}

// 一行スクロール
// (DECSTBM の影響を受ける)
void scroll() {
if (mode.Flgs.CrLf) XP = 0;
YP++;
if (YP > M_BOTTOM) {
uint16_t n = SCSIZE - SC_W - ((M_TOP + MAX_SC_Y - M_BOTTOM) * SC_W);
uint16_t idx = SC_W * M_BOTTOM;
uint16_t idx2;
uint16_t idx3 = M_TOP * SC_W;
memmove(&screen[idx3], &screen[idx3 + SC_W], n);
memmove(&attrib[idx3], &attrib[idx3 + SC_W], n);
memmove(&colors[idx3], &colors[idx3 + SC_W], n);
for (uint8_t x = 0; x < SC_W; x++) {
idx2 = idx + x;
screen[idx2] = 0;
attrib[idx2] = defaultAttr.value;
colors[idx2] = defaultColor.value;
}
tft.setAddrWindow(0, M_TOP * CH_H, MAX_SP_X, (M_BOTTOM + 1) * CH_H - 1);
for (uint8_t y = M_TOP; y <= M_BOTTOM; y++)
sc_updateLine(y);
YP = M_BOTTOM;
}
}

// パラメータのクリア
void clearParams(em m) {
escMode = m;
isDECPrivateMode = false;
nVals = 0;
vals[0] = vals[1] = vals[2] = vals[3] = 0;
hasParam = false;
}

// 文字描画
void printChar(char c) {
// [ESC] キー
if (c == 0x1b) {
escMode = em::ES; // エスケープシーケンス開始
return;
}
// エスケープシーケンス
if (escMode == em::ES) {
switch (c) {
case '[':
// Control Sequence Introducer (CSI) シーケンス へ
clearParams(em::CSI);
break;
case '#':
// Line Size Command シーケンス へ
clearParams(em::LSC);
break;
case '(':
// G0 セット シーケンス へ
clearParams(em::G0S);
break;
case ')':
// G1 セット シーケンス へ
clearParams(em::G1S);
break;
default:
// <ESC> xxx: エスケープシーケンス
switch (c) {
case '7':
// DECSC (Save Cursor): カーソル位置と属性を保存
saveCursor();
break;
case '8':
// DECRC (Restore Cursor): 保存したカーソル位置と属性を復帰
restoreCursor();
break;
case '=':
// DECKPAM (Keypad Application Mode): アプリケーションキーパッドモードにセット
keypadApplicationMode();
break;
case '>':
// DECKPNM (Keypad Numeric Mode): 数値キーパッドモードにセット
keypadNumericMode();
break;
case 'D':
// IND (Index): カーソルを一行下へ移動
index(1);
break;
case 'E':
// NEL (Next Line): 改行、カーソルを次行の最初へ移動
nextLine();
break;
case 'H':
// HTS (Horizontal Tabulation Set): 現在の桁位置にタブストップを設定
horizontalTabulationSet();
break;
case 'M':
// RI (Reverse Index): カーソルを一行上へ移動
reverseIndex(1);
break;
case 'Z':
// DECID (Identify): 端末IDシーケンスを送信
identify();
break;
case 'c':
// RIS (Reset To Initial State): リセット
resetToInitialState();
break;
default:
// 未確認のシーケンス
unknownSequence(escMode, c);
break;
}
clearParams(em::NONE);
break;
}
return;
}

// "[" Control Sequence Introducer (CSI) シーケンス
int16_t v1 = 0;
int16_t v2 = 0;

if (escMode == em::CSI) {
escMode = em::CSI2;
isDECPrivateMode = (c == '?');
if (isDECPrivateMode) return;
}

if (escMode == em::CSI2) {
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':
// CUU (Cursor Up): カーソルをPl行上へ移動
v1 = (nVals == 0) ? 1 : vals[0];
reverseIndex(v1);
break;
case 'B':
// CUD (Cursor Down): カーソルをPl行下へ移動
v1 = (nVals == 0) ? 1 : vals[0];
cursorDown(v1);
break;
case 'C':
// CUF (Cursor Forward): カーソルをPc桁右へ移動
v1 = (nVals == 0) ? 1 : vals[0];
cursorForward(v1);
break;
case 'D':
// CUB (Cursor Backward): カーソルをPc桁左へ移動
v1 = (nVals == 0) ? 1 : vals[0];
cursorBackward(v1);
break;
case 'H':
// CUP (Cursor Position): カーソルをPl行Pc桁へ移動
case 'f':
// HVP (Horizontal and Vertical Position): カーソルをPl行Pc桁へ移動
v1 = (nVals == 0) ? 1 : vals[0];
v2 = (nVals <= 1) ? 1 : vals[1];
cursorPosition(v1, v2);
break;
case 'J':
// ED (Erase In Display): 画面を消去
v1 = (nVals == 0) ? 0 : vals[0];
eraseInDisplay(v1);
break;
case 'K':
// EL (Erase In Line) 行を消去
v1 = (nVals == 0) ? 0 : vals[0];
eraseInLine(v1);
break;
case 'L':
// IL (Insert Line): カーソルのある行の前に Ps 行空行を挿入
v1 = (nVals == 0) ? 1 : vals[0];
insertLine(v1);
break;
case 'M':
// DL (Delete Line): カーソルのある行から Ps 行を削除
v1 = (nVals == 0) ? 1 : vals[0];
deleteLine(v1);
break;
case 'c':
// DA (Device Attributes): 装置オプションのレポート
v1 = (nVals == 0) ? 0 : vals[0];
deviceAttributes(v1);
break;
case 'g':
// TBC (Tabulation Clear): タブストップをクリア
v1 = (nVals == 0) ? 0 : vals[0];
tabulationClear(v1);
break;
case 'h':
if (isDECPrivateMode) {
// DECSET (DEC Set Mode): モードのセット
decSetMode(vals, nVals);
} else {
// SM (Set Mode): モードのセット
setMode(vals, nVals);
}
break;
case 'l':
if (isDECPrivateMode) {
// DECRST (DEC Reset Mode): モードのリセット
decResetMode(vals, nVals);
} else {
// RM (Reset Mode): モードのリセット
resetMode(vals, nVals);
}
break;
case 'm':
// SGR (Select Graphic Rendition): 文字修飾の設定
if (nVals == 0)
nVals = 1; // vals[0] = 0
selectGraphicRendition(vals, nVals);
break;
case 'n':
// DSR (Device Status Report): 端末状態のリポート
v1 = (nVals == 0) ? 0 : vals[0];
deviceStatusReport(v1);
break;
case 'q':
// DECLL (Load LEDS): LED の設定
v1 = (nVals == 0) ? 0 : vals[0];
loadLEDs(v1);
break;
case 'r':
// DECSTBM (Set Top and Bottom Margins): スクロール範囲をPt行からPb行に設定
v1 = (nVals == 0) ? 1 : vals[0];
v2 = (nVals <= 1) ? SC_H : vals[1];
setTopAndBottomMargins(v1, v2);
break;
case 'y':
// DECTST (Invoke Confidence Test): テスト診断を行う
if ((nVals > 1) && (vals[0] = 2))
invokeConfidenceTests(vals[1]);
break;
default:
// 未確認のシーケンス
unknownSequence(escMode, c);
break;
}
clearParams(em::NONE);
}
return;
}

// "#" Line Size Command シーケンス
if (escMode == em::LSC) {
switch (c) {
case '3':
// DECDHL (Double Height Line): カーソル行を倍高、倍幅、トップハーフへ変更
doubleHeightLine_TopHalf();
break;
case '4':
// DECDHL (Double Height Line): カーソル行を倍高、倍幅、ボトムハーフへ変更
doubleHeightLine_BotomHalf();
break;
case '5':
// DECSWL (Single-width Line): カーソル行を単高、単幅へ変更
singleWidthLine();
break;
case '6':
// DECDWL (Double-Width Line): カーソル行を単高、倍幅へ変更
doubleWidthLine();
break;
case '8':
// DECALN (Screen Alignment Display): 画面を文字‘E’で埋める
screenAlignmentDisplay();
break;
default:
// 未確認のシーケンス
unknownSequence(escMode, c);
break;
}
clearParams(em::NONE);
return;
}

// "(" G0 セットシーケンス
if (escMode == em::G0S) {
// SCS (Select Character Set): G0 文字コードの設定
setG0charset(c);
clearParams(em::NONE);
return;
}

// ")" G1 セットシーケンス
if (escMode == em::G1S) {
// SCS (Select Character Set): G1 文字コードの設定
setG1charset(c);
clearParams(em::NONE);
return;
}

// 改行 (LF / VT / FF)
if ((c == 0x0a) || (c == 0x0b) || (c == 0x0c)) {
scroll();
return;
}

// 復帰 (CR)
if (c == 0x0d) {
XP = 0;
return;
}

// バックスペース (BS)
if ((c == 0x08) || (c == 0x7f)) {
cursorBackward(1);
uint16_t idx = YP * SC_W + XP;
screen[idx] = 0;
attrib[idx] = 0;
colors[idx] = cColor.value;
sc_updateChar(XP, YP);
return;
}

// タブ (TAB)
if (c == 0x09) {
int16_t idx = -1;
for (int16_t i = XP + 1; i < SC_W; i++) {
if (tabs[i]) {
idx = i;
break;
}
}
XP = (idx == -1) ? MAX_SC_X : idx;
return;
}

// 通常文字
if (XP < SC_W) {
uint16_t idx = YP * SC_W + XP;
screen[idx] = c;
attrib[idx] = cAttr.value;
colors[idx] = cColor.value;
sc_updateChar(XP, YP);
}

// X 位置 + 1
XP ++;

// 折り返し行
if (XP >= SC_W) {
if (mode_ex.Flgs.WrapLine)
scroll();
else
XP = MAX_SC_X;
}
}

// 文字列描画
void printString(const char *str) {
while (*str) printChar(*str++);
}

// エスケープシーケンス
// -----------------------------------------------------------------------------

// DECSC (Save Cursor): カーソル位置と属性を保存
void saveCursor() {
b_XP = XP;
b_YP = YP;
bAttr.value = cAttr.value;
bColor.value = cColor.value;
}

// DECRC (Restore Cursor): 保存したカーソル位置と属性を復帰
void restoreCursor() {
XP = b_XP;
YP = b_YP;
cAttr.value = bAttr.value;
cColor.value = bColor.value;
}

// DECKPAM (Keypad Application Mode): アプリケーションキーパッドモードにセット
void keypadApplicationMode() {
Serial.println(F("Unimplement: keypadApplicationMode"));
}

// DECKPNM (Keypad Numeric Mode): 数値キーパッドモードにセット
void keypadNumericMode() {
Serial.println(F("Unimplement: keypadNumericMode"));
}

// IND (Index): カーソルを一行下へ移動
// (DECSTBM の影響を受ける)
void index(int16_t v) {
cursorDown(v);
}

// NEL (Next Line): 改行、カーソルを次行の最初へ移動
// (DECSTBM の影響を受ける)
void nextLine() {
scroll();
}

// HTS (Horizontal Tabulation Set): 現在の桁位置にタブストップを設定
void horizontalTabulationSet() {
tabs[XP] = 1;
}

// RI (Reverse Index): カーソルを一行上へ移動
// (DECSTBM の影響を受ける)
void reverseIndex(int16_t v) {
cursorUp(v);
}

// DECID (Identify): 端末IDシーケンスを送信
void identify() {
deviceAttributes(0); // same as DA (Device Attributes)
}

// RIS (Reset To Initial State) リセット
void resetToInitialState() {
tft.fillScreen(aColors[defaultColor.Color.Background]);
initCursorAndAttribute();
eraseInDisplay(2);
}

// "[" Control Sequence Introducer (CSI) シーケンス
// -----------------------------------------------------------------------------

// CUU (Cursor Up): カーソルをPl行上へ移動
// (DECSTBM の影響を受ける)
void cursorUp(int16_t v) {
YP -= v;
if (YP <= M_TOP) YP = M_TOP;
}

// CUD (Cursor Down): カーソルをPl行下へ移動
// (DECSTBM の影響を受ける)
void cursorDown(int16_t v) {
YP += v;
if (YP >= M_BOTTOM) YP = M_BOTTOM;
}

// CUF (Cursor Forward): カーソルをPc桁右へ移動
void cursorForward(int16_t v) {
XP += v;
if (XP >= SC_W) XP = MAX_SC_X;
}

// CUB (Cursor Backward): カーソルをPc桁左へ移動
void cursorBackward(int16_t v) {
XP -= v;
if (XP <= 0) XP = 0;
}

// CUP (Cursor Position): カーソルをPl行Pc桁へ移動
// HVP (Horizontal and Vertical Position): カーソルをPl行Pc桁へ移動
void cursorPosition(uint8_t y, uint8_t x) {
YP = y - 1;
if (YP >= SC_H) YP = MAX_SC_Y;
XP = x - 1;
if (XP >= SC_W) XP = MAX_SC_X;
}

// 画面を再描画
void refreshScreen() {

tft.setAddrWindow(0, 0, MAX_SP_X, MAX_SP_Y);
for (uint8_t i = 0; i < SC_H; i++)
sc_updateLine(i);
}

// ED (Erase In Display): 画面を消去
void eraseInDisplay(uint8_t m) {
uint8_t sl = 0, el = 0;
uint16_t idx = 0, n = 0;

switch (m) {
case 0:
// カーソルから画面の終わりまでを消去
sl = YP;
el = MAX_SC_Y;
idx = YP * SC_W + XP;
n = SCSIZE - (YP * SC_W + XP);
break;
case 1:
// 画面の始めからカーソルまでを消去
sl = 0;
el = YP;
idx = 0;
n = YP * SC_W + XP + 1;
break;
case 2:
// 画面全体を消去
sl = 0;
el = MAX_SC_Y;
idx = 0;
n = SCSIZE;
break;
}

if (m <= 2) {
memset(&screen[idx], 0x00, n);
memset(&attrib[idx], defaultAttr.value, n);
memset(&colors[idx], defaultColor.value, n);
tft.setAddrWindow(0, sl * CH_H, MAX_SP_X, (el + 1) * CH_H - 1);
for (uint8_t i = sl; i <= el; i++)
sc_updateLine(i);
}
}

// EL (Erase In Line): 行を消去
void eraseInLine(uint8_t m) {
uint16_t slp = 0, elp = 0;

switch (m) {
case 0:
// カーソルから行の終わりまでを消去
slp = YP * SC_W + XP;
elp = YP * SC_W + MAX_SC_X;
break;
case 1:
// 行の始めからカーソルまでを消去
slp = YP * SC_W;
elp = YP * SC_W + XP;
break;
case 2:
// 行全体を消去
slp = YP * SC_W;
elp = YP * SC_W + MAX_SC_X;
break;
}

if (m <= 2) {
uint16_t n = elp - slp + 1;
memset(&screen[slp], 0x00, n);
memset(&attrib[slp], defaultAttr.value, n);
memset(&colors[slp], defaultColor.value, n);
tft.setAddrWindow(0, YP * CH_H, MAX_SP_X, (YP + 1) * CH_H - 1);
sc_updateLine(YP);
}
}

// IL (Insert Line): カーソルのある行の前に Ps 行空行を挿入
// (DECSTBM の影響を受ける)
void insertLine(uint8_t v) {
int16_t rows = v;
if (rows == 0) return;
if (rows > ((M_BOTTOM + 1) - YP)) rows = (M_BOTTOM + 1) - YP;
int16_t idx = SC_W * YP;
int16_t n = SC_W * rows;
int16_t idx2 = idx + n;
int16_t move_rows = (M_BOTTOM + 1) - YP - rows;
int16_t n2 = SC_W * move_rows;

if (move_rows > 0) {
memmove(&screen[idx2], &screen[idx], n2);
memmove(&attrib[idx2], &attrib[idx], n2);
memmove(&colors[idx2], &colors[idx], n2);
}
memset(&screen[idx], 0x00, n);
memset(&attrib[idx], defaultAttr.value, n);
memset(&colors[idx], defaultColor.value, n);
tft.setAddrWindow(0, YP * CH_H, MAX_SP_X, (M_BOTTOM + 1) * CH_H - 1);
for (uint8_t y = YP; y <= M_BOTTOM; y++)
sc_updateLine(y);
}

// DL (Delete Line): カーソルのある行から Ps 行を削除
// (DECSTBM の影響を受ける)
void deleteLine(uint8_t v) {
int16_t rows = v;
if (rows == 0) return;
if (rows > ((M_BOTTOM + 1) - YP)) rows = (M_BOTTOM + 1) - YP;
int16_t idx = SC_W * YP;
int16_t n = SC_W * rows;
int16_t idx2 = idx + n;
int16_t move_rows = (M_BOTTOM + 1) - YP - rows;
int16_t n2 = SC_W * move_rows;
int16_t idx3 = (M_BOTTOM + 1) * SC_W - n;

if (move_rows > 0) {
memmove(&screen[idx], &screen[idx2], n2);
memmove(&attrib[idx], &attrib[idx2], n2);
memmove(&colors[idx], &colors[idx2], n2);
}
memset(&screen[idx3], 0x00, n);
memset(&attrib[idx3], defaultAttr.value, n);
memset(&colors[idx3], defaultColor.value, n);
tft.setAddrWindow(0, YP * CH_H, MAX_SP_X, (M_BOTTOM + 1) * CH_H - 1);
for (uint8_t y = YP; y <= M_BOTTOM; y++)
sc_updateLine(y);
}

// CPR (Cursor Position Report): カーソル位置のレポート
void cursorPositionReport(uint16_t y, uint16_t x) {
Serial3.print(F("\e["));
Serial3.print(String(y, DEC));
Serial3.print(F(";"));
Serial3.print(String(x, DEC));
Serial3.print(F("R")); // CPR (Cursor Position Report)
}

// DA (Device Attributes): 装置オプションのレポート
// オプションのレポート
void deviceAttributes(uint8_t m) {
Serial3.print(F("\e[?1;0c")); // 0 No options
}

// TBC (Tabulation Clear): タブストップをクリア
void tabulationClear(uint8_t m) {
switch (m) {
case 0:
// 現在位置のタブストップをクリア
tabs[XP] = 0;
break;
case 3:
// すべてのタブストップをクリア
memset(tabs, 0x00, SC_W);
break;
}
}

// LNM (Line Feed / New Line Mode): 改行モード
void lineMode(bool m) {
mode.Flgs.CrLf = m;
}

// DECSCNM (Screen Mode): // 画面反転モード
void screenMode(bool m) {
mode_ex.Flgs.ScreenReverse = m;
refreshScreen();
}

// DECAWM (Auto Wrap Mode): 自動折り返しモード
void autoWrapMode(bool m) {
mode_ex.Flgs.WrapLine = m;
}

// SM (Set Mode): モードのセット
void setMode(int16_t *vals, int16_t nVals) {
for (int16_t i = 0; i < nVals; i++) {
switch (vals[i]) {
case 20:
// LNM (Line Feed / New Line Mode)
lineMode(true);
break;
default:
Serial.print(F("Unimplement: setMode "));
Serial.println(String(vals[i], DEC));
break;
}
}
}

// DECSET (DEC Set Mode): モードのセット
void decSetMode(int16_t *vals, int16_t nVals) {
for (int16_t i = 0; i < nVals; i++) {
switch (vals[i]) {
case 5:
// DECSCNM (Screen Mode): // 画面反転モード
screenMode(true);
break;
case 7:
// DECAWM (Auto Wrap Mode): 自動折り返しモード
autoWrapMode(true);
break;
default:
Serial.print(F("Unimplement: decSetMode "));
Serial.println(String(vals[i], DEC));
break;
}
}
}

// RM (Reset Mode): モードのリセット
void resetMode(int16_t *vals, int16_t nVals) {
for (int16_t i = 0; i < nVals; i++) {
switch (vals[i]) {
case 20:
// LNM (Line Feed / New Line Mode)
lineMode(false);
break;
default:
Serial.print(F("Unimplement: resetMode "));
Serial.println(String(vals[i], DEC));
break;
}
}
}

// DECRST (DEC Reset Mode): モードのリセット
void decResetMode(int16_t *vals, int16_t nVals) {
for (int16_t i = 0; i < nVals; i++) {
switch (vals[i]) {
case 5:
// DECSCNM (Screen Mode): // 画面反転モード
screenMode(false);
break;
case 7:
// DECAWM (Auto Wrap Mode): 自動折り返しモード
autoWrapMode(false);
break;
default:
Serial.print(F("Unimplement: decResetMode "));
Serial.println(String(vals[i], DEC));
break;
}
}
}

// SGR (Select Graphic Rendition): 文字修飾の設定
void selectGraphicRendition(int16_t *vals, int16_t nVals) {
uint8_t seq = 0;
uint16_t r, g, b, cIdx;
bool isFore = true;
for (int16_t i = 0; i < nVals; i++) {
int16_t v = vals[i];
switch (seq) {
case 0:
switch (v) {
case 0:
// 属性クリア
cAttr.value = 0;
cColor.value = defaultColor.value;
break;
case 1:
// 太字
cAttr.Bits.Bold = 1;
break;
case 4:
// アンダーライン
cAttr.Bits.Underline = 1;
break;
case 5:
// 点滅 (明色表現)
cAttr.Bits.Blink = 1;
break;
case 7:
// 反転
cAttr.Bits.Reverse = 1;
break;
case 21:
// 二重下線 or 太字オフ
cAttr.Bits.Bold = 0;
break;
case 22:
// 太字オフ
cAttr.Bits.Bold = 0;
break;
case 24:
// アンダーラインオフ
cAttr.Bits.Underline = 0;
break;
case 25:
// 点滅 (明色表現) オフ
cAttr.Bits.Blink = 0;
break;
case 27:
// 反転オフ
cAttr.Bits.Reverse = 0;
break;
case 38:
seq = 1;
isFore = true;
break;
case 39:
// 前景色をデフォルトに戻す
cColor.Color.Foreground = defaultColor.Color.Foreground;
break;
case 48:
seq = 1;
isFore = false;
break;
case 49:
// 背景色をデフォルトに戻す
cColor.Color.Background = defaultColor.Color.Background;
break;
default:
if (v >= 30 && v <= 37) {
// 前景色
cColor.Color.Foreground = v - 30;
} else if (v >= 40 && v <= 47) {
// 背景色
cColor.Color.Background = v - 40;
}
break;
}
break;
case 1:
switch (v) {
case 2:
// RGB
seq = 3;
break;
case 5:
// Color Index
seq = 2;
break;
default:
seq = 0;
break;
}
break;
case 2:
// Index Color
if (v < 256) {
if (v < 16) {
// ANSI カラー (16 色のインデックスカラーが使われる)
cIdx = v;
} else if (v < 232) {
// 6x6x6 RGB カラー (8 色のインデックスカラー中で最も近い色が使われる)
b = ( (v - 16) % 6) / 3;
g = (((v - 16) / 6) % 6) / 3;
r = (((v - 16) / 36) % 6) / 3;
cIdx = (b << 2) | (g << 1) | r;
} else {
// 24 色グレースケールカラー (2 色のグレースケールカラーが使われる)
if (v < 244)
cIdx = clBlack;
else
cIdx = clWhite;
}
if (isFore)
cColor.Color.Foreground = cIdx;
else
cColor.Color.Background = cIdx;
}
seq = 0;
break;
case 3:
// RGB - R
seq = 4;
break;
case 4:
// RGB - G
seq = 5;
break;
case 5:
// RGB - B
// RGB (8 色のインデックスカラー中で最も近い色が使われる)
r = map(vals[i - 2], 0, 255, 0, 1);
g = map(vals[i - 1], 0, 255, 0, 1);
b = map(vals[i - 0], 0, 255, 0, 1);
cIdx = (b << 2) | (g << 1) | r;
if (isFore)
cColor.Color.Foreground = cIdx;
else
cColor.Color.Background = cIdx;
seq = 0;
break;
default:
seq = 0;
break;
}
}
}

// DSR (Device Status Report): 端末状態のリポート
void deviceStatusReport(uint8_t m) {
switch (m) {
case 5:
Serial3.print(F("\e[0n")); // 0 Ready, No malfunctions detected (default) (DSR)
break;
case 6:
cursorPositionReport(XP, YP); // CPR (Cursor Position Report)
break;
}
}

// DECLL (Load LEDS): LED の設定
void loadLEDs(uint8_t m) {
switch (m) {
case 0:
// すべての LED をオフ
digitalWrite(LED_01, LOW);
digitalWrite(LED_02, LOW);
digitalWrite(LED_03, LOW);
digitalWrite(LED_04, LOW);
break;
case 1:
// LED1 をオン
digitalWrite(LED_01, HIGH);
break;
case 2:
// LED2 をオン
digitalWrite(LED_02, HIGH);
break;
case 3:
// LED3 をオン
digitalWrite(LED_03, HIGH);
break;
case 4:
// LED4 をオン
digitalWrite(LED_04, HIGH);
break;
}
}

// DECSTBM (Set Top and Bottom Margins): スクロール範囲をPt行からPb行に設定
void setTopAndBottomMargins(int16_t s, int16_t e) {
if (e <= s) return;
M_TOP = s - 1;
if (M_TOP > MAX_SC_Y) M_TOP = MAX_SC_Y;
M_BOTTOM = e - 1;
if (M_BOTTOM > MAX_SC_Y) M_BOTTOM = MAX_SC_Y;
setCursorToHome();
}

// DECTST (Invoke Confidence Test): テスト診断を行う
void invokeConfidenceTests(uint8_t m) {
nvic_sys_reset();
}

// "]" Operating System Command (OSC) シーケンス
// -----------------------------------------------------------------------------

// "#" Line Size Command シーケンス
// -----------------------------------------------------------------------------

// DECDHL (Double Height Line): カーソル行を倍高、倍幅、トップハーフへ変更
void doubleHeightLine_TopHalf() {
Serial.println(F("Unimplement: doubleHeightLine_TopHalf"));
}

// DECDHL (Double Height Line): カーソル行を倍高、倍幅、ボトムハーフへ変更
void doubleHeightLine_BotomHalf() {
Serial.println(F("Unimplement: doubleHeightLine_BotomHalf"));
}

// DECSWL (Single-width Line): カーソル行を単高、単幅へ変更
void singleWidthLine() {
Serial.println(F("Unimplement: singleWidthLine"));
}

// DECDWL (Double-Width Line): カーソル行を単高、倍幅へ変更
void doubleWidthLine() {
Serial.println(F("Unimplement: doubleWidthLine"));
}

// DECALN (Screen Alignment Display): 画面を文字‘E’で埋める
void screenAlignmentDisplay() {
tft.setAddrWindow(0, 0, MAX_SP_X, MAX_SP_Y);
memset(screen, 0x45, SCSIZE);
memset(attrib, defaultAttr.value, SCSIZE);
memset(colors, defaultColor.value, SCSIZE);
for (uint8_t y = 0; y < SC_H; y++)
sc_updateLine(y);
}

// "(" G0 Sets Sequence
// -----------------------------------------------------------------------------

// G0 文字コードの設定
void setG0charset(char c) {
Serial.println(F("Unimplement: setG0charset"));
}

// "(" G1 Sets Sequence
// -----------------------------------------------------------------------------

// G1 文字コードの設定
void setG1charset(char c) {
Serial.println(F("Unimplement: setG1charset"));
}

// Unknown Sequence
// -----------------------------------------------------------------------------

// 不明なシーケンス
void unknownSequence(em m, char c) {
String s = (m != em::NONE) ? "[ESC]" : "";
switch (m) {
case em::CSI:
s = s + " [";
break;
case em::LSC:
s = s + " #";
break;
case em::G0S:
s = s + " (";
break;
case em::G1S:
s = s + " )";
break;
case em::CSI2:
s = s + " [";
if (isDECPrivateMode)
s = s + "?";
break;
}
Serial.print(F("Unknown: "));
Serial.print(s);
Serial.print(F(" "));
Serial.print(c);
}

// -----------------------------------------------------------------------------

// タイマーハンドラ
void handle_timer() {
canShowCursor = true;
}

// セットアップ
void setup() {
keyboard.begin(KBD_DAT, KBD_CLK);
Serial.begin(115200);
Serial3.begin(DEFAULT_BAUDRATE);

// LED の初期化
pinMode(LED_01, OUTPUT);
pinMode(LED_02, OUTPUT);
pinMode(LED_03, OUTPUT);
pinMode(LED_04, OUTPUT);
digitalWrite(LED_01, LOW);
digitalWrite(LED_02, LOW);
digitalWrite(LED_03, LOW);
digitalWrite(LED_04, LOW);

// TFT の初期化
tft.begin();
tft.setRotation(3);
fontTop = (uint8_t*)font6x8tt + 3;
resetToInitialState();
printString("\e[0;44m *** Terminal Init *** \e[0m\n");
setCursorToHome();

// カーソル用タイマーの設定
Timer3.pause();
Timer3.setPrescaleFactor(7200);
Timer3.setOverflow(2500); // 250ms
Timer3.attachInterrupt(TIMER_UPDATE_INTERRUPT, handle_timer);
Timer3.setCount(0);
Timer3.refresh();
Timer3.resume();
}

// ループ
void loop() {
bool needCursorUpdate = false;

// キーボード入力処理 (通信相手への出力)
if (keyboard.available()) {
char c = keyboard.read();
switch (c) {
case PS2_UPARROW:
Serial3.print(F("\e[A"));
break;
case PS2_DOWNARROW:
Serial3.print(F("\e[B"));
break;
case PS2_RIGHTARROW:
Serial3.print(F("\e[C"));
break;
case PS2_LEFTARROW:
Serial3.print(F("\e[D"));
break;
case PS2_F1:
Serial3.print(F("\e[P"));
break;
case PS2_F2:
Serial3.print(F("\e[Q"));
break;
case PS2_F3:
Serial3.print(F("\e[R"));
break;
case PS2_F4:
Serial3.print(F("\e[S"));
break;
default:
Serial3.print(c);
break;
}
needCursorUpdate = c;
}

// カーソル表示処理
if (canShowCursor || needCursorUpdate)
dispCursor(needCursorUpdate);

// シリアル入力処理 (通信相手からの入力)
while (Serial3.available()) {
char c = Serial3.read();
switch (c) {
case 0x07:
tone(SPK_PIN, 4000, 583);
break;
default:
printChar(c);
break;
}
}
}
ページ: 1 2 [3]
WP Forum Server by ForumPress | LucidCrew
バージョン: 1.7.5 ; ページロード: 0.086 sec.