日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

进入保护模式(一)——《x86汇编语言:从实模式到保护模式》读书笔记12

發(fā)布時間:2025/3/15 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 进入保护模式(一)——《x86汇编语言:从实模式到保护模式》读书笔记12 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

之前已經(jīng)做了一些理論上的鋪墊,這次我們就可以看代碼了。

一、代碼清單

;代碼清單11-1;文件名:c11_mbr.asm;文件說明:硬盤主引導扇區(qū)代碼 ;創(chuàng)建日期:2011-5-16 19:54;設置堆棧段和棧指針 mov ax,cs mov ss,axmov sp,0x7c00;計算GDT所在的邏輯段地址 mov ax,[cs:gdt_base+0x7c00] ;低16位 mov dx,[cs:gdt_base+0x7c00+0x02] ;高16位 mov bx,16 div bx mov ds,ax ;令DS指向該段以進行操作mov bx,dx ;段內(nèi)起始偏移地址 ;創(chuàng)建0#描述符,它是空描述符,這是處理器的要求mov dword [bx+0x00],0x00mov dword [bx+0x04],0x00 ;創(chuàng)建#1描述符,保護模式下的代碼段描述符mov dword [bx+0x08],0x7c0001ff mov dword [bx+0x0c],0x00409800 ;創(chuàng)建#2描述符,保護模式下的數(shù)據(jù)段描述符(文本模式下的顯示緩沖區(qū)) mov dword [bx+0x10],0x8000ffff mov dword [bx+0x14],0x0040920b ;創(chuàng)建#3描述符,保護模式下的堆棧段描述符mov dword [bx+0x18],0x00007a00mov dword [bx+0x1c],0x00409600;初始化描述符表寄存器GDTRmov word [cs: gdt_size+0x7c00],31 ;描述符表的界限(總字節(jié)數(shù)減一) lgdt [cs: gdt_size+0x7c00]in al,0x92 ;南橋芯片內(nèi)的端口 or al,0000_0010Bout 0x92,al ;打開A20cli ;保護模式下中斷機制尚未建立,應 ;禁止中斷 mov eax,cr0or eax,1mov cr0,eax ;設置PE位;以下進入保護模式... ...jmp dword 0x0008:flush ;16位的描述符選擇子:32位偏移;清流水線并串行化處理器 [bits 32] flush:mov cx,00000000000_10_000B ;加載數(shù)據(jù)段選擇子(0x10)mov ds,cx;以下在屏幕上顯示"Protect mode OK." mov byte [0x00],'P' mov byte [0x02],'r'mov byte [0x04],'o'mov byte [0x06],'t'mov byte [0x08],'e'mov byte [0x0a],'c'mov byte [0x0c],'t'mov byte [0x0e],' 'mov byte [0x10],'m'mov byte [0x12],'o'mov byte [0x14],'d'mov byte [0x16],'e'mov byte [0x18],' 'mov byte [0x1a],'O'mov byte [0x1c],'K';以下用簡單的示例來幫助闡述32位保護模式下的堆棧操作 mov cx,00000000000_11_000B ;加載堆棧段選擇子mov ss,cxmov esp,0x7c00mov ebp,esp ;保存堆棧指針 push byte '.' ;壓入立即數(shù)(字節(jié))sub ebp,4cmp ebp,esp ;判斷壓入立即數(shù)時,ESP是否減4 jnz ghalt pop eaxmov [0x1e],al ;顯示句點 ghalt: hlt ;已經(jīng)禁止中斷,將不會被喚醒 ;------------------------------------------------------------------------------- gdt_size dw 0gdt_base dd 0x00007e00 ;GDT的物理地址 times 510-($-$$) db 0db 0x55,0xaa

上面就是配書源碼。我們一點一點看。

二、源碼分析

(一)設置堆棧和棧指針

;設置堆棧段和棧指針 mov ax,cs mov ss,axmov sp,0x7c00

這個沒有什么好說的,就是初始化棧。這三行執(zhí)行后,SS=0; SP=0x7c00;

需要注意的是,這樣設置后,棧的區(qū)域從0x0000_7c00向下擴展(不含0x0000_7c00這個字節(jié)),該區(qū)域包含了很多BIOS數(shù)據(jù),包括實模式下的中斷向量表,所以一定要小心。

(二)安裝段描述符

;計算GDT所在的邏輯段地址 mov ax,[cs:gdt_base+0x7c00] ;低16位 mov dx,[cs:gdt_base+0x7c00+0x02] ;高16位 mov bx,16 div bx mov ds,ax ;令DS指向該段以進行操作mov bx,dx ;段內(nèi)起始偏移地址

