進入保護模式(三)——《x86彙編語言:從實模式到保護模式》讀書筆記17

(十)保護模式下的棧

76         ;如下用簡單的示例來幫助闡述32位保護模式下的堆棧操做 
77         mov cx,00000000000_11_000B         ;加載堆棧段選擇子
78         mov ss,cx
79         mov esp,0x7c00

第77~79行用來初始保護模式下的棧。棧段描述符是GDT中第3個(從0開始數)描述符,這個描述符的線性基地址是0x0000_0000,段界限是0x0000_7a00,粒度是字節,B=1,屬於可讀可寫、向下擴展的數據段。學習

我在博文數據段描述符和代碼段描述符(一)——《x86彙編語言:從實模式到保護模式》讀書筆記10中已經說過,spa

對於向上擴展的段(E=0),邏輯地址中的偏移值範圍能夠從0到(界限值*粒度);.net

對於向下擴展的段(E=1),邏輯地址中的偏移範圍能夠從(界限值*粒度)到0xFFFF(當B=0時)或者0xFFFF_FFFF(當B=1時)。指針

因此,對於描述符中的棧段,偏移範圍是0x0000_7a00~0xffff_ffff. 仔細琢磨一下,這和咱們的想法不是那麼一致,由於代碼79行,令ESP的初值是0x7c00,也就是說,咱們本打算定義一個偏移範圍是0x0000_7a00~0x0000_7c00的棧段。code

由於線性基地址是0x0000_0000,也就是說描述符定義的棧段,實際能夠訪問的物理空間是0x0000_7a00~0xffff_ffff,可是咱們卻但願這個棧能夠訪問的物理空間是0x0000_7a00~0x0000_7c00。示意圖(根據原書的圖11-14改編而成)以下圖所示。雖然這個棧不完美,可是不用擔憂,咱們會在後面的學習中用更好的方法來建立棧。眨眼blog

New0003棧

(十一)驗證32位下的棧操做

隱式的棧操做(如push、pop、call、ret、iret等)涉及兩個段,一個是指令所在的代碼段,一個是棧段。以前的博文咱們說過,對於可執行代碼段,字符串

D=1:默認是32位地址和32位或8位的操做數get

D=0:默認是16位地址和16位或8位的操做數it

注意:指令前綴0x66能夠用來選擇非默認值的操做數大小;前綴0x67能夠用來選擇非默認值的地址大小class

對於棧段,

B=1:棧指針使用ESP

B=0:棧指針使用SP

就本文的實驗代碼,其代碼段描述符的D位是1,其棧段描述符的B位也是1. 因此,當進行隱式的棧操做時,默認是32位操做數(好比壓棧的時候,壓入的是雙字),且用ESP進行操做。

因此,下面的代碼就用來驗證這個事實。

81         mov ebp,esp                        ;保存堆棧指針 
82         push byte '.'                      ;壓入當即數(字節)
83         
84         sub ebp,4
85         cmp ebp,esp                        ;判斷壓入當即數時,ESP是否減4 
86         jnz ghalt                          
87         pop eax
88         mov [0x1e],al                      ;顯示句點 
89      
90  ghalt:     
91         hlt                                ;已經禁止中斷,將不會被喚醒

在閱讀這段代碼的時候,我多少有點懷疑:書上說的究竟是不是真的?我想經過實踐來檢驗:探究一下PUSH指令在16位模式和32位模式下的執行規律。通過一番折騰,終於有告終果。請參考個人博文  16位模式/32位模式下PUSH指令探究——《x86彙編語言:從實模式到保護模式》讀書筆記16

第81行,複製esp的值給ebp;第82行,壓入一個字節(byte關鍵字不能省略);理論上,把ebp的值減去4後(第84行),應該和此時esp的值相等。爲了證實這一點,第85行比較ebp和esp的值,若是不相等,就跳轉到91行執行停機指令;若是相等,就把字符「.」顯示在以前的字符串後面。

OK,第11章的內容已經學習完了。最後咱們看一下代碼的運行結果吧,結果就是在屏幕的左上角顯示「Protect mode OK.」

執行結果

(完)

相關文章
相關標籤/搜索