News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Using a timer

Started by Jimg, September 09, 2005, 02:43:34 AM

Previous topic - Next topic

Jimg

The following code was written to provide a repeating process at a given rate while a button is held down with the mouse.
I thought I could just set a timer to the appropriate interval to get the rate I wanted.  What I found out is that the timer created with SetTimer has a granularity of 32 milli seconds.  If I set the interval to 1 millisec to get the fastest rate, I get 63 repetitions per second.  Not quite as fast as I had hoped.  If I set the interval to 2 or 3 or anthing up to 15, I get the same 63 repetitions/second.  If I set the interval from 17 to 31, I get 30 reps/second.  If I set the timer interval from 33 to 46, I get 20 reps/second. 

What's going on here?  And what do you suggest as a replacement for the built in timer?

.686p
.model  flat, stdcall
option  casemap :none   ; case sensitive
.nolist
include windows.inc

inv equ invoke
; some miscellaneous macros
soff Macro QuotedText:Vararg    ; returns offset to a string
Local LocalText
.data
LocalText db QuotedText,0
.code
EXITM <offset LocalText>
Endm

msg Macro qtext:VARARG
pusha
invoke MessageBox,0,soff(qtext),0,0
popa
endm

GetHandle Macro MyId:Req,MyHandle:Req
    invoke GetDlgItem,hWin,MyId
    mov MyHandle,eax         ; save handle to control
endm

uselib  MACRO   libname
    include     libname.inc
    includelib  libname.lib
ENDM

uselib user32
uselib kernel32
uselib comctl32

uselib shell32
;uselib comdlg32
;uselib gdi32
uselib masm32   ; for debug
uselib debug    ; for debug

.listall

DlgProc     PROTO :DWORD,:DWORD,:DWORD,:DWORD

.data?
buff  db 100 dup (?)    ; scratch
hWin  dd ?
hlab  dd ?  ; handle to label for test print
hbut  dd ?  ; handle to button being subclassed
hEd   dd ?  ; handle to edit box

OldButtonWnd dd ?
TimerVal     dd ?   ; number of tics to use for timer
TimerCount   dd ?   ; count timer ticks while pressing button
ButtonHandle dd ?   ; used as flag for timer, etc.
ET           dd ?   ; elapsed time in millisecs
.data
ALIGN 8
freq   dq 0
count1 dq 0
count2 dq 0
count3 dd 0
.code
Program:
    invoke  InitCommonControls
    invoke  GetModuleHandle, NULL
    invoke  DialogBoxParam, eax, 101, 0, ADDR DlgProc, 0
    invoke  ExitProcess, eax

DlgProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
    .if uMsg==WM_COMMAND
        mov eax,wParam  ; identifier of the control, or accelerator
        mov edx,eax     ; check hiword (notification code)
        shr edx,16
        .if ax == 1004
            .if dx == EN_CHANGE
                inv GetDlgItemInt,hWin,1004,0,TRUE
                mov TimerVal,eax
            .endif
        .endif
       
    .elseif uMsg== WM_INITDIALOG
        mov eax,hWnd    ; save handle for everyone
        mov hWin,eax
        GetHandle 1002,hlab
        GetHandle 1001,hbut
        GetHandle 1004,hEd
        ButtonWndProc proto :dword,:dword,:dword,:dword
        inv SetWindowLong,hbut,GWL_WNDPROC,ButtonWndProc
        mov OldButtonWnd,eax
        mov ButtonHandle,0  ; timer not running
        inv GetDlgItemInt,hWin,1004,0,TRUE
        mov TimerVal,eax
        inv SetFocus,hlab    ; get the focus off the button
    .elseif uMsg==WM_TIMER
        .if ButtonHandle!=0
            ; do something here each tick
            inc TimerCount
        .else
            invoke QueryPerformanceCounter, ADDR count2
            invoke QueryPerformanceFrequency, ADDR freq
            mov ecx,DWORD PTR count1
            sub DWORD PTR count2, ecx
            mov ecx, DWORD PTR count1 + 4
            sbb DWORD PTR count2 + 4, ecx
       
            finit
            fild  count2
            fild  freq
            fdiv
            mov   count3,1000
            fild  count3
            fmul
            fistp count3
            mov   eax, count3

            mov ET,eax
            mov edx,wParam  ; handle of the button pushed
            inv KillTimer,hWin,edx
            mov eax,TimerCount
            mov ecx,1000
            mul ecx
            div ET
            inv wsprintf,addr buff,soff("counts=%li    millisec=%li counts/sec=%li"),TimerCount,ET,eax
            inv SendMessage,hlab, WM_SETTEXT, 0,addr buff
        .endif 
    .elseif uMsg == WM_CLOSE
        invoke  EndDialog,hWin,0
    .endif

    xor eax,eax
    ret
DlgProc endp

ButtonWndProc PROC hEdit:DWord,uMsg:DWord,wParam:DWord,lParam:DWord
    mov edx,hEdit
    .if uMsg==WM_LBUTTONDOWN || uMsg==WM_LBUTTONDBLCLK ; was a button clicked?
        mov ButtonHandle,edx    ; pressed
        mov TimerCount,0
        inv SendMessage,hlab,WM_SETTEXT,0,soff("Testing")
        invoke QueryPerformanceCounter, ADDR count1
        inv SetTimer,hWin,edx,TimerVal,0    ; use buttonhandle as timer id, 1000 times/second
    .elseif uMsg==WM_LBUTTONUP
        mov ButtonHandle,0
        call MakeButtonLookNormal
    .else
        inv CallWindowProc,OldButtonWnd,hEdit,uMsg,wParam,lParam
    .endif 
    ret
ButtonWndProc EndP

MakeButtonLookNormal proc   ; call with handle of control in edx
    inv SendMessage, edx, BM_SETSTYLE, BS_PUSHBUTTON, TRUE  ; make button look normal
    inv SetFocus,hlab    ; get the focus off the button
    ret
MakeButtonLookNormal EndP

end Program



[attachment deleted by admin]

MichaelW

On my system the timer created by SetTimer appears to have a nominal granularity of 10ms.



[attachment deleted by admin]
eschew obfuscation

Jimg

Wierd.  Using your program, I get a granularity of 16 ms using xp sp2, athlon cpu.  I'm suprised.  I guess I expected better performance out of a cpu running at 2 ghz.  Thanks Michael.

hutch--

Its a brave man who tries to use a normal timer for very fine resolution. Usually you use a multimedia timer if you need anything like accuracy for fast intervals.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

dioxin

Jimg,
Timers in Windows change granularity according to circumstances so one application using a fine resolution timer can cause other timers in other applications to behave differently.
Also, the WM_TIMER message is a very low priority and doesn't get sent to your application if anything else is going on so it's not good for timing important events.

You're better off using the multimedia timers, see TimeSetEvent and TimeKillEvent at msdn.

Paul.

Jimg

Thank you Paul, I never heard of TimeSetEvent, that's one more new thing I learned today!

P1

Jimg,

Another technique you should be looking at is to start a thread and give it a High Priority for the most attention from Windows.  Another wise, depending on what is happenning in Windows, your process/thread could suffer from lack of focus.

Regards,  P1  :8)