前回boot0.asmを作ったわけですが、次のboot1.asmを作るにあたって用意しておくと多少楽になって整理がしやすい数値群があります。今回はboot0.asm側であらかじめそういった数値を用意しておこうと思います。
用意するのは
BOOT_DRIVE
VBR_START_LBA
ROOT_ENT_CNT
FAT_START_LBA
DIR_START_LBA
CLUS_SIZ
DATA_START_LBA
です。
それぞれ少し説明しますね。
BOOT_DRIVE
Bootされたドライブの値です。boot0.asm実行時にdlレジスタに渡されてきます。boot0.asmでしか拾えない値なので、保存して使いまわします。
VBR_START_LBA
文字通りVBRが開始されるLBAです。これはMBRのパーテションテーブルの中にしかないので、これまた保存して使いまわします。
ROOT_ENT_CNT
ルートディレクトリの中にいくつエントリが入るかを示します。FAT16ではROOTディレクトリがサイズ固定なので、この数値を見て最大値を算出します。BPBと呼ばれる領域に設定されています。
FAT_START_LBA
文字通りFATが解されるLBAです。計算で出します。VBR開始後、リザーブされた領域を挟んで、そのあとからFATが始まります。
FAT_START_LBA = VBR_START_LBA + BPB_FAT16.RsvdSecCnt
となります。
余談ですが、MBRはMAX512Byte、パーテションテーブルを除くと446Byte「しか」取れません。必達です。ですがVBRの方はVBRの開始からFATの開始までの領域を使うことができます。VBR_START_LBA + BPB_FAT16.RsvdSecCnt分だけ余裕があるわけです。
DIR_START_LBA
ディレクトリ領域が開始されるLBAです。FATの後に位置し、計算で出します。FATは2個あるので、FAT開始LBAのあと、FAT領域を2つ分追加した後がこの値になります。
DIR_START_LBA = FAT_START_LBA + BPB_FAT16.FATSz16 * BPB_FAT16.NumFATs
CLUS_SIZ
クラスタの総バイト数です。標準では4セクタ=1クラスタ、512Byte=1セクタなので0x800Byteになるはずです。
CLUS_SIZ = BPB_FAT16.SecPerClus * BPB_FAT16.BytesPerSec
DATA_START_LBA
DIR_START_LBAにディレクトリエントリの分の領域を要した後がデータ開始LBAになります。
DATA_START_LBA = ROOT_ENT_CNT * 32 / BPB_FAT16.BytesPerSec
boot0.asmの修正
boot0.asmにデータ保存用のコードを入れていきます。主処理の間にチマチマ出てきてうっとうしいし見通しが悪くなるんですが、まぁ、しかたがないですね。で、修正したソースがこれ。
; ============================================================
; boot0.asm -- Minimal MBR loader (for HDD, loads boot1 at LBA=2048)
; 制約: FAT16フォーマットのみ対応
; ============================================================
BITS 16
ORG 0x7C00
MBR_BASE equ 0x7C00
%include 'hifax.asm'
%macro PUTC 1
push ax
mov al, %1
out 0xE9, al
pop ax
%endmacro
start:
PUTC 'B'
PUTC '0'
PUTC ':'
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, MBR_BASE
sti
mov [BOOT_DRIVE], dl
; --- 表示(任意) ---
mov ax, msg_boot0
call ps
.load:
; -----------------------------------
; boot driveの保存
; -----------------------------------
xor ax, ax
mov es, ax
mov al, [BOOT_DRIVE]
mov es:[BOOT_DRV_OFF], al
; -----------------------------------
; boot_indicatorを4件分検査
; -----------------------------------
mov si, MBR_BASE ; MBR先頭
add si, PART_TBL_OFF ; パーティションテーブル開始位置
mov cx, 4 ; 4エントリ
.show_loop: ; ブート可能な最初のエントリを探す
mov al, [si] ; boot_indicator
cmp al, 0x80 ; ブート可能か
je .boot_ok
add si, PART_ENT_SZ ; 次のエントリ
loop .show_loop
mov ax, msg_fail2 ; ブート可能パーティションなし
call ps
jmp .hlt
.boot_ok:
; -----------------------------------
; VBR_START_LBAの保存
; -----------------------------------
mov ax, [si + PART_ENTRY.lba_start] ; start_lba VBR開始LBA
mov es:[VBR_START_LBA], ax
; --- Disk address packet (for INT 13h AH=42h) ---
; VBRを読み込む。コード節約のため最低限の設定しかしていない。
push cs
pop ds
mov si, dap
mov ax, es:[VBR_START_LBA]
mov [si + DAP.LBA_Low ], ax ; VBRのLBAを設定
mov word [si + DAP.LBA_High ], 0
; mov dl, [BOOT_DRIVE] ; HDD (0x00=FDD)
mov dl, es:[BOOT_DRV_OFF] ; HDD (0x00=FDD)
mov ah, 0x42 ; Extended Read
int 0x13
jc .fail
pop es
pop ds
mov ax, BOOT1_SEG
mov es, ax
xor ax, ax
mov ds, ax
mov si, BOOT1_OFF
; -----------------------------------
; ROOT_ENT_CNTの保存
; -----------------------------------
mov ax, es:[si + BPB_FAT16.RootEntCnt]
mov ds:[ROOT_ENT_CNT_OFF], ax
; -----------------------------------
; FAT開始LBAの保存
; -----------------------------------
mov si, BOOT1_OFF
mov ax, es:[si + BPB_FAT16.RsvdSecCnt]
add ax, ds:[VBR_START_LBA]
mov ds:[FAT_START_LBA], ax
; -----------------------------------
; DATA開始LBAの保存
; -----------------------------------
xor bx, bx
mov bl, es:[si + BPB_FAT16.NumFATs]
mov ax, es:[si + BPB_FAT16.FATSz16]
mov bh, 0
mul bx
add ax, ds:[FAT_START_LBA]
mov ds:[DIR_START_LBA], ax
; -----------------------------------
; SEC_PER_CLUS, CLUS_SIZの保存
; -----------------------------------
mov al, es:[si + BPB_FAT16.SecPerClus]
mov ds:[SEC_PER_CLUS_OFF], al
mov ah, 0
mov bx, es:[si + BPB_FAT16.BytesPerSec]
mul bx
mov ds:[CLUS_SIZ], ax
mov ax, ds:[ROOT_ENT_CNT_OFF]
mov bx, 32
mul bx
mov bx, es:[si + BPB_FAT16.BytesPerSec]
div bx
add ax, ds:[DIR_START_LBA]
; shr ax, 9
mov ds:[DATA_START_LBA], ax
; -----------------------------------
; boot1へ遷移
; -----------------------------------
jmp BOOT1_SEG:BOOT1_OFF ; jump to boot1 (VBR)
; push BOOT1_SEG
; push BOOT1_OFF
; retf
.fail:
mov al, ah
call phd1
mov ax, msg_fail
call ps
.hlt:
hlt
jmp .hlt
ps:
mov si, ax
.print:
lodsb
or al, al
jz .exit
mov ah, 0x0E
int 0x10
jmp .print
.exit:
ret
phd1:
push ax
mov ah, al
shr al, 4
call .n
mov al, ah
and al, 0x0F
call .n
pop ax
ret
.n:
cmp al, 9
jbe .d
add al, 'A' - 10
jmp .o
.d:
add al, '0'
.o:
push ax
out 0xE9, al
pop ax
ret
phd2:
push ax
mov al, ah
call phd1
pop ax
push ax
call phd1
pop ax
ret
; ds:ax -> es:bx , cx Byte
memcpy:
push ax
push bx
push cx
mov si, ax
mov di, bx
.loop:
mov al, ds:[si]
mov es:[di], al
inc si
inc di
loop .loop
pop cx
pop bx
pop ax
ret
BOOT_DRIVE db 0x0
; --- メッセージ ---
; msg_boot0 db "B0: loading Boot1...", 0x0D,0x0A,0
; msg_fail db "B0: Disk error!", 0x0D,0x0A,0
; msg_fail2 db "B0: Not Bootable", 0x0D,0x0A,0
msg_boot0 db "B0:loading Boot1...", 0x0D,0x0A,0
msg_fail db " Disk error!", 0x0D,0x0A,0
msg_fail2 db " Not Bootable", 0x0D,0x0A,0
; --- DAP構造体 (16バイト) ---
align 16
dap:
db 0x10 ; size of packet (16 bytes)
db 0x00 ; reserved
dw 4 ; sectors to read (1)
dw BOOT1_OFF ; offset to load
dw BOOT1_SEG ; segment to load
dq 0 ; LBA of VBR
times 446-($-$$) db 0 ; boot code: 446 bytes total
; --- パーティションテーブル + シグネチャ ---
; partition entry 1 (bootable, FAT16, start=2048, length=18432)
; PARTITION_ENTRY:
; boot_indicator db 0x80
; start_head db 0x01
; start_sector db 0x01
; start_cylinder db 0x00
; partition_type db 0x06
; end_head db 0xFE
; end_sector db 0x3F
; end_cylinder db 0x0F
; start_lba_l dw 0x0800
; start_lba_h dw 0x0000
; total_sectors_l dw 0x4800
; total_sectors_h dw 0x0000
; times 64-16 db 0 ; remaining partition entries
; dw 0xAA55
あと、定義ファイルも使用しています。これですね。
%define STACK_SEG 0x0050
%define BUF_SEG 0x0980
%define BUF_OFF 0x0000
%define BOOT1_SEG 0x0800
%define BOOT1_OFF 0x0000
%define APP_SEG 0x1000
%define APP_OFF 0x0000
%define MON_SEG 0x0050
%define MON_OFF 0x0000
%define PART_TBL_OFF 0x9000
%define BPB_ADDR 0x9100
%define BOOT_DRV_OFF 0x9040
%define VBR_START_LBA 0x9042
%define FAT_START_LBA 0x9046
%define DIR_START_LBA 0x904A
%define DATA_START_LBA 0x904E
%define SEC_PER_CLUS_OFF 0x9050
%define CLUS_SIZ 0x9052
%define BYTE_PER_SEC 0x905D
%define ROOT_ENT_CNT_OFF 0x9060
%define PART_TBL_OFF 446
%define PART_ENT_SZ 16
%define VBR_SECTORS 1
%define ENT_SECTORS 1
; ============================
; パーティションエントリ構造体 (16バイト)
; ============================
struc PART_ENTRY
.boot_flag resb 1
.chs_start resb 3
.type resb 1
.chs_end resb 3
.lba_start resd 1
.sectors resd 1
endstruc
;==========================================
; BIOS Parameter Block (BPB) + Extended BPB
; 対応: FAT12 / FAT16 / FAT32
;==========================================
struc BPB_FAT16
; --- 標準BPB部分(共通) ---
.jmpBoot resb 3 ; JMP命令 (例: EB 58 90)
.OEMName resb 8 ; OEM名 ("MSDOS5.0" など)
.BytesPerSec resw 1 ; セクタサイズ (通常 512)
.SecPerClus resb 1 ; 1クラスタあたりのセクタ数
.RsvdSecCnt resw 1 ; 予約セクタ数 (FATの前)
.NumFATs resb 1 ; FATテーブル数 (通常2)
.RootEntCnt resw 1 ; ルートディレクトリエントリ数 (FAT32では0)
.TotSec16 resw 1 ; 総セクタ数 (小容量用)
.Media resb 1 ; メディアタイプ (0xF8=HDD)
.FATSz16 resw 1 ; FATサイズ (FAT12/16のみ)
.SecPerTrk resw 1 ; トラックあたりセクタ数
.NumHeads resw 1 ; ヘッド数
.HiddSec resd 1 ; 隠しセクタ数
.TotSec32 resd 1 ; 総セクタ数 (大容量用)
endstruc
;==========================================
; Disk Address Packet (DAP)
; for INT 13h AH=42h/43h
;==========================================
struc DAP
.Size resb 1 ; 構造体のサイズ (必ず16)
.Reserved resb 1 ; 予約 (0)
.NumBlocks resw 1 ; 読み/書きするセクタ数
.BufferOff resw 1 ; 転送先バッファのオフセット
.BufferSeg resw 1 ; 転送先バッファのセグメント
.LBA_Low resd 1 ; 開始LBA下位16bit
.LBA_High resd 1 ; 開始LBA上位32bit
endstruc
;===============================
; FAT Directory Entry (32 bytes)
; FAT16 / FAT32 共通
;===============================
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
これでboot1.asmを作成する準備ができました。
次回こそはboot1.asmを公開したいと思います。
《2025/12/12 22:00:47》