xing59741 发表于 2012-12-6 11:38:16

【学习笔记】方式A的时钟

方式A的时钟.ASM
================================================================================

;程序运行结构
;01:程序建立消息循环
;02:窗口过程中创建窗口发送消息定时器
;03:设置客户区为无效矩形(每秒执行一次)
;04:然后在PAINT消息中开始绘画
; 05:执行_ShowTime子程序来
;06:执行_CalcClockParam子程序计算时钟的位置、大小等参数
;   07:比较客户区宽度和高度,以小的值作为时钟的直径
;08:画时钟圆周上的点
;   09:调用_DrawDot画12个大圆点
;    10:调用_CalcX子程序:计算时钟圆周上某个角度对应的 X 坐标
;    11:调用_CalcY子程序:计算时钟圆周上某个角度对应的 X 坐标
;   12:调用_DrawDot画60个小圆点
;    13:调用_CalcX子程序:计算时钟圆周上某个角度对应的 X 坐标
;    14:调用_CalcY子程序:计算时钟圆周上某个角度对应的 X 坐标
;15:画时钟指针
;   16:调用_DrawLine绘画秒针、计算线条两端的坐标
;   17:调用_DrawLine绘画分针、计算线条两端的坐标
;   18:调用_DrawLine绘画时针、计算线条两端的坐标
; 19:结束_ShowTime子程序
;20:退出
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.386
.model flat, stdcall
option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
includewindows.inc
includeuser32.inc
includelib user32.lib
includekernel32.inc
includelib kernel32.lib
includeGdi32.inc
includelib Gdi32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 等值定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN equ1000h
ID_TIMER equ1
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
hInstance dd?
hWinMain dd?
dwCenterX dd?    ;圆心X
dwCenterY dd?    ;圆心Y
dwRadius dd?    ;半径
.const
szClassName db'精简时钟',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 计算时钟的位置、大小等参数
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_CalcClockParam proc
local @stRect:RECT
invoke GetClientRect,hWinMain,addr @stRect ;获取客户区矩形左上角和右下角
;invoke GetWindowRect,hWinMain,addr @stRect
;获取的是程序的窗口坐标,不是按屏幕坐标
mov eax,@stRect.right    ;将右下角X坐标放入eax
;sub eax,@stRect.left    ;右下角X坐标减去左上角X坐标的值等于矩形宽度
mov ecx,@stRect.bottom   ;将右下角Y坐标放入ecx
;sub ecx,@stRect.top    ;右下角Y坐标减去左上角Y坐标的值等于矩形高度
;*****************************************************************************************************************
; 比较客户区宽度和高度,以小的值作为时钟的直径
;*****************************************************************************************************************
.if ecx > eax   ;高度大于宽度
   mov edx,eax    ;将宽度放入edx寄存器
   sub ecx,eax    ;高度减去宽度的值放入ecx,便于改变大小时,还是在中间
   shr ecx,1    ;将ecx的值除以2的1次方
   mov dwCenterX,0   ;将圆心x置0
   mov dwCenterY,ecx   ;将exc(宽高相差值的一半)放入圆心y
.else    ;高度小于或等于宽度
   mov edx,ecx    ;将高度放入edx寄存器
   sub eax,ecx    ;宽度减去高度然后把差值放入eax,便于改变大小时,还是在中间
   shr eax,1    ;将eax的值除以2的1次方
   mov dwCenterX,eax   ;将差值放入圆心x
   mov dwCenterY,0   ;将0放入放入圆心y
