1. 程式人生 > >擴充套件int13h呼叫詳解(修正)

擴充套件int13h呼叫詳解(修正)

擴充套件int13h呼叫詳解(修正) 
第一部分 簡 介 

一. 硬碟結構簡介 

1. 硬碟引數釋疑 

到目前為止, 人們常說的硬碟引數還是古老的 CHS (Cylinder/ 
Head/Sector)引數. 那麼為什麼要使用這些引數, 它們的意義是什麼? 
它們的取值範圍是什麼? 
很久以前, 硬碟的容量還非常小的時候, 人們採用與軟盤類似的結 
構生產硬碟. 也就是硬碟碟片的每一條磁軌都具有相同的扇區數. 由此 
產生了所謂的3D引數 (Disk Geometry). 既磁頭數(Heads), 柱面數 
(Cylinders), 扇區數(Sectors),以及相應的定址方式. 

其中: 

磁頭數(Heads) 表示硬碟總共有幾個磁頭,也就是有幾面盤片, 最大 
為 255 (用 8 個二進位制位儲存); 
柱面數(Cylinders) 表示硬碟每一面碟片上有幾條磁軌, 最大為 1023 
(用 10 個二進位制位儲存); 
扇區數(Sectors) 表示每一條磁軌上有幾個扇區, 最大為 63 (用 6 
個二進位制位儲存). 
每個扇區一般是 512個位元組, 理論上講這不是必須的, 但好象沒有取 
別的值的. 

所以磁碟最大容量為: 

255 * 1023 * 63 * 512 / 1048576 = 8024 MB ( 1M = 1048576 Bytes ) 
或硬碟廠商常用的單位: 
255 * 1023 * 63 * 512 / 1000000 = 8414 MB ( 1M = 1000000 Bytes ) 

在 CHS 定址方式中, 磁頭, 柱面, 扇區的取值範圍分別為 0 到 Heads - 1, 
0 到 Cylinders - 1, 1 到 Sectors (注意是從 1 開始). 

2. 基本 Int 13H 呼叫簡介 

BIOS Int 13H 呼叫是 BIOS 提供的磁碟基本輸入輸出中斷呼叫, 它可以 
完成磁碟(包括硬碟和軟盤)的復位, 讀寫, 校驗, 定位, 診斷, 格式化等功能. 
它使用的就是 CHS 定址方式, 因此最大識能訪問 8 GB 左右的硬碟 ( 本文中 
如不作特殊說明, 均以 1M = 1048576 位元組為單位). 

3. 現代硬碟結構簡介 

在老式硬碟中, 由於每個磁軌的扇區數相等, 所以外道的記錄密度要遠低 
於內道, 因此會浪費很多磁碟空間 (與軟盤一樣). 為了解決這一問題, 進一 
步提高硬碟容量, 人們改用等密度結構生產硬碟. 也就是說, 外圈磁軌的扇區 
比內圈磁軌多. 採用這種結構後, 硬碟不再具有實際的3D引數, 定址方式也改 
為線性定址, 即以扇區為單位進行定址. 
為了與使用3D定址的老軟體相容 (如使用BIOS Int13H介面的軟體), 在硬 
盤控制器內部安裝了一個地址***器, 由它負責將老式3D引數***成新的線性 
引數. 這也是為什麼現在硬碟的3D引數可以有多種選擇的原因 (不同的工作模 
式, 對應不同的3D引數, 如 LBA, LARGE, NORMAL). 

4. 擴充套件 Int 13H 簡介 

雖然現代硬碟都已經採用了線性定址, 但是由於基本 Int 13H 的制約, 使 
用 BIOS Int 13H 介面的程式, 如 DOS 等還只能訪問 8 G 以內的硬碟空間. 
為了打破這一限制, Microsoft 等幾家公司制定了擴充套件 Int 13H 標準 
(Extended Int13H), 採用線性定址方式存取硬碟, 所以突破了 8 G 的限制, 
而且還加入了對可拆卸介質 (如活動硬碟) 的支援. 

