基于WIN32汇编实现的仿Windows计算器

greenlight

发布日期: 2018-10-06 20:24:31 浏览量: 985
评分:
star star star star star star star star star star
*转载请注明来自write-bug.com

摘要

使用Win32编程设计一个功能及界面风格类似于Windows计算器的计算器程序,只要求实现标准型计算器。

主要实现的功能:包含基本的四则运算、倒数运算、平方根运算。并支持存储区的存储、清除、调出、累加等功能。

关键词:win32,Windows计算器,汇编,四则运算,倒数运算,平方根运算

Abstract

Using Win32 programming to design a calculator program with a functional and interface style similar to the Windows calculator.It’s only a standard calculator.

Mainly implemented functions including basic four arithmetic operations, reciprocal operations, and square root operations. It also supports the clearing, recalling, and accumulating functions of the storage area.

Keywords:Win32, Windows calculator program, Assembly Language , Arithmetic, Countdown, Square root operation

1 系统分析与设计

1.1 系统分析

本程序为Win32窗口应用程序,因此采用Windows开发包的文档中规定的Windows程序标准框架进行编程设计。

1.2 系统设计

按照Windows程序标准框架,主程序用于获得并保存本程序的句柄,并调用窗口主程序WinMain创建窗口并进入消息循环。WinMain程序将获取的消息分发给消息处理程序Calculate进行处理。

消息处理程序Calculate用于相应窗口创立、销毁、按键等消息并进行处理。

系统总体架构如下图:

1.3 界面设计

系统界面仿照Windows计算器程序界面设计,并使用资源文件进行定义,设计界面如下:

1.4 功能分析与设计

  • 数字:添加文本框字符串添加数字字符,调用函数BtnNum完成该功能
  • 小数点:为当前输入数字添加小数点,将判断是否小数点的变量HasPoint赋值为1
  • 正负号:将当前数字取相反数并在对话框显示,拟通过浮点运算求相反数并调用ShowNum函数显示数字
  • 双目运算符:计算结果,调用函数BtnOperator实现运算功能
  • 等号:计算结果,调用函数BtnEqual实现运算功能
  • 单目运算符:立即对当前数字进行运算并输出结果
  • MS:将当前数据保存在变量Remember中,并在记忆区存储情况的标签中显示相应的信息
  • M+:将当前数据加到变量Remember上,并在记忆区存储情况的标签中显示相应的信息
  • MR:将变量Remember数据显示到文本框中
  • MC:将变量Remember归零,并在记忆区存储情况的标签中显示相应的信息
  • C:初始化计算器,调用函数Init实现该功能,并在文本框显示0.
  • CE:将当前数字清零
  • Backspace:删除当前数据的末位数字

1.5 文件设计

  • 头文件(Calculator.inc):头文件中引入程序所需要的库以及常量和函数申明
  • 源文件(Calculator.asm):汇编程序源代码
  • 资源文件(Calculator.rc):定义程序的窗口界面以及相关资源
  • 说明文件(Calculator.exe.manifest):说明程序的相关配置及信息

2 系统实现

2.1 创建计算器界面