怎么理解這段代碼呢?

首先,在代碼清單的95、96行,有

gdt_size dw 0gdt_base dd 0x00007e00 ;GDT的物理地址

作者在這里聲明了標號gdt_base,還初始化了一個雙字——0x0000_7e00; 作者的意圖是從這個地方開始建立全局描述符表GDT。我們的程序就是一個引導扇區(qū),占用了512(=0x200)字節(jié)。程序加載的物理地址是0x7c00, 0x7c00+0x200 = 0x7e00. 可見,在物理地址的安排上,引導程序后面緊跟著就是GDT。

目前我們還是處在實模式下,所以要建立GDT,必須將GDT的線性地址(物理地址)轉(zhuǎn)換成實模式下使用的“段地址:偏移地址”的形式。

mov ax,[cs:gdt_base+0x7c00] ;

這句使了段超越前綴“cs”,表明訪問代碼段中的數(shù)據(jù);因為CS=0,所以就把物理地址(0x7c00+gdt_base)處的0x7e00傳送給了ax; 同樣地,將0x0000傳送給dx; 為了把線性地址轉(zhuǎn)換成邏輯地址,我們用DX:AX除以16,得到的商(AX)就是段地址,余數(shù)(DX)就是偏移地址。

mov bx,16???????
??????? div bx???????????
??????? mov ds,ax????????????????????????? ;令DS指向該段以進行操作
??????? mov bx,dx????????????????????????? ;段內(nèi)起始偏移地址
這幾行執(zhí)行之后,GDT的邏輯地址就是 DS:BX.

;創(chuàng)建0#描述符,它是空描述符,這是處理器的要求mov dword [bx+0x00],0x00mov dword [bx+0x04],0x00

處理器規(guī)定,GDT中的第一個描述符必須是空描述符。這是什么原因呢?因為很多時候,寄存器和內(nèi)存單元的初始值都會為0,再加上程序設計有問題,就會在無意中用全0的索引來選擇描述符,這當然是不好的。因此,處理器要求將第一個描述符定義成空描述符。所以,上面兩行代碼定義了一個空描述符。

;創(chuàng)建#1描述符,保護模式下的代碼段描述符mov dword [bx+0x08],0x7c0001ff mov dword [bx+0x0c],0x00409800

這兩行用來創(chuàng)建第二個描述符。之前的博文我們已經(jīng)掌握了數(shù)據(jù)段和代碼段描述符的格式,所以對這個描述符就不難理解了。

還記得我上一篇博文中寫了一個小程序嗎?http://blog.csdn.net/longintchar/article/details/50507218

趕緊用它來分析一下吧:

線性基地址:0x0000_7c00

段界限為0x001FF,因為G=0,所以該段的長度是512(2的9次方)字節(jié);

特權級:0

其他字段就不逐個說明了,相信你一定能懂。很明顯,這個描述符定義的段,就是主引導程序所在的區(qū)域。

接著看代碼。

;創(chuàng)建#2描述符,保護模式下的數(shù)據(jù)段描述符(文本模式下的顯示緩沖區(qū)) mov dword [bx+0x10],0x8000ffff mov dword [bx+0x14],0x0040920b

程序分析的結果是:

seg_base = 0XB8000
seg_limit = 0XFFFF
S = 1
DPL = 0
G = 0
D/B = 1
TYPE = 2
數(shù)據(jù)段: 可讀可寫

看來這個段是指向顯存的。

;創(chuàng)建#3描述符,保護模式下的堆棧段描述符mov dword [bx+0x18],0x00007a00mov dword [bx+0x1c],0x00409600

這是創(chuàng)建棧段的描述符。程序分析的結果是:

-----------------------
seg_base = 0
seg_limit = 0X7A00
S = 1
DPL = 0
G = 0
D/B = 1
TYPE = 6
數(shù)據(jù)段: 向下擴展,可讀可寫
------------------------

正如作者所說:段界限的值0x7a00加上1(0x7a01),就是ESP寄存器所允許的最小值。當執(zhí)行隱式的棧操作(如PUSH、CALL)時,處理器會檢查ESP的值,一旦發(fā)現(xiàn)它小于0x7a01,就會引發(fā)異常中斷。如果你還不理解,那么可以把書翻到215頁。作者說在棧操作時,必須符合以下規(guī)則:

實際使用的段界限+1 <= (ESP的內(nèi)容減操作數(shù)的長度) <= 0xFFFF_FFFF

