flat assembler
Message board for the users of flat assembler.
Index
> Windows > sort of creative way to handle your window message :) |
Author |
|
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 |
|||
22 Dec 2004, 11:50 |
|
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. ... |
|||
22 Dec 2004, 12:08 |
|
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. |
|||
22 Dec 2004, 12:09 |
|
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. |
|||
22 Dec 2004, 14:21 |
|
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.
|
|||
22 Dec 2004, 16:20 |
|
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. |
|||
22 Dec 2004, 16:46 |
|
vbVeryBeginner 22 Dec 2004, 18:01
hi,
roticv enhancement is nice fodder wrote:
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 thanks for the lea ebx, [ebx+4] |
|||
22 Dec 2004, 18:01 |
|
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 |
|||
22 Dec 2004, 18:31 |
|
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.
|
|||
22 Dec 2004, 20:41 |
|
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. |
|||
23 Dec 2004, 09:52 |
|
f0dder 23 Dec 2004, 12:41
Quote:
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 . 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 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. |
|||
23 Dec 2004, 12:41 |
|
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. |
|||
23 Dec 2004, 14:23 |
|
Madis731 23 Dec 2004, 14:25
You lack some math
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). |
|||
23 Dec 2004, 14:25 |
|
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 |
|||
23 Dec 2004, 16:00 |
|
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 } |
|||
28 Dec 2004, 00:22 |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.