汇编实验五子程序设计.doc

上传人:文库蛋蛋多 文档编号:2396842 上传时间:2023-02-17 格式:DOC 页数:17 大小:141.50KB
返回 下载 相关 举报
汇编实验五子程序设计.doc_第1页
第1页 / 共17页
汇编实验五子程序设计.doc_第2页
第2页 / 共17页
汇编实验五子程序设计.doc_第3页
第3页 / 共17页
汇编实验五子程序设计.doc_第4页
第4页 / 共17页
汇编实验五子程序设计.doc_第5页
第5页 / 共17页
点击查看更多>>
资源描述

《汇编实验五子程序设计.doc》由会员分享,可在线阅读,更多相关《汇编实验五子程序设计.doc(17页珍藏版)》请在三一办公上搜索。

1、福建农林大学金山学院实验报告系: 专业: 计算机科学与技术 年级: 08 姓名: 学号: 实验课程: 汇编语言 实验室号:_ 实验设备号: 实验时间: 指导教师签字: 成绩: 实验五 子程序设计1实验目的和要求1、学习子程序的编写,主子程序的调用2、不同模块间程序的调用和调试2实验环境实验的硬件环境是:IBMPC机及其兼容机实验的软件环境是: 操作系统:DOS 2.0以上;调试程序:DEBUG.COM;文本编程程序:EDIT.EXE、WPS.EXE;宏汇编程序:MASM.EXE(或ASM .EXE);连接装配程序:LINK .EXE;交叉引用程序:CREF.EXE(可有可无)。3实验内容及实验

2、数据记录1、数据段中的3个字符,调用子程序将其逐个显示出来。子程序的功能是显示一个字符。单步执行,观察IP的变化DATASEGMENTMAGDB ABCDATAENDSCODESEGMENTASSUMECS: CODE, DS: DATASTART:MOVAX, DATA MOVDS, AX MOVSI, 0 MOVCX, 3LL:MOV DL, MAGSICALL MADD INCSI LOOPLLMOVAH, 4CHINT21HMADDPROCMOV AH, 02H INT 21HRETMADDENDPCODEENDS END START2、阅读S31.ASM和S32.ASM两个模块中的程

3、序,并分别汇编,然后连接成一个可执行文件S31.EXE。具体步骤如下:MASM S31.ASM(分别汇编)MASM S32.ASMLINK S31 S32 (将两个文件连接成为一个文件名为S31)S31.EXE (运行)3、编程:利用主程序调用子程序,比较BUF1和BUF2缓冲区中不相等的字符,并将不相等的字符显示出来。(也可以将找到的不相等字符逐个显示,用INT 21H的02功能调用)4、编程:子程序搜索指定字符缓冲区中是否有n,如果有用y替代。调用子程序将BUF1,BUF2,BUF3中的n全部用y替代4算法描述及实验步骤1-1)文本编辑程序保存V1.asm,编译并连接后debug调试V1.

4、exe。跟踪如下u反汇编观察CALL指令所在程序段偏移量为000F而且Call到哪显而易见是0019即Mov AH,02与源程序一致而RET指令偏移量为001D但从中我们并不能得知其具体执行过程。1-2)t单步跟踪,图为执行CALL指令前各个寄存器的状态及查看SS:IP(d0769:0000)即栈顶内容为零。BP始终都为0实际上直到目前都没用到该堆栈。1-3)继续单步执行,此时IP指向下一条指令CALL 0019。即执行该指令,结果如下可看到SP变为了FFFE可知执行该指令的同时堆栈处于忙碌的变更中。之后IP直接指向CALL 所指0019。d0769:fffe查看栈顶内容如下高向低读为0012

5、由之前的反汇编得知0012正是INCSI指令的偏移量,而该指令位于Call指令的下一条。1-4)由此我们推断RET指令的作用就是弹出栈顶内容至IP来使得程序接着运行执行CALL指令后的程序。同样T单步执行观察如下很明显执行RET后IP指向了0012。SP又指向了栈底。得以证实了CALL调用子程序是通过堆栈保护了现场进入子程序,之后再通过RET恢复到了原状态2-1)单独阅读两个程序模块似乎毫无瓜葛,只知道S32.ASM模块中是实现的是二进制数转化为十六进制数并在显示器上显示的功能。S31.ASM实现的是通过子程序TRAN的调用完成对主程序着中获取的两位非压缩BCD数的二进制转化。按题目要求分别会

