<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 一、Intel 32 位處理器的工作模式 [![](https://box.kancloud.cn/2016-02-29_56d3a8fd6650b.jpg)](http://img.blog.csdn.net/20160128194403051) 如上圖所示,Intel 32 位處理器有3種工作模式。 (1)實模式:工作方式相當于一個8086 (2)保護模式:提供支持多任務環境的工作方式,建立保護機制 (3)虛擬8086模式:這種方式可以使用戶在保護模式下運行8086程序(比如cmd打開的console窗口,就是工作在虛擬8086模式) **有幾點需要特別說明:** (1)保護模式可分為16位和32位的,由段描述符中的D標志指明。對于32位代碼段和數據段,這個標志總是設為1;對于16位代碼和數據段,這個標志被設置為0. D=1:默認使用32位地址和32位或8位的操作數。 D=0:默認使用16位地址和16位或8位的操作數。(主要是為了能夠在32位處理器上運行16位保護模式的程序) 指令前綴0x66用來選擇非默認值得操作數大小,0x67用來選擇非默認值的地址大小。 (2)在實模式下,也可以使用32位的寄存器,比如 ~~~ mov eax,ecx mov ebx,0x12345678 ~~~ (3)在書中,把實模式和16位的保護模式統稱為“16位模式”;把32位保護模式稱為“32位模式”。我的博文也沿用這種叫法。 (4)32位處理器可以執行16位的程序,包括實模式和16位保護模式。 (5)當處理器在16位模式下運行時,可以使用32位的寄存器,執行32位運算。 (6)在16位模式下,數據的大小是8位或者16位的;控制轉移和內存訪問時,偏移量也是16位的。 (7)32位保護模式兼容80286的16位保護模式。 (8)在16位模式下,處理器把所有指令都看成是16位的。 結合(5)和(8),我們發現一個問題:當處理器運行16位模式下,既然把所有指令都看成16位的,那么怎么使用32位的寄存器,執行32位的運算呢?答案是利用指令前綴0x66和0x67.前面已經說過,**指令前綴0x66用來選擇非默認值的操作數大小,0x67用來選擇非默認值的地址大小。** 比如說,指令碼0x40在16位模式下對應的指令是 ~~~ inc ax ~~~ 如果加上前綴0x66,也就是指令碼66 40,當處理器在16位模式下運行,66 40對應的指令是 ~~~ inc eax ~~~ 同理,如果處理器運行在32位模式下,處理器認為指令是32位的,如果加了0x66,那么就表示指令的操作數是16位的。 在編寫程序的時候,我們應該考慮指令的運行環境。為了指令默認的運行環境,NASM提供了偽指令bits,用于指明其后的指令是被編譯成16位的還是32位的。比如: ~~~ [bits 16] mov cx,dx ;89 D1 mov eax,ebx ;66 89 D8 [bits 32] mov cx,dx ;66 89 D1 mov eax,ebx ;89 D8 ~~~ 注意,[bits 16]和[bits 32]的方括號是可以省略的。 # 二、PUSH指令探究 由于32位的處理器都擁有32位的寄存器和算術邏輯部件,而且同內存芯片之間的數據通路至少是32位的,因此,所有以寄存器或者內存單元為操作數的指令都被擴充,以適應32位的算術邏輯操作。而且,**這些擴展的操作即使是在16位模式下(實模式和16位保護模式)也是可用的。** 我在博文?[32位x86處理器編程導入——《x86匯編語言:從實模式到保護模式》讀書筆記08](http://blog.csdn.net/longintchar/article/details/50486017)?中已經總結了一般指令的擴展,在這里,我僅對PUSH指令進行實驗和總結。 實驗目的就是測試在3種模式下,PUSH指令的工作行為(比如SP或ESP到底怎么變化,壓入的數到底是多少)。所以,我列了一個單子,把所有能想到的形式都列出來了,其中有的我也不確定(或許這樣寫編譯都會報錯)。不管那么多,先寫出來,然后讓編譯器篩選吧![](https://box.kancloud.cn/2016-02-29_56d3a8f8abd34.jpg) ~~~ 1 ;測試各種push 2 3 ;操作數是立即數,分為一字節、兩字節、四字節 4 push 0x80 5 push byte 0x80 6 7 push 0x8000 8 push word 0x8000 9 10 push 0x87654321 11 push dword 0x87654321 12 13 ;操作數是寄存器,分為16位寄存器和32位寄存器 14 mov eax,0x86421357 15 push ax 16 push eax 17 18 ;操作數是內存單元,分為一字節、兩字節、四字節 19 push [data] 20 push byte [data] 21 push word [data] 22 push dword [data] ~~~ 是不是有的寫法明顯就不對呢? 首先,第20行,肯定不對。因為如果是內存操作數的話,不能用byte修飾。剩下來的錯誤,我會在后文揭曉答案。 ### 1.在實模式下的實驗 ### (1)實驗代碼 ~~~ 1 ;PUSH 指令實驗 2 3 jmp near start 4 5 data db 0x12,0x34,0x56,0x78 6 message db 'Hello,PUSH!' 7 8 start: 9 mov ax,0x7c0 ;設置數據段的段基地址 10 mov ds,ax 11 12 mov ax,0xb800 ;設置附加段基址到顯示緩沖區 13 mov es,ax 14 15 ;以下顯示字符串 16 mov si,message 17 mov di,0 18 mov cx,start-message 19 @g: 20 mov al,[si] 21 mov [es:di],al 22 inc di 23 mov byte [es:di],0x02 24 inc di 25 inc si 26 loop @g 27 28 ;測試各種push 29 push 0x80 30 push byte 0x80 31 32 push 0x8000 33 push word 0x8000 34 35 push 0x87654321 36 push dword 0x87654321 37 38 mov eax,0x86421357 39 push ax 40 push eax 41 42 ;push [data] 43 push word [data] 44 push dword [data] 45 46 push ds 47 push gs 48 49 jmp near $ 50 51 52 times 510-($-$$) db 0 53 db 0x55,0xaa ~~~ 這段代碼不是用的配書代碼,是我自己寫的。 第5行,定義了4字節的數據,這是為了后面驗證“push + 內存操作數”這一情況。 第6行,定義了一個字符串,要把它顯示在屏幕上。這樣做是為了調試方便,讓我們知道我們的程序已經RUN了。 第29行到47行,測試各種push,我會利用Bochs的調試功能,跟蹤每條Push的執行情況,把結果總結出來。 好的,我們開始編譯吧。 對于30行,有個警告: ~~~ push byte 0x80 ;warning: signed byte value exceeds bounds ~~~ 既然是警告,那么30行不必去掉。相反我們更加好奇了,看看執行時會發生什么。 對于35行,還是一個警告: ~~~ push 0x87654321 ;warning: word data exceeds bounds ~~~ 對于42行,呵呵,就是一個錯誤了。 ~~~ push [data] ; error: operation size not specified ~~~ 好吧,看來這樣不指定操作數的大小是不行的,所以我們把42行注釋掉。 然后再編譯,好的,可以了。 調試的過程就是不斷用n命令,反復用print-stack命令,還有reg命令等,仔細觀察棧的變化和SP的變化。(此處省略2000字) ### (2)實驗報告 小二,上實驗報告! [![](https://box.kancloud.cn/2016-02-29_56d3a8fd98f9e.jpg)](http://img.blog.csdn.net/20160127220223687) 通過上面的實驗,我們可以知道,如果CPU運行在實模式,如果用NASM編譯,push指令可以這么用: [![](https://box.kancloud.cn/2016-02-29_56d3a8fdcdb5d.jpg)](http://img.blog.csdn.net/20160127220230578) ### 2.在16位保護模式下的實驗 ### (1)關于16位保護模式 請參考我的博文?[關于80286——《x86匯編語言:從實模式到保護模式》讀書筆記15](http://blog.csdn.net/longintchar/article/details/50575391) ### (2)實驗代碼 實驗代碼由配書代碼(代碼清單11-1 (文件名:c11_mbr.asm))修改而成。目的就是我們要從實模式進入16位的保護模式,然后測試16位保護模式下PUSH指令的行為。 ~~~ 1 ;test push (16位保護模式下) 2 3 ;設置堆棧段和棧指針 4 mov ax,cs 5 mov ss,ax 6 mov sp,0x7c00 7 8 ;計算GDT所在的邏輯段地址 9 mov ax,[cs:gdt_base+0x7c00] ;低16位 10 mov dx,[cs:gdt_base+0x7c00+0x02] ;高16位 11 mov bx,16 12 div bx 13 mov ds,ax ;令DS指向該段以進行操作 14 mov bx,dx ;段內起始偏移地址 15 16 ;創建0#描述符,它是空描述符,這是處理器的要求 17 mov dword [bx+0x00],0x00 18 mov dword [bx+0x04],0x00 19 20 ;創建#1描述符,保護模式下的代碼段描述符 21 mov dword [bx+0x08],0x7c0001ff 22 mov dword [bx+0x0c],0x00009800 23 24 ;創建#2描述符,保護模式下的數據段描述符(文本模式下的顯示緩沖區) 25 mov dword [bx+0x10],0x8000ffff 26 mov dword [bx+0x14],0x0000920b 27 28 ;創建#3描述符,保護模式下的堆棧段描述符 29 mov dword [bx+0x18],0x00007a00 30 mov dword [bx+0x1c],0x00009600 31 32 ;初始化描述符表寄存器GDTR 33 mov word [cs: gdt_size+0x7c00],31 ;描述符表的界限(總字節數減一) 34 35 lgdt [cs: gdt_size+0x7c00] 36 37 in al,0x92 ;南橋芯片內的端口 38 or al,0000_0010B 39 out 0x92,al ;打開A20 40 41 cli ;保護模式下中斷機制尚未建立,應 42 ;禁止中斷 43 mov eax,cr0 44 or eax,1 45 mov cr0,eax ;設置PE位 46 47 ;以下進入保護模式... ... 48 jmp 0x0008:flush ;描述符選擇子:16位偏移 49 ;清流水線并串行化處理器 50 51 52 flush: 53 mov cx,00000000000_10_000B ;加載數據段選擇子(0x10) 54 mov ds,cx 55 56 ;以下在屏幕上顯示"ABCDEFGHIJK" 57 mov byte [0x00],'A' 58 mov byte [0x02],'B' 59 mov byte [0x04],'C' 60 mov byte [0x06],'D' 61 mov byte [0x08],'E' 62 mov byte [0x0a],'F' 63 mov byte [0x0c],'G' 64 mov byte [0x0e],'H' 65 mov byte [0x10],'I' 66 mov byte [0x12],'J' 67 mov byte [0x14],'K' 68 69 70 ;測試push 71 mov cx,00000000000_11_000B ;加載堆棧段選擇子 72 mov ss,cx 73 mov sp,0x7c00 74 75 76 push 0x80 77 push byte 0x80 ; warning: signed byte value exceeds bounds 78 79 push 0x8000 80 push word 0x8000 81 82 push 0x87654321 83 ;warning: word data exceeds bounds 84 push dword 0x87654321 85 86 87 mov eax,0x86421357 88 push ax 89 push eax 90 91 ;push [0x00]error: operation size not specified 92 push byte [0x00] 93 push word [0x00] 94 95 push dword [0x00] 96 97 push ds 98 push gs 99 push es 100 push cs 101 102 ghalt: 103 hlt ;已經禁止中斷,將不會被喚醒 104 105;------------------------------------------------------------------------------- 106 107 gdt_size dw 0 108 gdt_base dd 0x00007e00 ;GDT的物理地址 109 110 times 510-($-$$) db 0 111 db 0x55,0xaa ~~~ 對比32位保護模式的代碼,就會發現16位保護模式的代碼略有不同。 首先,比如說22行,段描述符的定義是 ~~~ 22 mov dword [bx+0x0c],0x00009800 ~~~ 因為80286中,段描述符的格式是 [![](https://box.kancloud.cn/2016-02-29_56d3a8fdee677.jpg)](http://img.blog.csdn.net/20160127220235237) 所以,高4字節的16~32位全部為0. 其次, ~~~ 47 ;以下進入保護模式... ... 48 jmp 0x0008:flush ;描述符選擇子:16位偏移 49 ;清流水線并串行化處理器 ~~~ 這里,沒有加偽指令[bits 32],而且,偏移flush沒有用dword修飾。因為操作數和偏移是16位的。 好了,代碼就說到這里,我們看實驗報告吧。 ### (3)實驗報告 [![](https://box.kancloud.cn/2016-02-29_56d3a8fe114df.jpg)](http://img.blog.csdn.net/20160127220238706) 通過和實模式的對比,可以發現,除了9、10兩行中的指令碼的偏移不一樣(這和數據存放的位置有關系,和PUSH沒有關系),PUSH指令的行為是驚人的相同。所以我們可以得出結論,16位保護模式下,PUSH的用法和實模式是一樣的。我想,這也是在原書中,作者把實模式和16位的保護模式統稱為“16位模式”,把32位保護模式稱為“32位模式”的原因吧。 ### 3.在32位保護模式下的實驗 ### (1)實驗代碼 實驗代碼由配書代碼(代碼清單11-1 (文件名:c11_mbr.asm))修改而成。 ~~~ 1 ;test push (32位保護模式) 2 3 ;設置堆棧段和棧指針 4 mov ax,cs 5 mov ss,ax 6 mov sp,0x7c00 7 8 ;計算GDT所在的邏輯段地址 9 mov ax,[cs:gdt_base+0x7c00] ;低16位 10 mov dx,[cs:gdt_base+0x7c00+0x02] ;高16位 11 mov bx,16 12 div bx 13 mov ds,ax ;令DS指向該段以進行操作 14 mov bx,dx ;段內起始偏移地址 15 16 ;創建0#描述符,它是空描述符,這是處理器的要求 17 mov dword [bx+0x00],0x00 18 mov dword [bx+0x04],0x00 19 20 ;創建#1描述符,保護模式下的代碼段描述符 21 mov dword [bx+0x08],0x7c0001ff 22 mov dword [bx+0x0c],0x00409800 23 24 ;創建#2描述符,保護模式下的數據段描述符(文本模式下的顯示緩沖區) 25 mov dword [bx+0x10],0x8000ffff 26 mov dword [bx+0x14],0x0040920b 27 28 ;創建#3描述符,保護模式下的堆棧段描述符 29 mov dword [bx+0x18],0x00007a00 30 mov dword [bx+0x1c],0x00409600 31 32 ;初始化描述符表寄存器GDTR 33 mov word [cs: gdt_size+0x7c00],31 ;描述符表的界限(總字節數減一) 34 35 lgdt [cs: gdt_size+0x7c00] 36 37 in al,0x92 ;南橋芯片內的端口 38 or al,0000_0010B 39 out 0x92,al ;打開A20 40 41 cli ;保護模式下中斷機制尚未建立,應 42 ;禁止中斷 43 mov eax,cr0 44 or eax,1 45 mov cr0,eax ;設置PE位 46 47 ;以下進入保護模式... ... 48 jmp dword 0x0008:flush ;16位的描述符選擇子:32位偏移 49 ;清流水線并串行化處理器 50 [bits 32] 51 52 flush: 53 mov cx,00000000000_10_000B ;加載數據段選擇子(0x10) 54 mov ds,cx 55 56 ;以下在屏幕上顯示"ABCDEFGHIJK" 57 mov byte [0x00],'A' 58 mov byte [0x02],'B' 59 mov byte [0x04],'C' 60 mov byte [0x06],'D' 61 mov byte [0x08],'E' 62 mov byte [0x0a],'F' 63 mov byte [0x0c],'G' 64 mov byte [0x0e],'H' 65 mov byte [0x10],'I' 66 mov byte [0x12],'J' 67 mov byte [0x14],'K' 68 69 70 ;測試push 71 mov cx,00000000000_11_000B ;加載堆棧段選擇子 72 mov ss,cx 73 mov esp,0x7c00 74 75 76 push 0x80 77 push byte 0x80 ;warning: signed byte value exceeds bounds 78 79 push 0x8000 80 push word 0x8000 81 82 push 0x87654321 83 push dword 0x87654321 84 85 mov eax,0x86421357 86 push ax 87 push eax 88 89 90 push word [0x00] 91 push dword [0x00] 92 93 push ds 94 push gs 95 push es 96 push cs 97 98 ghalt: 99 hlt ;已經禁止中斷,將不會被喚醒 100 101;------------------------------------------------------------------------------- 102 103 gdt_size dw 0 104 gdt_base dd 0x00007e00 ;GDT的物理地址 105 106 times 510-($-$$) db 0 107 db 0x55,0xaa ~~~ 如果對上面的代碼不熟悉的話,可以參考我的博文?[進入保護模式(一)——《x86匯編語言:從實模式到保護模式》讀書筆記12](http://blog.csdn.net/longintchar/article/details/50513772)?等文章。 ### (2)實驗報告 [![](https://box.kancloud.cn/2016-02-29_56d3a8fe2d5d7.jpg)](http://img.blog.csdn.net/20160128194409244) 根據測試報告,我們可以歸納出32位保護模式下,針對NASM編譯器的push指令用法: [![](https://box.kancloud.cn/2016-02-29_56d3a8fe539bf.jpg)](http://img.blog.csdn.net/20160128194413598) (完)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看