Skip to content
This repository was archived by the owner on May 8, 2019. It is now read-only.

Latest commit

 

History

History
767 lines (702 loc) · 25 KB

File metadata and controls

767 lines (702 loc) · 25 KB

第6部分 子程序与模块化

##一、课程简介

###1.1 课程说明

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

###1.2 实验环境

1.DOS 环境
实验环境中安装有dosemu可以模拟DOS环境,并提供DEBUGMASMLINK等汇编语言开发程序。如果您对实验环境不熟悉,推荐您先学习《新手入门之玩转实验楼》。
**2.进入汇编实验 **
在桌面上双击dosemu图标,直接进入DOS。再做如下操作:

C:\〉D:           ——回车后进入D盘  
D:\〉CD DOS       ——进入DOS子目录  
D:\dos〉DIR       ——列出目录中的文件(包括masm和link)  

以后的汇编实验程序都要在dos这个子目录中进行。 img

##二、子程序(过程)

###2.1 一个改造的例子

前几章介绍的5-7.ASM的程序只能从键盘输入一个两位的十进制数,用十六进制显示。如果想输入多位的十进制数还要修改程序,增加更多的条件判断转移指令,将进一步加大程序的复杂性,而采用子程序结构可以较好地解决问题。

  • 关注点:子程序结构 ,子程序的调用与返回

示例7-1
多次输入一个65535以内的十进制数并以十六进制显示出来。按ESC键结束。
设计思路:
(1) 设主程序标号LET0,一个子程序标号为LET1,另一个子程序标号LET2;
(2) 主程序是一个死循环,只有当按下ESC键时才能退出、结束程序;
(3) 子程序LET1功能为键盘输入,并把输入的数字变为十进制数,保存在X单元; (4) 子程序LET2功能为通过查表将X单元中的数值用十六进制显示出来。
程序框图:

img

实验步骤:

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

;7-1.asm  用子程序多次输入一个65535以内的十进制数并以十六进制显示出来。按ESC键结束
data segment
x dw 0
mess1 db 0dh,0ah,'input dec=$'
mess2 db 0dh,0ah,'out HEX=$'
hex db '0123456789ABCDEF'
data ends
code segment
assume cs:code,ds:data
start:
mov ax,data
mov ds,ax
;主程序
let0: mov x,0
mov dx,offset mess1			;显示提示1
mov ah,9
int 21h
call let1						;调用子程序1
call let2						;调用子程序2
jmp let0
;子程序1:键盘输入、形成十进制数x
let1: mov ah,1						;键盘输入十进制数
int 21h
cmp al,27						;是ESC键?
jz out1
sub al,30h						;其它字符?
jl exit						;是,转exit
cmp al,9
jg exit	
mov ah,0		
xchg ax,x						;形成十进制数
mov cx,10
mul cx	
add x,ax						;相加并保存
jmp let1
exit:	 ret							;ret返回主程序
;子程序2:查表,显示十六进制
let2: mov dx,offset mess2				;显示提示2
mov ah,9
int 21h
mov bx,x						;将x→bx
mov ch,4
mov cl,4		;将bx中的二进制数循环左移4位(十六进制数循环左移1位)
rept1:rol bx,cl			        ;例如0021→0210→2100→1002→0021
mov ax,bx						
and ax,000fh					;保留最低4位
mov si,ax
mov dl,hex[si]					;查表显示十六进制数
mov ah,2
int 21h
dec ch							;显示下一位
jnz rept1
ret							;返回主程序
out1: mov ah,4ch
int 21h
code ends
end start

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

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

(3)运行结果:

img

分析程序得知, 子程序调用CALL指令应该和子程序返回RET指令成对出现,使用了CALL指令,子程序中一定要有RET返回指令。本例中程序结构清晰,各个子程序功能相对独立,程序运行灵活。

**思考:**为什么RET指令能够返回到主程序?CALL指令在执行时实际上做了什么? ###2.2 标准的子程序结构

  • **关注点:过程定义伪指令 **

在示例7-1中,子程序是以第一条指令的标号作为子程序名调用的。所谓调用,实际上就是程序转移到该标号去继续执行。这种方式虽然简便,但是在模块化程序结构设计中,是不规范的。尤其是其它模块中的某个程序想要调用这个子程序时,还需要指明该子程序标号是在哪个模块、哪个代码段的哪个程序中。标准的用法是用8086汇编语言提供的过程定义伪指令PROC来定义子程序。

