汇编N阶乘.docx
《汇编N阶乘.docx》由会员分享,可在线阅读,更多相关《汇编N阶乘.docx(12页珍藏版)》请在冰豆网上搜索。
![汇编N阶乘.docx](https://file1.bdocx.com/fileroot1/2023-2/3/4047cc5e-3058-46ba-b965-e0111d540d7b/4047cc5e-3058-46ba-b965-e0111d540d7b1.gif)
汇编N阶乘
一、实验目的
1.掌握子程序的设计方法;
2.掌握递归子程序的设计思想;
3.体会堆栈操作在子程序嵌套中的重要作用。
二、实验原理及基本技术路线图(方框原理图)
一个子程序作为调用程序去调用另一子程序,这种关系称为子程序嵌套。
由于子程序嵌套对堆栈的使用很频繁,因此还要确保堆栈有足够空间,并要注意堆栈的正确状态,这包括CALL、RET、RETN、PUSH、POP、INT、IRET等与堆栈操作有关指令的正确使用。
在子程序嵌套的情况下,如果一个子程序调用的子程序就是它自身,这样的子程序称为递归子程序。
显然递归调用是子程序嵌套的一种特殊情况。
使用递归算法往往能设计出效率较高的程序。
设计递归子程序时,必须保证每一次后继调用都不能破坏它上一次调用时所生成的参数和中间结果,并且该过程不会修改它本身。
这就意味着当使用汇编语言设计递归子程序时,必须考虑每一次调用都应该把它的参数、寄存器和所有的中间结果保存到不同的存储区域。
最好的办法是利用堆栈来存储这些信息,一次调用所保存的信息称为一帧。
递归调用要使用大量的堆栈空间,一定要保证堆栈足够大,而且也要保证堆栈的正确使用,避免死机等不可预料的情况发生。
求N!
算法流程图:
三、所用仪器、材料(设备名称、型号、规格等)
1.操作系统平台:
WindowsServer2003
2.汇编环境:
MicrosoftMASM5.0
3.文本编辑器:
记事本
四、实验方法、步骤
1.将MASM5.0的文件置于C:
\assembly\目录下;
2.将masm.exe和link.exe所在目录(C:
\assembly\MASM5)添加到Path环境变量中;
3.在C:
\assembly\下新建一个JC.asm文件,打开JC.asm,输入汇编程序代码;
4.运行一个命令行窗口,将当前目录切换到C:
\assembly\,然后输入命令:
masmJC.asm[Enter],来汇编程序,根据汇编结果查看程序代码是否有语法错误,如有,则更正保存后重新汇编,直至没有错误为止,此时会生成JC.obj文件;
5.输入命令:
linkJC.obj[Enter],观察输出结果,如果没有错误,则生成JC.exe;
6.输入命令:
debugJC.exe[Enter],调试程序,并记录运行过程;
7.完成实验报告。
五、实验过程原始记录(数据、图表、计算等)
1.将C:
\assembly\MASM5目录添加到Path环境变量中:
2.新建名为JC.asm的文件,输入汇编程序代码:
程序源码:
STACKSGSEGMENTSTACK'S';定义堆栈
DW128DUP('ST')
STACKSGENDS
DATASEGMENT
N_VALDW3;定义N值
RESULTDW?
;结果
DATAENDS
CODESEGMENT
ASSUMECS:
CODE,DS:
DATA,SS:
STACKSG
FRAMESTRUC;定义帧结构
SAV_BPDW?
;保存BP值
SAV_CS_IPDW2DUP(?
);保存返回地址
NDW?
;当前N值
RESULT_ADDRDW?
;结果地址
FRAMEENDS
MAINPROCFAR
MOVAX,DATA
MOVDS,AX
LEABX,RESULT
PUSHBX;结果地址入栈
PUSHN_VAL;N值入栈
CALLFARPTRFACT;调用递归子程序
R1:
MOVAX,4C00H
INT21H
MAINENDP
FACTPROCFAR;N!
递归子程序
PUSHBP;保存BP值
MOVBP,SP;BP指向帧基地址
PUSHBX
PUSHAX
MOVBX,[BP].RESULT_ADDR
MOVAX,[BP].N;取帧中N值
CMPAX,0
JEDONE;N=0时退出子程序嵌套
PUSHBX;为下一次调用压入结果地址
DECAX
PUSHAX;为下一次调用压入(N-1)值
CALLFARPTRFACT
R2:
MOVBX,[BP].RESULT_ADDR
MOVAX,[BX];取中间结果(N-1)!
MUL[BP].N;N*(N-1)!
JMPSHORTRETURN
DONE:
MOVAX,1;0!
=1
RETURN:
MOV[BX],AX;存中间结果
POPAX
POPBX
POPBP
RET4
FACTENDP
CODEENDS
ENDMAIN
3.汇编源程序:
4.连接生成可执行文件:
5.调试程序:
(1)结果地址入栈
(2)N值入栈,并调用递归子程序
(3)保存BP值,BP指向帧基地址,准备取N值
(4)取到N值后判断是否等于零,显然3不等于零,准备为下一次调用压入结果地址
(5)为下一次调用压入结果地址和N-1的值,然后准备进行下一次调用
(6)第二次调用判断N值是否等于零,显然2不等于零
(7)第三次调用判断N值是否等于零,显然1不等于零
(8)第四次调用判断N值是否等于零,显然等于零,准备返回
(9)第四次调用返回0!
=1,存中间结果,返回
(10)第三次调用,取堆栈中的中间结果N-1,准备时行N*(N-1)的操作
(11)计算N*(N-1),开始第三次调用的返回
(12)第二次调用,取堆栈中的中间结果N-1,准备时行N*(N-1)的操作
(13)计算N*(N-1),开始第二次调用的返回
(14)当前中间结果的值
(15)第一次调用,取堆栈中的中间结果N-1,准备时行N*(N-1)的操作
(16)计算N*(N-1),开始第一次调用的返回
(17)第一次调用返回后,主程序执行完毕,RESULT中存放N的阶乘,程序结束并退出
六、实验结果、分析和结论(误差分析与数据处理、成果总结等。
其中,绘制曲线图时必须用计算纸)
1.子程序设计可以使用多种方法传递参数,但用堆栈传递参数时要特别注意所有与堆栈相关的操作。
2.递归子程序的堆栈调用比较复杂,需要对我们对每一次调用时堆栈的变化都非常清楚,这样不但思路清晰,而且在子程序返回过程中,返回值和堆栈指针的变化也能帮助我们理解程序运行的细节,以便检查错误。
3.分析递归子程序最好最简便的方法是将程序调用的相互关系和堆栈变化用图画出来,这样一步一步的按程序逻辑进行绘制,可以很好的检查程序逻辑设计是否有问题,是一种查错的好办法。