x86リアルモードでモニタプログラムを作ろうの第4回目です。今回はブート処理の2段階目、VBRからの起動になります。
前回、boot1.asmで使用するであろう数値群をあらかじめ用意したので、モニタプログラムを起動することだけなら簡単にできます。ちょっと重複しますが確認しておきますね。
①MBRからVBRのLBAを取り出して読み込み遷移する。
②BPBから必要な数値を得る。
③FAT開始位置を得る。
④DIR領域開始位置を得る
⑤DIR領域からファイルの開始LBAとサイズを得る。
⑥サイズとFATを比べながらファイルを読み込む
⑦ファイルに遷移する。
ざっくり言うとこういう流れになります。①②はboot0.asmでやってますので、boot1.asmは③~⑦をやることになります。盛りだくさんですね。
FATを利用して、となるとこうなるのですが、このシリーズではファイル位置は固定ですので(作り方がいつでも一緒なので同じ位置に作られる)単にモニタプログラムを起動するだけなら、モニタの開始位置とサイズがあれば実現できます。モニタが複数クラスタにまたがったとしても、連続領域に配置されることは確定なので、FATによるクラスタチェーンも追う必要はなかったりします。
要するに突き詰めれば⑤だけあればいいことになります。
モニタプログラムは『MONITOR .BIN』という名前なので、ダンプして調べてみるとこんな感じ。

