加载Loader.bin
1、突破512字節(jié)的限制
2、加載Loader進入內存
一、突破512字節(jié)的限制
一個操作系統(tǒng)從開機到開始運行,大致經歷"引導—》加載內核入內存—》跳入保護模式—》開始執(zhí)行內核"這樣一個過程。也就是說,在內核開始執(zhí)行之前不但要加載內核,還要準備保護模式等一系列工作,如果全部交給引導扇區(qū)來做,512字節(jié)很可能不夠用,所以,不放把這個過程交給另外的模塊來完成,我們把這個模塊叫做Loader。引導扇區(qū)負責把Loader加載如內存并且把控制權交它,其他的工作放心地交給 Loader來做,因為它沒有512字節(jié)的限制,將會靈活很多。
二、加載Loader進入內存
上一節(jié)我們已經詳細介紹了FAT12文件系統(tǒng)的數據結構,下面我們需要思考的是兩個問題:1、引導扇區(qū)通過怎樣的步驟才能找到文件;2、如何能夠把文件內容全都讀出來并加載進入內存。
下面我們先解決第一個問題:
1、? 如何讀取軟盤?
(1)????我們需要使用BIOS中斷int 13h來讀取軟盤。它的用法如下表所示:
在這里我們只介紹了2種工作方式,中斷int 13h還有其他的工作方式,如果需要可以自行查看內容。
(2)????由上表我們可以知道:當讀取某些扇區(qū)時,需要柱面(磁道)號(ch),起始扇區(qū)號(cl),磁頭號(dh)。我們如何通過計算得到這些數據呢?
(3)????現(xiàn)在萬事俱備只欠東風了,下面我們就書寫讀取軟盤扇區(qū)的函數ReadSector。
首先我們要知道該函數需要什么參數,這些參數存儲在什么位置?
參數1:扇區(qū)號,存儲在ax中
參數2:要讀取扇區(qū)的個數,存儲在cl中
參數3:數據緩沖區(qū),即讀取扇區(qū)數據后,將其存儲在什么位置,用es:bx指向該緩沖區(qū)。
即函數的作用:從第ax個Sector開始,將cl個Sector讀入es:bx中。
2、? 如何在軟盤中尋找Loader.bin文件
(1)????結合上一節(jié)所介紹的FAT12數據結構,從中我們可以知道,要尋找一個文件,首先需要在根目錄區(qū)中尋找該文件的根目錄條目;然后根據根目錄條目獲取文件開始簇數(也就是在數據區(qū)中存儲的扇區(qū));最后讀取文件內容到內存。
(2)????嗯,是的,下面就讓我們來完成第一步-----在根目錄區(qū)中尋找該文件的根目錄條目。
讓我們開始思考這個問題,
首先要知道根目錄區(qū)的開始扇區(qū)號是19,也就是說從第19扇區(qū)開始,根目錄區(qū)占用扇區(qū)總數為14,也就是說,如果不能發(fā)現(xiàn)Loader.bin,需要將14個扇區(qū)都進行查找,于是需要一個大的循環(huán)在外圍,控制著扇區(qū)的讀取。
緊接著,我們每讀取一個扇區(qū),一個扇區(qū)是512個字節(jié),一個根目錄條目占32個字節(jié),故一個扇區(qū)中存在512/32=16個根目錄條目,所以需要添加一個循環(huán),控制根目錄條目的變化,從0—16進行循環(huán)。
最后,針對每一個根目錄條目,我們只是要比較文件名,一個根目錄條目的文件名占用11個字節(jié),所以需要對每一個字節(jié)與"LOADER?? BIN"中的每一個字節(jié)進行比較,所以還是要添加一個循環(huán),來控制字符的變化,即從0—11.
用C語言來表示該問題就是:
for( i = 根目錄區(qū)的起始扇區(qū)號(19); i < 根目錄區(qū)占有的扇區(qū)數(14);? i++)????? {
?????????? for( j = 0;j < 一個扇區(qū)內存儲的根目錄條目個數(512/32=16); j++)??? {
??????????????????? for(k =0; k < 根目錄條目中文件名占用的空間(11個字符); k++) ???? {
???????????????????????????? if(k=10)jmp LABEL_FILENAMEFOUND
???????????????????????????? if(ds:si= es:di) si++; di++;
???????????????????????????? else? break;
??????????????????? }
?????????? }
}
(3)????下面讓我們來分析代碼:
首先需要介紹下面可能需要用到的幾個變量值:
BaseOfLoader?????????? equ? 09000h???? ;LOADER.BIN被加載到的位置---段地址
OffsetOfLoader??????? equ? 0100h?????? ;LOADER.BIN被加載到的位置---偏移地址
RootDirSectors???????? equ? 14???? ;根目錄占用空間(BPB_RootEntCnt*32+511)/512
SectorNoOfRootDirectory??????? equ? 19???? ;Root Directory 的第一個扇區(qū)號
;變量
wRootDirSizeForLoop????? dw??? RootDirSectors???????? ;Root Directory占用的扇區(qū)數,在循環(huán)中會遞減至0
wSectorNo???????????????? dw??? 0??????????????? ;要讀取的扇區(qū)號
bOdd?????????????????????????? db???? 0??????????????? ;奇數還是偶數
;字符串
LoaderFileName?????? db???? "LOADER? BIN",???? 0?????? ;LOADER.BIN之文件名
;調用中斷int 13h,實現(xiàn)軟驅復位 xor ah, ah xor dl, dl int 13h ;下面在A盤的根目錄中尋找LOADER.BIN ;wSectorNo表示要讀取的扇區(qū)號,SectorNoOfRootDirectory ;表示根目錄區(qū)的開始扇區(qū)號=19 mov word[wSectorNo], SectorNoOfRootDirectory LABEL_SEARCH_IN_ROOT_DIR_BEGIN: ;wRootDirSizeForLoop=RootDirSectors,表示根目錄占用的扇區(qū)數,即表示要讀取的扇區(qū)數;也就是 ;最外部循環(huán)中的控制變量(相當于i)。 ;判斷根目錄區(qū)所有扇區(qū)是不是已經讀取完畢,如果讀完表示沒有找到LOADER.BIN, ;跳入到LABEL_NO_LOADERBIN,否則,減1。 cmp word[wRootDirSizeForLoop], 0 jz LABEL_NO_LOADERBIN dec word[wRootDirSizeForLoop] ;為ReadSector函數準備參數,從第ax個Sector開始讀,將cl個Sector讀入es:bx中 mov ax, BaseOfLoader mov es, ax ;es<-BaseOfLoader mov bx, OffsetOfLoader ;bx<-OffsetOfLoader,于是es:bx=BaseOfLoader:OffsetOfLoader mov ax, [wSectorNo] ;ax<-Root Directory中的某Sector號,表示要讀取的扇區(qū)號 mov cl, 1 ;cl表示要讀取扇區(qū)個數=1 call ReadSector ;調用ReadSector函數之后,es:bx將存儲該扇區(qū)數據。 mov si, LoaderFileName ;ds:si->"LOADER BIN" mov di, OffsetOfLoader ;es:di->BaseOfLoader:OffsetOfLoader=es:bx ;即es:di指向存儲的該扇區(qū)數據 cld ;一個扇區(qū)是512個字節(jié),一個根目錄項占32個字節(jié),故512/32=16,因此需要比較16個根目錄項的文件名, ;故賦值dx=16,由dx來控制循環(huán)次數 mov dx, 10h LABEL_SEARCH_FOR_LOADERBIN: ;判斷dx是否為0,0意味著這個扇區(qū)內的所有根目錄項進行比較完畢,然后跳入到下一個扇區(qū),繼續(xù)進行比較, ;dx=0,則跳入LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR;否則,dx-- cmp dx, 0 jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR dec dx ;一個根目錄項的文件名占用11個字節(jié),故必須對其每個字節(jié)與"LOADER BIN"一一對比 ;故賦值cx=11,由cx來控制循環(huán)次數 mov cx, 11 LABEL_CMP_FILENAME: cmp cx, 0 jz LABEL_FILENAME_FOUND ;如果cx=0,意味著11個字符都相等,表示找到,跳轉到LABEL_FILENAME_FOUND dec cx ;否則,cx-- lodsb ;ds:si->al,ds:si指向的是字符串"LOADER BIN" cmp al, byte[es:di] ;進行一個字符的比較,如果相等,則比較下一個字符, jz LABEL_GO_ON ;跳入到LABEL_GO_ON jmp LABEL_DIFFERENT ;只要發(fā)現(xiàn)有一個不相等的字符就表明本Directory Entry不是我們要 ;找的LOADER.BIN,跳轉到LABEL_DIFFERENT,進如下一個Directory Entry比較。 LABEL_GO_ON: inc di ;將di++,進行一個字符的比較。 jmp LABEL_CMP_FILENAME ;跳轉到LABEL_CMP_FILENAME,繼續(xù)進行文件名比較。 LABEL_DIFFERENT: ;di&=E0是為了讓它指向本條目開頭,di初始化為某個條目開頭, ;在比較過程中,會將它不斷增1,當失敗之后,必須進行重新初始化 ;因為一個條目占用32個字節(jié),故and di,0FFE0h add di, 20h ;之后,di就指向了下一個條目 and di, 0FFE0h add di, 20h ;重新初始化si,使其指向"LOADER BIN"的開始位置 mov si, LoaderFileName jmp LABEL_SEARCH_FOR_LOADERBIN ;跳轉到LABEL_SEARCH_FOR_LOADERBIN LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR: add word[wSectorNo], 1 ;將要讀取的扇區(qū)號+1,進行下一個扇區(qū)的比較 jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN ;跳轉到LABEL_SEARCH_IN_ROOT_DIR_BEGIN,開始一個扇區(qū)的比較 ;如果最后沒有找到"LOADER BIN",則顯示“NO LOADER”字符串來表示。 LABEL_NO_LOADERBIN: mov dh, 2 ;"NO LOADER" call DispStr ;顯示字符串 %ifdef _BOOT_DEBUG_ mov dh, 2 ;"NO LOADER" call DispStr ;顯示字符串 mov ax, 4C00h int 21h ;沒有找到LOADER.BIN,返回到DOS %else jmp $ ;沒有找到LOADER.BIN,死循環(huán)在這里 %endif ;如果找到"LOADER BIN",則跳轉到LABEL_FILENAME_FOUNT,然后進行第二步驟,從 ;Directory Entry中讀取文件在數據區(qū)的開始簇號。 LABEL_FILENAME_FOUND:
(4)????對上面這段代碼畫出它的簡易流程圖如下:
3、? 如何將Loader.bin文件加載到內存?
現(xiàn)在我們已經有了Loader.bin的起始扇區(qū)號,我們需要用這個扇區(qū)號來做兩件事情:一件是把起始扇區(qū)裝入內存,另一件則是通過它找到FAT中的項,從而找到Loader占用的其余所有扇區(qū)。
此時裝入一個扇區(qū)對我們來說已經是很輕松的事了,可從FAT中找到一個項還是多少有些麻煩,下面我們就根據扇區(qū)號去FAT表中找到相應的項。在這里,將要寫一個函數GetFATEntry,函數的輸入就是扇區(qū)號(ax),輸出則是其對應的FAT項的值(ax)。
我們一起來思考這個函數如何去實現(xiàn),我們知道了扇區(qū)號x,然后我們去FAT1中尋找x所對應的FATEntry,我們已經知道一個FAT項占1.5個字節(jié)。所以我們用x*3/2=y………z,y為商(偏移量)(字節(jié)),相對于FAT1的初始位置的偏移量;Z為余數(0或者1),是判斷FATEntry是奇數還是偶數,0表示偶數,1表示奇數。然后我們讓y/512=m………n,m為商,n為余數,此時m為FATEntry所在的相對扇區(qū),n為在此扇區(qū)內的偏移量(字節(jié))。因為FAT1表前面還有1個引導扇區(qū),所以FATEntry所在的實際扇區(qū)號為m+1。然后讀取m+1和m+2兩個扇區(qū),然后在偏移n個字節(jié)處,取出FATEntry,相當于讀取兩個字節(jié)。此時再利用z,如果z為0的話,此FAT項為前一個字節(jié)和后一個字節(jié)的后4位,如果z為1的話,此FATEntry取前一個字節(jié)的前4位和后一個字節(jié)。
下面我們實現(xiàn)GetFATEntry函數,函數的輸入就是扇區(qū)號,輸出則是其對應的FATEntry的值。
總結
以上是生活随笔為你收集整理的加载Loader.bin的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sudo mount -o loop p
- 下一篇: 保护模式初步理解