30日OS自作入門-3日目(C言語導入前まで)-
最大の鬼門と言われる3日目に突入しました。ただ、今回はC言語導入前までのまとめをしたいとおもいます。
今回のまとめ
主に3日目の内容についてまとめていきます。3日目は主に割り込み命令を使って以下の処理を実現する内容です。
・ディスクからの読み込み
・ビデオモード設定(画面モードの切り替え)
アセンブリコードとimgファイルの中身
3日目のはじめのソースコードを、実際にアセンブリ命令を書いてimgファイルを作りqemuで起動しました。
書いたアセンブリ命令とマシンコードの対応関係については、以下のコマンドで生成されるlstファイルを見ると確認できます。
nasm ipl.nas -l ipl.lst -o ipl.img
上記のコマンドでは、lstファイルの生成とimgファイルの生成を同時にやります。
lstファイルは以下のようになっています。
1 ; haribote-ipl 2 ; TAB=4 3 4 ORG 0x7c00 5 6 00000000 EB4E JMP entry 7 00000002 90 DB 0x90 8 00000003 48415249424F5445 DB "HARIBOTE" 9 0000000B 0002 DW 512 10 0000000D 01 DB 1 11 0000000E 0100 DW 1 12 00000010 02 DB 2 13 00000011 E000 DW 224 14 00000013 400B DW 2880 15 00000015 F0 DB 0xf0 16 00000016 0900 DW 9 17 00000018 1200 DW 18 18 0000001A 0200 DW 2 19 0000001C 00000000 DD 0 20 00000020 400B0000 DD 2880 21 00000024 000029 DB 0,0,0x29 22 00000027 FFFFFFFF DD 0xffffffff 23 0000002B 48415249424F54454F- DB "HARIBOTEOS " 24 00000034 5320 25 00000036 4641543132202020 DB "FAT12 " 26 0000003E 00<rept> TIMES 18 DB 0x00 27 28 ; main program 29 30 entry: 31 00000050 B80000 MOV AX,0 32 00000053 8ED0 MOV SS,AX 33 00000055 BC007C MOV SP,0x7c00 34 00000058 8ED8 MOV DS,AX 35 36 ; reading disk 37 0000005A B82008 MOV AX,0x0820 38 0000005D 8EC0 MOV ES,AX 以下省略
一番左から順に「nasファイル内での行番号」「ファイル上でのアドレス」「マシンコード」「アセンブリ命令」という順になっています。
そして、先ほどのnasmコマンドで生成したimgファイルの中身をhexで表示してみます。imgファイルの中身をhexで表示する際は、以下のコマンドを使用。
xxd ipl.img | more
表示されたファイルの中身はこちら
00000000: eb4e 9048 4152 4942 4f54 4500 0201 0100 .N.HARIBOTE..... 00000010: 02e0 0040 0bf0 0900 1200 0200 0000 0000 ...@............ 00000020: 400b 0000 0000 29ff ffff ff48 4152 4942 @.....)....HARIB 00000030: 4f54 454f 5320 4641 5431 3220 2020 0000 OTEOS FAT12 .. 00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000050: b800 008e d0bc 007c 8ed8 b820 088e c0b5 .......|... .... 00000060: 00b6 00b1 02b4 02b0 01bb 0000 b200 cd13 ................ 00000070: 7203 f4eb fdbe 8a7c 8a04 83c6 013c 0074 r......|.....<.t 00000080: f1b4 0ebb 0f00 cd10 ebee 0a0a 6e6f 2064 ............no d 00000090: 7269 6e6b 2c20 6e6f 206c 6966 6521 0a00 rink, no life!.. 以下省略
先ほど示したlstファイルの「マシンコード」とimgファイルのhexを先頭から比較すると、対応していることがわかるかと思います。
毎回ではありませんが、たまにこういった確認をすると、自分で書いたコードがどのようイメージファイルになっているのかが確認できるのでいいかもしれません。
割り込み命令
今回出てくる割り込み命令では、「指定したディスクの場所を読み込む処理」と「ビデオモード設定」について。
ディスクの読み込み
- ディスクアクセスの方式はCHS*1
- 読み込む対象のドライブ番号を指定(FDDが1つしかない場合には0x00でおk)
- int 0x13で実行(戻り値はCF(キャリーフラグ)にセットされる。CF=1の場合、エラーが発生しており、AHにエラーコードが格納)
詳細については、以下のサイトを参照。
http://oswiki.osask.jp/?%28AT%29BIOS
もし、ディスクアクセスに関連したエラー処理をする場合には、
int 0x13
命令のあとに以下のような処理を追加すればいいのでしょう。
- CFにフラグが立っているかどうかを判断する
- フラグが立っている場合には、AHに記載されているエラーコードをみて、エラーコードに応じた処理を行う(ラベルを作って、エラーコードに応じてジャンプさせればいいのかな?)
ビデオモード設定
本の通り、ビデオモードは以下の通りまずはやってみました。
MOV AH, 0x00 MOV AL, 0x13 ; 8bitカラー INT 0x10
そうすると、本に書いてある通り真っ黒の画面がでてきました。カーソルもきえました。
ちょっと他のモードもになったので、本に書いてある以下のモードを試してみました。
1. MOV AL, 0x12 ; 4bitカラー 2. MOV AL, 0x03 ; 16色テキスト
そうすると以下のような結果になりました
- 1. の場合、AL=0x13と同じ結果(画面真っ黒、カーソルも消える)
- 2. の場合、画面は真っ黒になるものの、カーソルは点滅
(自分だけ違っていたら嫌なので、やってみた方コメください; )
イメージファイル内に記録されるファイル名とファイルの中身の位置
本の内容としては、haribote.sysのファイル名(haribote.nameと記載)とその中身については、イメージファイル上の以下の位置に記録されると書いてあります。
- ファイル名 :0x002600
- ファイルの中身:0x004200
んで、これらの位置というのがどのように決まっているのかがちょっと気になったので、調べてみました。
FAT12におけるHDD上の構成を図で書いてみました。
Boot Sectorはいいとして、File Allocation Tableについてもとばします。
「Root Directory」は、FAT12のファイルシステム上で管理されているファイルの目次(ファイル名一覧)という感じです。Windowsで言えばMFT(Master File Table)に該当する部分。
その下の「File Storage Space」は、ファイルの中身を記録している部分です。Root DirectoryとFile Storage Spaceという2つの領域があるということは、ファイル名などのメタ情報とファイルの中身が別々に管理されている(格納場所が1まとめになっていない)ということがわかるかと思います。
でそれぞれの開始位置が0x2600と0x4200になっているので、haribote.nameというファイル名はRoot Directoryの先頭に記録され、haribote.sysファイルの中身はFile Storage Spaceの先頭に記録した、ということになります。
で、今までのOS作成で、自分がどのような部分を書いていたのかを上の画像と見比べて確認してみます。
こんな感じで確認しておけば、自分が今どんな部分を作成しているのかわりと把握しやすくなるかなと。
上記の図の作成においては、以下のサイトを参考にいたしました。
The FAT File System
補足としてですが、クラスタのサイズについては、ファイルシステムによって違うので要注意ですね(NTFSは1cluster=8sector=512*8=4096が一般的です)。
ただ、今回本書では1cluster=1sectorで扱っていくっぽいので、あまりセクタとクラスタの違いは意識しなくて済みそうです(ファイルの論理サイズと物理サイズ、そしてファイルスラックの話があるのですが、フォレンジックの記事を書く際に出そうかと)。
セグメントレジスタ
今までよくわかっていなかったセグメントレジスタの使い方など。
時代背景的には、
- 昔はレジスタの記憶可能なサイズが16bit(64KB)しかなかった
- 容量の大きいメモリを積んでいる場合であったとしても、実質64KBしか使えない
- レジスタの使い方とか工夫して、もっとメモリつかえるようにできんかねー?
という流れでセグメントレジスタができたという流れでしょうか。
このセグメントレジスタと既存のレジスタを使ってメモリの番地を指定することで1MBまでメモリの番地を指定できるようになったと。
セグメントレジスタをつかってメモリの番地を指定する際、アセンブリではこのように記述します。
MOV ES, 0x0820 MOV BX, 0x0012 MOV AX, 0x4141 MOV [ES:BX],AX
この例だと、
- セグメントレジスタESに0x0820を格納
- BXに0x0012を格納
- AXに0x4141を格納
- ES×16+BXで格納先メモリアドレスの計算を行う。メモリアドレスは0x0820 × 16 + 0x0012 = 0x8212
- メモリアドレス0x8212にAXのデータを格納
セグメントレジスタを使ってメモリアドレスの指定を行う際は、「セグメントレジスタで大雑把に場所を決める」「レジスタを使って細かな場所を決める」と覚えておけば問題はないかと。
今のところの疑問点など
・割り込み命令の時にはなぜ特定のレジスタに引数を入れておくのか?
・今回、読み込みのリトライの部分とエラー表示の部分を書いたので、わざとエラー処理に飛ばす方法はあるのかな?
時間はかかりましたが、かなり濃い勉強ができました。
次は、3日目の残り部分をやっていきます。時間があれば4日目部分もまとめてやろうかと。