实验五 循环结构及子程序
一、实验学时
课堂2学时,课后2学时
二、实验目的
通过实验掌握下列知识:
1) 加深对循环结构的理解。
2)掌握循环程序的设计方法。
3)学习子程序的定义和调用方法。
4)掌握子程序、子程序的嵌套、递归子程序的结构。
5) 掌握子程序设计、编制及调试。
6) 熟练掌握DEBUG的常用命令,学会用DEBUG调试程序。
三、实验要求
1) 做好实验预习,完成各种软件安装与操作。
2) 按照实验内容与步骤的要求,独立思考,认真仔细地完成实验。
3) 截取各步骤截图,写出实验报告。
四、实验内容
1)编制在屏幕上显示九九乘法表的程序。
2)编制在屏幕上显示用*组成的三角形的程序。
题目1参考代码:
DATA SEGMENT
S2 DB ?
S1 DB ?
X DB 1
Y DB 1
S DB 0AH
FCH=2AH
FDH=3DH
FKG=20H
FHH=0AH
FHC=0DH
DATA ENDS
STACK1 SEGMENT STACK
DB 20H DUP(0)
STACK1 ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK1
START:MOV AX,DATA
MOV DS,AX
MOV CL,9
LL1:
PUSH CX ;将CL压入堆栈段
MOV CL,Y
LL2:
CALL L1 ;调用子程序L1
INC X
LOOP LL2
MOV X,1
INC Y
MOV DL,FHH ;输出换行和回车
MOV AH,2
INT 21H
MOV DL,FHC
MOV AH,2
INT 21H
POP CX ;从堆栈段中取出CL
LOOP LL1
MOV AH,4CH ;程序结束
INT 21H
L1 PROC ;计算X*Y=Z并且输出X*Y=Z的子程序
MOV DL,X
ADD DL,30H
MOV AH,2
INT 21H
MOV DL,FCH
MOV AH,2
INT 21H
MOV DL,Y
ADD DL,30H
MOV AH,2
INT 21H
MOV DL,FDH
MOV AH,2
INT 21H
MOV AL,X
MUL Y
DIV S
MOV S1,AH
ADD AL,30H
MOV DL,AL
MOV AH,2
INT 21H
ADD S1,30H
MOV DL,S1
MOV AH,2
INT 21H
MOV DL,FKG ;输出空格
MOV AH,2
INT 21H
RET
L1 ENDP ;子程序结束
CODE ENDS
END START
题目2参考代码:
DATA SEGMENT
N DB ?
FCH=2AH
FDH=3DH
FKG=20H
FHH=0AH
FHC=0DH
DATA ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATA
START:
MOV AX,DATA
MOV DS,AX
MOV AH,1 ;输入一位16进制数确定输出行数 INT 21H CMP AL,39H JBE L1 SUB AL,07H
L1:SUB AL,30H MOV N,AL
XOR BX,BX
MOV DL,FCH
MOV CL,N
MOV DL,FHH ;输出换行和回车
MOV AH,2
INT 21H
MOV DL,FHC
MOV AH,2
INT 21H
LOP:PUSH CX
INC BL
MOV CL,BL
MOV DL,FCH
LOP2:MOV AH,02H
INT 21H
LOOP LOP2
POP CX
MOV DL,0AH
MOV AH,02H
INT 21H
MOV DL,FHC
MOV AH,02H
INT 21H
CMP BL,9
JZ L2
LOOP LOP
L2:MOV AH,4CH ;程序结束
INT 21H
CODES ENDS
END START
五、实验步骤
5.1 九九乘法表
编写代码:
DATA SEGMENT
S2 DB ? ; 用于存储结果的变量,初始化为未知(?)
S1 DB ? ; 用于存储结果的变量,初始化为未知(?)
X DB 1 ; 乘法运算的第一个因子
Y DB 1 ; 乘法运算的第二个因子
S DB 0AH ; 除法运算的除数
FCH=2AH ; ASCII码值,用于输出字符“,”
FDH=3DH ; ASCII码值,用于输出字符“=”
FKG=20H ; ASCII码值,用于输出空格
FHH=0AH ; ASCII码值,用于输出换行
FHC=0DH ; ASCII码值,用于输出回车
DATA ENDS
STACK1 SEGMENT STACK
DB 20H DUP(0) ; 定义一个大小为20H(32)的堆栈段
STACK1 ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK1
START:
MOV AX,DATA ; 将数据段的地址加载到寄存器AX中
MOV DS,AX ; 将数据段的地址存储到DS寄存器中
MOV CL,9 ; 初始化循环计数器为9
LL1:
PUSH CX ; 将CL压入堆栈段,保存循环计数器的值
MOV CL,Y ; 将Y的值存储到循环计数器中
LL2:
CALL L1 ; 调用子程序L1
INC X ; X加1
LOOP LL2 ; 循环LL2,直到循环计数器变为0
MOV X,1 ; 将X重新设为1
INC Y ; Y加1
MOV DL,FHH ; 输出换行
MOV AH,2
INT 21H
MOV DL,FHC ; 输出回车
MOV AH,2
INT 21H
POP CX ; 从堆栈段中取出之前保存的循环计数器的值
LOOP LL1 ; 循环LL1,直到循环计数器变为0
MOV AH,4CH ; 程序结束
INT 21H
L1 PROC ; 计算X*Y=Z并输出结果的子程序
MOV DL,X ; 将X的值存储到DL寄存器中
ADD DL,30H ; 将DL转换为ASCII码值
MOV AH,2 ; 设置打印字符的功能号为2
INT 21H ; 输出DL中的字符
MOV DL,FCH ; 输出字符“,”
MOV AH,2
INT 21H
MOV DL,Y ; 将Y的值存储到DL寄存器中
ADD DL,30H ; 将DL转换为ASCII码值
MOV AH,2 ; 设置打印字符的功能号为2
INT 21H ; 输出DL中的字符
MOV DL,FDH ; 输出字符“=”
MOV AH,2
INT 21H
MOV AL,X ; 将X的值存储到AL寄存器中
MUL Y ; AL = AL * Y,结果存储在AX中
DIV S ; AX = AX / S,商存储在AL中
MOV S1,AH ; 将商存储到S1中
ADD AL,30H ; 将AL转换为ASCII码值
MOV DL,AL ; 将结果存储在DL寄存器中
MOV AH,2 ; 设置打印字符的功能号为2
INT 21H ; 输出DL中的字符
ADD S1,30H ; 将S1转换为ASCII码值
MOV DL,S1 ; 将结果存储在DL寄存器中
MOV AH,2 ; 设置打印字符的功能号为2
INT 21H ; 输出DL中的字符
MOV DL,FKG ; 输出空格
MOV AH,2
INT 21H
RET
L1 ENDP ; 子程序结束
CODE ENDS
END START
对以上代码进行分析:
代码中定义了一些变量,如X和Y,它们是乘法运算的两个因子,还有S,它是除法运算的除数。此外,还定义了一些常量,用于控制输出的格式。
代码中的循环部分使用了两个嵌套的循环。外层循环从1到9,内层循环从1到Y的值。每次循环,代码会调用一个名为L1的子程序,计算X和Y的乘积,并将结果输出。
L1子程序首先将X和Y的值转换为ASCII码,并输出它们。然后,子程序执行乘法运算,并将结果除以S。最后,子程序将计算得到的商和乘法结果输出。
在循环结束后,代码会输出换行和回车符,用于分隔每次乘法结果的输出。
用文字编辑工具将源程序输入,其扩展名为Cheng.ASM。将源文件放到Dosbox挂载的目录下面,如图5-1所示。
用MASM对源文件进行汇编,生成cheng.obj,如图5-2所示,输入指令masm cheng.asm。
OBJ文件是目标文件的一种形式,它包含了汇编代码转换后的机器代码和符号表等信息。OBJ文件是链接器的输入文件之一,链接器将多个OBJ文件和库文件链接在一起生成可执行文件。
接着用LINK将.OBJ文件连接成可执行的cheng.EXE文件,如图5-3所示,输入指令link cheng.obj。
最后使用cheng.exe进行运行,如图5-4所示,九九乘法表显示正确。
5.2 杨辉三角
编写代码:
DATA SEGMENT
N DB ?
FCH=2AH
FDH=3DH
FKG=20H
FHH=0AH
FHC=0DH
DATA ENDS
STACK1 SEGMENT STACK
DB 100H DUP(0) ; 堆栈段的定义,这里分配了100H(256字节)的空间
STACK1 ENDS
CODES SEGMENT
ASSUME CS:CODES, DS:DATA, SS:STACK1 ; 关联代码段、数据段和堆栈段
START:
MOV AX,DATA ; 将数据段的地址加载到寄存器AX中
MOV DS,AX ; 将数据段的地址存储到DS寄存器中,设置数据段寄存器
MOV AH,1 ; 读取输入的行数
INT 21H ; 读取输入的值存储在AL寄存器中
CMP AL,39H ; 检查输入的值是否小于等于'9'
JBE L1 ; 如果是,则跳转到标签L1处理输入
SUB AL,07H ; 如果不是,则减去7,将输入的十六进制数转换为十进制
L1:
SUB AL,30H ; 将输入的ASCII码值转换为十进制数
MOV N,AL ; 将输入的行数存储到N中
XOR BX,BX ; 使用BX作为计数器,初始化为0
MOV DL,FCH ; 输出换行和回车符号
MOV AH,2
INT 21H
MOV DL,FHC
MOV AH,2
INT 21H
LOP:
PUSH CX ; 保存CX寄存器的值,用于内层循环计数器
INC BL ; BL作为内层循环计数器,每次循环递增
MOV CL,BL ; 将内层循环计数器的值存储到CL中,用于内层循环
MOV DL,FCH ; 输出字符“,”
LOP2:
MOV AH,02H ; 设置打印字符的功能号为2
INT 21H ; 输出DL中的字符
LOOP LOP2 ; 循环内层循环LOP2,直到CX变为0
POP CX ; 从堆栈中取出之前保存的CX寄存器的值,恢复计数器的值
MOV DL,0AH ; 输出换行符号
MOV AH,02H
INT 21H
MOV DL,FHC ; 输出回车符号
MOV AH,02H
INT 21H
CMP BL,9 ; 检查内层循环计数器的值是否为9
JZ L2 ; 如果是,则跳转到标签L2结束程序
LOOP LOP ; 循环外层循环LOP,直到CX变为0
L2:
MOV AH,4CH ; 程序结束
INT 21H
CODES ENDS
END START
对代码进行分析:
首先,定义了一些数据段和堆栈段。数据段用于存储程序中需要使用的数据,而堆栈段用于存储程序执行时的临时数据。
然后,进入代码段,开始执行程序。首先,读取用户输入的行数,并将其存储在变量N中。然后,使用计数器BX和循环来逐行输出杨辉三角。
内层循环(LOP2)用于输出每一行中的数字和分隔符号。使用内层循环计数器BL来确定每行输出的数字个数,并通过MOV CL, BL将其存储在CL寄存器中。然后,使用INT 21H指令将数字和分隔符号输出到屏幕上。
外层循环(LOP)用于控制输出的行数。使用外层循环计数器CX来确定当前行数,并通过比较CX和N的值来决定是否继续循环。
最后,通过输出换行符和回车符,以及调用INT 21H指令的方式来结束程序。
用文字编辑工具将源程序输入,其扩展名为yang.ASM。将源文件放到Dosbox挂载的目录下面,如图5-5所示。
用MASM对源文件进行汇编,生成yang.obj,如图5-6所示,输入指令masm yang.asm。
接着用LINK将.OBJ文件连接成可执行的yang.exe文件,如图5-7所示,输入指令link yang.obj。
最后使用yang.exe进行运行,如图5-8所示,首先输入一个数字,接着会显示杨辉三角。
六、实验小结
6.1 问题和解决办法
1.问题1:在输入MASM yang.asm之后,并没有生成对应的LST和CRF文件。
解决方法:没有输入LST和CRF文件的名字和后缀,不会生成。
2.问题2:输入masm chengfabiao.asm之后,显示Unable to open input file:chengfabiao.asm。
解决方法:dos系统不支持8位以上的文件名,将文件名改为8位或8位以下。
3.问题3:使用link yang.obj的时候,显示"link:warning l4021 : no stack segment"。
解决方法: “link:warning l4021 : no stack segment”
表明链接器(linker)在连接过程中发现缺少堆栈段(stack segment)。要解决这个问题,可以添加一个堆栈段的定义,并确保在代码中使用堆栈时正确地进行操作。
4.问题4:运行杨辉三角,发现第一行总是有两个*,后面几行正常。
解决方法:在内层循环LOP2之前,没有初始化BL寄存器的值。在LOP2之前添加"MOV BL, 0 ; 初始化BL寄存器为0"来初始化BL寄存器。
6.2 心得体会
在本次实验中,我通过编写程序来实现两个具体的功能:显示九九乘法表和显示用星号组成的三角形。这个实验帮助我加深了对循环结构的理解,掌握了循环程序的设计方法,并学习了子程序的定义和调用方法。
编写九九乘法表程序时,我使用了嵌套的循环结构。外层循环控制被乘数,内层循环控制乘数。通过循环的迭代,我成功地打印出了完整的九九乘法表。这个实验让我更加熟悉了循环结构的应用,加深了我对循环的理解。
编写用星号组成的三角形程序时,我运用了循环结构和条件判断。通过嵌套循环和适当的条件判断,我成功地打印出了由星号组成的三角形图案。这个实验让我掌握了如何使用循环和条件判断来构建特定的图形模式。
我还学会了如何使用子程序。通过定义和调用子程序,将程序的不同功能模块化,提高了代码的可读性和可维护性。同时,我也学会了如何在子程序中嵌套其他子程序,以及如何使用递归子程序来解决一些特定的问题。
通过完成这个实验,我不仅掌握了循环结构、子程序的设计和调用方法,还加深了对DEBUG工具的理解和应用。这些知识和技能对我的编程能力有着很大的提升,并为我以后学习和应用更复杂的程序打下了坚实的基础。