利用资源文件定义系统界面,代码如下,文件分别定义了对话框,菜单和Icon图标等资源,为了在程序中方便对消息的处理,此处有意连续定义了ID_NUM0~ID_NUM9。

  1. #include "resource.h"
  2. #define ISOLATION_AWARE_ENABLED
  3. #define ID_NUM0 300
  4. #define ID_NUM1 301
  5. #define ID_NUM2 302
  6. #define ID_NUM3 303
  7. #define ID_NUM4 304
  8. #define ID_NUM5 305
  9. #define ID_NUM6 306
  10. #define ID_NUM7 307
  11. #define ID_NUM8 308
  12. #define ID_NUM9 309
  13. #define ID_NEG 310
  14. #define ID_POINT 311
  15. #define ID_MUL 312
  16. #define ID_DIV 313
  17. #define ID_SUB 314
  18. #define ID_ADD 315
  19. #define ID_EQU 316
  20. #define ID_PER 317
  21. #define ID_DAO 318
  22. #define ID_SQRT 319
  23. #define ID_MC 320
  24. #define ID_MR 321
  25. #define ID_MS 322
  26. #define ID_MPLUS 323
  27. #define ID_M 324
  28. #define ID_BACK 325
  29. #define ID_CE 326
  30. #define ID_C 327
  31. #define ID_RESULT 328
  32. #define ID_COPY 1001
  33. #define ID_PASTE 1002
  34. #define ID_STANDARD 1003
  35. #define ID_SCIENCE 1004
  36. #define ID_PACKET 1006
  37. #define ID_HELP 1007
  38. #define ID_ABOUT 1008
  39. #define ID_EXIT 1009
  40. Calculator DIALOGEX 0, 0, 170, 133
  41. STYLE DS_CENTER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
  42. CLASS "Calculator"
  43. CAPTION "计算器"
  44. FONT 8, "Tahoma"
  45. BEGIN
  46. PUSHBUTTON "0",ID_NUM0,36,99,23,16,0
  47. PUSHBUTTON "1",ID_NUM1,36,81,23,16,0
  48. PUSHBUTTON "2",ID_NUM2,61,81,23,16,0
  49. PUSHBUTTON "3",ID_NUM3,87,81,23,16,0
  50. PUSHBUTTON "4",ID_NUM4,36,63,23,16,0
  51. PUSHBUTTON "5",ID_NUM5,61,63,23,16,0
  52. PUSHBUTTON "6",ID_NUM6,87,63,23,16,0
  53. PUSHBUTTON "7",ID_NUM7,36,44,23,16,0
  54. PUSHBUTTON "8",ID_NUM8,61,44,23,16,0
  55. PUSHBUTTON "9",ID_NUM9,87,44,23,16,0
  56. PUSHBUTTON "+/-",ID_NEG,61,99,23,16,0
  57. PUSHBUTTON ".",ID_POINT,87,99,23,16,0
  58. PUSHBUTTON "/",ID_DIV,113,44,23,16,0
  59. PUSHBUTTON "*",ID_MUL,113,63,23,16,0
  60. PUSHBUTTON "-",ID_SUB,113,81,23,16,0
  61. PUSHBUTTON "+",ID_ADD,113,99,23,16,0
  62. PUSHBUTTON "sqrt",ID_SQRT,139,44,23,16,0
  63. PUSHBUTTON "%",ID_PER,139,63,23,16,0
  64. PUSHBUTTON "1/x",ID_DAO,139,81,23,16,0
  65. PUSHBUTTON "=",ID_EQU,139,99,23,16,0
  66. PUSHBUTTON "MC",ID_MC,6,44,23,16,0
  67. PUSHBUTTON "MR",ID_MR,6,63,23,16,0
  68. PUSHBUTTON "MS",ID_MS,6,81,23,16,0
  69. PUSHBUTTON "M+",ID_MPLUS,6,99,23,16,0
  70. PUSHBUTTON "Backspace",ID_BACK,36,23,42,16,0
  71. PUSHBUTTON "CE",ID_CE,79,23,41,16,0
  72. PUSHBUTTON "C",ID_C,122,23,41,16,0
  73. EDITTEXT ID_RESULT,5,2,160,13,ES_RIGHT | ES_NUMBER ,0
  74. CTEXT "",ID_M,9,23,17,14,SS_SUNKEN | NOT WS_BORDER
  75. END
  76. Menu MENU LOADONCALL
  77. BEGIN
  78. POPUP "编辑(&F)"
  79. BEGIN
  80. MENUITEM "复制(&C) Ctrl+C",ID_COPY
  81. MENUITEM "粘贴(&P) Ctrl+P",ID_PASTE
  82. MENUITEM SEPARATOR
  83. MENUITEM "关闭(&E)",ID_EXIT
  84. END
  85. POPUP "查看(&V)"
  86. BEGIN
  87. MENUITEM "标准型(&T)",ID_STANDARD
  88. MENUITEM "科学型(&S)",ID_SCIENCE,GRAYED
  89. MENUITEM SEPARATOR
  90. MENUITEM "数字分组(&I)",ID_PACKET
  91. END
  92. POPUP "帮助(&H)"
  93. BEGIN
  94. MENUITEM "帮助主题(&H)",ID_HELP
  95. MENUITEM SEPARATOR
  96. MENUITEM "关于计算器(&A)",ID_ABOUT
  97. END
  98. POPUP "", GRAYED
  99. BEGIN
  100. MENUITEM "复制(&C) Ctrl+C",1001
  101. MENUITEM "粘贴(&P) Ctrl+P",1002
  102. MENUITEM SEPARATOR
  103. MENUITEM "标准型(&T)",1003
  104. MENUITEM "科学型(&S)",1004,GRAYED
  105. MENUITEM SEPARATOR
  106. MENUITEM "数字分组(&I)",1006
  107. MENUITEM SEPARATOR
  108. MENUITEM "帮助主题(&H)",1007
  109. MENUITEM "关于计算器(&A)",1008
  110. MENUITEM SEPARATOR
  111. MENUITEM "关闭(&E)",1009
  112. END
  113. END
  114. Icon ICON MOVEABLE PURE LOADONCALL DISCARDABLE "Calculator.ico"

2.2 引入头文件及库

在Calculator.inc头文件中统一定义程序所需的头文件及引入库,代码如下:

  1. ;--------------------------- 头文件声明---------------------------
  2. include windows.inc
  3. include user32.inc
  4. include kernel32.inc
  5. include comctl32.inc
  6. include masm32.inc
  7. include shell32.inc
  8. ;--------------------------- 引入库声明---------------------------
  9. includelib user32.lib
  10. includelib comctl32.lib
  11. includelib masm32.lib

2.3 定义常量

