匯編學習筆記(3)-80x86指令集


前言

  (1)指令的一般格式

    [標號:] 助記符 [操作數1 , [操作數2]] [; 注釋]

  一行一條指令

  助記符就是指令的名稱,每條指定必定有個助記符。 助記符前面的標號是給匯編編譯器看的,由我們自己取名,一般取表示本段功能的相關的名字,對編譯器而言表示的是指令的地址。

  每個指令根據指令作用的不同會帶有一個或者兩個操作數,如果有兩個操作數,則操作數中間用  逗號""  隔開。 ;之后到本行結束為注釋 是寫個我們自己看的內容,用於描述指令的功能,方便理解程序功能。編譯器會將注釋內容直接舍棄。 

  注意: 每條指令都會有一些使用限制,有些限制可能是適用於所有指令的,我會盡量用相同的顏色標記出相同的限制。

 

  (2)全局規則:

    源操作數和目的操作數類型要保持一致,要么都是字,要么都是字節

    除了串操作指令,源操作數和目的操作數不能同時為 內存單元(可以使用其他寄存器中轉)

      段寄存器之間數據不能互相傳遞

    cs段寄存器只能用專門的指令來操作

 

  (3)指令集分類

    根據指令集的功能大致可分為六類:

    (1) 數據傳送

      MOV;  XCHG;  LEA;   LDS;  LES;  PUSH;  POP;  

    (2) 算數運算

      ADD;  ADC;  INC; SUB;  SBB;  DEC;  NEG;  CMP; MUL;  IMUL;  DIV;  IDIV;  CBW;  CWD;

    (3) 邏輯運算

      NOT;  AND;  OR;  XOR; TEST; SAL;  SHL;  SAR;SHR; ROL; ROR; RCL; RCR;

    (4) 串操作

    (5) 程序控制

      JMP;   JC;   JNC;   JP;  JPE;   JA;  JNBE;  JAE;  JNB;  JB;  JNAE;  JE;  JZ;  JNZ;  JNEL;  JO;  JNO;  JS;  JNS;  JG;  JNLE;  JGE;  JNL;  JL;  JNGE;  JLE;  JNG;  JCXZ;  LOOP;  LOOPE;  LOOPZ;  LOOPNE  ;LOOPNZ;  JCXZ;

    (6) CPU控制(標志位控制)

       LAHF;  SAHF;  PUSHF;  POPF;  CLC;  STC;  CMC;  CLD;  STD;  CLI;  STI;

 

