ASM:《X86汇编语言-从实模式到保护模式》第10章:32位x86处理器的编程架构
★PART1:32位的x86處理器執行方式和架構
1. 寄存器的拓展(IA-32)
?????? 從80386開始,處理器內的寄存器從16位拓展到32位,命名其實就是在前面加上e(Extend)就好了,8個通用寄存器被命名為EAX,EBX,ECX,EDX,ESI,EDI,ESP和EBP,同樣的,操作的時候必須要和寄存器的長匹配,比如下面的操作就是錯的。
?????? 32位通用寄存器的高16位不可以單獨使用,但是他們的低16位依然可以按照8086的使用方法一樣使用。處理器在32位保護模式下可以使用全部的32條地址線,訪問4GB內存。同時,EIP變成了32位的,當然在實模式下也可以只使用實模式下的16位。標志寄存器FLAGS也拓展到16位,其前各個16位和16位模式下各個標志一樣。
?????? 在32位模式下對內存的訪問理論上是不需要分段的,但是IA-32也是基于分段模型的,當然也可以只分一個段,基地址是0x00000000,段的長度是4GB,在這種情況下,可以視為不分段,也就是平坦模式(Flat Mode)。
?????? 在32位模式下,處理器要求在加載程序的時候,先定義該程序擁有的段,然后允許使用這些段,定義段的時候,除了基地址(起始地址)外,還附加了段界限,特權級別,類型等屬性。當程序訪問一個段時候處理器將用固件實施各種檢查工作,以防對內存的違規訪問,如圖,32位模式下,傳統的段位寄存器,如CS,SS,DS,ES,保存的不再是16位的段基地址,而是段的選擇子(選擇要訪問的段,段的定義在GDT或者LDT上),除了段選擇子,每個段寄存器還包括了一個不可見的部分,稱位描述符高速緩存器,里面有段的基地址和各種訪問屬性,這部分內容程序不可訪問,由處理器自動使用,另外32位模式還多加了兩個段位寄存器FS和GS。
2. x86的32位處理器的基本工作模式
?????? ①:16位保護模式(80286)
?????? 80286是一款16位的處理器,在實模式下和8086一樣,只能使用最大為64KB的段,雖然他有24根地址線,理論上可以訪問224的內存(16MB),但是還是只能分成多個段來訪問。但是80286訪問內存的時候,不需將段地址左移,因為在段寄存器的描述符高速緩存中有24位的段物理基地址,這樣一來,段可以位于16MB內存空間中的任何位置,而不再限于1MB的范圍內,也不必非得對齊16字節,但是80286的通用寄存器是16位的,只能提供16位的偏移地址,所以和8086一樣,即使運行在16位保護模式下,段的長度也不能超過64KB。
?????? ②:32位保護模式(80836和以后的x86 32位處理器)
?????? 80386處理器的寄存器是32位的,而且擁有32根地址線,可以訪問232,即4GB的內存,80386以及后續的所有32位處理器,都有兼容模式,可以運行實模式下的8086,且剛加電的時候,這些處理器都自動處于實模式下,可以運行8086的程序,這個時候它相當于一個非??斓?086處理器,在實模式下經過一定的設定就可以進入保護模式。32位保護模式下,段的基地址是32位的,偏移地址也是32位的。32位保護模式兼容80286的16位保護模式。
3. 線性地址(Liner Address)
?????? IA-32處理器編程,需要在程序中給出段地址和偏移地址,因為分段是IA-32的基本特征之一,傳統上,段地址和偏移地址稱為邏輯地址,偏移地址被叫做有效地址(Effective Address,EA),在指令中給出有效的地址叫做尋址方式(Addressing Mode),比如:
?????? 段的管理是由處理器的段部件負責產生的,段部件將段地址和偏移地址相加,得到訪問內存的地址。一般來說,段部件產生的地址就是物理地址。IA-32處理器支持多任務,在多任務環境下,任務的創建需要分配內存,當任務終止后,還要回收他所占用的內存。在分段模型下,內存的分配是不定長的,所以IA-32處理器支分頁功能,分頁功能將物理內存空間劃分成邏輯空間上的頁,頁的大小是固定的,一般為4KB,通過使用頁,可以簡化內存管理。(只有開啟了頁功能才能使用頁),當頁功能開啟后,段部件產生的地址就不再是物理地址了,而是線性地址,線性地址還要經頁部件轉換后,才是物理地址。
?????? 線性地址的概念用來描述任務的地址空間,IA-32處理器上的每個任務都擁有4GB的虛擬內存空間,這是一段長4GB的平坦空間,就像一段平直的線段,因此叫線性地址空間,相應的,由段部件產生的地址,就對應著線性地址空間上的每一個點,這就是線性地址(也就是線性地址是段部件的一個對應,是物理地址的一個映射)。
4. 流水線(Pipe-Line)
?????? 在8086時期,處理器已經有了指令預取隊列,當指令執行時,如果總線總是空閑的(沒有訪問內存的操作),就可以在指令執行的同時預取指令并提前譯碼,這種做法可以大大地提升程序的執行速度。(原理就是利用處理器的不同指令大多不會再一個時鐘周期內完成)??梢园岩粋€指令分解成若干個細小的步驟,并分配給相應的段元來完成。每個單元的執行時獨立的,并行的。這么做以后,每個步驟的執行就會在時間上重疊起來,這種執行指令的方法就是流水線技術。根據貪婪算法,流水線的執行效率受執行時間最長的那一級的限制,要縮短各級的執行時間。如果要縮短各級的執行時間,那么就要讓每一級的任務減少,與此同時,就需要把一些復雜的任務再進行分解,如此,在2000年的時候Intel推出的Pentium處理器(采用NetBurst微結構,進一步分解指令的執行過程,采用31級超深流水線)。
5. 高速緩存(Cache)
?????? 因為與處理器的寄存器相比,內存的訪問速度(幾十ns)和硬盤的訪問速度(幾十ms)都非常低慢,所以人們就弄了一個高速緩存的機制,高速緩存說白了就是一個存儲器(也是一個靜態存儲器,容量較小,但速度可以與處理器匹配。),當然這只是簡單來說的,事實上高速緩存的實現是很復雜的,里面有很多計算的原理,不同CPU的實現還可能不一樣,高速緩存利用了程序運行的時候,總是局部性的(比如訪問剛剛訪問過的指令或者數據,這被稱為程序運行的局部性規律。)每當處理器要訪問內存,首先檢索高速緩存。如果要訪問的內容已經在高速緩存中了,那么直接從高速緩存中獲得,這被稱為命中(Hit);否則,稱為不中(Miss),在不中的情況下,處理器災區的需要的內容前必須重新裝載高速緩存,而不只是直接到內存中去哪個內容,高速緩存的裝載是以塊為單位的,包括那個所需數據的鄰近的內容,為此需要額外的時間來等待塊從內存載入高速緩存,在這個過程中損失的時間稱為不中懲罰(miss penalty)。
6. 亂序執行(Out-Of-Order Executive)
?????? 為了執行流水線技術,需要降至零拆分成更小的可獨立執行部分,即拆分成微操作(micro operations),簡寫為μops。
?????? 有些簡單指令只用一個微操作,比如:
有些指令可以拆分成兩個微操作,一個從內存中讀取數據并保存到臨時寄存器,另一個用于將EAX寄存器和臨時寄存器的數值相加。
再比如:
這個可以拆分成三個微操作一個從內存中讀數據,一個執行相加的動作,第三個用于將相加的結果寫回到內存中。一旦將指令拆分成為操作,處理器就可以在必要的時候亂序執行,比如:
?????? 這里,指令mov [mem2],eax可以拆分成兩個微操作,如此,在執行邏輯左移指令的同時,處理器可以提前從內存中讀取mem2的內容,典型的,如果數據不在高速緩存中,那么處理器在獲取mem1的內容之后,會立即開始獲取mem2的內容,于是同時,shl指令已經開始執行了。同理,亂序執行可以大大加快如push,call等指令的執行速度。
7. 寄存器重命名
?????? 書上有一個例子:
? ? ??
?????? 代碼上做了兩件事情,一個是將mem1的內容進行左移3個單位,另一個是將mem3的內容+2,如果將后面三個操作所用的寄存器名名稱不同的名字,那么這個操作也不會被影響,所以處理器為最后三條指令使用了另一個不同的臨時寄存器,因此左移和加法可以并行地進行。再比如:
?????? 假定現在mem1的內容在高速緩存中,可以立即取得,但mem2的內容不在高速緩存中,也就是說,算術左移可以在add之前就進行了,所以我們為左移設定一個新的臨時寄存器,那么這樣eax的內容認識以前的,他將一直保存著這個值,直到ebx的內容就緒,然后和它一同做加法運算。如果沒有寄存器重命名機制,左移操作將不得不等待從內從中讀取mem2的內容到EBX寄存器中以及加法操作。
?????? 在所有操作都完成后,那個代表EAX寄存器的最終結果的臨時寄存器的內容被寫入真實的EAX寄存中,該處理過程被稱為引退(Retirement)。所有通用寄存器,棧指針,標志,浮點寄存器,甚至段寄存器都有可能被重命名。
8. 分支目標預測
?????? 如果遇到轉移指令,那么后面那些進入流水線的指令將會無效,所以我們要清空流水線,那么為了解決這個問題,Intel引入了分支預測技術,因為程序中可能出現大量的循環,所以遇到轉移指令(條件轉移指令,loop等),處理器將會預測他下一次還會轉移到某個標號處而不是往下執行,在處理器內部,有一個小容量的高速緩存器,叫做分支目標緩存器(Branch Target Buffer,BTB)。當處理器執行了一條分支語句后,它會在BTB中記錄當前指令的地址,分支目標的地址,以及本次分支預測的結果,下一次,在轉移指令實際執行前,處理器會查看BTB,看看有沒有最近轉移的記錄,如果能找到對應的條目,則推測和上一次相同的分支,把改分支的指令送入流水線。如果預測失敗,再清空流水線,刷新BTB。
★PART2:32位的x86系統的指令系統
1. 32位處理器的尋址方式
?????? 如果處理器在16位模式下,沒有指令前綴0x66,則認為指令是傳統的16位尋址方式,如果有指令前綴0x66,則是32位尋址方式,在32位模式下,沒有指令前綴0x66,則認為指令是傳統的32位尋址方式,如果有指令前綴0x66,則是16位尋址方式。指令默認使用32位寬度的寄存器和32位的立即數,如果存在內存尋址,則偏移量也是32位的。
?????? 32位模式下,內存尋址可以使用全部的32位通用寄存器作為基址寄存器,還可以加上一個除了ESP的32位通用寄存器作為變址寄存器,變址寄存器還可以允許乘以1,2,4和8作為比例因子。最后還可以在加上一個8位或者32位的偏移量。比如
??
2. 操縱數大小的指令前綴
?????? 每一條指令都可以擁有前綴,比如重復前綴(rep/repe/repne)、超越前綴指令(如ES:),總線封鎖前綴(LOCK)等。前綴是可選得,每個前綴的長度是1個字節,每個指令都可以擁有0-4個前綴。
?????? 16位模式下的編碼格式和32位的編碼格式是一樣的,但是解釋是不同的,所以編寫的時候就要考慮指令的運行環境,為了指明程序的默認運行環境,編譯器提供了偽指令bits,用于指明其后的指令應該被編譯成16位的,還是32位的(16位模式是默認的編譯模式,如果沒頭指定指令的編譯模式,則默認是16bits的)。
3. 一般指令的拓展
?????? 32位處理器擁有32位寄存器和算術邏輯部件。而且系統內存芯片之間的數據通路至少是32位的,所有寄存器或者內存單元為操作數的指令都被擴充,這些拓展操作即使是在16位模式下(實模式或者保護模式)都是可以用的,比如最簡單的加法指令。
?????? 壓棧出棧的操作比較特殊。
?????? 壓入一個字節立即數,則無論是在16位模式下或者是32位模式下,一定要使用byte,關鍵字byte在push和pop操作上是不會給機器碼加上指令前綴的,但是執行的時候,無論什么時候,push(pop)都不會壓入(彈出)一個字節的數據,在16為模式下,這兩個指令使用SP指針,按照有符號數的規則填充到字節的高八位,然后壓棧(彈出),32位使用ESP,按照有符號數的規則填充到高24位再壓棧(彈出)。
?????? 如果要壓入一個字立即數,則無論是在16位模式下或者是32位模式下,一定要使用前綴word,在16位模式下,執行push操作,SP先-2,然后直接壓入該字;在32位模式下,會填充字的高16位(按照有符號數的規則),ESP相應-4,然后壓入雙字,pop操作類似。
?????? 如果要壓入一個雙字立即數,則無論是在16位模式下或者是32位模式下,一定要使用dword,而且棧指針寄存器(SP或者ESP)都要-4。pop類似。
?????? (壓入(彈出)通用寄存器可以不用關鍵字byte,word,dword修飾,內存單元一定要關鍵字byte,word,dword修飾,處理器壓入內存操作行為和壓入立即數的行為是一樣的)。
?????? 壓入段寄存器的時候,在16位模式下,先將SP的內容-2,然后直接壓入;如果是在32位模式下,則會先將段寄存器的內容用0拓展到32位(高16位全是0),然后,將ESP的內容減去4,然后再壓入拓展后的32位的值。(段寄存器能用的只有16位)。
?
轉載于:https://www.cnblogs.com/Philip-Tell-Truth/p/5204154.html
總結
以上是生活随笔為你收集整理的ASM:《X86汇编语言-从实模式到保护模式》第10章:32位x86处理器的编程架构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【小白的CFD之旅】14 实例反思
- 下一篇: Makefile的补充学习