在Calculator.inc中定义程序所需常量,代码如下:

  1. ;---------------------------- 常量声明----------------------------
  2. ID_NUM0 equ 300
  3. ID_NUM1 equ 301
  4. ID_NUM2 equ 302
  5. ID_NUM3 equ 303
  6. ID_NUM4 equ 304
  7. ID_NUM5 equ 305
  8. ID_NUM6 equ 306
  9. ID_NUM7 equ 307
  10. ID_NUM8 equ 308
  11. ID_NUM9 equ 309
  12. ID_NEG equ 310
  13. ID_POINT equ 311
  14. ID_MUL equ 312
  15. ID_DIV equ 313
  16. ID_SUB equ 314
  17. ID_ADD equ 315
  18. ID_EQU equ 316
  19. ID_PER equ 317
  20. ID_DAO equ 318
  21. ID_SQRT equ 319
  22. ID_MC equ 320
  23. ID_MR equ 321
  24. ID_MS equ 322
  25. ID_MPLUS equ 323
  26. ID_M equ 324
  27. ID_BACK equ 325
  28. ID_CE equ 326
  29. ID_C equ 327
  30. ID_RESULT equ 328
  31. ID_COPY equ 1001
  32. ID_PASTE equ 1002
  33. ID_STANDARD equ 1003
  34. ID_SCIENCE equ 1004
  35. ID_PACKET equ 1006
  36. ID_HELP equ 1007
  37. ID_ABOUT equ 1008
  38. ID_EXIT equ 1009
  39. ID_NOTIFYICON equ 2000
  40. WM_SHELLNOTIFY equ WM_USER+1

2.4 函数声明

在Calculator.inc声明了自定义函数的原型,代码如下:

  1. ;---------------------------- 函数声明----------------------------
  2. WinMain PROTO :DWORD, :DWORD, :DWORD, :DWORD ; 窗口主程序
  3. Calculate PROTO :DWORD,:DWORD,:DWORD,:DWORD ; 消息处理程序
  4. PackNum PROTO ; 数字分组子程序
  5. UnpackNum PROTO ; 数字不分组子程序
  6. BtnNum PROTO :DWORD ; 数字按键消息处理程序
  7. ShowNum PROTO ; 显示数据子程序
  8. ShowTextM PROTO ; 显示存储信息子程序
  9. Init PROTO ; 初始化计算器子程序
  10. GetResult PROTO ; 计算结果子程序
  11. BtnOperator PROTO ; 双目运算符消息处理程序
  12. BtnEqual PROTO ; 等于消息处理程序

数据段定义代码如下:

  1. ;===================== Start 数据段定义Start =====================
  2. .data
  3. ProgramName db "计算器",0 ;程序名
  4. Author db "作者:桂杨",0 ;作者
  5. HelpFile db "rc.hlp",0 ;帮助文档
  6. hInstance db ? ;主程序句柄
  7. hEdit db ? ;输出文本框句柄
  8. hTextM db ? ;记忆标签句柄
  9. hMenu db ? ;菜单句柄
  10. hIcon db ? ;Icon句柄
  11. DialogName db "Calculator",0 ;对话框名称
  12. MenuName db "Menu",0 ;菜单名称
  13. IconName db "Icon",0 ;Icon名称
  14. TextM db 'M',0 ;M
  15. Output db "0.",0,30 dup(0) ;输出字符串
  16. IsStart db 1 ;判断是否运算开始
  17. HasPoint db 0 ;判断是否存在小数点
  18. HasEqueal db 0 ;判断是否存在等号
  19. Remember dq 0.0 ;记忆数据
  20. Number dq 0.0 ;记录临时数据
  21. Result dq 0.0 ;记录结果
  22. Operand dq 0.0 ;记录操作数
  23. IsPacket db 0 ;数字分组
  24. Operator db '.' ;记录运算符
  25. IsError db 0 ;记录是否出现异常
  26. Div0 db "除数不能为零。",0
  27. FunctionError db "函数输入无效。",0
  28. hGlobal HANDLE ? ;剪切板内存块句柄
  29. pGlobal db ? ;pointer to allocate memory
  30. NumLittle REAL8 1.0E-12
  31. Num10 REAL8 10.0 ;实数10
  32. Num100 REAL8 100.0 ;实数100
  33. NotifyIcon NOTIFYICONDATA<> ;通知栏图标
  34. ;======================= End 数据段定义End =======================

2.5 程序说明

2.5.1 主程序

主程序用于获得并保存本程序的句柄,调用WinMain主程序创建窗口并获取和分发消息,然后结束程序。

主程序流程图及原代码如下:

  1. invoke GetModuleHandle,NULL
  2. ;获得并保存本程序的句柄
  3. mov hInstance,eax
  4. invoke WinMain,hInstance,0,0,SW_SHOWDEFAULT
  5. invoke ExitProcess,eax
  6. ;退出程序,返回eax

2.5.2 WinMain主程序

WinMain主程序用于创建窗口并获取和分发消息。