數據傳送指令

  1. 數據傳送 MOV

    MOV DST, SRC

    這條指令上面已經用過很多次了,作用就是將SRC的數據復制到DST,指令本身不會對SRC的數據做任何的修改。

    限制:

      源操作數可以是寄存器,累加器,內存單元,立即數  目的操作數可以是寄存器,累加器,內存單元

      源和目的不能同時是段寄存器

      代碼段寄存器CS 不能作為目的

      指令指針寄存器不能作為源,也不能作為目的寄存器

      立即數不能直接傳送到段寄存器,立即數永遠不能作為目的操作數

 

  2. 交換指令 XCHG 

    XCHG VALUE1, VALUE2

    這條指令交換兩個數值。

    限制

      兩個操作數不能有段寄存器,也不能同時是內存單元

      不能有立即數

 

  3.地址傳送指令

     (1) LEA指令 (Load Effective Address)

      LEA REG, OPRD

      該指令的作用是將操作數OPRG的地址傳送到REG寄存器中,和MOV有本質區別,MOV是將操作數OPRG的內容傳送到寄存器,所以LEA的源操作數一定是內存,目的操作數一定是16位寄存器

      限制:

        OPRG 必須是內存地址

        REG 必須是16位通用寄存器

 

    (2) LDS指令 (Load pointer into DS)

      LDS REG, OPRD

      這個指令是LEA的 高級版本,傳送的是32位地址,指令會將段地址存儲到DS寄存器,而偏移部分則存儲到REG寄存器中。

      限制:

        REG可以16位通用寄存器,或者指針寄存器(sp bp,IP不可以)和變址寄存器(SI DI),但是一般都是變址寄存器

        OPRD 必須是32位地址

 

    (3) LES指令 (Load pointer into ES)

      LES REG, OPRD

      和LDS是一模一樣的,唯一的區別就是段地址存儲到ES寄存器中

 

  4.堆棧操作指令

    堆棧是用來存儲臨時數據的一段內存,由SS和SP兩個寄存器定義, SS 定義了堆棧的基礎地址,SP定義了堆棧的當前位置,如下圖所示  

      

     

 SS 在低地址,SP在高地址,有數據入棧之后SP就向上移動,當SP和SS移動到一個位置的時候即表示堆棧滿了,所以SS定義堆棧地址,SP定義堆棧大小,因此堆棧的方向是向低地址方向生長的,先入棧的數據地址反而高。在這種機制下也存在一個問題,就是出棧的時候就沒有一個限制了。應為SP到哪里才算是堆棧空了? 所以使用的時候要當心。

  用途:

    現場和返回地址的保護

    寄存器內容的保護

    傳遞參數

    存儲局部變量

 

  入棧指令 PUSH

   PUSH SRC

   該指令將源操作數SRC壓入棧中,他先將SP指針減2,然后將SRC存入SP所指位置。

     SRC可以是通用寄存器也可以是段寄存器也可以是內存單元。數據存儲入棧的時候16位數據高字節存儲在高地址,低字節存儲在低地址。

 

   出棧指令 POP

    POP DST

   將棧頂數據傳送到DST中,DST可以是通用寄存器,段寄存器(除CS)也可以是內存單元

 

 