.endif
shr edx,1   ;将距离短的高度或者宽度除以2的1次方
mov dwRadius,edx    ;半径
add dwCenterX,edx    ;圆心x加上半径
add dwCenterY,edx    ;圆心y加上半径
ret      ;得到圆心坐标(x,y)返回
_CalcClockParam endp
_dwPara180 dw 180   ;类型:字
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 计算时钟圆周上某个角度对应的 X 坐标
; X = 圆心X + Sin(角度) * 半径
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_CalcXproc _dwDegree,_dwRadius   ;角度,半径
local @dwReturn    ;存储X坐标结果
fild dwCenterX    ;将圆心x按整型放入st0(浮点栈顶)
fild _dwDegree    ;将角度按整型放入st0(浮点栈顶),别的浮点数据栈+1
fldpi      ;将圆周率压入浮点栈
fmulp st(1),st   ;角度*圆周率(没有操作数的隐含规则:st(1),st(0)),清空参数1和参数2并将结果放入栈顶(st0)
;fistp @dwReturn
;操作完成后st(1)和st(0)会被清空,别的栈会上升,结果保存在st(0)
;如果fmul操作后带有参数,那么栈不会清空,并且把结果存放至参数1
;参数后面加上P代表弹出栈顶,fmulp st(1),st中的结果放在了参数1中,不是st(0)
fild _dwPara180    ;将类型为【字】的数据180按整型放入栈顶
fdivp st(1),st   ;【角度*Pi】/180并把结果存放至st0
;参数1和参数2都会被清空,结果存在栈顶
fsin      ;Sin(角度*圆周率/180)
fild _dwRadius    ;将半径按整型放入st0
fmulp st(1),st(0)   ;半径*Sin(角度*圆周率/180)【隐含操作数:st(1),st(0)】
faddp st(1),st(0)   ;X+半径*Sin(角度*圆周率/180)
fistp @dwReturn    ;将st(0)按整型存储至@dwReturn并将st(0)弹出
mov eax,@dwReturn    ;将数据放入eax
ret
_CalcXendp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 计算时钟圆周上某个角度对应的 Y 坐标
; Y = 圆心Y - Cos(角度) * 半径
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_CalcYproc _dwDegree,_dwRadius   ;;角度,半径
local @dwReturn    ;存储Y坐标结果
fild dwCenterY    ;将圆心y按整型放入栈顶
fild _dwDegree    ;将角度按整型放入栈顶
fldpi      ;将圆周率放入栈顶
fmulp st(1),st(0)   ;(角度*圆周率)st(0)和st(1)相乘后清空st(0)和st(1)并把结果放入栈顶
;参数后面加上P代表弹出栈顶,fmulp st(1),st中的结果放在了参赛1中,不是st(0)
fild _dwPara180    ;将180按整型存入栈顶
fdivp st(1),st   ;参数1除以参数2后清空st(0)和st(1)并将结果放入栈顶
fcos      ;Cos(角度*圆周率/180)
fild _dwRadius    ;将半径存放至栈顶
fmulp st(1),st(0)   ;半径*Cos(角度*圆周率/180)【隐含操作数:st(1),st(0)】
fsubp st(1),st   ;参数1减去参数2,将结果存放至st(1),并且将st(0)弹出
fistp @dwReturn    ;将st(0)按照整型存储至@dwReturn并弹出st(0)
mov eax,@dwReturn    ;将数据放入eax
ret
_CalcYendp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 按照 _dwDegreeInc 的步进角度,画 _dwRadius 为半径的小圆点
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_DrawDot proc _hDC,_dwDegreeInc,_dwRadius;设备环境句柄,步进角度,小圆点半径
local @dwNowDegree,@dwR   ;当前角度,局部半径
local @dwX1,@dwY1    ;X和Y坐标
mov @dwNowDegree,0    ;当前角度置0
mov eax,dwRadius    ;将半径放入eax
sub eax,5   ;半径减去5
mov @dwR,eax    ;将运算过的半径放入局部半径
.while @dwNowDegree <= 360   
   finit   ;浮点堆栈清空
;*****************************************************************************************************************
; 计算小圆点的圆心坐标
;*****************************************************************************************************************
   invoke _CalcX,@dwNowDegree,@dwR ;调用_CalcX子程序,参数:当前角度,局部半径
   ;计算时钟圆周上某个角度对应的 X 坐标
   mov @dwX1,eax
   invoke _CalcY,@dwNowDegree,@dwR ;调用_CalcY子程序,参数:当前角度,局部半径
   ;计算时钟圆周上某个角度对应的 Y 坐标
   mov @dwY1,eax   ;eax是Y坐标
   
   mov esi,@dwX1
   add esi,_dwRadius
   push esi
   pop @dwX1
   sub esi,_dwRadius
   sub esi,_dwRadius
   mov edi,@dwY1
   add edi,_dwRadius
   push edi
   pop @dwY1
   sub edi,_dwRadius
   sub edi,_dwRadius
   
   invoke Ellipse,_hDC,esi,edi,@dwX1,@dwY1
   mov eax,_dwDegreeInc   ;步进角度放入eax
   add @dwNowDegree,eax;角度相加
