起動するまでの長い道のり 前置き篇

 なんだかとても間が空いてしまった。色々忙しかったせいもあるけれど、それ以上に起動する部分で色々詰まってしまっていた。今日ようやく、FDからカーネルイメージを読み込み、D言語のクラスを使ってシリアルポートに文字を出力するところまで到達した。D言語さえ使えれば後は何でもできるだろう(嘘)。

 今までこの日記の対象読者をあんまり考えていなかったけれど、ようやく実際の製作の記録に入るので、ある程度相手を想定しようと思う。JavaとかC言語で多少プログラムを作ったことのある専門学校生1年目くらいのレベルを想定する。それだと私自身とあんまり変わらないかもしれないけど。(因みに、私はプログラミングを誰かに教えてもらったことはない。最終学歴は専門学校で、しかも小説を書くための学校だった……)

 前置きはこれくらいにして、OS製作の話に入る。
 とりあえず最初に起動する部分を作ってしまおうと思う。昔懐かしいフロッピーディスクから起動して、画面に文字を表示するまでだ。

とりあえず起動しよう

 パソコンは電源を入れれば起動する。これはごく当然のことだ。パソコン”ユーザー”である限りは。あるいはちょっと進化して、普通のプログラマーでいたとしても。
 しかし、OSなんてものを作ろうとしたときは、この起動するという長い道のりにいきなりぶつかることになる。実際はブートローダというもの使って手間を省く方法もあるそうだが、まだよく知らない(笑)。自前で起動して初めて一人前のOSじゃないか!(そうか?)
 というわけで「起動した」といえる状態まで行くことを最初の目標にする。

どうすれば起動できるものを作れるのか

ライブラリを使わない

 普通のプログラムはOSの上でダブルクリックしたりするとごく当たり前のように起動する。だけどそれは、OSというお膳立てがあるからできることだ。プログラムがどのようにディスクからロードされて実行されるのか、それだけで本が1冊書けてしまうほど大変なことらしい。
 しかも、普通のプログラムというものはOSにあるライブラリを利用している。WindowsのDLLなどがそれだ。多分ほとんどのプログラムは、自分自身のコードよりもDLL等で呼び出すコードの方がずっと多いと思う。実行時間から言っても、プログラムの「外側」(ちょっと語弊があるけど)のコードを動かしている時間の方が長いだろう。

 OSを自作する場合、そういった既存のライブラリを利用することはできない。
 別の言い方をすれば、既存のライブラリを利用するようなソースコードを書いてはいけない。

 ふーんと思うだけかもしれないけど、最近のプログラミング言語は思いっきりライブラリに依存しているのだ。ライブラリはある意味言語の一部だとさえ言える。例えばJavaなんか標準ライブラリがなければほとんど何もできない言語だろう。それに、普段何気なく書いているコードだってライブラリを使っている。例えばC++は、ライブラリがないとnewができない。なぜなら、メモリを確保するためにはOSの機能を呼び出す必要があって、そのためのライブラリを使うからだ。
 それでも、C言語C++ならばライブラリ依存部分を切り離してコードを書くことが比較的簡単だ(絶対的に言えば大変かもしれない)。これから私が使おうとしているD言語は……まあJavaC#よりは簡単かな……。