示例7-2 从键盘键入一个多位十进制数X,回车结束输入。按十进制位相加后显示十进制结果Y。

设计思路:
(1) 主程序分别调用三个子程序。
(2) 子程序SUBR1为键盘输入多位十进制数且直接保存到X,输入的位数在BX;
(3) 子程序SUBR2将保存的X去掉ASCII码,按位相加,相加的结果在BX中;
(4) 子程序SUBR3将BX中的数用十进制显示;
(5) 采用将结果除以10保存余数的方法将BX中的数转换为十进制数,并用十进制数的ASCII码显示结果。
(6) 传参寄存器为BX。

实验步骤:

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

;7-2.asm  键入一个十进制数x,按位相加后显示十进制结果y。
data segment
  infor1 db 0ah,0dh,'x=$'
  infor2 db 0ah,0dh,'y=$'
 	   x  db 20 dup(?)
data ends
code segment 
assume cs:code,ds:data
start:	mov ax,data
mov ds,ax
;主程序
main proc far		;主程序定义,远程的
mov x,0
mov dx,offset infor1			;显示提示1
mov ah,9
int 21h
mov bx,0						;传参寄存器bx清0
call subr1					;调子程序1
mov cx,bx					;保存x的位数
mov ax,0
mov bx,0
call subr2					;调子程序2
mov dx,offset infor2			;显示提示2
mov ah,9
int 21h	
call subr3					;调子程序3
jmp main
out1:	mov ah,4ch
int 21h
main endp
;子程序1:键盘输入、保存
subr1 proc near		;定义子程序1,近程的
mov ah,1						;键盘输入十进制数
int 21h
cmp al,0dh					;回车?
jz exit
cmp al,'0'					;其它非法字符?
jl out1						;是转out1,直接退出
cmp al,'9'
jg out1	
mov x[bx],al					;保存键入的数码
inc bx						;BX=数码个数
jmp subr1
exit:	cmp bx,0					;第一键就是回车
jz out1
ret							;返回主程序
subr1 endp
;子程序2,按位相加
subr2 proc near        ;定义子程序2,近程的
mov ah,x[bx]					;取出键入的数码
and ah,0fh					;去掉ASCII码
add al,ah					;按位相加
inc bx
loop subr2					;循环累加
mov ah,0
mov bx,ax					;相加结果→bx传参寄存器
ret							;返回主程序
subr2 endp
;子程序3,将bx中的数显示为十进制数
subr3 proc near        ;定义子程序3,近程的
mov ax,bx					;bx为传参寄存器
mov cx,0
mov bx,10					
let1:								;将ax变为十进制数
mov dx,0						;字除法的高字清0
inc cx						;统计余数个数
div bx						;除以10,商在AX,余数在DX
push dx						;保存余数
cmp ax,0
jnz let1
let2:								;循环显示余数,循环次数在cx中
pop ax						;将余数弹入ax
add ax,0030h					;调整为ASCII码
mov dl,al					;2号功能显示
mov ah,2
int 21h
loop let2
ret							;返回主程序
subr3 endp
code ends
end start

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

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

(3)运行结果: img
###2.3 子程序传参

  • **关注点:存储单元地址的传参 **

示例7-4 求两个数组ARRAY1和ARRAY2的正数累加和。累加和分别放在TOTAL1和TOTAL2中。
设计思路:
(1) 采用BX和SI寄存器分别存放数组的地址和累加和单元的地址,将地址值传送到子程序中;
(2) 在子程序的运算过程中用基址变址方式到存储器中取出数组元素;
(3) 子程序的运算结果通过寄存器间接寻址方式保存到存储单元。

实验步骤:

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

;7-4.asm  分别求两个数组ARRAY1和ARRAY2的正数累加和。累加和分别放在TOTAL1和TOTAL2中。
data segment 
array1 dw 3,2,-5,8,-7
array2 dw 4,-1,5,6,2
total1 dw ?
total2 dw ?
m dw 5
data ends
code segment
assume cs:code,ds:data
main proc far
start:	mov ax,data
	mov ds,ax
	mov cx,m						;数组元素个数
	lea bx,array1					;数组1的偏移地址→bx传参寄存器
	lea di,total1					;累加和1的偏移地址→di传参寄存器
	call summ						;调子程序,求数组1的正数累加和
	lea bx,array2
	lea di,total2
	call summ						;求数组2的正数累加和
	mov ah,4ch
	int 21h