.endw
ret
_DrawDot endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 秒针、分针、时针:画 _dwDegree 角度的线条,半径=时钟半径-参数_dwRadiusAdjust
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_DrawLine proc _hDC,_dwDegree,_dwRadiusAdjust;参数:设备环境句柄,当前的角度,需要校正长度的值
local @dwR   ;存储校正长度的秒针
local @dwX1,@dwY1,@dwX2,@dwY2;局部变量:从(x1,y1)画线到(x2,y2)
mov eax,dwRadius    ;存储半径至eax
sub eax,_dwRadiusAdjust   ;校正指针长度
mov @dwR,eax    ;存储指针长度至局部变量@dwR
;*****************************************************************************************************************
; 计算线条两端的坐标
;*****************************************************************************************************************
invoke _CalcX,_dwDegree,@dwR   ;计算时钟圆周上某个角度对应的 X1 坐标、参数:角度,半径
mov @dwX1,eax    ;将x1放入局部变量@dwX1
invoke _CalcY,_dwDegree,@dwR   ;计算时钟圆周上某个角度对应的 Y1 坐标、参数:角度,半径
mov @dwY1,eax    ;将y1放入局部变量@dwY1
add _dwDegree,180    ;角度增加180度(半个圆)
invoke _CalcX,_dwDegree,8   ;计算时钟圆周上某个角度对应的 X2 坐标、参数:角度,半径:8
mov @dwX2,eax    ;将x2放入局部变量@dwX2
invoke _CalcY,_dwDegree,8   ;计算时钟圆周上某个角度对应的 Y2 坐标、参数:角度,半径:8
mov @dwY2,eax    ;将y2放入局部变量@dwY2
invoke MoveToEx,_hDC,@dwX1,@dwY1,NULL;指定当前新的(x,y)坐标位置
invoke LineTo,_hDC,@dwX2,@dwY2   ;从当前位置画直线到(x,y),超过了圆心点
ret
_DrawLine endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ShowTime proc _hWnd,_hDC
local @stTime:SYSTEMTIME   ;结构体包含系统时间相关
pushad
invoke GetLocalTime,addr @stTime   ;将时间信息返回到@stTime结构体中
invoke _CalcClockParam    ;该子程序计算时钟的位置、大小等参数
;*****************************************************************************************************************
; 画时钟圆周上的点
;*****************************************************************************************************************
invoke CreateSolidBrush,255   ;获取预定义库存中的画笔、刷子、字体、或调色板的句柄
invoke SelectObject,_hDC,eax   ;设置在某个设备环境句柄中使用什么绘图工具
invoke _DrawDot,_hDC,360/12,3   ;该子程序画12个大圆点:设备环境句柄,步进角度,半径
invoke _DrawDot,_hDC,360/60,1   ;该子程序画60个小圆点:设备环境句柄,步进角度,半径
;*****************************************************************************************************************
; 画时钟指针
;*****************************************************************************************************************
invoke CreatePen,PS_SOLID,1,0   ;黑色画笔
;该函数创建一个逻辑画笔,它有指定的样式,宽度和颜色,能被任何DC选择为当前画笔
;nPenStyle :画笔工作的样式
;PS_SOLID:实心笔
;PS_DASH:短线式笔
;PS_DOT   :点式笔
;PS_DASHDOT:点线笔
;PS_DASHDOTDOT:双点线笔
;PS_NULL   :笔不可见
;PS_INSIDEFRAME   :实线线条,和PS_SOLID相同,但是如果画笔宽度大于1像素时,宽度会像内部扩展
;nWidth:指定的宽度(逻辑单位)
;crColor:笔的基准颜色
invoke SelectObject,_hDC,eax   ;设置设备环境使用实心笔来绘画
invoke DeleteObject,eax    ;删除上一次使用的绘画工具
movzx eax,@stTime.wSecond   ;将当前秒值扩展为双字放入eax
mov ecx,360/60    ;将每秒的度数6放入ecx
mul ecx   ;秒针度数 = 秒 * 360/60
invoke _DrawLine,_hDC,eax,5   ;参数:设备环境句柄,当前的角度,需要校正长度的值
;*****************************************************************************************************************
invoke CreatePen,PS_SOLID,2,255   ;创建画笔参数:实心笔、宽度为2、颜色红色
invoke SelectObject,_hDC,eax   ;设置画笔为这个设备环境服务
invoke DeleteObject,eax    ;删除上次的绘画工具
movzx eax,@stTime.wMinute   ;当前分钟值扩展为双字放入eax
mov ecx,360/60    ;将分钟度数为6放入ecx
mul ecx   ;分针度数 = 分 * 360/60
invoke _DrawLine,_hDC,eax,20   ;参数:设备环境句柄,当前的角度,需要校正长度的值
;*****************************************************************************************************************
invoke CreatePen,PS_SOLID,3,250*10000h;创建画笔参数:实心笔、宽度为3、颜色蓝色
invoke SelectObject,_hDC,eax   ;设置画笔为这个设备环境服务
invoke DeleteObject,eax    ;删除上次的绘画工具
movzx eax,@stTime.wHour   ;当前时钟值扩展为双字放入eax
.if eax >= 12    ;如果大于12小时则减去12
   sub eax,12