VBR領域の後、FAT1、FAT2の領域があって、その後ろにDIR領域があります。『MONITOR .BIN』はDIR領域の2番目のエントリにあって、開始クラスタは0x0002、サイズは0x000008c5となっています。(注:リトルエンディアン)
開始クラスタが2番目ということはセクタ数は0セクタ(先頭2クラスタは予約)。データ開始LBAの最初に『MONITOR BIN』ですね。
データ開始LBAは前回保存したものがあるのでそれを使うとLBAは0x08a4、バイト数で0x114800になります。
これらを踏まえてboot1.asmのプロトタイプとmonitor.asmのスタブを作ってみます。
boot1.binプロトタイプ
boot1.asmプロトタイプです。前回保存した各種数値を取り出して使えるようにしつつ、LBA0x8a4にある『MONITOR .BIN』をサイズ0x08c5Byte(5セクタ)で読込、遷移させます。
こんな感じ。
[BITS 16]
org 0x8000
%macro PUTC 1
push ax
mov al, %1
out 0xE9, al
pop ax
%endmacro
%include 'hifax.asm'
jmp start
nop
OEMLabel db 'MSDOS5.0' ; 8 bytes
BytesPerSec dw 0x0200
SecPerClus db 4 ; 10MB パーティションでは4が妥当
RsvdSecCnt dw 0x0004 ; mformatのデフォルト
NumFATs db 2
RootEntCnt dw 0x0200 ; HDDでは512が一般的
TotSec16 dw 0xf800 ; パーティションサイズ (9MB)
Media db 0xf8 ; HDD
FATSz16 dw 0x0040 ; FAT16サイズ(要計算)114?
SecPerTrk dw 0x0020
NumHeads dw 4
HiddSec dd 0 ; パーティション開始LBA
TotSec32 dd 0
; ======================================================
start:
PUTC 'B'
PUTC '1'
PUTC ':'
cli
mov ax, 0x7000
mov ss, ax
mov sp, 0xfff0
sti
mov ax, _s_msg_start
call ps
xor ax, ax
mov es, ax
; -----------------------------------
; Bootドライブ
; -----------------------------------
mov al, es:[BOOT_DRV_OFF]
mov [BOOT_DRIVE], al
; -----------------------------------
; VBR_LBA = start_lba
; -----------------------------------
mov ax, es:[VBR_START_LBA]
mov word [VBR_LBA], ax
; -----------------------------------
; SEC_PER_CLUS
; -----------------------------------
mov al, es:[SEC_PER_CLUS_OFF]
mov byte [SEC_PER_CLUS], al
; -----------------------------------
; ROOT_ENT_CNT
; -----------------------------------
mov ax, es:[ROOT_ENT_CNT_OFF]
mov [ROOT_ENT_CNT], ax
; -----------------------------------
; FAT_LBA
; -----------------------------------
mov ax, es:[FAT_START_LBA]
mov [FAT_LBA], ax
; -----------------------------------
; DATA_LBA
; -----------------------------------
mov ax, es:[DATA_START_LBA]
mov word [DATA_LBA], ax
; -----------------------------------
; DIR_LBA
; -----------------------------------
mov ax, es:[DIR_START_LBA]
mov [DIR_LBA], ax
; -----------------------------------
; CLUS_SIZ
; -----------------------------------
mov ax, es:[CLUS_SIZ_OFF]
mov [CLUS_SIZ], ax
; -----------------------------------
; BYTE_PER_SEC
; -----------------------------------
mov ax, es:[BYTE_PER_SEC_OFF]
mov [BYTE_PER_SEC], ax
; FILEのLBAを算出
mov ax, 0
mov bx, [DATA_LBA] ; データ開始LBA
add ax, bx
mov [FILE_LBA], ax ; ファイルのLBA
; ファイルサイズ・セクタ変換
; mov ax, es:[di+28] ; File size low word
mov ax, 0x08c5
add ax, 511
shr ax, 9
mov [FILE_SECTOR], ax ; ファイルのセクタ数
mov si, dap
mov ax, [FILE_SECTOR]
mov word [si + DAP.NumBlocks], ax ; sectors
mov ax, MON_OFF
mov word [si + DAP.BufferOff], ax ; buffer offset
mov word [si + DAP.BufferSeg], MON_SEG ; buffer segment
mov ax, [FILE_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 ax, MON_SEG
; mov es, ax
; mov si, MON_OFF
; mov cx, 0x20
; call dump_mem
jmp MON_SEG:MON_OFF
jmp hlt
disk_error:
mov al, 'X'
out 0xE9, al
mov al, ah
call phd1
jmp hlt
done:
hlt:
hlt
jmp hlt
ps:
push ax
mov si, ax
.loop:
lodsb
test al, al
jz .exit
mov ah, 0x0e
int 0x10
jmp .loop
.exit:
pop ax
ret
phd1:
push ax
and al, 0x0f
cmp al, 0x09
ja .gt_9
add al, 0x30
jmp .cnv_end
.gt_9:
add al, 0x37
.cnv_end:
out 0xE9, al
; mov bl, al
pop ax
ret
phd2:
push ax
shr al, 4
call phd1
pop ax
push ax
call phd1
pop ax
ret
phd4:
push ax
mov al, ah
call phd2
pop ax
; push ax
call phd2
; pop ax
ret
dump_mem:
push si
push cx
push ax
.next:
test cx, cx
jz .done
mov al, [es:si]
call phd2
mov al, ' '
out 0xE9, al
inc si
dec cx
jmp .next
.done:
pop ax
pop cx
pop si
ret
_s_filename db 'MONITOR BIN', 0x00
_s_msg_start db 'B1:Start', 0x0d, 0x0a, 0x00
BOOT_DRIVE db 0
SEC_PER_CLUS db 0
ROOT_ENT_CNT dw 0
VBR_LBA dw 0
DATA_LBA dw 0
DIR_LBA dw 0
FILE_SECTOR dw 0
FILE_LBA dw 0
FAT_LBA dw 0
CLUS_SIZ dw 0
BYTE_PER_SEC dw 0
align 16
dap: ;
db 0x10, 0x00 ; size / reserved
dw 0 ; sectors
dw 0x0000 ; buffer offset
dw 0x0 ; buffer segment
dq 0x0 ; LBA (low=2048, high=0)
monitor.asmスタブ
monitor.asmのスタブは遷移されたことがわかるだけでいいので、メッセージを出すだけにします。こんな感じでしょうか。
[BITS 16]
[SECTION .text]
jmp start
%include 'hifax.asm'
; ---------------- デバッグ出力 ----------------
%macro PUTC 1
push ax
mov al,%1
out 0xE9,al
pop ax
%endmacro
global start
; ---------------- 本体 ----------------
start:
PUTC 'M'
PUTC ':'
cli
mov ax, STACK_SEG
mov ss, ax
mov sp, 0xFF00
sti
; mov ax, cs
mov ax, MON_SEG
mov ds, ax
mov es, ax
mov ax, _s_msg_monitor
call ps
hlt:
hlt
jmp hlt
%include 'common.asm'
_s_msg_monitor db 'Hello from Monitor', 0x0d, 0x0a, 0x00
times 1280-($-$$) db 0
実行結果
実行するとこんな感じになります。

Hello from Monitorが表示されてますね。正しく読み込まれて遷移されたことがわかります。これで一つのゴールが明確になりました。monitorを読み込んで遷移する。このゴールを崩さないように、ディレクトリエントリの検索、fatチェーンの追跡を行うことになります。
さて、そろそろいい時間なので、今日はここまでです。お疲れさまでした。
細切れにして情報量少なくなってごめんなさいです。
でもね、この程度でも書くのとってもしんどいんですよ?
ともあれ、また次回お会いしましょう。
《2025/12/14 4:59:42》