main endp
;子程序summ求累加和
summ proc near
	push cx							;保存循环次数
	mov ax,0
	mov si,0
loop1:cmp word ptr[bx][si],0			;判断正负数
	jle exit
	add ax,[bx][si]					;求正数的累加和
exit: add si,2
	loop loop1
	mov word ptr[di],ax				;保存到存储单元,用存储单元传参
	pop cx
	ret
summ endp
code ends
	end start

运行说明:
在DEBUG下调试执行。执行带有子程序的程序时,要注意在找断点时要找程序结束指令MOV AH,4CH和INT 21H,而不要随意进入子程序。因为主程序是用CALL指令进入子程序的,涉及到堆栈操作,子程序返回时是通过RET返回的,要修改栈指针。稍有不慎,就会使堆栈混乱,造成死机。
(2)在dos子目录下保存为7-4.asm,经过汇编masm 7-4.asm,连接link 7-4.obj,生成7-4.exe。

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

(3)运行结果:
img
断点是001F,执行G 001F之后,子程序也一起执行了。查看数据段,可看到在0014单元是第一个累加和等于000DH,其后为第二个累加和为0011H。

示例7-5 利用子程序编程实现矩阵的乘法C=A×B。
矩阵的乘法:A=a﹙m,n﹚和B=b﹙n,m﹚,其结果矩阵为C=c﹙m,m﹚。其元素c﹙i,j﹚为A的第i行向量与B的第j列向量的内积。(设m=3,n=4)
img

设计思路:
(1) 实现矩阵乘法需要三重循环。
(2) 采用主程序MAIN负责最外层循环,控制A矩阵的行;子程序SUBR1用双重循环完成A矩阵的一行与B矩阵所有列的乘加。
(3) 设置Y单元为A矩阵每行的首地址,Y以4为间隔增加。将Y入栈传参,子程序从堆栈中读取Y→bx。进入子程序后,由于栈指针SP要改变,因此用BP作为取参的指针;Y在当前栈指针+4的堆栈单元中。
(4) 用BX、SI、DI寄存器作为三个矩阵A、B、C的下标;BX以1为间距增加,SI以3为间距增加,DI以1为间距增加。

实验步骤:

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

; 7-5.asm  堆栈传参。实现两个矩阵的乘法c=a*b 
data segment
  a db 1,1,1,1
    db 2,2,2,2
    db 3,3,3,3
  b db 1,1,1
    db 2,2,2
    db 3,3,3
    db 4,4,4
 m  dw 3						;A矩阵3行4列
 n  dw 4						;B矩阵4行3列
 y  dw 0
 c  db 9 dup(?)					;C矩阵3行3列
data ends
code segment
   assume cs:code,ds:data
main proc far
start:	mov ax,data
	mov ds,ax
   	mov cx,m					;主程序循环次数3次
	mov di,0
	mov si,0
rept3:push y					;用堆栈传参,保存y
	push cx						;保存主程序中循环次数
	mov cx,m					;子程序外循环次数
	call subr1
	pop cx
	pop y						;弹出保存的A上一行首址
	add y,4						;指向A矩阵下一行
	loop rept3	
	mov ah,4ch
	int 21h
main endp
;子程序subr1
subr1 proc near
	mov bp,sp					;call指令后的栈指针值→bp
rept2:	push cx					;共做3列
	mov bx,[bp+4]				;从堆栈中取出y→bx
	mov si,m					;m=3
	sub si,cx					;B的起始下标
	mov cx,n					;n=4,子程序内循环次数			
rept1:				
	mov al,a[bx]				;A的下标变化0,1,2,3
	mov dl,b[si]
	imul dl						;乘加
   	add c[di],al				;C矩阵
	inc bx						;A行的下一个元素
	add si,3					;B的一列
	loop rept1					;循环4次
	inc di						
	pop cx
	loop  rept2			
	ret
subr1 endp
code ends
     end start

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

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

(3)运行结果:

img

运行说明:
(1) 矩阵C的9个元素从001E单元开始存放,分别是十六进制的0A、0A、0A,14、14、14,1E、1E、1E。
(2) 程序中堆栈变化情况如下:(图中字母改为Y了)

img

可看出,主程序中的两条指令PUSH指令和一条CALL指令把相关的参数入栈保存;进入子程序后先执行了”mov bp,sp”,把此时的栈指针SP保存到基址指针BP中;接着又执行”push cx”,栈指针指在当前SP位置上。BP+4就是Y所在栈单元的地址。