二. Boot Sector 結構簡介 

1. Boot Sector 的組成 

Boot Sector 也就是硬碟的第一個扇區, 它由 MBR (Master Boot Record), 
DPT (Disk Partition Table) 和 Boot Record ID 三部分組成. 

MBR 又稱作主引導記錄佔用 Boot Sector 的前 446 個位元組 ( 0 to 0x1BD ), 
存放系統主載入程式 (它負責從活動分割槽中裝載並執行系統載入程式). 
DPT 即主分割槽表佔用 64 個位元組 (0x1BE to 0x1FD), 記錄了磁碟的基本分割槽 
資訊. 主分割槽表分為四個分割槽項, 每項 16 位元組, 分別記錄了每個主分割槽的資訊 
(因此最多可以有四個主分割槽). 
Boot Record ID 即引導區標記佔用兩個位元組 (0x1FE and 0x1FF), 對於合法 
引導區, 它等於 0xAA55, 這是判別引導區是否合法的標誌. 
Boot Sector 的具體結構如下圖所示 (參見 NightOwl 大俠的文章): 

  
 
            


2. 分割槽表結構簡介 
分割槽表由四個分割槽項構成, 每一項的結構如下: 
BYTE State : 分割槽狀態, 0 = 未啟用, 0x80 = 啟用 (注意此項) 
BYTE StartHead : 分割槽起始磁頭號 
WORD StartSC : 分割槽起始扇區和柱面號, 底位元組的低6位為扇區號, 
高2位為柱面號的第 Array,10 位, 高位元組為柱面號的低 8 位 
BYTE Type : 分割槽型別, 如 0x0B = FAT32, 0x83 = Linux 等, 
00 表示此項未用 
BYTE EndHead : 分割槽結束磁頭號 
WORD EndSC : 分割槽結束扇區和柱面號, 定義同前 
DWORD Relative : 線上性定址方式下的分割槽相對扇區地址 
(對於基本分割槽即為絕對地址) 
DWORD Sectors : 分割槽大小 (總扇區數) 

注意: 在 DOS / Windows 系統下, 基本分割槽必須以柱面為單位劃分 
( Sectors * Heads 個扇區), 如對於 CHS 為 764/255/63 的硬碟, 分割槽的 
最小尺寸為 255 * 63 * 512 / 1048576 = 7.844 MB. 

3. 擴充套件分割槽簡介 

由於主分割槽表中只能分四個分割槽, 無法滿足需求, 因此設計了一種擴充套件 
分割槽格式. 基本上說, 擴充套件分割槽的資訊是以連結串列形式存放的, 但也有一些特 
別的地方. 
首先, 主分割槽表中要有一個基本擴充套件分割槽項, 所有擴充套件分割槽都隸屬於它, 
也就是說其他所有擴充套件分割槽的空間都必須包括在這個基本擴充套件分割槽中. 對於 
DOS / Windows 來說, 擴充套件分割槽的型別為 0x05或0x0F(>8GB). 
除基本擴充套件分割槽以外的其他所有擴充套件分割槽則以連結串列的形式級聯存放, 後 
一個擴充套件分割槽的資料項記錄在前一個擴充套件分割槽的分割槽表中, 但兩個擴充套件分割槽 
的空間並不重疊. 
擴充套件分割槽類似於一個完整的硬碟, 必須進一步分割槽才能使用. 但每個擴 
展分割槽中只能存在一個其他分割槽. 此分割槽在 DOS/Windows 環境中即為邏輯盤. 
因此每一個擴充套件分割槽的分割槽表 (同樣儲存在擴充套件分割槽的第一個扇區中)中最多 
只能有兩個分割槽資料項(包括下一個擴充套件分割槽的資料項). 
擴充套件分割槽和邏輯盤的示意圖如下: 

     
 



三. 系統啟動過程簡介 