6、变S31.asm和S32.asm如下 2-2)分别编辑程序存为S31.asm和S32.asm文件。进入masm目录。汇编连接如下均正常通过。2-3)运行程序S31.exe结果如下等待输入两位字符:输入两个字符1和2立即就跟出了000C,输入字符4和5跟出了002D,程序实现了将2位非压缩BCD码转化为了16进制数并打印输出到显示器。可见diso子程序虽然在另一个程序段中但在主程序中还是能被CALL唯一和在同一段内 的调用方式不同的是需要指明是段内子程序,还是段外子程序。指令中的NEAR PTR或FARPTR正是此作用。 3-1)分析题目要求容易得知,程序可分为两部分,一是控制数据缓冲区的数据获

7、取,而自然就是数据处理了亦是题目要求中的子程序实现的功能。首先定义数据缓冲区有BUF1 DB MY NAME IS ZERO! LEN1 EQU $-BUF1 BUF2 DB YOU ARE MY GIRL,RIGHT? LEN2 EQU $-BUF2这里就有一个值得考虑的问题,就是缓冲区内的数据长度不等该怎么处理。对以等长直接下两指针即可方便循环控制。为方便起见,当然取短的不失为是好的选择,在与长的字符串中比较至短字符串长度LEN1结束并输出不等字符后指针DI所指向字符余下部分直接输出。因此有如下示意图:A输出A T输出B A输出D GTBACCDDIGSIBQ 输出BQ3-2)因而如何取较

8、短的一者作为循环条件及SI和DI的初始成了问题。比较可取的方法是先将LEN1假设为最短,传送给CX,同时将BUF1送AX,BUF2送BX。然后比较CX与LEN2,分支中处理数据交换。于是有MOV CX,LEN1 MOV AX,BUF1 MOV BX,BUF2 CMP CX,LEN2 JB L SUB CX,LEN1-LEN2 XCHG AX,BX L:MOV SI,AX MOV DI,BX问题得以解决。既然这样设计,则字符显示功能就应该分离出子程序另作为又一个子程序,方便主程序和比较字符的子程序的调用。3-3)于是有代码如下DATA SEGMENT BUF1 DB MY NAME IS ZER

9、O! LEN1 EQU $-BUF1;计算BUF1长度 BUF2 DB YOU ARE MY GIRL,RIGHT? LEN2 EQU $-BUF2;计算BUF2长度 DATAENDS CODE SEGMENT ASSUMECS:CODE,DS:DATA START:MOV AX,DATA MOV DS,AX MOV CX,LEN1;假设BUF1最短送入CX MOV AX,BUF1;AX存放短字符串首偏移/ MOV AX,OFFSET BUF1MOV BX,BUF2;BX存放长字符串首偏移/ MOV BX,OFFSET BUF2;MOV DH,LEN2-LEN1 ;下一个类似语句仅在分支中忽略

10、了LEN2大于LEN1CMP CX,LEN2 JB L SUB CX,LEN1-LEN2 MOV DX,CX ;暂时存放两字符串差/ MOV DH,CLXCHG AX,BX;LEN2较短则交换数据 L:MOV SI,AX;SI定位较短者 MOV DI,BX;DI定位较长者 S:CALL CMPP;调用子程序CMPP INC SI INC DI LOOP S;比较字符未完成继续 Y:INC DI ;应该移至Call后才正确MOV CX,DX ; MOV CL,DH并移至Y之前MOV AL,DI ;MOV DL,DICALL SHOW;较长字符串余下部分输出 CMPP PROC MOV AL,SI

11、 ;MOV DL,SICMP AL,DI ;CMP DL,DIJE SKIP CALL SHOW;字符不等调用子程序输出字符 MOV AL,DI ;MOV DL,DIINT 21H SKIP: RET CMPP ENDP SHOW PROC MOV AH,2;子程序调用dos的2号功能 INT 21H RET SHOW ENDP CODE ENDS END START4-1)要求子程序首先检索字符缓冲区内的N,并用Y替代。首先是各个缓冲区定义如下:DATA SEGMENT BUF1 DB MN name is ZERO! BUF2 DB NOU ARE MN GIRL,RIGHT? BUF3