主程序流程图及源代码如下:

  1. WinMain proc hInst:DWORD, hPrevInst:DWORD, CmdLine:DWORD, CmdShow:DWORD
  2. LOCAL wc:WNDCLASSEX ;窗口类
  3. LOCAL msg:MSG ;消息
  4. LOCAL hWnd:HWND ;对话框句柄
  5. mov wc.cbSize,sizeof WNDCLASSEX ;WNDCLASSEX的大小
  6. mov wc.style,CS_BYTEALIGNWINDOW or CS_BYTEALIGNWINDOW ;窗口风格or CS_HREDRAW or CS_VREDRAW
  7. mov wc.lpfnWndProc,OFFSET Calculate ;窗口消息处理函数地址
  8. mov wc.cbClsExtra,0 ;在窗口类结构后的附加字节数,共享内存
  9. mov wc.cbWndExtra,DLGWINDOWEXTRA ;在窗口实例后的附加字节数(!注意点)
  10. mov eax,hInst
  11. mov wc.hInstance,eax ;窗口所属程序句柄
  12. mov wc.hbrBackground,COLOR_BTNFACE+1 ;背景画刷句柄
  13. mov wc.lpszMenuName,NULL ;菜单名称指针
  14. mov wc.lpszClassName,OFFSET DialogName ;类名称指针
  15. invoke LoadIcon,hInst,addr IconName ;加载Icon
  16. mov wc.hIcon,eax ;图标句柄
  17. invoke LoadCursor,NULL,IDC_ARROW
  18. mov wc.hCursor,eax ;光标句柄
  19. mov wc.hIconSm,0 ;窗口小图标句柄
  20. invoke RegisterClassEx,addr wc ;注册窗口类
  21. invoke CreateDialogParam,hInst,addr DialogName,0,addr Calculate,0 ;调用对话框窗口
  22. mov hWnd,eax ;保存对话框句柄
  23. invoke ShowWindow,hWnd,CmdShow ;最后一个参数可设置为SW_SHOWNORMAL
  24. invoke UpdateWindow,hWnd ;更新窗口
  25. StartLoop: ;消息循环
  26. invoke GetMessage,addr msg,0,0,0 ;获取消息
  27. cmp eax,0
  28. je ExitLoop
  29. invoke TranslateMessage,addr msg ;转换键盘消息
  30. invoke DispatchMessage,addr msg ;分发消息
  31. jmp StartLoop
  32. ExitLoop: ;结束消息循环
  33. mov eax,msg.wParam
  34. ret
  35. WinMain endp

2.5.3 消息处理程序

消息处理程序用于处理用户消息。