リアルモード

 やや突っ込んだ話になるけど、というかOSなんか作るんだから突っ込んだ話になるのは当然だけど、IntelのCPUには32ビットで動作するプロテクトモードと16ビットで動作するリアルモードという2つのモードがある。(もう1つあるけど無視)
 普段OSが動いていてユーザが使うときはプロテクトモードで動いている。ところが、PCが起動した直後はリアルモードなのだ。実用的なOSを作ろうとしたら、リアルモードからプロテクトモードに移行しなければならない。
 また、起動直後に動く部分はリアルモードのプログラムになっていないといけない。

 プロテクトモードは、その名前のとおりメモリや入出力(IO)ポートの保護があるモードだ。OSは、どこの馬の骨か分からないプログラムから自分自身を守るために、何重かの防御装置を用意している。そのお蔭で、プログラムが暴走してもヘンなダイアログが出てくるくらいで済む。システムやデータがぶっ壊れることはあまりない。多分ない。きっとない。

 リアルモードでは、そのような防御装置がない。メモリは誰でもどこからでもアクセス可能だし、IOポートだって叩きまくりだ。

 さらに、リアルモードでは16ビットのメモリ範囲や命令しか扱えない。CPUが16ビットだとか32ビットだとかまだ良く分からない人もいるかもしれない。実は私も良く分からない。この16ビットというのは、CPUにあるレジスタという作業用の変数みたいなものの大きさのことらしい。また、利用できる最大メモリ量=ポインタとかで扱える最大のメモリ範囲(アドレス範囲)にも関係する。ぶっちゃけ、ビット数=C言語のポインタのサイズとなる。
 16ビットで動作するということは、ポインタのサイズが16ビットになるということだ。16ビットになるということは最大値が65535になるということだ。ポインタの最大値が65535になるということは、なんと、メモリが64KBまでしか使えないということだ!(滝汗) そりゃないよな、今どき1G2G当たり前やんか。
 大量のメモリに慣れきって腑抜けたメタボリック症候群患者である我々現代人には、この制限はきつすぎる。そして非常に残念なことに、PC起動直後はこの窮屈なモードになっているのだ。

 本当にマジで起動するOSをイチから作る場合は、このリアルモードで立ち上がり、各種設定(色々ある……)をこなした上でプロテクトモードに移っていく処理を自分で書かなければならない。

 64KBなんて窮屈だけど、まあやれば何とかできるかもしれない。で、「やる」にはどうするか。リアルモードのプログラムを書くにはどうすればいいか。
 実は普通の高級言語(C言語とかC++とかもちろんD言語も)は、プロテクトモードのプログラムを書くことを前提に作られている。だってそれが普通の状態なんだもん。え、じゃあどうやってリアルモードのプログラムなんか書くの、というと、アセンブラを使う。

 アセンブラ。このマニアックでどこか先鋭的な、チクチクとした感じのする響き。アセンブラ。思い浮かぶのは、ハンダ付けの松脂の香り。メタリックシルバーのトゲの集合体。黄緑の大地に走る黄金(きん)の回路(サーキット)……。ああアセンブラ

 これで思いっきり引く人もいるかもしれない。少なくとも私は引いた。え、おまえD言語使うとかさっき散々言ったじゃねえか! と突っ込む人もいるかもしれないが、流石にこればっかりは無理だった。勘弁してください。あ、自前でコンパイラ書けばできますよもちろん。やってみたらどうですか?*1


 まあそんなわけで本当に一番最初に起動する部分はアセンブラを使う必要がある。というかアセンブラを使うのが一番簡単だ(汗)。

BIOS

 というわけで窮屈な上にアセンブラという絶望の淵に立ってしまった。しかし救いはある。BIOSがそれだ。
 BIOSは、PC起動時に毎回目にしていると思う。起動直後にディスプレイに出てくるメモリ容量とかCPUのクロック数とかは、BIOSが出しているものだ。PCを長く使っている人は、ハードウェアの設定などでBIOS画面を立ち上げたことが何回かあるだろう。
 実は、基本的な入出力の機能をプログラムに提供するという役目がBIOSにはある。何せBIOSとはBasic Input/Output Systemの略なのだ。プロテクトモードでのOS&ライブラリとプログラムとの関係に近い。
 起動直後の我らが自作OSは、このBIOSを利用してよい。というか、起動直後のリアルモードでなければBIOSは利用できない。FDの読み込みとかキーボード入力の取得とか画面への文字表示とか、結構色んなことができる。プロテクトモードに移ってしまった後はきっとBIOSが恋しくなる。

 あと、OSの最初の部分(ブートセクタ)を読み込んで実行するという役割もBIOSにはある。ブートセクタを書く側としては、BIOSが用意してくれる実行環境に合わせてプログラムを書くことになる。

*1:余談:mixinにコンパイル時関数実行を駆使すれば、コンパイルタイムアセンブラという奇妙なものを実装することで、ひょっとしたらD言語でもリアルモードのコードを生成できるようになるかもしれない。でも起動可能なイメージにするためにはリンカの助けが必要だろうけど