就拿這個例子來說,因為G=0,所以段界限就是0x7a00. 假設現(xiàn)在ESP的內(nèi)容是0x7a04,此時執(zhí)行下面的指令:

push edx

因為壓入的是雙字,所以處理器會先將ESP的值減去4,于是ESP=0x7a00. 因為0x7a00小于0x7a01,因此會引發(fā)異常中斷。

(三)LGDT指令

好了,現(xiàn)在描述符已經(jīng)安裝完畢,接下來的工作是加載描述符表的線性基地址和界限到GDTR寄存器。相關的指令是lgdt. 該指令的格式為:

lgdt m48

也就是說,該指令的操作數(shù)內(nèi)存操作數(shù)。注意,該指令在實模式和保護模式下都可以執(zhí)行,也不影響任何標志位。

這個內(nèi)存操作數(shù)指向一個6字節(jié)的內(nèi)存區(qū)域,要求低16位是GDT的界限值(表的總字節(jié)數(shù)減去1),高32位是GDT的線性基地址。

gdt_size dw 0gdt_base dd 0x00007e00 ;GDT的物理地址

還記得嗎,這是代碼中事先定義了6字節(jié)的空間。前兩個字節(jié)就是為了保存GDT的界限值。

;初始化描述符表寄存器GDTRmov word [cs: gdt_size+0x7c00],31 ;描述符表的界限(總字節(jié)數(shù)減一) lgdt [cs: gdt_size+0x7c00]

第一句寫入界限值,第二句把6字節(jié)加載到GDTR寄存器。

注意,到目前為止,我們依然在實模式下。

(四)關于A20

1.A20 GATE 起源[1]

在8086/8088中,只有20根地址線,所以可以訪問的地址是2^20=1M。但由于8086/8088是16位地址模式,能夠表示的地址范圍是0-64K,所以為了訪問1M內(nèi)存,Intel采取了分段的模式。

即:物理地址=16位段地址*16 + 16位偏移

但這種方式引起了新的問題,通過上述分段模式,能夠表示的最大內(nèi)存為:FFFFh:FFFFh=FFFF0h+FFFFh=10FFEFh

但8086/8088只有20位地址線,所以當訪問100000h~10FFEFh之間的內(nèi)存時,系統(tǒng)并不認為訪問越界而產(chǎn)生異常,而是自動從重新0開始計算,也就是說系統(tǒng)計算實際地址的時候是按照對1M求模的方式進行的,這種技術被稱為wrap-around(回繞)。

到了80286,系統(tǒng)的地址總線發(fā)展為24根,這樣能夠訪問的內(nèi)存可以達到2^24=16M。為了兼容,Intel在設計80286時提出的目標是:在實模式下,系統(tǒng)所表現(xiàn)的行為應該和8086/8088所表現(xiàn)的完全一樣。但最終,80286芯片卻存在一個BUG:如果程序員訪問100000H~10FFEFH之間的內(nèi)存,系統(tǒng)將實際訪問這塊內(nèi)存,而不是象過去一樣重新從0開始。

為了解決上述問題,IBM使用鍵盤控制器上剩余的一些輸出線來管理第21根地址線(從0開始數(shù)是第20根),被稱為A20Gate;如果A20 Gate打開,則當程序員給出100000H~10FFEFH之間的地址的時候,系統(tǒng)將真正訪問這塊內(nèi)存區(qū)域;如果A20Gate被禁止,則當程序員給出100000H~10FFEFH之間的地址的時候,系統(tǒng)仍然使用8086/8088的方式。絕大多數(shù)IBM PC兼容機默認的A20Gate是被禁止的。由于在當時沒有更好的方法來解決這個問題,所以IBM使用了鍵盤控制器來操作A20 Gate,但是這種操作太麻煩了,要使用一大堆指令。

2.Alt_A20_GATE

Alt_A20_GATE ,又稱Fast A20. 通過端口0x92的bit1來打開A20,具體方法是:先從端口讀出原數(shù)據(jù),接著將bit1置1,然后再寫入該端口,這樣就打開了A20.

正如代碼所示

in al,0x92 ;南橋芯片內(nèi)的端口 or al,0000_0010Bout 0x92,al ;打開A20

?

一次學太多會不會覺得累呢?我們就說到這里,下次繼續(xù)…

?

【參考資料】

[1] 如煙海的專欄. http://blog.csdn.net/ruyanhai/article/details/7181842

總結

以上是生活随笔為你收集整理的进入保护模式(一)——《x86汇编语言:从实模式到保护模式》读书笔记12的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。