flat assembler
Message board for the users of flat assembler.

Index > Windows > sort of creative way to handle your window message :)

Author
Thread Post new topic Reply to topic
vbVeryBeginner



Joined: 15 Aug 2004
Posts: 884
Location: \\world\asia\malaysia
vbVeryBeginner 22 Dec 2004, 11:32
hi,
presented is another method to handle long winded window messages.
if you had feel sick with current method, you could try my method :p

Code:
proc proc_window,hWnd,uMsg,wParam,lParam
  push ebx esi edi
            cmp  [uMsg],WM_COMMAND
              je   .wmCOMMAND
             cmp  [uMsg],WM_INITMENUPOPUP
                je   .wmINITMENUPOPUP
               cmp  [uMsg],WM_CREATE
               je   .wmCREATE
              cmp  [uMsg],WM_SIZE
         je   .wmSIZE
                cmp  [uMsg],WM_CLOSE
                je   .wmCLOSE
               cmp  [uMsg],WM_DESTROY
              je   .wmDESTROY
             cmp  [uMsg],WM_SETFOCUS
             je   .wmSETFOCUS

        .wmDEFAULT:
             invoke  DefWindowProc,[hWnd],[uMsg],[wParam],[lParam]
                       jmp  .wmBYE
    


well, one problem with above method is they grab a lot of attention in editor when they are simply not the focus :p

well, it is time to have a new method Smile

Code:
proc proc_message,msg,msgAddr
  push ebx
    mov  eax,[msg]
      mov  ebx,[msgAddr]
@@:
       cmp  dword [ebx],0
  je   .msgDEFAULT
    cmp  [ebx],eax
      pushf
       add  ebx,4
  popf
        je   @f
     add  ebx,4
  jmp  @b

@@:
      pop  eax
    leave
       jmp  dword [ebx]

.msgDEFAULT:
    pop  ebx
    return
endp

proc proc_window,hWnd,uMsg,wParam,lParam
jmp  @f
.msg       dd WM_COMMAND,          .wmCOMMAND
  dd WM_INITMENUPOPUP,    .wmINITMENUPOPUP
    dd WM_CREATE,           .wmCREATE
   dd WM_SIZE,             .wmSIZE
     dd WM_CLOSE,            .wmCLOSE
    dd WM_DESTROY,          .wmDESTROY
  dd WM_SETFOCUS,         .wmSETFOCUS
 dd 0
@@:
             push ebx esi edi
    stdcall proc_message,[uMsg],.msg

        .wmDEFAULT:
             invoke  DefWindowProc,[hWnd],[uMsg],[wParam],[lParam]
                       jmp  .wmBYE
    


so, what do you think?
the new method would slightly increase ur executable run-time instruction count, i hope you won't mind :p
Post 22 Dec 2004, 11:32
View user's profile Send private message Visit poster's website Reply with quote
roticv



Joined: 19 Jun 2003
Posts: 374
Location: Singapore
roticv 22 Dec 2004, 11:50
I don't think the pushf and popf makes the code look nice

Code:
        push ebx
        mov  eax,[msg]
        mov  ebx,[msgAddr]
        add ebx.4
@@:
        cmp  dword [ebx-4],0
        je   .msgDEFAULT
        cmp  [ebx-4],eax
        je   @f
        add  ebx,8
        jmp  @b

    
Post 22 Dec 2004, 11:50
View user's profile Send private message Visit poster's website MSN Messenger Reply with quote
f0dder



Joined: 19 Feb 2004
Posts: 3175
Location: Denmark
f0dder 22 Dec 2004, 12:08
pushf and popf are pretty slow instructions, aren't they?

It's not good to mix code and data, put your table in a read-only data section instead. Even when having the table in the code section, there's no reason to "jmp @f" to skip it, put the table *after* the dispatching code.

Furthermore, why have a "proc_message" rather than inlining the code in the wndproc handler? And you don't have to do register preservation either, as long as you only use eax,ecx,edx.

