「30日でできる!OS自作入門」を今更ながらやってみる - 3日目
今日も今日とてOSやるぞ
2日目は こちら
3日目 - 32ビットモード突入とC言語導入
今日は重いですね…頑張ってまとめます。
1 - さあ本当のIPLを作ろう
さて書き足された部分のまとめをしていきます。
フロッピーディスクの構造を学ぼう
INT 0x13
は前回やったとおり割り込み命令ですね。
INT : ソフトウェア割り込み命令のこと これだけだと何がなんだかわかりませんが、今のところはBIOSの関数呼び出しの一種だと思っておけばいいらしいです。 BIOSというのは「basic input output system」の略です。いかにも画面に文字表示したりキーボード入力できそうな雰囲気ですね。これを用いてハロワを表示させていたんですね〜
「30日でできる!OS自作入門」を今更ながらやってみる - 2日目 - 無限遠まで突撃中
さて何をさせるのでしょうか。本によると
ディスクの読み込み、書き込み、セクタのベリファイおよびシーク
らしいです。パラメータの解説をします。
AH
: モードの選択AL
: 処理するセクタ数CH
:シリンダ番号 & 0xff
CL
:セクタ番号(bit0-5) | (シリンダ番号 & 0x300) >> 2
DH
: ヘッド番号DL
: ドライブ番号ES:BX
: バッファアドレス(ベリファイ時とシーク時は参照しない)- 戻り値 :
FLAGS.CF == 0
: エラーなし、AH == 0
FLAGS.CF == 1
: エラーあり、AH
にエラーコード(リセットファンクションと同じ)
今回は AH = 0x02
でディスクの読み込みです。
まずは戻り値です。
CF
というのは「キャリーフラグ」というもので、1ビット記録するレジスタです。本来はキャリー状態というものを記録するのですが、扱いやすいのでいろいろ使われるらしいです。
ちなみにキャリーというのは carry 、つまり けた上げ のことです。計算していてレジスタが桁あふれしたときにキャリーフラグがぴょこっと1になることでうまいこと計算ができるんですね〜大学の論理回路の授業でやりました。詳しい解説は この方の記事 を読むといいです。(キャリーフラグはどうやら符号なし演算のときに使うらしいですね)
さて、残りを解説するにはフロッピーディスクの構造を説明しなくてはいけません。以下の図を見てください。
この図はフロッピーディスク内部の磁気ディスクの構造です。円筒状にシリンダ(図ではトラック)が配置されており、セクタという区分で分割統治されていて、磁気ヘッドから読み込みます。シリンダは全部で80あり、セクタは各シリンダ内に18あります。磁気ヘッドは表と裏で2つあります。今の解説で上のパラメータは大体わかりましたね。
ドライブ番号というのは、フロッピーディスクドライブが沢山つながっている場合に識別するための番号です。ドライブが1つの場合は0番を指定します。
残りはバッファアドレスですね。これは メモリのどこに読み込むか を表すものです。なぜ2つのレジスタで表すのでしょう?というのも 前回 を思い出してほしいのですが、 BX
というレジスタは16ビットでしたね。ということは、 BX
一つだけだと 0x0000 ~ 0xffff
までしか、つまり64KBまでしか表すことができないのです。
これを回避するために2つレジスタを用いてメモリの番地を表します。具体的にはバッファアドレスは ES * 16 + BX
で表されます。 ES
でおおざっぱに指定してから BX
で細かく指定、という感じですね。これで 0xffff * 16 + 0xffff = 1,114,095
バイト、つまり約1MB使えます。やったー。
セグメントレジスタ
ところで先程登場した ES
レジスタですが、これはセグメントレジスタと呼ばれるレジスタです。末尾に S
がつくレジスタですね。セグメントとは何ぞやと思ったので調べてみました。
セグメント方式 (memory segmentation)は、メモリ管理の方式の一つ。プログラムやデータをセグメントまたはセクションという「可変な」大きさのまとまりで管理する。セグメントは、メモリ空間上で、情報の属性などによって分類されたグループである。セグメント方式でメモリ位置を参照するには、セグメントを識別する値とセグメント内のオフセットを指定する。 (Wikipediaより)
なんだか難しくてよくわかりませんが、つまりセグメントレジスタでメモリをおおざっぱに区切っているということなのかな?(違うよ!ということであればコメントください)
大事なのは最後の文章で、「メモリ位置を参照するには、セグメントを識別する値とセグメント内のオフセットを指定」とあります。さっきの例だと、セグメントを識別する値とは ES
のことで、セグメント内のオフセットとは BX
のことなのでしょう。
メモリの番地指定の裏設定
ここで新事実が発覚。なんと今までメモリの番地指定では、実は暗黙的に DS
がセグメントレジスタとして指定されていたんだよ!!!(ΩΩΩ < ナ、ナンダッテー!?)
つまり我々が
MOV CX,[1234]
だと思っていたのは、実は
MOV CX,[DS:1234]
だったのです。この理由から、 MOV DS,0
の初期化が必要だったのです。(0以外にすると番地の計算がややこしいことになりますから…)
2 - エラーになったらやり直そう
AH = 0x00
、 DL = 0x00
で INT 0x13
してやると、ドライブのシステムリセットが走るようです。
3 - 18セクタまで読んでみる
4 - 10シリンダ分を読み込んでみる
特に難しいところはないので省略!!!
5 - OS本体を書き始めてみる
ここでやることは、 OSのプログラムが保存されているメモリ番地にブートセクタからジャンプする ことです。そのためにはまずOSのプログラムがどの番地に保存されているか調べる必要があります。とりあえず普通にコピーしてバイナリエディタで覗いてみましょう。
…
本によって進めると、空ディスクにファイルを保存すると
- ファイル名は
0x002600
以降に入る - ファイルの中身は
0x004200
以降に入る
ということがわかりました。ブートセクタから 0x004200
に飛べばいいですね。
6 - ブートセクタからOS本体を実行させてみる
ブートセクタからOS本体に飛ばす処理をしていますね。
7 - OS本体の動作を確認してみる
真っ黒画面表示です。画面関係なので INT 0x10
で割り込みさせればいいですね。
パラメータは以下のとおりです。
AH = 0x00
AL = モード
- 戻り値 : なし
8 - 32ビットモードへの準備
C言語で開発したいので32ビットモードへの移行を目標にします…が32ビットモードではBIOSの機能が使えなくなります。(BIOSは16ビットモードを前提に書かれているので…)なのでI/O関係のことは今のうちにやっておきましょう。具体的にはキーボードの状態をBIOSから教えてもらいます。
AH = 0x02
: シフトフラグステータス取得
詳細は ここ で調べました。
あとVRAMはメモリマップの色々なところに散らばっているらしいです。びっくりしないようにしましょう。
9 - ついにC言語導入へ
asmhead.nas
に100行くらい書き足されたようです…が今はその内容は明かされません。今後の解説に期待ですね。
C言語で書かれたOS本体は簡単ですね。割愛します。
さてこのcファイルをコンパイルして ipl10.nas
や asmhead.nas
なんかとリンクするわけなんですが、そこら辺は筆者であるKさんが用意した(のを先人が整備した)ツールが大活躍です。あまり気にせず進められるでしょう…と言いたいのですが、一つ悩んだのがあって、hikalium氏のツールの使い方で1時間位つまりました。ちょっと分かりづらいのでここで共有しておきます。
z_tools
内の haritol concat
の使い方ですが、
copy hoge+fuga piyo
は、
haritol concat piyo hoge fuga
と対応しているようです。参考にしてください。
10 - とにかくHLTしたい
C言語ではHLTできないのでアセンブリでHLTする関数を書きます。少し気になったところなのですが、 naskfunc.nas
の
[SECTION .text]
ってリンカスクリプトの .text
セクションと同じなんですかね?知っている人、教えてくださいm(-_-)m
あと内部のリンクの仕組みがまだわからないですね…分かり次第追記します。
今日はここまで
お疲れ様でした。また明日〜
4日目は こちら