系統啟動過程主要由一下幾步組成(以硬碟啟動為例): 

1. 開機 :-) 
2. BIOS 加電自檢 ( Power On Self Test -- POST ) 
記憶體地址為 0ffff:0000 
3. 將硬碟第一個扇區 (0頭0道1扇區, 也就是Boot Sector) 
讀入記憶體地址 0000:7c00 處. 
4. 檢查 (WORD) 0000:7dfe 是否等於 0xaa55, 若不等於 
則轉去嘗試其他啟動介質, 如果沒有其他啟動介質則顯示 
"No ROM BASIC" 然後宕機. 
5. 跳轉到 0000:7c00 處執行 MBR 中的程式. 
6. MBR 首先將自己複製到 0000:0600 處, 然後繼續執行. 
7. 在主分割槽表中搜索標誌為活動的分割槽. 如果發現沒有活動 
分割槽或有不止一個活動分割槽, 則轉停止. 
8. 將活動分割槽的第一個扇區讀入記憶體地址 0000:7c00 處. 
Array. 檢查 (WORD) 0000:7dfe 是否等於 0xaa55, 若不等於則 

顯示 "Missing Operating System" 然後停止, 或嘗試 
軟盤啟動. 
10. 跳轉到 0000:7c00 處繼續執行特定系統的啟動程式. 
11. 啟動系統 ... 

以上步驟中 2,3,4,5 步是由 BIOS 的載入程式完成. 6,7,8,Array,10 
步由MBR中的載入程式完成. 

一般多系統載入程式 (如 SmartFDISK, BootStar, PQBoot 等) 
都是將標準主引導記錄替換成自己的載入程式, 在執行系統啟動程式 
之前讓使用者選擇要啟動的分割槽. 
而某些系統自帶的多系統載入程式 (如 lilo, NT Loader 等) 
則可以將自己的載入程式放在系統所處分割槽的第一個扇區中, 在 Linux 
中即為 SuperBlock (其實 SuperBlock 是兩個扇區). 

注: 以上各步驟中使用的是標準 MBR, 其他多系統載入程式的引導 
過程與此不同. 


第二部分 技術資料 

第一章 擴充套件 Int13H 技術資料 

一. 簡介 
設計擴充套件 Int13H 介面的目的是為了擴充套件 BIOS 的功能, 使其支援 
多於1024柱面的硬碟, 以及可移動介質的瑣定, 解鎖及彈出等功能. 

二. 資料結構 

1. 資料型別約定 
BYTE 1 位元組整型 ( 8 位 ) 
WORD 2 位元組整型 ( 16 位 ) 
DWORD 4 位元組整型 ( 32 位 ) 
QWORD 8 位元組整型 ( 64 位 ) 

2. 磁碟地址資料包 Disk Address Packet (DAP) 
DAP 是基於絕對扇區地址的, 因此利用 DAP, Int13H 可以輕鬆地逾 
越 1024 柱面的限制, 因為它根本就不需要 CHS 的概念. 
DAP 的結構如下: 

struct DiskAddressPacket 

BYTE PacketSize; // 資料包尺寸: 
//(固定值,恆等於16,即10H,指本結構所佔用的儲存空間) 
BYTE Reserved; // ==0 
WORD BlockCount; // 要傳輸的資料塊個數(以扇區為單位) 
DWORD BufferAddr; // 傳輸緩衝地址(segment:offset) 
QWORD BlockNum; // 磁碟起始絕對塊地址 
}; 

