この章では ROM フォントの構造と PocketLinux のドライバの内部について説明します。
ROM 上のフォントは
/* necmg.h */
#define NECMG_FONTROM_BASE 0x01400000UL
/* mgfont.c */
#define p_K12x12 0x00000
#define p_A6x12 0x3f000
#define p_K16x16 0x44800
#define p_A8x16 0x83800
#define p_K24x24 0x89800
#define p_A12x24 0x127000
#define p_A8x8 0x89000
において、たとえば JIS X 0201 6x12 フォント全体は
p_A6x12 + NECMG_FONTROM_BASEから始まる物理アドレスに存在し、
個々のフォントは JIS X 0201 (0x00 〜 0xff)の場合、
static inline unsigned char* sfont_pos(unsigned short ch){
return &(necmg_sfont->p[ch * necmg_sfont->byte_size]);
}
JIS X 0208 フォント(0x21 0x21 〜 0x7e 0x7e)の場合、
static inline unsigned char* wfont_pos(unsigned char ch, int ch_2){
/*
* ch 第一オクテット。
* ch_2 第二オクテット(先に走る側; LSB)
*/
int adr = (int )(ch - 0x21) * 96 + (ch_2 - 0x20);
return &(necmg_wfont->p[adr * necmg_wfont->byte_size]);
}
で計算されるアドレスからグリフイメージが存在します。それぞれのグリフは
LSB が左に、各バイト内は msb が左に、そして上から下に向けて
pixel が置かれています。
24x24pixel font (byte_width = 3) の場合、
<-- 左 ---------------- 右 -->
<-- LSB -><-- 2 --><-- MSB -> LSB/MSB: Least/Most significant Byte
<76543210><76543210><76543210> lsb/msb: Least/Most significant bit
msb lsb
<---------- グリフ ---------->
のように配置されており、24x24/8 = 72 バイト ですが
グリフ下に空白があり、これを含めて
1 グリフ毎に 80 バイト(byte_size = 80) 使われています。
同じフォーマットながら 12x12 pixel font や 12x24 pixel font は注意を要し、
<-- LSB -><-- MSB-->
<76543210><76543210>
<-- グリフ--><空き->
となっているため unsigned short (sizeof(unsigned short) = 16)
で読み出すと語内でグリフが連続していません。
necmg_mgfont_init()内部で
フォントが necmg_fontrom_base に vremap() されて以後
初めて ROM フォントを使えるようになりますが、
con_init() によって console を初期化する時点では
vremap() がまだ使えません。
vremap() は
kernel 内のメモリ管理システムを通じて物理アドレスをカーネル空間にマップしますが、
それはmem_init()によって初期化されていなければなりません。
そして con_init() は mem_init() などが出すエラーメッセージをいち早く
コンソールに出力しなければならない必要から、mem_init() の後に置くことができません。
そこで vremap() が使えるようになるまでの間
カーネル空間に最初からおかれている clB8x8
をフォントとして使うことができるようにしてあります。
ただ、このフォントは内部構造が上記の
ROM フォントとかなり違うことに注意してください(
後述
)。
PocketLinux 上では 各 ROM Font は struct necmg_fontmetric を通じて管理されています:
/* necmg.h */
struct necmg_fontmetric {
unsigned char *p; /* グリフ先頭のアドレス */
unsigned long size; /* フォント全体のサイズ */
int type; /* 0: ascii, 1: jis x0208, 2: internal ascii */
int allocated; /* 0: not allocated, 1: romfont, 2: kmalloced font */
int pixel_width;
int pixel_height;
int byte_width; /* font 1 scanline あたりバイト数 */
int byte_size; /* font 1 文字あたりバイト数 */
necmg_font_draw draw;
unsigned char* shift_mask[4];
};
アドレスを sfont_pos() で計算するものは 0, wfont_pos で
計算しるものは 1. font_clB8x8_data は例外的に 2 になります。
その font が使えるかどうか、kfree() できるかどうかを表します。
フォントの幅(pixel 単位)です。
フォントの高さ(pixel 単位)です。
フォントの ROM 上での幅(バイト単位)です。
フォントの ROM 上での大きさ(バイト単位)です。
このフォントをディスプレイに描くための renderer です。 これを通じて汎用のレンダラ以外の、 そのフォント固有のレンダラを使うことができ、現在 6x12, 8x16 の場合は実際に専用のレンダラを持っています。プロトタイプは
typedef void (*necmg_font_draw)(unsigned short ch, mgfb_t ppos, ...);
と宣言されていて、ディスプレイ上 ppos の位置にフォントchを
rendering します。
実際には JIS X 0201 のフォントの場合、
draw( (ch & 0xff) | (ch_attribute << 8) , ppos);
JIS X 0208 のフォントの場合、
draw( (ch & 0x7f) | (ch_attribute << 8) , ppos, (ch_2 & 0x7f));
として使われます。ここで ch は 0x00 〜 0xff のフォント、
ch ch_2 は 0x21 0x21 〜 0x7e 0x7e のフォント、ch_attribute は表示の際の
文字属性を表します。
現在 JIS X 0208 はスクリーン上 JIS X 0201 の倍のサイズをとりますが、
文字の右側の属性を左側と異なるようにすることはできません。
Font rendering の詳細については後述の FONT レンダラの章を参照してください。
ディスプレイ上バイト境界にないところから文字を描く時のための 補助マスクです。事前に計算したものが置かれています。
デバイスファイル/dev/mgfontは FONT ROM 領域を
ユーザースペースから mmap するためにあるデバイスです。
cr--r--r-- 1 root root 58, 0 Mar 22 22:56 mgfont
cr--r--r-- 1 root root 58, 0 Mar 22 22:56 mgfont0
cr--r--r-- 1 root root 58, 1 Mar 22 22:56 mgfont1
cr--r--r-- 1 root root 58, 2 Mar 22 22:56 mgfont2
cr--r--r-- 1 root root 58, 3 Mar 22 22:56 mgfont3
cr--r--r-- 1 root root 58, 4 Mar 22 22:56 mgfont4
cr--r--r-- 1 root root 58, 5 Mar 22 22:56 mgfont5
cr--r--r-- 1 root root 58, 6 Mar 22 22:56 mgfont6
cr--r--r-- 1 root root 58, 7 Mar 22 22:56 mgfont7
cr--r--r-- 1 root root 58, 3 Mar 22 22:56 mgfonts
として用意され、それぞれ
を扱います。mgfonts と mgfont3 が重複していますが、これは JIS X 0208 の 12x12pixel size の内蔵 ROM フォントがフォント領域の先頭に あることによる共有(手抜き)です。
効率的な問題から実用上は mgfonts だけが使われるでしょう。
/* mgl-1.5-0610/mgl.c */
int fn = open("/dev/mgfonts", O_RDONLY);
if(fn < 0){
perror("open /dev/mgfonts");
exit(1);
}
font_base = (unsigned char *) mmap((caddr_t)0, 0x200000,
PROT_READ,
MAP_SHARED | MAP_FILE,
fn, 0);
などとして使います。read や seek することもできますが、 FONT イメージをまるごと取得する以外に使い道があるとは思えません。
/dev/mgfont* に対する ioctl は二つ定義されています。
返り値は unsigned long でフォント全体のサイズが返ります。
mgfonts と mgfont3 が共有されている都合から、
mgfonts の場合は
全体でなく 12x12 フォントのサイズが返されてしまいます。
返り値は sizeof(struct necmg_fontmetric) で、
そのフォントの現在の fontmetric が返ります。
もちろん p, draw, shift_mask はカーネル内のアドレスなので
ユーザー空間からアクセスしても何もありません。
p (フォントアドレス) は mmap して取得すればいいとして、draw
はカーネル内の関数をユーザー空間から call するのは
たかだか 1 文字描くにはあまりにオーバーヘッドが大きいので
用意されていません。
mgfonts と mgfont3 の問題はこちらの場合も同じです。
カーネル内部の RAM に置かれた clB8x8 フォントは rendering の簡便さを目的として ROM フォントと内部構造が異なります。
clB8x8 は 1 pixel について 2 bit を使い、
<- 左 ------ 右 ->
<- LSB -><- MSB ->
<0011223344556677>
<-- グリフ ------>
LSB を左において上から下に pixel を配置し 0x00 〜 0xff に対するグリフを 持っています。
1 pixel が 2 bit に展開されているのはディスプレイが 2bpp だからですが、 カーネルメモリが無駄に消費されているので将来 ROM フォントと同じ構造になるか、 あるいはカーネルから削除されるかもしれません。
また、日本語コンソール移行後は非バイトバウンダリでの font rendering が
発生しますが、clB8x8 は rendering function
( necmg_font_draw_iascii() ) の手抜きから非バイトバウンダリでの
表示ができるようになっていません。
もっともデフォルトフォントが 8x8 になるように(i.e. 80x30)
コンソールサイズを切替えた場合は
ROM フォントの 8x8 pixel size のものにデフォルトフォントが切り替わり
clB8x8 が使われることはありませんから、
この問題がユーザーサイドで露見することはないはずです。