匯編語言期末復習——第五章 模塊化程序設計


一、子程序結構

  • 子程序=函數=過程
  • 子程序指令
    • 子程序調用指令
      • CALL LABEL ;調用標號、子程序名指定的子程序
      • 分成近調用(段內調用)和遠調用(段間調用)
      • 入棧返回地址:將CALL下條指令的地址壓入堆棧
        • 近調用:IP入棧
        • 遠調用:CS和IP都入棧
    • 子程序返回指令
      • RET
      • 分為有參數返回和無參數返回,都是出棧返回地址
    • 過程定義偽指令
      • 過程名  PROC
        …………
        …………
        過程名  ENDP

        過程定義應該書寫於.EXIT和END之間;也可以安排在主程序開始執行的第一條語句之前

      • PROC后面可以加參數:NEAR/FAR
  • 子程序設計
    • 利用過程定義偽指令聲明
    • 最后利用RET返回主程序,主程序執行CALL指令調用子程序
    • 壓入彈出成對使用使堆棧保持平衡
    • 子程序開始時保護用到的寄存器內容,返回前逆序彈出恢復到原來的寄存器中。
    • 安排在代碼段的主程序之外
    • 允許嵌套和遞歸
  •   例:回車換行子程序
    • ;eg502.asm
          .model small
          .stack
          .data
      
          .code
          .startup
          
          call dispcrlf
      
          .exit
      dispcrlf proc ;回車換行子程序
          push ax    ;保護寄存器
          push dx
          mov dl,0dh    ;輸出回車字符
          mov ah,2
          int 21h
          mov dl,0ah    ;輸出換行字符
          mov ah,2
          int 21h
          pop dx    ;恢復寄存器
          pop ax
          ret    ;子程序返回
      dispcrlf endp
      
          end

