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

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

Chapter 8 「マウス制御と32ビットモード切り替え」

今回やった内容

  • マウスデータ解読
  • マウスカーソルを動かす
  • asmhead.asmを解読する

今回短いです。

環境

作業記録

 7日目まででマウス割り込み受け取りが出来たので、今回は実際にマウスが動かせるようになるまで実装します。

 マウスは動きがあるたびに3バイトデータが送られてきます(7日目参照)。この3バイトのデータを全て受け取ってあげないと正しくマウスカーソルを動かす事ができないので、ちゃんとデータを受け取るようなコードを書きます。

// bootpack.c

unsigned char mouse_buf[3], mouse_phase = 0;

enable_mouse();

while(1){
    io_cli();

    if(fifo8_status(&keyinfo) + fifo8_status(&mouseinfo) == 0){
        io_stihlt();
    }
    else{
        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();

            if(mouse_phase == 0){
                if(data == 0xfa) phase = 1;
            }
            else if(mouse_phase == 1){
                phase = 2;
                mouse_buf[0] = data;
            }
            else if(mouse_phase == 2){
                phase = 3;
                mouse_buf[1] = data;
            }
            else if(mouse_phase == 3){
                phase = 1;
                mouse_buf[0] = data;

                // 情報描画
                char s[40];
                sprintf(s, "%x, %x, %x", mouse_buf[0], mouse_buf[1], mouse_buf[2]);
                boxfill8(binfo->vram, binfo->width, COL8_008484, 20 ,20, 100, 40);
                putstr8(binfo->vram, binfo->width, 20, 20, COL8_FFFFFF, s);
            }
        }
    }
}

 phase変数で何バイト目のデータを受信しているかを記録させて、3バイトを順番に受信するようになっています。3バイト目を受信した時に情報を描画し、phase変数を1にしてあげています。このループを繰り返すことで(終了しない限り)マウスデータを永遠に受け取れるようになっています。

 このコードを実行すると…

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

 マウスデータが画面に描画されました〜!マウスデータの3バイトはそれぞれ押されたボタンの情報、左右の動き、上下の動きに対応しているみたいです。これらを組み合わせることでマウスが動かせるようになります。


 HariMain関数が肥大化してきたので、マウス関連の処理を関数にしてあげます。関数にするのはすぐ終わる(移動するだけ)ので、次の章も続けて進めることにします。

 先ほどのコードだと生のマウスデータしか得られないので、扱いやすい形式にしてあげるようなコードを書きます。具体的には、マウスデータを扱う構造体を作ってあげます。それに合わせてマウスデータ受信部分も少し書き直します。

// bootpack.c

struct MOUSE_DEC{
    unsigned char buf[3], phase;
    int x, y, btn;
};

// マウスデータを色々する
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char data){
    // データ受信準備
    if(mdec->phase == 0){
        if(data == 0xfa){
            mdec->phase = 1;
        }
        return 0;
    }

    // 1バイト目受信
    if(mdec->phase == 1){
        // 正しい1バイト目かどうか検証
        if((data & 0xc8) == 0x08){
            mdec->buf[0] = data;
            mdec->phase = 2;
        }
        return 0;
    }

    // 2バイト目受信
    if(mdec->phase == 2){
        mdec->buf[1] = data;
        mdec->phase = 3;
        return 0;
    }

    // 3バイト目受信
    if(mdec->phase == 3){
        mdec->buf[2] = data;
        mdec->phase = 1;
        mdec->btn = mdec->buf[0] & 0x07;
        mdec->x = mdec->buf[1];
        mdec->y = mdec->buf[2];

        if((mdec->buf[0] & 0x10) != 0){
            mdec->x |= 0xffffff00;
        }
        if((mdec->buf[0] & 0x20) != 0){
            mdec->y |= 0xffffff00;
        }

        mdec->y *= -1;  // マウスはY座標の符号が逆
        return 1;
    }

    return -1;
}

 これで、押されたボタンの情報・マウスの移動量が簡単に扱えるようになりました。
 このコードに合わせて情報描画部分も少し手直ししました。

