「30日でできる!OS自作入門」をRustで。25日目
「30日でできる!OS自作入門 」のC言語の部分をできるだけRustですすめてみる。今回は25日目の内容。
BEEP音をならす
BEEP音をならすシステムコールをつくる。
// console.rs
#[no_mangle]
pub extern "C" fn hrb_api(/*省略*/) -> usize {
// 省略
} else if edx == 20 {
if eax == 0 {
let i = in8(0x61);
0x61, i & 0x0d);
out8(} else {
let i = 1193180000 / eax;
0x43, 0xb6);
out8(0x42, i as u8);
out8(0x42, (i >> 8) as u8);
out8(let i = in8(0x61);
0x61, (i | 0x03) & 0x0f);
out8(}
}
0
}
アプリ側は以下のようになる。(asmのコードは本と同じなので省略する)
// beepdown/src/lib.rs
[no_mangle]#[start]
pub extern "C" fn hrmain() {
let timer_index = alloc_timer();
unsafe {
.clone(), 128);
_api_inittimer(timer_index}
let mut i = 20000000;
while i >= 20000 {
unsafe {
;
_api_beep(i), 1);
_api_settimer(timer_index}
if get_key(1) != 128 {
break;
}
-= i / 100;
i }
unsafe { _api_beep(0) };
;
end()}
QEMUの環境しか手元になく、実行できなかったが、一旦できたことにして次にいく。
パレットの追加
パレットの色を増やす。
// vga.rs
pub fn init_palette() {
let eflags = asm::load_eflags();
asm::cli();
asm::out8(0x03c8, 0);
for i in 0..16 {
// 書き込むときは上位2ビットを0にしないといけない。See: http://oswiki.osask.jp/?VGA#o2d4bfd3
asm::out8(0x03c9, COLOR_PALETTE[i][0] / 4);
asm::out8(0x03c9, COLOR_PALETTE[i][1] / 4);
asm::out8(0x03c9, COLOR_PALETTE[i][2] / 4);
}
let mut color_palette2: [u8; 216 * 3] = [0; 216 * 3];
for b in 0..6 {
for g in 0..6 {
for r in 0..6 {
+ g * 6 + b * 36) * 3 + 0] = r as u8 * 51;
color_palette2[(r + g * 6 + b * 36) * 3 + 1] = g as u8 * 51;
color_palette2[(r + g * 6 + b * 36) * 3 + 2] = b as u8 * 51;
color_palette2[(r }
}
}
for i in 0..216 {
asm::out8(0x03c9, color_palette2[i * 3] / 4);
asm::out8(0x03c9, color_palette2[i * 3 + 1] / 4);
asm::out8(0x03c9, color_palette2[i * 3 + 2] / 4);
}
asm::store_eflags(eflags);
}
今回のアプリケーションは以下の通り
// color/src/lib.rs
#[no_mangle]
#[start]
pub extern "C" fn hrmain() {
unsafe { _api_initmalloc() };
let buf_addr = unsafe { _api_malloc(144 * 164) };
let sheet_index = open_window(buf_addr, 144, 164, -1, b"color".as_ptr() as usize);
for y in 0..128 {
for x in 0..128 {
let r = x * 2;
let g = y * 2;
let b = 0;
let ptr = unsafe { &mut *((buf_addr + x + 8 + (y + 28) * 144) as *mut u8) };
*ptr = (16 + (r / 43) + (g / 43) * 6 + (b / 43) * 36) as u8;
}
}
unsafe { _api_refreshwin(sheet_index as usize, 8, 28, 136, 156); }
unsafe { _api_getkey(1) };
;
end()}
実行結果
以下の通り、追加された色を確認できた。
見かけ上の色を増やす
さらに、以下のようにすることで、見かけ上の色を増やせる。
// color/src/lib.rs
#[no_mangle]
#[start]
pub extern "C" fn hrmain() {
unsafe { _api_initmalloc() };
let buf_addr = unsafe { _api_malloc(144 * 164) };
let sheet_index = open_window(buf_addr, 144, 164, -1, b"color".as_ptr() as usize);
for y in 0..128 {
for x in 0..128 {
let ptr = unsafe { &mut *((buf_addr + x + 8 + (y + 28) * 144) as *mut u8) };
*ptr = rgb2pal(x as i32 * 2, y as i32 * 2, 0, x as i32, y as i32);
}
}
unsafe { _api_refreshwin(sheet_index as usize, 8, 28, 136, 156); }
unsafe { _api_getkey(1) };
;
end()}
fn rgb2pal(r: i32, g: i32, b: i32, x: i32, y: i32) -> u8 {
let table: [i32; 4] = [3, 1, 0, 2];
let x = x & 1;
let y = y & 1;
let i = table[(x + y * 2) as usize];
let r = (r * 21) / 256;
let g = (g * 21) / 256;
let b = (b * 21) / 256;
let r = (r + i) / 4;
let g = (g + i) / 4;
let b = (b + i) / 4;
return (16 + r + g * 6 + b * 36) as u8;
}
実行結果は以下の通り。
コンソール画面を2つ起動する
コンソール画面を複数起動できるようにする。
変更が多いので、詳細な差分はGitHubのdiffを参照として、ここではかいつまんで説明する。
まずは素朴に今までコンソール用に用意していた変数を配列にして複数もてるようにする。
// lib.rs
#[no_mangle]
#[start]
pub extern "C" fn hrmain() {
// 省略
const CONSOLE_COUNT: usize = 2;
let mut console_sheets: [usize; CONSOLE_COUNT] = [0; CONSOLE_COUNT];
let mut console_bufs: [usize; CONSOLE_COUNT] = [0; CONSOLE_COUNT];
let mut console_tasks: [usize; CONSOLE_COUNT] = [0; CONSOLE_COUNT];
for ci in 0..CONSOLE_COUNT {
// 今までコンソール用に準備していたものを単純に置き換えていく
= sheet_manager.alloc().unwrap();
console_sheets[ci] = memman
console_bufs[ci] .alloc_4k((CONSOLE_WIDTH * CONSOLE_HEIGHT) as u32)
.unwrap() as usize;
// 省略
}
単純に増やすことで一応画面は起動するが、動作しない。
これはメモリのアドレス直指定で受け渡していたConsoleの情報などが使えないためである。
これに対応するため、Task
にconsole_addr
、ds_base
を持つようにして動的に受け渡しができるようにする。
// mt.rs
#[derive(Debug, Clone, Copy)]
pub struct Task {
pub select: i32,
pub flag: TaskFlag,
pub level: usize,
pub priority: i32,
pub tss: TSS,
pub fifo_addr: usize,
pub console_addr: usize,
pub ds_base: usize,
}
今までCONSOLE_ADDR
、DS_BASE_ADDR
などの定数でメモリを指定してた部分を、task.console_addr
、task.ds_base
に置き換える。
また、セグメントも分けるようにする。
// console.rs
pub fn cmd_app<'a>(&mut self, filename: &'a [u8], fat: &[u32; MAX_FAT]) {
// 省略
let content_gdt = task.select / 8 + 1000; // <- 1003 固定から修正
let app_gdt = task.select / 8 + 2000; // <- 1004 固定から修正
}
実行結果
実行してみると、以下の通り、複数コンソールでそれぞれアプリケーションを実行できるようになったことが確認できた。
task_aウィンドウを開かないようにする
大本のタスクから直接起動しているウィンドウは削除する。
shi_win
など、ウィンドウを起動している関連の部分をまるごと削除した。詳細はGitHub上のdiffを参照。
ただ、削除するだけだと動かない。コンソール用Fifoの初期化タイミングを前にずらす必要がある。
// lib.rs
#[no_mangle]
#[start]
pub extern "C" fn hrmain() {
// 省略
let mut console_task_mut = &mut task_manager.tasks_data[console_tasks[ci]];
// ここから追加
= memman.alloc_4k(128 * 4).unwrap() as usize;
console_fifos[ci] let console_fifo = unsafe { &mut *(console_fifos[ci] as *mut Fifo)};
*console_fifo = Fifo::new(128, Some(console_tasks[ci]));
.fifo_addr = console_fifos[ci];
console_task_mut// ここまで追加
let console_esp = memman.alloc_4k(64 * 1024).unwrap() + 64 * 1024 - 12;
実行結果
以下の通り、コンソールだけが起動するようになった。
25日目は以上となる。ここまでの内容のコードはyoshitsugu/hariboteos_in_rustのday25としてタグを打ってある。