PacketSize 儲存了 DAP 結構的尺寸, 以便將來對其進行擴充. 在 
目前使用的擴充套件 Int13H 版本中 PacketSize 恆等於 16. 如果它小於 
16, 擴充套件 Int13H 將返回錯誤碼( AH=01, CF=1 ). 
BlockCount 對於輸入來說是需要傳輸的資料塊總數, 對於輸出來說 
是實際傳輸的資料塊個數. BlockCount = 0 表示不傳輸任何資料塊. 
BufferAddr 是傳輸資料緩衝區的 32 位地址 (段地址:偏移量). 資料 
緩衝區必須位於常規記憶體以內(1M). 
BlockNum 表示的是從磁碟開始算起的絕對塊地址(以扇區為單位), 
與分割槽無關. 第一個塊地址為 0. 一般來說, BlockNum 與 CHS 地址的關係 
是: 
BlockNum = 
(cylinder * NumberOfHeads + head) * SectorsPerTrack + sector - 1; 

其中 cylinder, head, sector 是 CHS 地址, NumberOfHeads 是磁碟 
的磁頭數, SectorsPerTrack 是磁碟每磁軌的扇區數. 
也就是說 BlockNum 是沿著 扇區->磁軌->柱面 的順序記數的. 這一順 
序是由磁碟控制器虛擬的, 磁碟表面資料塊的實際排列順序可能與此不同 
(如為了提高磁碟速度而設定的間隔因子將會打亂扇區的排列順序). 

3. 驅動器引數資料包 Drive Parameters Packet 
驅動器引數資料包是在擴充套件 Int13H 的取得驅動器引數子功能呼叫中 
使用的資料包. 格式如下: 


struct DriveParametersPacket 

WORD InfoSize; // 資料包尺寸: 
//(固定值,等於26,即1AH,指本結構所佔用的儲存空間) 
WORD Flags; // 資訊標誌 
DWORD Cylinders; // 磁碟柱面數 
DWORD Heads; // 磁碟磁頭數 
DWORD SectorsPerTrack; // 每磁軌扇區數 
QWORD Sectors; // 磁碟總扇區數 
WORD SectorSize; // 扇區尺寸 (以位元組為單位) 
}; 
資訊標誌用於返回磁碟的附加資訊, 每一位的定義如下: 

0 位: 
0 = 可能發生 DMA 邊界錯誤 
1 = DMA 邊界錯誤將被透明處理 
如果這位置 1, 表示 BIOS 將自動處理 DMA 邊界錯誤, 也就是說 
錯誤程式碼 0ArrayH 永遠也不會出現. 

1 位: 
0 = 未提供 CHS 資訊 
1 = CHS 資訊合法 
如果塊裝置的傳統 CHS 幾何資訊不適當的話, 該位將置 0. 

2 位: 
0 = 驅動器不可移動 
1 = 驅動器可移動 

3 位: 表示該驅動器是否支援寫入時校驗. 

4 位: 
0 = 驅動器不具備介質更換檢測線 
1 = 驅動器具備介質更換檢測線 


5 位: 
0 = 驅動器不可鎖定 
1 = 驅動器可以鎖定 
要存取驅動器號大於 0x80 的可移動驅動器, 該位必須置 1 
(某些驅動器號為 0 到 0x7F 的裝置也需要置位) 

6 位: 
0 = CHS 值是當前儲存介質的值 (僅對於可移動介質), 如果 
驅動器中有儲存介質, CHS 值將被返回. 
1 = CHS 值是驅動器支援的最大值 (此時驅動器中沒有介質). 

7 - 15 位: 保留, 必須置 0. 

三. 介面規範 

1. 暫存器約定 
在擴充套件 Int13H 呼叫中一般使用如下暫存器約定: 

ds:si ==> 磁碟地址資料包( disk address packet ) 
dl ==> 驅動器號 
ah ==> 功能程式碼 / 返回碼 

在基本 Int13H 呼叫中, 0 - 0x7F 之間的驅動器號代表可移動驅動器 
0x80 - 0xFF 之間的驅動器號代表固定驅動器. 但在擴充套件 Int13H 呼叫中 
0x80 - 0xFF 之間還包括一些新出現的可移動驅動器, 比如活動硬碟等. 
這些驅動器支援先進的鎖定,解鎖等功能. 
ah 返回的錯誤碼除了標準 Int13H 呼叫規定的基本錯誤碼以外,又增加 
了以下錯誤碼: 

