※最高に手抜き
どうも、中学生です。
初めましてこんにちは。今回は初めて記事を書くのです。
OSを作りたいと思いましてですね、RustでOSを作ってみたんですよ(語彙力皆無)
とりあえず本編に行きましょうか。
環境
MacBook Air M1
RustでIntelのOSを作りますわよ
参考にした本はなく、AIだけで作りましてよ!
1日目

いきなりですが環境構築を終えました...1時間で。
早すぎませんか?(自画自賛)
でもAIの書いたものをコピペしていただけなんですけど。でも文字出すところまでいけました。いいでしょ♡
だがまだなんも理解しとらんのだ。だからこんな技術ブログにあるまじき文しか書けないのだ。何これ
Q. 技術ブログってこんなんでいいのかな?
A. よくねぇよ!
と言うわけでまだ1日目なので俺は何も理解していないんですよ。今日何をしたのかAIに聞いてみましょう
1. はじめに
中学生だけどOS自作に興味を持って「はりねずみ本」を参考に始めた。
M1 Mac(ARM)からx86_64(Intel)をターゲットにするという、ちょっと複雑な環境に挑戦。
2. やったこと(技術スタック)
言語: Rust (Nightly)
ツール: cargo-bootimage, QEMU
設定: .cargo/config.toml でのクロスコンパイル設定
3. ぶつかった壁:PageAlreadyMapped エラー
現象: QEMUを起動しようとしたら、メモリエラー(パニック)が発生。
原因: ブートローダーとカーネルがメモリの同じ住所(0x1000付近)を使おうとして衝突していた。
解決策: rustflags = ["-C", "link-arg=-Ttext=0x400000"] を追加して、カーネルの開始地点を4MB地点までずらした。
※ここ、エンジニアが一番「ほう、やるじゃん」と思うポイントです!
4. VGAバッファへの直接書き込み
0xb8000 という魔法の住所に、文字データと色データを直接叩き込んだ。
unsafe を使ってハードウェアを直接操作する、OS自作ならではの醍醐味を味わった。
5. まとめ
1時間くらい格闘して、画面に「Hi」が出たときは感動した。
次は println! を実装したい。
...ハリネズミ本ってなんだよ!!(迫真)
と、言うわけでとりあえず画面に文字が出たので今日は俺の勝ち。なんで負けたか明日までに考えといてください
2日目
Oh...再起動ループ...。
再起動ループが始まってしまったぞい
なぜだ...なぜなんだ!!
理由が全く理解できないぞ!!
Pythonで言うprintを作ろうとしたらしいんだけど
なんか再起動ループ入っちゃったみたいで...
と言うわけでprint_charは使えるようになったぞい
ただ何も理解できてないぞい...
ちな直すにはメモリ管理が必要らしいぞい
OSこわい...。
3日目

