天天看點

Foxdisk05-啟動原理2

(請保留 -> 作者:羅冰 )

觀察Foxdisk3的makefile,可以看到使用的是bcc -ms的編譯開關。這是要求編譯器采用Samll記憶體模式進行編譯,其特點是棧和資料都在64K以内,Code在另外一個64K内。為什麼采用Small方式?主要是我當時設計的時候,沒有考慮到後面的各種新的需求,以為代碼可以控制在64K内完成;另外,這種模式比較便于彙編和C的混合編寫,不用去考慮複雜的資料段、代碼段的定位。

Foxdisk05-啟動原理2

圖1 Makefile節選

在整個代碼編寫過程中,除了引導部分(也即Loader.asm),其他的代碼盡量用C編寫。但是有兩部分的代碼仍舊無法避免使用彙編,一是時鐘中斷,為了讓幾個任務并行工作,使用了彙編;二是32位數的乘除計算。我們在平常使用C/C++及其他進階語言的時候,大數的計算很平常。那是因為編譯器已經提供了庫函數調用,友善程式員使用,作業系統已經提供了這樣的機制進行支援。Foxdisk某種程度上相當于簡單的作業系統,這些機制完全沒有提供。是以,在進行大數計算時,隻能自己來寫這些函數了。

回到我們的啟動話題。Loader.asm包含了所有啟動的秘密,其承載了将Foxdisk的演出場景安排好的任務,相當于整個程式的導演,也是整個程式最難讀的地方。我試着以自己的語言,将其原了解釋清楚。

Loader.asm在編譯後,将連結成一段512位元組的二進制碼。當然,它不是單獨存在的,是與其他代碼混雜着存在于Foxdisk.exe中。這段512位元組碼将由Setup.c中的main()函數拷貝到MBR區,起到引導的作用。

Bios在釋放控制權的時候,将把MBR區的代碼加載到0x7C00處,并跳轉執行。也即從Loader.asm中的load_start處開始執行。

整個Loader.asm實作的功能可以概括為三個:1) 将存儲在硬碟上的Foxdisk代碼拷貝到指定的記憶體;2)将Foxdisk運作的資料拷貝到另外一段64K的記憶體中;3) 設定棧,并遠端跳轉(retf)至Foxdisk的BootEntry處,開始運作Foxdisk。Loader.asm中的幾個變量,nCodeSect:Foxdisk代碼段占用的硬碟扇區數;nDataSect:Foxdisk資料段占用的扇區數;iBegin: Foxdisk程式鏡像所存儲的硬碟起始位置;這些變量均由Setup.c中的函數進行初始化,也即安裝的時候确定。另外一個變量iDisk,其值為0x80或者0x81,表示BIOS所認的第一個硬碟還是第二個硬碟,也是由Setup.c中函數對其進行初始化的。如果Foxdisk鏡像存儲在第二個硬碟上,則必須設定為0x81。現在想來,其作用不大,而且容易給調試帶來麻煩。

load_start到load_chs,嘗試使用擴充中斷0x13(ah=0x40以上的int 0x13)通路磁盤中的資料,加載Foxdisk的代碼段至0x7000:0起始的64K記憶體,資料段加載至0x8000:0開始的64K記憶體。

如果磁盤不支援磁盤的擴充中斷(這種情況基本不可能),則回到原始的硬碟通路中斷,嘗試将Foxdisk的代碼段和資料段加載到指定的記憶體中。從load_chs到load_ok間做的就是這件事,其功能與上一段其實是相同的,大部分情況下也不會執行。

從标志load_ok開始,進行設定棧以及跳轉的工作。Retf是條遠跳轉指令,它将棧中的4個位元組當成CS:IP,跳轉過去執行。以上工作均在實模式下進行,不用考慮複雜的權限問題。注意,在retf執行前,bx中已經包含了BootEntry的位址,執行前的push bx就是将位址壓棧,準備遠端跳轉。

至此,我們可以暫時抛去晦澀難懂的彙編,進入了C的世界。

下一篇部落格是啟動原理的最後一篇,介紹Foxdisk的整體邏輯架構,從邏輯層面介紹我是如何編排實作Foxdisk的功能。

繼續閱讀