算數運算指令

  基本規則

    加減運算對 有符號數和無符號數是一視同仁的,意思就是對數據的加減運算,他會同時認為是無符號運算而影響CF AF標志位,並同時認為是有符號運算而影響OF SF標志位。

    只有通用寄存器和內存單元可以存放計算結果。兩個操作數中不能同時是內存單元

    如果有兩個操作數,類型必須一致

 

  1. 加法指令

    (1)普通加法指令 ADD

      ADD OPD1, OPD2

      這條指令完成功能是 將OPD1 和 OPD2 的內容相加存儲到OPD1中,C代碼表示的話就是 OPD1 = OPD1 + OPD2

    

    (2)帶進位加指令 ADC

      ADC OPD1, OPD2

      這條指令完成功能是 將OPD1 + OPD2 + 進位標志CF 的結果相加存儲到OPD1中,C代碼表示的話就是 OPD1 = OPD1 + OPD2 +CF

 

    (3)加1指令 INC

      INC OPD

      這條指令的功能等效於 ADD OPD , 1,這條指令不影響

    

    簡單解釋下加法命令對標志位的影響

    比如指令組

    MOV AX 7896H      ;   AH = 78H  AL= 96H,對各標志位無影響

      ADD AL, AH    ;   AL = AL + AH,     AL(10EH) = 96H + 78H, 而進制就是  0001 0000 1110 = 1001 0110 + 0111 1000

              ; 這里注意 AL=0E, 應為AL只能保存8位所以最高位的1 被舍棄了,AL= 0000 1110

              ; 顯然結果也不為0 所以ZF = 0

               ; 結果中1的位數是奇數 PF = 0

               ;  首先按照無符號數相加 這里很明顯的 最高位進位了所以 CF =1

              ;  然后看AF寄存器 0110 + 1000 = 1110 也是很明顯的沒有發生進位所以 AF =0              

              ; 然后看有符號數計算怎么算按照有符號數的話 96H 由於最高位是1 所以就是負數了 表示的值就是 -6A,注意CPU認為數據都是補碼形式保存。

              ; 實際的計算是 AL(0EH) = -6AH + 78H,這就是設計的巧妙了,無論是按照有符號還是無符號,二進制結果都是一樣的。

               ;    首先看SF 顯然數據是正數 所以 SF =0

              ;   再看OF 數據也沒超過表示范圍 OF =0

 

  2.減法指令

    (1)普通減法指令 SUB

      SUB OPD1, OPD2

      這套指令完成的功能就是 將 OPD1 -OPD2的結果存儲到 OPD1中, C代碼表示就是OPD1=OPD1-OPD2

 

    (2)帶借位減法指令 SBB

      SBB OPD1, OPD2

      這套指令的功能就是 將 OPD1 -OPD2 - CF的內容保存到OPD1中,C代碼表示就是OPD1 = OPD1 -OPD2 -1

 

    (3)減1指令 DEC  

      DEC OPD

      等效於 SUB OPD1 , 1

 

    (4)取補指令 NEG

      NEG OPD

      這條指令的作用 將 0-OPD 的結果存儲到 OPD中,C代碼表示就是 OPD = 0 - OPD

 

    (5)比較指令 CMP

      CMP OPRD1, OPRD2

      這條指令的作用是OPRD1-OPRD2 結果不保存,但是影響標志位

 

    簡單解釋下減法法命令對標志位的影響

    MOV BX, 9048H  ; BL=48H   BH = 90H

    SUB BH,BL      ;  BL(48H) =  90H  - 48H   1001 0000 - 0100 1000  = 0100 1000 

              ; 顯然結果也不為0 所以ZF = 0

              ; 結果中1的位數偶數 PF = 1

               ; 按照無符號數   整體沒發生借位所以CF =0

              ; 0000 - 0100 借位了所以  AF =1

              ; 按照有符號數 90H 實際表示的數是 - 70H 

              ; 所以實際的計算是 -70H - 48H = -B8H = -184( 1 0100 1000)

              ; 很明顯溢出了 所以OF = 1

              ; 由於溢出了符號位被覆蓋了所以本來是負數的 現在變成正數了所以SF = 0

        

  3.乘法指令

    乘法是這樣的被乘數總是隱藏在AL 或者AX寄存器中,根據成數的長度選擇不同寄存器

    (1)無符號乘法 MUL

      MUL OPD1

      所以如果OPD 是字節那么   就是 AX = AL * OPD,如果OPD是字那么   就是 DX AX = AX * OPD,DX存儲數據的高16位,AX存儲數據的低16位

      如果結果的高半部分不為0 其實意思 字節 * 字節 = 字,或者  字* 字 = 雙字了 CF =1 ,OF =1

 

    (2)有符號乘法 IMUL

      IMUL OPDR 

      用法和MUL完全一樣

      由於有符號乘法的符號位始終在高字節中,所以  字節 * 字節 永遠等於 字,或者  字* 字 永遠等於 雙字了 ,那么標記位的變化是根據高位中是否置包含符號位而沒有其他有效數據來判斷,僅僅是符號位則 cf =0 of =0

      含有有效數據則CF =1 OF =1

      舉例:

        字數據相乘結果是   1000 0000 1010 1010  那么高位 1000 0000 僅僅是符號位有數據那么 CF =0 OF =0, 如果結果是1000 0001 1010 1010那么高位1000 0001 中除了符號位,還有其他有效數據所以

        CF =1 ,OF =1

 

  4.除法指令

    和乘法指令一樣,被除數總是存儲在AX 或者 DX和 AX寄存器中

    (1)無符號除法指令 DIV

       DIV OPDR

     如果OPDR 是字節數據,那么商存儲在 AL寄存器 余數存儲在 AH寄存器,如果OPDR 是字數據那么商存儲在AX寄存器 余數存儲在DX寄存器

     不影響標志位,但是如果除數是0 ,或者AL寄存器,或者AX寄存器無法存儲下 商的時候會認為是溢出,導致觸發0號中斷

 

    (2)有符號觸發指令 IDIV

      IDIV OPDR

      存儲結果的方式和無符號除法一樣 

      也不影響標志位,當除數是0,或者寄存器無法保存下結果的時候觸發0號中斷

 

  5.符號位擴展指令

    (1)字節轉換為字指令 CBW 

      CBW

