汇编语言期末复习——第四章 程序结构


一、顺序程序结构

  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