B0h 驅動器中的介質未被鎖定 

B1h 驅動器中的介質已經鎖定 

B2h 介質是可移動的 

B3h 介質正在被使用 

B4h 鎖定記數溢位 

B5h 合法的彈出請求失敗 

2. API 子集介紹 
1.x 版的擴充套件 Int13H 呼叫中規定了兩個主要的 API 子集. 

第一個子集提供了訪問大硬碟所必須的功能, 包括 檢查擴充套件 In13H 
是否存在( 41h ), 擴充套件讀( 42h ), 擴充套件寫( 43h ), 校驗扇區( 44h ), 
擴充套件定位( 47h ) 和 取得驅動器引數( 48h ). 
第二個子集提供了對軟體控制驅動器鎖定和彈出的支援, 包括 檢查擴充套件 
Int13H 是否存在( 41h ), 鎖定/解鎖驅動器( 45h ), 彈出驅動器( 46h ), 
取得驅動器引數( 48h ), 取得擴充套件驅動器改變狀態( 4Arrayh ), int 15h. 
如果使用了呼叫規範中不支援的功能, BIOS 將返回錯誤碼 ah = 01h, 
CF = 1. 

3. API 詳解 

1) 檢驗擴充套件功能是否存在 
入口: 
AH = 41h 
BX = 55AAh 
DL = 驅動器號 

返回: 
CF = 0 
AH = 擴充套件功能的主版本號 
AL = 內部使用 
BX = AA55h 
CX = API 子集支援點陣圖 
CF = 1 
AH = 錯誤碼 01h, 無效命令 

這個呼叫檢驗對特定的驅動器是否存在擴充套件功能. 如果進位標誌置 1 
則此驅動器不支援擴充套件功能. 如果進位標誌為 0, 同時 BX = AA55h, 則 
存在擴充套件功能. 此時 CX 的 0 位表示是否支援第一個子集, 1位表示是否 
支援第二個子集. 
對於 1.x 版的擴充套件 Int13H 來說, 主版本號 AH = 1. AL 是副版本號, 
但這僅限於 BIOS 內部使用, 任何軟體不得檢查 AL 的值. 

2) 擴充套件讀 
入口: 
AH = 42h 
DL = 驅動器號 
DS:DI = 磁碟地址資料包(Disk Address Packet) 

返回: 
CF = 0, AH = 0 成功 
CF = 1, AH = 錯誤碼 

這個呼叫將磁碟上的資料讀入記憶體. 如果出現錯誤, DAP 的 BlockCount 
項中則記錄了出錯前實際讀取的資料塊個數. 

3) 擴充套件寫 
入口: 
AH = 43h 
AL 
0 位 = 0 關閉寫校驗 
1 開啟寫校驗 
1 - 7 位保留, 置 0 
DL = 驅動器號 
DS:DI = 磁碟地址資料包(DAP) 
返回: 
CF = 0, AH = 0 成功 
CF = 1, AH = 錯誤碼 

這個呼叫將記憶體中的資料寫入磁碟. 如果打開了寫校驗選項, 但 BIOS 
不支援, 則會返回錯誤碼 AH = 01h, CF = 1. 功能 48h 可以檢測BIOS是否 
支援寫校驗. 
如果出現錯誤, DAP 的 BlockCount 項中則記錄了出錯前實際寫入的數 
據塊個數. 

4) 校驗扇區 
入口: 
AH = 44h 
DL = 驅動器號 
DS:DI = 磁碟地址資料包(Disk Address Packet) 

返回: 
CF = 0, AH = 0 成功 
CF = 1, AH = 錯誤碼 


這個呼叫校驗磁碟資料, 但並不將資料讀入記憶體.如果出現錯誤, DAP 的 
BlockCount 項中則記錄了出錯前實際校驗的資料塊個數. 

