1、- 1 -汇编语言实训任务书题目: 计算器 学生姓名学号: 杜政毅 12730102,惠攀龙 12730108,郭益婷 13730104 班级: 13 软件工程 1 班 题目类型:软件工程(R ) 指导教师: 刘树群 一、设计目的用汇编语言编程设计一个简单的计算器,实现四则运算数的交互式输入,并显示运算结果。通过设计,掌握键盘和鼠标控制、显示输出和基本算术运算指令的应用,进一步提高综合运用知识的能力。 二、设计内容(包括原始数据、技术参数、条件、设计要求等): 使用按键式操作,对任意给定的正确四则运算序列,能按照意图分部计算出其计算的有效结果数值并输出。三、设计要求题目的选择是根据汇编语言的特
2、点主要针对系统内部的一些中断调用、系统功能调用、系统数据处理、文件数据处理、以及相关的 API 而设计的,是对所学课程知识较为系统的应用。 由于汇编语言编程相对其他语言编程比较复杂,因此,在设计上应把重点放在程序的功能实现上,不要太在意程序的结构和显示界面的设计。不要局限于题目所规定的要求,可以灵活裁减,设计出功能更完善程序。 另外,也可以自己设计题目,同时,提出详细的功能描述,经指导老师审查同意后也可作为课程设计的题目。 四、提交的成果- 2 -1. 设计说明书一份,内容包括:包含任务书一份1) 中文摘要 100 字,关键词 3-5 个。2) 实训题目、内容、要求。3)总体设计(包含几大功能
3、模块)。4)详细设计(各功能模块的具体实现算法流程图)5)数据结构设计描述,各模块(函数)的功能介绍,参数说明等。6)调试分析(包含各模块的测试用例,及测试结果。)7)总结(实训完成了哪些功能,还有哪些地方需要改进,及通过实训有何收获,调试程序的体会等)8) 参考文献、致谢等。五、主要参考资料1李敬兆等编.汇编语言程序设计.合肥:中国科学技术大学出版社,2008.9 2李敬兆等编.汇编语言程序设计实验教程及习题解答.合肥:中国科学技术大学出版社,2008.9 3王爽等编. 汇编语言.北京:清华大学出版社,2003.5 4曹加恒等编.新一代汇编语言课程设计.北京:高等教育出版社,2003.6 5
4、杨路明等编.汇编语言程序设计.长沙:中南大学出版社,2005.8 6詹仕华等编.汇编语言程序设计.北京:中国电力出版社,2008.2 7詹仕华等编.汇编语言程序设计习题解答及课程设计、设计辅导.北京:中国电力出版社,2008.2 8宋彩莉等编.汇编语言程序设计学习指导.西安:西安交通大学出版社,2008.2 六、各阶段时间安排(共 1 周)日期 内容 地点星期一 教师讲解设计要求,准备参考资料 实验室星期二 分析系统,方案设计 实验室- 3 -星期三 方案设计,编程 实验室星期四 调试程序,书写实训说明书 实验室星期五 完成实训说明书,答辩 实验室- 4 -摘要本次课程设计的题目是用汇编语言实
5、现一个简单的计算器,要求:编写一个程序,每运行一次可执行程序,可 以实现加减乘除四则运算。计算器是最简单的计算工具,简单计算器具有加、减 乘、除四项运算功能。想要用汇编语言实现简单的计算器,就必须通过对数据存储,寄存器的使用 加减乘除相关指令以及模块的调用等汇编语言知识进行运用,以实现一个基本功能完善,界面友好,操作简便易行的计算器。用汇编语言实现简单计算器还涉及到输入输出模块的设计,加减乘除运算的判断以及退出程序的判断的设计。通过对各种指令的合理使用,设计各个功能模块。当实现各个程序模块后,通过程序的调用最终实现一个简单的计算器。关键字:汇编语言,仿 Windows 计算器,四则运算,调用,
6、功能模块- 5 -目录一、程序设计目的及要求 .- 3 -1.1 设计目的 - 3 -1.2 设计内容 - 3 -1.3 设计要求 - 3 -二、系统总体分析 .- 4 -三、详细设计 .- 5 -3.1 主程序及窗口主程序结构 - 5 -3.2 各功能模块的具体实现 - 5 -3.3 源文件(Calculator.asm)中各个模块分析 - 8 -四、调试过程及分析 .- 17 -4.1 窗口检测 - 17 -4.2 四则运算、开方以及倒数运算测试 - 17 -五、说明书 .- 19 -六、心得体会 .- 20 -七、参考文献 .- 21 -八、致谢 .- 22 -附录(源程序) .- 23
7、 - 6 -一、程序设计目的及要求1.1 设计目的本次程序的设计题目是使用汇编实现设计一个功能及界面风格类似于Windows 计算器的计算器程序,要求实现标准型计算器,其中包含基本的四则运算、倒数运算、开方运算。支持存储区的存储、清除、调出、累加等功能。计算器是最简单的计算工具,简单计算器具有加、减、乘、除四项运算功能。通过使用汇编语言设计实现简单计算器,以此进一步了解和掌握对数据存储,寄存器的使用,加减乘除相关指令以及模块的调用等汇编语言知识的有效运用。本次课程设计以实现一个基本功能完善,界面友好,操作简便易行的计算器为最终目的。通过对具有加减乘除基本功能的计算器的设计实现,学会使用汇编语言
8、实现输入输出模块的设计,模块合理调用的设计,加减乘除运算的判断以及退出程序的判断的设计。通过对各种指令的合理使用,熟悉并加深对各种指令的用法。学会使用汇编语言设计各个功能模块。当实现各个程序模块后,学会通过程序的调用最终实现一个具有基本计算功能的简单计算器。1.2 设计内容使用按键式操作,对任意给定的正确四则运算序列,能按照意图分部计算出其计算的有效结果数值并输出。1.3 设计要求a. 遵循模块化、结构化的编程思路,用汇编实现简单的算法。 b.程序必须正确运行 c.程序简明易懂,多标明注释,具有良好的程序书写风格d.适当优化程序,提高程序的运行效率- 7 -二、系统总体分析本次汇编语言课程设计
9、的最终目的是要实现一个简单计算器,要求编写一个程序,每运行一次可执行程序,可以实现数的加减乘除四则运算。比如,十进制数的加减乘除四则运算。我们曾经学习过两个具体数字进行加减法运算,但是对于简单计算器用汇编语言实现难点在于这两个要做运算的数是未知的,是由自己调用中断输入到屏幕上并要用程序存储起来的数,然后才能对这两个数进行运算,而且做的是加法运算、减法运算乘法运算还是除法运算也未可知,为此我们还要判断用户所输入的运算是四则运算中的哪一个运算。此外,运算过程中的进位或是借位,选择用什么样的方式进行输出,如何实现清屏等也是要解决的问题。设计程序进行判断所输入的算式是做加法运算、减法运算、乘法运算还是
10、除法运算,即判断输入的运算符是+号、-号、*号、/号中的哪一个,因为输入的格式固定了,所以只需要把存进数据区的第三个字符拿来与加减乘除四个运算符号进行比较,和哪一个运算符号一样就调用相对应的运算模块进行计算。根据计算器程序所要实现的功能完成各个功能模块。设计程序主体部分,根据主体部分所需的功能模块对各个子模块一一进行设计编写。子模块中包含有:do_add 加法运算模块,do_sub 减法运算模块,do_mul 乘法运算模块,do_div 除法运算模块,input 输入模块等。 最后还要对这个简易计算器进行功能完善。为了使程序符合我们使用者的操作习惯,设计当用户根据提示信息输入一个算式后,按下
11、enter 键或是=符号键时,程序依据输入的算式进行计算,并将结果显示在屏幕上。如果用户输入错误,则返回,提示信息让用户重新输入算式,当用户按下 Q 或 q 键时退出程序。在各个子功能模块设计好的情况下,通过主题模块的合理调用,最终实现一个具有简单运算功能的计算器。- 8 -三、详细设计3.1 主程序及窗口主程序结构按照系统总体分析,主程序用于获得并保存本程序的句柄,并调用窗口主程序 WinMain 创建窗口并进入消息循环。WinMain 程序将获取的消息分发给消息处理程序 Calculate 进行处理。消息处理程序 Calculate 用于相应窗口创立、销毁、按键等消息并进行处理。主程序和窗
12、口主程序结构及消息处理程序Calculate 结构图如 3-1 所示:图 3-1 主程序和窗口主程序及消息处理程序结构图主程序流程图及原代码如下:invoke GetModuleHandle,NULL ;获得并保存本程序的句柄mov hInstance,eaxinvoke WinMain,hInstance,0,0,SW_SHOWDEFAULTinvoke ExitProcess,eax ;退出程序,返回 eax 值- 9 -3.2 各功能模块的具体实现(1) 将 Calculator.inc 头文件中统一定义程序所需的头文件及引入库。源程序如下:;- 头文件声明include windows
13、.incinclude user32.incinclude kernel32.incinclude comctl32.incinclude masm32.incinclude shell32.inc;- 引入库声明includelib user32.libincludelib comctl32.libincludelib masm32.lib(2) 在 Calculator.inc 中定义程序所需常量如下:ID_NUM0 equ 300ID_NUM1 equ 301ID_NUM2 equ 302ID_NUM3 equ 303ID_NUM4 equ 304ID_NUM5 equ 305ID_NU
14、M6 equ 306ID_NUM7 equ 307ID_NUM8 equ 308- 10 -ID_NUM9 equ 309ID_NEG equ 310ID_POINT equ 311ID_MUL equ 312ID_DIV equ 313ID_SUB equ 314ID_ADD equ 315ID_EQU equ 316ID_PER equ 317ID_DAO equ 318ID_SQRT equ 319ID_MC equ 320ID_MR equ 321ID_MS equ 322ID_MPLUS equ 323ID_M equ 324ID_BACK equ 325ID_CE equ 326I
15、D_C equ 327ID_RESULT equ 328ID_COPY equ 1001ID_PASTE equ 1002ID_STANDARD equ 1003ID_SCIENCE equ 1004- 11 -ID_PACKET equ 1006ID_HELP equ 1007ID_ABOUT equ 1008ID_EXIT equ 1009ID_NOTIFYICON equ 2000WM_SHELLNOTIFY equ WM_USER+1(3)在 Calculator.inc 声明了自定义函数的原型,主要有窗口主程序 Calculate、消息处理程序、数字分组子程序、数字不分组子程序、数字
16、按键消息处理程序、显示数据子程序、显示存储信息子程序、计算结果子程序、双目运算符消息处理程序、初始化计算器子程序、等于消息处理程序等函数声明如下:;- 函数声明WinMainPROTO :DWORD, :DWORD, :DWORD, :DWORD ; 窗口主程序 Calculate PROTO :DWORD,:DWORD,:DWORD,:DWORD ; 消息处理程序PackNum PROTO ; 数字分组子程序UnpackNum PROTO ; 数字不分组子程序 BtnNum PROTO :DWORD ; 数字按键消息处理程序ShowNum PROTO ; 显示数据子程序ShowTextM P
17、ROTO ; 显示存储信息子程序Init PROTO ; 初始化计算器子程序GetResult PROTO ; 计算结果子程序BtnOperator PROTO ; 双目运算符消息处理程序BtnEqual PROTO ; 等于消息处理程序(4)数据段定义如下:.dataHelpFile db “rc.hlp“,0 ;帮助文档- 12 -hInstance dd ? ;主程序句柄hEdit dd ? ;输出文本框句柄hTextM dd ? ;记忆标签句柄hMenu dd ? ;菜单句柄hIcon dd ? ;Icon 句柄DialogName db “Calculator“,0 ;对话框名称Me
18、nuName db “Menu“,0 ;菜单名称IconName db “Icon“,0 ;Icon 名称TextM db M,0 ;MOutput db “0.“,0,30 dup(0) ;输出字符串IsStart db 1 ;判断是否运算开始HasPoint db 0 ;判断是否存在小数点HasEqueal db 0 ;判断是否存在等号Remember dq 0.0 ;记忆数据Number dq 0.0 ;记录临时数据Result dq 0.0 ;记录结果Operand dq 0.0 ;记录操作数IsPacket db 0 ;数字分组Operator db . ;记录运算符IsError
19、db 0 ;记录是否出现异常Div0 db “除数不能为零。“,0FunctionError db “函数输入无效。“,0hGlobal HANDLE ? ;剪切板内存块句柄pGlobal dd ? ;pointer to allocate memory- 13 -NumLittle REAL8 1.0E-12 Num10 REAL8 10.0 ;实数 10Num100 REAL8 100.0 ;实数 100NotifyIcon NOTIFYICONDATA=ID_NUM0) 双目运算符按钮invoke BtnOperator.elseif eax = ID_EQU ;等于按钮invoke B
20、tnEqual.elseif eax = ID_PER ;百分号按钮mov Operator,*invoke GetResultinvoke UnpackNuminvoke StrToFloat,addr Output, addr Numberfinitfld Numberfld Num100fdivfstp Numberinvoke FloatToStr2,Number,addr Outputinvoke ShowNum.elseif eax = ID_DAO ;倒数按钮invoke UnpackNuminvoke StrToFloat,addr Output, addr Number- 1
21、8 -finitfld Numberfldzfcomi ST(0),ST(1)jnz NotZeromov IsError,1invoke SendMessage,hEdit,WM_SETTEXT,0,addr Div0retNotZero: fstp Numberfstp Numberfld1fld Numberfdiv.if HasEqueal=1fst Result.endiffstp Numberinvoke FloatToStr2,Number,addr Outputinvoke ShowNumc 复制粘贴按钮,主要包括字符串的复制粘贴、剪切板的清空等功能的实现。 .elseif e
22、ax = ID_COPY ;复制invoke GlobalAlloc,GMEM_MOVEABLE,35 ;配置一个内存块mov hGlobal ,eaxinvoke GlobalLock,hGlobal ;锁定内存块- 19 -mov pGlobal ,eaxlea esi,Outputmov edi,pGlobalmov ecx,35rep movsb ;复制字符串invoke GlobalUnlock,hGlobal ;解锁内存块invoke OpenClipboard, NULL ;打开剪切板invoke EmptyClipboard ;清空剪切板invoke SetClipboardD
23、ata,CF_TEXT,hGlobal ;把内存句柄交给剪贴簿invoke CloseClipboard ;关闭剪切板.elseif eax = ID_PASTE ;粘贴invoke IsClipboardFormatAvailable,CF_TEXT ;确定剪贴簿是否含有 CF_TEXT格式的数据invoke OpenClipboard,NULL ;打开剪切板invoke GetClipboardData,CF_TEXT ;得到代表文字的内存块代号mov hGlobal,eaxinvoke GlobalLock ,hGlobal ;解锁内存块mov pGlobal,eaxmov ecx,35
24、lea edi,Outputmov esi,eaxrep movsb ;复制字符串invoke GlobalUnlock ,hGlobal ;解锁内存块invoke CloseClipboard ;关闭剪切板- 20 -invoke ShowNum(3)PackNum 函数将输出数据的字符串 Output 进行数字分组。它首先获取小数点以前的数字位数并保存在寄存器 eax 中,然后将(eax-1)/3 即为需要添加的字符,数目,并保存在 eax 中,对于小数点以后的字符都向后移动 eax 位,对于小数点以前的字符,向后移动 eax 位并用 ecx 计数,当 ecx 计数到 3 是添加字符,并将
25、 ecx 设为 1 且 eax 减一,重复上述步骤直到 eax 等于 0。函数源代码如下:PackNum proc USES eax ebx ecx edxlea esi,Outputmov eax,0.while (BYTE PTResi!=.)inc eaxinc esi.endw.while (BYTE PTResi!=0)inc esi.endwdec eaxmov edx,0mov ecx,3div ecx.while (BYTE PTResi!=.)mov bx,esimov esi+eax,bxdec esi.endw- 21 -mov bx,esimov esi+eax,bxd
26、ec esimov ecx,0.while (eax!=0).if(ecx3)mov bx,esimov esi+eax,bxinc ecx.elsemov BYTE PTResi+eax,dec eaxmov ecx,1.endifdec esi.endwlea esi,Output.while (BYTE PTResi!=0)mov bx,esiinc esi.endwretPackNum endp(4)UnpackNum 函数将进行数字分组输出的字符串 Output 解分组。它首先获- 22 -取 Output 地址存在 esi 中,然后 ecx 赋 0,并将 Output 中字符向前移
27、动 ecx个单位,遇见,字符则将 ecx 加 1,直到字符串结束。函数源代码如下:UnpackNum proc USES ecxlea esi,Outputmov ecx,0.while (BYTE PTResi+ecx!=0).if(BYTE PTResi=“,“)inc ecx.endifmov bx,esi+ecxmov esi,bxinc esi.endwretUnpackNum endp(5) ShowNum 函数将 Output 字符串处理后在文本框中显示出来。它首先调UnpackNum 函数对 Output 解分组,然后获取 Output 地址存在 esi、edi 中,通过循环将
28、 Output 尾地址存在 esi 中,将字符.地址存在 edi 中,如果 edi等于 esi 则表明 Output 中无字符.,则在结尾添加字符.。如果IsPacked 等于 1 则对 Output 调用 UnpackNum 函数对其分组,最后向文本框发送 WM_SETTEXT 消息显示数据。 函数源代码如下:ShowNum procinvoke UnpackNumlea esi,Outputlea edi,Output.while (BYTE PTResi!=0)- 23 -inc esi.endw.while (BYTE PTRedi!=.) 初始化mov HasPoint,0 ;清除小
29、数点mov HasEqueal,0fldzfst Number ;清除结果fst Operandmov Operator,. ;清除运算符mov IsError,0finit ;初始化 FPUretInit endp- 28 -四、调试过程及分析对程序源代码进行功能调试和功能验证。程序代码运行成功后,要对窗口进行检测,再者,分别检验测试其四则运算、开方、倒数运算等功能,并对结果进行分析。4.1 窗口检测 看其热键按钮等功能是否能正常使用。其测试如图 4-1 所示:图 4-1 窗口测试4.2 四则运算、开方以及倒数运算测试 进行数据输入,对结果进行分析。(1)加法运算,计算 2211.045+1000,如图 4-2 所示:图 4-2 加法运算(2)减算运算,计算 170-50,如图 4-3 所示:- 29 -图 4-3 减法运算(3)乘法运算,计算 100*999,如图 4-4 所示:图 4-4 乘法运算(4)除法运算,计算 888/222,如图 4-5 所示:图 4-5 除法运算(5)开方运算,计算 625 开方,如图 4-6 所示:- 30 -图 4-6 开放运算(6)倒数运算,计算 1000 的倒数,如图 4-7 所示:图 4-7 倒数运算