BIOSと戯れてみる

前にx86系のPCをブートさせて遊んでみる『ブートで遊ぼっ!』というのを7回ほど書いてみました。テキストは『OSを書く:初歩から一歩ずつ』で、練習問題も最後までやりました。もう思い残すところはない!と言いたいところなんですが、書き足りないところとか、遊び足りないところもあるので、少し続けてみようと思います。
環境はAMD64+Windows10+VMware Workstation 17 Player+Debian 12.4で変わりません。

ディスク読み込み

前回シリーズではさらっと済ませたような気がするんですが、ディスク読み込みのお話です。

呼び出しパラメータ

最初、これを見た時には頭がくらくらしました。「なにをどうすればいいの?」って。
調べてみたらさらに頭が痛くなりました。
シリンダって何?ヘッドって何?ファイル構造を知らないとダメ?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番地に読み込めばいいじゃない。
そういうことなのでした。

ディスク書き込み

読み込みのお話が出たので、書き込みのお話もしましょう。

呼び出しパラメータ

書き込み操作のパラメータは以下です。

何のことはない、読み込みとほぼ一緒です。ならば同じように使えるんじゃないかな?
メモリに何かを書きこんで、それをディスクに書く、で行けるんじゃないかな?

; 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とだけ打つと実行までされてしまいます。今回は実行前のファイルと実行後のファイルを比べたいのでこういう風に。

これで実行しないで止まるので、中を覗きます。


実行後に値が変わっていれば書き込み成功です。

はい。確かに2セクタ3セクタ4セクタが書き換わっています。

さて、今回はこれでお終いです。次のネタどうしよう…。

《2024/5/28 18:00:20》

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です