实验二:分支与循环程序设计
一、实验目的
1、掌握程序设计中的3种基本结构(顺序结构、选择程序、循环程序)。
2、熟练使用汇编语言的指令:数据传送类指令、数据运算类指令、逻辑判断类指令与转移指令、循环指令等。
3、初步了解系统功能调用的使用方法,尝试使用01H号功能调用进行字符输入的方法及使用02H号功能调用进行字符输出(显示)的方法。
二、实验内容
1、计算1+2+3+…+10,将结果显示在屏幕上。
2、利用01H号功能调用输入10个一位数字,并将其由ASCII码转换为二进制数,依此保存到变量BUF的10个字节中,变量BUF的形式为BUF DB 10 DUP(?)。编程求出这10个数中的最大数和最小数,将最大数存入MAX单元、最小数存入MIN单元,并将其在屏幕上显示出来。
3、对于第2题,怎样修改程序可以同时实现将10个数字的累加功能。
4、对于第2题,若要求输入的是两位数,又该怎么办?
三、设计思想
3.1 实验内容一
我们需要先对1到10进行循环求和,再将计算结果显示在屏幕上。
对于求和,我们先对用于存放累加和的寄存器AX进行初始化,并将10赋值给CX寄存器(设定赋值操作循环次数为10次);然后设置循环,在next循环下进行AL和CL的ADD操作,每循环一次CX-1的同时都会在AL中进行累加,直到CX=0时完成循环。
对于在屏幕上显示结果,由于1-10相加结果为两位数,我们需要依次按位输出。首先,初始化被除数AX(补充AH为0)、除数BL为10,然后利用div BL指令计算出累加和的十位数字和个位数字,其中商(十位数字)在AL中,余数(个位数字)在AH中,再进行套路式转化:分别通过add AH,30H指令将两个数字变为字符形式,再利用mov DL,AL、mov AH,02h、int 21H实现打印输出,至此完成求和并以十进制显示结果。需要注意的是,在十位数字输出后AH内容进行更改,所以需要提前存储好AX中的内容。
其具体流程图见下图1 所示:
图1 实验内容一流程图
3.2 实验内容二
我们需要获取用户的输入并比较大小最后输出最大值和最小值。
由于数据需要保存在BUF中,且需要输出max和min,故先在DATA数据段内设置相关变量。其后,在代码段内对后续需要的DS、AX、CX计数器和SI变址器进行初始化。
对于所有的用户交互信息提示,我们均可先利用 变量名 DB '提示内容',0DH,0AH,'$'指令进行初始化,再利用 mov DX,offset 变量名、mov AH,9、int 21H来实现提示输出。
输入指令MOV AH,01H、INT 21H完成一位字符的输入,由于输入是16进制,所以需要通过and AL,0fH指令将ASCII码转换为二进制数,并通过MOV BUF[SI],AL指令将二进制数存入到变量BUF中。
与高级语言类似。若为第一个数据输入,则暂时将最大值和最小值设为该数据。若不是第一个数据输入,则需对该数据进行大小判断:先将该数据与当前最小值min进行比较,若该值比min小,则将min替换为该值;若该值大于或等于min,则跳入ifGreater框架,将该数据与当前最大值max进行比较,若该值比max大,则将max替换为该值。
随着数据的每一次输入,我们都比较一次输入值和最大值、最小值的相对关系。通过10次循环,就可以在完成数据的存入的同时,求解出最大值和最小值。
最后,将max和min分别赋值给DL寄存器,通过add DL,30H将数值转换为字符,利用MOV AH,02H和INT 21H指令对它们进行屏幕输出,至此完成所有操作。
其具体流程见下图2所示:
图2 实验内容二流程图
3.3 实验内容三
实验内容三是在实验内容二的基础上进行拓展,所有的流程同第二部分一致,只是增加了累加求和。累加求和可以在一个循环下完成,原理同实验内容一一致。最后将累加和赋值给DL寄存器,通过add DL,30H将数值转换为字符,利用MOV AH,02H和INT 21H指令对进行输出。
其具体流程见下图3 所示:
图3 实验内容三流程图
3.4 实验内容四
实验内容四也是在实验内容二的基础上进行拓展,流程同实验二,但是需要输入两位数据,且最后显示输出也需要进行合理的数值转换。
对于两位数据的获取:我们首先通过一次MOV AH,01H INT 21H输入一位后,通过SUB AL,30H指令转换进制,同时赋值给BL数值10,让其与AL进行相乘反制得到高位10进制,而后将AL赋值给DL进行高位数据保存。再次输入一位低位数据,转换为10进制后,与DL进行相加得到两位数据。通过循环十次得到一个数组内全是两位数。
对于两位数据的输出:我们需要将这两位数转换为ASCII码后进行高低位分别计算,然后依次输出至屏幕。其具体流程见下图4所示:
图4 实验内容四流程图
四、程序代码
4.1 实验内容一
code segment
assume CS:code
start: ;程序入口
;计算1+2+3+…+10,累加和=AL
mov AL,0
mov CX,10 ;循环次数
next: ;循环入口
add AL,CL
loop next
;将累加和AL显示在屏幕上,AL=55----->'5' '5'
;1.求十位数字
mov AH,0
mov BL,10 ;除数
div BL ;商=AL,余数=AH
add AL,30H ;把AL中的数转为相应字符,如5-->‘5’
;2.求个位数字
add AH,30H ;个位数字字符
;输出十位数字与个位数字
mov DL,AL
push AX
mov AH,2
int 21H
pop AX
mov DL,AH
mov AH,2
int 21H
;程序退出
mov AH,4cH
int 21H
code ends
end start
4.2 实验内容二
data segment
buf DB 10 DUP(?)
max DB ? ;最大值
min DB ? ;最小值
inputMessage DB 'Input 10 1-digit number:',0DH,0AH,'$'
maxMessage DB 0DH,0AH,'The max integer is:','$'
minMessage DB 0DH,0AH,'The min integer is:','$'
data ends
code segment
assume cs:code,ds:data ;关联段地址寄存器与具体段
start:
;把data段地址赋值给ds
mov AX,data ;data段名表示data段的段地址,常量
mov DS,AX
;输出提示
mov DX,offset inputMessage
mov AH,9
int 21H
;输入10个一位数字,由ASCII码转换为二进制数,保存到变量BUF,同时找最大值、最小值
mov CX,10
mov si,0
next:
;输入一个字符
mov AH,1
int 21H
;由ASCII码转换为二进制数,保存到变量BUF
and AL,0fH
mov BUF[si],AL
cmp CX,10 ;是否第一次循环
je firstNumber
cmp AL,min
jnl ifGreater ;不小于跳转,判断是否更大
mov min,AL ;发现更小的值,更换min
jmp L1
ifGreater:
cmp AL,max
jng L1
mov max,AL ;发现更大的值,更换max
jmp L1
firstNumber:
mov min,AL
mov max,AL
L1:
;准备下一次循环
;输出空格
mov DL," "
mov AH,2
int 21H
inc si
loop next
;输出最大值、最小值
;输出最大值提示字符串
mov dx,offset maxMessage
mov AH,9
int 21H
mov DL,max
add DL,30H ;最大值转为字符
mov AH,2 ;显示DL中的字符
int 21H
;输出最小值提示字符串
mov dx,offset minMessage
mov AH,9
int 21H
mov DL,min
add DL,30H ;最小值转为字符
mov AH,2 ;显示DL中的字符
int 21H
;程序退出
mov AH,4cH
int 21H
code ends
end start
4.3 实验内容三
;对于第2题,修改程序同时实现将10个数字的累加功能
data segment
buf DB 10 DUP(?)
max DB ? ;最大值
min DB ? ;最小值
sum DB ? ;累加和
inputMessage DB 'Input 10 1-digit number:',0DH,0AH,'$'
maxMessage DB 0DH,0AH,'The max integer is:','$'
minMessage DB 0DH,0AH,'The min integer is:','$'
sumMessage DB 0DH,0AH,'The sum is:','$'
data ends
code segment
assume cs:code,ds:data ;关联段地址寄存器与具体段
start:
;把data段地址赋值给ds
mov AX,data ;data段名表示data段的段地址,常量
mov DS,AX
;输出提示
mov DX,offset inputMessage
mov AH,9
int 21H
;输入10个一位数字,由ASCII码转换为二进制数,保存到变量BUF,同时找最大值、最小值
mov CX,10
mov si,0
mov sum,0 ;累加和初始化
next:
;输入一个字符
mov AH,1
int 21H
;由ASCII码转换为二进制数,保存到变量BUF
and AL,0fH
;累加AL到sum
add sum,AL
;AL保存数组BUF
mov BUF[si],AL
cmp CX,10 ;是否第一次循环
je firstNumber
cmp AL,min
jnl ifGreater
mov min,AL ;发现更小的值
jmp L1
ifGreater:
cmp AL,max
jng L1
mov max,AL ;发现更大的值
jmp L1
firstNumber:
mov min,AL
mov max,AL
L1:
;准备下一次循环
;输出空格
mov DL," "
mov AH,2
int 21H
inc si
loop next
;输出最大值、最小值
;输出最大值提示字符串
mov dx,offset maxMessage
mov AH,9
int 21H
mov DL,max
add DL,30H ;最大值转为字符
mov AH,2 ;显示DL中的字符
int 21H
;输出最小值提示字符串
mov dx,offset minMessage
mov AH,9
int 21H
mov DL,min
add DL,30H ;最小值转为字符
mov AH,2 ;显示DL中的字符
int 21H
;输出累加和提示字符串
mov dx,offset sumMessage
mov AH,9
int 21H
;1.求十位数字
mov AL,sum
mov AH,0
mov BL,10 ;除数
div BL ;商=AL,余数=AH
add AL,30H ;把AL中的数转为相应字符
;2.求个位数字
add AH,30H ;个位数字字符
;输出十位数字与个位数字
mov DL,AL
push AX
mov AH,2
int 21H
pop AX
mov DL,AH
mov AH,2
int 21H
;程序退出
mov AH,4cH
int 21H
code ends
end start
4.4 实验内容四
data segment
buf DB 10 DUP(?)
max DB ? ;最大值
min DB ? ;最小值
inputMessage DB 'Input 10 2-digit number:',0DH,0AH,'$'
maxMessage DB 0DH,0AH,'The max integer is:','$'
minMessage DB 0DH,0AH,'The min integer is:','$'
data ends
code segment
assume cs:code,ds:data ;关联段地址寄存器与具体段
start:
;把data段地址赋值给ds
mov AX,data ;data段名表示data段的段地址,常量
mov DS,AX
;输出提示
mov DX,offset inputMessage
mov AH,9
int 21H
;输入10个两位数字,由ASCII码转换为二进制数,保存到变量BUF,同时找最大值、最小值
mov CX,10
mov BX,0
mov SI,0
input:
;两位数的十位数的处理
;输入
mov AH,01H
int 21H
;由ASCII码转换为二进制数
sub AL,30H
mov AH,0
;将两位数的十位数字*10
mov BL,10
mul BL ;将AL与BL相乘
mov DL,AL
;两位数的个位数的处理
;输入
mov AH,01H
int 21H
;由ASCII码转换为二进制数
sub AL,30H
mov AH,0
;求得两位数数值,并保存到变量BUF
add AL,DL
mov BUF[SI],AL
;判断是否第一次循环
cmp CX,10
je firstNumber
;比较当前值与最小值
cmp AL,min
jnl ifGreater ;当前值更大实现跳转
mov min,AL ;当前值更小,更换min
jmp L1
firstNumber:
mov min,AL
mov max,AL
ifGreater:
;比较当前值与最大值
cmp AL,max
jng L1 ;当前值更小实现跳转(无需替换最大值、最小值)
mov max,AL ;当前值更大,更换max
jmp L1
L1:
;准备下一次循环
;输出空格
mov DL," "
mov AH,2
int 21H
inc SI
loop input
;输出最大值、最小值
;输出最大值提示字符串
mov dx,offset maxMessage
mov AH,9
int 21H
;1.求十位数字
mov AL,max
mov AH,0
mov BL,10 ;除数
div BL ;商=AL,余数=AH
add AL,30H ;把AL中的数转为相应字符
;2.求个位数字
add AH,30H ;个位数字字符
;输出十位数字与个位数字
mov DL,AL
push AX
mov AH,2
int 21H
pop AX
mov DL,AH
mov AH,2
int 21H
;输出最小值提示字符串
mov dx,offset minMessage
mov AH,9
int 21H
;1.求十位数字
mov AL,min
mov AH,0
mov BL,10 ;除数
div BL ;商=AL,余数=AH
add AL,30H ;把AL中的数转为相应字符
;2.求个位数字
add AH,30H ;个位数字字符
;输出十位数字与个位数字
mov DL,AL
push AX
mov AH,2
int 21H
pop AX
mov DL,AH
mov AH,2
int 21H
;程序退出
mov AH,4cH
int 21H
code ends
end start
五、结果分析
5.1 实验结果
5.1.1 内容一运行结果
5.1.2 内容二运行结果
5.1.3 内容三运行结果
5.1.4 内容四运行结果
5.2 实验心得
这次是汇编语言的第二次实验。在上一次实验中,我已经熟练掌握了汇编语言的上机步骤以及DEBUG调试方法,因此这次实验的重点在于分支循环的设计、输入输出指令以及通过寄存器进行相关的数制转换。
对于分支循环的设计,因为与高级语言中的for、while思想类似,因此在理解和操作上并不困难。
对于输入输出指令,由于理论课上还没有介绍这方面的内容,所以在实验的前一天晚上我在网上查阅相关的输入、输出等指令操作进行自学。后来,在实验课上付峰老师又进行了非常细致地讲解,让我更加熟悉与掌握这块内容。其中,可以用mov AH,1、int 21H输入一个字符;用mov DL,AH、mov AH,2、int 21H输出一个字符;用变量名 DB '内容',0DH,0AH,'$'指令进行初始化,再利用 mov DX,offset 变量名、mov AH,9、int 21H来输出字符串。
对于数制转换中断指令,只有一位数据是非常方便的,转换成16进制既可以用加上30H的方法,又可以用and AL,0fH的方法。而对于多位数据转换则有些复杂,需要将数据保存至一个寄存器中进行高八位、低八位的计算,且高八位还需要进行相应的乘10操作,充当十进制中的十位数。四个实验内容下来,我发现这其中的数制转换存在相应的套路,通过上述程序,让我对于汇编语言进行数制转换也有一定熟悉。
而在这个实验中,中断指令以及数值转换都是为了用户更好交互,比如无提示信息,用户将不知道这里需要做什么,或者说如果输出的是ASCII码或者16进制,可能让我们程序员通过结果DEBUG也有点困难。
当然我对于汇编语言的指令:传送类、数据运算类、逻辑判断类、转移、循环等指令都有了更深入的了解。同时,我了解了系统功能调用的使用方法,如:使用01H功能号输入,使用02H功能号进行字符输出显示,使用09H功能号进行字符串输出显示。