16位和32位代碼段

在對ix86編程時,有時候需要從實模式變換到保護模式(如在DOS時代要訪問擴展內存,或者編寫引導代碼,當然,如果在32位的操作系統下面編程,是碰不到這個問題的),總是要涉及16位代碼段和32位代碼之間的跳轉問題。因此有必要對他們進行區分。

16位代碼段和32位代碼段的主要區別是,在16位代碼段中,跳轉目標的偏移用16位表示,而在32位代碼段中,跳轉目標的偏移用32位表示。

在實模式下,CPU總是進行16位的跳轉,即當它在解析跳轉的目標時,總是讀取內存中的16位的值作爲跳轉目標。爲此,彙編器要配合CPU,產生這種用16位表示偏移量的代碼。

在保護模式下,問題要稍微複雜一點,因爲此時CPU如何解析跳轉目標,和目標代碼段的屬性有關係。在保護模式下,每一個代碼段都由一個代碼段描述符表示。在代碼段描述符中有一個字段表示這個段是16位代碼段還是32位代碼段,如果跳轉目標在16位代碼段中,CPU就從內存中讀取一個16位的值作爲跳轉的偏移;如果跳轉目標在32位代碼段中,CPU就從內存中讀取一個32位的值作爲跳轉的偏移。由此可見,在保護模式下,偏移量並不總是32位的值。在32位的操作系統中,用戶程序總是被編譯成32位的代碼,爲此,彙編器要配合CPU,產生用32位表示偏移量的代碼。

現在問題出來了,當CPU從實模式變換到保護模式時,如何產生出正確的可以讓CPU運行的代碼?

有兩種方法可以解決這個問題。

第一種,在實模式下執行的代碼理所當然的要編譯成16位偏移的代碼,在保護模式下執行的代碼也編譯成16位偏移的代碼,並且在代碼段的描述符中,把代碼段設成16位的。這種方法的優點是,即使彙編器不能產生32位偏移的代碼,我們也可以進行保護模式下的程序設計。這種方法的缺點是,因爲偏移量是16位的,所以代碼段的大小受到限制。

第二種,在實模式下執行的代碼還是要編譯成16位偏移的代碼,在保護模式下執行的代碼編譯成32位偏移的代碼,並且在代碼段的描述符中,把代碼段設成32位的。這種方法的優點是,程序能夠充分利用32位處理器的強勁功能。但是這種方法有一個麻煩的地方:我們不可避免的要從16位偏移的代碼跳轉到32位偏移的代碼。這又如何實現?因爲在編寫16位偏移的代碼時,一般情況下,我們需要讓彙編器產生16位偏移的代碼,但是對於跳轉到32位代碼段的那個跳轉指令,我們又希望彙編器產生32位偏移的代碼。這是一個矛盾。要解決這個矛盾,有好幾種方法可供選擇。我知道三種方法:1.不要彙編器產生代碼,而是由程序員以變量定義的形勢,把機器代碼寫入代碼段。這種方法不要彙編器的幫助。2.有的彙編器允許程序員指定跳轉偏移量的尺寸,並根據程序員的指定產生正確的代碼,例如NA***。3.把彙編器從16位編譯模式轉換到32位的編譯模式,NA***和GAS都支持這種方法。但是有的彙編器只允許在段定義時指定編譯模式,在段內不能再改變編譯模式,這種方法就行不通了。


代碼段時32位還是16位是由段定義說明中的[BITS16][BITS32]決定的。

[BITS16]表示這個段是按照16位進行編譯的,代碼地址(比如一個label)都是16位的;[BITS32]表示編譯時這個段中指令的地址都是32位的。


D/B:當描述符指向的是可執行代碼段時,這一位叫做D位,D=1時,CPU假定地址爲32位,操作數爲32/8位,D=0CPU假定地址爲16位,操作數爲16/8位。

對於代碼段,此時這個標誌稱爲D標誌並用於指出該段中的指令引用有效地址和操作數的默認長度。如果該標誌置位,則默認值是32位地址和32位或8位的操作數;如果該標誌爲0,則默認值是16位地址和16位或8位的操作數。指令前綴0x66可以用來選擇非默認值的操作數大小;前綴0x67可用來選擇非默認值的地址大小

對於代碼段,D/B就是告訴CPU當前代碼段是32位代碼段還是16位代碼段。


32位的代碼段,彙編器在編譯生成機器碼的時候,指令偏移地址都是32位的,操作數爲32爲或者8位的。如果32位代碼段中用到了16位的操作數,則彙編器會自動在16位操作數前加16-bit數前綴。

• The operand-size prefix (66H)
• The address-size prefix (67H)



實模式下,指令偏移地址都是16位的。

發佈了99 篇原創文章 · 獲贊 69 · 訪問量 78萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章