走向分支

1. 课程说明

本课程基于《汇编语言(第 2 版)》郑晓薇 编著,机械工业出版社,可以配合该教材使用。本课程由郑晓薇授权制作,提取教材中的实例以及实验内容,可以在实验楼环境中完成所有实例及实验。实验课程制作符合教材原版实例驱动教学以及实验训练贯穿始终的特点。

本教材可在 当当网 购买(点击链接即可进入购买页面)。

本课程实验列表中的最后模块为配套教材的习题参考答案,可供读者学习参考。

2. 实验环境

DOS 环境:

实验环境中安装有 dosemu 可以模拟 DOS 环境,并提供 DEBUGMASMLINK 等汇编语言开发程序。

3. 分支程序设计

汇编语言程序和高级语言程序一样,有顺序、分支、循环、子程序四种结构形式。计算机程序在执行过程中,可以改变程序的执行顺序,根据一定的条件进行转移,使程序完成更复杂的功能。

汇编语言提供了无条件转移指令和条件转移指令,利用这些指令可以编制分支程序。条件转移指令是对标志位进行判断之后作转移的,也就是说在转移指令之前应该执行那些能使标志发生改变的指令。

本实验我们将学习汇编语言分支程序的设计方法。

3.1 分支程序例一

本例子为教材的示例 5-1。

  • **关注点**:条件转移指令的用法

设计目标

设计分支程序,实现下列公式计算。X、Y 为字型。假设 X 单元中保存三个数:9,-6,34,分别作判断和计算。

设计思路

(1)在数据段中定义 2 个字型变量 X、Y,均为带符号数;

(2)在 X 单元中依次取出三个数分别作判断,根据 X 的大小作分支转移;

(3)采用寄存器相对寻址方式(MOV AX,X[SI])取出 X 的三个值;

(4)标号 OUT1 是各路分支的公共出口。

程序框图

实验步骤

(1)双击桌面上的记事本gedit,录入下列程序:

;5-1.asm  用正常程序格式编写分支程序
data segment
    x dw 9,-6,34
    y dw 3 dup(?)
data ends
code segment 
    assume cs:code,ds:data
start:
    mov ax,data
    mov ds,ax
    mov cx,3        ;循环三次
    mov si,0                
let0:
    mov ax,x[si]    ;取出X
    cmp ax,0        ;X ≥0 ?
    jge let1        ;是,转到let1
    mov bx,ax        ;否,计算X*X
    imul bx                
    jmp out1        ;跳到公共出口out1
let1:
    cmp ax,10        ;X ≥10 ?
    jge let2        ;是,转到let2
    sal ax,1        ;否,计算2X+3
    add ax,3
    jmp out1        ;跳到公共出口out1
let2:
    mov bl,6        ;计算X/6
    idiv bl            ;商在al,余数在ah
out1:
    mov y[si],ax    ;保存Y
    add si,2
    dec cx            ;CX-1
    cmp cx,0                
    jnz let0        ;CX≠0转移到let0
    mov ah,4ch    ;CX=0,程序结束
    int 21h
code ends
end start

(2)在 dos 子目录下保存为 5-1.asm,经过汇编 masm 5-1.asm,连接 link 5-1.obj,生成 5-1.exe。

D:\dos〉masm 5-1.asm
D:\dos〉link 5-1.obj
D:\dos〉dir
D:\dos〉5-1.exe

(3)运行结果:在 DOS 下执行程序 5-1.exe 后又返回到 DOS,没有显示。说明程序中没有加入显示的指令。

D:\dos〉debug 5-1.exe
-U
-U
-G 0039
-D DS:0

要观察运行结果,采用 DEBUG 执行 5-1.exe。在 DEBUG 下,连续用反汇编 U 命令查看;找到断点 0039,用 G 0039 执行;再用 D 命令查看结果。

前三个字单元分别是 9,-6,34,从 6 号单元开始存放三个结果。若 X = 9,Y = 0015H = 21;若 X = -6,Y = 0024H = 36;若 X=34,Y 的商 = 5,余数 = 4。

对上述分支程序的分析:(条件转移指令的用法可参考本书 5.2 节)

  • 1)程序中采用 CMP 比较指令对分支的条件进行判断;
  • 2)根据判断的结果用条件转移指令 JGE、JNZ 等进行转移。条件满足转移到标号处执行程序,条件不满足则继续执行转移指令的下一条;
  • 3)无条件转移指令 JMP 直接跳到标号处执行;
  • 4)转移指令所跳转的方向,既可以是正方向跳转,也可以向反方向跳转。例如程序后部的指令 JNZ LET0,标号 LET0 在程序的前部,因此是往回跳转。可以看出,向反方向跳转构成了循环。

**小贴士**:条件转移指令格式

条件转移指令的格式如下:

条件转移指令操作码    OPR
; 这里的 OPR 代表标号

条件转移指令的作用是当条件满足时,转移到由标号指出的那条指令去执行,条件不满足则继续执行本转移指令的下一条。

例如 JZ LET1,表示结果为 0 就转移到 LET1 那条指令去执行。JZ 中的 Z 是零标志 ZF,当 ZF = 1 时,表示之前的运算结果为 0。

(1)条件转移指令包括 10 种指令:

(2)无符号数比较转移指令包括 4 种指令:

(3)带符号数比较转移指令包括 4 种指令:(由于带符号数的最高位为符号位,因此带符号数的数值与无符号数不一样,要用另外的比较转移指令)

3.2 分支程序例二

本例子为教材的示例 5-2。

设计目标

设计分支程序。计算 Y=5X-18,如果结果为负,求绝对值。并显示十进制结果。

设计思路

(1)用数据段保存 X、Y。为简便,X 定义为字节,Y 定义为字;

(2)用符号位 SF 判断运算结果的正负,为负数则求补(绝对值),如果是正数,直接保存结果;

(3)采用将 AX 中的结果除以 10、取得余数的方法获得结果的十进制数;

(4)将余数变为 ASCII 码,用 DOS 中断调用的 2 号功能显示出来;

(5)用 9 号功能显示提示信息。

程序框图

实验步骤

(1)双击桌面上的记事本 gedit,录入下列程序:

;5-2.asm  计算Y=5X-18,用正常程序格式
data segment
    x db  -6
    y dw ?
    cc  db 0ah,0dh,'Y=$'
data ends
code segment 
    assume cs:code,ds:data
start:
    mov ax,data
    mov ds,ax
    mov al,5        ;5X
    imul x
    sub ax,18        ;-18
    jns let0        ;结果不为负则转移
    neg ax        ;结果为负,求绝对值
let0:
    mov y,ax        ;保存结果
    ;将ax中的二进制数变为十进制数,并显示
    mov cx,0
    mov bx,10                
let1:                        
    mov dx,0
    inc cx            ;统计余数个数
    idiv bx        ;AX/10,商在AX,余数在DX
    push dx        ;保存余数
    cmp ax,0        ;商为0,则退出循环
    jnz let1
    mov dx,offset cc ;9号功能显示提示
    mov ah,9
    int 21h
let2:            ;循环执行cx次,显示十进制结果    
    pop ax        ;将余数弹入ax
    add ax,0030h    ;调整为ASCII码
    mov dl,al        ;2号功能,显示一个字符
    mov ah,2
    int 21h
    dec cx
    cmp cx,0
    jnz let2
    mov ah,4ch
    int 21h
code ends
end start

(2)在 dos 子目录下保存为 5-2.asm,经过汇编 masm 5-2.asm,连接 link 5-2.obj,生成 5-2.exe。

D:\dos〉masm 5-2.asm
D:\dos〉link 5-2.obj
D:\dos〉5-2.exe

(3)运行结果:

**思考**:如果从键盘输入 X,并且允许输入负数-6,程序如何修改?

**提示**:增加键盘输入指令,先判断从键盘输入的是否为 “-” 负号;如果是,再执行一次键盘输入,把输入的数去掉 ASCII 码(AND AL,0FH)、求绝对值(NEG AL),并将 AL 保存到 x 单元。

**练习**:编写程序,X、Y 都是大于 0 的数。若 X ≥ Y,Z = X – Y;否则,Z = Y / X,以十进制显示结果。除法运算仅显示商。

**思考**:如果除法运算既要显示商又要显示余数,程序如何修改?

3.3 分支程序例三

从前面章节的学习中,我们知道,无论是键盘输入的数字或字母,还是在存储区中定义的字符,都是以 ASCII 码形式表示的,在程序中经常要对它们进行判断。对数字或字母的判断有多种方法。

本节我们采用位操作的方法来区分数字和大小写字母,and、or、not(与或非)逻辑指令和 test 测试指令都是按位操作的。

**关注点**:与或非逻辑指令的用法

本例子为教材的示例 5-3。

设计目标

从键盘输入一串字符,如果是数字存入 NUMB 单元,如果是字母,将大写字母存入 CAPI 单元,小写字母存入 LETT 单元,分别统计个数,输入回车时退出。

设计思路

(1)用 TEST 测试指令来区分数字和字母:

  • 数字和字母的第 6 位不同。因此,区别数字和字母用 TEST AL,40H; 第 6 位为 0 是数字,第 6 位为 1 则为字母。
  • 大小写字母为第 5 位不同。区别大小写字母用 TEST AL,20H; 第 5 位为 0 是大写字母,第 5 位为 1 则为小写字母。

(2)用 CMP 指令排除其它字符;

(3)数字、大写字母、小写字母的个数分别放在 DI、SI、BX 中。

实验步骤

(1)双击桌面上的记事本gedit,录入下列程序:

;5-3.asm  分支程序。区分和统计键入的数字、大写字母、小写字母
data segment
    numb db 10 dup(?)
    capi db 10 dup(?)
    lett db 10 dup(?)
data ends
code segment
    assume cs:code,ds:data
start:
    mov ax,data
    mov ds,ax
let0: mov ah,1        ;键盘输入
    int 21h
    cmp al,0dh        ;回车?
    jz exit                ;是,转EXIT
    test al,40h            ;区分数字和字母
    jz let1                ;是数字,转LET1
    test al,20h            ;区分大小写字母
    jz let2                ;是大写,转LET2
    cmp al,7ah          ;排除不是小写字母
    ja exit
    mov lett[bx],al        
    inc bx                ;小写个数加1 
    jmp let0
let2: cmp al,5ah        ;排除大小写之间的字符
    ja exit
    mov capi[si],al
    inc si                ;大写个数加1        
    jmp let0
let1: cmp al,'0'        ;排除其它字符
    jb exit
    cmp al,'9'
    ja exit
    mov numb[di],al
    inc di                ;数字个数加1
    jmp let0
exit: mov ah,4ch
    int 21h
code ends
    end start

(2)在 dos 子目录下保存为 5-3.asm,经过汇编 masm 5-3.asm,连接 link 5-3.obj,生成 5-3.exe。

D:\dos〉masm 5-3.asm
D:\dos〉link 5-3.obj
D:\dos〉5-3.exe

(3)运行结果:由于本程序没有输出功能,若要查看运行结果,需要在 DEBUG 下运行。

D:\dos〉debug 5-3.exe
-u
-u
-g 3e

多次执行 u 命令,找到程序后部的 MOV AH,4CH 指令,设置本程序的断点在 003e,即 MOV AH,4CH 处。执行 G 3e 之后,从键盘输入若干字符“23sdfAZ”(如上图所示),回车后显示出各个寄存器的值。其中,数字个数 DI=2,大写字母个数 SI=2,小写字母个数 BX=3。在数据段内存中,分别保存了键入的数字和大小写字母。

**小贴士**:TEST 测试指令的用法

TEST 指令将两个操作数做按位相与操作,结果不回送,但是改变了标志位。

例如:假设 AL=97H,要测试 AL 的第 3 位是否为 0;如果为 0,转移到 LET1 执行。指令为 TEST AL,08H。把数值转换成二进制:97H=10010111,08H=00001000。可看到,08H 只有第 3 位为 1,其余位都是 0,任何数和 08H 相与的结果只保留了第 3 位的值,其余为全部清 0。如果第 3 位上为 0,那么整个结果就为 0,否则结果不为 0,零标志 ZF 会相应地做出改变。这样,根据结果是否为 0 就可以进行分支转移了。

练习:

  • (1)画出 5-3.ASM 的流程图。
  • (2)在 5-3.ASM 中增加显示功能,分别显示提示信息和统计个数值。

3.4 分支程序例四

本例子为教材的示例 5-4。

设计目标

从键盘输入英文单词,将其中的小写字母变为大写。

设计思路

(1)用 AND 指令将小写字母的 ASCII 码的第 5 位变为 0 即为大写字母;

(2)用 DOS 的 9 号功能显示提示信息;

(3)用 DOS 的 10 号功能输入英文字母。

实验步骤

(1)双击桌面上的记事本 gedit,录入下列程序:

;5-4.asm  输入英文单词,将小写字母转换为大写。
data segment
    mess1 db 0ah,0dh,'input:$'
    mess2 db 0ah,0dh,'output:$'
    buff db 10,?,10 dup(?)
data ends
code segment
    assume cs:code,ds:data
start:
    mov ax,data
    mov ds,ax
prog1:
    mov dx,offset mess1                ;显示提示1
    mov ah,9
    int 21h
    mov dx,offset buff                ;输入字串
    mov ah,10
    int 21h
    mov cl,buff+1                    ;实际输入的字母个数
    mov bx,2                            ;第一个字母的地址
    mov dx,offset mess2                ;显示提示2
    mov ah,9
    int 21h
let1:
    and buff[bx],0dfh                ;小写字母变为大写
    mov dl,buff[bx]                    ;循环显示每个字母
    mov ah,2        
    int 21h
    inc bx
    dec cl
    jnz let1            
    mov ah,4ch
    int 21h
code ends
    end start

(2)在 dos 子目录下保存为 5-4.asm,经过汇编 masm 5-4.asm,连接link 5-4.obj,生成 5-4.exe。

D:\dos〉masm 5-4.asm
D:\dos〉link 5-4.obj
D:\dos〉5-4.exe

(3)运行结果:

**分析思考**:本例采用 “逻辑与” AND 指令将某位屏蔽为 0。and buff[bx],0dfh 该指令中源操作数为十六进制 dfh,写成二进制为 11011111,可看出除了第 5 位为 0,其余都是 1 。因此,不论目的操作数是何值,只要是和 DFH 相与,结果的第 5 位清 0,其余位保持不变。这样就达到了将小写字母变为大写字母的目的。

“与” AND、“或” OR、“非” NOT 等逻辑指令的用法见本书 5.3 节。

3.5 分支程序例五

本例子为教材的示例 5-5。

设计目标

计算 0~9 的立方值并显示。

设计思路

(1)从键盘输入 0~9;可多次输入,按 ESC 键退出;

(2)将输入的数字去掉 ASCII 码;

(3)用连乘计算立方值,注意百位的判断;

(4)用除以 10 取余得到百位、十位、个位数,并用 x 的三个单元分别存放;

(5)显示十进制结果。

实验步骤:

(1)双击桌面上的记事本gedit,录入下列程序:

;5-5.asm  计算0-9立方值。ESC退出
data segment
    mess1 db 0ah,0dh,'input:$'
    mess2 db 0ah,0dh,'output:$'
    x     db ?,?,?
data ends
code segment
    assume cs:code,ds:data
start:
    mov ax,data
    mov ds,ax
let0:mov dx,offset mess1                ;显示提示
    mov ah,9
    int 21h
    mov ah,1                            ;输入0-9
    int 21h
    cmp al,27                            ;按ESC退出
    jz out1
    cmp al,'0'                            ;输入限制
    jb let0
    cmp al,'9'
    ja let0
    and al,0fh                            ;去掉ASCII码
    mov ah,0
    mov bl,al                            ;求立方
    mul bl
    mul bl                                ;立方值在ax
    mov bl,10                            ;立方值变为十进制
    div bl
    add ah,30h                            ;个位加上ASCII码
    mov x,ah                            ;x 保存个位
    cmp al,10                            ;高位≥10?
    jb let2                                ;小于转移
    mov ah,0                            ;≥10继续除以10
    div bl
    add ah,30h                            ;十位加上ASCII码
    mov x+1,ah                            ;x+1 十位
    add al,30h                        
    mov x+2,al                            ;x+2 百位
    jmp let3
let2:add al,30h                            ;十位数
    mov x+1,al
let3:mov ax,0
    mov dx,offset mess2
    mov ah,9
    int 21h
    mov dl,x+2                            ;显示立方值
    mov ah,2
    int 21h
    mov dl,x+1
    int 21h
    mov dl,x
    int 21h
    mov word ptr x,0                    ;将x单元清0
    mov x+2,0                           ;将x+2单元清0
    jmp let0                            ;返回,再输入数字
out1:mov ah,4ch                            ;退出
    int 21h
code ends
    end start

(2)在 dos 子目录下保存为 5-5.asm,经过汇编 masm 5-5.asm,连接 link 5-5.obj,生成 5-5.exe。

D:\dos〉masm 5-5.asm
D:\dos〉link 5-5.obj
D:\dos〉5-5.exe

(3)运行结果:

**思考**:求立方值还可以用查立方表的方法实现。结合查表操作,修改程序。

3.6 分支程序例六

本例子为教材的示例 5-6。

  • **关注点**:BCD 码,移位操作和十进制运算调整指令

设计目标

从键盘输入两个两位的十进制数,做加法运算,并显示结果。

从键盘输入数字 0~9 的 ASCII 码为 30H~39H,要想用它们做十进制运算可以将其转换为 BCD 码。数字的 ASCII 码与压缩 BCD 码之间的转换有多种方式,此处采用位运算实现。

设计思路

(1)键盘输入一个两位数之后回车,再输入另外一个两位数。将 4 个数去掉 30H 保存到 X 单元。比如输入 12 和 34,相加结果应为 46;
(2)用移位操作将 4 个数据两两合并为相应的 BCD 码;
(3)相加后用 DAA 十进制调整指令调整,如果有百位的进位,用变量 Z 记住;
(4)用移位以及加 3030H 操作将 BCD 码再变为 ASCII 码,显示百位、十位、个位数值。

实验步骤

(1)双击桌面上的记事本gedit,录入下列程序:

;5-6.asm  输入两个2位十进制数,相加并显示结果。
data segment
    x db 4 dup(?)
    z db ?  
data ends
code segment
assume cs:code,ds:data
start:
    mov ax,data
    mov ds,ax
    mov cx,2                ;允许输入两个数据
    mov si,0
let0:
    mov ah,1                ;键盘输入12↓34↓
    int 21h
    cmp al,0dh                ;回车?
    jz let1                    ;是,转let1输入第2个数
    and al,0fh                ;去掉30h
    mov x[si],al            ;保存到x
    inc si
    jmp let0
let1:
    mov ah,2                ;换行
    mov dl,0ah
    int 21h
    dec cx
    jnz let0                
    ;将保存在x单元中的01、02、03、04合并为BCD码
    mov ax,0
    mov al,x                ;第1个数变为BCD码12H
    mov cl,4
    shl al,cl                ;01左移4位后变为10
    add al,x+1
    mov bl,x+2                ;第2个数变为BCD码34H
    shl bl,cl                ;03左移4位后变为30
    add bl,x+3
    add al,bl                ;BCD码相加后ax=0046
    daa                        ;对AX作十进制调整
    ;显示十进制结果
    jnc let2                ;没有百位则转let2
    mov z,'1'                ;标记有百位进位
let2:
    mov cl,4
    shl ax,cl                ;左移4位,ax=0460
    rol al,cl                ;al左移4位后ax=0406
    add ax,3030h            ;ax=3436——将BCD码变为ASCII码
    mov bx,ax
    mov dl,0ah                ;换行显示
    mov ah,2
    int 21h
    cmp z,'1'                ;有百位?
    jnz out1
    mov dl,z                ;显示百位数1
    int 21h
out1:
    mov dl,bh                ;显示十位
    int 21h
    mov dl,bl                ;显示个位    
    int 21h
    mov ah,4ch
    int 21h
code ends
    end start

(2)在 dos 子目录下保存为 5-6.asm,经过汇编 masm 5-6.asm,连接 link 5-6.obj,生成 5-6.exe。

D:\dos〉masm 5-6.asm
D:\dos〉link 5-6.obj
D:\dos〉5-6.exe

(3)运行结果:

第 1 次执行 12 + 34 = 46,第 2 次执行 78 + 89 = 167。

**注意**:若只输入个位数,必须输入 01、02 等,保持两位数的输入。

**小贴士**:移位操作

有关移位指令的其他用法参考本书 5.3.5 节。

**小贴士**:十进制调整指令

BCD 码是用二进制编码来表示十进制数,我们希望在计算机中按十进制运算规则进行运算,但计算机实际上进行的是二进制运算。例如十进制运算 5 + 7 = 12,用 BCD 码表示为 0101 + 0111。按照二进制相加,结果等于 1100,而这个结果不是 BCD 码;那么再把它加 6,结果就是 00010010,即 BCD 码表示的 12。

通过观察二进制数运算结果和对应的十进制运算结果的差别可知,只要是结果大于 9,就应该对计算结果做修正调整,这样获得的数值就符合逢十进一的十进制运算规则。

8086 指令系统中提供了相关的十进制调整指令。有关十进制调整指令的用法参考本书 4.3.7 节。

**思考**:如果显示提示信息,程序如何修改?

3.7 分支程序例七

本例子为教材的示例 5-7。

设计目标

十进制与十六进制转换。将键盘输入的一个两位十进制数以十六进制形式显示在屏幕上。可多次输入直到按下 ESC 键。

设计思路

(1)用 DOS 的 1 号功能输入一个两位数,以回车结束;

(2)将输入的数字减去 30H 保存在 X 单元,第 1 个数字扩大 10 倍再与第 2 个数相加,变为十进制数;

(3)用 9 号功能显示提示信息;

(4)将十进制数除以 16,形成十六进制数;

(5)再将十六进制数转换为 ASCII 码,用 2 号功能显示。

实验步骤

(1)双击桌面上的记事本gedit,录入下列程序:

;5-7.asm  可多次输入一个两位十进制数并以十六进制显示出来,按ESC键退出。
data segment
    x db 2 dup(?)
    mess1 db 0dh,0ah,'decimal=$'
    mess2 db 0dh,0ah,'HEX=$'
data ends
code segment
    assume cs:code,ds:data
start:
    mov ax,data
    mov ds,ax
let0:
    mov x,0
    mov x+1,0
    mov si,0
    mov dx,offset mess1    ;显示提示1
    mov ah,9
    int 21h
let1:
    mov ah,1                ;键盘输入十进制数
    int 21h
    cmp al,27                ;是ESC键?
    jz out1
    cmp al,0dh            ;回车?
    jz let2                            ;是,转let2
    and ax,000fh                    ;去掉ASCII码
    mov x[si],al                    ;保存到x
    inc si                            ;统计输入的位数
    jmp let1
let2:
    mov dx,offset mess2                ;显示提示2
    mov ah,9
    int 21h
    cmp si,1                        ;判断输入的位数
    ja let3                            ;输入了两位数转let3
    mov bl,x
    mov cl,1
    jmp let5                        ;只输入1位数则直接去显示
let3:
    mov al,x
    mov cl,10
    mul cl                            ;形成两位十进制数
    add al,x+1
    mov ah,0
    mov bl,16                        ;除以16,转换为十六进制
    div bl
    mov bx,ax                        ;AH为余数即低位,AL为商即高位
    ;分别显示十六进制高位、低位
    mov cl,2
let4:
    cmp bl,10                        ;判断十六进制数码
    jl let5
    add bl,7                        ; ≥10 则加7(是字母A~F) 
let5:                                
    add bl,30h                        ;加上ASCII码
    mov dl,bl
    mov ah,2                        ;显示
    int 21h
    mov bl,bh                        ;再去显示低位
    dec cl
    jnz let4
    jmp let0                        ;返回let0继续输入
out1:
    mov ah,4ch
    int 21h
code ends
    end start

(2)在 dos 子目录下保存为 5-7.asm,经过汇编 masm 5-7.asm,连接 link 5-7.obj,生成 5-7.exe。

D:\dos〉masm 5-7.asm
D:\dos〉link 5-7.obj
D:\dos〉5-7.exe

(3)运行结果:

**分析思考**:

  • (1)键盘输入和结果显示是一个用户程序的常用功能。我们希望键盘输入的是十进制数,进行运算后能显示出十进制或者十六进制数。而键盘输入和屏幕显示涉及到对外设硬件的访问,由于汇编语言是直接控制机器的符号语言,没有提供类似的功能函数或语句,需要自己设计相关的程序段实现这个功能。
  • (2)这段程序中用到的转移指令有一些是往回跳转的,无论是无条件转移 JMP 还是条件转移指令 JNZ 等都有这种转移方向。这种分支走向已经把程序的流程构成了一个环,就是循环结构。
  • (3)画出 5-7.asm 程序流程图。
  • (4)这个程序只能输入一个两位的十进制数,如果能输入多位十进制数转换成十六进制数,那么就是一个小的工具软件了。程序的修改可参考 7.1.2 节示例 7-1。

3.8 分支程序例八

本例子为教材的示例 5-8。

设计目标

当我们设计菜单程序时,如果有 3 项功能,希望分别按下 1、2、3 键就转移到 3 个不同的程序段执行。菜单程序的设计可以利用分支程序实现。

设计菜单程序。实现三个功能:

  1. 小写字母转换为大写
  2. 计算立方值
  3. 退出

设计思路

(1)在数据段中定义菜单。每行换行显示;

(2)用 9 号功能显示菜单;

(3)根据输入选择执行某段程序。

实验步骤

(1)双击桌面上的记事本 gedit,录入下列程序:

;5-8.asm  菜单程序设计1、输入字串,将小写转换为大写2、计算0-9立方值3、退出。
data segment
    mess0 db 0ah,0dh,'1. input string'
          db 0ah,0dh,'2. calculate cube'
          db 0ah,0dh,'3. exit'
          db 0ah,0dh,'select:$'
    mess1 db 0ah,0dh,'input:$'
    mess2 db 0ah,0dh,'output:$'
    buff  db 10,?,10 dup(?)
    x     db ?,?,?
data ends
code segment
    assume cs:code,ds:data
start:
    mov ax,data
    mov ds,ax
let0:
    mov dx,offset mess0                ;显示菜单
    mov ah,9
    int 21h
    mov ah,1                            ;输入选择
    int 21h 
    cmp al,'1'
    jz prog1
    cmp al,'2'
    jz prog2
    jmp prog3                        ;按其它键均退出
    prog1:                            ;菜单1
    ;5-4.asm  程序部分
    ;输入英文单词,将小写字母转换为大写
    ;……
    jmp let0                            ;返回主菜单
    prog2:                            ;菜单2
    ;5-5.asm  程序部分
    ;计算0-9立方值
    ;……
    jmp let0                            ;返回主菜单
    prog3:                            ;菜单3.退出
    mov ah,4ch
    int 21h
    code ends
    end start

(2)在 dos 子目录下保存为 5-8.asm,经过汇编 masm 5-8.asm,连接 link 5-8.obj,生成 5-8.exe。

D:\dos〉masm 5-8.asm
D:\dos〉link 5-8.obj
D:\dos〉5-8.exe

(3)运行结果:

**分析思考**:

  • (1)这个菜单中的前两个功能程序 5-4.asm 和 5-5.asm 在前几节中我们已经编写过,加入到菜单程序中时要注意只加入该程序的主要部分而不是全部。还要注意各段程序的标号不能冲突。
  • (2)菜单选择部分采用比较指令 CMP AL,’1’ 和条件转移指令 JZ PROG1 实现分支,这种设计思想对于菜单项较少的情况比较简便;当菜单项增多时,比较指令和条件转移指令就要多次使用,程序冗长。在这种情况下,可以采用教材 5.5.3 节中介绍的分支表的方法。

3.9 分支程序例九

本例子为教材的示例 5-10。

**关注点**:如何显示十六进制数

设计目标

用查表的方法将内存单元中的字用十六进制显示出来。

设计思路

(1)设内存单元 X1 中共有 4 个字。这些数据以十进制形式书写,在汇编时,系统自动变为二进制保存,我们可以将其看成是十六进制。比如 49 保存后是 0031H,298 是 012AH;