... Smile
Post 22 Dec 2004, 12:08
View user's profile Send private message Visit poster's website Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3499
Location: Bulgaria
JohnFound 22 Dec 2004, 12:09
You definately have to see Fresh library "msgutils.asm" and "msgutils.inc" - especially the procedure JumpTo. Also, check how to use it for example in "MainWindow.asm" line 45 and down.

btw:
Code:
pushf
add  ebx, 4
popf
    


is equal to:
Code:
lea   ebx, [ebx+4]
    


Regards.
Post 22 Dec 2004, 12:09
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
IronFelix



Joined: 09 Dec 2004
Posts: 141
Location: Russia, Murmansk region
IronFelix 22 Dec 2004, 14:21
I have seen another way of message handling in MASM examples. It is the fastest way, i think, but it takes more memory and require initialization of this memory.
Here it is:

First, you declare array of dwords of 1024 uninitialized elements:

DispatchArray rd 1024

Second, you initialize it with the default value before window creation:

push edi
mov edi,DispatchArray
mov eax,WndProc.default
mov ecx,1024
rep stosd
pop edi

(not the fastest way, i agree)

Third, you put your label addresses into this array (also before window creation):

mov [DispatchArray+WM_COMMAND*4],WndProc.WM_COMMAND
mov [DispatchArray+WM_CLOSE*4],WndProc.WM_CLOSE
mov [DispatchArray+WM_DESTROY*4],WndProc.WM_DESTROY
and so on...

Fourth, your WndProc looks like this:

proc WndProc,hWnd,uMsg,wParam,lParam

mov eax,[uMsg]
cmp eax,1024
jae .default
jmp dword [DispatchArray+eax*4]

.WM_COMMAND:
; do something

jmp .msg_processed

.WM_CLOSE:
; do something

jmp .msg_processed

.WM_DESTROY:

; do something

jmp .msg_processed

; and so on ...

.default:

invoke DefWindowProc,[hWnd],eax,[wParam],[lParam]
jmp .exit

.msg_processed: xor eax,eax
.exit:

endp

Such message handling mechanism allow to handle only system messages (uMsg up to 1024), but at very high speed because of only 1 comparison is needed.
Post 22 Dec 2004, 14:21
View user's profile Send private message Reply with quote
f0dder



Joined: 19 Feb 2004
Posts: 3175
Location: Denmark
f0dder 22 Dec 2004, 16:20
Felix, that method takes up a lot of space and isn't too good for cache... it's okay for a limited range where you need most of the cases handled (ie, processing byte-sized data and having to do code for most of the values), and isn't too good for cache. For a wndproc handler, you might as well load the uMsg into EAX, and do a bunch of CMP+JE. It can be macroized so it's no uglier than the table approach. Or, if you have a lot of messages to handle, structure CMP+JCC sequences to split the compares into a binary search. That way you could search up to 2**32 values with, iirc, a max of 8 compares.
Post 22 Dec 2004, 16:20
View user's profile Send private message Visit poster's website Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3499
Location: Bulgaria
JohnFound 22 Dec 2004, 16:46
IMHO, the only good optimization of message handling procedure is for size, not for speed. The message flow is very slow, so any speed optimisation in this procedure will not influent the total program speed at all.
On the other hand, the method used in Fresh standard library is proved to be the smalest one it uses only (IIRC) 4 bytes per message + 5bytes call + 18 bytes JumpTo procedure.

Regards.
Post 22 Dec 2004, 16:46
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
vbVeryBeginner



Joined: 15 Aug 2004
Posts: 884
Location: \\world\asia\malaysia
vbVeryBeginner 22 Dec 2004, 18:01
hi,
roticv enhancement is nice Smile

fodder wrote:

why have a "proc_message"

yup, u have the logic
Code:
proc_msg2:
        pop  edx
    pop  eax
    pop  ebx
    add  ebx,4
@@:
       cmp  dword [ebx-4],0
        je   msg2_DEFAULT
   cmp  [ebx-4],eax
    je   @f
     add  ebx,8
  jmp  @b