二、參數傳遞

  • 傳遞參數的多少反應程序模塊間的耦合程度
  • 傳遞的內容:
    • 數據本身
    • 數據的存儲地址
  • 傳遞方法:
    • 寄存器
    • 變量
    • 堆棧
  1. 寄存器傳遞參數
    • 把參數存於約定的寄存器
      • 少量數據直接傳遞數值
      • 大量數據只能傳遞地址
    • 帶有出口參數的寄存器不能保存和恢復
    • 使用低八位寄存器時不保證不影響高八位數據
    • 例:顯示一個數據的數值(數值和ASC碼的轉換)
      • ;eg410.asm
            .model small
            .stack
            .data
        STRING DB 'AX=',4 DUP(?),'H','$'
            .CODE
            .startup
                MOV AX,123AH
                MOV CX,4
                XOR BX,BX
            AGAIN:
                ROL AX,1
                ROL AX,1
                ROL AX,1
                ROL AX,1
                PUSH AX
                CALL HTOASC
                MOV STRING+3[BX],AL
                INC BX
                POP AX
                LOOP AGAIN
        
                MOV AH,9
                MOV DX,OFFSET STRING
                INT 21H
            .exit
                HTOASC PROC
                AND AL,0FH
                OR AL,30H
                CMP AL,'9'
                JBE ABCD
                ADD AL,7
                ABCD: RET
                HTOASC ENDP
        
            end

         

    • 有符號十進制整數的顯示
      • ;eg504.asm
            .model small
            .stack
            .data
        array    dw 6789,-1234,0,1,-9876,32767,-32768,5678,-5678,9000
        count    = lengthof array
            .code
            .startup
            mov cx,count
            xor bx,bx
        again:    mov ax,array[bx]     ;將AX=入口參數
            call dispsiw    ;調用子程序,顯示一個數據
            add bx,type array
            call dispcrlf    ;光標回車換行以便顯示下一個數據
            loop again
            .exit
        dispsiw    proc    ;顯示有符號十進制數的通用子程序
            push ax    ;入口參數:AX=欲顯示的數據(補碼)
            push bx
            push dx
            test ax,ax    ;判斷數據是零、正數或負數
            jnz dsiw1
            mov dl,'0'    ;是零,顯示“0”后退出
            mov ah,2
            int 21h
            jmp dsiw5
        dsiw1:    jns dsiw2    ;是負數,顯示“-”
            mov bx,ax    ;AX數據暫存於BX
            mov dl,'-'
            mov ah,2
            int 21h
            mov ax,bx
            neg ax    ;數據求補(絕對值)
        dsiw2:    mov bx,10
            push bx    ;10壓入堆棧,作為退出標志
        dsiw3:    cmp ax,0    ;數據(商)為零,轉向顯示
            jz dsiw4
            xor dx,dx    ;擴展被除數DX.AX
            div bx    ;數據除以10:DX.AX÷10
            add dl,30h    ;余數(0~9)轉換為ASCII碼
            push dx    ;數據各位先低位后高位壓入堆棧
            jmp dsiw3
        dsiw4:    pop dx    ;數據各位先高位后低位彈出堆棧
            cmp dl,10    ;是結束標志10,則退出
            je dsiw5
            mov ah,2    ;進行顯示
            int 21h
            jmp dsiw4
        dsiw5:    pop dx
            pop bx
            pop ax
            ret    ;子程序返回
        dispsiw    endp
        
        dispcrlf    proc    ;回車換行子程序
            push ax    ;保護寄存器
            push dx
            mov dl,0dh    ;輸出回車字符
            mov ah,2
            int 21h
            mov dl,0ah    ;輸出換行字符
            mov ah,2
            int 21h
            pop dx    ;恢復寄存器
            pop ax
            ret    ;子程序返回
        dispcrlf    endp
        
            end

         

  2. 變量傳遞參數
    • 子程序和主程序如果在同一模塊不需要特殊說明
    • 如果不在同一個模塊需要利用PUBLEC EXTREN說明
    • 例:
      • 二進制輸入程序
        ;eg505.asm
            .model small
            .stack
            .data
        count    = 5
        array    dw count dup(0)
        temp    dw ?    ;共享變量
            .code
            .startup
            mov cx,count
            mov bx,offset array
        again:    call readbw    ;調用子程序,輸入一個數據
            mov ax,temp    ;獲得出口參數
            mov [bx],ax    ;存放到數據緩沖區
            add bx,type array
            call dispcrlf    ;光標回車換行以便輸入下一個數據
            loop again
            .exit
        
        readbw    proc    ;二進制輸入子程序
            push ax    ;出口參數:共享變量TEMP
            push bx
             push cx
        rdbw1:    xor bx,bx    ;BX用於存放二進制結果
            mov cx,16    ;限制輸入字符的個數
        rdbw2:    mov ah,1    ;輸入一個字符
            int 21h
            cmp al,'0'    ;檢測鍵入字符是否合法
            jb rderr    ;不合法則返回重新輸入
            cmp al,'1'
            ja rderr
            sub al,'0'    ;對輸入的字符進行轉化
            shl bx,1    ;BX的值乘以2
            or bl,al    ;BL和AL相加
            loop rdbw2    ;循環鍵入字符
            mov temp,bx    ;把BX的二進制結果存放TEMP返回
            pop cx
            pop bx
            pop ax
            ret
        rderr:     push ds    ;保護DS
            mov ax,cs    ;因信息保存在代碼段,所以需要設置DS=CS
            mov ds,ax
            lea dx,errmsg    ;顯示錯誤信息
            mov ah,9
            int 21h
            pop ds    ;恢復DS
            jmp rdbw1
        errmsg    db 0dh,0ah,'Input error, enter again: $'
        readbw    endp
        
        dispcrlf    proc    ;回車換行子程序
            push ax    ;保護寄存器
            push dx
            mov dl,0dh    ;輸出回車字符
            mov ah,2
            int 21h
            mov dl,0ah    ;輸出換行字符
            mov ah,2
            int 21h
            pop dx    ;恢復寄存器
            pop ax
            ret    ;子程序返回
        dispcrlf    endp
        
            end

         

      • 有符號十進制數輸入程序
        ;eg506.asm
            .model small
            .stack
            .data
        count    = 5
        array    dw count dup(0)
        temp    dw ?    ;共享變量
            .code
            .startup
            mov cx,count
            mov bx,offset array
        again:    call readsiw    ;調用子程序,輸入一個數據
            mov ax,temp    ;獲得出口參數
            mov [bx],ax    ;保存到數據緩沖區
            add bx,2
            call dispcrlf    ;分行
            loop again
            .exit
        readsiw    proc    ;輸入有符號十進制數的通用子程序
            push ax    ;出口參數:變量TEMP=補碼表示的二進制數值
            push bx    ;說明:負數用“-”引導
            push cx
            xor bx,bx    ;BX保存結果
            xor cx,cx    ;CX為正負標志,0為正,-1為負
        rsiw0:    mov ah,1    ;輸入一個字符
            int 21h
            cmp al,'+'    ;是“+”,繼續輸入字符
            jz rsiw1
            cmp al,'-'    ;是“-”,設置-1標志
            jnz rsiw2
            mov cx,-1
        rsiw1:    mov ah,1    ;繼續輸入字符
            int 21h
        rsiw2:    cmp al,'0'    ;不是0~9之間的字符,則輸入數據結束
            jb rsiw3
            cmp al,'9'
            ja rsiw3
            sub al,30h    ;是0~9之間的字符,則轉換為二進制數
            xor ah,ah    ;AL零位擴展為AX
            shl bx,1    ;利用移位和加法實現數值乘10:BX←BX×10
            mov dx,bx    ;參見例3-8
            shl bx,1
            shl bx,1
            add bx,1
            add bx,ax    ;已輸入數值乘10后,與新輸入數值相加
            jmp rsiw1    ;繼續輸入字符
        rsiw3:    cmp cx,0    ;是負數,進行求補
            jz rsiw4
            neg bx
        rsiw4:    mov temp,bx    ;設置出口參數
            pop cx
            pop bx
            pop ax
            ret    ;子程序返回
        readsiw    endp
        
        dispcrlf    proc    ;回車換行子程序
            push ax    ;保護寄存器
            push dx
            mov dl,0dh    ;輸出回車字符
            mov ah,2
            int 21h
            mov dl,0ah    ;輸出換行字符
            mov ah,2
            int 21h
            pop dx    ;恢復寄存器
            pop ax
            ret    ;子程序返回
        dispcrlf    endp
        
            end

         

  3. 堆棧傳遞參數
    1. 注意PUSH后不能是常數
    2. 例子:
      • 計算有符號數的平均值程序
        ;eg507.asm
            .model small
            .stack
            .data
        array    dw 675, 354, -34, 198, 267, 0, 9, 2371, -67, 4257
            .code
            .startup
            mov ax,lengthof array
            push ax    ;壓入數據個數
            mov bx,offset array
            push bx    ;壓入數組的偏移地址
            call mean    ;調用求平均值子程序,出口參數:AX=平均值(整數部分)
            add sp,4    ;平衡堆棧(壓入了4個字節數據)
            call dispsiw    ;顯示
        
            .exit
        mean    proc    ;計算16位有符號數平均值子程序
            push bp    ;入口參數:順序壓入數據個數和數組偏移地址
            mov bp,sp    ;出口參數:AX=平均值
            push bx    ;保護寄存器
            push cx
            push dx
            mov bx,[bp+4]    ;BX=堆棧中取出的偏移地址
            mov cx,[bp+6]    ;CX=堆棧中取出的數據個數
            xor ax,ax    ;AX保存和值
        mean1:    add ax,[bx]    ;求和
            add bx,type array    ;指向下一個數據
            loop mean1    ;循環
            cwd    ;將累加和AX符號擴展到DX
            idiv word ptr [bp+6]    ;有符號數除法,AX=平均值(余數在DX中)
            pop dx    ;恢復寄存器
            pop cx
            pop bx
            pop bp
            ret
        mean    endp
        dispsiw    proc    ;顯示有符號十進制數的通用子程序
            push ax    ;入口參數:AX=欲顯示的數據(補碼)
            push bx
            push dx
            test ax,ax    ;判斷數據是零、正數或負數
            jnz dsiw1
            mov dl,'0'    ;是零,顯示“0”后退出
            mov ah,2
            int 21h
            jmp dsiw5
        dsiw1:    jns dsiw2    ;是負數,顯示“-”
            mov bx,ax    ;AX數據暫存於BX
            mov dl,'-'
            mov ah,2
            int 21h
            mov ax,bx
            neg ax    ;數據求補(絕對值)
        dsiw2:    mov bx,10
            push bx    ;10壓入堆棧,作為退出標志
        dsiw3:    cmp ax,0    ;數據(商)為零,轉向顯示
            jz dsiw4
            xor dx,dx    ;擴展被除數DX.AX
            div bx    ;數據除以10:DX.AX÷10
            add dl,30h    ;余數(0~9)轉換為ASCII碼
            push dx    ;數據各位先低位后高位壓入堆棧
            jmp dsiw3
        dsiw4:    pop dx    ;數據各位先高位后低位彈出堆棧
            cmp dl,10    ;是結束標志10,則退出
            je dsiw5
            mov ah,2    ;進行顯示
            int 21h
            jmp dsiw4
        dsiw5:    pop dx
            pop bx
            pop ax
            ret    ;子程序返回
        dispsiw    endp
            end

         

