前にx86系のPCをブートさせて遊んでみる『ブートで遊ぼっ!』というのを7回ほど書いてみました。テキストは『OSを書く:初歩から一歩ずつ』で、練習問題も最後までやりました。もう思い残すところはない!と言いたいところなんですが、書き足りないところとか、遊び足りないところもあるので、少し続けてみようと思います。
環境はAMD64+Windows10+VMware Workstation 17 Player+Debian 12.4で変わりません。
ディスク読み込み
前回シリーズではさらっと済ませたような気がするんですが、ディスク読み込みのお話です。
呼び出しパラメータ
パラメータはこうだそうです。
AH : 0x02 : セクタ読み込みファンクション
AL : 読み込むセクタ数
CH : シリンダの下位8ビット(トラックは合計10ビットで0始まり)
CL : シリンダの上位2ビット(トラックは合計10ビットで0始まり)
読み込み開始セクタ (セクタは合計6ビットで1始まり)
DH : ヘッド番号(ヘッドは0始まり)
DL : セクタを読み込むドライブ番号
ES:BX : 読み込んだセクタを格納するバッファの先頭アドレス
最初、これを見た時には頭がくらくらしました。「なにをどうすればいいの?」って。
調べてみたらさらに頭が痛くなりました。
シリンダって何?ヘッドって何?ファイル構造を知らないとダメ?FAT?太ってないよ?
結局欲しい情報は
・読み込み対象装置
・読み込むセクタ(位置)
・セクタ数
らしいということが何となくわかりました。装置はHDD、今回の場合はアセンブルしたファイルそのもので0x80を指定すればいいそうです。セクタはMBRが1セクタ使ってるので、その先である2以降。セクタ数については作ったファイルが2048Byteなのでトータル4セクタ、先にも書いたけど1セクタをMBRに使っているので残り3セクタかな。
ここでシリンダとヘッダについてなのですが、HDDで最初の1セクタ読んだらヘッダ、シリンダが変更される、なんてことはまず考えられない。今回トータルでも4セクタしかないので、最後まで読んでもヘッダとシリンダが変わることはないだろう。という予想のもと、両方0で実装しました。
読み込みアドレス
そもそもブートローダーが512Byteとかケチ臭いことを言わず、ザクっと全部読み込んでくれたらBoot時点で苦労はしないのです(暴論)
nasm君は512Byteを超えようが知らん顔してバイナリ作ってるし。
このあたり、詳しい解説とかは他の方に任せます。いろんな方がいろんな書き方で解説してますし。
で、この時点で何が問題かというと、こんな感じかな
nasmでアセンブルした時、とある命令は512番地(正確には0x7c00+512番地)に居る前提で動こうとするんだけど、泣き別れで読み込まれてしまうと、前提が違うので動かない、という状況です。
こうならないために、セグメントレジスタを調整とかするんですが、ヘッダ、シリンダのお話や、ファイルシステムのお話、セグメントのお話なんかがいっぺんに来るので、なんだか難しい解り難いものになっちゃうわけです。
で、このシリーズは「遊びよ。遊びっ♪」と割り切っているので、面倒なことはとりあえず置いといて、動くようにしました。
ハードディスク側は、1セクタの512byteの先に、続きが書かれているのはまず間違いないです。PC側は512番地の後に続きがあれば動く。
ならば、2セクタから先を512番地に読み込めばいいじゃない。
そういうことなのでした。
ディスク書き込み
読み込みのお話が出たので、書き込みのお話もしましょう。
呼び出しパラメータ
書き込み操作のパラメータは以下です。
AH : 0x03 : セクタ書き込みファンクション
AL : 書き込むセクタ数
CH : シリンダの下位8ビット(トラックは合計10ビットで0始まり)
CL : シリンダの上位2ビット(トラックは合計10ビットで0始まり)
書き込み開始セクタ (セクタは合計6ビットで1始まり)
DH : ヘッド番号(ヘッドは0始まり)
DL : セクタを書き込むドライブ番号
ES:BX : 書き込むセクタを格納したバッファの先頭アドレス
何のことはない、読み込みとほぼ一緒です。ならば同じように使えるんじゃないかな?
メモリに何かを書きこんで、それをディスクに書く、で行けるんじゃないかな?
; bios.asm mov ax, 0x07c0 mov ds, ax mov es, ax mov ah, 0x0 mov al, 0x3 int 0x10 ; set data to top of 2nd sector mov si, _change_msg2 mov bx, 0x0200 loop2: lodsb or al, al je loop2_end mov [bx], al add bx, 1 jmp loop2 loop2_end: ; set data to top of 3rd sector mov si, _change_msg3 mov bx, 0x0400 loop3: lodsb or al, al je loop3_end mov [bx], al add bx, 1 jmp loop3 loop3_end: ; set data to top of 4th sector mov si, _change_msg4 mov bx, 0x0600 loop4: lodsb or al, al je loop4_end mov [bx], al add bx, 1 jmp loop4 loop4_end: ; write disk mov bx, 0x200 ; Destination address to write mov ah, 0x03 ; Write Sectors To Drive mov dl, 0x80 ; Drive mov al, 0x03 ; Sectors To Write Count ; mov ch, 0x00 ; Cylinder mov cl, 0x02 ; Sector(starts from 1, not 0) ; set 2. becouse not need MBR mov dh, 0x00 ; Head int 0x13 ; Execute disk write jmp hang ; end of proccess ; hang: jmp hang _change_msg2: db 'All out!', 0x0d, 0x0a, 0x00 _change_msg3: db 'Pull the throttie!', 0x0d, 0x0a, 0x00 _change_msg4: db "All right Let's Go!", 0x0d, 0x0a, 0x00 times 510-($-$$) db 0 db 0x55 db 0xAA _second_sector: db 'Forth Gate Open!', 0x0d, 0x0a, 0x00 times 0x0400 -($-$$) db 0 _third_sector: db 'Quickly!', 0x0d, 0x0a, 0x00 times 0x0600-($-$$) db 0 _fourth_sector: db 'Forth Gate Ooen!', 0x0d, 0x0a, 0x00 times 0x0800 -($-$$) db 0
PlayWithBIOS.1
今までみたいにmakeとだけ打つと実行までされてしまいます。今回は実行前のファイルと実行後のファイルを比べたいのでこういう風に。
make clean
make bios.bin
これで実行しないで止まるので、中を覗きます。
実行後に値が変わっていれば書き込み成功です。
はい。確かに2セクタ3セクタ4セクタが書き換わっています。
さて、今回はこれでお終いです。次のネタどうしよう…。
《2024/5/28 18:00:20》