harib04a: キーコードの取得
DAY6で作った、キーボード入力時の割り込み処理を改善していく。
int.c - 修正前
void inthandler21(int *esp)
/* PS/2キーボードからの割り込み */
{
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
boxfill8(binfo->vram, binfo->scrnx, COL8_000000, 0, 0, 32 * 8 - 1, 15);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, "INT 21 (IRQ-1) : PS/2 keyboard");
for (;;) {
io_hlt();
}
}
int.c - 修正後
#define PORT_KEYDAT 0x0060
void inthandler21(int *esp)
{
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
unsigned char data, s[4];
io_out8(PIC0_OCW2, 0x61); /* IRQ-01受付完了をPICに通知 */
data = io_in8(PORT_KEYDAT);
sprintf(s, "%02X", data);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
return;
}
コメントの通り。IRQ1だったので、0x61にしているが、 IRQ3の場合だったら、インクリメントされて0x63になるとのこと。 これをPICに実行してあげると、割り込み信号のブロッキングが解除されて、 PICによる割り込み信号のモニタが再開される?
io_out8(PIC0_OCW2, 0x61); /* IRQ-01受付完了をPICに通知 */
その後、以下で装置番号0x0060から8bitを読み出し、 符号なしchar型のdataに代入。
data = io_in8(PORT_KEYDAT);
その後、sprintfでdataの内容をsにコピー。 putfont8で画面に描画している。
sprintf(s, "%02X", data);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
これで、入力した1文字に対応の文字コードが都度画面に表示されるようになった。
harib04b: キーボードからの割り込み処理と文字描画処理の分割
画面描画は処理コストが高く、今のコードだとそれに引きずられて割り込み処理も 処理が遅くなってしまうので分割を行う。
まずは、int.cに手を入れ、描画処理を外す。 新たにflagを立てる機構を作り、未出力の入力データがdataに 格納されているか否かを判定できるようにする。
int.c
#define PORT_KEYDAT 0x0060
struct KEYBUF keybuf;
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); /* IRQ-01受付完了をPICに通知 */
data = io_in8(PORT_KEYDAT);
if (keybuf.flag == 0) {
keybuf.data = data;
keybuf.flag = 1;
}
return;
}
次にbootpack.cの処理を変えていく。(今までは無限ループで、io_hlt();するのみだった)
bootpack.c
for (;;) {
io_cli();
if (keybuf.flag == 0) {
io_stihlt();
} else {
i = keybuf.data;
keybuf.flag = 0;
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
}
まず、 io_cli(); で割り込み処理を禁止する。
flagが0の場合は、未処理の入力データがない状態なので、 io_stihlt();で STI(割り込み禁止の解除?) と HLT(CPUお休み) を実行して終わり。
flagが1の場合は、flagを0にした後で、これまで int.cに書いていた描画処理を行う。
harib04c: キーボードからの割り込み処理のキューイング(FIFOバッファ)
04bの実装だと、入力された1byteを処理中に実行された割り込みは 全て読み捨てられてしまうため、そうではなくキューイングを行うようにする。
int.c
struct KEYBUF keybuf;
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); /* IRQ-01受付完了をPICに通知 */
data = io_in8(PORT_KEYDAT);
if (keybuf.next < 32) {
keybuf.data[keybuf.next] = data;
keybuf.next++;
}
return;
}
使いされた部分のコードが以下のように動き、値を32個まで配列に格納していく。
1回目の処理
- keybuf.data[0] = (初回で入力された値)
- keybuf.next ++ / 0 → 1に代わる /
2回目の処理
- keybuf.data[1] = (2回目入力された値)
- keybuf.next ++ / 1 → 2に代わる /
bootpack.c
for (;;) {
io_cli();
if (keybuf.next == 0) {
io_stihlt();
} else {
i = keybuf.data[0];
keybuf.next--;
for (j = 0; j < keybuf.next; j++) {
keybuf.data[j] = keybuf.data[j + 1];
}
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
}
}
ここは本文の説明で問題なく理解できた。
harib04d: キューイング機構(FIFOバッファ)の改良
04cの実装だと、最大32個の配列の要素をfor文でずらす必要が出てくる。 この処理をなくしたいので、実装を変える。
まずは、構造体の定義から。 next_r
, next_w
,len
が追加された。
bootpack.h
/* int.c */
struct KEYBUF {
unsigned char data[32];
int next_r, next_w, len;
};
次にキューへの入力処理。 入力された値は、常に keybuf.data[keybuf.next_w] に入る。
keybuf.lenは「現在何個のキューを持っているのか」 keybuf.next_wは「現在添え字を何個使ったのか」をカウントしており、32に達すると0にリセットされる。
int.c
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); /* IRQ-01受付完了をPICに通知 */
data = io_in8(PORT_KEYDAT);
if (keybuf.len < 32) {
keybuf.data[keybuf.next_w] = data;
keybuf.len++;
keybuf.next_w++;
if (keybuf.next_w == 32) {
keybuf.next_w = 0;
}
}
return;
}
キューからの取り出し処理もそれに合わせて書き直されている。
bootpack.c
for (;;) {
io_cli();
if (keybuf.len == 0) {
io_stihlt();
} else {
i = keybuf.data[keybuf.next_r];
keybuf.len--;
keybuf.next_r++;
if (keybuf.next_r == 32) {
keybuf.next_r = 0;
}
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
}
}
harib04e:キューイング機構(FIFOバッファ)の共通モジュール化
04b-04dで開発のキューイング機構を他の処理(マウスの入力とか)にも使えるように コードを修正していく。
まずは構造体の定義から。FIFOバッファの最大長を変数で自由に設定できるように変更。
bootpack.h
/* fifo.c */
struct FIFO8 {
unsigned char *buf;
int p, q, size, free, flags;
};
構造体の初期化関数。
fifo.c
void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf)
/* FIFOバッファの初期化 */
{
fifo->size = size;
fifo->buf = buf;
fifo->free = size; /* 空き */
fifo->flags = 0;
fifo->p = 0; /* 書き込み位置 */
fifo->q = 0; /* 読み込み位置 */
return;
}
キューへのPUSH処理。 fifo->buf[fifo→p] 値を格納したあとで、fifo->p++;し、fifo->free–;でfree領域の残数をデクリメントする。 fifo→pがキューの最大中に達したら、カウンターをリセットする機能と、 溢れたときにflagを立てる処理付き。
fifo.c
int fifo8_put(struct FIFO8 *fifo, unsigned char data)
/* FIFOへデータを送り込んで蓄える */
{
if (fifo->free == 0) {
/* 空きがなくてあふれた */
fifo->flags |= FLAGS_OVERRUN;
return -1;
}
fifo->buf[fifo->p] = data;
fifo->p++;
if (fifo->p == fifo->size) {
fifo->p = 0;
}
fifo->free--;
return 0;
}
キューからのPOP処理。 やっていることは、PUSHとほぼ同じ。
fifo.c
int fifo8_get(struct FIFO8 *fifo)
/* FIFOからデータを一つとってくる */
{
int data;
if (fifo->free == fifo->size) {
/* バッファが空っぽのときは、とりあえず-1が返される */
return -1;
}
data = fifo->buf[fifo->q];
fifo->q++;
if (fifo->q == fifo->size) {
fifo->q = 0;
}
fifo->free++;
return data;
}
最後にステータスチェック用のコード。 FIFOバッファの最大中 - 残っているfree領域を引き算して、 キュー長を戻り値で返しているだけ。
fifo.c
int fifo8_status(struct FIFO8 *fifo)
/* どのくらいデータが溜まっているかを報告する */
{
return fifo->size - fifo->free;
}
以上を踏まえて、init.cを書き直していく。
push側の処理は、fifo8_put(keyfifo, data); だけになった。 FIFO8型の構造体 keyinfoを作成後、fifo8_put関数でput処理している。
init.c
struct FIFO8 keyfifo;
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); /* IRQ-01受付完了をPICに通知 */
data = io_in8(PORT_KEYDAT);
fifo8_put(keyfifo, data);
return;
}
POP側の処理も、 i = fifo8_get(keyfifo); とシンプルになった。
bootpack.c
for (;;) {
io_cli();
if (fifo8_status(keyfifo) == 0) {
io_stihlt();
} else {
i = fifo8_get(keyfifo);
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
}
harib04f:マウスが動くようにする
マウスは初期状態では、割り込み信号がブロックされている。 ので、その解除が必要であり、そのために bootpack.c を修正し、 キーボードの初期化処理と一緒にマウスの初期化処理を実装する。
キーボードが制御信号で制御可能な状態になると、 io_in8(PORT_KEYSTA)の2ビット目が0になる。のでそれまでひたすら無限ループを回して待つ。
bootpack.c
void wait_KBC_sendready(void)
{
/* キーボードコントローラがデータ送信可能になるのを待つ */
for (;;) {
if ((io_in8(PORT_KEYSTA) KEYSTA_SEND_NOTREADY) == 0) {
break;
}
}
return;
}
キーボードの初期化関数。 前述の wait_KBC_sendready(void) で制御Readyになったかを見つつ、 io_out8で
bootpack.c
#define PORT_KEYDAT 0x0060
#define PORT_KEYSTA 0x0064
#define PORT_KEYCMD 0x0064
#define KEYSTA_SEND_NOTREADY 0x02
#define KEYCMD_WRITE_MODE 0x60
#define KBC_MODE 0x47
void init_keyboard(void)
{
/* キーボードコントローラの初期化 */
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE); /* キーボードをWRITE_MODEに */
wait_KBC_sendready();
io_out8(PORT_KEYDAT, KBC_MODE);
return;
}
最後に以下でマウスを有効化する。
bootpack.c
#define KEYCMD_SENDTO_MOUSE 0xd4
#define MOUSECMD_ENABLE 0xf4
void enable_mouse(void)
{
/* マウス有効 */
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
wait_KBC_sendready();
io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
return; /* うまくいくとACK(0xfa)が送信されてくる */
}
harib04g:マウスからのデータ受信
割り込みは受け取れるようになったので、今度はマウスを動かす度に、 都度割り込みが発生するようにコードを変えていく。
キーボードと同様に割り込み処理を受け付けたら、 PICに受付完了を通知後に、 fifo8_put でputするようにする。
init.c
void inthandler2c(int *esp)
/* PS/2マウスからの割り込み */
{
unsigned char data;
io_out8(PIC1_OCW2, 0x64); /* IRQ-12受付完了をPIC1に通知 */
io_out8(PIC0_OCW2, 0x62); /* IRQ-02受付完了をPIC0に通知 */
data = io_in8(PORT_KEYDAT);
fifo8_put(mousefifo, data);
return;
}
次に、bootpack.cの中でのキューのPOP処理のelseにマウス処理のパターンを足す。(書き方はキーボードと同様)
fifo8_init(mousefifo, 128, mousebuf);
enable_mouse();
for (;;) {
io_cli();
if (fifo8_status(keyfifo) + fifo8_status(mousefifo) == 0) {
io_stihlt();
} else {
if (fifo8_status(keyfifo) != 0) {
i = fifo8_get(keyfifo);
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
} else if (fifo8_status(mousefifo) != 0) {
i = fifo8_get(mousefifo);
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 47, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
}
}
}
マウスが動くたびに、座標が画面に描画されるようになった!