三、宏結構

  1. 宏匯編
    1. 定義和調用
      • ;聲明
        宏名 MACRO[形參表] …… …… ENDM
        ;調用
        宏名 實參列表
      • 例:
        • 宏定義
          WriteString    macro msg
                  push ax
                  push dx
                  lea dx,msg
                  mov ah,9
                  int 21h
                  pop dx
                  pop ax
                  endm
        • 宏調用
          WriteString  msg
        • 宏展開
          push ax
              push dx
              lea dx,msg
              mov ah,9
              int 21h
              pop dx
              pop ax
    2. 宏和子程序
      • 宏:
        • 僅是源程序級的簡化:宏調用在匯編時進行程序語句的展開,不需要返回;不減小目標程序,執行速度沒有改變
        • 通過形參、實參結合實現參數傳遞,簡捷直觀、靈活多變

        • 當程序段較短或要求較快執行時,應選用宏

      • 子程序

        • 還是目標程序級的簡化:子程序調用在執行時由CALL指令轉向、RET指令返回;形成的目標代碼較短,執行速度減慢

        • 需要利用寄存器、存儲單元或堆棧等傳遞參數

        • 當程序段較長或為減小目標代碼時,要選用子程序

  2. 一些其他的說明

    • MASM具體支持的多模塊程序結構的方法:

      • 源文件包含

      • 模塊連接

      • 子程序庫

      • 庫文件包含

    •  源文件包含

      • 各種常量定義、聲明語句等組織在包含文件(*.inc)

      • 常用的或有價值的宏定義放在宏定義文件(*.mac)

      • 常用的子程序形成匯編語言源文件(*.asm)

    • 使用源文件包含偽指令INCLUDE將指定的文本文件內容插入主題源程序文件。  

