匯編語言期末復習——第四章 程序結構


一、順序程序結構

  1. 程序的靜態分析  略
  2. 程序的動態分析  略
  3. 實例:讀取CMOS RAM數據程序    略

二、分支程序結構

  1. 無條件轉移指令
    1. 代碼段寄存器CS指出代碼段的段基地址,指令指針IP給出將要執行的偏移地址
    2. 轉移范圍
      1. 段內轉移——只改變IP  
        1. 段內轉移也稱近轉移(NEAR)
        2. 如果轉移范圍可以用1字節編碼表達,即向地址增大方向轉移127字節,向地址減小方向轉移128字節,形成短轉移(SHORT)
      2. 段間轉移——更改CS和IP
        1. 也稱遠轉移(FAR)
    3. 指令尋址方式
      1. 相對尋址方式
        • 提供目標地址相對於當前指令指針IP的位移量
        • 目標地址(轉移后的IP)=當前IP+位移量
      2. 直接尋址方式
        • 直接提供目標地址
        • 目標地址(轉移后的CS和IP)=指令操作數
      3. 間接尋址方式
        • 指示寄存器或存儲單元
        • 目標地址來自寄存器或存儲單元,間接獲得
      4. 注:目標地址=目的地址=轉移地址
    4. JMP指令
      1. 四種類型
        1. 段內轉移,相對尋址
          JMP LABLE    ;IP=IP+位移量

           

        2. 段內轉移,間接尋址
          JMP R16    ;IP=R16   寄存器間接尋址   
          JMP M16    ;IP=M16  存儲器間接尋址

           

        3. 段間轉移,直接尋址
          JMP LABLE    ;IP=LABLE的偏移地址,CS=LABLE的段選擇器

           

           

        4. 段間轉移,間接尋址
          JMP M32    ;IP=M32,CS=M32+2

           

      2. MASM會根據存儲模型和目標地址等信息自動識別是哪種轉移。
        1. 匯編程序提供了操作符:
          • SHORT
          • NEAR PTR 
          • FAR PTR
          • 可以強制轉換一個符號、段名或子程序名的類型,形成相應的控制轉移。
      3. 實例:
        ;eg403.asm
            .model small
            .stack
            .data
        nvar    dw ? 
        fvar    dd ?
            .code
            .startup
        labl0:    jmp labl1             ;段內(短)轉移、相對尋址
            nop
        labl1:    jmp near ptr labl2    ;段內(近)轉移、相對尋址
            nop
        labl2:    mov ax,offset labl3
            jmp ax                      ;段內轉移、寄存器間接尋址
            nop
        labl3:    mov ax,offset labl4
            mov nvar,ax 
            jmp nvar                    ;段內轉移、存儲器間接尋址
            nop
        labl4:    jmp far ptr labl5     ;段間轉移、直接尋址
            nop
        labl5:    mov ax,offset labl6
            mov word ptr fvar,ax
            mov dx,seg labl6
            mov word ptr fvar+2,dx
            jmp fvar                    ;段間轉移、間接尋址
            nop
        labl6:    
            .exit
            end

         

  2. 條件轉移指令
    1. 格式及說明
      • JCC LABEL
        ;條件滿足,發生轉移;
        ;否則順序執行下條指令

         

      • LABEL表示目標地址,采用段內相對尋址(只能是-128~127之間的短轉移)
      • 條件轉移指令不影響標志,但要利用標志。
      • 16種,兩類標志判斷如下:
        • 單個標志狀態作為條件
        • 兩數大小關系作為條件
      •  

         

    2. 實例!開始瘋狂上代碼!
      • 個數折半(如果是奇數就加一,如果是偶數直接顯示)
        •     mov ax,885             ;假設一個數據
              shr ax,1               ;數據右移進行折半
              jnc goeven             ;余數為0,即CF=0條件成立,不需要處理,轉移
              add ax,1               ;否則余數為1,即CF=1,進行加1操作
          goeven:    call dispuiw    ;顯示結果
        • 上述代碼可以利用ADC優化成:
              mov ax,887    ;假設一個數據
              shr ax,1      ;數據右移進行折半
              adc ax,0      ;余數=CF=1,進行加1操作;余數=CF=0,沒有加1
              call dispuiw  ;顯示結果
      • 位測試程序(如果AL的D1=1 顯示“READY TO" GO! D1=0顯示”NOT READY")

        •     .data
          no_msg     db 'Not Ready!','$'
          yes_msg    db 'Ready to Go!','$'
              .code
              .startup
              mov al,56h               ;假設一個數據
              test al,02h              ;測試D1位(使用D1=1,其他位為0的數據)
              jz nom                   ;D1=0條件成立,轉移
              mov dx,offset yes_msg    ;D1=1,顯示准備好
              jmp done                 ;跳轉過另一個分支體!
          nom:    mov dx,offset no_msg ;顯示沒有准備好
          done:    mov ah,9
              int 21h
      • 奇偶校驗程序(在字符的ASC碼中的1的個數為奇數令校驗位為0)程序實現在ASC碼的最高位加上校驗位
        • Tdata    db ?            ;保存待發送數據的變量
          
              mov ah,1             ;1號功能
              int 21h              ;鍵盤輸入
              and al,7fh           ;最高位置“0”、其他位不變,同時標志PF反映“1”的個數
              jnp next             ;個數為奇數,則轉向NEXT
              or al,80h            ;最高位置“1”、其他位不變 
          next:    mov Tdata,al    ;保存待發送的數據
      • 數據大小比較程序(相等顯示“EQUAL”,第一個數大顯示“FIRST”,第二個數大顯示“SECOND”)

        • ;eg407.asm
              .model small
              .stack
              .data
          var1    dw -3765
          var2    dw 8930
          msg0    db 'Equal$'
          msg1    db 'First$'
          msg2    db 'Second$'
              .code
              .startup
              mov ax,var1           ;取第1個數據
              cmp ax,var2           ;與第2個數據比較
              je equal              ;兩數相等,轉移
              jnl first             ;第1個數據大,轉移
              mov dx,offset msg2    ;第2個數據大
              jmp done
          first:    mov dx,offset msg1
              jmp done
          equal:    mov dx,offset msg0
          done:    mov ah,9         ;顯示結果
              int 21h
              .exit
              end

           

           

  3. 單分支
      • 求絕對值
        • ;eg408.asm
              .model small
              .stack
              .data
          var    dw 0b422h             ;有符號數據
          result    dw ?               ;保存絕對值
              .code
              .startup
              mov ax,var
              cmp ax,0                 ;比較AX與0
              jge nonneg               ;條件滿足:AX≥0,轉移
              neg ax                   ;條件不滿足:AX<0,為負數,需求補得正值
          nonneg:    mov result,ax     ;分支結束,保存結果
              .exit
              end
      • 字母判斷程序

        •  

              mov ah,1
              int 21h       ;輸入一個字符,從AL返回值
              cmp al,'A'    ;與大寫字母A比較
              jb done       ;比大寫字母A小,不是大寫字母,轉移
              cmp al,'Z'    ;與大寫字母Z比較
              ja done       ;比大寫字母Z大,不是大寫字母,轉移
              or al,20h     ;轉換為小寫
              mov dl,al
              mov ah,2
              int 21h       ;顯示小寫字母
          done:

           

  4. 雙分支程序
      • 顯示數據最高位
        • var    dw 0b422h     ;有符號數據
          
              mov bx,var
              shl bx,1         ;BX最高位移入CF標志
              jc one           ;CF=1,即最高位為1,轉移
              mov dl,'0'       ;CF=0,即最高位為0:DL←'0'
              jmp two          ;一定要跳過另一個分支體
          one:    mov dl,'1'   ;DL←'1'
          two:    mov ah,2
              int 21h          ;顯示

           可以使用ADC消除分支:

        • XOR DL,DL
              mov bx,var
              shl bx,1    ;BX最高位移入CF標志
              adc dl,'0'
              mov ah,2
              int 21h     ;顯示
      • 有符號數運算溢出程序
        • ;eg411.asm
              .model small
              .stack
              .data
          var1    dw 24680
          var2    dw -9999
          var3    dw ?
          okmsg    db 'Correct!','$'              ;正確信息
          errmsg    db 'ERROR ! Overflow!','$'    ;錯誤信息
              .code
              .startup
              mov ax,var1 
              sub ax,var2                 ;求差
              jo error                    ;有溢出,轉移
              mov var3,ax                 ;無溢出,保存差值
              mov dx,offset okmsg         ;顯示正確
              jmp disp
          error:    mov dx,offset errmsg  ;顯示錯誤
          disp:    mov ah,9
              int 21h
              .exit
              end

           

  5. 多分支程序 
    • 可以使用表結構實現多分支