@@:
  jmp  dword [ebx]

        msg2_DEFAULT:
           jmp  edx

...
...
          push .msg
           push [uMsg]
         push .wmDEFAULT
             jmp  proc_msg2
    

and save some instruction


johnfound:
just knew that the idea have something alike with yours, and yours is better Smile
thanks for the
lea ebx, [ebx+4]
Post 22 Dec 2004, 18:01
View user's profile Send private message Visit poster's website Reply with quote
vbVeryBeginner



Joined: 15 Aug 2004
Posts: 884
Location: \\world\asia\malaysia
vbVeryBeginner 22 Dec 2004, 18:31
back to traditional,
it is possible to use only MACRO based on:
Code:
macroName
.msg    dd WM_COMMAND,          .wmCOMMAND 
        dd WM_INITMENUPOPUP,    .wmINITMENUPOPUP 
        dd WM_CREATE,           .wmCREATE 
        dd WM_SIZE,             .wmSIZE 
        dd WM_CLOSE,            .wmCLOSE 
        dd WM_DESTROY,          .wmDESTROY 
        dd WM_SETFOCUS,         .wmSETFOCUS 
        dd 0 
    


to have below result;

Code:
cmp  [uMsg],WM_COMMAND
je   .wmCOMMAND
cmp  [uMsg],WM_INITMENUPOPUP
je   .wmINITMENUPOPUP
...
    


and i guess, that is the fastest :p
Post 22 Dec 2004, 18:31
View user's profile Send private message Visit poster's website Reply with quote
f0dder



Joined: 19 Feb 2004
Posts: 3175
Location: Denmark
f0dder 22 Dec 2004, 20:41
vbVeryBeginner, I would do "mov eax, [uMsg]" first, to avoid stack access on each CMP. You could also load wParam and lParam into registers for somewhat shorter+faster code in the handlers. There's a lot of nifty small tricks to do. All sort of pointless (except for coding joy) for a wndproc, but they could matter for other kinds of message handling.
Post 22 Dec 2004, 20:41
View user's profile Send private message Visit poster's website Reply with quote
IronFelix



