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

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

Chapter 18 「dirコマンド」

今回やった内容

  • コンソールでコマンドを打てるようにする

環境

作業記録

Enterキー 対応

 現在のコンソールはEnterキーが全く効かないので、Enterに対応した処理を書いてあげます。Enterキーに対応したキーidの条件分岐を書いて、その中でカーソルの位置変更と改行処理を行います。

スクロール対応

 コンソールの一番下の行で改行をした時に、コンソール画面をスクロールするような処理を書きます。スクロールとありますが、描画する画素をずらしていくだけなので過去ログを見ることは出来ません…。


 スクロールと改行はほとんど同じ処理をしているので1つの関数にまとめます。

int console_newline(struct SHEET *sheet, int cursor_y){
    int x0 = 8, y0 = 28, width = 384, height = 256;

    // 改行
    if(cursor_y < 28 + height - 16){
        cursor_y += 16;
        return cursor_y;
    }

    // ずらし
    for(int y = y0; y < y0 + height - 16; ++ y){
        for(int x = x0; x < x0 + width; ++ x){
            sheet->buf[x + y * sheet->buf_width] = sheet->buf[x + (y + 16) * sheet->buf_width];
        }
    }

    // 新しい行を作成
    for(int y = y0 + height - 16; y < y0 + height; ++ y){
        for(int x = x0; x < x0 + width; ++ x){
            sheet->buf[x + y * sheet->buf_width] = COL8_000000;
        }
    }

    sheet_refresh(sheet, x0, y0, x0 + width, y0 + height);
    return cursor_y;
}

 改行したいときはこの関数を呼ぶだけでよくなりました(楽)。


コマンド対応

 改行・スクロールが実装できたので、早速コマンドの実装をしていきます。
 キー入力のたびに変数にキー情報を格納していき、Enterが押された時にそのデータを比較して登録されているコマンドかどうかを判定します。

 これもいくつかのコマンドに対応できるように関数を作ってあげます。

int console_command_exec(struct SHEET *sheet, char command[], int cursor_y){
    char s[40];

    /*  mem  */
    if(mstrcmp(command, "mem") == 0){
        struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;

        msprintf(s, "total : %d MB", mem_size / (1024 * 1024));
        putstr8_ref(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, s, 16);
        cursor_y = console_newline(sheet, cursor_y);
        msprintf(s, "free : %d MB", memman_total(memman) / (1024 * 1024));
        putstr8_ref(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, s, 16);
        cursor_y = console_newline(sheet, cursor_y);
        cursor_y = console_newline(sheet, cursor_y);

        return cursor_y;
    }

    /*  clear  */
    if(mstrcmp(command, "clear") == 0){
        for(int y = 28; y < 28 + 256; ++ y){
            for(int x = 8; x < 8 + 384; ++ x){
                sheet->buf[x + y * sheet->buf_width] = COL8_000000;
            }
        }
        sheet_refresh(sheet, 8, 28, 8 + 384, 28 + 256);

        return 28;
    }

    /*  hello  */
    if(mstrcmp(command, "hello") == 0){
        putstr8_ref(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, "Hello!!", 7);
        cursor_y = console_newline(sheet, cursor_y);
        cursor_y = console_newline(sheet, cursor_y);

        return cursor_y;
    }

    /* bad command */
    putstr8_ref(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, "Bad Command...", 14);
    cursor_y = console_newline(sheet, cursor_y);
    cursor_y = console_newline(sheet, cursor_y);

    return cursor_y;
}

 「mem」コマンドと「clear」コマンド、そして「hello」コマンドを作ってみました。ここまでで一度実行してみると…

 ちゃんとコマンドが動いていますね!!!!動画内にはありませんでしたが、登録していないコマンドを入力すると「Bad Command...」と表示されます。


lsコマンド・ファイル情報読み取り

 lsコマンドを実装していきます。
 このHariboteOSでは、起動時にディスクのデータを一気に読み込んでメモリ上に配置するようにしています。このデータを読むことでファイル情報を持ってくることができます。

 本によると…ファイル情報はこの構造体のような形式(32バイト)で格納されているみたいです。  

struct FILEINFO{
    unsigned char name[8], ext[3], type;
    char reserve[10];
    unsigned short time, date, clustno;
    unsigned int size;
};

 ファイル情報が配置されている番地も分かっているので、これでファイル情報を読むことができます。

#define ADR_DISKIMG 0x00100000

struct FILEINFO *file_info = (struct FILEINFO *) (ADR_DISKIMG + 0x002600);

 先頭アドレスを渡すだけで後ろに続く番地が自動的に計算されるので楽ですね〜。

 …ということで早速lsコマンドを実装します。memコマンドなどと同じように書いて、出力する内容を変えるだけです。

/*  ls  */
if(mstrcmp(command, "ls") == 0){
    // ファイル情報が置かれている所のポインタ
    struct FILEINFO *file_info = (struct FILEINFO *) (ADR_DISKIMG + 0x002600);

    // ファイル情報読み込み->表示
    for(int idx = 0; idx < 224; ++ idx){
        // 終わり
        if(file_info[idx].name[0] == 0x00){
            break;
        }

        // ファイル情報がある
        if(file_info[idx].name[0] != 0xe5 && (file_info[idx].type & 0x18) == 0){
            msprintf(s, "filename.ext    : %d", file_info[idx].size);
            for(int n_idx = 0; n_idx < 8; ++ n_idx){
                s[n_idx] = file_info[idx].name[n_idx];
            }
            s[9] = file_info[idx].ext[0];
            s[10] = file_info[idx].ext[1];
            s[11] = file_info[idx].ext[2];
            putstr8_ref(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, s, 30);
            cursor_y = console_newline(sheet, cursor_y);
        }
    }

    cursor_y = console_newline(sheet, cursor_y);
    return cursor_y;
}

 HariboteOSのディスクイメージ作成時にいくつかのファイルを入れておきました。これで実行すると…

 ちゃんとファイル情報が読めました!

まとめ

 まだ少ないですがいくつかのコマンドを実行することができました!ただ今のままだとコマンド処理がif文の中に直接書かれているので変えたいですね…。

 次回はファイルの中身を読み込んで、アプリケーション実行までやってみるみたいです。

 途中で stcmp 関数を使うことになるのですが、標準ライブラリをincludeしないオプションをつけてビルドしているので使えません。ということで自作しました。

char mstrcmp(char str_a[], char str_b[]){
    // 文字列同士の大小を比較
    int idx = 0;
    for(; str_a[idx] != '\0' && str_b[idx] != '\0'; ++ idx){
        if(str_a[idx] > str_b[idx]){
            return 1;
        }else if(str_a[idx] < str_b[idx]){
            return -1;
        }
    }

    // 比較が中断されたら
    if(str_a[idx] == '\0' && str_b[idx] == '\0'){
        return 0;
    }else if(str_a[idx] == '\0'){
        return -1;
    }else{
        return 1;
    }
}