KON ----- Kanji ON Linux console ----- このファイルは KON の内部構造をドキュメント化したものです。まだ未完成ですが、 バグ取りや改造の際の参考にして下さい。 1.文字出力の流れ KON では仮想テキスト VRAM を通して画面出力を行っています。PTY からの出力は BUFSIZ 単位でフラグメント化されて VT エミュレータに引き渡されます。VT エミュ レータは1文字ずつ解析して仮想テキストに出力します。1つのフラグメントが処理さ れた時点でコンソールがアクティブならば仮想テキストの内容をそのまま VGA 画面に書 き込みます。 1.1.疑似端末 1.2.VT エミュレータ ●構造体 con VT エミュレータに於けるテキスト状態情報は con 構造体に記憶されています。 VT エミュレータではテキスト座標系は左端を原点[0, 0]として計算しています。 struct _con_info { char x, 現在の X 座標 y, 現在の Y 座標 xmax, X 座標最大値 ymin, Y 座標最小値 ymax, Y 座標最大値 tab; TAB 個数 u_char fcol, フォアグランドカラー bcol, バックグランドカラー attr, 文字属性フラグ sfonth, 半角フォントの高さ wfonth, 全角フォントの高さ knj1; 漢字コード第1バイト enum { ESC_NEUTRAL, ESC 外 ESC_START, '\x1B' 受信 ESC_BRA, '[' 受信 ESC_SI, '$' 受信 ESC_SO, '(' 受信 ESC_QUESTION, '?' 受信 } esc; エスケープシーケンスの状態変数 bool knj, JIS 漢字モードフラグ soft, ソフトウェアスクロールフラグ ins, 挿入モードフラグ active, アクティブコンソールフラグ saved, 焼き付け防止状態 wrap; wrap 状態フラグ }; ●変数 scrollLine con.y がリージョン(con.ymin〜con.ymax の範囲)外になればスクロールする必要が 有ります。しかし、uum などの FEP ではシステムラインに文字出力する際に必ずリー ジョン外になります。そこで、\Ey:xH によるカーソル移動に対してはスクロールしない ようにしました。これ以外の方法で con.y がリージョン外に出た場合は、変数 scrollLine に適当なスクロール行数が加算されます。con.ymin より小さい場合は負 数、con.ymax より大きい場合は正数がそれぞれ加算されます。制御コード(表示不可能 文字)が入力された時点で変数 scrollLine を実際のスクロールに反映します。 1.3.仮想テキスト 仮想テキストは3つのバッファからなります。 ●テキストバッファ テキストのキャラクタコードが格納されます。半角文字はその ASCII コードがそのま ま格納されます。漢字の場合は EUC/SJIS ともに JIS コードに変換されて格納されます。 また、BIG5 では Mule 拡張の "ESC$(0" が来れば BIG5 の 16bit コードに変換されて 格納されます。また、EUC の場合はどの言語でも漢字同様に 8bit が落されて格納され ますので、BIG5 で EUC にすると情報が落ちます。 ●色・属性バッファ 色と属性の情報が格納されます。ただし、全角文字の場合は若いアドレスにしか格納 されません。各ビットの意味は以下の様なものになっています。 MSB LSB |b7|b6|b5|b4|b3|b2|b1|b0| b2〜b0: フォアグランド色 b3: 未使用 b6〜b4: バックグランド色 b7: 下線属性 ●フラグバッファ フラグバッファは該当テキストの変更情報と言語情報を持っています。MSBは変更情 報で、テキストへの書き込みが起こると 0 になり、それが実画面へ反映されると 1 と なります。次の 2bit は Double Byte コードを意味しており、第 1 バイトならば b5 が、第 2 バイトなら b6 が 1 となります。第 1 バイトの b3-b0 には言語情報が入り ます。今後、対応言語が増えれば b4 あるいは 第 2 バイトの空きビットを言語情報に 割り当てる予定である。言語情報は、 enum { DF_GB2312, DF_JISX0208, DF_KSC5601, DF_JISX0212, DF_BIG5_0, DF_BIG5_1 }; と定義しています。 MSB LSB | b7| b6| b5|b4||b3|b2|b1|b0| |CLEAN_S|LATCH_2|LATCH_1| ||<--------->| |0=latch| byte2| byte1| || LANG| 80x30 の 標準 VGA 画面でテキスト座標[5, 3](左隅は[0, 0])に「1あ」を白色背景色 無しで表示すると、 相対アドレス: 80 x 3 + 5 = 245 「1」の ASCII コード: 0x31 「あ」の JIS コード: 0x2422 白色背景色なし: 00000111(B) = 0x07 textBuff[245] = 0x31 attrBuff[245] = 0x07 flagBuff[245] = 0x00 textBuff[246] = 0xA2 textBuff[247] = 0x24 attrBuff[246] = 0x07 attrBuff[247] = 0x07 flagBuff[246] = 0x22 flagBuff[247] = 0x40 となります。 2.VGA 仮想テキストを採用することにより VGA 操作部分を小さくすることができました。 EGA などへの移植も楽になったと思います。 2.1.レジスタ 2.2.基本操作 ●構造体 dInfo 表示能力を示す情報を保存しています。各値は kon.cfg から読み込まれたもの、 あるいはそれらにより算出されたものです。 struct dispInfo { int gsize; グラフィックメモリサイズ = glineByte * gydim short gxdim, グラフィック X ドット数 glineChar の整数倍になるように正規化された値 gydim, グラフィック Y ライン数 txmax, テキスト X 最大値(con.xmax の初期値) tymax, テキスト Y 最大値(con.ymax の初期値) glineChar, テキスト1行分のグラフィックライン数 = gydim / (tymax + 1) glineByte, グラフィック1行分のバイト数 = gxdim / 8 tlineByte; テキスト1行分のグラフィックバイト数 = glineChar * glineByte }; ●構造体 cInfo カーソルに関する情報です。点滅回数は偶数なら表示中を意味しており、スクリーン セイバーのカウンターにも使われています。addr は VgaSetCursorAddress によって 計算されます。kanji はカーソル位置が全角なら非0値となります。表示スイッチは ESC シーケンスによって操作されます。 struct cursorInfo { short kanji; 漢字の上にあれば TRUE u_int addr; VRAM アドレス bool sw; FALSE なら表示禁止 int interval; 点滅間隔 int count; 点滅用カウント bool shown; 表示中フラグ }; 2.3.WideText KON では VGA を高解像度モードに移行するために VGA の CRT 制御レジスタと MISC レジスタを kon.cfg ファイルで指定できる様にしています。VGA 本来の規格からすれば 800x600 までの解像度であれば CRT 制御レジスタと MISC レジスタのみによって VGA の解像度を変更できるように思えるのですが、SVGA カードが持つ複数のモードの殆んど は他のレジスタに依存しているようです。 ●構造体 regText, regGraph VGA のレジスタ情報です。regGraph の場合は CRT 制御レジスタ、misc レジスタは kon.cfg から読み込まれます。CRT 制御レジスタは更に計算が施されます。dInfo.gydim は kon.cfg から読み込まれる時に表示有効な値に変換されます。この値を基に VGA 側 の表示ライン数を変更する必要があります。表示ライン数は CRT 制御レジスタにあり、 0x12 の全ビット : 下位16ビット 0x07 の第2ビット: 第9ビット 0x07 の第7ビット: 第10ビット となっていますので、 regGraph.crt[0x12] = (dInfo.gydim - 1) & 0xFF; regGraph.crt[7] &= 0xBD; regGraph.crt[7] |= ((dInfo.gydim - 1) & 0x100) >> 7; regGraph.crt[7] |= ((dInfo.gydim - 1) & 0x200) >> 3; という操作を施しています。 struct vgaRegs { u_char crt[VGACRT_CNT], CRT 制御レジスタ att[VGAATTR_CNT], 属性レジスタ gra[VGAGRP_CNT], グラフィックス制御レジスタ seq[VGASEQ_CNT], シーケンサ mis; misc レジスタ }; ●変数 LineComp8, LineComp9 ライン比較レジスタは 0x18 にありますが、その第9ビットと第10ビットはそれぞれ 0x07 および 0x09 にあります。これらのビット以外の値とレジスタ番号をまとめた物が LineComp8 と LineComp9 です。 3.ソケットによる通信 様々な拡張性を考えてソケットを用意したのですが、まだフォントロードにしか使わ れていません。まともなプロトコルを作成・実装する知識も時間もないので通信方式が いい加減なものになっています。 3.1.ソケット名 KON のソケットは /tmp に作成されます。その名前は .kon の後に数文字1文字をと もなっています。この数字は KON が利用している疑似端末名の末尾の数文字と等しくな ります。また、GON ライブラリの SocketClientOpen 関数を利用すれば自動的に適当な ソケット名を使うので、名前を意識すること無くソケットをオープンすることができま す。 3.2.基本的な通信方式 通信は messageHeader 構造体を用いて行われます。 struct messageHeader { u_char cno, クライアント番号(未使用) cmd; 機能コード }; クライアントが機能コードを指定することによって通信が開始されます。機能コード は半角文字1文字です。 S: 半角文字フォントロード W: 全角文字フォントロード 機能コードを認識した KON は ACK を機能コードとしいて送り返します。認識でき なかった場合は NAK を返します。以後、データを受信するごとに KON は ACK あるい は NAK を送信します。クライアント側は NAK を受け取れば直ちに送信を中止なけれ ばなりません。 3.3.フォントロードの方式 フォントローダが KON にフォントデータを送信する際の手順について説明します。 フォントローダ (fld.*) <-- /usr/tmp/.kon? --> KON Function: S|W --------------------> S = 半角フォント W = 漢字フォント <-------------------- 肯定応答 (ACK) fontInfo 構造体 --------------------> <-------------------- 肯定応答 (ACK) フォントデータを BUFSIZ でフラグメント化して以下を繰り返す: データグラム --------------------> <-------------------- 肯定応答 (ACK) 4.マウスドライバ マウスドライバは selection-1.4 を参考に書きました。したがって5機種対応となっ ていますが、こちらでテストしているのは MircoSoft と Mouse Systems のみです。 ●構造体 mInfo マウスに関する情報を管理しています。座標情報は全てテキスト座標系で表現されて います。カーソル表示寿命はカーソルが点滅するごとにデクリメントされ、0になった 時点でマウスカーソルが表示されなくなります。マウスの状態が変化した時は常にこの 値を MOUSE_LIFETIME(デフォルト 22) で初期化するようになっています。 struct mouseInfo { char x, y, マウスカーソル位置 dx, dy, マウスカーソル移動値 sx, sy, 左ボタンクリック位置 sw, カーソル表示寿命 stat; ボタン状態 }; ●変数 dx, dy 関数 MouseAnalyzePacket で使われている static 変数 dx, dy はグラフィックス 座標系のマウス移動値です。マウスをゆっくりと動かした場合でもちゃんと反映される 様にするために用意しました。 ●変数 oldstat 関数 MouseAnalyzePacket で使われている static 変数 oldstat は mInfo.stat の 前回の状態です。 ●ファイル /tmp/.kontmp マウスでカットしたバッファを格納するファイルです。 5.多言語に関する扱い フォントは lib/coding.c に登録されているものが利用できます。新たなフォント の登録は Single Byte の場合は coding.c:fSRegs, coding.c:fldSRegs に、 Double Byte の場合は coding.c:fDRegs, coding.c:fldDRegs, そして vt.h の DF_?????? に記述して下さい。それぞれの情報は 1 対 1 になっていますので、 順序を守って下さい。 6.GON ライブラリ KON との通信を容易に行うために用意したライブラリです。 6.1.メモリ操作関数 ■void PortOutw(u_short value, u_short port) I/O ポートへ2バイト単位の出力を行います。 ■void PortOutb(char value, u_short port) I/O ポートへ1バイト単位の出力を行います。 ■u_char PortInb(unsigned short port) I/O ポートから1バイト単位の入力を行います。 ■void bzero2(void *buff, int n) stosb を使った高速なゼロ埋め関数です。結果は bzero と同じです。 ■void wzero(void *buff, int n) stosw を使った高速なゼロ埋め関数です。n バイトをゼロ埋めすることに注意して 下さい。 ■void lzero(void *buff, int n) stosl を使った高速なゼロ埋め関数です。n バイトをゼロ埋めすることに注意して 下さい。 ■void bmove(void *dst, void *src, int n) movsb を使った高速なバッファ転送関数です。 ■void brmove(void *dst, void *src, int n) movsb を使った高速な逆方向バッファ転送関数です。src, dst から逆方向に n バイトの転送を行います。 ■void wmove(void *dst, void *src, int n) movsw を使った高速なバッファ転送関数です。n バイト転送することに注意して下 さい。 ■void lmove(void *dst, void *src, int n) movsl を使った高速なバッファ転送関数です。n バイト転送することに注意して下 さい。 6.2.ソケット操作関数 ■void SocketKill(int fd) ファイルディスクプリタで指定されたソケットをクローズし、KON のソケットを 削除する。 ■int SocketRecCommand(int fd, struct messageHeader *mh) ソケットから、messageHeader 構造体を読み込む ■int SocketSendCommand(int fd, char *cmd) クライアントから、KON に機能コードを送ります。 ■int SocketClientOpen() KON のソケットをオープンします。 ■int SocketSendData(u_char *buff, int size, int fd) データバッファを BUFSIZ でフラグメント化しながらソケットへ送り出します。 ■char socketName[MAX_SOCKET_NAME+1] 関数 SocketClientOpen で作成されたソケット名が記憶されるバッファです。 6.3.その他 ■FILE *CapSearchLabel(char *label) kon.cfg から指定されたラベルを検索する関数。ラベルの別名も調べる。一致する ラベルを見つけた場合は FILE 構造体へのポインタを返す。