.endif
mov ecx,360/12    ;将每个时钟间隔度数放入ecx
mul ecx   ;eax = 时钟间隔度数*几点钟(小于<12)
movzx ecx,@stTime.wMinute   ;将当前分钟放入ecx
shr ecx,1   ;将ecx除以2的1次方
add eax,ecx   ;eax = 当前时钟角度加上分钟角度的一半
invoke _DrawLine,_hDC,eax,40   ;参数:设备环境句柄,当前的角度,需要校正长度的值
;*****************************************************************************************************************
invoke GetStockObject,NULL_PEN   ;取得空画笔的句柄
invoke SelectObject,_hDC,eax   ;设置设备环境句柄为空画笔
invoke DeleteObject,eax    ;删除上一次使用的绘画工具
popad
ret
_ShowTime endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcWinMain proc uses ebx edi esi hWnd,uMsg,wParam,lParam
local @stPS:PAINTSTRUCT
mov eax,uMsg
.if eax == WM_TIMER   ;定时器消息
   invoke InvalidateRect,hWnd,NULL,TRUE ;将客户区改变为无效区域,TRUE代表擦除背景
.elseif eax == WM_PAINT   ;绘画窗口的消息
   invoke BeginPaint,hWnd,addr @stPS ;开始绘画,将要绘画的信息放入@stPS结构体中,返回值为设备环境句柄
   invoke _ShowTime,hWnd,eax;执行子程序,参数1为窗口句柄,参数2为设备环境句柄
   invoke EndPaint,hWnd,addr @stPS;结束绘画
.elseif eax == WM_CREATE   ;创建窗口的消息
   invoke SetTimer,hWnd,ID_TIMER,1000,NULL ;创建1s执行一次的定时器
;*****************************************************************************************************************
.elseif eax == WM_RBUTTONUP   ;单击右键,退出
   invoke KillTimer,hWnd,ID_TIMER;清理定时器
   invoke DestroyWindow,hWinMain;摧毁窗口
   invoke PostQuitMessage,NULL;退出消息循环
;*****************************************************************************************************************
.elseif eax == WM_CLOSE   ;退出
   invoke KillTimer,hWnd,ID_TIMER;清理定时器
   invoke DestroyWindow,hWinMain;摧毁窗口
   invoke PostQuitMessage,NULL;退出消息循环
;*****************************************************************************************************************
.else
   invoke DefWindowProc,hWnd,uMsg,wParam,lParam
   ret
.endif
;*****************************************************************************************************************
xor eax,eax
ret
_ProcWinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain proc
local @stWndClass:WNDCLASSEX
local @stMsg:MSG
invoke GetModuleHandle,NULL
mov hInstance,eax
;*****************************************************************************************************************
; 注册窗口类
;*****************************************************************************************************************
invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
invoke LoadIcon,hInstance,ICO_MAIN
mov @stWndClass.hIcon,eax
mov @stWndClass.hIconSm,eax
invoke LoadCursor,0,IDC_ARROW
mov @stWndClass.hCursor,eax
push hInstance
pop @stWndClass.hInstance
mov @stWndClass.cbSize,sizeof WNDCLASSEX
mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW
mov @stWndClass.lpfnWndProc,offset _ProcWinMain
invoke CreateHatchBrush,HS_DIAGCROSS,200*100h ;设置背景颜色
mov @stWndClass.hbrBackground,eax
mov @stWndClass.lpszClassName,offset szClassName
invoke RegisterClassEx,addr @stWndClass
;*****************************************************************************************************************
; 建立并显示窗口
;*****************************************************************************************************************
invoke CreateWindowEx,WS_EX_CLIENTEDGE or WS_EX_TOPMOST,offset szClassName,offset szClassName,WS_OVERLAPPEDWINDOW,550,200,240,250,\
   NULL,NULL,hInstance,NULL
mov hWinMain,eax
invoke ShowWindow,hWinMain,SW_SHOWNORMAL
invoke UpdateWindow,hWinMain
;*****************************************************************************************************************
; 消息循环
;*****************************************************************************************************************
.while TRUE
   invoke GetMessage,addr @stMsg,NULL,0,0
   .break .if eax == 0
   invoke TranslateMessage,addr @stMsg
   invoke DispatchMessage,addr @stMsg
.endw
ret
_WinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
call _WinMain
invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start


方式A的时钟.RC

=========================================================================================

#include<resource.h>
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//宏命令
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#defineICO_MAIN 0x1000   //图标
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN ICON"Main.ico"

因為·有你 发表于 2012-12-6 12:41:07

写得不错啊,赞一个,来坐个沙发吧

xing59741 发表于 2012-12-6 12:49:35

给大家参考吧

夜空.silence 发表于 2012-12-6 12:54:15

赞一个。。。。。
页: [1]
查看完整版本: 【学习笔记】方式A的时钟