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

Posted on August 17, 2019 , Tags: OS自作入門, OS, Rust

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

素数の表示

以下のアプリケーションを実行して素数を表示してみる。

実行してみて気がついたが、_api_putstr0で呼び出されるput_stringが呼出のたびにcursor_xを初期位置に移動させてしまっており、前の出力を上書きしてしまっていた。

のようにして初期位置にもどさないオプションもつけられるようにした。
これで以下のように1000までの素数が表示できた。

素数の表示

ここで、本ではallocaの説明にはいっていくのだが、Rustで書いているせいか、allocaを使わなくても問題なく10000までの素数も表示できてしまった。
そこで、allocaについては軽く学んでおくとしてスキップすることにした。

ファイルIOの作成

ファイルIO(とはいえreadのみ)のシステムコールをつくる。
Taskにファイルのメタ情報を持つようにする。

// console.rs
#[no_mangle]
pub extern "C" fn hrb_api(/* 省略 */) -> usize {
    // 省略
    } else if edx == 21 {
        let fhandlers =
            unsafe { &mut *(task.file_handler_addr as *mut [FileHandler; MAX_FILE_HANDLER]) };
        let mut fhandler: Option<&mut FileHandler> = None;
        for i in 0..MAX_FILE_HANDLER {
            if fhandlers[i].buf_addr == 0 {
                fhandler = Some(&mut fhandlers[i]);
                break;
            }
        }
        let mut i = 0;
        loop {
            let chr = unsafe { *((ebx as usize + i as usize + ds_base) as *const u8) };
            if chr == 0 {
                break;
            }
            i += 1;
        }
        let filename = unsafe { *((ebx as usize + ds_base) as *const [u8; 30]) };
        let fat = unsafe { *(task.fat_addr as *const [u32; MAX_FAT]) };
        if let Some(fhandler) = fhandler {
            let finfo = search_file(&filename[0..i]);
            let reg_eax = unsafe { &mut *((reg + 7 * 4) as *mut usize) };
            if let Some(finfo) = finfo {
                *reg_eax = fhandler as *const FileHandler as usize;
                fhandler.buf_addr = memman.alloc_4k(finfo.size).unwrap() as usize;
                fhandler.size = finfo.size as i32;
                fhandler.pos = 0;
                finfo.load_file(fhandler.buf_addr, &fat, ADR_DISKIMG + 0x003e00);
            } else {
                *reg_eax = 0;
            }
        }
    } else if edx == 22 {
        let mut fh = unsafe { &mut *(eax as *mut FileHandler) };
        memman.free_4k(fh.buf_addr as u32, fh.size as u32).unwrap();
        fh.buf_addr = 0;
    } else if edx == 23 {
        let mut fh = unsafe { &mut *(eax as *mut FileHandler) };
        if ecx == 0 {
            fh.pos = ebx;
        } else if ecx == 1 {
            fh.pos += ebx;
        } else if ecx == 2 {
            fh.pos = fh.size + ebx;
        }
        if fh.pos < 0 {
            fh.pos = 0;
        }
        if fh.pos > fh.size {
            fh.pos = fh.size;
        }
    } else if edx == 24 {
        let fh = unsafe { &mut *(eax as *mut FileHandler) };
        let reg_eax = unsafe { &mut *((reg + 7 * 4) as *mut i32) };
        if ecx == 0 {
            *reg_eax = fh.size;
        } else if ecx == 1 {
            *reg_eax = fh.pos;
        } else if ecx == 2 {
            *reg_eax = fh.pos - fh.size;
        }
    } else if edx == 25 {
        let mut fh = unsafe { &mut *(eax as *mut FileHandler) };
        let mut size: usize = 0;
        for i in 0..(ecx as usize) {
            if fh.pos == fh.size {
                break;
            }
            let ptr = unsafe { &mut *((ebx as usize + ds_base + i) as *mut u8) };
            let buf = unsafe { &*((fh.buf_addr + fh.pos as usize) as *const u8) };
            *ptr = *buf;
            fh.pos += 1;
            size = i + 1;
        }
        let reg_eax = unsafe { &mut *((reg + 7 * 4) as *mut usize) };
        *reg_eax = size;
    }
    0
}

impl Console {
    pub fn cmd_app<'a>(&mut self, filename: &'a [u8], fat: &[u32; MAX_FAT]) {
        // 省略
        // start_app後
            // クローズしていないファイルをクローズ
            let fhandlers =
                unsafe { &mut *(task.file_handler_addr as *mut [FileHandler; MAX_FILE_HANDLER]) };
            for i in 0..8 {
                let mut fhandler = &mut fhandlers[i];
                if fhandler.buf_addr != 0 {
                    memman
                        .free_4k(fhandler.buf_addr as u32, fhandler.size as u32)
                        .unwrap();
                    fhandler.buf_addr = 0;
                }
            }
        // 省略
    }
}

これを使ってテスト用のアプリを作る。
本ではfopenとfreadしか使っていないが、折角なので、いろいろやってみることにした。

最初の20バイトを読み飛ばした上で、10バイトだけとってくるようなアプリになっている

実行結果

以下の通り、ipl.asmの一部を表示することができた。

ipl.asmをcat

catコマンドをアプリケーションに置き換える

catをOS組み込みで作っていたが、アプリケーションで置き換える。
コンソールからファイル名をうけとれるようにするために、そのようなシステムコールを作る。

もともとの実行されたコマンドラインはTaskにもつようにした。

アプリケーションは以下の通り。

もちろん、既存のcmd_catは削除している。

実行結果

以下の通り、cat コマンドをアプリケーションとして実行することができた

cat readme.md

日本語フォントの導入

フォントファイルのロード

日本語表示ができるようにする。
まずはTaskにlang_modeという表示モードを持たせるようにする

フォントファイルは本のものをそのまま使う

他にも細かいところは修正したが省略する。
ここまでで以下のようなアプリケーションで半角カタカナが表示できる。

実行結果

以下の通り、cat コマンドをアプリケーションとして実行することができた

半角カタカナ表示

全角表示

全角表示部分を追加する。
ちょっと汚いが、Consoleの方に全角の表示ロジックは入れた。
細かいところを記載すると多いので、概要だけ載せる。

全角文字を出す場合に、2バイト分の内容が必要になるので、1バイト目をタスクに記憶している。
表示ロジックは本と同様なので割愛する。

実行結果

本のipl20.nasをcatして、以下のように漢字やひらがなが表示されたことが確認できた。

全角表示

EUC対応

EUC対応も本にならって導入する。
ほぼJISの場合と同じなので、コードは割愛する。

実行結果

langmode 2を指定することで、EUCで作ったテキストファイルを表示できた。

EUC表示

langmodeを確認できるシステムコール

langmodeもアプリケーションから確認できるようにする。

これでアプリケーションはlangmodeによって文字コードを切り替えて表示することができる。

実行結果

以下の通り、langmodeによってメッセージを切り替えることができた。

langmodeによる切り替え

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