本記事の構成および論理分析にはAI(人工知能)を使用しています。情報の正確性は、システム管理者(UNIXユーザー)による手動検証済みです。
「いちばんやさしい!OS自作超入門」第3章まででつまずいたこと | UNIX Cafe

先日、自作LC-3 VMのコードをGitHubに公開し、仮想マシン制作の連載がひと区切りつきました。
この連載を通して、C言語のビット演算や配列を使いながら、「命令を読み込み、レジスタを更新し、メモリを読み書きする」というCPUの基本的な流れをかなり近くで見ることができたと思います。
ただ、LC-3VMで学んだのはあくまで「ハードウェア(CPU)に近い部分」です。
ここからさらに一歩進んで、マルチタスクやメモリ管理といった「OS(オペレーティングシステム)の仕組み」を学びたくなりました。
そこで、次なるステップとして選んだ教材が『いちばんやさしい!OS自作超入門』です。
この本の素晴らしいところは、複雑な本物のCPU仕様に迷わされないよう、あえて「仮想CPU」を使ってOSの本質だけを学べる点です。「シンプルなCPUを相手にする」というアプローチは、まさに私たちがLC-3VMでやってきたことと同じ。これなら知識がそのまま活かせそうだとワクワクしています。
この記事では、お目当ての「シングルタスクOSの開発」に入る前に、第三章までの準備段階で躓いたところを整理します。
第3章まででつまずいたことを整理する
「いちばんやさしい!OS自作超入門」を読みながら、GitHubで公開されているos_book_codeを使って第3章まで進めてみました。
第3章までは、まだ本格的なOS開発というより、アセンブラ、CPUエミュレータ、簡単なアセンブリプログラムを動かすための準備が中心です。
ただ、書籍本文の手順と現在のGitHub最新版では、いくつか前提が違って見えるところがありました。特にemu.pyの役割と、HALT / RETの違いで迷いやすいです。
この記事では、第3章まででつまずいた点と、その確認結果をまとめます。第4章から始まる「シングルタスクOSを開発」に入る前の準備メモです。
使用環境
- macOS
- Python 3
- GitHubリポジトリ: https://github.com/sueyasu/os_book_code
- 補助リポジトリ: https://github.com/k1117n-cmyk/os-book-code-practice
- 作業ディレクトリ:
os_book_codeローカル側のディレクトリ名です。
この記事では、現在のGitHub最新版を手元に置いている前提で書きます。書籍本文とGitHub最新版のコードが、常に同じ段階を前提にしているとは限らない点に注意します。
本文中で確認している「現在のGitHub最新版」は著者公開のsueyasu/os_book_codeを指し、early.pyを使った単体実行の手順は、この記事用に用意した補助リポジトリk1117n-cmyk/os-book-code-practiceで試せるようにしています。
第3章までの位置づけ
第3章までの内容を大きく分けると、次のようになります。
- 第1章: ソースコードを入手してサンプルを動かします
- 第2章: CPUの仕組みと基本的な考え方を確認します
- 第3章: アセンブリ言語で簡単なプログラムを書きます
ここでは、OS本体を作る前に、アセンブリコードをバイナリに変換し、それをCPUエミュレータで実行する流れを確認しています。
GitHubで公開されているasm.py、emu.pyと、書籍で前提としているemu.pyの役割を分けておくと、後の章に進みやすくなります。
第1章 開発環境を用意する
最初はPythonの環境設定です。この書籍はPythonで書かれた32bit 仮想CPUの上で動くOSを開発しますので、最初に前提となる仮想CPUを動かすための、Pythonの環境を構築します。
venvのドットで迷う
テキスト P.12
書籍の環境構築では、仮想環境を作るコマンドとして次のような形が出てきます。
python3 -m venv .
source bin/activateここで指定している.は「現在のディレクトリ」を意味します。
つまり、このコマンドは「現在のディレクトリそのものを仮想環境にする」という指定です。.venv/というディレクトリを作る指定ではありません。
実行すると、カレントディレクトリ直下に次のようなファイルやディレクトリが作られます。
bin/
include/
lib/
pyvenv.cfg作業ディレクトリ直下に仮想環境の中身を置きたくない場合は、作成先のディレクトリ名を明示します。
python3.12 -m venv .venv
source .venv/bin/activatepython -m venv .の.は、.venvの省略形ではありません。この点は最初に混乱しやすいところです。
1-4 ソースコードの入手とサンプルコードの実行
Wrote hello.binは正常なログ
テキスト P.14
書籍では、次のようなコマンドが出てきます。
python asm.py hello.asm
python emu.py hello.bin1行目を実行すると、次のようなログが表示されます。
Wrote hello.bin (28 bytes)これはエラーではありません。
python asm.py hello.asmは、hello.asmを機械語のバイナリに変換するコマンドです。この段階では、プログラムはまだ実行されていません。
Wrote hello.bin (28 bytes)は、hello.asmのアセンブルに成功し、hello.binという28バイトのバイナリを書き出した、という正常な中間ログです。
その次のエミュレータ実行で、初めてhello.binの中身がCPU上で実行されます。
emu.py hello.binでSimple OSが起動する
現在のGitHub最新版を使っていると、書籍どおりに次を実行しても、期待したHello, World!が表示されないことがあります。
python emu.py hello.bin代わりに、次のような表示になります。
Welcome to Simple OS!
>これはhello.binが実行された結果ではありません。
現在のリポジトリ直下にあるemu.pyは、引数で渡したhello.binを読む作りではなく、カレントディレクトリのos.binを読み込んでSimple OSを起動する作りになっています。
そのため、次のように引数を付けても、現在のemu.pyでは引数は使われません。
python emu.py hello.bin
python emu.py mul.bin
python emu.py pi.binどれも実際にはos.binのSimple OSが起動します。
hello.binを直接動かすにはearly.pyを使う
書籍序盤のhello.binを単体プログラムとして直接実行したい場合は、初期章用のemu.pyを使う必要があります。
ただ、現在の著者公開のリポジトリには、初期章用のemu.pyは残っていませんので、代わりに自作のearly.pyを用意しました。
python3 asm.py asm_code/hello.asm
python3 early.py asm_code/hello.binこの場合は、書籍序盤の説明に近い形で、hello.binが直接CPUエミュレータ上で実行されます。
期待する出力は次のような形になります。
Hello, World!
CPU halted.
R0: 00000000
R1: 00000000
R2: 00000000
R3: 00000000
R4: 00000000
R5: 00000000
R6: 00000000
R7: 00000000
R8: 0000000C
R9: 00000000
TP: 00000000
SP: 000FFFFF
PC: 0000000C
PT: 00000000
VT: 00000000
CR: 00000000一方、python emu.pyでSimple OSが起動している場合、CPU halted.とレジスタ一覧はhello.binの実行結果として出ているわけではありません。
Simple OSのプロンプトでexitを入力したときに、OSが終了してCPU halted.が表示されます。
Welcome to Simple OS!
> exit
bye.
CPU halted.
R0: 00000004
R1: 000C0004
R2: 00000000
R3: 00000000
R4: 00000000
R5: 00000000
R6: 00000000
R7: 00000000
R8: 0008064E
R9: 0008061C
TP: 00000016
SP: 000FF000
PC: 000802CC
PT: 000FFF00
VT: 000FF800
CR: C0000004この時に出力されるレジスタ一覧は、os.binの実行結果になります。
つまり、この2つは別の実行経路として分けて考える必要があります。
early.pyはこのブログ記事のGitHubで公開していますので、気になる方は試してみてください。
3-3 アセンブリ言語でプログラムを作成してみよう
HALTとRETの違い
第3章までのサンプルを動かすうえで、HALTとRETの違いも重要になります。
簡単に分けると、次のようになります。
HALT: CPUを停止しますRET: 呼び出し元へ戻ります
単体プログラムとして直接実行する場合、最後はHALTが自然です。そこでプログラム全体を終わらせ、CPUを停止します。
一方、Simple OSのexecコマンドから外部プログラムとして実行する場合、最後はRETが必要になります。RETで呼び出し元、つまりOS側のコマンドループへ戻るためです。
pi.asmの末尾が書籍とGitHubで違う
テキスト P.69
円周率を1000桁計算するpi.asmで、最後の命令が書籍とGitHub最新版で違っていました。
書籍側では、単体プログラムとして直接実行する流れに見えるため、最後がHALTになっています。
一方、GitHub最新版では最後がRETになっています。
Git履歴を見ると、次の変更が確認できました。
- HALT
+ RETコミットメッセージは次の内容でした。
execコマンドで起動できるように終了方法を変更つまり、GitHub最新版のpi.asmは、Simple OSのexecコマンドから起動する前提に変更されています。
- Commit 4c3898e : 5 files changed
- sueyasucommittedon Nov 1, 2025
pi.binをSimple OSのexecから実行する
現在のGitHub最新版のpi.asmを使う場合、末尾はRETのままでOKです。
実行手順は次のようになります。
cd /path/os_book_code
python asm.py os.asm
python asm.py pi.asm
mkdir -p dir
cp pi.bin dir/pi.bin
python emu.pySimple OSが起動したら、プロンプトで次を入力します。
> exec pi.binここで注意が必要なのは、exec pi.binはリポジトリ直下のpi.binを直接読むわけではない、という点です。
emu.pyのexec処理では、内部的にdir/<ファイル名>を開きます。そのため、pi.binは事前にdir/pi.binとして置いておく必要があります。
dir/pi.binが無い状態で実行すると、次のエラーになります。
File open error.これはpi.asmの文法エラーではなく、execが読む場所にpi.binが無いという意味です。
第3章までで分かったこと
第3章までを現在のGitHub最新版で動かすときは、次の点を押さえておくと混乱しにくいです。
asm.pyは.asmから.binを作りますWrote ...はアセンブル成功のログです- 現在の
emu.pyはos.binを起動します python emu.py hello.binとしても、現在のemu.pyはhello.binを読みません- 書籍序盤の単体プログラムを直接実行するなら
early.pyを使います HALTはCPU停止ですRETは呼び出し元へ戻る命令ですpi.asmは単体実行ならHALT、Simple OSのexecから実行するならRETにしますexec pi.binはdir/pi.binを読みます
書籍本文とGitHub最新版が、常に同じ段階のコードを前提にしているとは限りません。
迷ったときは、エミュレータが実際にどのファイルをopen()しているか、PCの初期値がどこになっているか、プログラムの最後がHALTかRETかを見ると切り分けやすいです。
第4章へ進む前に
第4章からは「シングルタスクOSを開発」に入ります。
ここからは、emu.pyがos.binを起動すること自体が本筋になります。第3章までで混乱しやすかった「単体プログラムを直接実行する世界」と「OSを起動して、その上でプログラムを動かす世界」が、ここから少しずつつながっていきます。
第4章へ進む前に、次の3つを区別できていればOKです。
asm.py: アセンブリをバイナリに変換しますearly.py: 書籍序盤の単体プログラムを直接実行しますemu.py: 現在のGitHub最新版ではos.binを読み込んでSimple OSを起動します
ここまで整理できたので、次はシングルタスクOSの開発に進みます。
「いちばんやさしい!OS自作超入門」 第4章でつまずいたこと
次回はシングルタスクOSの開発です。第4章を進める中で気づいたこと、つまずいた点と、手元で確認した対応を、書籍の進行順に整理していきます。