消息处理程序流程图及代码如下:

  1. Calculate proc hWin:DWORD,uMsg:UINT,aParam:DWORD,bParam:DWORD
  2. LOCAL pt:POINT
  3. .if uMsg == WM_INITDIALOG
  4. invoke GetDlgItem,hWin,ID_RESULT ;获取输出文本框句柄
  5. mov hEdit,eax ;保存文本框句柄
  6. invoke GetDlgItem,hWin,ID_M ;获取记忆标签句柄
  7. mov hTextM,eax ;保存记忆标签句柄
  8. invoke LoadIcon,hInstance,addr IconName ;载入Icon
  9. mov hIcon,eax ;保存Icon句柄
  10. invoke SendMessage,hWin,WM_SETICON,ICON_SMALL ,eax
  11. invoke LoadMenu,hInstance,addr MenuName ;加载菜单
  12. mov hMenu,eax ;保存菜单句柄
  13. invoke SetMenu,hWin,eax
  14. invoke CheckMenuRadioItem, hMenu, ID_STANDARD, ID_SCIENCE,ID_STANDARD,MF_BYCOMMAND ;选中标准型
  15. invoke SendMessage,hEdit,WM_SETTEXT,0,addr Output ;显示"0."
  16. .elseif uMsg == WM_SIZE
  17. .if aParam==SIZE_MINIMIZED ;最小化
  18. mov NotifyIcon.cbSize,sizeof NOTIFYICONDATA
  19. push hWin
  20. pop NotifyIcon.hwnd
  21. mov NotifyIcon.uID,ID_NOTIFYICON
  22. mov NotifyIcon.uFlags,NIF_ICON+NIF_MESSAGE+NIF_TIP
  23. mov NotifyIcon.uCallbackMessage,WM_SHELLNOTIFY
  24. mov eax,hIcon
  25. mov NotifyIcon.hIcon,eax
  26. invoke lstrcpy,addr NotifyIcon.szTip,addr ProgramName
  27. invoke ShowWindow,hWin,SW_HIDE ;隐藏窗口
  28. invoke Shell_NotifyIcon,NIM_ADD,addr NotifyIcon
  29. .endif
  30. .elseif uMsg == WM_SHELLNOTIFY
  31. .if aParam==ID_NOTIFYICON
  32. .if (bParam==WM_LBUTTONDOWN) ;单击通知栏图标
  33. invoke ShowWindow,hWin,SW_SHOW ;显示窗口
  34. invoke Shell_NotifyIcon,NIM_DELETE,addr NotifyIcon ;删除通知栏图标
  35. .elseif (bParam==WM_RBUTTONDOWN) ;右键通知栏图标
  36. invoke GetCursorPos,addr pt
  37. invoke GetSubMenu,hMenu,3
  38. invoke TrackPopupMenu,eax,TPM_LEFTALIGN,pt.x,pt.y,NULL,hWin,NULL
  39. .endif
  40. .endif
  41. .elseif uMsg == WM_CHAR ;热键操作
  42. mov eax,aParam
  43. sub eax,'0'
  44. add eax,ID_NUM0
  45. .if (eax>=ID_NUM0) && (eax<=ID_NUM9) ;数字按钮
  46. invoke Calculate,hWin,WM_COMMAND,eax,0
  47. .elseif (eax==0ffh) ;ID_COPY
  48. invoke Calculate,hWin,WM_COMMAND,ID_COPY,0
  49. .elseif (eax==112h) ;ID_PASTE
  50. invoke Calculate,hWin,WM_COMMAND,ID_PASTE,0
  51. .elseif (eax==104h) ;ID_BACK
  52. invoke Calculate,hWin,WM_COMMAND,ID_BACK,0
  53. .elseif (eax==265) ;ID_EQU
  54. invoke Calculate,hWin,WM_COMMAND,ID_EQU,0
  55. .elseif (eax==298) ;ID_POINT
  56. invoke Calculate,hWin,WM_COMMAND,ID_POINT,0
  57. .elseif(eax==295) ;ID_ADD
  58. invoke Calculate,hWin,WM_COMMAND,ID_ADD,0
  59. .elseif (eax==297) ;ID_SUB
  60. invoke Calculate,hWin,WM_COMMAND,ID_SUB,0
  61. .elseif (eax==294) ;ID_MUL
  62. invoke Calculate,hWin,WM_COMMAND,ID_MUL,0
  63. .elseif (eax==299) ;ID_DIV
  64. invoke Calculate,hWin,WM_COMMAND,ID_DIV,0
  65. .endif
  66. .elseif uMsg == WM_COMMAND
  67. mov eax,aParam
  68. .if eax == ID_CE ;清零按钮CE
  69. lea esi,Output
  70. mov BYTE PTR[esi],'0'
  71. mov BYTE PTR[esi+1],'.'
  72. mov BYTE PTR[esi+2],0
  73. .if IsError==1
  74. invoke Init
  75. .endif
  76. invoke SendMessage,hEdit,WM_SETTEXT,0,addr Output
  77. .elseif eax == ID_C ;初始化按钮C
  78. invoke Calculate,hWin,WM_COMMAND,ID_CE,bParam
  79. invoke Init
  80. .elseif IsError==1
  81. ret
  82. .elseif eax == ID_BACK ;退格按钮Backspace
  83. invoke UnpackNum
  84. .if IsStart==0
  85. lea esi,Output
  86. .while BYTE PTR[esi]!=0
  87. inc esi
  88. .endw
  89. .if BYTE PTR[esi-1]=='.'
  90. .if HasPoint==1
  91. mov HasPoint,0
  92. .else
  93. .if BYTE PTR[esi-3]=='-'
  94. lea esi,Output
  95. mov BYTE PTR[esi],'0'
  96. mov BYTE PTR[esi+1],'.'
  97. mov BYTE PTR[esi+2],0
  98. .else
  99. mov BYTE PTR[esi-2],'.'
  100. mov BYTE PTR[esi-1],0
  101. .endif
  102. .endif
  103. .else
  104. mov BYTE PTR[esi-1],0
  105. .endif
  106. lea esi,Output
  107. .if BYTE PTR[esi]=='.'
  108. mov BYTE PTR[esi],'0'
  109. mov BYTE PTR[esi+1],'.'
  110. mov BYTE PTR[esi+2],0
  111. .endif
  112. invoke ShowNum
  113. .endif
  114. .elseif (eax >= ID_NUM0) && (eax <= ID_NUM9) ;数字按钮
  115. .if HasEqueal==1
  116. invoke Init
  117. .endif
  118. invoke BtnNum,eax
  119. .elseif eax == ID_POINT ;小数点按钮
  120. mov BYTE PTR HasPoint,1
  121. mov BYTE PTR IsStart,0
  122. .elseif eax == ID_NEG ;正负号按钮
  123. invoke UnpackNum
  124. invoke StrToFloat,addr Output, addr Number
  125. finit
  126. fldz
  127. fld Number
  128. fsub
  129. fstp Number
  130. invoke FloatToStr2,Number,addr Output
  131. invoke ShowNum
  132. .elseif (eax >= ID_MUL) && (eax <= ID_ADD) ;双目运算符按钮
  133. invoke BtnOperator
  134. .elseif eax == ID_EQU ;等于按钮
  135. invoke BtnEqual
  136. .elseif eax == ID_PER ;百分号按钮
  137. mov Operator,'*'
  138. invoke GetResult
  139. invoke UnpackNum
  140. invoke StrToFloat,addr Output, addr Number
  141. finit
  142. fld Number
  143. fld Num100
  144. fdiv
  145. fstp Number
  146. invoke FloatToStr2,Number,addr Output
  147. invoke ShowNum
  148. .elseif eax == ID_DAO ;倒数按钮
  149. invoke UnpackNum
  150. invoke StrToFloat,addr Output, addr Number
  151. finit
  152. fld Number
  153. fldz
  154. fcomi ST(0),ST(1)
  155. jnz NotZero
  156. mov IsError,1
  157. invoke SendMessage,hEdit,WM_SETTEXT,0,addr Div0
  158. ret
  159. NotZero: fstp Number
  160. fstp Number
  161. fld1
  162. fld Number
  163. fdiv
  164. .if HasEqueal==1
  165. fst Result
  166. .endif
  167. fstp Number
  168. invoke FloatToStr2,Number,addr Output
  169. invoke ShowNum
  170. .elseif eax == ID_SQRT ;开方按钮
  171. invoke UnpackNum
  172. invoke StrToFloat,addr Output, addr Number
  173. finit
  174. fld Number
  175. fldz
  176. fcomi ST(0),ST(1)
  177. jb Positive
  178. mov IsError,1
  179. invoke SendMessage,hEdit,WM_SETTEXT,0,addr FunctionError
  180. ret
  181. Positive: fstp Number
  182. fsqrt
  183. .if HasEqueal==1
  184. fst Result
  185. .endif
  186. fstp Number
  187. invoke FloatToStr2,Number,addr Output
  188. invoke ShowNum
  189. .elseif eax == ID_MC ;MC按钮
  190. fldz
  191. fstp Remember
  192. invoke SendMessage,hTextM,WM_SETTEXT,0,NULL
  193. .elseif eax == ID_MR ;MR按钮
  194. invoke FloatToStr2,Remember,addr Output
  195. invoke ShowNum
  196. mov IsStart,0
  197. .elseif eax == ID_MS ;MS按钮
  198. invoke UnpackNum
  199. invoke StrToFloat,addr Output, addr Remember
  200. invoke ShowTextM
  201. .elseif eax == ID_MPLUS ;M+按钮
  202. finit
  203. fld Remember
  204. invoke UnpackNum
  205. invoke StrToFloat,addr Output, addr Remember
  206. fld Remember
  207. fadd
  208. fstp Remember
  209. invoke ShowTextM
  210. .elseif eax == ID_COPY ;复制
  211. invoke GlobalAlloc,GMEM_MOVEABLE,35 ;配置一个内存块
  212. mov hGlobal ,eax
  213. invoke GlobalLock,hGlobal ;锁定内存块
  214. mov pGlobal ,eax
  215. lea esi,Output
  216. mov edi,pGlobal
  217. mov ecx,35
  218. rep movsb ;复制字符串
  219. invoke GlobalUnlock,hGlobal ;解锁内存块
  220. invoke OpenClipboard, NULL ;打开剪切板
  221. invoke EmptyClipboard ;清空剪切板
  222. invoke SetClipboardData,CF_TEXT,hGlobal ;把内存句柄交给剪贴簿
  223. invoke CloseClipboard ;关闭剪切板
  224. .elseif eax == ID_PASTE ;粘贴
  225. invoke IsClipboardFormatAvailable,CF_TEXT ;确定剪贴簿是否含有CF_TEXT格式的数据
  226. invoke OpenClipboard,NULL ;打开剪切板
  227. invoke GetClipboardData,CF_TEXT ;得到代表文字的内存块代号
  228. mov hGlobal,eax
  229. invoke GlobalLock ,hGlobal ;解锁内存块
  230. mov pGlobal,eax
  231. mov ecx,35
  232. lea edi,Output
  233. mov esi,eax
  234. rep movsb ;复制字符串
  235. invoke GlobalUnlock ,hGlobal ;解锁内存块
  236. invoke CloseClipboard ;关闭剪切板
  237. invoke ShowNum
  238. .elseif eax == ID_PACKET ;数字分组
  239. .if IsPacket==0
  240. invoke CheckMenuItem,hMenu,ID_PACKET,MF_CHECKED ;选中数字分组
  241. .else
  242. invoke CheckMenuItem,hMenu,ID_PACKET,MF_UNCHECKED ;选中数字分组
  243. .endif
  244. xor IsPacket,1
  245. invoke ShowNum
  246. .elseif eax == ID_HELP ;帮助
  247. invoke WinHelp,hWin,addr HelpFile,HELP_CONTENTS,1
  248. .elseif eax == ID_ABOUT ;关于
  249. invoke ShellAbout,hWin,addr ProgramName,addr Author,hIcon
  250. .elseif eax == ID_EXIT ;关闭
  251. invoke Calculate,hWin,WM_CLOSE,aParam,bParam
  252. .endif
  253. .elseif uMsg == WM_CLOSE
  254. invoke Shell_NotifyIcon,NIM_DELETE,addr NotifyIcon
  255. invoke EndDialog,hWin,NULL
  256. invoke PostQuitMessage,0 ;退出消息循环
  257. .else
  258. invoke DefWindowProc,hWin,uMsg,aParam,bParam
  259. ret
  260. .endif
  261. invoke SetFocus,hWin
  262. xor eax,eax ;关于WM_KEYDOWN原因
  263. ret
  264. Calculate endp