// bootpack.c

// マウスデータを3バイト受信できたら描画
 if(mouse_decode(&mdec, data) != 0){
    // マウスの情報とか表示
    char s[40];
    sprintf(s, "[---, %d, %d]", mdec.x, mdec.y);

    if((mdec.btn & 0x01) != 0) s[1] = 'L';
    if((mdec.btn & 0x02) != 0) s[2] = 'R';
    if((mdec.btn & 0x04) != 0) s[3] = 'C';

    boxfill8(binfo->vram, binfo->width, COL8_008484, 32, 20, 200, 35);
    putstr8(binfo->vram, binfo->width, 32, 20, COL8_FFFFFF, s);
}

 そしてコードを実行すると…

f:id:guguru0014:20190124230836p:plain
「---」の部分に押されたボタンの情報が出てくる スクショミスった

 ちゃんと移動量が表示されています。マウスが動くまで後少しです…!


 移動量取得ができたので、後はマウスカーソルを動かすような処理を書くだけです!次の順番で処理を行うことでマウスカーソルを動かします。

  1. マウスデータ受信
  2. 画面に出ているマウスカーソルを背景色で塗りつぶす
  3. マウスカーソルの座標を移動量だけ変化させる
  4. マウスカーソルを描画し直す

 コードにするとこうなりました。

// マウスデータを3バイト受信できたら描画
if(mouse_decode(&mdec, data) != 0){
    // マウスの情報とか表示
    char s[40];
    sprintf(s, "[---, %d, %d]", mdec.x, mdec.y);
    if((mdec.btn & 0x01) != 0) s[1] = 'L';
    if((mdec.btn & 0x02) != 0) s[2] = 'R';
    if((mdec.btn & 0x04) != 0) s[3] = 'C';

    boxfill8(binfo->vram, binfo->width, COL8_008484, 32, 20, 200, 35);
    putstr8(binfo->vram, binfo->width, 32, 20, COL8_FFFFFF, s);

    // マウス描画準備
    boxfill8(binfo->vram, binfo->width, COL8_008484, mouse_x, mouse_y, mouse_x + 15, mouse_y + 15); // 前フレームのマウスを塗りつぶす
    mouse_x += mdec.x;
    mouse_y += mdec.y;
    if(mouse_x < 0) mouse_x = 0;
    if(mouse_y < 0) mouse_y = 0;
    if(mouse_x > binfo->width) mouse_x = binfo->width;
    if(mouse_y > binfo->height) mouse_y = binfo->height;

    // マウス描画
    char *mouse;
    spintf(s, "(%d, %d)", mouse_x, mouse_y);
    boxfill8(binfo->vram, binfo->width, COL8_008484, 0, 0, 100, 16);
    putstr8(binfo->vram, binfo->width, 0, 0, COL8_FFFFFF, s);
    init_mouse_cursor8(mouse, COL8_008484);
    putblock8_8(binfo->vram, binfo->width, 16, 16, mouse_x, mouse_y, mouse, 16);

}

 コードが完成したので実行します…

 マウスカーソルが動きました〜!5日目からずっと続けてきた事がついに完成しました!マウスカーソルを描画し直す時に何も考えずにVRAMを書き換えてるのでタスクバーが無残な姿になっていますが…とりあえず動きました。マウス動かすのにここまで時間がかかるとは思っていませんでした。


 8日目後半の内容はasmhead.asmの解説となっています。特に引っかかるところもなく理解する事ができました。久しぶりのアセンブリで少し新鮮でした。

 (…後半薄い)

まとめ

 タスクバーを破壊するなどまだ気になる部分もありますが、とりあえずマウスカーソルが動かせるようになりました!8日目以上です!

(…まとめも若干薄い気がする)