四、課后習題!

5.1(1)指令“CALL BX“采用了指令的什么尋址方式?

    寄存器間接尋址

  (5)子程序采用堆棧傳遞參數,為什么要特別注意堆棧平衡問題?

    子程序保持堆棧平衡才能保證執行RET指令時當前棧頂的內容是正確的返回地址。

    主程序保持平衡才能釋放傳遞參數占用的堆棧空間,否則多次調用可能使堆棧溢出。

5.2 判斷題

  (2)CALL指令的執行並不影響堆棧指針SP。

    錯,IP入棧 sp-2

  (5)子程序需要保護寄存器,包括保護傳遞入口參數和出口參數的通用寄存器。

    錯,出口參數寄存器不能保護。

  (6)利用INCLUDE包含的源文件實際上只是源程序的一部分。

    對。

  (7)宏調用與子程序調用一樣都要使用CALL指令實現。

    錯。宏調用:宏名+參數列表

  (8)宏定義可以與子程序一樣,書寫與主程序之后。

    錯,不可以。

5.3 填空題

  (1)指令”RET i16"的功能相當於"RET"指令和“ADD SP,    2    .”組合。

  (4)數值10在計算機內部用二進制“1010”編碼表示,用十六進制表達是:    0AF   .如果將該編碼加37H,則為   41H   ,他是字符   A   的ASCII碼值。

  (5)利用堆棧傳遞子程序參數的方法是固定的,例如尋址堆棧段數據的寄存器是   BP   

  (7)過程定義開始是“TEST PROC"語句,則過程定義結束的語句是   TEST ENDP  。宏定義開始是”DISP MACRO"語句,則宏定義結束的語句是   ENDM  