デェえぇぇぇえぇぇ絵キタ〜!
OS完成だぜ〜
何をしたのか正直わかってないぜ〜
AIにやったことをまとめてもらうぜ〜
1. 「再起動ループ」という強敵との戦い
昨日まで動いていたコードが、機能を追加した途端に「再起動」を繰り返すようになったべ。
原因:CPUが「ルール違反だ!」と怒るトリプルフォールト。
解決:難しい「割り込み(IDT)」という正攻法を一旦諦めて、泥臭いけど確実な「ポーリング(窓口を覗き見する)」作戦に切り替えて、見事勝利したべ!
2. キーボードとの「筆談」に成功
キーボードを叩くと画面に文字が出る仕組みをゼロから作ったべ。
I/Oポート通信:inb 命令を使って、マザーボード上のチップから「スキャンコード(キーの番号)」を直接奪い取った。
文字変換(デコード):数字でしかない合言葉を、match 文を使って「A」や「B」といった人間にわかる文字に翻訳したべ。
3. 世界に一つだけの「自作コマンド」を実装
ただ文字を出すだけじゃなく、OSが意思(?)を持つようになったべ。
記憶:打たれた文字を c1, c2 という変数に一時保存。
判定:エンターキーが押された瞬間に中身をチェック。
返信(ECHO & HI):打った文字を緑色で返し、さらに HI と打てば黄色い HELLO! で返事をする「対話型OS」を完成させたんだべ!
感想
わぁすごい(脳死)
5日で作る予定が3日でできたぜ〜
YATTA〜
一部コード
main.rs
#![no_std]
#![no_main]
use core::panic::PanicInfo;
#[no_mangle]
pub extern "C" fn _start() -> ! {
let vga_ptr = 0xb8000 as *mut u8;
let mut cursor: isize = 0;
let mut c1 = 0u8;
let mut c2 = 0u8;
let mut count = 0;
// 1. 画面クリア
unsafe {
for i in 0..(80 * 25) {
*vga_ptr.offset(i * 2) = b' ';
*vga_ptr.offset(i * 2 + 1) = 0x07;
}
}
let mut last_scancode = 0;
loop {
if unsafe { inb(0x64) } & 1 == 1 {
let scancode = unsafe { inb(0x60) };
if scancode < 0x80 && scancode != last_scancode {
let character = match scancode {
0x1e => b'A', 0x30 => b'B', 0x2e => b'C', 0x20 => b'D',
0x12 => b'E', 0x21 => b'F', 0x22 => b'G', 0x23 => b'H',
0x17 => b'I', 0x24 => b'J', 0x25 => b'K', 0x26 => b'L',
0x32 => b'M', 0x31 => b'N', 0x18 => b'O', 0x19 => b'P',
0x10 => b'Q', 0x13 => b'R', 0x1f => b'S', 0x14 => b'T',
0x16 => b'U', 0x2f => b'V', 0x11 => b'W', 0x2d => b'X',
0x15 => b'Y', 0x2c => b'Z', 0x39 => b' ',
_ => b'?',
};
if scancode != 0x1c {
unsafe {
*vga_ptr.offset(cursor * 2) = character;
*vga_ptr.offset(cursor * 2 + 1) = 0x0f;
}
cursor += 1;
if count == 0 { c1 = character; count = 1; }
else if count == 1 { c2 = character; count = 2; }
}
else {
// エンターキー:改行してエコー
cursor = ((cursor / 80) + 1) * 80;
unsafe {
*vga_ptr.offset(cursor * 2) = b'>';
*vga_ptr.offset(cursor * 2 + 1) = 0x07;
*vga_ptr.offset(cursor * 2 + 2) = c1;
*vga_ptr.offset(cursor * 2 + 3) = 0x0a; // 緑
*vga_ptr.offset(cursor * 2 + 4) = c2;
*vga_ptr.offset(cursor * 2 + 5) = 0x0a;
}
cursor = ((cursor / 80) + 1) * 80;
// 「HI」判定
if c1 == b'H' && c2 == b'I' {
unsafe {
// 1文字ずつ手動で書く!これが一番安全だべ!
*vga_ptr.offset(cursor * 2) = b'H';
*vga_ptr.offset(cursor * 2 + 2) = b'E';
*vga_ptr.offset(cursor * 2 + 4) = b'L';
*vga_ptr.offset(cursor * 2 + 6) = b'L';
*vga_ptr.offset(cursor * 2 + 8) = b'O';
*vga_ptr.offset(cursor * 2 + 10) = b'!';
for j in 0..6 {
*vga_ptr.offset(cursor * 2 + (j * 2) + 1) = 0x0e; // 黄色
}
}
cursor = ((cursor / 80) + 1) * 80;
}
count = 0; c1 = 0; c2 = 0;
}
}
last_scancode = scancode;
}
}
}
unsafe fn inb(port: u16) -> u8 {
let result: u8;
core::arch::asm!("in al, dx", in("dx") port, out("al") result);
result
}
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! { loop {} }
だぜ〜
終わり
終わりだぜ〜
ふざけまくったぜ〜
ちな作業時間は大体5時間な〜