「30日でできる!OS自作入門」をRustで。19日目

Posted on July 14, 2019 , Tags: OS自作入門, OS, Rust

「30日でできる!OS自作入門 」のC言語の部分をできるだけRustですすめてみる。今回は19日目の内容。

cat(type)コマンドを追加する

前回に引き続きコマンド追加となる。
今回はcatに相当する機能ということで、例によって本ではtypeになっているがここではcatとして実装する。

前回、コマンド文字列を抽出処理を関数として書いたが、Rustのsplitでも書けそうなことに気づいたので、splitを使う方針に変更する。

あわせて、display_errorという文字列を表示するだけのマクロを用意してエラメッセージ表示を完結に記述できるようにした。

ファイルの中身を表示するにあたり、FileInfoのメソッドとしてファイルの中身の先頭番地を返せるようにしておく。

これを使って、表示ロジックを書いていく。

// console.rs
fn exec_cmd(
    // 省略
) -> isize {
    // 省略
        } else if cmd == "cat" {
        // ファイル名となるところを抽出
        let mut filename = cmdline_strs.skip_while(|strs| strs.len() == 0);
        let filename = filename.next();
        if filename.is_none() {
            display_error!("File Not Found", cursor_y);
        }
        let filename = filename.unwrap();
        // 拡張子の前後でわける
        let mut filename = filename.split(|c| *c == b'.');
        let basename = filename.next();
        let extname = filename.next();
        let mut b = [b' '; 8];
        let mut e = [b' '; 3];
        if let Some(basename) = basename {
            for fi in 0..b.len() {
                if basename.len() <= fi {
                    break;
                }
                if b'a' <= basename[fi] && basename[fi] <= b'z' {
                    // 小文字は大文字で正規化しておく
                    b[fi] = basename[fi] - 0x20;
                } else {
                    b[fi] = basename[fi];
                }
            }
        } else {
            display_error!("File Not Found", cursor_y);
        }
        if let Some(extname) = extname {
            for fi in 0..e.len() {
                if extname.len() <= fi {
                    break;
                }
                if b'a' <= extname[fi] && extname[fi] <= b'z' {
                    e[fi] = extname[fi] - 0x20;
                } else {
                    e[fi] = extname[fi];
                }
            }
        }
        let mut target_finfo: Option<FileInfo> = None;
        for findex in 0..MAX_FILE_INFO {
            let finfo = unsafe {
                *((ADR_DISKIMG + ADR_FILE_OFFSET + findex * core::mem::size_of::<FileInfo>())
                    as *const FileInfo)
            };
            if finfo.name[0] == 0x00 {
                break;
            }
            if finfo.name[0] != 0xe5 {
                if (finfo.ftype & 0x18) == 0 {
                    let mut filename_equal = true;
                    for y in 0..finfo.name.len() {
                        if finfo.name[y] != b[y] {
                            filename_equal = false;
                            break;
                        }
                    }
                    for y in 0..finfo.ext.len() {
                        if finfo.ext[y] != e[y] {
                            filename_equal = false;
                            break;
                        }
                    }
                    if filename_equal {
                        target_finfo = Some(finfo);
                        break;
                    }
                }
            }
        }
        if let Some(finfo) = target_finfo {
            let content_length = finfo.size;
            let mut cursor_x = 8;
            for x in 0..content_length {
                let chr = unsafe { *((finfo.content_addr() + x as usize) as *const u8) };
                if chr == 0x09 {
                    // タブ
                    loop {
                        write_with_bg!(
                            sheet_manager,
                            sheet_index,
                            sheet.width,
                            sheet.height,
                            cursor_x,
                            cursor_y,
                            Color::White,
                            Color::Black,
                            1,
                            " "
                        );
                        cursor_x += 8;
                        if cursor_x == MAX_CURSOR_X {
                            cursor_x = 8;
                            cursor_y = newline(cursor_y, sheet_manager, sheet_index);
                        }
                        if (cursor_x - 8) & 0x1f == 0 {
                            // 32で割り切れたらbreak
                            break;
                        }
                    }
                } else if chr == 0x0a {
                    // 改行
                    cursor_x = 8;
                    cursor_y = newline(cursor_y, sheet_manager, sheet_index);
                } else if chr == 0x0d {
                    // 復帰
                    // 何もしない
                } else {
                    write_with_bg!(
                        sheet_manager,
                        sheet_index,
                        sheet.width,
                        sheet.height,
                        cursor_x,
                        cursor_y,
                        Color::White,
                        Color::Black,
                        1,
                        "{}",
                        chr as char
                    );
                    cursor_x += 8;
                    if cursor_x == MAX_CURSOR_X {
                        cursor_x = 8;
                        cursor_y = newline(cursor_y, sheet_manager, sheet_index);
                    }
                }
            }
            cursor_y = newline(cursor_y, sheet_manager, sheet_index);
        } else {
            display_error!("File Not Found", cursor_y);
        }
    } else {

やたらと長くなってしまったが、やっていることは割と素直で、ファイルを探し、あれば中身を読み込んで表示、ということをしている。
タブや改行はそれぞれ別に処理するようにしている。

実行結果

以下の内容のテキストファイルをimgに読み込ませておく

catを実行してみると、ファイルの中身が正しく表示された。

catコマンド

File Allocation Table (FAT)を参照するようにする

上記のcatだと512バイトを超すファイルの場合に、うまく表示されないことがある。
その場合、FATを参照にして次の512バイトがどこに配置されているかを知ることができる。

まずは準備する。

これを使って、先程のファイルの中身のロード部分を書き換える。

実行結果の画面は以前と変わらないので省略する。

アプリケーションの起動

ここまでで、OS上でアプリケーションを起動できる準備が整った。
簡単なアプリケーションとして以下のHLTするだけのアプリケーションを起動する。

流れとしては、まずはcatと同様、ファイルを探す。見つかった場合、今度はcatとは異なり、GDTでセグメントを登録し、farjmpでアプリケーションを起動する。
catと共通処理である、ファイルを探す部分を関数として抽出しておく。

hltコマンドとして先ほどのHLTするだけのasmファイルをアセンブルしたhlt.binを探して起動するようにする。

実行結果

hltと入力してエンターキーを押すと、以下のような画面のままコンソール画面が反応しなくなり、HLTされていそうなことがわかる。尚、入力フォームウィンドウは問題なく反応する。

hlt

また、hlt.asmの内容を以下の通り変更すると、意図通り全体がフリーズするようになる。

19日目は以上となる。ここまでの内容のコードはyoshitsugu/hariboteos_in_rustのday19としてタグを打ってある。