自作OSに挑戦する日記 7日目

 「30日でできる!OS自作入門」を読んで分かったことや、とりあえず書いておきたいことなどを書いていきます。
 この本はChapterが1から30まであるので、各チャプター毎に1記事書いていきます。

Chapter 7 「FIFOとマウス制御」

今回やった内容

  • キーコード取得
  • 割り込み処理を書き直す
  • FIFOバッファ作成
  • マウスの割り込みを受けれるようにする
  • マウスからデータを受信

環境

内容の都合上、今回から動作確認はQEMUを用いることにしました(´・ω・`)

作業記録

 6日目まででキーボードの割り込みを受けれるようになったので、次はキーコードを取得してみます。
 割り込みを受け取った後はPICに通知してあげないと、PICが割り込み監視を再開してくれないらしいです…。なのでちゃんとPICに通知してあげるようなコードを書きます。また、キーデータはIN命令を使って読み込めるみたいなので、以前作成した関数io_in8を使って読み込むようにします。

// int.c

void inthandler21(void){
    struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
    io_out8(PIC0_OCW2, 0x61);    // 割り込みを受け取ったらPICに通知する!!
    unsigned char data = io_in8(PORT_KEYDAT);

    〜略〜
}

 ここでsprintf関数が必要になるのですが、コンパイルに-nostdlibオプションをつけているためsprintf関数を使う事ができません(これをつけないとコンパイルできない…)。いろいろ探してみたところ、sprintf関数を自作してしかも公開してくれている方がいたのでありがたく使わせてもらうことにしました。

bttb.s1.valueserver.jp

 必要なものが揃ったのでコンパイルします。そして実行すると…

f:id:guguru0014:20190123214813p:plain

 これでキー入力が完成しました!
 出力と入力の基本が完成したので、やっとこのHariboteOSも「OSっぽい」から「OS」にランクアップできたのでは…


 …キー入力に対応したのですが、今のコードだと割り込み中にやっている処理が多く、多くのキー入力があった時に処理がもたついてしまっています。
 ということで、割り込み中にやっている処理をなるべく減らして処理を止める時間を少なくするようにします。具体的には、キーデータを保存するような構造体を別に作っておいて割り込み中はその構造体へのアクセスしかしないようにします。そして割り込み終了後に改めて構造体へアクセスすることで、キーデータを取得するようにします。

// int.c

struct KEYBUF{
    unsigned char data, flag;
}
struct KEYBUF keybuf;

void inthandler21(void){
    struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
    io_out8(PIC0_OCW2, 0x61);    // 割り込みを受け取ったらPICに通知する!!
    unsigned char data = io_in8(PORT_KEYDAT);

    if(keybuf.flag == 0){
        keybuf.data = data;
        keybuf.flag = 1;
    }
}

 合わせてbootpack.cも変更します。

// bootpack.c

void HariMain(void){
    
〜〜

    while(1){
        io_cli();    // キー処理中に割り込みが入ると混乱するので割り込みフラグ0

        if(keybuf.flag == 0){
            io_stihlt();
        }else{
            int data = keybuf.data;
            keybuf.flag = 0;
            io_sti();    // データを取得してから割り込みフラグを1にする

            sprintf(s, "%x", data);
            boxfill8(binfo->vram, binfo->width, COL8_008484, 0, 16, 15, 31);
            putstr8(binfo->vram, binfo->width, 0, 16, COL8_FFFFFF, s);
        }
    }

〜〜

}

 このように工夫することで割り込み中の処理を減らし、処理のもたつきをなくす事ができました(なんとなくキーの遅延が無くなったような…)。


 この次はFIFOバッファの実装をしますが、知っている内容だったのですんなり進める事ができました。完成したコードがこれです。

// fifo.c

#include "bootpack.h"

#define FLAGS_OVERRUN 0x0001

// FIFOバッファの初期化
void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf){
    fifo->size = size;
    fifo->buf = buf;
    fifo->free = size;
    fifo->flags = 0;
    fifo->p = 0;    // 書き込み位置
    fifo->q = 0;    // 読み込み位置
}

// FIFOバッファに値を追加
int fifo8_put(struct FIFO8 *fifo, unsigned char data){
    // 溢れ
    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;
}

// FIFOバッファから値を取り出す
int fifo8_get(struct FIFO8 *fifo){
    // 要素なし
    if(fifo->free == fifo->size){
        return -1;
    }

    int data = fifo->buf[fifo->q];
    fifo->q ++;
    if(fifo->q == fifo->size) fifo->q = 0;
    fifo->free ++;
    return data;
}

// FIFOバッファにどれだけデータが溜まってるかを返す
int fifo8_status(struct FIFO8 *fifo){
    return fifo->size - fifo->free;
}

 先ほどのKEYBUFはキーボード専用だったのですが、このFIFOバッファは汎用的に扱う事ができるのでマウスなどでも利用できるようになりました。


 最後は、6日目に反応してくれなかったマウスを動かせるようにします。
 マウスを動かせるようにするためには、マウス専用回路を有効にするようにしてあげないといけないらしいです…。初期のPCにはマウスが必要なく、後から付け足したのでこうなっているだとか。今の時代に合わせて変えたりしないんでしょうか、もしくは今の基盤だとそうなっているのかも。また調べてみます。

 マウスを使えるようにするためには、マウス制御回路をまず有効にした後マウス自体も有効にしてあげる必要があるみたいです。また、マウス制御回路はキーボード制御回路に入っているらしく、キーボード制御回路を有効にすることで一緒に有効になってくれるみたいです。

// bootpack.c

// キーボード制御回路が制御命令を受け付けれるようになるまで待機するための関数
void wait_KBC_sendready(void){
    while(1){
        if((io_in8(PORT_KEYSTA) & KEYSTA_SEND_NOTREADY) == 0){
            break;
        }
    }
}

// キーボードコントローラ初期化
void init_keyboard(){
    wait_KBC_sendready();
    io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);
    wait_KBC_sendready();
    io_out8(PORT_KEYDAT, KBC_MODE);
}

// マウス有効化
void enable_mouse(){
    wait_KBC_sendready();
    io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
    wait_KBC_sendready();
    io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
}

 これだけだと割り込みを受け付けるだけで肝心のマウスデータを受け取る事ができないので、コードを足してあげます。キーデータを受け取る時とほとんど同じコードです(ちょっと楽)。

// bootpack.c

void HariMain(void){
    
〜〜

    while(1){
        io_cli();

        if(fifo8_status(&keyinfo) + fifo8_status(&mouseinfo) == 0){
            io_stihlt();
        }else{
            char s[40];

            if(fifo8_status(&keyinfo) != 0){       // キーボード
                int data = fifo8_get(&keyinfo);
                io_sti();
                sprintf(s, "%x", data);
                boxfill8(binfo->vram, binfo->width, COL8_008484, 0, 20, 20, 40);
                putstr8(binfo->vram, binfo->width, 0, 20, COL8_FFFFFF, s);
            }
            else if(fifo8_status(&mouseinfo) != 0){ // マウス
                int data = fifo8_get(&mouseinfo);
                io_sti();
                sprintf(s, "%x", data);
                boxfill8(binfo->vram, binfo->width, COL8_008484, 20, 20, 40, 40);
                putstr8(binfo->vram, binfo->width, 20, 20, COL8_FFFFFF, s);
            }
        }
    }

〜〜

}

 これでマウスを動かすための全ての準備が整いました。早速実行してみます…

f:id:guguru0014:20190123225539p:plain
←キーデータ マウスデータ→

 キーデータに加えてマウスデータの受け取りもできるようになりました〜!静止画だと分からないですが、マウスの動きに合わせて数値が変わります(これだけでずっと遊べる)。

まとめ

 自作OS1週間終わりました!画面出力とキーボードとマウスからの入力まで完成させる事ができました!OSと呼べるような基本的な機能は実装できたような気がします…。
 明日はマウスを動かせるようにするみたいです〜。明日も頑張りたいと思います。