《汇编语言程序设计》——仿windows计算器
|
《汇编语言程序设计》
——计算器程序设计
目录
一、 题目与目标
1. 题目
使用Win32编程设计一个功能及界面风格类似于Windows计算器的计算器程序,只要求实现标准型计算器。
主要实现的功能:
包含基本的四则运算、倒数运算、平方根运算。支持存储区的存储、清除、调出、累加等功能。
2. 学习目的
Ø WIN32汇编程序编写。
Ø 用汇编实现简单的算法。
Ø 浮点数运算(浮点指令或者自己编程模拟)。
Ø 综合解决问题的能力。
二、 分析与设计
1. 系统分析
本程序为Win32窗口应用程序,因此采用Windows开发包的文档中规定的Windows程序标准框架进行编程设计。
2. 系统设计
按照Windows程序标准框架,主程序用于获得并保存本程序的句柄,并调用窗口主程序WinMain创建窗口并进入消息循环。WinMain程序将获取的消息分发给消息处理程序Calculate进行处理。主程序及窗口主程序结构如下图:
消息处理程序Calculate用于相应窗口创立、销毁、按键等消息并进行处理,根据系统功能,消息处理程序Calculate结构图如下:
3. 功能分析
如图所示,Windows自带的计算器按照功能划分可以分为以下5个区域:
显示区:文本框,用于显示输入的操作数及结果
数字键入区:在显示区中显示数字、小数点、正负号等;
运算区:包含双目运算符(+ - * /)、单目运算符(sqrt()、%、1/x)、等于号等
记忆区:清除记忆(MC)、显示记忆(MR)、记忆当前(MS)、记忆加(M+)以及记忆区存储情况的标签
清除键区:退格(Backspace)、清除当前数据(CE)、初始化操作(C)
4. 功能设计
Ø 数字:添加文本框字符串添加数字字符,调用函数BtnNum完成该功能;
Ø 小数点:为当前输入数字添加小数点,将判断是否小数点的变量HasPoint赋值为1
Ø 正负号:将当前数字取相反数并在对话框显示,拟通过浮点运算求相反数并调用ShowNum函数显示数字
Ø 双目运算符:计算结果,调用函数BtnOperator实现运算功能
Ø 等号:计算结果,调用函数BtnEqual实现运算功能
Ø 单目运算符:立即对当前数字进行运算并输出结果
Ø MS:将当前数据保存在变量Remember中,并在记忆区存储情况的标签中显示相应的信息
Ø M+:将当前数据加到变量Remember上,并在记忆区存储情况的标签中显示相应的信息
Ø MR:将变量Remember数据显示到文本框中;
Ø MC:将变量Remember归零,并在记忆区存储情况的标签中显示相应的信息
Ø C:初始化计算器,调用函数Init实现该功能,并在文本框显示0.
Ø CE:将当前数字清零
Ø Backspace:删除当前数据的末位数字
5. 界面设计
系统界面仿照Windows计算器程序界面设计,并使用资源文件进行定义,设计界面如下:
6. 文件设计
程序源文件包含两个部分:
Ø 头文件(Calculator.inc):头文件中引入程序所需要的库以及常量和函数申明
Ø 源文件(Calculator.asm):汇编程序源代码
Ø 资源文件(Calculator.rc):定义程序的窗口界面以及相关资源
Ø 说明文件(Calculator.exe.manifest):说明程序的相关配置及信息
三、 程序系统说明书
1. 创建计算器界面
利用资源文件定义系统界面,代码如下
|
文件分别定义了对话框,菜单和Icon图标等资源,为了在程序中方便对消息的处理,此处有意连续定义了ID_NUM0~ID_NUM9
2. 引入头文件及库
在Calculator.inc头文件中统一定义程序所需的头文件及引入库
|
3. 定义常量
在Calculator.inc中定义程序所需常量
|
4. 函数声明
在Calculator.inc声明了自定义函数的原型
|
数据段定义
|
5. 程序说明
Ø 工具子程序说明
n PackNum
PackNum函数将输出数据的字符串Output进行数字分组。它首先获取小数点以前的数字位数并保存在寄存器eax中,然后将(eax-1)/3即为需要添加的字符‘,’数目,并保存在eax中,对于小数点以后的字符都向后移动eax位,对于小数点以前的字符,向后移动eax位并用ecx计数,当ecx计数到3是添加字符‘,’并将ecx设为1且eax减一,重复上述步骤直到eax等于0。
函数的流程图如下:
函数源代码如下:
|
n UnpackNum
UnpackNum函数将进行数字分组输出的字符串Output解分组。它首先获取Output地址存在esi中,然后ecx赋0,并将Output中字符向前移动ecx个单位,遇见‘,’字符则将ecx加1,直到字符串结束。
函数的流程图如下:
函数源代码如下:
|
n ShowNum
ShowNum函数将Output字符串处理后在文本框中显示出来。它首先调用UnpackNum函数对Output解分组,然后获取Output地址存在esi、edi中,通过循环将Output尾地址存在esi中,将字符‘.’地址存在edi中,如果edi等于esi则表明Output中无字符‘.’,则在结尾添加字符‘.’。如果IsPacked等于1则对Output调用UnpackNum函数对其分组,最后向文本框发送WM_SETTEXT消息显示数据。
函数的流程图如下:
函数源代码如下:
|
n BtnNum
BtnNum函数响应数字按钮消息,向文本框中添加字符。
函数源代码如下:
|
n BtnOperator
BtnOperator函数响应运算符按钮消息,进行运算并输出结果。首先判断是否为等号,如果不是则调用GetResult函数先进行一次运算,然后将当前操作符存入Operator变量中。
函数源代码如下:
|
n BtnEqual
BtnEqual函数响应等号按钮消息,进行运算并输出结果。首先判断是否为起始状态,如果不是则调用GetResult函数,并将HasEqual变量置1。
函数源代码如下:
|
n GetResult
BtnEqual函数响应等号按钮消息,进行运算并输出结果。首先判断是否为起始状态,如果不是则调用GetResult函数,并将HasEqual变量置1。
函数源代码如下:
|
n ShowTextM
ShowTextM函数判断Remember中的值是否为0,如果不是是则在标签中显示‘M’,否则清空标签中内容。
函数源代码如下:
|
n Init
Init函数负责进行必要的初始化操作,如对状态变量的初始化以及的FPU的初始化。
函数源代码如下:
|
Ø 主程序
主程序用于获得并保存本程序的句柄,调用WinMain主程序创建窗口并获取和分发消息,然后结束程序。
主程序流程图及原代码如下:
|
|
Ø WinMain主程序
WinMain主程序用于创建窗口并获取和分发消息。
主程序流程图如下:
程序源代码如下:
|
Ø 消息处理程序
消息处理程序用于处理用户消息。
消息处理程序流程图如下:
消息处理程序源代码如下:
|
四、 设计与思考
1. 为什么使用对话框?
使用对话框做为主程序窗口的启发来源于《Windows程序设计》(【美】Charles Petzold 北京大学出版社)中的范例《HEXCALC:窗口还是对话框?》HEXCALC程序可能是写程序偷懒的经典之作,这个程序完全不呼叫CreateWindow,也不处理WM_PAINT消息,不取得设备内容,也不处理鼠标消息。但是它只用了不到150行的原始码,就构成了一个具有完整键盘和鼠标接口以及10种运算的十六进制计算器。受到它的启发,以及为了利用资源文件定义系统界面的简洁与方便,于是本程序将对话框就作为主程序。
事实上对话框就是窗口。通常Windows使用它自己内部的窗口消息处理程序处理对话框窗口的消息,然后,Windows将这些消息传送给建立对话框的程序内的对话框程序。在本程序中,我们让Windows使用对话框模板建立一个窗口,但是自己写程序处理这个窗口的消息,方便而简洁。
2. 如何应用系统的外观?
为了能够利用系统的外观,根据《如何将 Windows XP 主题应用于 Office COM 加载项》(http://support.microsoft.com/kb/830033/zh-cn)一文,定义说明文件Calculator.exe.manifest,然后在资源文件中添加代码 #define ISOLATION_AWARE_ENABLED 1 即可。
|
3. 关于最小化
本程序中的最小化按钮与Windows计算器的最小化大不一样!单击本程序的最小化按钮就会将主程序最小化到系统托盘,当单击系统托盘的小图标时,窗口就会显示出来,右键单击系统托盘图标时则会显示菜单栏。如图:
这样的设计为用户节省了空间,并且在不需要的时候不影响其它应用程序的工作。它的启发与阅读《Iczelion的win32汇编教程》不无关系,其中的第二十三课 系统托盘中的快捷图标详细的介绍了相关的内容。
4. 关于计算器
当你点击计算器中的“帮助”→“关于计算器”的时候你会看到下面的弹出窗口:
您可能以为自己在使用Windows计算器,哈哈,其实这完全是笔者玩的一个小把戏,这一切很简单,仅仅是调用了一个有关Shell的函数而已—— invoke ShellAbout,hWin,addr ProgramName,addr Author,hIcon 。小小的添加项为程序添加了几分乐趣,为此我还特点观看了一点有关shell的知识,在其中《Win32开发人员参考库第五卷:windows Shell》(David Iseminger ,机械工业出版社,2001)
此外“帮助”→“帮助主题”时也会弹出一个帮助的窗口,方便用户了解和使用计算器。这也仅仅是调用函数WinHelp弹出了windows自带的帮助文档。
5. 为什么要设计安装文件?
由于本程序项目工程比较复杂,而且需要包含相应的帮助文档、图标文件以及相关的文件,以及创立并修改注册表的键值一保存相关信息,并且为了确保能够在不同的系统上运行提高兼容性,特意使用Visual Studio2008制作了安装文件。安装文件的界面友好,明确的提示用户需要进行的操作。
6. 为什么要播放音乐?
如果您仔细的话会发现该计算器还添加了MID音乐播放的功能,您可以选择一个MID音乐来播放,也可以暂停它或者继续播放,使您在工作之余能够稍稍放松。之所以要写这个是希望能够学习Windows通用对话框的调用以及打开文件并进行播放。(注:由于这段代码是闲暇之余添加上去的,所以上面的说明可能并未包含该部分的)
五、 课程设计的体会
对于Win32的初学者,最大的问题莫过于假设Win32汇编程序设计的环境了,一个方便的汇编程序的编写和调试环境对开发人员来说非常重要。受《Intel汇编语言程序设计(第五版)》(【美】Kip R Irvine,电子工业出版社,2008)启示以及自己对Visual Studio的熟悉,笔者选择了Visual Studio2008 作为开发环境,它能够自动进行链接、汇编并生成应用程序,非常的方便。至于其他环境的架设(如MASM32等),本人将相关资料整理成了博客(http://blog.csdn.net/KingWolfOfSky/archive/2009/07/23/4375411.aspx)。
《Windows程序设计(第五版)》(【美】Charles Petzold 北京大学出版社,1999)确实是一本好书,它详细的讲述了Win32图形界面编程的方法。使用对话框应用程序的启发也来自于中的范例《HEXCALC:窗口还是对话框?》。这可惜这本书已经不再出版了。
设计过程中关于对FPU的操作,以及浮点数转化和表示。关于FPU一节,《Intel汇编语言程序设计(第五版)》(【美】Kip R Irvine,电子工业出版社,2008)已经做了很详细深入的介绍,关于浮点数的表示相关问题,本人查阅了IEEE相关的规定,并整理成了Blog——《计算机中浮点数的表示与IEEE 754》(http://blog.csdn.net/KingWolfOfSky/archive/2009/09/08/4533404.aspx)
在学习Win32编程的过程中更令人迷人的是windows操作系统对进程、内存的管理与调度,于是本人饶有兴趣的参看了《现代操作系统》(【荷】Andrew S. Tanenbaum 机械工业出版社,2009)以及《Windows核心编程(第五版)》(【美】Jeffery Richter 清华大学出版社,2008);虽然并不是十分清楚,但是对其中的工作原理有了一定的了解。
程序中设计的问题的确让人烦恼,例如无法改变PUSHBUTTON的字体颜色,除非自绘,然而对于美工不好的我来说这的确不是一个好的选择。曾经花费两天的时间试图改变PUSHBUTTON的字体颜色,显然以失败而告终,这告诉我们应当了解一些语言和架构能完成什么、不能做到什么,这样才算真正的了解它。
六、 参考资料
Ø 《80X86汇编语言程序设计》,王元珍、曹忠升、韩宗芬,华中科技大学出版社,2005
Ø 《Iczelion的Win32汇编教程》
Ø 《Intel汇编语言程序设计(第五版)》,【美】Kip R Irvine,电子工业出版社,2008
Ø 《汇编语言编程艺术》,Randall Hyde,清华大学出版社 ,2005
Ø 《IBM PC汇编语言程序设计(第五版)》,Peter Abel,人民邮电出版社,2002
Ø 《Win32开发人员参考库第五卷:Windows Shell》,David Iseminger,机械工业出版社,2001
Ø 《Microsoft MASM 参考手册》
Ø 《现代操作系统》,【荷】Andrew S. Tanenbaum 机械工业出版社,2009
Ø 《Windows核心编程(第五版)》,【美】Jeffery Richter 清华大学出版社,2008
Ø 《Windows程序设计(第五版)》,【美】Charles Petzold ,北京大学出版社,1999
Ø 《Intel® 64 and IA-32 Architectures Software Developer's Manuals》
Ø MSDN Library: www.microsoft.com/china/MSDN/library/
七、 附录
1. 系统模块总图