5) 鎖定/解鎖驅動器 
入口: 
AH = 45h 
AL 
= 0 鎖定驅動器 
= 1 驅動器解鎖 
= 02 返回鎖定/解鎖狀態 
= 03h-FFh - 保留 
DL = 驅動器號 

返回: 
CF = 0, AH = 0 成功 
CF = 1, AH = 錯誤碼 

這個呼叫用來縮定指定驅動器中的介質. 
所有標號大於等於 0x80 的可移動驅動器必須支援這個功能. 如果 
在支援可移動驅動器控制功能子集的固定驅動器上使用這個功能呼叫, 將 
會成功返回. 
驅動器必須支援最大255次鎖定, 在所有鎖定被解鎖之前, 不能在物理上 
將驅動器解鎖. 解鎖一個未鎖定的驅動器,將返回錯誤碼 AH= B0h. 如果鎖定一 
個已鎖定了255次的驅動器, 將返回錯誤碼 AH = B4h. 
鎖定一個沒有介質的驅動器是合法的. 

6) 彈出可移動驅動器中的介質 
入口: 
AH = 46h 
AL = 0 保留 
DL = 驅動器號 

返回: 
CF = 0, AH = 0 成功 
CF = 1, AH = 錯誤碼 

這個呼叫用來彈出指定的可移動驅動器中的介質. 
所有標號大於等於 0x80 的可移動驅動器必須支援這個功能. 如果 
在支援可移動驅動器控制功能子集的固定驅動器上使用這個功能呼叫, 將 
會返回錯誤碼 AH = B2h (介質不可移動). 如果試圖彈出一個被鎖定的介質 
將返回錯誤碼 AH = B1h (介質被鎖定). 
如果試圖彈出一個沒有介質的驅動器, 則返回錯誤碼 Ah = 31h (驅動器 
中沒有介質). 
如果試圖彈出一個未鎖定的可移動驅動器中的介質, Int13h會呼叫 Int15h 
(AH = 52h) 來檢查彈出請求能否執行. 如果彈出請求被拒絕則返回錯誤碼(同 
Int15h). 如果彈出請求被接受,但出現了其他錯誤, 則返回錯誤碼 AH = B5h. 

7) 擴充套件定位 
入口: 
AH = 47h 
DL = 驅動器號 
DS:DI = 磁碟地址資料包(Disk Address Packet) 

返回: 
CF = 0, AH = 0 成功 
CF = 1, AH = 錯誤碼 

這個呼叫將磁頭定位到指定扇區. 

8) 取得驅動器引數 
入口: 
AH = 48h 
DL = 驅動器號 
DS:DI = 返回資料緩衝區地址 

返回: 
CF = 0, AH = 0 成功 
DS:DI 驅動器引數資料包地址, (參見前面的文章) 
CF = 1, AH = 錯誤碼 

這個呼叫返回指定驅動器的引數. 

Array) 取得擴充套件驅動器介質更換檢測線狀態 
入口: 
AH = 4Arrayh 
DL = 驅動器號 

返回: 
CF = 0, AH = 0 介質未更換 
CF = 1, AH = 06h 介質可能已更換 

這個呼叫返回指定驅動器的介質更換狀態. 
這個呼叫與 Int13h AH = 16h 子功能呼叫相同, 只是允許任何驅動器 
標號. 如果對一臺支援可移動介質功能子集的固定驅動器使用此功能,則永遠 
返回 CF = 0, AH = 0. 
簡單地將可移動介質鎖定再解鎖就可以啟用檢測線, 而無須真正更換介質. 


10) Int 15h 可移動介質彈出支援 
入口: 
AH = 52h 
DL = 驅動器號 
返回: 
CF = 0, AH = 0 彈出請求可能可以執行 
CF = 1, AH = 錯誤碼 B1h 或 B3h 彈出請求不能執行 

這個呼叫是由 Int13h AH=46h 彈出介質功能呼叫內部使用的.