Joined: 09 Dec 2004
Posts: 141
Location: Russia, Murmansk region
IronFelix 23 Dec 2004, 09:52
Thanks, fOdder, for your advice, but please explain, how to use binary search in message handling?
Some words about CMP: according to the Intel documentation (don't know exactly about AMD), there are 2 kinds of branch prediction algoritms: static and dynamic. In case of our WndProc i think last one is not used, because our cached jump addresses are excluded by Windows mesage handling routines. So, static branch prediction algoritm is used. And in this case there are 3 rules (as far as i remember):
1. Unconditional jump (JMP and CALL) will be done in any direction (backward and forward).
2. Conditional jump (Jxxx) forward will not be done.
3. Conditional jump (Jxxx) backward will be done.

That's why i try to avoid a lot of comparisons - wrong branch prediction results in speed decrease (and not so small decrease).
And finally a little suggestion for discussion:

cmp eax,WM_CLOSE
je .WM_CLOSE
cmp eax,WM_DESTROY
je .WM_DESTROY
cmp eax,WM_MOUSEMOVE
je .WM_MOUSEMOVE

invoke DefWindowProc ...

May be it is faster (but not so convinient of course):

cmp eax,WM_MOUSEMOVE
jne .WM_CLOSE

.WM_MOUSEMOVE;
; process WM_MOUSEMOVE
jmp .default

.WM_CLOSE:
cmp eax,WM_CLOSE
jne .WM_DESTROY

jmp .default

... and so on

I mean (and it is written n documentation), that it will be faster if we put more often executed piece of code first. In case CMP+JE we always make our default handler most often used.

Thanks.
Post 23 Dec 2004, 09:52
View user's profile Send private message Reply with quote
f0dder



Joined: 19 Feb 2004
Posts: 3175
Location: Denmark
f0dder 23 Dec 2004, 12:41
Quote:

how to use binary search in message handling?

The basic idea behind a binary search is that you can cut the amount of remaining searches in half for each branch you do. The general Binary Search algorithm is well described and available on the net. As for using it for message handling, it's *not* something you want to code by hand, it's annoying to maintain (each time you add a new message to be handled, you need to re-construct/balance the "search tree" and re-output all the CMP/Jxx pairs). If you want to do it, code a tool to generate the code Smile. For MASM, bitRAKE coded a macro that generates *almost* as good code as what a C compiler would output.

And sorry, it's not 8 compares for a 32bit input. It's calculated as log(n)/log(2), which is log(2**32)/log(2), and of course effectively ends up being 32 compares. This sure beats a linear search, and of course this is if you want to handle all 2**32 possible values. If you only have, for instance, 432 messages to handle, you can do with about 9 compares.

It still doesn't matter too much for a windows message handler, but it certainly is more compact than a direct jumptable and it requires less compares/iterations than searching a table. Of course a table could also be searched with binary search... there's a lot of possibilities Smile

Btw, for branch prediction stuff with the usual "lots of CMP/Jcc sequences", you could put the wndproc code after all the message handling routines, which would make all the JE's point backwards. You could even combined this with the binary search method, so that the VERY most encountered messages (perhaps five or so of them?) would be handled with CMP+JE pairs, and everything else would be binary-searched.

Again-again-again, this all doesn't matter for a wndproc... but it's nice to keep in mind for other kinds of diverse branch-on-value situations where you are *not* limited by the speed of the message handling code in windows.
Post 23 Dec 2004, 12:41
View user's profile Send private message Visit poster's website Reply with quote
IronFelix



Joined: 09 Dec 2004
Posts: 141
Location: Russia, Murmansk region
IronFelix 23 Dec 2004, 14:23
Ok, fOdder, you have convinced me. Message handler must be small only. Now i think that

cmp eax,WM_xxx
je .WM_xxx

.WM_xxx:
....

jmp .msg_processed

is a very good solution.

Thanks.
Post 23 Dec 2004, 14:23
View user's profile Send private message Reply with quote
Madis731



Joined: 25 Sep 2003
Posts: 2139
Location: Estonia
Madis731 23 Dec 2004, 14:25
You lack some math Very Happy
ok sorry, but I must correct you *a bit*.
Binary search always gives you a result in n-times where n is in formula 2^n where 2^n is the nearest upper power of 2 of searced array elements.
OK, an example: you have 432 messages -> nearest power of 2 is 512 where 2^n=512 => n=9 (you were right there).
Post 23 Dec 2004, 14:25
View user's profile Send private message Visit poster's website Yahoo Messenger MSN Messenger Reply with quote
f0dder



Joined: 19 Feb 2004
Posts: 3175
Location: Denmark
f0dder 23 Dec 2004, 16:00
IronFelix, the method used in Fresh is even smaller, as mentioned by JohnFound. And it's pretty cute. An okay solution for wndproc dispatching, where speed isn't very important.

Thanks for the correction, Madis. Might be a minor detail, but still Smile
Post 23 Dec 2004, 16:00
View user's profile Send private message Visit poster's website Reply with quote
vbVeryBeginner



Joined: 15 Aug 2004
Posts: 884
Location: \\world\asia\malaysia
vbVeryBeginner 28 Dec 2004, 00:22
oh man,
it is so simple yet i hope somebody to write it for me :-p
i should have explore macroinstruction earlier :-p

Code:
macro m_message [msg, msgHandler] {
    cmp  eax,msg
        je   msgHandler
}

; example usage
; =============
 mov  eax,[uMsg]
m_message \
     WM_SIZE,        .wmSIZE,        WM_CREATE,      .wmCREATE,\
        WM_SETFOCUS,    .wmSETFOCUS,    WM_CLOSE,       .wmCLOSE,\
 WM_DESTROY,     .wmDESTROY
    


this add one more parameter, so that it could compare based on supplied register.
Code:
macro m_message reg, [msg, msgHandler] {
 cmp  reg,msg
        je   msgHandler
}
    
Post 28 Dec 2004, 00:22
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.