【OS学习笔记】六 实模式:编写主引导扇区代码
上一篇文章學習了:計算機的啟動過程(點擊鏈接查看上一篇文章)
這篇文章學習記錄為:編寫主引導扇區代碼。
參考:《X86匯編語言-從實模式到保護模式》-李忠。純學習筆記,更詳細內容請閱讀正版書籍。如有侵權請聯系我刪除文章。實際上,從這篇文章開始,我們才開始進入到實模式的學習。之前的五篇文章,都是預備的學習知識。點擊下面鏈接復習相關的預備知識:
1、回顧主引導扇區
在前面的學習中,我們知道在計算機重新啟動后,如果硬盤是首選的啟動設備,那么處理器就會跳轉到硬盤的0面0道1扇區去執行代碼。這里成為主引導扇區。
主引導扇區的大小為512字節、ROM-BIOS將它加載到處理器的地址空間的邏輯地址0x0000:0x7c00處,也就是物理地址0x07c00處,然后判斷它是否有效。
而判斷一個主引導扇區是否有效的方法是判斷它最后的兩個字節是否是0x55和0xAA。ROM-BIOS首先檢測這兩個位置是否正確,如果正確,則以一個段間轉移指令jmp 0x0000:0x7c00處執行代碼。
一般來說,主引導扇區的代碼,負責計算出操作系統所在的硬盤位置,然后將操作系統的自舉代碼加載到內存,也用一個jmp指令跳轉到那里繼續執行,直到操作系統完成啟動。
我們本篇文章的主要內容就是,編寫一段代碼,將它寫到主引導扇區,讓處理器執行。為了更加明顯的顯示我們的代碼是正確的,我們選擇在屏幕上顯示一行字符串。
2、在屏幕上顯示文字
在編寫代碼之前,我們首先來了解一下如何在屏幕上顯示文字。
請注意,這里可不是使用printf或者cout或者System.out.println的地方。我們這里是在沒有操作系統的情況下,想要在顯示屏上顯示文字。
想要顯示文字,就要把想要顯示的內容寫到顯存即可。顯存是什么?也是一種存儲器,只不過專門存儲需要在顯示器上顯示的內容的。其他詳細原理自己百度吧,或者看本文的參考書籍,有詳細的解釋。
如下圖所示,是一個字符在屏幕上顯示的簡單的原理圖:
處理器為了直接訪問顯存,將顯存映射到處理器的尋址空間中。如下圖:
我們知道8086可以訪問1M的內存空間。其中0x00000-0x9FFFF屬于常規內存,由內存條提供。0xF0000-0xFFFFF由主板上的ROM-BIOS提供。
中間還剩余的320KB的空洞,即0xA0000-0xEFFFF,這段空間就由外設來提供,其中就包括顯卡的顯存部分。
由于歷史原因,一直以來0xB8000-0xBFFFF這段物理地址空間,是留給顯卡的。
3、分析主引導扇區代碼
這段代碼是本文參考書籍的代碼,先把代碼貼上,不算長,如果看不懂,不要被嚇跑了。下面的分析,肯定可以讓你明白這個程序的意思。
1 ;代碼清單5-1 2 ;文件名:c05_mbr.asm3 ;文件說明:硬盤主引導扇區代碼4 ;創建日期:2011-3-31 21:15 5 6 mov ax,0xb800 ;指向文本模式的顯示緩沖區,顯存的段地址,7 mov es,ax ;一般用DS段寄存器,但是DS有其他用處,這里我們使用ES寄存器8 9 ;以下是顯示字符串"Label offset:"10 mov byte [es:0x00],'L'11 mov byte [es:0x01],0x0712 mov byte [es:0x02],'a'13 mov byte [es:0x03],0x0714 mov byte [es:0x04],'b'15 mov byte [es:0x05],0x0716 mov byte [es:0x06],'e'17 mov byte [es:0x07],0x0718 mov byte [es:0x08],'l'19 mov byte [es:0x09],0x0720 mov byte [es:0x0a],' '21 mov byte [es:0x0b],0x0722 mov byte [es:0x0c],"o"23 mov byte [es:0x0d],0x0724 mov byte [es:0x0e],'f'25 mov byte [es:0x0f],0x0726 mov byte [es:0x10],'f'27 mov byte [es:0x11],0x0728 mov byte [es:0x12],'s'29 mov byte [es:0x13],0x0730 mov byte [es:0x14],'e'31 mov byte [es:0x15],0x0732 mov byte [es:0x16],'t'33 mov byte [es:0x17],0x0734 mov byte [es:0x18],':'35 mov byte [es:0x19],0x0736 37 mov ax,number ;取得標號number的偏移地址38 mov bx,1039 40 ;設置數據段的基地址,只是在同一個段,偏移地址是不一樣的41 mov cx,cs42 mov ds,cx43 44 ;求個位上的數字45 mov dx,046 div bx47 mov [0x7c00+number+0x00],dl ;保存個位上的數字48 49 ;求十位上的數字50 xor dx,dx51 div bx52 mov [0x7c00+number+0x01],dl ;保存十位上的數字53 54 ;求百位上的數字55 xor dx,dx56 div bx57 mov [0x7c00+number+0x02],dl ;保存百位上的數字58 59 ;求千位上的數字60 xor dx,dx61 div bx62 mov [0x7c00+number+0x03],dl ;保存千位上的數字63 64 ;求萬位上的數字 65 xor dx,dx66 div bx67 mov [0x7c00+number+0x04],dl ;保存萬位上的數字68 69 ;以下用十進制顯示標號的偏移地址70 mov al,[0x7c00+number+0x04]71 add al,0x3072 mov [es:0x1a],al ;將al寄存器中的ASCII數字傳送到顯示緩沖區73 mov byte [es:0x1b],0x04 ;下一字節存放顯示屬性,0x04代表:黑底紅字,無閃爍,無加亮74 75 mov al,[0x7c00+number+0x03]76 add al,0x3077 mov [es:0x1c],al78 mov byte [es:0x1d],0x0479 80 mov al,[0x7c00+number+0x02]81 add al,0x3082 mov [es:0x1e],al83 mov byte [es:0x1f],0x0484 85 mov al,[0x7c00+number+0x01]86 add al,0x3087 mov [es:0x20],al88 mov byte [es:0x21],0x0489 90 mov al,[0x7c00+number+0x00]91 add al,0x3092 mov [es:0x22],al93 mov byte [es:0x23],0x0494 95 mov byte [es:0x24],'D'96 mov byte [es:0x25],0x0797 98 infi: jmp near infi ;無限循環,防止處理器再接著取下面的數據,數據當成指令取執行會導致錯誤或運行不正常99 100 number db 0,0,0,0,0 101 102 times 203 db 0 103 db 0x55,0xaa那么為什么每將一個字符傳送到顯存后,后面要繼續傳動一個0x07呢?實際上是這樣的:
顯存中,每一個字符的ASCII碼后面跟的是該字符的顯示屬性。包括字符的顏色和背景色。如下圖:
在8086下,80x25文本模式下的顏色表如下:
由以上可知,我們顯示的字符屬性是0x07,黑底白字,無閃爍,無加亮。也就是黑底白字。
10行-35行依次將字符寫入到緩存中,后面依次寫入字符的屬性。這很好理解!!!
實際上一個程序經過編譯后,編譯器會給每一條代碼一個匯編地址,這個匯編地址實際上是從0開始。
在分段機制中,偏移地址也是從0開始。實際上,這個匯編地址就是與偏移地址是對應的。如下圖:
理解了什么是匯編地址與偏移地址的關系后(不理解的看原書第五章),我們就來將number處的匯編地址在屏幕上顯示出來。
number就代表那個地址的值。我這里已經提前知道這個地址是:0x012E也就是十進制302。
由之前的學習內容知道直接將302傳送到顯存的話,是不可能在屏幕上顯示302的。我們只能將302進行拆分,將每一個數位都拆解出來,一個一個傳送給顯存。如何拆解?每次除以10…太簡單了就不寫了。
我們既然想將number的匯編地址分解為一個個數位,就得找一個地方,將分解后的數字先咱是存起來。你可以想到用寄存器先存起來,但是寄存器,畢竟就那么8個通用的寄存器,而且本段代碼也用了好幾個了,所以這里無法使用寄存器來暫時存我們的數據。
一個辦法就是在內存找到一個地方,來存儲。這里,我們的主引導扇區是512字節,我們寫的代碼很少不到300字節,所以我們選擇在主引導扇區的最后先開辟一個空間用于存儲number的分解后的數字。
那么第100行,就定義了五字節的數據,賦值為0。當然你也可以賦值為其他值,反正后面是呀被覆蓋的。
41-42行:我們將DS寄存器指向代碼段,就是讓數據段寄存器DS與代碼段寄存器CS保持一致。因為我們這里將數據與代碼都放到一個段里面了,所以數據段與代碼段是一個段(正常不能不放到一個段,我們初學,先這么寫,后面會分段) 其實用CS來訪問數據也可以,但是我們還是習慣用DS來訪問數據,所以這里就有這么兩句賦值代碼。
44-67行:求numberi的各個數位的數字,然后存到我們預先開辟好的空間中。
70-93行:先將各個數位轉化成十進制顯示,然后送入到顯存,在每一個字符后面寫入顯示屬性0x04,代表黑底紅字。
95-96行:顯示字符D 以代表我們前面顯示的number地址是10進制顯示的。黑底白字。
98行:無限循環,防止處理器再接著取下面的數據,數據當成指令取與執行會導致錯誤或運行不正常
102行:由于主引導扇區是512字節的,我們寫的程序并沒有達到512字節。所以我們應該將主引導扇區未滿的地方填滿。我們這里采取了一些特殊手段得知有203字節未填滿,搜易我們了連續聲明203個字節用于存儲0. 至于使用了什么特殊手段,不必要知道,因為后面的學習中會學習使用正常的的手段來得知這個未填滿的字節有多少。
103行:一個有效的主引導扇區,它的最后必須是0x55和0xaa。
4、編譯主引導扇區代碼并加載運行
在上一篇文章,我們已經安裝了VirtualBox 虛擬機軟件,并在里面創建了一臺名為LEARNASM的虛擬計算機。除此之外,還為它創建了一塊虛擬硬盤。
然后我們參考書上4.2.4節的內容,將我們匯編代碼編譯好的二進制bin文件寫到虛擬硬盤的主引導扇區中。啟動虛擬機,就會運行我們寫的代碼,運行結果如下:
今天的程序運行的很順利。
5、總結
了解匯編的運行機制,對以后深入學習高級語言,很有幫助:比如JVM。
筆記記得不是很全,像匯編的語法以及如何將代碼寫到虛擬硬盤的主引導扇區這些都沒有寫。如果又不懂的可以加我聯系方式一起交流。
學習探討加個人:
qq:1126137994
微信:liu1126137994
總結
以上是生活随笔為你收集整理的【OS学习笔记】六 实模式:编写主引导扇区代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【数据结构学习之完全从零实现所有数据结构
- 下一篇: swap关于指针的使用