三、循環程序結構

  1. 循環指令
    • LOOP LABEL
      • CX=CX-1 如果CX不等於0,循環到label,否則順序執行
    • JCXZ LABEL
      • CX=0轉移,否則順序執行
    • 目標地址采用相對短轉移
    • 使用CX作為計數器
      • 如果CX=0將循環pow(2,16)次發生錯誤
      • 此時可以使用JCXZ先判斷CX是否為零
    • 實例:數組求和程序
      • ARRAY  DW  136,-138,133,130,-161
        SUM  DW  ?
        
        XOR AX,AX
        MOV CX,LENGTHOF ARRAY
        MOV BX,OFFSET ARRAY
        AGAIN:  ADD AX,ARRAY[BX]
        ADD BX,TYPE ARRAY
        LOOP AGAIN
        MOV SUM,AX
  2. 計數控制循環
    • 求最大值程序
      • ;eg414.asm
            .model small
            .stack
            .data
        array    dw -3,0,20,900,587,-632,777,234,-34,-56    ;假設一個數組
        count    = lengthof array    ;數組的元素個數
        max    dw ?    ;存放最大值
            .code
            .startup
            mov cx,count-1           ;元素個數減1是循環次數
            mov si,offset array
            mov ax,[si]              ;取出第一個元素給AX,用於暫存最大值
        again:    add si,2
            cmp ax,[si]              ;與下一個數據比較
            jge next                 ;已經是較大值,繼續下一個循環比較
            mov ax,[si]              ;AX取得更大的數據
        next:    loop again          ;計數循環
            mov max,ax               ;保存最大值
            .exit
            end
    • 簡單的加密解密程序
        • ;eg415.asm
              .model small
              .stack
              .data
          key    db 234    ;假設的一個密鑰
          buffer    db 'This is a secret.','$'    ;待加密的信息(字符串)
          count    = sizeof buffer-1    ;不處理最后結尾字符
          msg1    db 'Encrypted message: ','$'
          msg2    db 13,10,'Original messge: ','$'
              .code
              .startup
              mov cx,count    ;CX=字符個數,作為循環的次數
              xor bx,bx    ;BX指向待處理的字符
              mov al,key    ;AL=密鑰
          encrypt:    xor buffer[bx],al    ;異或加密
              inc bx    ;指向下一個字符
              cmp bx,cx
              jb encrypt    ;沒有指向最后字符,繼續處理
              mov dx,offset msg1    ;顯示提示信息
              mov ah,9
              int 21h
              mov dx,offset buffer    ;顯示加密后的密文
              mov ah,9
              int 21h
              ;
              xor bx,bx    ;BX指向待處理的字符
              mov al,key    ;AL=密鑰
          decrypt:    xor buffer[bx],al    ;異或解密
              inc bx
              dec cx
              jnz decrypt    ;等同於指令:loop decrypt
              mov dx,offset msg2
              mov ah,9
              int 21h
              mov dx,offset buffer    ;顯示解密后的明文
              mov ah,9
              int 21h
              .exit
          
              end

             

  3. 條件控制循環
    • 字符個數統計(統計以0結尾的字符串中的字符個數)
      • ;eg416a.asm
          include io.inc
            .model small
            .stack
            .data
        string    db 'Do you have fun with Assembly?',0    ;以0結尾的字符串
            .code
            .startup
            xor bx,bx    ;BX用於記錄字符個數,同時也用於指向字符的指針
        again:    mov al,string[bx]
            cmp al,0    ;也可以使用指令“test al,al”
            jz done
            inc bx    ;個數加1
            jmp again    ;繼續循環
        done:    mov ax,bx    ;顯示個數
            call dispuiw
            .exit
            end
    • 斐波那契
      • ;eg417a.asm
          include io.inc
            .model small
            .stack
            .data
        
            .code
            .startup
            mov ax,1    ;AX=F(1)=1
            call dispuiw    ;顯示第1個數
            call dispcrlf    ;回車換行
            call dispuiw    ;顯示第2個數
            call dispcrlf    ;回車換行
            mov bx,ax    ;BX=F(2)=1
        again:    add ax,bx    ;AX=F(N)=F(N-2)+F(N-1)
            jc done
            call dispuiw    ;顯示一個數
            call dispcrlf    ;回車換行
            xchg ax,bx    ;AX=F(N-2),BX=F(N-1)
            jmp again
        done:
            .exit
            end
  4. 多重循環    
    • 冒泡排序
      • ;eg418.asm
            .model small
            .stack
            .data
        array    dw 587,-632,777,234,-34    ;假設一個數組
        count    = lengthof array    ;數組的元素個數
            .code
            .startup
            mov cx,count    ;CX←數組元素個數
            dec cx    ;元素個數減1為外循環次數
        outlp:    mov dx,cx    ;DX←內循環次數
            mov bx,offset array
        inlp:    mov ax,[bx]    ;取前一個元素
            cmp ax,[bx+1]    ;與后一個元素比較
            jng next
            ;前一個不大於后一個元素,則不進行交換
            xchg ax,[bx+1]    ;否則,進行交換
            mov [bx],ax
        next:    inc bx    ;下一對元素
            dec dx
            jnz inlp    ;內循環尾
            loop outlp    ;外循環尾
            .exit
        
            end
    • 字符剔除(剔除空格
      • ;eg419.asm
            .model small
            .stack
            .data
        string    db 'Let us have a try !',0dh,0ah,'$'    ;以“$”結尾的字符串
            .code
            .startup
            mov dx,offset string    ;顯示處理前的字符串
            mov ah,9
            int 21h
            mov si,offset string
        outlp:    cmp byte ptr [si],'$'    ;外循環,先判斷后循環
            jz done    ;為“$”結束
        again:    cmp byte ptr [si],' '    ;檢測是否是空格
            jnz next    ;不是空格繼續循環
            mov di,si    ;是空格,進入剔除空格分支
        inlp:    inc di    ;該分支是循環程序
            mov al,[di]    ;前移一個位置
            mov [di-1],al
            cmp byte ptr [di],'$'    ;內循環,先循環后判斷
            jnz inlp    ;內循環結束處
            jmp again    ;再次判斷是否為空格(處理連續空格情況)
        next:    inc si    ;繼續對后續字符進行判斷處理
            jmp outlp    ;外循環結束處
        done:    mov dx,offset string    ;顯示處理后的字符串
            mov ah,9
            int 21h
            .exit
        
            end

         

  5. 串操作指令
    1. 以字節、字和雙字為單位多個數據存放在連續的主存區形成數據串,(也就是數組)
    2. 特殊的尋址方式:
      • 源操作數用寄存器SI間接尋址,默認在數據段DS中,即DS:[SI],允許段超越
      • 目的操作數用寄存器DI間接尋址,默認在附加段ES中,即ES:[DI],不允許段超越
      • 每執行一次串操作源指針SI和目的指針DI自動±1或±2
      • CLD設置方向標志DF=0 指針增加
      • STD設置方向標志DF=1 指針減小
    3. 串傳送
      1. 傳送 MOVS 
        • MOVSB  ES:[DI] ← DS:[SI]  SI++,DI++
        • MOVSW  ES:[DI] ← DS:[SI]  SI+=2,DI+=2
      2. 存儲 STOS
        • STOSB  ES:[DI]←AL  DI++
        • STOSW ES:[DI]←AX  DI+=
      3. 讀取 LODS
        • AL←DS:[SI]  SI++
        • AX←DS:[SI] SI+=2
      4. 重復REP  計數器也是CX

四、習題

4.1(2)數據的直接尋址和指令的直接尋址有什么區別?

    數據的直接尋址直接給偏移地址(變量名/有效地址)

    指令的直接尋址使用段內轉移相對量(標號)

  (5)什么是奇偶校驗?

    包括校驗位在內的數據為為“1”的個數為奇數是奇校驗。

  (6)助記符JZ和JE為什么表達同一條指令?

    JZ為兩個數相減差值為零,JE為兩個數相等。

  (9)如果循環體的代碼量遠超過128字節,還能用LOOP指令實現計數控制循環嗎?

    短轉移前后不能超過128字節。

  (10)什么是“先循環、后判斷”循環結構?

    先執行一遍循環體。

4.2 判斷題

  (2)指令的相對尋址都是段內轉移。

    對。

  (4)JMP指令對應高級語言的GOTO語句,所以不能使用。

    錯,可以使用。

  (6)JA和JG指令的條件都是“大於”,所以是同一個指令的兩個助記符。

    錯。JA判斷無符號數JG判斷有符號數。

  (7)JC和JB指令的判斷條件都是CF=1,所以是同一條指令。

    錯。JC前有可能是移位,JB必須是有減法操作才能判斷。

  (10)若CX=0,則LOOP指令和JCXZ指令都發生轉移。

    對,而且LOOP會執行pow(2,16)次。

4.3填空題

  (2)MASM給短轉移、近轉移、和遠轉移定義的類型名依次是   SHORT    NEAR     FAR  。

  (3)假設BX=1256H,字變量TABLE的偏移地址是20A1H,數據段偏移地址32F7H處存放3280H,執行指令“JMP BX"后IP=    1256H   ,執行指令”JMP TABLE[BX]“后IP=  3280H   。

  (4)"CMP AX,3721H"指令之后是JZ指令,發生轉移的條件是AX=   3271H  ,此時ZF=    1   

  (5)執行""SHR BX,1"指令后,JNC發生轉移,說明BX的D0=    0   

  (6)在DX等於0時轉移,可以使用指令“CMP DX,     0    ",也可以使用"TEST DX,    FFFFH   "構成條件,然后使用JE指令實現轉移。

4.4 已知VAR1、VAR2、VAR3和VAR4是16位有符號整數,用匯編語言程序片段實現如下C語句:

  VAR4=(VAR1*6)/(VAR2-7)+VAR3

;用匯編語言實現var4=(var1*6)/(var2-7)+var3
    .model small
    .stack
    .data
var1    dw  10
var2 dw 9
var3 dw 15
var4 dw ?
    .code
    .startup
    mov ax,var1
    mov bx,6
    mul bx
    mov cx,var2
    sub cx,7
    div cx
    add ax,var3
    mov var4,ax
    
    add var4,30h
    mov dx,var4
    mov ah,9
    int 21h
    .exit
    end

 

4.7 為了驗證例4-3程序的執行路徑,可以在每個標號前后增加顯示一個數字的功能,是的程序運行后顯示數碼123456.

;eg403.asm
    .model small
    .stack
    .data
nvar    dw ? 
fvar    dd ?
    .code
    .startup
labl0: 
    jmp labl1    ;段內(短)轉移、相對尋址
    nop
labl1:    
    mov ah,2
    mov dl,'1'
    int 21h
    jmp near ptr labl2    ;段內(近)轉移、相對尋址
    nop
labl2:    
    mov ah,2
    mov dl,'2'
    int 21h
    mov ax,offset labl3
    jmp ax    ;段內轉移、寄存器間接尋址
    nop
labl3:    
    mov ah,2
    mov dl,'3'
    int 21h
    mov ax,offset labl4
    mov nvar,ax
    jmp nvar    ;段內轉移、存儲器間接尋址
    nop
labl4:
    mov ah,2
    mov dl,'4'
    int 21h
    jmp far ptr labl5    ;段間轉移、直接尋址
    nop
labl5:    
    mov ah,2
    mov dl,'5'
    int 21h
    mov ax,offset labl6
    mov word ptr fvar,ax
    mov dx,seg labl6
    mov word ptr fvar+2,dx
    jmp fvar    ;段間轉移、間接尋址
    nop
labl6:    
    mov ah,2
    mov dl,'6'
    int 21h

    .exit
    end

 

4.13 8086處理器的指令CWD將AX符號擴展到DX。假若沒有該指令,編程實現該指令功能。

;編程實現將ax符號擴展到dx
    .model small
    .stack
    .data
    .code
    .startup
;第一種方法:用符號擴展的含義
    mov ax,45h ;初始化ax
    test ax,ax
    js one
    mov dx,0
    jmp over1
one: 
     mov dx,0ffffh
over1:    
    nop            ;空操作,第一種方法結束

;第二種方法:用移位實現
    mov ax,0f045h ;初始化ax
    mov bx,ax  ;為了不改變ax的值,將ax值賦值給bx
    shl bx,1      ;取ax的最高位到cf
    rcr dx,1      ;將cf的值移入dx最高位
    mov cl,15
    sar dx,cl    ;dx算數右移15,dx所有位變成ax的符號位

.exit
    end

 

4.15 編程:先提示輸入數字"INPUT NUMBER:0~9",然后再下一行顯示輸入的數字,結束;如果不是鍵入了0~9數字,就提示錯誤'ERROR',繼續等待輸入數字。

;輸入數字
    .model small
    .stack
    .data
errormsg    db 'Error!',0dh,0ah,'$'    ;9個信息
msg    db 'Input number(1~9): $'    ;提示輸入字符串
crlf    db 0dh,0ah,'$'    ;回車換行字符
    .code
    .startup
again:    
    mov dx,offset msg
    mov ah,9
    int 21h    ;提示輸入
    mov ah,1    ;等待按鍵
    int 21h
    push ax    ;暫時將輸入的按鍵字符保存到堆棧
    mov dx,offset crlf    ;回車換行
    mov ah,9
    int 21h
    pop ax    ;恢復按鍵字符
    cmp al,'1'    ;數字 < 1?
    jb error
    cmp al,'9'    ;數字 > 9?
    ja error
    mov dl,al
    mov ah,2
    int 21h ;顯示數字
    jmp done;結束
error:
    mov dx,offset errormsg
    mov ah,9
    int 21h    ;提示輸入
    jmp again
done:
    .exit
    end

 


免責聲明!

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



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