##三、子程序与模块化 本节实验取自教材中第七章的《实例七 子程序与模块化》 ###3.1 模块化结构

在复杂的程序设计中,采用模块化结构可以划分功能,分解程序;使程序由复杂变为简单,由混乱变为清晰,程序代码易读。在各个子程序之间存在参数传递问题,因此在编写程序时,确定传参方式和嵌套调用层次十分重要。另外,每个子程序的功能应该用注释标明,程序采用的算法和指令的用途也应该标注出来,便于以后的阅读和修改。

示例7-6 从键盘输入学生姓名和成绩,按成绩升序排序,并显示出排序结果。
本题目的关键之处在于从键盘输入的姓名和成绩都是ASCII码,排序时成绩要变为二进制数或BCD码。排序时不光是成绩顺序要改变,而且姓名顺序也要随之改变,显示的结果才正确。这是一个较大的程序,采用模块化结构将功能分解,用子程序调用和嵌套实现。

设计思路:
(1) 主程序和5个子程序。子程序分别是INPUT键盘输入、COPY数据转存、CHANGE十进制数ASCII码→二进制、SORT按成绩排序和PRINT打印排序名单。
(2) 用变量P控制输入的人数,本例中P=3。
(3) 姓名和成绩输入分别用DOS中断调用的10号功能实现字串输入。由于10号功能可以设定输入的字符个数和获得实际输入个数,使用方便。但输入最后字符之后,回车符0DH也被保存;需要将其改为$,便于输出时直接用9号功能显示姓名和成绩。
(4) 用BUFFER1和BUFFER2作为键入的姓名和成绩的缓存区,然后将所有人名和成绩用串传送指令转存到SNAME和SCORE1中保存,打印输出时可以利用。
(5) 将SCORE1中成绩的十进制数ASCII码转换为二进制数→SCORE2;
(6) 按SCORE2中的成绩排序,同时将保存在MINGCI中的输入次序号也一起交换,以次序号作为排序指针,在SNAME和SCORE1中查找相应的人名和成绩;
(7) 打印排序名单时,从MINGCI中取出次序号作为位移量,到SNAME和SCORE1中取出姓名和对应的成绩用9号功能显示。排序后MINGCI中先取出的次序号一定是成绩最高的人的,其他类推。
程序框图:
img
img

实验步骤:

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

;7-6.asm  从键盘输入学生姓名和成绩,按成绩升序排序。
data segment
	infor0 db 0ah,0dh,'sort=$'
	infor1 db 0ah,0dh,'input name:$'
	infor2 db 0ah,0dh,'input score:$'
	n equ 8							;姓名长度
	m equ 4							;成绩长度(3位+回车符)
	p equ 3							;输入的人数
	q equ 3							;成绩位数
	buff1 db n,?,n+1 dup('$')		;姓名缓冲区,加$符以便输出时用
	buff2 db m,?,m+1 dup('$')		;成绩缓冲区
	sname db p dup (n+1 dup('$'))	;保存姓名
	score1 dw p dup (m+1 dup('$'))	;保存成绩
	score2 dw p dup (m+1 dup(0))
	mingci db p dup(0)				;名次
	x dw ?
	sign1 dw 0
	sign2 dw 0
	cont db '1'						;计数
data ends
code segment
	assume cs:code,ds:data,es:data
main proc far
start:
	mov ax,data
	mov ds,ax
	mov es,ax
	mov bx,0
	mov cx,0
	call input
	call sort
	call print
	mov ah,4ch
	int 21h
main endp
;子程序1,输入姓名、成绩
input proc
	inc bx							;输入次数统计
	cmp bx,p						;输入次数>p?
	ja exit
	lea dx,infor1					;显示提示1 
	mov ah,9
	int 21h
	lea dx,buff1					;输入姓名
	mov ah,10
	int 21h
	;可删掉mov al,buff1+1					;实际输入个数→al
	;add al,2						;+2,包含buff1的0,1号单元
	;mov ah,0
	;mov si,ax						;回车0d所在位置,跟在最后一个字符后
	;mov buff1[si],'$'				;将0d换为$,便于输出显示
	lea dx,infor2					;显示提示2
	mov ah,9
	int 21h
	lea dx,buff2					;输入成绩
	mov ah,10
	int 21h
	;mov al,buff2+1					;实际输入个数
	;add al,2						;个数+2,包含0,1单元,为找到0d
	;mov ah,0
	;mov si,ax
	;mov buff2[si],'$'				;将0d换为$,便于输出显示
	mov mingci[bx-1],bl				;bx为输入次数,保存输入的次序
	cmp bx,1						;第一次输入转let1
	jz let1
	add sign1,n+1					;姓名间隔为n+1
	add sign2,q						;成绩间隔为q
