x86 從實模式到保護模式 學習筆記(1)


第五章 編寫主引導扇區代碼

本章介紹了一個簡短的主引導扇區代碼,其主要功能是在屏幕上顯示“label offset”,并顯示一個標號的物理地址。

如何在屏幕上顯示

在主引導扇區階段, 0xB8000-0xBFFFF這段物理地址對應的內存是留給顯卡的,每屏2000個字符,按順序存放在這段空間里。所以我們想要在屏幕上輸出,就要改寫這段內存。

    mov ax,0xb800                 ;指向文本模式的顯示緩沖區
    mov es,ax                     ;以下顯示字符串"Label offset:"
    mov byte [es:0x00],'L'
    mov byte [es:0x01],0x07

這段代碼主要用于改寫0xB8000位置的第一個字符表示區域,先把0xb800放入寄存器es中,然后以es作為基地址載入L的ASC碼,最后輸入8bit顏色信息??偣彩褂?6bit顯示一個字符。

將標號地址輸出到屏幕上

下一步想要保存一個標號的物理地址,首先我們獲得這個標號的地址:

    mov ax,number                 ;取得標號number的偏移地址
    mov bx,10

然后分別求每一位的地址:

    ;設置數據段的基地址
    mov cx,cs
    mov ds,cx

    ;求個位上的數字
    mov dx,0
    div bx
    mov [0x7c00+number+0x00],dl   ;保存個位上的數字

需要注意的是,在做16位除法時,被除數的高8位需要保存在寄存器dx中,低8位需要保存在ax中,做完除法后,商保存在ax中,余數保存在dx中。
接下來,我們把存好的十進制數輸出到顯示屏上:

    ;以下用十進制顯示標號的偏移地址
    mov al,[0x7c00+number+0x04]
    add al,0x30
    mov [es:0x1a],al
    mov byte [es:0x1b],0x04

最后,使程序進入無限循環,并且補充主引導扇區到512字節,并補充結尾標志:0x55AA

    infi: jmp near infi                 ;無限循環
      
    number db 0,0,0,0,0
  
    times 203 db 0
              db 0x55,0xaa

本章的代碼解讀就到這里,后面有一些調試相關的內容,不在此贅述

一個小問題

代碼中設置了數據段基地址,但是卻沒有用到,這是因為什么?跟全局描述符表有關系么?

第六章 相同的功能,不同的代碼

本章主要介紹了顯示上一章講述內容的不同實現方法

打印label offset

    jmp near start
     
mytext db 'L',0x07,'a',0x07,'b',0x07,'e',0x07,'l',0x07,' ',0x07,'o',0x07,\
        'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07
number db 0,0,0,0, 0

start:
     mov ax,0x7c0                  ;設置數據段基地址 
     mov ds,ax
     
     mov ax,0xb800                 ;設置附加段基地址 
     mov es,ax
     
     cld
     mov si,mytext                 
     mov di,0
     mov cx,(number-mytext)/2      ;實際上等于 13
     rep movsw

本章節使用的方法是把要打印的內容直接存在內存當中,然后把數據段直接搬運到顯示數據段。
內容較好理解,需要注意的是movsw可以從[ds:si]把數據搬運到[es:di]中去,cld則是方向標志位清零指令,代表由低地址向高地址的方向傳送數據,若方向標志位為1,則相反。同時rep movsw則表示“repeat”執行movsw,直到CX寄存器為0。每次執行movsw,CX寄存器自動減一。

把標號地址存儲為十進制

    ;得到標號所代表的偏移地址
    mov ax,number
    
    ;計算各個數位
    mov bx,ax
    mov cx,5                      ;循環次數 
    mov si,10                     ;除數 
digit: 
    xor dx,dx
    div si
    mov [bx],dl                   ;保存數位
    inc bx 
    loop digit

本章使用了循環來把標號地址轉換為十進制。代碼也十分簡單,不贅述。
小tips:把number地址保存到ax中時,number地址存在哪里呢?需要占用內存空間么?事實上,mov ax, number這條指令翻譯成機器碼后,number的地址是直接寫入到這條指令中的,事實上這條指令是一條載入立即數的指令,而不是一眼看上去的寄存器之間的指令。

把標號地址輸出到顯示器并無限循環

    ;顯示各個數位
    mov bx,number 
    mov si,4                      
show:
    mov al,[bx+si]
    add al,0x30
    mov ah,0x04
    mov [es:di],ax
    add di,2
    dec si
    jns show
    
    mov word [es:di],0x0744

    jmp near $

同樣的,運用循環的方法把數據搬運到顯示區域,那么為什么不仍然使用movsw指令把它們傳輸過去呢?由于我們的高地址位存放的是十進制中的高位,而去顯示的時候則需要地址位存放十進制的高位,所以無論是正向還是反向傳輸數據都不能滿足我們的需求,這時候就需要我們自己來實現這個功能了。

代碼中出現了[bx+si]基址+變址的尋址方式,支持這種尋址方式的摯友一下四種:

[bx+si]
[bx+di]
[bp+si]
[bp+di]

其他的方式都不支持。最后,我們通過jns show這條指令來保證循環的正確執行。jns這條指令在符號標志位(SF)為0時跳轉,為1時不跳轉。而dec指令會改變符號標志位,所以在si減為0后再次執行時,si減為0xffff,改變了符號標志位,結束了循環。

最后,輸出了字符D并開始無限循環。

最終的結束區域使用$符號計算出需要填寫0的數量,并寫入,然后寫入結束標志0x55AA

times 510-($-$$) db 0
                 db 0x55,0xaa
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容