將AL寄存器的數據的符號位放到到AH,功能就是AX =AL

效果就是假設  AL= 1001 0001 AH = 0000 0000,CBW后 AL= 0001 0001 AH = 1000 0000 

 

(2)字轉雙字 CWD

CWD

將AX的符號位擴展到DX中,作用同CBW一樣

 

邏輯運算和移位指令

  基本規則

    如果指令有兩個操作數,則最多只能有一個是存儲器操作數

    只有通用寄存器和存儲器可以用於存放操作結果

    

  1. 邏輯運算指令

    (1)否操作指令 NOT

      NOT OPRD

      這條指令的作用就是將操作數OPRD取反,然后存儲到OPRD中去

 

    (2)與操作指令 AND

      AND OPRD1, OPRD2

      將兩個操作數進行按位的與操作,然后結果存儲到OPRD1中,注意這個操作會影響PF ZF SF 標志位,所以有個技巧就是自己與自己進行於運算可以清除 進位標志CF

 

    (3)或操作指令 OR

      OR OPRD1, OPRD2

      將兩個操作數進行或操作,結果存儲到OPRD1中,這個操作也是會影響會影響PF ZF SF 標志位,並且也可是通過自己或自己的方式清除 進位標志CF

 

    (4)異或操作 XOR

      XOR OPRD1, OPRD2

      將兩個操作數進行異或操作,結果保存到OPRD1中,這個操作也是會影響會影響PF ZF SF 標志位,如果自己和自己異或將會得到0 並且也會清除 進位標志 CF

 

    (5)測試指令 TEST

      TEST OPRD1, OPRD2

      這條指令也是進行邏輯與操作的,和AND指令的不同在於他不將結果存儲到OPRD1中,也就是說這個指令只影響標志位,不影響操作數。

      舉例:

        比如想測試AL中的 第6和第2位是否為1 則可以使用指令

        test al, 01000100B

        如果都為一則ZF標志位會置位,否則ZF=0

 

  2.一般移位指令

  移位指令分為 左移 和 右移指令,在這基礎上還分為邏輯移動和算數移動兩種,所以一共有四條指令

    SAL OPRD, m    ; 算數左移

    SHL OPRD, m     ;邏輯左移

    SAR OPRD, m     ;算數右移

    SHR OPRD, m       ;邏輯右移

  其中 m 移動的位數, 要么是1 要么是AL寄存器

  移位操作會影響 PF, SF, ZF和 OF標志位

  對於左移操作,其實算數左移和邏輯左移是一模一樣的, 將操作數OPRD 向左移動m位,每移動一位右邊會補0 ,同時移動出去的最高位會進入CF 標志位。

  對於右移操作,算數右移會保持左邊的符號位不動其他位右移,而邏輯右移則不考慮符號位不符號位的通通右移, 空位用0 補足,最高位進入CF標志位 。所以算數右移1位相當於  有符號數/無符號數 除以2    而邏輯右移通通認為是無符號數 除以2

 

  3. 循環移位指令

    循環移位和一般移位的區別就是,一般移位就是移動多少位之后移出去的直接丟棄,后面補0,而循環移位會將移動出去的位放到另一端,將向一個圈不停的轉,舉個例子

  比如 操作數  

    一般左移的結果就是    01010101 => 10101010  => 01010100 => 10101000 => 01010000  => 10100000  => 01000000

    循環左移的結果就是    01100101 =>  11001010 => 10010101 => 00101011 => 01010110 => 10101100 => 01011001    

  循環移位分為帶進位的循環移位和不帶進位的循環移位

    ROL  OPRD, m        ;  循環左移,他每移動一位,操作數左移,最高位進入最低位,同時最高位進入CF

    ROR  OPRD, m        ;  循環右移,他每移動一位,操作數右移,最低位進入最高位,同時最低位進入CF

    RCL  OPRD, m        ;  帶進位的循環左移,他每移動一位,操作數左移,最高位進入CF標志,原來的CF進入最低位

    RCR  OPRD, m        ;  帶進位的循環右移,他每移動一位,操作數左移,最低位進入CF標志,原來的CF進入最高位

     帶進位標志的循環移動就相當於 在原來的操作數前面多加了一位進行移位操作,原來是 8位操作數那么CF就添加到最前面變成9位操作數進行移位操作。所以 不帶進位的循環移動每移動8位相當於還原,帶進位的循環移動每移動9位則相當於還原。

  帶進位的循環的一個用處就是可以將其他操作數的最低位或者最高位送入CF,然后將CF循環入另一個操作數來完成數據的拼裝。

 

