作業系統開發:BIOS/MBR 引導
該系列文章是在學習《作業系統真相還原》時通過自己的話加以理解總結的筆記,首先,致敬作者-鄭剛!在讀本書時不得不佩服,作者底層功力的深厚,不愧是北大的學子,其講解的也通俗易懂,十分詳細,我會努力把它學好,學精,並加以改進,做出一款自己DIY的作業系統。
BIOS 軟體接力第一棒
BIOS 基本輸入輸出系統,BIOS程式碼所做的工作是一成不變的,所以他是被固化到ROM中的一塊只讀區域中,在開機時此ROM會被對映到低端1MB記憶體的頂部,原因是系統在開啟時預設是實地址模式(該模式最大定址範圍0-fffff),所以其定址範圍也就被限制在了0xF0000-x0xFFFFF
區域中,這64KB的記憶體就是BIOS的執行程式碼.
在開機的一瞬間,CPU的CS:IP暫存器會被強制初始化為0xF000:0xFFF0
,在實地址模式下該地址需要乘以16也就是左移四位加上偏移地址得到,於是0xF000:0xFFF0
就等效於0xFFFF0
此處的地址距離0xFFFFF
只有16個位元組的空間,裡面存放著一條jmp far f000:e05b = fe05b
的彙編指令,該指令將跳轉到真正的BIOS開始的位置.
接著BIOS將會通過自身的程式碼對硬體進行自檢測,在初始化硬體後,則開始向記憶體0x000-0x3ff
中初始化資料結構以及拷貝中斷向量表,緊接著BIOS將會通過呼叫int 19h
中斷,此中斷用以檢測計算機中的硬碟,如果檢測到0盤0道1扇區末尾的兩個位元組是0x55,0xaa
則認為此扇區確實存在,於是就會將此區域中的內容,載入到記憶體7c00的位置,並通過一條jmp far 0:0x7c00h
MBR 收到跳轉來源,繼續執行。
此處的7c000就是MBR程式碼的開始位置,之所以是7C00是因為,DOS中要求最小記憶體是32KB,而MBR大小必須是512位元組(1KB),所以選擇32kB中的最後1KB的位置最為合適,32KB(0x8000)-1KB(0x400)=>0x7c00
,這就是7C00的由來,同時還需要保證第510-511位元組必須為0x55,0xaa
才可以.
儲存以下彙編程式碼,並使用 nasm -o mbr.bin mbr.asm
編譯簡易版MBR檔案.
SECTION MBR vstart=0x7c00 ; 告訴編譯器載入到7c00記憶體處 mov ax,cs mov ds,ax mov es,ax mov ss,ax mov fs,ax mov sp,0x7c00 mov ax,Message mov bp,ax ; 儲存字串地址 mov cx,15 ; 儲存字串長度 mov ax,01301h ; 子功能號13是顯示字元及屬性 mov bx,000ch ; 頁號位0,使用黑色為背景色,紅色為字型顏色 mov dl,0 int 10h ; 10h中斷,用來顯示字元 ret Message: db "hello lyshark !" times 510-($-$$) db 0 ; 填充510位元組為0 db 0x55,0xaa ; mbr的結束標誌
進入Bochs目錄下執行bximage.exe
生成一個映像檔案,預設是a.img,並將編譯好的mbr.bin寫入到映象中
dd if=mbr.bin of=a.img bs=512 count=1 conv=notrunc
在Bochs目錄下新建並編輯bosh.src儲存,然後執行bochs.exe -f bosh.src
模擬執行MBR程式碼.
megs:32
romimage:file=$BXSHARE/BIOS-bochs-latest
vgaromimage:file=$BXSHARE/VGABIOS-lgpl-latest
floppya:1_44=a.img,status=inserted
boot:floppy
log:bochsout.txt
mouse:enabled=0
keyboard: keymap=$BXSHARE/keymaps/x11-pc-de.map
上方螢幕會比較混亂,這裡我們先來進行清屏操作,清屏中斷呼叫也是int10
SECTION MBR vstart=0x7c00 ; 告訴編譯器載入到7c00記憶體處
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0x600 ; 清屏範圍,也就是寬度
mov bx,0x0
mov cx,0x0 ; 清屏 左上角(0,0)
mov dx,0x184f ; 清屏 右下角(80=0x4f,25=0x18)
int 0x10
mov ax,Message
mov bp,ax ; 儲存字串地址
mov cx,15 ; 儲存字串長度
mov ax,01301h ; 子功能號13是顯示字元及屬性
mov bx,000ch ; 頁號位0,使用黑色為背景色,紅色為字型顏色
mov dl,0
int 10h ; 呼叫10h號中斷,用來顯示字元
ret
Message: db "hello lyshark !"
times 510-($-$$) db 0 ; 填充510位元組為0
db 0x55,0xaa ; mbr的結束標誌
執行結果,如下,但是,列印字串,在底部,因為游標在底部。
設定游標到頂部,這裡百度一下游標中斷,發現了。
接著改進程式碼
SECTION MBR vstart=0x7c00 ; 告訴編譯器載入到7c00記憶體處
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0x600 ; 清屏範圍,也就是寬度
mov bx,0x0
mov cx,0x0 ; 清屏 左上角(0,0)
mov dx,0x184f ; 清屏 右下角(80=0x4f,25=0x18)
int 0x10
mov dh,0x0 ; 設定游標列號
mov dl,0x0 ; 設定游標行號
mov bh,0x0 ; 頁碼
int 0x10
mov ax,Message
mov bp,ax ; 儲存字串地址
mov cx,15 ; 儲存字串長度
mov ax,01301h ; 子功能號13是顯示字元及屬性
mov bx,000ch ; 頁號位0,使用黑色為背景色,紅色為字型顏色
mov dl,0
int 10h ; 呼叫10h號中斷,用來顯示字元
ret
Message: db "hello lyshark !"
times 510-($-$$) db 0 ; 填充剩餘的510位元組的空間為0
db 0x55,0xaa ; mbr的結束標誌
完美結果。
讓我們對顯示卡說點什麼?
上面我們通過呼叫BIOS提供的int 0x10
中斷來實現列印字元操作,但我們在後期必須要藉助顯示卡來輸出影象,而顯示卡是外部裝置,必須通過匯流排來操作。
由於CPU使用的訊號是TTL電平,而外部裝置都是機械裝置,故他們不會使用該電平驅動,這就導致CPU與硬體裝置沒有辦法實現溝通,硬體工程師們提供的方法是,在這兩者之間架起一座橋,也就是在CPU和外設之間加上一層IO介面,該介面的作用就是實現CPU和外設之間相互做協調轉換。
其次外部裝置的種類也是多種多樣的,其輸出的訊號可能是數字訊號,也可能是模擬訊號,而我們的CPU只能處理數字訊號,數字訊號需要經過數模轉換器<D/A>
成模擬量才能送到外設來驅動硬體工作,模擬量也同樣需要經過模數轉換器<A/D>
轉換成數字量才能被CPU直接處理,所以介面電路中需要包括A/D轉換器和D/A轉換器。
由於CPU使用的訊號是TTL電平,而外部裝置都是機械裝置,故他們不會使用該電平驅動,這就導致CPU與硬體裝置沒有辦法實現溝通,硬體工程師們提供的方法是,在這兩者之間架起一座橋,也就是在CPU和外設之間加上一層IO介面,該介面的作用就是實現CPU和外設之間相互做協調轉換。
其次外部裝置的種類也是多種多樣的,其輸出的訊號可能是數字訊號,也可能是模擬訊號,而我們的CPU只能處理數字訊號,數字訊號需要經過數模轉換器<D/A>
成模擬量才能送到外設來驅動硬體工作,模擬量也同樣需要經過模數轉換器<A/D>
轉換成數字量才能被CPU直接處理,所以介面電路中需要包括A/D轉換器和D/A轉換器。
SECTION MBR vstart=0x7c00 ; 告訴編譯器載入到7c00記憶體處
mov ax,cs
mov sp,0x7c00
mov ax,0xb800
mov gs,ax
mov ax,0x600 ; 清屏範圍,也就是寬度
mov bx,0x0
mov cx,0x0 ; 清屏 左上角(0,0)
mov dx,0x184f ; 清屏 右下角(80=0x4f,25=0x18)
int 0x10
mov dh,0x0 ; 設定游標列號
mov dl,0x0 ; 設定游標行號
mov bh,0x0 ; 頁碼
int 0x10
mov byte [gs:0x00],'M'
mov byte [gs:0x01],0xa4 ; 顯示A=綠色閃爍 4=紅色
mov byte [gs:0x02],'B'
mov byte [gs:0x03],0xa5
mov byte [gs:0x04],'R'
mov byte [gs:0x05],0xa6
ret
times 510-($-$$) db 0 ; 填充剩餘的510位元組的空間為0
db 0x55,0xaa ; mbr的結束標誌