12、DB no,you are MN Friend! LEN DB $DATAENDS4-2)由于各个缓冲区大小不一,所以各缓冲区大小的获取很重要,它将是每一轮循环的控制参数CX。可将它作为子程序的入口参数,来实现。而另一入口参数自然就是各缓冲区的首字符的偏移量SI。所以子程序实现:Displace PROC S:CMP SI,N JNE NEXT MOV SI,Y NEXT:INC SI LOOP S RET Displace NEDP4-3)主程序实现入口参数的初始化过程,所以有BUF1的实现,其它如是:MOV CX,BUF2-BUF1 ;一直犯同样的错误MOV SI,BUF1 ;CX初始化字

13、符串长度,SI初始为字符串入口CALL Displace ;调用Displace子程序4-4)所以有最初代码如下为便于观察,处理后打印输出各字符串DATA SEGMENT BUF1 DB MY NAME IS ZERO! BUF2 DB NOU ARE MN GIRL,RIGHT? BUF3 DB NO,YOU ARE MY FRIEND! LEN DB $ DATAENDS CODE SEGMENT ASSUMECS:CODE,DS:DATA START: MOV AX,DATA MOV DS,AX MOV CX,BUF2-BUF1 MOV SI,BUF1 CALL Displace MOV

14、 CX,BUF3-BUF2 MOV SI,BUF2 CALL Displace MOV CX,LEN-BUF3 MOV SI,BUF2 CALL Displace MOV AH,4CH INT 21H Displace PROC S:CMP SI,N JNE NEXT MOV SI,Y NEXT:MOV DL,SICALL Show INC SI LOOP S RET Displace NEDPShow PROC MOV AH,2 INT 21H RET Show EDNP5调试过程3-1)编辑最初代码编译出错更正错误MOV AX,BUF1为 MOV AX,OFFSET BUF1 MOV BX

15、,BUF2为 MOV BX,OFFSET BUF2及录入错字,正常通过编译。运行程序出错如下:显然忘了退出到dos界面是一个原因,但不是主要原因。检查程序发现对于较长字符串的余下部分的处理忘了循环。修正后出现如下错误:修改MOV 4CH 为MOV AH,4CH重新编译。运行结果仍旧如上不停打印字符,显然问题很大。3-2)还是单步跟踪来找错,debug V3.exe进入debug环境调试V3.exe。如图发现虽然调用了dos的2号输出字符功能,但把本该传送给DL的字符错传给了AL,更正之。并替换之前的DX的使用。终于输出如下结果,为了便已对比,为程序添加换行输出功能。4-1)编辑程序代码,编译出

16、错,安提示定位到错误处修改之。没引起注意,还是同样的错误, MOV CX,BUF2-BUF1 、MOV SI,BUF1 修改为 MOV CX,OFFSET BUF2-BUF1 、MOV SI,OFFSET BUF1 为了引以为戒,这以后干脆直接用LEA指令,以避免事后犯同样的错误。而Constant expected不知到是否和程序指令冲突了定义还是怎么,先修改之为LENTH。错误23和25行均容易更正。指定内存大小BYTE PTR即可。4-2)之后才发现程序段未下结束标记修改之,重新编译如下看来其它错误并非由以上引起。同时LENTH还是不行。所以猜想是后边的DB问题,恍然大悟,这里无须开辟空

17、间,直接用伪指令EQU即可。修改后:LEN EQU $。重新编译。至于后面的问题是由于修改之前的初始化过程LEA又犯了其它错误,LEA 指令将地址载入目的操作数。可是操作数明明可以是表达式的,为什么提示当前模式非法呢。4-3)暂时用原始方法修改回来:MOV CX,OFFSET BUF2-BUF1,其它同。可又出现了如下错误:17行中 MOV CX,LEN-BUF1提示操作数不在同一段内或是常量,对比之前(上一题)的使用,我们忽略了这样一个问题,由于EQU并没在内存开辟新空间,为此将LEN视为常量,之前我们都是常量表达式出现,所以未出错。这里BUF1是地址即内存操作数,所以猜想问题可能处在此处。

18、修改EQU 指令为LENEQU $-BUF3,同时修改指令MOV CX,LEN-BUF3为MOV CX,LEN,问题应该可以解决。31和36是子程序的标识,想是违反了命名规则。修改为大写一试,错误并非由此引起。仔细观察子程序的定义,原来是ENDP错写成EDNP。修改之正常通过编译并连接运行。4-4)经以上修改,最终完整程序代码如下DATA SEGMENT BUF1 DB MN name is ZERO! BUF2 DB NOU ARE MN GIRL,RIGHT? BUF3 DB no,you are MN Friend! LEN EQU $-BUF3 DATAENDS CODE SEGMEN