程序控制

   基本規則 

  8086/8080 CPU提供了大量的用於流程控制的指令,按照功能可以分為如下四類

    無條件轉移指令和有條件轉移指令

    循環指令

    過程調用和過程返回指令

    軟中斷指令和中斷放回指令

  同時根據轉移的時候是否設置CS寄存器的值又分為 段內轉移(近轉移) 和 段間轉移(遠轉移)

    條件轉移和 循環指令 只能是 段內轉移

    軟中斷指令和中斷放回指令 只能是  段間轉移

    無條件轉移和過程調用以及過程返回指令 則是 段內 段間都可以

  對於無條件轉移和過程調用指令而言,根據確定目的地址的方式不同還分為直接轉移和間接轉移    

  無條件,條件,循環指令是不影響標志位的

 

  1. 無條件轉移指令

    (1)無條件段內直接轉移指令 JMP

    JMP 標號

    這條指令是的控制無條件地轉移到標號地址處。 例如

    NEXT:    MOV AX,CX

        ......

        JMP NEXT

   對於這條指令對應的機器指令是

    指令操作碼 | 地址差

   地址差會在編譯的時候由編譯器計算出,計算的是目的地和JMP指令之后下一條指令的差值,所以這條指令的實際操作就是將差值加到IP寄存器上。

   如果地址差需要一個字節表示那么稱之為 短轉移, 如果需要一個字表示那么稱之為近轉移。使用一個字還是一個字節表示地址差會在編譯的時候計算。編譯器是順序編譯的,如果編譯到這條JMP指令的時候還沒出現JMP的那個標號

   那么編譯器就無法計算出地址差,這個時候編譯器會默認使用字作為操作數,如果你能確定實際上字節就夠了,那么你可以是使用SHORT指令顯式的告訴編譯器使用字節地址,具體指令格式如下

    JMP SHORT NEXT

   這個使用地址差的轉移方式,稱之為相對轉移,相對轉移是有利於代碼段的浮動加載,意思是無論代碼加載在內存哪個位置都可以很好的運行。

 

    (2)無條件段內間接轉移 JMP

    JMP OPRD

    這條指令控制無條件的轉移到OPDR指定的地指出, OPRD可以是通用寄存器也可以是字存儲單元,如下

    JMP CX

    JMP WORD PTR [1234H]

   

    (3)無條件段間直接轉移 JMP

      JMP FAR PTR 標號

      FAR PTR 就是只是編譯器這是一個斷間轉移。

      JMP FAR PTR NEXT

      生成的機器指令將會是

      操作碼   偏移地址  段地址

      這種指令中直接包含轉移目的地的轉移方式叫做絕對轉移

 

    (4)無條件斷間間接轉移 JMP

      JMP OPRD  

      OPRD 必須是雙字的所以指令一般如下

      JMP DWORD PTR [1234H]

      那么內存1234H處低字節存儲的就是IP 高字節存儲的就是CS

  

    順帶一提PTR 類似於 指針的意思,   XXX ptr 就指定接下來的數據 是 什么類型的指針  作用大致相當於 C 中的  強制類型轉換 (XXX*)    比如       DWORD PTR  A 相當於 (DWORD*) A

    

  2. 條件轉移指令

    條件轉移都是段內轉移,同時先使用相對轉移,即通過在IP地址上加個地址差的方式實現。

    條件轉移是不影響標志位的。

    下圖是網上找到的關於條件轉移指令的說明

    

 