let1:
	call copy						;子程序嵌套
	jmp input
exit:
	ret
input endp
;子程序2,数据转存
copy proc
	mov cx,n+1						;姓名长度+1(包含$)
	lea si,buff1+2
	lea di,sname					;姓名传送到sname
	add di,sign1					;加上间隔值
	cld
	rep movsb
	mov cx,n
	mov ax,'$'						;用$覆盖姓名区,清除已输入的姓名
	lea di,buff1+2
	rep stosb
	mov cx,m+1						;成绩位数+1(包含$)
	lea si,buff2+2
	lea di,score1					;成绩传送到score1
	add di,sign2					;加上间隔值
	cld
	rep movsb
	lea si,buff2+2
	mov di,sign2					
	call change						;二进制成绩→score2
	ret
copy endp
;子程序3,十进制数ASCII码→二进制
change proc
	mov x,0
	mov cx,[si-1]					;成绩的位数→cx
	and cx,000fh					;保留低4位 
rept2:
	mov al,[si]						;按位取出成绩
	cmp al,30h						;是否在0-9之间
	jl exit1
	cmp al,39h
	jg exit1
	and ax,000fh					;去掉ASCII码		
	xchg ax,x	
	mov dx,10						;将ax中前一次形成的数扩大10倍
	mul dx	
	add x,ax						;保存到x
	inc si
	loop rept2
	mov ax,x						;按十进制形成的成绩→以二进制保存
	mov score2[di],ax				;二进制成绩送入score2
	mov x,0	
	add sign2,2						;下一个成绩单元
exit1:	ret
change endp
;子程序4 ,按成绩排序
sort proc 
	mov cx,p 		       			 ;数组长度
	dec cx
loop1:push cx						 ;保存外循环次数
	mov bx,0
	mov si,0
loop2:mov ax,score2[bx]
	cmp ax,score2[bx+m+1]			 ;m+1=5,下一人的成绩
	jge next					 	 ;降序
	xchg ax,score2[bx+m+1]			 ;交换成绩
	mov score2[bx],ax
	mov al,mingci[si]		
	xchg al,mingci[si+1]			 ;交换名次
	mov mingci[si],al
next:add bx,m+1				     	 ;比较下一个成绩
	inc si
	loop loop2
	pop cx							 ;恢复外循环次数
	loop loop1
	ret
sort endp
;子程序5,打印排序名单
print proc
	lea dx,infor0					;显示结果提示
	mov ah,9
	int 21h
	mov cx,p
	mov bx,0
	mov ax,0
	mov di,0
rept3:	
	mov dl,0ah						;回车换行
	mov ah,2
	int 21h
	mov dl,0dh
	int 21h
	mov dl,cont					   ;显示名次序号
	mov ah,2
	int 21h
	inc cont
	mov dl,0ah						;回车换行
	mov ah,2
	int 21h
	mov dl,0dh
	int 21h
	mov ax,0
	mov al,mingci[di]				;取名次
	dec al							;位置-1,因为地址从0开始
	mov bl,9						;姓名位移量ax=al×9(包含$)
	mul bl							;乘积在ax
	lea dx,sname
	add dx,ax						;偏移地址+姓名位移量
	mov ah,9						;显示姓名
	int 21h
	mov dl,0ah						;回车换行
	mov ah,2
	int 21h
	mov dl,0dh
	int 21h
	mov ax,0
	mov bx,0
	mov al,mingci[di]				;取名次
	dec al							;地址从0开始
	mov bl,5						;成绩位移量=al×5(包含$)
	mul bl
	lea dx,score1
	add dx,ax						;偏移地址+成绩位移量
	mov ah,9						;显示成绩
	int 21h	
	inc di
   loop rept3
	ret 
print endp
code ends
end start

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

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

(3)运行结果:
img

  • 1)本例中p设为3,可输入3个人的姓名和成绩,更改p值可输入多人。
  • 2)程序采用在输入缓冲区中加$符号的做法,将每个名字用$分隔,每个成绩也用$分隔;在输出时可用9号功能直接显示。