19、T ASSUMECS:CODE,DS:DATA START: MOV AX,DATA MOV DS,AX MOV CX,OFFSET BUF2-BUF1 LEA SI,BUF1 ;CX初始化BUF1长度,SI初始为BUF1入口 CALL RE ;调用子程序进行字符检索和替换 MOV CX,OFFSET BUF3-BUF2 LEA SI,BUF2 ;CX初始化BUF2长度,SI初始为BUF2入口 CALL RE ;调用子程序进行字符检索和替换 MOV CX,LEN LEA SI,BUF3 ;CX初始化BUF3长度,SI初始为BUF3入口 CALL RE;调用子程序进行字符检索和替换 MOV AH

20、,4CH INT 21H ;退出到DOS界面 RE PROC S:CMP BYTE PTR SI,N ;检索字符N JNE NEXT MOV BYTE PTR SI,Y ;找到N则替换为Y NEXT:MOV DL,SI ;将每一个遍历字符传送给DL准备显示 CALL SHOW;显示字符的子程序 INC SI LOOP S RET RE ENDP ;字符处理操作子程序 SHOW PROC MOV AH,2 INT 21H RET SHOW ENDP ;子程序调用DOS的2号显示功能 CODE ENDS ENDSTART 6. 实验结果3)对比字符串得知结果比较部分正确,但”RIGHT?“部分只有

21、输出I。仔细检查代码发现在给CL赋值前CH并未初始化为0。在Y:前添加MOV CH,0;修改之,但不是问题所在,忽略了一个很大的问题就是由于CX是在分支里保存两串差值,导致了在LEN1小于LEN2时并没给CX第二次初始化为差值,所以有了该问题。因此在MOV BX,OFFSET BUF2后添加指令MOV DH,LEN2-LEN1,将MOVCL,DH提前出Y之上。修改后最终还露了R,原因就是进入余下字符串前DI多加了1,将INC DI移动至CALL之后,重新编译运行的结果正确如下:4)运行程序输出正确结果,至此该程序实现了所设计的功能。7. 总结在前几次实验的基础与学习中不断的吸取教训,此次实验完

22、成的很顺利,设计的逻辑很少出现问题,唯一应该引起注意的是指令的语法规则在这次实验中是阻碍实验前行的一大障碍,粗心大意的编辑程序没有保持分析设计需求时的严谨态度,给自己添了原本不该出现的不少麻烦。总之几次实验下来,受益匪浅,不仅是巩固了理论知识,还很大程度上提升了我分析并解决问题的能力,更重要的是每一次实验中的羁绊让我对生活和学习的态度的着重要性有了新的认识。这将会是我人生中的一大财富。附录:附源程序:NAME S31.ASM不同模块间的段间调用。从键盘输入2位非压缩BCD数,存入AX寄存器中。为了将其转换为二进制数,编写一个子程序TRAN。显示子程序DISP完成将16进制数转换为对应的ASCI

23、I码并显示该字符,显示子程序在另一模块S32中。输入数据为00时结束。EXTRNDISP: FARCODESEGMENTPARA CODEASSUMECS: CODESTART:MOVAH, 01H INT21H MOVBL, AL INT21H MOVAH, AL MOVAL, BLCMPAX, 3030H JEEXIT CALLNEAR PTR TRAN CALLFAR PTR disp JMPSTARTEXIT:MOV AH, 4CH INT21HTRANPROCNEAR ;将输入的ASCII码转换成2进制数ANDAX, 0F0FH ;将ASCII码转换成非压缩BCD码,;高位在AL中

24、MOVBL, AH MOVCL, 10D MULCL ADDAL, BLRETTRANENDPCODEENDSEND STARTNAMES32.ASMPUBLICDISPCODE1SEGMENTPARA CODEASSUMECS: CODE1DISP PROC FAR MOVBL, AL MOVBH, 00 MOVCH, 4ROLL:MOVCL, 4 ROLBX, CL MOVDL, BL ANDDL, 0FH CMPDL, 9 JBENEXT1 ADDDL, 07HNEXT1:ADDDL, 30H MOVAH, 02H INT21H DECCH JNZROLLRETDISPENDPCODE1 ENDSEND

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 建筑/施工/环境 > 项目建议


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号