有符號數 大於使用G 等於使用E 小於使用L

無符號數 大於使用A 等於使用E 小於使用B

N 表示不

 

條件轉移指令需要配合CMP指令或者TEST等其他指令來配合使用

先使用CMP等指令進行操作,這會影響到標志位,然后使用以上的指令就會根據 CMP等指令影響的標志位做出反映。

 

 循環指令

    使用條件轉移指令可以實現循環,但是為了方便操作8086CPU還是設計了四個循環指令

        (1)計數循環指令 LOOP

    LOOP 標號

    這條指令使寄存器CX的值減1,如果結果不等於0,則轉移到標號,否則順序執行LOOP指令后的指令。

    所以LOOP 相當於

    DEC CX

    JNZ 標號

 

  (2)等於/全零循環指令 LOOPE/LOOPZ

  LOOPE 標號

 或者

  LOOPZ 標號

  這條指令使寄存器CX的值減1,如果結果不等於0,並且ZF=1, 則轉移到標號,否則順序執行LOOP指令后的指令。這條指令的CX減一操作不影響標志位。

      

      (3)不等於/非全零循環指令 LOOPNE/LOOPNZ

    LOOPNE 標號

    或者

    LOOPNZ 標號

    這條指令使寄存器CX的值減1,如果結果不等於0,並且ZF=0, 則轉移到標號,否則順序執行LOOP指令后的指令。這條指令的CX減一操作不影響標志位。

 

  (4)跳轉指令 JCXZ

    JCXZ 標號

    該指令執行當CX=0的時候轉跳到標號處,否則順序執行

 

 標志操作指令

  此組命令是專門針對標記寄存器和標志位進行的。

  1. 標志傳送指令

    (1) LAHF指令(Load AH with Flags)

      LAHF

      此指令是將標志位的低8位(SF ZF AF PF CF)保存到AH 寄存器中

 

    (2) SAHF指令(Store AH with Flags)

      SAFH

      此指令和LAHF是相對的,SAFH指令是將AH寄存器中的內容傳送到標記寄存器的低八位

 

    (3) PUSHF 指令

      PUSHF

      此指令是將標志寄存器的內容全部壓棧。

 

    (4) POPF 指令

      POPF

      此命令和PUSHF是一對的,將堆棧中的數據傳送到標記寄存器。

 

  2. 標志位操作指令

    此組指令是專門用來處理指定標志位的。

    (1) 清進位標志位 CLC (Clear Carry flag)

      CLC

      使進位標志設為0

 

    (2) 置進位標志位 STC (SeT Carry flag)

      STC  

      使進位標志設為1

 

    (3) 進位標志取反 CMC (CoMplement Carry flag)

      CMC

      如果CF=1 則 CF 置為0 ,如果CF=0 則 CF置為1

 

    (4) 清方向標志 CLD (CLear Direction flag)

      CLD  

      使方向標志DF 置為0,使串操作地址按照增的方式變化

 

    (5) 置方向標志 STD (Set Direction flag)

      STD

      使方向標志DF 置為0, 使串操作地址按照減的方式變化

    

    (6) 清中斷允許標志 CLI (CLear Interrupt enable flag)

      CLI

      該指令使中斷允許標志IF 設為0 ,於是CPU就不響應外部裝置的可屏蔽中斷,但是對不可屏蔽中斷和內部中斷都沒有音響。

 

    (7) 置中斷允許標志 STI (SeT Interrupt enable flag)

      STI

      該指令使中斷允許標志IF 設為1 ,這樣CPU就可以相應可屏蔽中斷


免責聲明!

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



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