BIOS 將 MBR 載入 0000:7C00h,初始暫存器:CS=DS=ES=SS=0, IP=7C00h, SP=0400h。MBR 首要任務是將自身從 7C00h 重定位至 0600h,騰出 7C00h 給啟動扇區(VBR)。
start:
xor ax, ax
mov ss, ax ; SS=0
mov sp, 7C00h ; 新堆疊指向 7C00h 以上
sti ; 開啟中斷
push ax
pop es ; ES=0
push ax
pop ds ; DS=0
cld ; 方向旗標清零(正向複製)
mov si, 7C1Bh ; 來源:原始代碼起始
mov di, 061Bh ; 目的:重定位後位址
push ax
push di
mov cx, code_end - loc_restart ; 複製長度
rep movsb ; 複製代碼到 0600h
retf ; 遠程返回到 0:061Bh (loc_restart)
loc_restart:
mov si, 07BEh ; SI 指向分區表起始 (7BEh)
mov cl, 4 ; 4 個分區項
loc_nextpe:
cmp [si], ch ; 比較啟動旗標 [si] 與 0 (CH=0)
jl short loc_boot ; 有號比較:若 <0 (即 80h) 則啟動
jnz short loc_bad ; 不等於 0 且非 80h → 無效值
add si, 10h ; 下一項
loop loc_nextpe
int 18h ; 4 項皆無啟動旗標 → ROM BASIC
loc_boot:
mov dx, [si] ; DX = 啟動旗標(80h) + 起始磁頭
mov bp, si ; BP 保存分區表項指標(傳遞給 VBR)
loc_nextrpe: ; 檢查剩餘項是否全為 0
add si, 10h
dec cx
jz short loc_tabok ; 全檢查完畢
cmp [si], ch ; 其他項必須為 0
je loc_nextrpe
; 發現第二個 80h 或非 0/80h 值
loc_bad:
mov si, offset msg1+1 ; "Invalid partition table"
jmp short loc_halt
關鍵邏輯:
00h(非啟動)與 80h(啟動中),其他值視為錯誤。INT 18h 進入 ROM BASIC(古老機制)。MBR 根據分區系統 ID 選擇讀取模式:
| 系統 ID | 檔案系統 | 讀取模式 |
|---|---|---|
| 0Eh | FAT32 LBA | 優先 INT 13h AH=42h (LBA 48-bit) |
| 0Bh, 0Ch | FAT32 | 傳統 INT 13h AH=02h,但第 6 次重試改讀備份扇區(+6 扇區) |
| 其他 | FAT12/16, NTFS 等 | 傳統 INT 13h AH=02h (CHS) |
loc_check13ext:
mov bx, 55AAh
push ax
mov ah, 41h ; INT 13h AH=41h:檢測擴展功能
int 13h
pop ax
jc short loc_no13ext ; CF=1 不支援
cmp bx, 0AA55h ; 回傳簽章檢查
jne short loc_no13ext
test cl, 1 ; CL bit 0 = 支援擴展存取
jz short loc_no13ext
; 支援擴展:修改自我代碼跳過傳統讀取
mov ah, al
mov [bp+24h], dl ; 記錄磁碟機號
mov word ptr ds:[6A1h], 1EEBh ; 寫入 JMP SHORT 指令跳過傳統路徑
此處展示了 MBR 的自我修改代碼技術:若支援 LBA,直接在記憶體中修改跳轉指令,避開傳統 CHS 讀取路徑。
loc_int13old:
mov di, 0Ah ; 重試計數器 = 10
loc_readin:
mov ax, 0201h ; AH=02h(讀取), AL=01h(1 扇區)
mov bx, sp ; ES:BX = 0:7C00h (堆疊剛好在 7C00h)
xor cx, cx
cmp di, 5
jg short loc_readbackup ; 前 5 次讀主要扇區
mov cx, [bp+25h] ; 第 6 次起:讀備份扇區 (CH=柱面, CL=扇區)
loc_readbackup:
add cx, [bp+2] ; 起始扇區 + 偏移
int 13h ; 呼叫 BIOS 讀取
jc short loc_retry ; CF=1 讀取失敗
; 讀取成功:驗證魔術數字
mov si, offset msg3 ; "Missing operating system"
cmp word ptr ds:[7DFEh], 0AA55h
je short loc_gobootrecok ; 驗證通過 → 跳轉執行
; 驗證失敗:嘗試備份
sub di, 5
jg loc_readin
int13ext proc near
; 輸入:DX:AX = 邏輯扇區號 (LBA), CL = 扇區數, ES:BX = 緩衝區
push si
xor si, si
push si ; 保留位元組
push si ; 保留位元組
push dx ; LBA 高 32 位
push ax ; LBA 低 32 位
push es ; 緩衝區段地址
push bx ; 緩衝區偏移
push cx ; 扇區數
mov si, 10h ; 封包大小 = 16 位元組
push si
mov si, sp ; DS:SI 指向磁碟位址封包 (DAP)
push ax
push dx
mov ax, 4200h ; AH=42h, AL=00h
mov dl, [bp+24h] ; 磁碟機號
int 13h
; ... 後續處理緩衝區指標推進、返回狀態 ...
retn
int13ext endp
DAP (Disk Address Packet) 結構:
DAP STRUCT
cbSize db 10h ; 封包大小
Reserved db 0
BlockCount dw ? ; 扇區數 (max 127)
Reserved2 db 0
BufferPtr dd ? ; 緩衝區 32-bit 位址 (段:偏移)
LBA dq ? ; 64-bit 邏輯扇區號
DAP ENDS
loc_endofretry: ; 10 次重試全失敗
test si, si
jnz loc_msg ; SI 有錯誤訊息指標 → 顯示
mov si, offset msg2 ; "Error loading operating system"
jmp loc_msgh
loc_gobootrecok: ; 驗證通過
jmp short loc_bootrecok
loc_bootrecok: ; 移交控制權給 VBR (0:7C00h)
mov di, sp ; DI = 7C00h
push ds
push di
mov si, bp ; SI = 分區表項指標 (BP 保存的)
retf ; 遠程跳轉到 0:7C00h
; 此時暫存器狀態:
; CS=DS=ES=SS=0, IP=7C00h, DI=SP=7C00h, SI=BP→分區表項
msg1 db 'Invalid partition table', 0
msg2 db 'Error loading operating system', 0
msg3 db 'Missing operating system', 0
; 37 位元組保留區 (多語言訊息)
partition_tab: ; 位於 7BEh
; 第 1 項:啟動中, FAT32 (0Bh), LBA=3Fh, 大小=383B41h 扇區
db 80h, 01h, 01h, 00h, 0Bh, 7Fh, 7Fh, 0C8h
dd 0000003Fh, 00383B41h
; 第 2 項:非啟動, 擴充分區 (05h)
db 00h, 00h, 41h, 0C9h, 05h, 7Fh, 0FFh, 13h
dd 00383B80h, 0028BA80h
; 第 3, 4 項:全 0
magicword dw 0AA55h ; 7FEh: MBR 有效標誌
Win98 SE MBR 體現了從傳統 CHS 向 LBA 過渡期的設計智慧:自我修改代碼動態選擇最佳讀取路徑、完善的重試與備份機制、嚴格的分區表合法性檢查。這段 446 位元組的代碼(不含分區表與魔術數),是 PC 相容機啟動鏈中最關鍵、最精煉的一環。