2.5.4 工具子程序说明

2.5.4.1 PackNum

PackNum函数将输出数据的字符串Output进行数字分组。它首先获取小数点以前的数字位数并保存在寄存器eax中,然后将(eax-1)/3即为需要添加的字符‘,’数目,并保存在eax中,对于小数点以后的字符都向后移动eax位,对于小数点以前的字符,向后移动eax位并用ecx计数,当ecx计数到3是添加字符‘,’并将ecx设为1且eax减一,重复上述步骤直到eax等于0。

函数的流程图及代码如下:

  1. PackNum proc USES eax ebx ecx edx
  2. lea esi,Output
  3. mov eax,0
  4. .while (BYTE PTR[esi]!='.')
  5. inc eax
  6. inc esi
  7. .endw
  8. .while (BYTE PTR[esi]!=0)
  9. inc esi
  10. .endw
  11. dec eax
  12. mov edx,0
  13. mov ecx,3
  14. div ecx
  15. .while (BYTE PTR[esi]!='.')
  16. mov bx,[esi]
  17. mov [esi+eax],bx
  18. dec esi
  19. .endw
  20. mov bx,[esi]
  21. mov [esi+eax],bx
  22. dec esi
  23. mov ecx,0
  24. .while (eax!=0)
  25. .if(ecx<3)
  26. mov bx,[esi]
  27. mov [esi+eax],bx
  28. inc ecx
  29. .else
  30. mov BYTE PTR[esi+eax],','
  31. dec eax
  32. mov ecx,1
  33. .endif
  34. dec esi
  35. .endw
  36. lea esi,Output
  37. .while (BYTE PTR[esi]!=0)
  38. mov bx,[esi]
  39. inc esi
  40. .endw
  41. ret
  42. PackNum endp

2.5.4.2 UnpackNum

UnpackNum函数将进行数字分组输出的字符串Output解分组。它首先获取Output地址存在esi中,然后ecx赋0,并将Output中字符向前移动ecx个单位,遇见‘,’字符则将ecx加1,直到字符串结束。

函数的流程图及代码如下:

  1. UnpackNum proc USES ecx
  2. lea esi,Output
  3. mov ecx,0
  4. .while (BYTE PTR[esi+ecx]!=0)
  5. .if(BYTE PTR[esi]==",")
  6. inc ecx
  7. .endif
  8. mov bx,[esi+ecx]
  9. mov [esi],bx
  10. inc esi
  11. .endw
  12. ret
  13. UnpackNum endp

2.5.4.3 ShowNum

ShowNum函数将Output字符串处理后在文本框中显示出来。它首先调用UnpackNum函数对Output解分组,然后获取Output地址存在esi、edi中,通过循环将Output尾地址存在esi中,将字符‘.’地址存在edi中,如果edi等于esi则表明Output中无字符‘.’,则在结尾添加字符‘.’。如果IsPacked等于1则对Output调用UnpackNum函数对其分组,最后向文本框发送WM_SETTEXT消息显示数据。

函数的流程图及代码如下:

  1. ShowNum proc
  2. invoke UnpackNum
  3. lea esi,Output
  4. lea edi,Output
  5. .while (BYTE PTR[esi]!=0)
  6. inc esi
  7. .endw
  8. .while (BYTE PTR[edi]!='.') && (edi<esi)
  9. inc edi
  10. .endw
  11. .if esi==edi
  12. mov BYTE PTR[esi],'.'
  13. mov BYTE PTR[esi+1],0
  14. .endif
  15. .if IsPacket==1
  16. invoke PackNum
  17. .endif
  18. invoke SendMessage,hEdit,WM_SETTEXT,0,addr Output
  19. ret
  20. ShowNum endp

2.5.4.4 BtnNum

BtnNum函数响应数字按钮消息,向文本框中添加字符。

函数源代码如下:

  1. BtnNum proc USES eax,Num:DWORD
  2. lea esi,Output
  3. mov eax,Num
  4. sub eax,252
  5. .if IsStart==1
  6. mov [esi],eax
  7. inc esi
  8. mov BYTE PTR[esi],'.'
  9. inc esi
  10. mov BYTE PTR[esi],0
  11. mov IsStart,0
  12. .else
  13. .while BYTE PTR[esi]!='.'
  14. inc esi
  15. .endw
  16. .if HasPoint==1
  17. .while BYTE PTR[esi]!=0
  18. inc esi
  19. .endw
  20. mov [esi],ax
  21. inc esi
  22. mov BYTE PTR[esi],0
  23. .else
  24. .if BYTE PTR[Output]=='0'
  25. lea esi,Output
  26. mov [esi],eax
  27. mov BYTE PTR[esi+1],'.'
  28. mov BYTE PTR[esi+2],0
  29. .else
  30. mov [esi],eax
  31. inc esi
  32. mov BYTE PTR[esi],'.'
  33. inc esi
  34. mov BYTE PTR[esi],0
  35. .endif
  36. .endif
  37. .endif
  38. invoke ShowNum
  39. ret
  40. BtnNum endp

2.5.4.5 BtnOperator

BtnOperator函数响应运算符按钮消息,进行运算并输出结果。首先判断是否为等号,如果不是则调用GetResult函数先进行一次运算,然后将当前操作符存入Operator变量中。

函数源代码如下:

  1. BtnOperator proc USES eax
  2. .if HasEqueal!=1
  3. invoke GetResult
  4. .endif
  5. .if eax == ID_MUL
  6. mov Operator,'*'
  7. .elseif eax == ID_DIV
  8. mov Operator,'/'
  9. .elseif eax == ID_SUB
  10. mov Operator,'-'
  11. .elseif eax == ID_ADD
  12. mov Operator,'+'
  13. .endif
  14. mov HasEqueal,0
  15. ret
  16. BtnOperator endp

2.5.4.6 BtnEqual

BtnEqual函数响应等号按钮消息,进行运算并输出结果。首先判断是否为起始状态,如果不是则调用GetResult函数,并将HasEqual变量置1。

函数源代码如下:

  1. BtnEqual proc
  2. .if (IsStart==1) && (HasEqueal==0)
  3. fstp Number
  4. fst Number
  5. fld Number
  6. .endif
  7. invoke GetResult
  8. mov HasEqueal,1
  9. ret
  10. BtnEqual endp

2.5.4.7 GetResult

BtnEqual函数响应等号按钮消息,进行运算并输出结果。首先判断是否为起始状态,如果不是则调用GetResult函数,并将HasEqual变量置1。

函数源代码如下:

  1. GetResult proc USES eax
  2. invoke UnpackNum
  3. finit
  4. .if (IsStart==1) && (HasEqueal==0)
  5. .else
  6. .if HasEqueal!=1
  7. invoke StrToFloat,addr Output, addr Operand
  8. .endif
  9. fld Result
  10. fld Operand
  11. .if Operator=='.'
  12. fst Result
  13. jmp Show
  14. .elseif Operator=='+'
  15. fadd ST(1),ST(0)
  16. .elseif Operator=='-'
  17. fsub ST(1),ST(0)
  18. .elseif Operator=='*'
  19. fmul ST(1),ST(0)
  20. .elseif Operator=='/'
  21. fldz
  22. fcomi ST(0),ST(1)
  23. jnz NotZero
  24. mov IsError,1
  25. invoke SendMessage,hEdit,WM_SETTEXT,0,addr Div0
  26. ret
  27. NotZero: fstp Operand
  28. fdiv ST(1),ST(0)
  29. .endif
  30. fstp Operand
  31. fst Result
  32. Show: mov IsStart,1
  33. mov HasPoint,0
  34. invoke FloatToStr2,Result,addr Output
  35. invoke ShowNum
  36. .endif
  37. ret
  38. GetResult endp

2.5.4.8 ShowTextM

ShowTextM函数判断Remember中的值是否为0,如果不是是则在标签中显示‘M’,否则清空标签中内容。

函数源代码如下:

  1. ShowTextM proc
  2. fld NumLittle
  3. fldz
  4. fsub Remember
  5. fabs
  6. fcomi ST(0),ST(1)
  7. ja NotZero
  8. invoke SendMessage,hTextM,WM_SETTEXT,0,NULL
  9. jmp PopNumLittle
  10. NotZero:invoke SendMessage,hTextM,WM_SETTEXT,0,addr TextM
  11. PopNumLittle:fstp Operand
  12. fstp Operand
  13. mov IsStart,1
  14. mov HasPoint,0
  15. ret
  16. ShowTextM endp

2.5.4.9 Init

Init函数负责进行必要的初始化操作,如对状态变量的初始化以及的FPU的初始化。

函数源代码如下:

  1. Init proc
  2. mov IsStart,1 ;初始化
  3. mov HasPoint,0 ;清除小数点
  4. mov HasEqueal,0
  5. fldz
  6. fst Number ;清除结果
  7. fst Operand
  8. mov Operator,'.' ;清除运算符
  9. mov IsError,0
  10. finit ;初始化FPU
  11. ret
  12. Init endp

3 参考文献

  1. 《80X86汇编语言程序设计》,王元珍、曹忠升、韩宗芬,华中科技大学出版社,2005
  2. 《Iczelion的Win32汇编教程》
  3. 《Intel汇编语言程序设计(第五版)》,【美】Kip R Irvine,电子工业出版社,2008
  4. 《汇编语言编程艺术》,Randall Hyde,清华大学出版社 ,2005
  5. 《IBM PC汇编语言程序设计(第五版)》,Peter Abel,人民邮电出版社,2002
  6. 《Win32开发人员参考库第五卷:Windows Shell》,David Iseminger,机械工业出版社,2001
  7. 《Microsoft MASM 参考手册》
  8. 《现代操作系统》,【荷】Andrew S. Tanenbaum 机械工业出版社,2009
  9. 《Windows核心编程(第五版)》,【美】Jeffery Richter清华大学出版社,2008
  10. 《Windows程序设计(第五版)》,【美】Charles Petzold,北京大学出版社,1999
  11. 《Intel® 64 and IA-32 Architectures Software Developer’s Manuals》
  12. MSDN Library: http://www.microsoft.com/china/MSDN/library/
上传的附件 cloud_download 基于WIN32 汇编实现的防Windows计算器.zip ( 3.08mb, 44次下载 )
error_outline 下载需要11点积分

发送私信

幸福的绿光在哪里

20
文章数
20
评论数
最近文章
eject