8086匯編語言學習(七) 8086跳轉指令


8086跳轉指令

  目前為止,我們的程序的指令執行都是線性的,從上到下,由CPU自動的增加IP的值,順序的執行指令。但對於復雜的需求,只有線性的指令執行方式是遠遠不夠的。

  對於高級語言,有着如if/else的邏輯跳轉分支,如for/while的循環結構,還有函數子程序的調用與返回等等。正是有了這些能夠控制程序執行指令的不同方式,才能具有足夠的表達能力,滿足足夠復雜的需求,成為一門圖靈完備的語言。那么上述的邏輯跳轉、循環,在基於圖靈機的CPU硬件上是如何實現的呢?通過8086匯編的跳轉指令的學習,我們得以一窺究竟。

  CPU是通過CS:IP來獲取下一條指令的值,那么通過指令修改CS、IP這兩個寄存器的值,便可以控制CPU所執行的指令了。可由於控制CPU執行指令的CS、IP十分的關鍵,因此8086並不允許像其它普通的寄存器一般使用mov等指令對CS、IP修改(mov IP,1000H是非法的),而是提供了專門的指令來控制CS、IP的值,這一類指令被稱為8086跳轉指令。

  跳轉指令按照類型可以分為五種:無條件跳轉指令、有條件跳轉指令、循環指令、過程調用與返回指令以及中斷指令。

無條件跳轉指令(jmp)

  jmp既可以只修改IP,也可以同時修改CS和IP。作為跳轉指令,在編程時需要指定跳轉的位置,進而修改CS/IP的值。

段內轉移

  段內短轉移(IP 變化-128~127):段內短轉移的格式為 jmp short [標號]

assume cs:codesg

codesg segment
    start:mov ax,0
          jmp short s
          add ax,1
        s:inc ax
codesg ends
 
end start

  段內近轉移(IP 變化-32768~32767):當所要跳轉的間隔大於短轉移的時候,就需要使用段內近轉移。段內近轉移和短轉移類似,格式為 jmp near ptr [標號]

  段內轉移只修改IP,不修改CS的值。

段間轉移

  當跳轉的間隔超過了段內近轉移的限制時,就需要使用段間轉移了。段間轉移的格式為jmp far ptr [標號]。和內存尋址一樣,jmp指令所要跳轉的位置也可以通過寄存器或是指令中的立即數指定。

jmp寄存器跳轉

  jmp [16位寄存器] 例如 jmp ax,寄存器跳轉屬於段內跳轉

jmp內存跳轉  

  jmp word ptr [內存單元地址] 例如: jmp word ptr 2345H,jmp word ptr [bx] ,[]內只要是符合內存尋址方式的語法皆可。jmp word ptr處理的是16位數,屬於段內轉移。

  jmp dword ptr [內存單元地址]  jmp dword ptr和jmp word ptr類似,只不過會將對應地址的處的兩個字/四字節的數據作為偏移地址,其中IP等於指定的內存地址,CS等於指定的內存地址+2(示例)。jmp dword ptr處理的是32位數,屬於段間轉移。

跳轉指令原理

  就轉移指令的實現原理來看,段內轉移是通過相對地址偏移量來控制的段內短轉移可以使得IP偏移2^8的范圍,即(-128~127),而段內近轉移可以使得IP偏移2^16的范圍,即(-32768~32767)。 

  8086的CPU是16位的,在20位的尋址范圍內進行更大幅度的跳轉,16位的偏移地址是不夠的,因此段間轉移的指令是通過絕對地址來實現的。

  雖然理論上段內轉移都可以使用段間轉移來實現,但是由於不同的跳轉指令所占用的內存空間是不一樣的(段內短轉移=8位指令+8位偏移地址=16bit,段內近轉移=8位指令+16位偏移地址=24bit,段間轉移=8位指令+16位段地址+16位偏移地址=40bit)。所以編程時,在滿足需求的前提下還是盡可能的使用更簡單,更節約內存的無條件跳轉指令,提高效率。

  jmp是最直接的無跳轉指令,類似於C語言的goto。對於喜歡結構化編程的人來說,goto的跳轉過於靈活很容易使得大項目中代碼變得晦澀混亂,但是匯編程序所構建的項目不會特別大,jmp還是非常直接和方便的。(從另一個角度看,正是因為匯編語言的抽象能力不夠強,導致很難構建出足夠大型、復雜同時還很可靠的程序)

有條件跳轉指令(jcxz)

  jcxz(jmp if CX is zero)有條件跳轉指令,類似於段內短跳轉jmp short,所能變化的ip范圍同樣為(-128~127)。格式為 jcxz [標號]。唯一的不同在於,只有當滿足條件寄存器cx=0時,才會進行跳轉,否則就和正常情況一樣IP自增,按順序執行下一條指令,這也是jcxz被稱為有條件跳轉指令的原因(只有滿足條件才進行跳轉)。

  需要特別注意的是,通用寄存器ax/bx/cx/dx並不是完全等價的,在某些場合下會具有一些特別的作用,例如上述jcxz便依賴寄存器cx。其作用可以從寄存器的全名中可見一斑,ax/bx/cx/dx並不是英文字母abcd的簡稱,而分別是accumulate-register累加寄存器、based-register基地址寄存器、count-register計數寄存器、data-register 數據寄存器。 

  ax accumulate-register累加寄存器:ax一般用於存放算術、邏輯運算中的操作數或結果。同時I/O指令也都需要使用ax與外設接口傳遞數據。

  bx based-register基地址寄存器 :bx一般用於存放訪問內存時的地址。在8086內存尋址時,指定偏移地址時有提到過。

  cx count-register計數寄存器:cx一般用於有條件跳轉、循環、串操作指令。jcxz和loop循環等指令都依賴於cx寄存器。

  dx data-register 數據寄存器:dx一般用於寄存器間接尋址中的I/O指令中存放I/O端口的地址。

循環指令(loop)

  循環指令同樣依賴寄存器cx。格式為loop [標號]。loop指令的語義是,首先將cx自減1,如果cx不為0,則跳轉至標號處。否則什么也不做,離開循環,順序執行下移。

  循環指令的跳轉范圍和有條件跳轉指令一樣,ip的變化范圍為(-128~127)。

用C風格的偽代碼表示為:

cx--;
if(cx == 0){
    jmp short 【標號】
}else{
    順序執行下一條指令
}

過程調用/返回以及中斷指令(call/ret、int等)  

  過程調用以及CPU處理硬件中斷時,同樣涉及到了程序執行指令的跳轉,分別對應了過程調用/返回指令(例如 call/ret),中斷指令(例如 int)。

  基於內容的相關性,過程調用會在后面的8086匯編子程序進行詳細介紹,而中斷指令則會在中斷相關部分進行展開。

總結  

  不同的跳轉指令都有着跳轉間隔的限制,如果超出了跳轉指令所約定的范圍,則編譯器會在編譯時發現並報錯。

  跳轉指令,特別是無條件跳轉指令需要慎用,無所顧及的使用跳轉指令很容易使得程序的可讀性降低,變成一團剪不斷,理還亂的面團。這在高級語言程序的開發中同樣適用,人的大腦所能同時理解的內容是有限的,必須通過合理的抽象將復雜程序構建成有着良好結構的黑箱子。而隨意的全局變量和不必要的輸入/輸出則會破壞這種模塊化的結構,使人費解,在迭代中逐步脫離開發人員的控制,變成一個吞噬時間的無底洞。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM