第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院教学重点综合应用第 2章硬指令和第
3章伪指令,第 4章从程序结构角度展开程序设计,重点掌握:
分支结构程序设计
循环结构程序设计
子程序结构程序设计第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院
4.1 顺序程序设计
顺序程序完全按指令书写的前后顺序执行每一条指令,是最基本,最常见的程序结构例 4.1 计算例 4.2 移位例 4.1
.model small
.stack
.data
X dw 5
Y dw 6
Z dw 7
W dw?
.code
.startup
mov ax,X
add ax,Y
add ax,Z
mov W,ax
.exit 0
end
例 4.2- 1/2.data
qvar dq 1234567887654321h
.code
mov al,byte ptr qvar[6]
mov byte ptr qvar[7],al
mov al,byte ptr qvar[5]
mov byte ptr qvar[6],al
mov al,byte ptr qvar[4]
mov byte ptr qvar[5],al
mov al,byte ptr qvar[3]
mov byte ptr qvar[4],al
图示例 4.2- 2/2mov al,byte ptr qvar[2]
mov byte ptr qvar[3],al
mov al,byte ptr qvar[1]
mov byte ptr qvar[2],al
mov al,byte ptr qvar[0]
mov byte ptr qvar[1],al
mov byte ptr qvar[0],0
12 34 56 78 87 65 43 21h
34 56 78 87 65 43 21 00h移位后图示第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院
4.2 分支程序设计
分支程序根据条件是真或假决定执行与否
判断的条件是各种指令,如 CMP,TEST等执行后形成的状态标志
转移指令 Jcc和 JMP可以实现分支控制;还可以采用 MASM 6.x提供的条件控制伪指令实现单分支:求绝对值双分支:例 4.3等多分支:例 4.4等第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院单分支程序设计
条件成立跳转,否则顺序执行分支语句体;注意选择正确的条件转移指令和转移目标地址例题 求绝对值;计算 AX的绝对值
cmp ax,0
jns nonneg ;分支条件,AX≥ 0
neg ax ;条件不满足,求补
nonneg:mov result,ax ;条件满足;计算 AX的绝对值
cmp ax,0
jl yesneg ;分支条件,AX< 0
jmp nonneg
yesneg:neg ax ;条件不满足,求补
nonneg:mov result,ax ;条件满足
Good
Bad
第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院双分支程序设计
条件成立跳转执行第 2个分支语句体,
否则顺序执行第 1个分支语句体 。 注意第
1个分支体后一定要有一个 JMP指令跳到第 2个分支体后例题 显示 BX最高位 -1
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 ;显示例题 显示 BX最高位 -2
shl bx,1 ;BX最高位移入 CF
jnc one ;CF= 0,即最高位为 0,转移
mov dl,’1’;CF= 1,即最高位为 1,DL← ’1’
jmp two ;一定要跳过另一个分支体
one,mov dl,’0’ ;DL← ’0’
two,mov ah,2
int 21h ;显示例题 显示 BX最高位 -3
mov dl,’0’ ;DL← ’0’
shl bx,1 ;BX最高位移入 CF
jnc two ;CF= 0,最高位为 0,转移
mov dl,’1’ ;CF= 1,最高位为 1,DL← ’1’
two,mov ah,2
int 21h ;显示双分支程序可以改为单分支程序例 4.3 判断有无实根- 1/2
.startup
mov al,_b
imul al
mov bx,ax ;BX中为 b2
mov al,_a
imul _c
mov cx,4
imul cx ;AX中为 4ac( DX无有效数据 )
例 4.3 判断有无实根- 2/2
cmp bx,ax ;比较二者大小
jge yes ;条件满足?
mov tag,0;第一分支体:条件不满足,tag← 0
jmp done ;跳过第二个分支体
yes,mov tag,1;第二分支体:条件满足,tag← 1
done,.exit 0
第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院多分支程序设计
多个条件对应各自的分支语句体,哪个条件成立就转入相应分支体执行 。 多分支可以化解为双分支或单分支结构的组合,例如:
or ah,ah ;等效于 cmp ah,0
jz function0 ;ah= 0,转向 function0
dec ah ;等效于 cmp ah,1
jz function1 ;ah= 1,转向 function1
dec ah ;等效于 cmp ah,2
jz function2 ;ah= 2,转向 function2
图示第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院地址表形成多分支
需要在数据段事先安排一个按顺序排列的转移地址表
输入的数字作为偏移量 。 因为只有 2个字节 16位偏移地址,所以偏移量需要乘
2
关键是要理解间接寻址方式 JMP指令地址表 分支 1地址 分支 2地址,..
table dw disp1,disp2,disp3,disp4,...
.data
msg db 'Input number(1~8):',0dh,0ah,'$'
msg1 db 'Chapter 1,,..',0dh,0ah,'$'
msg2 db 'Chapter 2,,..',0dh,0ah,'$‘
...
msg8 db 'Chapter 8,,.,',0dh,0ah,'$'
table dw disp1,disp2,disp3,disp4
dw disp5,disp6,disp7,disp8;取得各个标号的偏移地址例 4.4 数据段- 1/3
此处等同于 offset disp1
start1,mov dx,offset msg;提示输入数字
mov ah,9
int 21h
mov ah,1 ;等待按键
int 21h
cmp al,'1' ;数字 < 1?
jb start1
cmp al,'8' ;数字 > 8?
ja start1
and ax,000fh ;将 ASCII码转换成数字例 4.4 代码段- 2/3
dec ax
shl ax,1 ;等效于 add ax,ax
mov bx,ax
jmp table[bx];( 段内 ) 间接转移,IP←[table+bx]
start2,mov ah,9
int 21h
.exit 0
disp1,mov dx,offset msg1 ;处理程序 1
jmp start2
...
例 4.4 代码段- 3/3
可以改为 call table[bx]
对应修改为 ret
第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院
4.3 循环程序设计
循环结构一般是根据某一条件判断为真或假来确定是否重复执行循环体
循环指令和转移指令可以实现循环控制;还可以采用 MASM 6.x提供的循环控制伪指令实现循环指令 LOOPE:例 4.6
转移指令:例 4.7
多重循环:例 4.8等循环指令 LOOP:例 4.5等第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院循环结构结束初始化 循环的初始状态循环体循环的工作部分及修改部分计数控制循环条件控制循环修改部分控制条件Y
N
.model small
.stack
.data
sum dw?
.code
.startup
xor ax,ax ;被加数 AX清 0
mov cx,100
again,add ax,cx;从 100,99,...,2,1倒序累加
loop again
mov sum,ax ;将累加和送入指定单元
.exit 0
end
例 4.5 求和计数控制循环循环次数固定
.startup
mov ax,wordX ;测试目标送 AX
mov cx,16 ;循环计数器置初值
mov dl,-1 ;计位器置初值
again,inc dl
test ax,1
ror ax,1 ;循环指令不影响 ZF
loope again;CX≠ 0且 ZF=1( 测试位为 0),继续循环
je notfound
mov byteY,dl
jmp done
notfound,mov byteY,-1 ;ZF=1,16个位均为 0
done,.exit 0
例 4.6
计数控制循环最大循环次数固定,满足条件退出
mov bx,offset string
again,mov al,[bx] ;取一个字符
or al,al ;是否为结尾符 0
jz done ;是,退出循环
cmp al,'A' ;是否为大写 A~ Z
jb next
cmp al,'Z'
ja next
or al,20h;是,转换为小写字母 ( 使 D5=1)
mov [bx],al ;仍保存在原位置
next,inc bx
jmp again ;继续循环
done,.exit 0
例 4.7 大小写条件控制循环利用标志退出大小写字母仅 D5位 不同第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院冒泡法
“冒泡法,是一种排序算法,不是最优的算法,但它易于理解和实现
冒泡法从第一个元素开始,依次对相邻的两个元素进行比较,使前一个元素不大于后一个元素;
将所有元素比较完之后,最大的元素排到了最后;
然后,除掉最后一个元素之外的元素依上述方法再进行比较,得到次大的元素排在后面;如此重复,直至完成就实现元素从小到大的排序
这需要一个双重循环程序结构图示
mov cx,count ;CX← 数组元素个数
dec cx ;元素个数减 1为外循环次数
outlp:mov dx,cx ;DX← 内循环次数
mov bx,offset array
inlp,mov al,[bx] ;取前一个元素
cmp al,[bx+1] ;与后一个元素比较
jna next;前一个不大于后一个元素,则不进行交换
xchg al,[bx+1];否则,进行交换
mov [bx],al
next,inc bx ;下一对元素
dec dx
jnz inlp ;内循环尾
loop outlp ;外循环尾例 4.8
计数控制双重循环;现有一个以 $结尾的字符串,要求剔除其中的空格
.data
string db ’Let us have a try !’,’$’
.code
.startup
mov si,offset string
outlp,cmp byte ptr [di],’$’;外循环,先判断后循环
jz done ;为 $结束
cmp byte ptr [si],’ ’;检测是否是空格
jnz next ;不是空格继续循环例 4.9 剔除空格- 1/2
mov di,si ;是空格,进入剔除空格分支;该分支是循环程序段
inlp,inc di
mov al,[di] ;前移一个位置
mov [di-1],al
cmp byte ptr [di],’$’;内循环,先循环后判断
jnz inlp
jmp outlp
next,inc si ;继续对后续字符进行处理
jmp outlp
done,.exit 0 ;结束例 4.9 剔除空格- 2/2 条件控制双重循环第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院
4.4 子程序设计
把功能相对独立的程序段单独编写和调试,作为一个相对独立的模块供程序使用,就形成子程序
子程序可以实现源程序的模块化,可简化源程序结构,可以提高编程效率子程序设计要利用过程定义伪指令参数传递是子程序设计的重点和难点子程序可以嵌套;
一定条件下,还可以递归和重入
4.4.1 过程定义伪指令过程名 proc [near|far]
...
过程名 endp
过程名 ( 子程序名 ) 为符合语法的标识符
NEAR属性 ( 段内近调用 ) 的过程只能被相同代码段的其他程序调用
FAR属性 ( 段间远调用 ) 的过程可以被相同或不同代码段的程序调用
对简化段定义格式,在微型,小型和紧凑存储模式下,过程的缺省属性为 near;在中型,大型和巨型存储模式下,过程的缺省属性为 far
对完整段定义格式,过程的缺省属性为 near
用户可以在过程定义时用 near或 far改变缺省属性第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院子程序的常见格式
subname proc ;具有缺省属性的 subname过程
push ax ;保护寄存器,顺序压入堆栈
push bx ;ax/bx/cx仅是示例
push cx
… ;过程体
pop cx ;恢复寄存器,逆序弹出堆栈
pop bx
pop ax
ret ;过程返回
subname endp ;过程结束;子程序功能:实现光标回车换行
dpcrlf proc ;过程开始
push ax ;保护寄存器 AX和 DX
push dx
mov dl,0dh ;显示回车
mov ah,2
int 21h
mov dl,0ah ;显示换行
mov ah,2
int 21h
pop dx ;恢复寄存器 DX和 AX
pop ax
ret ;子程序返回
dpcrlf endp ;过程结束例题 无参数传递的子程序
ALdisp proc ;实现 al内容的显示
push ax ;过程中使用了 AX,CX和 DX
push cx
push dx
push ax ;暂存 ax
mov dl,al ;转换 al的高 4位
mov cl,4
shr dl,cl
or dl,30h ;al高 4位变成 3
cmp dl,39h
jbe aldisp1
add dl,7 ;是 0Ah~ 0Fh,还要加上 7
aldisp1,mov ah,2 ;显示
int 21h
例 4.10 子程序- 1/3
pop dx ;恢复原 ax值到 dx
and dl,0fh ;转换 al的低 4位
or dl,30h
cmp dl,39h
jbe aldisp2
add dl,7
aldisp2,mov ah,2 ;显示
int 21h
pop dx
pop cx
pop ax
ret ;过程返回
ALdisp endp
例 4.10 子程序- 2/3
..,;主程序,同例 4.8源程序
mov bx,offset array;调用程序段开始
mov cx,count
displp,mov al,[bx]
call ALdisp ;调用显示过程
mov dl,',' ;显示一个逗号,分隔数据
mov ah,2
int 21h
inc bx
loop displp ;调用程序段结束
.exit 0
..,;过程定义
end
例 4.10 主程序- 3/3
第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院
4.4.2 子程序的参数传递
入口参数 ( 输入参数 ),
主程序提供给子程序
出口参数 ( 输出参数 ),
子程序返回给主程序
参数的形式:
① 数据本身 ( 传值 )
② 数据的地址 ( 传址 )
传递的方法:
① 寄存器 ② 变量 ③ 堆栈第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院例 4.11 求校验和
子程序计算数组元素的,校验和,
校验和是指不记进位的累加入口参数,数组的逻辑地址 ( 传址 )
元素个数 ( 传值 )
出口参数,求和结果 ( 传值 )
把参数存于约定的寄存器中,可以传值,也可以传址 。
子程序对带有出口参数的寄存器不能保护和恢复 ( 主程序视具体情况进行保护 )
子程序对带有入口参数的寄存器可以保护,也可以不保护;但最好一致例 4.11a
入口参数,CX=元素个数,
DS:BX=数组的段地址:偏移地址出口参数,AL=校验和用寄存器传递参数
.startup;设置入口参数 ( 含有 DS← 数组的段地址 )
mov bx,offset array;BX← 数组的偏移地址
mov cx,count ;CX← 数组的元素个数
call checksuma ;调用求和过程
mov result,al ;处理出口参数
.exit 0
例 4.11a 主程序
checksuma proc
xor al,al ;累加器清 0
suma,add al,[bx] ;求和
inc bx ;指向下一个字节
loop suma
ret
checksuma endp
end
例 4.11a 子程序
主程序和子程序直接采用同一个变量名共享同一个变量,实现参数的传递
不通模块间共享时,需要声明例 4.11b
入口参数:
count=元素个数,
array=数组名 ( 段地址:偏移地址 )
出口参数:
result=校验和用变量传递参数;主程序
call checksumb;子程序
checksumb proc
push ax
push bx
push cx
xor al,al ;累加器清 0
mov bx,offset array;BX← 数组的偏移地址
mov cx,count;CX← 数组的元素个数例 4.11b- 1/2
sumb,add al,[bx] ;求和
inc bx
loop sumb
mov result,al ;保存校验和
pop cx
pop bx
pop ax
ret
checksumb endp
例 4.11b- 2/2
主程序将子程序的入口参数压入堆栈,
子程序从堆栈中取出参数
子程序将出口参数压入堆栈,主程序弹出堆栈取得它们例 4.11c
入口参数:
顺序压入偏移地址和元素个数出口参数:
AL=校验和用堆栈传递参数
.startup
mov ax,offset array
push ax
mov ax,count
push ax
call checksumc
add sp,4
mov result,al
.exit 0
例 4.11c 主程序图示要注意堆栈的分配情况,保证参数存取正确,子程序正确返回,并保持堆栈平衡
checksumc proc
push bp
mov bp,sp ;利用 BP间接寻址存取参数
push bx
push cx
mov bx,[bp+6] ;SS:[BP+6]指向偏移地址
mov cx,[bp+4] ;SS:[BP+4]指向元素个数
xor al,al
sumc,add al,[bx]
inc bx
loop sumc
pop cx
pop bx
pop bp
ret
checksumc endp
例 4.11c 子程序图示第 4 章
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院
4.4.3 子程序的嵌套、递归与重入
子程序内包含有子程序的调用就是子程序嵌套,没有什么特殊要求,经常应用
当子程序直接或间接地嵌套调用自身时称为递归调用
子程序的重入是指子程序被中断后又被中断服务程序所调用
子程序的重入不同于子程序的递归
重入是被动地进入,而递归是主动地进入
重入的调用间往往没有关系,而递归的调用间却是密切相关的
递归子程序也是可重入子程序
HTTP://DLS.ZZU.EDU.CN 郑州大学远程教育学院第 4章 教学要求
1,掌握基本程序结构 ―― 顺序结构,分支结构,循环结构,子程序及其汇编语言程序设计
2,熟悉常见程序设计问题:
多精度运算,查表 ( 查代码,特定值等 )
ASCII,BCD及十六进制数据间的代码转换数据范围判断 ( 0~ 9,A~ Z,a~ z)
字母大小写转换;字符串传送,比较等操作求最小最大值,数据求和,统计字符个数子程序的寄存器和共享变量传递参数