- x86/x64體系探索及編程
- 鄧志著
- 2361字
- 2019-03-01 11:49:35
第3章 編寫本書的實(shí)驗(yàn)例子
在前一章里對(duì)x86/x64平臺(tái)的匯編語(yǔ)言作了簡(jiǎn)要的介紹,在這一章里,我們將為本書的實(shí)驗(yàn)例子建立一個(gè)可運(yùn)行的實(shí)驗(yàn)平臺(tái)。
本書的所有例子都是基于祼機(jī)(無(wú)任何操作系統(tǒng))運(yùn)行,因此我們有兩種選擇。
① 編寫自己的MBR代碼放在0扇區(qū)里,由BIOS讀取MBR加載到0x7c00,在后面由這個(gè)MBR讀取磁盤中的扇區(qū)。
② 借助其他的文件格式,例如:使用FAT32文件格式,生成一個(gè)FAT32格式的可啟動(dòng)盤。我們需要編寫自己的活動(dòng)分區(qū)引導(dǎo)記錄。
本書的所有例子中的硬盤映像文件和U盤都使用FAT32文件格式,有比較通用的好處,在絕大多數(shù)機(jī)器上都可以讀取運(yùn)行。
3.1 實(shí)驗(yàn)的運(yùn)行環(huán)境
本書的實(shí)驗(yàn)例子可以運(yùn)行在三種環(huán)境里。
① Bochs模擬器:任何支持x64的版本都可以,筆者使用的是目前最新的2.5.1版本。
② VMware虛擬機(jī):使用較新的7版本或8版本。
③ 在真實(shí)機(jī)器上運(yùn)行。
筆者的機(jī)器是Intel Westmere微架構(gòu)的Core i5移動(dòng)處理器筆記本,是SandyBridge架構(gòu)的上一代i5處理器。本書很多實(shí)驗(yàn)例子只能在真實(shí)機(jī)器上運(yùn)行,Bochs和VMware并不支持那些功能。
問(wèn)題1:使用軟盤還是U盤?甚至是硬盤?
在Bochs里可以使用軟盤或硬盤的映像(image)文件,在真實(shí)機(jī)器上我們可以使用U盤作為介質(zhì)啟動(dòng)計(jì)算機(jī)運(yùn)行實(shí)驗(yàn)程序。本書例子的運(yùn)行使用以下三種介質(zhì)。
① 軟盤映像:統(tǒng)一使用文件名為demo.img,可以在Bochs或VMware里運(yùn)行。
② 硬盤映像:統(tǒng)一使用文件名為c.img,可以在Bochs里運(yùn)行。
③ U盤:將生成的硬盤映像直接寫入U(xiǎn)盤,在真實(shí)機(jī)器上運(yùn)行。
因此,本書的例子中最終生成兩個(gè)映像文件:demo.img文件用于在Bochs或VMware里運(yùn)行;c.img文件用于在Bochs或真實(shí)機(jī)器上運(yùn)行。
使用FAT32文件格式
在使用軟盤啟動(dòng)時(shí),我們直接將boot代碼寫在MBR里。而當(dāng)使用U盤啟動(dòng)真實(shí)機(jī)器時(shí)會(huì)遇到一些麻煩,我們的代碼寫在MBR里,某些機(jī)器將啟動(dòng)不了。
因此,當(dāng)使用U盤在真實(shí)機(jī)器上測(cè)試時(shí),將使用FAT32文件格式,也就是硬盤映像c.img使用FAT32文件格式。
如下所示,對(duì)于軟盤映像(文件名為demo.img)和硬盤映像(文件名為c.img),有兩種文件組織方式。
① 軟盤映像文件(demo.img):將boot模塊直接寫入軟盤的0扇區(qū),那么boot代碼就是我們的MBR程序,BIOS將讀軟盤的0扇區(qū)(我們的boot代碼)到內(nèi)存的7c00h位置上。
② 硬盤映像文件(c.img)和U盤:將boot模塊寫入映像文件和U盤的63號(hào)扇區(qū),BIOS讀取硬盤或U盤的0扇區(qū)(這是FAT32文件格式啟動(dòng)盤生成的MBR代碼)代碼到內(nèi)存7c00h,然后再由這個(gè)MBR代碼來(lái)從63號(hào)扇區(qū)讀我們的boot程序。
無(wú)論如何,我們的boot程序最終是運(yùn)行在7c00h區(qū)域里,然后我們的boot程序的職責(zé)是負(fù)責(zé)加載后續(xù)的模式(包括lib16、lib32、lib64、protected和long模式)。
3.2 生成空白的映像文件
現(xiàn)在我們需要生成兩個(gè)空白的映像文件:demo.img和c.img(硬盤),以便編譯后文件寫入映像文件中。在以后實(shí)驗(yàn)里,我們既可以使用demo.img也可以使用c.img來(lái)運(yùn)行實(shí)驗(yàn)例子。
demo.img是軟盤映像文件,它的大小為1.44MB;c.img是硬盤映像文件,它的大小為1MB(對(duì)于本書的代碼來(lái)說(shuō)足夠了)。
當(dāng)然,如果您不需要使用c.img或U盤進(jìn)行測(cè)試,只生成demo.img也足夠了(只是不能在真實(shí)機(jī)器上運(yùn)行,除非使用軟盤來(lái)啟動(dòng)機(jī)器)。
生成空白映像文件的方法有很多,可以使用任何一個(gè)十六進(jìn)制編輯軟件生成,也可以使用類似WinISO軟件來(lái)生成。本書介紹使用nasm及Bochs自帶的bximage工具來(lái)生成。
3.2.1 使用nasm編譯器生成
不用覺得奇怪,確實(shí)可以用nasm來(lái)生成一個(gè)空白的映像文件。
實(shí)驗(yàn)3-1:在真實(shí)機(jī)器上測(cè)試boot代碼
下面是示例源代碼。
代碼清單3-1(topic03\ex3-1\demo.asm):
;demo.asm ; Copyright (c) 2009-2012 mik ; All rights reserved. ; 建立一個(gè) 1.44MB 的 floppy 映像文件,名為:demo.img ; ; 生成命令:nasm demo.asm -o demo.img ; ; 用 0 填滿 1.44MB floppy 的空間 times 0x168000-($-$$) db 0
這個(gè)代碼中讓nasm生成0來(lái)填滿floppy的1.44MB空間,命令如下。
nasm demo.asm –o demo.img
經(jīng)過(guò)nasm編譯后生成一個(gè)bin格式的文件,命名為demo.img,那么這個(gè)demo.img就可以作為我們所需要的空白軟盤映像文件。
3.2.2 使用bximage工具
使用Bochs自帶的disk建立工具bximage是最簡(jiǎn)單的方法,基本上敲幾個(gè)回車鍵就
可以了,既可以用來(lái)生成軟盤映像,也可以用來(lái)生成硬盤映像文件。
E:\x86\source\topic03\ex3-1>bximage =================================================================== bximage Disk Image Creation Tool for Bochs $Id:bximage.c,v 1.34 2009/04/14 09:45:22 sshwarts Exp $ =================================================================== Do you want to create a floppy disk image or a hard disk image? Please type hd or fd. [hd] fd
在這一步里選擇生成軟盤還是硬盤映像,輸入fd生成軟盤映像文件。
Choose the size of floppy disk image to create,in megabytes. Please type 0.16,0.18,0.32,0.36,0.72,1.2,1.44,1.68,1.72,or [1.44] I will create a floppy image with cyl=80 heads=2 sectors per track=18 total sectors=2880 total bytes=1474560
這里顯示了軟盤的大小,默認(rèn)為1.44MB,輸入回車鍵。
What should I name the image? [a.img] Writing:[] Done. I wrote 1474560 bytes to a.img. The following line should appear in your Bochsrc: floppya:image="a.img",status=inserted (The line is stored in your windows clipboard,use CTRL-V to paste) Press any key to continue
接著輸入文件名,默認(rèn)為a.img,這里我們輸入demo.img。
然后我們使用bximage工具,用同樣的步驟來(lái)生成一個(gè)1MB大小的c.img文件(硬盤映像文件)。那么當(dāng)前的目錄下就同時(shí)有了demo.img和c.img兩個(gè)文件。我們的硬盤映像文件只需要1MB就足夠了。
使用bximage的好處是可以生成一條Bochs配置行,直接粘貼插入到Bochs配置文件中,保存后就Bochs可以使用軟盤映像或硬盤映像了。
3.3 設(shè)置Bochs配置文件
我們可以在Bochs自帶示例配置文件bochsrc-sample.txt上進(jìn)行修改(不建議這樣做),也可以復(fù)制為新的文件(文件名為bs),再進(jìn)行更改,下面是關(guān)鍵的地方。
floppya:1_44=demo.img,status=inserted ata0-master:type=disk,path="c.img",mode=flat,cylinders=2,heads=16,spt=63
上一行是軟盤的配置,使用demo.img作為軟盤映像文件,下一行是硬盤的配置,使用c.img作為硬盤映像文件。
#boot:floppy boot:disk
配置啟動(dòng)項(xiàng),可以選擇floppy或disk(用于硬盤映像)。在這里,筆者選擇使用硬盤映像作為啟動(dòng)介質(zhì),那么在以后的實(shí)驗(yàn)里都統(tǒng)一在Bochs里使用硬盤映像。
而在VMware里統(tǒng)一使用floppy映像,這樣做的好處是,既測(cè)試了floppy,也測(cè)試了硬盤(為使用U盤作測(cè)試)。
在稍后,我們將看到如何將c.img文件寫入U(xiǎn)盤在真實(shí)機(jī)器上測(cè)試。
3.4 源代碼的基本結(jié)構(gòu)
本書的源代碼按照章來(lái)組織,每章的所有例子在統(tǒng)一的目錄下,例如:topic18\目錄下存放有第18章的所有例子。
每個(gè)topic目錄下有數(shù)個(gè)實(shí)驗(yàn)例子,每個(gè)例子組織為一個(gè)子目錄,如:topic18\ex18-1\目錄下存放著第18章的實(shí)驗(yàn)例子1的源代碼。
整個(gè)x86\source\目錄下的文件組織如下所示。
x86\source\目錄是所有源碼的根目錄,下面有以下內(nèi)容。
① inc\目錄:定義所有源碼的支持頭文件,典型的有l(wèi)ib.inc、support.inc、protected.inc等文件。它們定義了一些源碼使用的常量和宏。
② lib\目錄:下面是所有庫(kù)函數(shù)的實(shí)現(xiàn)代碼,典型的有l(wèi)ib16.asm、lib32.asm、lib64.asm、apic.asm、debug.asm等文件,所有的例子都要使用這些庫(kù)文件。
③ common\目錄:下面有所有實(shí)驗(yàn)例子的共用代碼,典型的有boot.asm、setup.asm、long.asm、handler32.asm等文件。
④ topic01\到topicXX\目錄:每個(gè)目錄代表一章。每個(gè)topic目錄下有數(shù)個(gè)子目錄,各代表一個(gè)實(shí)驗(yàn)例子。
它們的下一層目錄里,例如:ex1-1\是第1章里的實(shí)驗(yàn)1的源代碼,這些目錄下面典型的有boot.asm,setup.asm、protected.asm、long.asm等文件。
注意:在這些子目錄下(與實(shí)驗(yàn)源碼在同一目錄)還有其他重要的文件。
① demo.img:軟盤映像文件。
② c.img:硬盤映像文件。
③ bs:Bochs配置文件。
④ config.txt:merge工具的配置文件(后面將會(huì)了解到)。
3.5 編譯源代碼
所有的源代碼都是使用nasm編譯生成的,如果整個(gè)x86\source\根目錄在e:盤下,則編譯topic03\ex3-2\setup.asm源文件所使用的nasm命令行如下所示。
當(dāng)前工作目錄進(jìn)入到x86\source\topic03\里,需要為nasm提供一個(gè)include目錄(也就是源碼的根目錄,使用-I<XXX>參數(shù)),最后目標(biāo)文件需要指明在哪個(gè)目錄下。
默認(rèn)情況下,生成一個(gè)setup二進(jìn)制文件放在topic03\ex3-2\目錄下。你可以為它指定一個(gè)輸出文件名。
e:\x86\source\topic03>nasm –I..\ ex3-2\setup.asm –o setup.bin
使用-o參數(shù),提供一個(gè)輸出文件名。這個(gè)輸出文件也可以指定輸出目錄。
3.6 映像文件內(nèi)的組織
在將目錄下的源文件編譯生成bin文件后,需要將這些bin文件寫入demo.img(軟盤映像文件)或者c.img(硬盤映像文件)中,這些bin文件在映像文件中如何組織呢?
如下所示,軟盤映像文件(demo.img)分為五大部分:boot模塊,setup模塊,protected模塊,long模塊和庫(kù)代碼模塊。
① boot模塊:放在0號(hào)扇區(qū)。
② setup模塊:從1號(hào)扇區(qū)開始。
③ lib16模塊:從20號(hào)扇區(qū)開始。
④ protected模塊:從64號(hào)扇區(qū)開始。
⑤ long模塊:從128號(hào)扇區(qū)開始。
⑥ lib32模塊:從192號(hào)扇區(qū)開始。
硬盤映像文件(c.img)與軟盤映像文件的不同是:boot模塊放在63號(hào)扇區(qū)里,0號(hào)扇區(qū)是FAT32文件格式的MBR代碼。
3.7 使用merge工具
將nasm編譯后生成的所有bin格式文件按前面所述結(jié)構(gòu)分別寫入軟盤映像文件(demo.img)和硬盤映像文件(c.img)中,方法有很多。
① 最原始和麻煩的方法是:使用hex編輯軟件逐個(gè)將bin文件寫入demo.img或c.img中。
② 使用dd工具:這個(gè)免費(fèi)的工具可以進(jìn)行磁盤/文件的復(fù)制/寫入/合并等操作。
dd工具的使用示例如下。
dd if=boot of=demo.img count=1
if提供輸入文件,of提供輸出文件,count提供數(shù)量,以block為單位(512字節(jié)),作用是將boot寫入demo.img的0開始處,即軟盤磁盤映像的0扇區(qū)處。
dd if=boot of=demo.img count=1 seek=1
上面這個(gè)命令是將boot模塊寫入到demo.img的1扇區(qū),使用seek參數(shù)跳過(guò)1個(gè)block。
編寫merge工具
然而,這些都不是好方法,另一個(gè)方法是使用筆者為本書的實(shí)驗(yàn)例子編寫的merge工具。merge是一個(gè)合并寫入映像工具,類似于dd操作,可批量寫入文件。merge工具將讀取當(dāng)前目錄下的配置文件config.txt來(lái)做相應(yīng)的寫入工作。
merge工具和dd工具可以在x86\tools\目錄找到,筆者使用的是Windows 7平臺(tái)。如果系統(tǒng)平臺(tái)上提示缺少某些DLL文件,請(qǐng)使用筆者提供的merge工具源碼,自行使用VC來(lái)編譯生成merge工具。
3.7.1 merge的配置文件
merge工具需要在當(dāng)前目錄提供一個(gè)配置文件,文件名必須為config.txt
e:\x86\source>merge Error:cannot open the config file:config.txt (系統(tǒng)找不到指定的文件)
當(dāng)無(wú)config.txt文件時(shí),執(zhí)行merge命令會(huì)出現(xiàn)上面的錯(cuò)誤提示。
下面是config.txt文件的配置示例。
# 輸入文件,輸入文件 offset,輸出文件,輸出文件 offset,寫入 block 數(shù)( 1 block=512 bytes) # **** 每一項(xiàng)用逗號(hào)分隔 **** # # example: # #模塊名 offset 輸出文件名 offset count(1 count=512 bytes) #------------------------------------------------- # boot, 0,demo.img,0,1 # setup, 0,demo.img,1,2 # init, 0,demo.img,3,5 # # 意思是: # boot 模塊從 block 0 開始寫入 demo.img 寫入位置為 block 0,寫入 1 個(gè) block # setup 模塊從 block 0 開始寫入 demo.img 寫入位置為 block 1,寫入 2 個(gè) block # init 模塊從 block 0 開始寫入 demo.img 寫入位置為 block 3,寫入 5 個(gè) block # 下面是第2章中使用到的配置實(shí)例: boot,0,demo.img,0,1
#是注釋,注意要單獨(dú)在一行里注釋,每一行對(duì)應(yīng)一條記錄(共可容納20條記錄),每條記錄指示merge怎樣寫入到目標(biāo)文件里。每條記錄有5個(gè)域。
① 需要寫入的模塊名(源文件)。
② 源文件的偏移扇區(qū)。
③ 寫入的目標(biāo)文件名。
④ 寫入的目標(biāo)起始扇區(qū)位置。
⑤ 寫入扇區(qū)的數(shù)量。
3.7.2 執(zhí)行merge命令
我們執(zhí)行merge命令將輸出下面的信息。
e:\x86\source\topic02\ex2-2>merge entry #0: uboot ---> c.img: success entry #1: setup ---> c.img: success entry #2: e:\x86\source\lib\lib16 ---> c.img: success entry #3: boot ---> demo.img: success entry #4: setup ---> demo.img: success entry #5: e:\x86\source\lib\lib16 ---> demo.img: success
上面的信息表明:有6條信息已成功寫入到目標(biāo)文件,merge工具已經(jīng)同時(shí)寫入到c.img和demo.img文件里。若輸出下面的信息
e:\x86\source\topic02\ex2-2>merge entry #0: uboot ---> c.img: success entry #1: setup ---> c.img: success entry #2: e:\x86\source\lib\lib16 ---> c.img: success fatal:open the file for read (系統(tǒng)找不到指定的文件)
上面的出錯(cuò)信息表明:在寫入第3條記錄時(shí),其中一個(gè)文件(源文件或目標(biāo)文件)不存在。但并不影響前面已經(jīng)寫入的記錄
3.8 使用U盤啟動(dòng)真實(shí)機(jī)器
把所有源碼生成的bin格式文件寫入到硬盤映像文件c.img中后,我們就可以直接把c.img文件寫入到U盤。
3.8.1 使用merge工具寫U盤
merge工具也可以寫入U(xiǎn)盤,只要在config.txt配置文件里增加一條寫U盤的記錄即可。
######## 下面是寫入 u 盤 ####### c.img,0,\\.\g:,0,200
這條記錄的源文件是c.img,目標(biāo)文件是\\.\g:(這個(gè)文件是U盤的符號(hào)鏈接),寫入200個(gè)扇區(qū)(這里的扇區(qū)數(shù)只要滿足所有的源碼大小就可以了,并不需要寫入1MB大小)。
查詢U盤的掛接點(diǎn)
要寫入U(xiǎn)盤,我們必須先知道U盤的文件名(注意:在你的機(jī)器上可能會(huì)是不同的文件名)。
記錄下來(lái):可以使用U盤對(duì)應(yīng)的卷名、設(shè)備名或者掛接名作為寫入對(duì)象。
使用dd工具可以查找磁盤設(shè)備的卷名、設(shè)備名以及符號(hào)鏈接名,如下所示。
E:\x86\source\topic02>dd --list rawwrite dd for windows version 0.6beta3. Written by John Newbigin <jn@it.swin.edu.au> This program is covered by terms of the GPL Version 2. Win32 Available Volume Information \\.\Volume{99bbfcb8-fc50-11e0-ae9d-88ae1d4bdccc}\ link to \\?\Device\HarddiskVolume20 removeable media Mounted on \\.\g:
上面執(zhí)行了dd–list命令,我們看到U盤掛接在\\.\g:上,我們可以把這個(gè)掛接點(diǎn)作為U盤文件名。
當(dāng)向config.txt增加一條寫\\.\g:文件的記錄,執(zhí)行merge命令將輸出下面的信息。
e:\x86\source\topic02\ex2-2>merge entry #0: uboot ---> c.img: success entry #1: setup ---> c.img: success entry #2: e:\x86\source\lib\lib16 ---> c.img: success entry #3: boot ---> demo.img: success entry #4: setup ---> demo.img: success entry #5: e:\x86\source\lib\lib16 ---> demo.img: success entry #6: c.img ---> \\.\g:: success
留意最后一條記錄#6,c.img文件已經(jīng)成功寫入\\.\g:(U盤)文件里。
3.8.2 使用hex編輯軟件寫U盤
在config.txt配置文件無(wú)誤時(shí),寫入一般都會(huì)成功(除了文件不存在外,我沒失敗過(guò))。如果merge工具寫U盤失敗,你可以使用傳統(tǒng)的方法,使用任何一個(gè)hex軟件(十六進(jìn)制編輯軟件)來(lái)寫U盤。
這里推薦一個(gè)比較好用輕量級(jí)的免費(fèi)hex編輯軟件HxD,HxD運(yùn)行需要使用管理員權(quán)限,在HxD里打開c.img源文件和U盤,將c.img文件使用覆蓋寫入到U盤。使用十六進(jìn)制編輯軟件總能獲得成功,即使用系統(tǒng)不能識(shí)別磁盤的情況下。
但是請(qǐng)小心使用這項(xiàng)功能,當(dāng)用寫模式打開disk時(shí),務(wù)必看清楚是不是自己要寫的U盤,以防誤寫了硬盤。
實(shí)驗(yàn)3-2:在真實(shí)機(jī)器上測(cè)試boot代碼
現(xiàn)在在真實(shí)的機(jī)器上測(cè)試我們的代碼,用上述的方法將boot模塊和setup模塊寫入U(xiǎn)盤,使用U盤啟動(dòng)機(jī)器運(yùn)行。boot和setup模塊將在稍后講述。
為了支持讀取U盤,編譯boot.asm的時(shí)候請(qǐng)使用以下命令。
nasm –Ie:\x86\source ex3-2\boot.asm –d UBOOT –o ex3-2\uboot
編譯時(shí)使用–d參數(shù)定義一個(gè)符號(hào)UBOOT(這是很重要的),這個(gè)符號(hào)用來(lái)編譯生成讀0x80的drive號(hào)。
是的,您無(wú)須在意是U盤還是硬盤,使用硬盤形式讀取U盤總能成功。大多數(shù)情況下U盤以USB-HDD形式工作。這是U盤模擬硬盤形式。如果不是,請(qǐng)留意BIOS檢測(cè)出來(lái)的是USB-FDD還是USB-ZIP,然后在BIOS啟動(dòng)選項(xiàng)中做相應(yīng)的設(shè)置。
在真實(shí)機(jī)器上的運(yùn)行結(jié)果如下所示。
上面顯示的信息“the message from setup module at sector 20…”是來(lái)自setup模塊,寫在U盤的第20號(hào)扇區(qū),用來(lái)演示讀取U盤扇區(qū)功能。
3.9 編寫boot代碼
完整的boot.asm代碼和setup.asm代碼在topic03\ex3-2\目錄下,下面是boot的主體代碼。
代碼清單3-2(topic03\ex3-2\boot.asm):
; boot.asm ; Copyright (c) 2009-2012 mik ; All rights reserved. ; ; 編譯命令是:nasm boot.asm -o boot (用軟盤啟動(dòng)) ; nasm boot.asm –o boot –d UBOOT (用U盤啟動(dòng)) ; 生成 boot 模塊后,寫入 demo.img(磁盤映像)的第 0 扇區(qū)(MBR) %include "..\inc\support.inc" %include "..\inc\ports.inc" bits 16 ;-------------------------------------- ; now,the processor is real mode ;-------------------------------------- ; Int 19h 加載 sector 0 (MBR) 進(jìn)入 BOOT_SEG 段,BOOT_SEG 定義為 0x7c00 org BOOT_SEG start: cli ; enable a20 line FAST_A20_ENABLE sti ; set BOOT_SEG environment mov ax,cs mov ds,ax mov ss,ax mov es,ax mov sp,BOOT_SEG ; 設(shè) stack 底為 BOOT_SEG call clear_screen mov si,hello call print_message mov si,20 ; setup 模塊在第20號(hào)扇區(qū)里 mov di,SETUP_SEG - 2 call load_module ; 使用 load_module() 讀多個(gè)扇區(qū) mov si,SETUP_SEG call print_message mov si,word [load_message_table + eax * 2] call print_message next: jmp $
boot代碼的主要功能是顯示信息和從磁盤中讀取扇區(qū)到內(nèi)存中。我們主要關(guān)注如何讀取磁盤。
問(wèn)題:使用CHS方式還是LBA方式?使用int 13h/ah=02h還是int 13h/ah=42h擴(kuò)展功能形式讀取磁盤?
在本書中,所有例子使用的都是LBA方式,LBA形式方便較直觀地反映出模塊在映像或磁盤的位置,在前面寫入U(xiǎn)盤的實(shí)驗(yàn)中就是以LBA方式寫入。
代碼清單3-3(topic03\ex3-2\boot.asm):
;---------------------------------------------------------------------- ; read_sector(int sector,char *buf):read one floppy sector(LBA mode) ; input: ; esi - sector ; di - buf ;---------------------------------------------------------------------- read_sector: pusha push es push ds pop es ; 測(cè)試是否支持 int 13h 擴(kuò)展功能 call check_int13h_extension test ax,ax jz do_read_sector_extension ; 支持 mov bx,di ; data buffer mov ax,si ; disk sector number ; now:LBA mode --> CHS mode call LBA_to_CHS ; now:read sector %ifdef UBOOT mov dl,0x80 ; for U 盤或者硬盤 %else mov dl,0 ; for floppy %endif mov ax,0x201 int 0x13 setc al ; 0:success 1:failure jmp do_read_sector_done ; 使用擴(kuò)展功能讀扇區(qū) do_read_sector_extension: call read_sector_extension mov al,0 do_read_sector_done: pop es popa movzx ax,al ret
上面的read_sector()函數(shù)負(fù)責(zé)讀入1個(gè)扇區(qū),輸入?yún)?shù)接受的是LBA方式,同時(shí)應(yīng)該使用int 13h的擴(kuò)展功能號(hào)來(lái)進(jìn)行讀/寫,不但是因?yàn)樗苤С指鼘挼淖x/寫范圍,而且還因?yàn)闉樗褂酶?jiǎn)單。它的工作邏輯如下所示。
測(cè)試BIOS是否支持?jǐn)U展的int 13h功能,不支持的話使用原來(lái)的int 13h功能擴(kuò)展。
3.9.1 LBA轉(zhuǎn)換為CHS
盡管代碼清單3-3中的read_sector()輸入的是讀取的LBA扇區(qū)號(hào),但仍然可以使用舊的int 13h/ah=02來(lái)讀磁盤。這就需要將LBA尋址轉(zhuǎn)換為CHS尋址。轉(zhuǎn)換的工作交由LBA_to_CHS()完成。
代碼清單3-4(topic03\ex3-2\boot.asm):
;------------------------------------------------------- ; LBA_to_CHS():LBA mode converting CHS mode for floppy ; input: ; ax - LBA sector ; output: ; ch - cylinder ; cl - sector (1-63) ; dh - head ;------------------------------------------------------- LBA_to_CHS: mov cl,SPT div cl ; al=LBA / SPT,ah=LBA % SPT ; cylinder=LBA / SPT / HPC mov ch,al shr ch,(HPC / 2) ; ch=cylinder ; head=(LBA / SPT ) % HPC mov dh,al and dh,1 ; dh=head ; sector=LBA % SPT + 1 mov cl,ah inc cl ; cl=sector ret
LBA_to_CHS()的結(jié)果是ch存放cylinder,cl存放sector,dh存放head。SPT和HPC定義在頭文件support.inc中,下面是support.inc中的部分代碼。
代碼清單3-5(inc\support.inc):
; SPT(sector per track):每磁道上的 sector 數(shù) ; HPC(head per cylinder):每個(gè) cylinder 的 head 數(shù) ; 下面是 floppy 的參數(shù) %define SPT 18 %define HPC 2
代碼清單3-4中的LBA_to_CHS()對(duì)于軟盤來(lái)說(shuō)能很好并且高效地處理,可是它有一些漏洞。
cylinder的值應(yīng)該包括cl的高2位和ch的8位,組合成共10位的cylinder值,最大是0x3FF,int 13h/ah=02h能訪問(wèn)的最大cylinder數(shù)是1024個(gè)(0~1023)。
int 13h/ax=02h功能號(hào)中能最大處理的head是256個(gè)(0~255),最大處理的sector是64個(gè)(0~63)。
8G的限制:int 13h/ah=02h最大能讀取的范圍是1024×64×256×512=8G。
對(duì)于floppy來(lái)說(shuō)不可能達(dá)到超過(guò)256個(gè)cylinder,所以代碼清單3-5中的LBA_to_CHS()可以很好地工作,現(xiàn)在的BIOS都支持int 13h的擴(kuò)展功能,代碼清單3-3中的read_sector()實(shí)際讀磁盤是交由另一個(gè)函數(shù)read_sector_extension()去做(注:這里沿用了C函數(shù)的稱呼,在匯編中應(yīng)該稱為過(guò)程)。
3.9.2 測(cè)試是否支持int 13h擴(kuò)展功能
在使用int 13h擴(kuò)展前應(yīng)該先測(cè)試BIOS是否支持int 13h的擴(kuò)展讀/寫功能,執(zhí)行測(cè)試的是check_int13h_extension(),代碼如下。
代碼清單3-6(topic03\ex3-2\boot.asm):
;-------------------------------------------------------- ; check_int13h_extension():測(cè)試是否支持 int13h 擴(kuò)展功能 ; ouput: ; 0 - support,1 - not support ;-------------------------------------------------------- check_int13h_extension: push bx mov bx,0x55aa mov ah,0x41 %ifdef UBOOT mov dl,0x80 ; for hard disk %endif int 0x13 setc al ; 失敗 jc do_check_int13h_extension_done cmp bx,0xaa55 setnz al ; 不支持 jnz do_check_int13h_extension_done test cx,1 setz al ; 不支持?jǐn)U展功能號(hào):AH=42h-44h,47h,48h do_check_int13h_extension_done: pop bx movzx ax,al ret
代碼清單3-6中的check_int13h_extension()使用int 13h/ah=41h測(cè)試是否支持int 13h擴(kuò)展讀/寫,支持則返回0,不支持則返回1。
3.9.3 使用int 13h擴(kuò)展讀磁盤
代碼清單3-7(topic03\ex3-2\boot.asm):
;-------------------------------------------------------------- ; read_sector_extension():使用擴(kuò)展功能讀扇區(qū) ; input: ; esi - sector ; di - buf (es:di) ;-------------------------------------------------------------- read_sector_extension: xor eax,eax push eax push esi ; 要讀的扇區(qū)號(hào) (LBA) - 64 位值 push es push di ; buf 緩沖區(qū) es:di - 32 位值 push word 0x01 ; 扇區(qū)數(shù),word push word 0x10 ; 結(jié)構(gòu)體 size,16 bytes mov ah,0x42 ; 擴(kuò)展功能號(hào) %ifdef UBOOT mov dl,0x80 %else mov dl,0 %endif mov si,sp ; 輸入結(jié)構(gòu)體地址 int 0x13 add sp,0x10 ret
int 13h/ah=42h擴(kuò)展功能需要一個(gè)disk address packet結(jié)構(gòu)體地址作為參數(shù),在讀之前應(yīng)該要構(gòu)造這樣一個(gè)結(jié)構(gòu)體數(shù)據(jù),這個(gè)結(jié)構(gòu)體大致如下。
typedef struct Disk_Address_Packet { short packet_size; /* struct's size */ short sectors; /* 讀多少個(gè) sector */ char *buffer; /* buffer address(segment:offset 形式)*/ long long start_sector; /* 從哪個(gè) sector 開始讀 */ } DAP;
在read_sector_extension()代碼中分別壓入start_sector、buffer、sector,以及packet_size來(lái)構(gòu)建一個(gè)結(jié)構(gòu)體數(shù)據(jù),結(jié)構(gòu)體的大小是0x10(16個(gè)字節(jié))。
代碼清單3-7中定義的drive號(hào)根據(jù)所定義的符號(hào)進(jìn)行選擇,如果命令行如下。
nasm boot.asm –o boot –d UBOOT
編譯命令預(yù)定義了一個(gè)符號(hào)UBOOT,用來(lái)開啟為U盤或硬盤進(jìn)行讀盤。
3.9.4 最后看看load_module()
最后需要介紹的是load_module(),它用來(lái)加載一個(gè)模塊。
代碼清單3-8(topic03\ex3-2\boot.asm):
;------------------------------------------------------------------- ; load_module(int module_sector,char *buf): 加載模塊到 buf 緩沖區(qū) ; input: ; esi:module_sector 模塊的扇區(qū) ; di:buf 緩沖區(qū) ; example: ; load_module(SETUP_SEG,SETUP_SECTOR); ;------------------------------------------------------------------- load_module: call read_sector ; read_sector(sector,buf) test ax,ax jnz do_load_module_done mov cx,[di] ; 讀取模塊 size test cx,cx setz al jz do_load_module_done add cx,512 - 1 shr cx,9 ; 計(jì)算 block(sectors) do_load_module_loop: call dot dec cx jz do_load_module_done inc esi add di,0x200 call read_sector test ax,ax jz do_load_module_loop do_load_module_done: ret
load_module()用來(lái)加載多個(gè)扇區(qū)數(shù)據(jù),在模塊頭部是2個(gè)字節(jié)的模塊size值。代碼中先讀出的size值,然后計(jì)算模塊占用多少sectors,再由read_sector()去讀取扇區(qū)數(shù)據(jù)。
實(shí)驗(yàn)3-3:測(cè)試分別從floppy和hard disk啟動(dòng)
分別為硬盤和軟盤讀取編譯兩個(gè)版本。
編譯硬盤啟動(dòng)版本
使用下面的編譯參數(shù)(加上–dUBOOT)。
nasm –I..\ ex3-2\boot.asm –o ex3-2\uboot –d UBOOT
輸出文件名為uboot,放在ex3-2\目錄下。
編譯軟盤啟動(dòng)版本
nasm –I..\ ex3-2\boot.asm
接著使用merge工具寫入demo.img和c.img映像文件中,demo.img是軟盤映像,而c.img是硬盤映像。當(dāng)然,需要在config.txt配置文件中寫入相應(yīng)的配置,然后執(zhí)行merge命令。
entry #0: boot ---> demo.img: success entry #1: setup ---> demo.img: success entry #2: uboot ---> c.img: success entry #3: setup ---> c.img: success
我們?cè)贐ochs的配置文件里分別用floppy和hard disk映像進(jìn)行啟動(dòng),然后測(cè)試它們的運(yùn)行結(jié)果,以下是使用floppy映像啟動(dòng)的結(jié)果。
而以下是使用硬盤映像啟動(dòng)的結(jié)果,注意它們顯示的最后一行信息是不同的。
對(duì)比一下這里的運(yùn)行結(jié)果和實(shí)驗(yàn)3-2在真實(shí)機(jī)器上測(cè)試的結(jié)果是否一致。
是的,它們是一致的,U盤在使用USB-HDD模式時(shí)和硬盤的效果是一樣的。
3.10 總結(jié)
這一章里的所有源碼都在topic03\目錄下,有完全的實(shí)驗(yàn)環(huán)境,包括以下內(nèi)容。
- boot.asm源碼和編譯后的boot模塊。
- setup.asm源碼和編譯后的setup模塊。
- Bochs配置文件bs,Bochs的版本是2.5.1。
- merge工具的配置文件config.txt。
- demo.img映像文件(1.44MB軟盤)。
- c.img 映像文件。
- Div+CSS 3.0網(wǎng)頁(yè)布局案例精粹
- 自動(dòng)控制原理
- Blockchain Quick Start Guide
- 數(shù)據(jù)挖掘方法及天體光譜挖掘技術(shù)
- Windows游戲程序設(shè)計(jì)基礎(chǔ)
- Troubleshooting OpenVPN
- Visual FoxPro數(shù)據(jù)庫(kù)基礎(chǔ)及應(yīng)用
- Practical Big Data Analytics
- 計(jì)算機(jī)組成與操作系統(tǒng)
- 一步步寫嵌入式操作系統(tǒng)
- 貫通Hibernate開發(fā)
- 與人共融機(jī)器人的關(guān)節(jié)力矩測(cè)量技術(shù)
- 樂(lè)高創(chuàng)意機(jī)器人教程(中級(jí) 上冊(cè) 10~16歲) (青少年iCAN+創(chuàng)新創(chuàng)意實(shí)踐指導(dǎo)叢書)
- 機(jī)器人剛?cè)狁詈蟿?dòng)力學(xué)
- 工業(yè)機(jī)器人與自控系統(tǒng)的集成應(yīng)用