5.5 請按如下說明編寫子程序。

子程序功能:把用ASCII碼表示的兩位十進制數轉換為壓縮BCD碼

入口參數:DH=十位數的ASCII碼,DL=個位數的ASCII碼

出口參數:AL=對應BCD碼

    ;把用asc碼表示的兩位十進制數轉換稱壓縮的BCD碼
    ;入口參數:DH=十位數的ASC碼,DL=個位數的ASC碼
    ;出口參數:AL=對應的BCD碼
    .model small
    .stack
    .data    ;數據段
        inmsg    db 'Enter two numbers(0-9):  $'
        errmsg    db 0dh,0ah,'Input error, enter again: $'
    .code    ;代碼段,主程序
    .startup
    mov dx,offset inmsg
    mov ah,9
    int 21h
    
    ;輸入兩位十進制數字,結果分別保存到DH和DL
rdhw1:    xor dx,dx    ;;DX一開始是0哇
    mov bx,2    ;限制輸入字符的個數
    mov cl,8
rdhw2:    mov ah,1
    int 21h    ;輸入一個字符;;輸入進AL
    cmp al,'0'    ;檢測鍵入字符是否合法
    jb rderr    ;不合法則返回重新輸入
    cmp al,'9'
    ja rderr    ;不合法則返回重新輸入
    shl dx,cl;;將十位上的數左移至高八位DH上 在第二次循環的時候起作用!!
    or dl,al;;這步應該是相當於ADD DL,AL吧
    dec bx
    jnz rdhw2 ;繼續輸入    
    call btobcd ;調用子程序
    int 3   ;中斷
    jmp done
    
rderr:     
    mov dl,0dh    ;輸出回車字符
    mov ah,2
    int 21h
    mov dl,0ah    ;輸出換行字符
    mov ah,2
    int 21h
    lea dx,errmsg     ;顯示錯誤信息
    mov ah,9
    int 21h
    jmp rdhw1 ;重新輸入
done: nop
    .exit    ;主程序結束,退出
    
btobcd    proc    ;子程序
    ;出口參數:AL=輸入的數據
    push cx
    xor ax,ax
   mov cl,4
   and dh,0fh    ;dh高4位清零
    or al,dh    ;AL和DH相加;;AL不是零么???
    and dl,0fh    ;減30H;;為啥不直接用SUB呢???為什么???
    shl ax,cl    ;AX左移4位
    or al,dl    ;AL和DL相加
    pop cx
    ret
btobcd endp
    end

;為什么子程序保護了CX不保護DX?? 入口參數不用保護嘛??

 

5.7 編寫一個程序,在鍵盤上按一個鍵,將其返回的ASCII碼值表示出來,如果按下ESC鍵(1BH)則退出。

;顯示輸入字符的ASC碼
;如果輸入的是ESC(1BH)則退出
    .model small
    .stack
    .data
asc db '00H',13,10,'$'
crlf db 13,10,'$'
    .code
    .startup
ReadAsc:    
   mov ah,1
    int 21h
    cmp al,1Bh
    jz done ;輸入一個字符,輸入ESC退出;ESC的ASCii碼為1BH
    mov dx,offset crlf
    mov ah,9
    int 21h    ;輸出回車換行
    mov cx,2
    xor bx,bx;;BX用做asc數組下標
again:    
    rol al,1    ;高4位循環移位進入低4位,作為子程序的入口參數
    rol al,1
    rol al,1
    rol al,1
    push ax    ;子程序利用AL返回結果,所以需要保存AX中的數據
    call htoasc    ;調用子程序
    mov asc[bx],al    ;保存轉換后的ASCII碼
    pop ax    ;恢復保存的數據
    inc bx
    loop again
    mov dx,offset asc
    mov ah,9
    int 21h    ;顯示
    jmp ReadAsc
    
