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

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

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

mallocを実装する

OSからアプリ用のメモリを確保してアプリに渡せるようにする

アプリ側は以下のようになる。

[CS:0x0020]などのセグメントレジスタつきの表記がインラインアセンブリだと扱いづらく、別ファイルで本通りのアセンブリ言語でのコードを書くことにした。

実行結果

実行結果の画面としては変わらないが、無事mallocをつかって表示ができた。

点を描画

点を描画するAPIをつくる。本の内容は少しとばして、最初から複数ランダムで表示、および最後に描画するようにする。

// console.rs
#[no_mangle]
pub extern "C" fn hrb_api(/*省略*/) -> usize {
    // 省略
    } else if edx == 6 {
        let mut sheet_index = ebx as usize;
        let mut refresh = true;
        if sheet_index >= MAX_SHEETS { // refreshしたくない場合はMAX_SHEETS = 256を足す
            refresh = false;
            sheet_index -= MAX_SHEETS;
        }
        let sheet = sheet_manager.sheets_data[sheet_index];
        let string = unsafe { *((ebp as usize + ds_base) as *const [u8; 30]) };
        use crate::vga::ScreenWriter;
        use core::fmt::Write;
        let mut writer = ScreenWriter::new(
            Some(sheet.buf_addr),
            to_color(eax as i8).unwrap(),
            esi as usize,
            edi as usize,
            sheet.width as usize,
            sheet.height as usize,
        );
        write!(writer, "{}", from_utf8(&string[0..(ecx as usize)]).unwrap()).unwrap();
        if refresh {
            sheet_manager.refresh(sheet_index, esi, edi, esi + ecx * 8, edi + 16);
        }
    } else if edx == 7 {
        let mut sheet_index = ebx as usize;
        let mut refresh = true;
        if sheet_index >= MAX_SHEETS {  // refreshしたくない場合はMAX_SHEETS = 256を足す
            refresh = false;
            sheet_index -= MAX_SHEETS;
        }
        let sheet = sheet_manager.sheets_data[sheet_index];
        boxfill(
            sheet.buf_addr,
            sheet.width as isize,
            to_color(ebp as i8).unwrap(),
            eax as isize,
            ecx as isize,
            esi as isize,
            edi as isize,
        );
        if refresh {
            sheet_manager.refresh(sheet_index, eax, ecx, esi + 1, edi + 1);
        }
    } else if edx == 8 {
    // 省略
    } else if edx == 11 {
        // 点の描画
        let mut sheet_index = ebx as usize;
        let mut refresh = true;
        if sheet_index >= MAX_SHEETS {
            refresh = false;
            sheet_index -= MAX_SHEETS;
        }

        let sheet = sheet_manager.sheets_data[sheet_index];
        let ptr = unsafe {
            &mut *((sheet.buf_addr + sheet.width as usize * edi as usize + esi as usize) as *mut u8)
        };
        *ptr = eax as u8;
        if refresh {
            sheet_manager.refresh(sheet_index, esi, edi, esi + 1, edi + 1);
        }
    } else if edx == 12 {
        // refreshのみをするシステムコール
        let sheet_index = ebx as usize;
        sheet_manager.refresh(sheet_index, eax, ecx, esi, edi);
    }
    0
}

これらのシステムコールを使って、アプリケーション側は以下のようにする。

ここで、ランダムな点を描画するため、randクレートを導入している。randクレートはCargo.tomlに

のように書けば、no_stdで使えるようだ。

実行結果

以下の通り、点がランダムに複数描画されているウィンドウを表示できた。

点の描画

線の描画

次に、線の描画もできるようにする。

draw_linevga.rsに書く。

本と同様、現状小数点の計算ができないため、<< 10 した上で計算し、計算結果を読み込むときに>> 10している。

アプリケーション側は以下のようなコードとする。

_api_linewin はどうもインラインアセンブリでやるとfor iiの値がかわってしまってうまく動かないので、本通りの内容をasmfunc.asmに書くことで対応した。

実行結果

以下の通り、線を描画することができた。

線の描画

ウィンドウのクローズ

現状アプリが終了してもウィンドウが閉じないので、ウィンドウも閉じられるようにする。

14と15のシステムコールを追加した。14はウィンドウを閉じるだけのシステムコール、15はキー入力を受け付けるシステムコールとなる。
これにより、何かキーが入力されたらウィンドウを閉じる、ということができるようになる。

また、強制終了時にも閉じるように、SheetTaskの情報を持たせ、アプリケーションの終了時にそのアプリケーション用のタスクにひもづいているSheetがまだ閉じていなかったら閉じるようにする。

アプリケーションのコードとしては、上記の線描画のコードを少し修正して以下のようにする。

実行結果

以下の通り、キー入力でのウィンドウの終了、および強制終了でのウィンドウ終了が確認でき

ウィンドウのクローズ

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