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