x86リアルモードで動くモニタプログラムのHyfaxを作るお話の5回目です。前回ではBOOT1.BINが起動するところまでを作成し確認しました。今回はBOOT1.BINにディレクトリ検索機能とFATチェーン追跡機能を追加してBOOT1.BINを完成させたいのですが、果たしてうまく事が運ぶかどうか。不安でいっぱいです。
BOOT1.BINの機能
BOOT1.BINはVBRに置かれ、MBRにあるboot0.binからメモリに読み込まれ実行されます。そしてFAT16ファイルシステム内にあるMONITOR.BINを検索して読み込み、さらにこれを実行します。MBRは完全固定で、VBRは半分ほど固定でディスク内に配置されるので、読み込む際に考慮する点は多くなく、というかとても少なく、ある意味LBA固定で作成することもできます。MONITOR.BINもLBAを固定で済ますこともできないわけじゃないです。というか、前回それをやりました。が、ご存じのようにファイル内にはいくつもファイルを置くことができますし、並び順も固定ではありません。なのでFAT16ファイルシステムを使うのであれば(言い換えれば使うときに便利にするためには)、手順を踏まなければいけません。
ファイルシステムを使うからどこにMONITOR.BINを置いてもいいかというと、そうはいきません。ブートプログラムはサイズ制限が厳しいのでMONITOR.BINはルートディレクトリに置くことになります。BOOT1.BINはルートディレクトリ管理領域からMONITOR.BINの開始クラスタ番号とサイズを読み取り、メモリに読み込んで実行することになります。
ディスクイメージダンプ
ここでもう一度ディスクイメージのダンプを見てみます。

ルートディレクトリの管理領域は0x110800Byteから始まります。数字の羅列でなにがなにやらなんですが、その右側にあるASCII表示を見ると、『MONITOR BIN』『APP BIN』『APP1 BIN』……とどこかで見たような名前があります。そうファイル名です。ディレクトリエントリは1エントリ=32Byteでファイル名、属性、開始クラスタ番号、サイズ等を持っています。最大エントリ数であるROOT_ENT_CNT(0x0200)個並んでいる中から、お目当てのファイル名を探し出して開始クラスタ番号、サイズを取り出し使用するわけです。
ディレクトリエントリの構造はこんな感じ。この例だと『MONITOR BIN』の開始クラスタは0x0002、サイズは0x000008c5ですね。(注:リトルエンディアン)あ、FAT16前提なので上位クラスタ番号は使いません。
struc DIR_ENTRY
.Name resb 8 ; 00h ファイル名
.Ext resb 3 ; 08h 拡張子
.Attr resb 1 ; 0Bh 属性
.NTRes resb 1 ; 0Ch 予約 (NT)
.CrtTime10 resb 1 ; 0Dh 作成時刻10ms
.CrtTime resw 1 ; 0Eh 作成時刻
.CrtDate resw 1 ; 10h 作成日
.LstAccDate resw 1 ; 12h アクセス日
.FstClusHI resw 1 ; 14h 上位クラスタ番号 (FAT32)
.WrtTime resw 1 ; 16h 更新時刻
.WrtDate resw 1 ; 18h 更新日
.FstClusLO resw 1 ; 1Ah 下位クラスタ番号
.FileSize resd 1 ; 1Ch ファイルサイズ
endstruc
実装
前々回DIR_START_LBA、DATA_START_LBA、ROOT_ENT_CNTを保存しました。
①DIR_START_LBAから32Byteずつ取り出し
②先頭の11文字を照合して
③目当てのファイルだったら
④下位クラスタ番号とサイズを取り出し
⑤クラスタ番号からLBA値を算出し
⑥DATA_START_LBAから始まってその値にあたる部分からファイルサイズだけ読み込む
という実装になります。うむぅ。FATのクラスタチェーンの話は次回ですね。今回は連続して置かれているだろうことを信じてサイズをそのまま使いましょう。(今回の例ではクラスタが連続していないということはあり得ないんですけどね。)
検索処理をコードにするとこんな感じになります。
前回のコードのFILEのLBAを算出する部分とファイルサイズ・セクタ変換する部分をまるっと置き換えると動くはずです。
; --- 転送先を設定 ---
mov bx, BUF_SEG
mov es, bx
mov bx, BUF_OFF
; -----------------------------------
; Dir EntからTarget Fileのエントリを探す
; -----------------------------------
mov ax, [DIR_LBA]
mov word [DIR_ENT_SEC_CNT], 32
mov si, _s_filename
dir_ent_sec_loop:
; -----------------------------------
; Dir Entを読み込む
; -----------------------------------
; Dir Entを読み込むための設定
mov si, dap
mov word [si + DAP.NumBlocks], ENT_SECTORS ; sectors
mov word [si + DAP.BufferOff], BUF_OFF ; buffer offset
mov word [si + DAP.BufferSeg], BUF_SEG ; buffer segment
mov ax, [DIR_LBA]
mov word [si + DAP.LBA_Low], ax ; LBA low Low
mov dword [si + DAP.LBA_High], 0 ; LBA high
mov dl, [BOOT_DRIVE]
mov ah, 0x42
int 0x13
jc disk_error
mov di, BUF_OFF
mov cx, 16
find_entry:
mov al, es:[di]
cmp al, 0x00
je not_found
cmp al, 0xE5
je next_entry
mov al, es:[di+11]
test al, 0x18 ; Volume or Dir
jnz next_entry
; --- compare 11 bytes "MONITOR BIN" ---
push di
mov si, _s_filename
mov bx, 11
cmp_loop:
mov al, es:[di]
mov ah, [si]
cmp al, ah
jne cmp_ng
inc di
inc si
dec bx
jnz cmp_loop
jmp find_dir_ent
cmp_ng:
pop di
next_entry:
add di, 32
loop find_entry
next_sec:
mov ax, [DIR_LBA]
inc ax
mov [DIR_LBA], ax
mov ax, [DIR_ENT_SEC_CNT]
dec ax
mov [DIR_ENT_SEC_CNT], ax
cmp ax, 0
jnz dir_ent_sec_loop
not_found:
mov al, 'N'
out 0xE9, al
jmp done
disk_error:
mov al, 'X'
out 0xE9, al
mov al, ah
call phd1
jmp hlt
find_dir_ent:
; --- 一致 ---
; クラスタ・LBA変換
pop di
mov bx, es:[di+26] ; FirstCluster low
mov ax, es:[di+20] ; FirstCluster high
mov [TARGET_CLUS], bx ; ファイルのあるクラスタ
sub bx, 2
mov al, [SEC_PER_CLUS]
mov ah, 0
mul bx
mov bx, ax
mov ax, [DATA_LBA] ; データ開始LBA
add ax, bx
mov [FILE_LBA], ax ; ファイルのLBA
; ファイルサイズ・セクタ変換
mov ax, es:[di+28] ; File size low word
add ax, 511
shr ax, 9
mov [FILE_SECTOR], ax ; ファイルのセクタ数
これを見て『なんをしてるかわかんな~い』と思ったあなた。悲観する必要はありません。書いたわたしが今そう思ってます。書いた時点では理解していても時間がたつと忘れます。ホント。コメント大事です。きっぱり。
実行結果
一応実行結果もあげておきますね。

まぁ、前回と変わらないんですけどねw
とはいえゴールが明確に示され、達成がはっきりわかるのはいいことです。うんうん。
さて、今回はここまでにします。次回こそラスボス感のある掛け算七の段こと、FATクラスタチェーンの追跡を実装しようと思います。
お疲れさまでした。
《2025/12/14 17:35:29》