OS自作入門 七日目

OS自作入門7日目のまとめ

自分のためのまとめなので、他の人には意味不かも。

FIFOとマウス制御

FIFOについては6日目で説明した。

キーコードの取得

//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;
}
まずは以下の部分。
        io_out8(PIC0_OCW2, 0x61);       /* IRQ-01受付完了をPICに通知 *
これを行うことで、PICに対して割り込みを受け取ったということを通知することができる。これを行わないと、PICは割り込みをCPUに対して行った後、機器からの割り込みを受け付けない。そのためこれは非常に重要である。らしい。このとき出力するデータは0x61となっているが、これは以下の式で求められる。
 0x60+(IRQ number)
キーボードはIRQ1であるので、この式に当てはめると0x61となる。

次に装置番号0x60からデータを8ビット入力する。これがキーコード。

割り込み処理は手早く

上の割り込み処理は、割り込みハンドラ中に文字の表示を行っている。
割り込みはいつ起きるかわからないので、割り込みハンドラ内の処理は最小限にするべきである。
そこで割り込みハンドラ中ではデータの受け取りのみを行い、他の場所で文字表示を行うようにする。

キーボードからのデータを受けとるためにバッファを用意する。data,flagという変数をもつ構造体である。
キーボード用割り込みハンドラではキーボードからのデータをそのバッファに保存するだけにする。
//bootpack.h
struct KEYBUF {
       unsigned char data, flag;
};

//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;
} 

dataにデータが入っていればflagを立てる。この状態だとflagが立っている状態のときに入力されたデータは破棄されることになる。このflagを用いて、main関数内で文字表示を行う。
       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);
               }
       }
flagが立っていなければ
               if (keybuf.flag == 0) {
                       io_stihlt();
割り込みを有効にしてhltする。hltしているあいだはPICからの割り込みによって動作が再開するので、割り込みがくればそれを処理することになる。
flagが立っていればバッファをクリアして割り込みを有効にして、文字を表示する。

FIFOバッファを作る

上で使っていたバッファでは連続でデータがきたときに対処できないので、ここでは複数のデータを保存できるバッファを作っている。ここで使用するのは6日目で説明されていたFIFOという方式のバッファである。
       if (keybuf.next < 32) {
               keybuf.data[keybuf.next] = data;
               keybuf.next++;
バッファを32個用意して、上限まで読み込む。取り出しは以下のように
                       i = keybuf.data[0];
                       keybuf.next--;
                       for (j = 0; j < keybuf.next; j++) {
                               keybuf.data[j] = keybuf.data[j + 1];
                       }
一番若いデータを取り出したら、以下の様にして全てのデータをずらす。

FIFOバッファを改良する

ずらし作業を行うと非常にたくさんのメモリアクセスが起きるので、動作が遅い。
そこでずらしを行わない方法でバッファを改良する。具体的にはバッファをヒトツナガリになった輪っかのように扱う。
そのためのバッファは以下のようにする。
//bootpack.h
struct KEYBUF {
       unsigned char data[32];
       int next_r, next_w, len;
};
書き込みは以下のように行う。書き込みバッファが最後までいったら、最初に戻すようにする。
//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;
}
読み込みも同様にする。
                       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);


FIFOバッファを整理する

キーボードだけでなく、他の用途にも使えるようにする。
そのためのバッファは以下のようにする。
/* fifo.c */
struct FIFO8 {
       unsigned char *buf;
       int p, q, size, free, flags;
};
バッファのアドレスをbufに、サイズはは変数sizeの値にする。可変にすることでいろんな用途に使える。
pが書き込み番地用変数。qが読み込み番地用変数。このバッファに書き込むには以下のようにする。
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;
}
freeで空きの数を確認しておき、空きがない場合はflagを立てる。
読み込んだら、次に読み込むべき番地を1つ増やす。空きサイズを1つ増やす。
もしバッファの最後までいったら最初に戻す。
読み込みは以下のようにする。
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;
}
書き込みの逆をしているだけ。

マウス

マウスを使うにはマウス制御回路とマウスに有効化命令を発行しなければいけない。
マウス制御回路はキーボード制御回路内に入っているので、キーボード制御回路の初期化と一緒に行う。
#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);
       wait_KBC_sendready();
       io_out8(PORT_KEYDAT, KBC_MODE);
       return;
}
キーボード制御回路が信号を受け取れるようになるまで待って、モード設定のためのデータを送信。
もう一度待ってから、マウスを利用できるモード番号を送信。これが
#define KBC_MODE                                0x47
である。次にマウスに対する有効化命令。
#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)が送信されてくる */
}
キーボードコントローラの初期化と同じようなことをしている。
#define KEYCMD_SENDTO_MOUSE             0xd4
このコマンドをキーボード制御回路に送信すると、次の命令をマウスへ送信してくれるので、そのあとでマウスを有効化する制御番号
 #define MOUSECMD_ENABLE                 0xf4
を送信している。こうしてマウスを有効化するとマウスからの割り込みが発生する。
マウスから割り込みが起きるとマウス割り込みハンドラが呼び出される。

マウスからのデータ受信

マウスからのデータを受信する方法は、ほぼキーボードと同じ。
struct FIFO8 mousefifo;

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;
}
マウスはPICのスレーブ側に付いているので、マスタとスレーブのPICに対して受付完了の合図を出す。
データの受け取りが、キーボードと同じ装置番号だが、これは割り込み番号で区別しているらしい。
受け取りは以下のようにする。
       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);
                       }
               }
       }
まずは割り込みを禁止。
最初のif文で、割り込みが入っているからどうかを判定。
入っていなければhltしておく。
割り込みが入っていれば、それぞれのバッファからデータを読み出してきて、割り込みを可能にして、受信したデータを表示。



  • test -- takahashiq (2007-07-25 21:17:43)
名前:
コメント:



最終更新:2007年07月25日 21:26
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。