2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

中学生がAIを使い倒して、コピペだけで3日でOSを自作してしまった話

2
Last updated at Posted at 2026-04-10

※最高に手抜き

どうも、中学生です。

初めましてこんにちは。今回は初めて記事を書くのです。
OSを作りたいと思いましてですね、RustでOSを作ってみたんですよ(語彙力皆無)
とりあえず本編に行きましょうか。

環境

MacBook Air M1
RustでIntelのOSを作りますわよ
参考にした本はなく、AIだけで作りましてよ!

1日目

スクリーンショット 2026-04-07 14.40.34.png
いきなりですが環境構築を終えました...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日目

スクリーンショット 2026-04-10 13.28.46.png
デェえぇぇぇえぇぇ絵キタ〜!
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時間な〜

2
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?