###3.2 实验示例

采用多种传参方式能够实现复杂程序的设计要求,同时也应该根据题目来选择寄存器、存储器传参方式。

**示例7-7 **将键入的两个十进制数相加,并显示十进制结果。

设计思路:
(1) 主程序调用三个子程序。主程序用JMP构成循环,可多次做计算;如果按下的不是数字键则退出循环,结束程序;
(2) SUBR1子程序1:功能为键盘输入,数字键ASCII码→十进制数(该十进制数保存为二进制),用存储单元X传参;
(3) SUBR2子程序2:功能为两数相加,以寄存器BX传参;
(4) SUBR3子程序3:功能为显示十进制数。先将二进制数→十进制数。将传参寄存器BX中的二进制数用除以10取余数的方法转换为十进制数,再将余数加30H变为十进制数的ASCII码,然后显示。

实验步骤:

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

;7-7.asm  寄存器、存储器传参。键入两个十进制数相加,并显示十进制结果。
data segment
  x dw ?,?
  cc1 db 0ah,0dh,'x1=$'
  cc2 db 0ah,0dh,'x2=$'
  cc3 db 0ah,0dh,'y=x1+x2=$'
data ends
code segment 
assume cs:code,ds:data
start:
mov ax,data
mov ds,ax
;主程序
main proc far
mov cx,0
mov bx,0
mov si,0
mov dx,offset cc1				;显示提示1
mov ah,9
int 21h
call subr1					;输入第1个数
mov dx,offset cc2				;显示提示2
mov ah,9
int 21h	
mov bx,0
mov cx,0
mov si,2
call subr1					;输入第2个数
call subr2					;相加
mov dx,offset cc3				;显示提示3
mov ah,9
int 21h	
call subr3					;显示结果
jmp main
out1:							;结束
mov ah,4ch
int 21h
main endp
;子程序1:键盘输入、数字键ASCII码→十进制数(以二进制保存)
subr1 proc near
mov ah,1						;键盘输入十进制数
int 21h
cmp al,0dh					;回车?
jz exit
cmp al,'0'					;其它字符?
jl out1						;是其它字符转out1,退出,结束。
cmp al,'9'
jg out1	
and ax,000fh					;形成十进制数(以二进制保存)
xchg ax,bx							
mov cx,10
mul cx						;乘以10 → 十位、百位
add bx,ax		
jmp subr1
exit:cmp cx,0					;先键入了回车,退出
jz out1
mov x[si],bx					;存储单元x传参
ret 
subr1 endp
;子程序2,两数相加
subr2 proc near
mov bx,x
add bx,x+2					;寄存器BX传参
ret
subr2 endp
;子程序3,显示十进制数。将二进制数→十进制数
subr3 proc near
mov ax,bx
mov cx,0
mov bx,10						;将ax变为十进制数
let1:
mov dx,0
inc cx
idiv bx						;除以10,取余数
push dx						;保存余数
cmp ax,0
jnz let1
let2:							;显示结果
pop ax						;将余数弹入ax
add ax,0030h					;余数调整为ascii码
mov dl,al
mov ah,2						;显示
int 21h
loop let2
ret
subr3 endp
code ends
end start

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

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

(3)运行结果:
img

实验结果分析:
(1) 该程序要求输入正数,运算结果的最大值不能超过65535,即16位寄存器保存无符号数的范围。
(2) 输入的两个十进制数的位数可以不一样。
(3) 如果输入负的十进制数,程序应该怎样编写?(可参考第8章示例8-4)

###3.3 实验任务

1.实验目的
通过分析和运行示例程序,掌握子程序设计思路和技巧。根据子程序传参方法,尝试实现较复杂的程序功能。

2.实验内容
参考示例7-7,完成下列实验内容:

  • 1)实现两个键入的十进制数相减运算。(提示:如果结果为负数,需要求绝对值)
  • 2)实现两个键入的十进制数相乘运算。(提示:要考虑结果溢出问题)
  • 3)实现两个键入的十进制数相除运算。(提示:分别显示商和余数)
  • 4)键入一个十六进制数,求其真值(用十进制显示,负数前加负号“-”)(7-2b.asm)

3.实验要求

  • 1)4个题目可任选2个
  • 2)实验内容用截图形式记录实验结果
  • 3)写出实验结果分析

4.实验拓展
设计一个小计算器。用菜单选择计算功能。