done:
   nop
    .exit
    
htoasc    proc    ;將AL低4位表達的一位十六進制數轉換為ASCII碼
    and al,0fh    ;只取AL的低4位
    or al,30h    ;AL高4位變成3,實現加30H
    cmp al,39h    ;是0~9,還是A~F
    jbe htoend
    add al,7    ;是A~F,其ASCII碼再加上7
htoend:    ret    ;子程序返回
htoasc    endp 

    end

 

5.8 編寫一個子程序,它以二進制形式顯示AL中8位數據,並設計一個主程序驗證。

;AL中八位數據用二進制顯示
    .model small
    .stack
    .data
    .code
    .startup
    mov al,35H ;用於驗證結果,子程序運行正確的話應該輸出00110101
    call asctob
    nop
    .exit

asctob    proc    ;將AL用二進制形式輸出
    push ax
    push bx
    push cx
    push dx
    mov cx,8
    mov bl,al  ;將al的初始數據賦值給bl
    
again:
    xor dl,dl
    rol bl,1    
    adc dl,30h    
    mov ah,2
    int 21h
    loop again
    pop dx
    pop cx
    pop bx
    pop ax
    ret    ;子程序返回
asctob    endp 

    end

 

    .model small
    .stack
    .data
    .code
    .startup
mov al,12h
mov cx,8
mov bl,al
again:
    call cout
    loop again
    .exit
cout proc
push ax
push dx
shl bl,1
jc one
mov dl,'0'
jmp over
one: mov dl,'1'
over:mov ah,2
int 21h
pop dx
pop ax
ret
cout endp
    end

 

5.11 編寫一個計算字節校驗和的子程序。所謂“校驗和”是指不計進位的累加,常用於檢查信息的正確性。主程序提供入口參數,有數據個數和數據緩沖區的首地址。子程序回送求和結果和這個出口參數。

;eg101.asm
    .model small
    .stack
    .data
        array db 1,2,3,4,5,6
        sum  db ?
    .code
    .startup
    mov bx,offset array  ;入口參數1:數據緩沖區首地址
    mov cx,lengthof array ;入口參數2:數據個數
    call getSum
    int 3
    mov sum, al ;出口參數為al
    .exit
    
getSum    proc    ;求校驗和
    push bx
    push cx
    xor ax,ax 
again:
    add al,[bx]
    inc bx
    loop again
    pop cx
    pop bx
    ret    ;子程序返回
getSum    endp 

    end

 

5.13 設計一個從低地址到高地址逐個字節顯示某個主存區與內容的子程序DISPMEM。入口參數:AX=主存偏移地址,CX=字節個數(主存區域的長度)。同時編寫一個主程序進行驗證。

;將ax=主存偏移地址,cx=字節個數的主存區域從低地址到高地址逐個字節顯示
    .model small
    .stack
    .data
MSG db 'abcdefg'
crlf db 13,10,'$'    
    .code
    .startup

MOV AX,OFFSET MSG
MOV CX,20
CALL DISP

    .exit
DISP PROC
PUSH AX
PUSH SI
PUSH DX
PUSH BX
XOR BX,BX
MOV BX,AX
XOR SI,SI
A1:    
        MOV AL,[BX+SI]
        ROL AL,1
        ROL AL,1
        ROL AL,1
        ROL AL,1
        PUSH AX
        AND AL,0FH
        OR AL,30H
        CMP AL,39H
        JBE A2
        ADD AL,7
A2:
        MOV DL,AL
        MOV AH,2
        INT 21H
        INC SI
        
        POP AX
        ROL AL,1
        ROL AL,1
        ROL AL,1
        ROL AL,1
        AND AL,0FH
        OR AL,30H
        CMP AL,39H
        JBE A3
        ADD AL,7
A3:
       MOV DL,AL
        MOV AH,2
        INT 21H
        
        MOV DX,OFFSET crlf
        MOV AH,9
        INT 21H
        
LOOP A1
POP BX
POP DX
POP SI
POP AX
RET
DISP ENDP
    end

 


免責聲明!

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



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