(2)建立一个十六进制数码表 HEX。用查表方式可取得相对应的十六进制数的字符;

(3)1 位十六进制数是由 4 位二进制组成,用循环左移 4 位的方式将要显示的数位移到最低 4 位处,将其余的高 12 位都清零;

(4)采用 HEX[DI]寄存器相对寻址方式查表,以 HEX 表地址作为相对量,用 DI 寄存器存放十六进制某一位数,对应取出字符;改变 DI 的值可取出不同的数位值;

(5)用 DOS 的 2 号功能显示该数位值。

实验步骤

(1)双击桌面上的记事本 gedit,录入下列程序:

;5-10.asm  用查表方法将内存单元中的字以十六进制显示出来。
data segment
    x1 dw 49,298,23456,65530
    count db 4
    hex db '0123456789ABCDEF'     ;十六进制数码表
    mess db 0dh,0ah,'HEX=$'
data ends
code segment
    assume cs:code,ds:data
start:
    mov ax,data
    mov ds,ax
    mov si,0
let0:
    mov dx,offset mess            ;显示提示
    mov ah,9
    int 21h
    mov bx,x1[si]                ;取出x1,如49=0031h
    mov ch,4
    mov cl,4
let1:                            ;每次循环左移4位
    rol bx,cl                    ;0031→0310→3100→1003→0031
    mov ax,bx                    
    and ax,000fh                ;保留最低4位
    mov di,ax                    ;放入DI
    mov dl,hex[di]                ;查表显示某位
    mov ah,2                    ;显示
    int 21h
    dec ch
    jnz let1                    ;返回let1显示十六进制数的下一位
    add si,2
    dec count                    ;X1的4个字都显示了?
    jnz let0                    ; 返回let0,显示下一字
out1:
    mov ah,4ch
    int 21h
code ends
    end start

(2)在 dos 子目录下保存为 5-10.asm,经过汇编 masm 5-10.asm,连接 link 5-10.obj,生成 5-10.exe。

D:\dos〉masm 5-10.asm
D:\dos〉link 5-10.obj
D:\dos〉5-10.exe

(3)运行结果:

**分析思考**:

  • (1)这个程序也是要显示十六进制数,但是不采用将数值除以 16 的方法,而是用查字符表的方式。可以对多位十进制数(不超过 65535)转换,方法简便。
  • (2)本程序结合了移位指令、逻辑指令和转移指令实现分支。
  • (3)由于是字单元,因此最多显示 4 位十六进制数。如果要显示更多位,如何编程?

4. 走向分支

本节实验取自教材中第五章的《实例五 走向分支》。

分支判断是计算机具有智能的表现,而这种智能是受控于人的意志的。也就是说,需要编写程序人员的智慧和判断,设计出计算机能理解的指令程序供机器运行。

在分支程序设计中,要通过条件判断做转移。而条件转移指令有四种类型,根据标志的转移、无符号数比较转移、带符号数比较转移和 CX 为 0 转移。具体用哪种转移指令更好呢? 这需要根据题目内容来选择。

实验目的

通过分析和运行示例程序,对分支程序设计有更深一步的了解。掌握分支程序设计方法,设计出具有自己风格的分支程序。

实验内容

(1)菜单程序设计
  1. 参考示例 5-8 设计菜单程序,包含示例 5-2、示例 5-3 及退出三个菜单项。将示例 5-2 改为从键盘输入 X;示例 5-3 增加显示功能,显示出统计个数。
(2)分支程序设计

参考示例 5-10 和示例 5-7,完成下列实验内容:

  1. 修改示例程序,显示出二进制数。显示结果供参考:
  2. 5-7.ASM 是一个十进制与十六进制转换的小工具,改写为输入十进制数 0~255,显示出相应的十六进制数。
实验要求
  1. 画出程序框图;
  2. 实验内容用截图形式记录实验结果;
  3. 写出实验结果分析。
实验拓展
  1. 参考第 9 章示例 9-3,在屏幕上清屏、开窗口,将菜单程序带颜色地显示在窗口中。提示:可利用宏库 9-4.MAC 中的功能。
  2. 编写一个将 AX 寄存器中值依次循环左移 1 位、并依次显示出该十六进制数的程序。

类似文章