Linuxで書くOS自作入門 3日目(前半)
「30日でできる!OS自作入門」の3日目
環境はUbuntu, アセンブラはNASMで開発中
長くなりそうなのでharib00d, 10シリンダ読み込むプログラムまでをここに書きます.
書き換えた部分の概要
BPB(BIOS Parameter Block)の後にディスクをメモリに読み込むプログラムを書く.ディスク読み込みはINT
命令によって実行される.
- INT命令は割り込み(Interrupt)処理の呼び出しの命令であり,BIOSの機能を呼び出す.
- そもそもBIOSはBasic Input/Output Systemの略で,ハードウェアとの最も低レベルな入出力を行うプログラム. コンピュータのモデルごとに設計され,マザーボード上のメモリに埋め込まれている.
セクタごとにディスクを読み込んで行く.メモリのオフセットにはヘッド,シリンダ,セクタのサイズを考える必要があるためメモ.
(下記はフロッピーディスクの場合)
- 1ディスク = 2ヘッド = 160シリンダ = 2,880セクタ = 1,474,560バイト
- 1ヘッド = 80シリンダ = 1,440セクタ = 737,280バイト
- 1シリンダ = 18セクタ = 9,216バイト
- 1セクタ = 512バイト
読み込みループ部分
CYLS EQU 10 ; 読み込むシリンダ数
EQUは定数を定義できる.アセンブラが後で数字に書き換えてくれる.
BPB部分は前回と同じなので省略.
entry: MOV AX,0 ; レジスタ初期化 MOV SS,AX MOV SP,0x7c00 ; ブートセクタは0x00007c00に読み込まれる MOV DS,AX ; ディスクを読む MOV AX,0x0820 MOV ES,AX ; セグメントレジスタ(ES:BX)なのでメモリ上では0x8200 MOV CH,0 ; シリンダ番号 MOV DH,0 ; ヘッド番号 MOV CL,2 ; セクタ番号 readloop: MOV SI,0 ; エラー回数リセット retry: MOV AH,0x02 ; AH=0x02 : ディスク読み込み MOV AL,1 ; 処理セクタ数 MOV BX,0 ; バッファアドレス MOV DL,0x00 ; ドライブ番号 0->Aドライブ INT 0x13 ; ディスクBIOS呼び出し JNC next ; エラーがおきなければnextへ ADD SI,1 ; SI(エラー回数)に1を足す CMP SI,5 ; SIと5を比較 JAE error ; エラー回数5回以上 だったらerrorへ MOV AH,0x00 MOV DL,0x00 ; ドライブ番号 0->Aドライブ INT 0x13 ; ドライブのリセット JMP retry next: MOV AX,ES ; アドレスを0x200進める 0x200は1セクタ分 ADD AX,0x0020 ; セグメントレジスタ(ES:BX)なので0x200->0x20 MOV ES,AX ; ADD ES,0x020 という命令がないのでこうしている ADD CL,1 ; CLに1を足す CMP CL,18 ; CLと18を比較 JBE readloop ; CL(セクタ番号) <= 18 だったらreadloopへ MOV CL,1 ; CLを1に戻す ADD DH,1 CMP DH,2 JB readloop ; DH(ヘッド番号) < 2 だったらreadloopへ MOV DH,0 ADD CH,1 CMP CH,CYLS JB readloop ; CH(シリンダ番号) < CYLS だったらreadloopへ JMP success
INT命令を呼び出す前に,各レジスタの値を設定する必要がある.(AT)BIOS - os-wiki
ヘッド,シリンダ,セクタの値を指定することでディスクの読み込み部分は確定する.
メモリ上への読み込み部分はセグメントレジスタ[ES:BX]を指定する.実際のメモリアドレスはES * 16 + BX
となるので,ESに代入するときは16進数だと下一桁の0が無くなるので注意する.
読み込みのエラーが起こる場合があるので,エラー回数をレジスタSIでカウントし,5回までならやり直すように設定した.(QEMUでエミュレートするだけなら特に意味はない)
後は,セクタ,シリンダ,ヘッドの順に,比較命令とジャンプ命令の組み合わせによって繰り返しをプログラムする.
終了処理
fin: HLT ; 何かあるまでCPUを停止させる JMP fin ; 無限ループ
終了時のプログラムは前回と同じ.
本誌では読み込み成功時には何も表示されないプログラムとなっているが,本当に読み込めているか不安になってしまうため,オリジナルで成功時のメッセージを表示するように書き換えた.
success: MOV SI,msg2 JMP putloop error: MOV SI,msg putloop: MOV AL,[SI] ADD SI,1 ; SIに1を足す CMP AL,0 JE fin MOV AH,0x0e ; 一文字表示ファンクション MOV BX,15 ; カラーコード INT 0x10 ; ビデオBIOS呼び出し JMP putloop msg: DB 0x0a, 0x0a ; 改行を2つ DB "load error" DB 0x0a ; 改行 DB 0 msg2: DB 0x0a, 0x0a ; 改行を2つ DB "load success" DB 0x0a ; 改行 DB 0 TIMES 0x7dfe-0x7c00-($-$$) DB 0 ; 0x7dfeまでを0x00で埋める命令 DB 0x55, 0xaa ;ブートセクタが有効であることを示す2バイト
実行
従来のMakefileで実行すると失敗した.
QEMUのオプションでフロッピーディスクのブートを明示してないことが原因だった.原因の特定にかなりの時間を消費した.
qemu-system-i386 -fda haribote.img # -fdaオプションをつける
今度はちゃんと成功表示が出た.