Search didn't gave any relevant result and I am still puzzled why all tutorial examples has the same message pump?
I have two major questions here. And both are logical issues.
So, here's the "message loop" as presented in all tutorial examples I have laid my eyes on:
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
...
.WHILE TRUE
INVOKE GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
INVOKE TranslateMessage, ADDR msg
INVOKE DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
First question:
Is there any specific reason to put wc, hwnd and msg as LOCAL variable on stack?
Does this makes application faster, more stable or anyway better? Why is it so good to use stack?
Because this works fine:
.DATA?
msg MSG <>
hwnd dd ?
Is there any benefit to use LOCAL to allocate those variables and structures? And is there any benefit if you avoid using stack?
Second question:
What is the reason to use ADDR msg in every INVOKE?
Does WinXP somehow moves msg STRUCT without our knowledge so we have to use ADDR always?
Because this also works:
.DATA?
msg MSG <>
msg1 dd ?
.CODE
lea eax,msg
mov msg1,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
...
.WHILE TRUE
INVOKE GetMessage, msg1,NULL,0,0
.BREAK .IF (!eax)
INVOKE TranslateMessage, msg1
INVOKE DispatchMessage, msg1
.ENDW
mov eax,msg.wParam
ret
WinMain endp
Am I right to say that INVOKE DispatchMessage, ADDR msg will always assemble as:
lea eax,msg
push eax
call DispatchMessage
And INVOKE DispatchMessage, msg1 will assmeble as:
push msg1
call DispatchMessage
If this works and if it looks 'nicer', where is the catch? I see that first Invoke is almost exactly like C/C++. And second is "optimized" and more like asm. So what's wrong with it in a long run?
I am newbie, but I would love to learn about theory of asm and Win OS also :)
Especially if I'll learn WHY are some things like they are :cheekygreen:
Quote from: CoR on January 31, 2007, 06:19:43 AM
First question:
Is there any specific reason to put wc, hwnd and msg as LOCAL variable on stack?
...
Is there any benefit to use LOCAL to allocate those variables and structures? And is there any benefit if you avoid using stack?
This is a long discussion :). What is the benefit of using local variables?
Have a look at this dummy function:
MyStruct STRUCT
FirstMember:DWORD
SecondMember:DWORD
MyStruct ENDS
SomeDummy PROC
LOCAL FirstVar:DWORD
LOCAL FirstStruct:MyStruct
mov FirstVar, 10
mov FirstStruct.SecondMember, eax
ret
SomeDummy ENDP
This will generate the following code:
PUSH EBP ;preserve ebp in the stack
MOV EBP,ESP;store the stack point in ebp
ADD ESP,-0C;make place for our local variables in the stack (4 = SIZEOF FirstVar, 8 = SIZEOF MyStruct)
MOV [EBP-4],0A ;code in our proc
MOV [EBP-8],EAX
LEAVE ;restore esp and ebp
;mov esp, ebp
;pop ebp
RET ;return
If you use global variables (in .DATA or .DATA?), you will have 12 bytes in memory all the time when your exe is running.
The benefit is that local variables are "allocated" at run time, so your exe size will be smaller.
On the other hand, using globals will improve the speed of that function, because there's no need for
PUSH EBP
MOV EBP,ESP
ADD ESP,-0C
.
.
.
LEAVE
RET
and MOV [EBP-8],EAX will be replaced with something like mov [400012], eax, which may improve the speed, also (not shure about this one).
Quote from: CoR on January 31, 2007, 06:19:43 AM
Second question:
What is the reason to use ADDR msg in every INVOKE?
Does WinXP somehow moves msg STRUCT without our knowledge so we have to use ADDR always?
ADDR doesn't have anything to do with the version of windows that you are running. ADDR is a macro that can be used only inside INVOKE, and it computes the offset - in memory - for it's operand.
- MyVar in .DATA, .DATA, .CONST section
PUSH OFFSET MyVar
lea eax, MyVar
push eax
will work, but there's 2 instructions,and there's no need for that.
- MyVar is a local variable
lea eax, MyVar ;lea eax, [ebp-4]
push eax
As a result, your code may become:
.DATA?
msg MSG <>
msg1 dd ?
.CODE
mov msg1, OFFSET msg
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
...
.WHILE TRUE
INVOKE GetMessage, msg1,NULL,0,0
.BREAK .IF (!eax)
INVOKE TranslateMessage, msg1
INVOKE DispatchMessage, msg1
.ENDW
mov eax,msg.wParam
ret
WinMain endp
but you may note that you don't need msg1 (address of msg is a constant that you know)
INVOKE is just a macro like any other macro. It is designed to help you write mode readable code. You may use it or not, it's your choice. Let's take a look at some examples:
INVOKE MyFunction, SomeVar
is same as
push SomeVar
call MyFunction
I see no need to use the longer form.
INVOKE MyFunction, SomeVar, ADDR SomeVar2, ADDR SomeStruct1
is same as
push OFFSET SomeStruct1 ;or lea eax, ... for locals
push OFFSET SomeVar2; or lea eax, ... for locals
push SomeVar
call MyFunction
here, you may do a little speed increase, if both SomeVar2 and SomeStruct1 are locals
using invoke will generate this code
lea eax, SomeStruct1
push eax
lea eax, SomeVar2
push eax
wich you may do like this
lea eax, SomeStruct1
lea ecx, SomeVar2
push eax
push ecx
I use invoke in calls to functions with many parameters, when the values that I want to pass are in registers, are constants (offsets too)
for example (presuming that esi points to the path and name):
INVOKE CreateFile,ADDR FileName,GENERIC_READ,FILE_SHARE_DELETE,NULL,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE
to
sub esp, 6*4 ; six parameters, 4 bytes each
mov dword ptr [esp], esi
mov dword ptr [esp+4],GENERIC_READ
mov dword ptr [esp+8], FILE_SHARE_DELETE
.
.
.
but, I have to admit, the speed increase is not very big.
HTH
Regards,
Nick
CoR,
Your question is a good one, the message loop is fundamental to a Windows GUI application and it works in an interesting way. It is fundamentally a polling loop but with an important difference, it simply stops until GetMessage() returns another message and this is very efficient in terms of processor usage. An app that is not receiving messages uses no processor power at all.
To get it all up and running in a GUI app, you first load a WNDCLASSEX structure wih all of the arguments required to create a window including the address of the message handling procedure, nominally caled the "WndProc". You register the class with Windows then with that class you create a window using CreateWindowEx(). Before the CreateWindowEx() function returns it goes to the "WndProc" once to handle the "WM_CREATE" message then returns with the identifier that is normally called a window handle. You use this handle to interact with the window after it is created.
After any other stuff that you may want to start like menus or similar, you show the window with ShowWindow() then fall into the message loop. The action with the message loop continues until GetMessage() returns ZERO which is normally a programmatically controlled event. Otherwise it polls the operating system with GetMessage() and if nothing is available, it simply sits there until another message becomes available.
Some messages are sent directly by the OS to the WndProc but typically keyboard and a few other messages are returned to the message loop where they can be processed first then dispatched to the WndProc. You will notice that an application never calls a "WndProc". It is only ever called by the OS on the basis of a message needing to be sent to the window. The "WndProc" is called a "callback because of this and it is a reasonably typical technique in Windows for various types of Windows including the ordinary controls like buttons and so on.
A "WndProc" and a control subclass are almost identical except that a control already has a default "WndProc" so when you want to process messages for a control you have to intercept the message stream and then return to the default handler.
ICZ's tutorials(I ain't gonna bother butchering his name) mostly use a WinMain procedure, if you were asking about the point of it....I quite honestly can't see one.
On the other hand I can see why he did it, in another post someone referred to their program code as "code: main", this rational is a fall-out from C users. Me, I just skip the whole WinMain procedure and go directly to the code it uses, basically removing as much LOCAL data as possible in favor of either static(.DATA) or dynamic(.DATA?) data. Even my WNDCLASSEX structures tend to be put in the .Data section so I don't have to bother writing commands just to update data that never changes(I.E. SizeOf WNDCLASSEX).
Not sure if that's what you were asking, but it's how I do things.
Why not a Message Loop with MMX: :lol
.data
wc dd sizeof WNDCLASSEX
dd CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW
dd offset WndProc, 0, 0, 400000h, 0, 0
dd COLOR_WINDOW +1, 0
dd offset ClassName, 0
align 8 ;
MyStack dd offset TranslateMessage ; ret address for GetMessage
dd offset msg ; parameter for GetMessage
hWnd dd 0 ; hWnd ; parameter for GetMessage
dd 0 ; parameter for GetMessage
dd 0 ; parameter for GetMessage
dd offset DispatchMessage ; ret address for TranslateMessage
dd offset msg ; parameter for TranslateMessage
dd offset Mess_Loop ; ret address for DispatchMessage
dd offset msg ; parameter for DispatchMessage
dd offset GetMessage ;
dd 0 ; one dd more for 8 bytes allignment
msg MSG <>
.code
Start:
push IDI_APPLICATION
push 0
call LoadIcon
mov wc.WNDCLASSEX.hIcon, eax
mov wc.WNDCLASSEX.hIconSm, eax
push IDC_ARROW
push 0
call LoadCursor
mov wc.WNDCLASSEX.hCursor, eax
push offset wc
call RegisterClassEx ; Register Main Window Class
push 0 ; Create Main Window
push 400000h
push 0
push 0
push 680
push 800
push 2
push 180
push WS_OVERLAPPEDWINDOW
push offset CaptionText
push offset ClassName
push 0
call CreateWindowEx
mov hWnd, eax
push eax
push SW_SHOWNORMAL
push eax
call ShowWindow
call UpdateWindow
;
mov edi, dword ptr [GetMessage+2] ; edi->jmp address of GetMessage API
mov esi, offset MyStack
mov edi, [edi] ; edi->real address of GetMessage API
add esp, -6*4
movq MM3, [esi+8] ; esi-> offset MyStack
movq MM5, [esi+28]
pxor MM4, MM4
movq [esp], MM5
movq [esp+8], MM3
movq [esp+16], MM4
jmp edi ; edi->real address of GetMessage API
;Message Loop
Align 8 ;
Mess_Loop: ;
add esp, -9*4 ;
movq MM3, [esi+0*8] ; esi-> offset MyStack
movq MM4, [esi+1*8] ;
movq MM5, [esi+2*8] ;
movq MM6, [esi+3*8] ;
movq MM7, [esi+4*8] ;
movq [esp+0*8], MM3 ;
movq [esp+1*8], MM4 ;
movq [esp+2*8], MM5 ;
movq [esp+3*8], MM6 ;
movq [esp+4*8], MM7 ;
jmp edi ; edi->real address of GetMessage API
;
;Exit Message Loop
MLoopExit: ;
;
;FreeMemArray ;
mov ebx, MemArray ;
xor edi, edi ;
mov ecx, [ebx] ;
@@: ;
test ecx, ecx ;
je @f ;
mov [ebx], edi ; edi = 0
add ebx, 4 ;
invoke VirtualFree, ecx, edi, MEM_RELEASE
mov ecx, [ebx] ;
jmp @b ;
@@: ;
invoke VirtualFree, hMemOrdLists, 0, MEM_RELEASE
invoke ImageList_Destroy, hImageList
Invoke HeapFree, hProcHeap, edi, MemArray
push edi ; edi = 0
jmp ExitProcess ; Exit
End Start
Regards,
Lingo
@TomRiddle:
I do not know why, buy I think I am developing a fetish for .DATA?. Lingo and you have some mean wc in .DATA. I like it :)
Another question, does both .DATA and .DATA? have same access time or not? I know it's all the same memory, but does each one have advantage over the other? Eg. retains longer in cache or some weird CPU benefits in speed or addressing or something.
@hutch:
I didn't know that message loop loops (pun:) ONLY if there's message to process. It makes sense optimizing it only if application has some heavy duty message cycle. Like games!
@TNick:
Thanks for the LOCAL example ;) I fully understand it!
But I lost you in second part.
.DATA?
msg MSG <>
.CODE
...
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
...
.WHILE TRUE
INVOKE GetMessage, msg,NULL,0,0
.BREAK .IF (!eax)
INVOKE TranslateMessage, msg
INVOKE DispatchMessage, msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
This code does not work. It assembles without error. But when I start it, window closes immediately. Did I made some mistake or I do need to put mov msg1, OFFSET msg even msg is in .DATA?
I have some trouble understanding wc in .DATA Little help is needed here (http://www.masm32.com/board/index.php?topic=6482.0)
CoR,
With the WNDCLASSEX structure, it is used to hold the arguments for the RegisterClassEx() call that follows it. This is a common technique in windows to load a structure then call a function passing the address of the structure instead of a long list of parameters. You can create the structure either locally on the stack or if its only a demo you can put it in the .DATA section with preloaded values. There is no advantage in the .DATA section method.
With the message loop, its operation is defined by the operating system so the GetMessage(), TranslateMessage() if used and DispatchMessage() calls are not subject to optimisation but if you are processing code from the message loop it is subject to normal code optimisation techniques.
Games comonly hog a large amount of processor time as they do not have to worry about processor slice sharing so they will tend to use the message loop more like an old fashioned polling loop rather than a Windows GUI app that must co-exist with other running applications.
Hutch, man, now you are scaring me :wink What do you mean by "...if its only a demo you can put it in the .DATA section with preloaded values" ?
Will wc. defined in .DATA work same as wc. defined as LOCAL or not? On every comp with Win OS?
What about lingo's wc defined as wc dd param1, param2, ...
You said that there's no advantage to put wc. in .DATA. Is there any downfall or reason not to do things like that?
edit: Same question for "to use or not to use" WinMain? Wrom your experience is there any downfall of writing direct code and skipping WinMain and stack allocatons?
To me it all looks the same. And I deduce that that code should be 100% same in computers eyes. And we all know how deduction can be wrong :lol So better to be save than sorry :wink
I am trying to make some mental picture of what I can and what I can not do with asm. Luckily I decided to make little project and learn things as project develops. Otherwise I would be more than lost. I mean, I am pretty lost right now, but at least I know where am I going and what my next step will be :bg
Huge mistake there ... Wasn't careful! Sorry!
This is a complete example of what I am using to start a new project.
[EDIT]
Now, about your last post...
I had explained in my first post what is the difference between a LOCAL variable and one in the data section. Major difference: you can give a initial value for the one in data section. A variable located in the stack will have an random initial value (depending on what vase on the stack before at that position). From this point of view, it's similar to variables in .DATA? section. Second difference is the reference method. When you write
mov eax, Var01
and Var01 is a local one, the generated code will look like this:
mov eax, [EBP--4]
If it's a Global one (in .DATA section, in .DATA? section or even in .CONST section), it will look like this
mov eax, [04040010]
As I said before, I'm not shore which method is faster.
About using or not WinMain, as you can see in the attached template, I don't use it and I didn't use it at all. Icz tutorial uses this form to make it look like a C program, I think. WinMain is not a callback function and, therefore, Windows doesn't care how you're creating your windows. WndProc, on the other hand, can have any name, but it has to have that particular number of parameters, because it's a callback function.
[/EDIT]
Regards,
Nick
[attachment deleted by admin]
Here's my version of registering a window:
sub ebx,ebx
mov esi,32512
push ebx
call GetModuleHandle
mov hinst,eax
mov edi,eax
push ebx ;hIconSm
push OFFSET frmclass ;lpszClassName
push ebx ;lpszMenuName
push 1 ;hbrBackground
push esi
push ebx
call LoadCursor
push eax ;hCursor
push esi
push ebx
call LoadIcon
push eax ;hIcon
push edi ;hInstance
push ebx ;cbWndExtra
push ebx ;cbClsExtra
push OFFSET frmproc ;lpfnWndProc
push 3 ;style
push 48 ;cbSize
push esp
call RegisterClassEx
add esp,48
I'm going back to the original post because there seems to be little in the way of actual answers in the follow ups. TNick gave a good summary of the differences between .DATA and LOCAL and Hutch gave some good general info on message loops so between them you should be right. I'll give my take on your direct, original, questions.
Quote from: CoR on January 31, 2007, 06:19:43 AM
I am still puzzled why all tutorial examples has the same message pump?
Because Microsoft has suggested this format as the best way to write it. Since they're the ones writing the APIs themselves (and hence constantly benchmarking and testing them), they probably know best.
Quote from: CoR on January 31, 2007, 06:19:43 AM
First question:
Is there any specific reason to put wc, hwnd and msg as LOCAL variable on stack?
Does this makes application faster, more stable or anyway better? Why is it so good to use stack?
...
Is there any benefit to use LOCAL to allocate those variables and structures? And is there any benefit if you avoid using stack?
Yes, though in most cases it has no effect. Hutch mentioned that using the .DATA section will work fine for demos and this is true. However, if you are writing a complicated program involving multiple threads each with their own message loop (aka pump), you will need local variables to ensure that the threads don't overwrite each other's data. The .DATA/.DATA? section is common to all threads belonging to the same process (note that you can't have two message loops on the same thread since each will block).
In terms of speed, the APIs being called involve transistions to kernel mode. There's absolutely nothing you can do to slow these down any more. As soon as GUI is involved, performance becomes largely irrelevant. (Disclaimer: You can obviously block the thread somewhere else waiting for something, ie. an event/semaphore/mutex/file copy/long operation/etc., however, this has nothing to do with where your message loop variables are stored.)
Quote from: CoR on January 31, 2007, 06:19:43 AM
Second question:
What is the reason to use ADDR msg in every INVOKE?
Does WinXP somehow moves msg STRUCT without our knowledge so we have to use ADDR always?
...
If this works and if it looks 'nicer', where is the catch? I see that first Invoke is almost exactly like C/C++. And second is "optimized" and more like asm. So what's wrong with it in a long run?
There's no catch. You can get the address of the structure at the start, store it in another variable (or even ESI/EDI/EBX, which are preserved during API calls) and use that instead. In the long run, the net gain is zero. The API calls are the bottlenecks and there's nothing you can do to speed them up. (In reality, the biggest bottleneck in GUI programming is the user, and NOTHING can speed them up :bg ) If you put the structures in the .DATA/.DATA? section, you can use OFFSET which will assemble to
push DWORD PTR [00410000h] but again, there is nothing to be gained by this when compared to the other performance issues. In general, I believe most people will find
ADDR msg more readable than
pMsg or something similar. (In general, I believe most people never read the code inside a message loop anyway.)
Quote from: CoR on January 31, 2007, 06:19:43 AM
I am newbie, but I would love to learn about theory of asm and Win OS also :)
Especially if I'll learn WHY are some things like they are :cheekygreen:
Good. Do you read Raymond Chen's (http://blogs.msdn.com/oldnewthing) blog? He very often writes about the history of Win32, compatibility issues which MS deal with, reasons for why things are as they are and has been known to flex some pretty awesome debugging muscles (even in assembly).
Cheers,
Zooba :U
Ok Nic, I think I've got full understanding of local and global variables now. On first glance it looks like C but when you see how LOCAL and .DATA are made... ASM really makes a difference.
Though I'll have to read this thread from time to time. Just to plant it in my mind firmly ;) Thanks man :)
So WinMain or not it'll work nice everywhere, any time. That's nice to hear. Zooba, thanks for untangling a lot of my noob problems.
And as far as wc. goes is it OK to say that there's more safety if U allocate it as LOCAL. And nothing else?
p.s. Raymond Chen's blog is nice one :wink
Quote from: CoR on February 02, 2007, 07:52:29 PM
And as far as wc. goes is it OK to say that there's more safety if U allocate it as LOCAL. And nothing else?
Nothing else of note. You also avoid the problems associated with global variables, you get to use the variable names in other places without breaking your message loop, the size of your executable is smaller (by a few bytes, but some people care about those things) if you don't use the .DATA section (.DATA? is okay) and you are able to use the same message loop for different threads (because each thread has their own stack, but there's only one .DATA/.DATA? section for all threads).
Cheers,
Zooba :U
Something I didn't like much about the tutorials was that he used a WinMain procedure, I just usually skip it in favor of having it all in the main Code region.
Here, btw, is my message pump
L_StartLoop:
Invoke GetMessage, Addr St_Msg, Null, 0, 0
JumpIfRegisterIsZero Eax, L_ExitLoop
Invoke TranslateMessage, Addr St_Msg
Invoke DispatchMessage, Addr St_Msg
Jump L_StartLoop
L_ExitLoop:
[EDIT]
On a separate note, re-reading the thread, I get this view:
QuoteLate in the afternoon, CoR is driving to a friends house when he suddenly notices he is lost. Lost? Whatever can he do, when off in the distance like a beacon of hope looms a sign "Masm32 Forum". Pulling to the side of the road a grizzly man in a full length beard appears.
"What can we do for you stranger?", he says adjusting some un-named tool in his hand.
CoR replies, "Well, I was on my way to a friends house, but I got kinda lost, was hoping you could help me."
"Lost eh?", says another patron of the old time Masm32 station, "Well now, let's get you found again."
"Whose lost?", yells a man from behind the pumps.
Words are a great thing aren't they. :eek
[/EDIT]
Thanks zooba, it's all clear now :U
@TomRiddle
Oh yes, I was lost :wink And I am still far from knowledge I need to make dictionary. But I am getting there thanks to you guys :bg
EDIT:
My goal is to learn asm. But in my way I need to have some project with meaning. Sort of a beacon, like you described, to group and gather my thoughts.
TomRiddle,
"Here, btw, is my message pump
...
...
JumpIfRegisterIsZero Eax, L_ExitLoop "
I can't see how you manage the situation
with GetMessage return value = -1
Why?
from MSDN:
Return Value
If the function retrieves a message other than WM_QUIT, the return value is nonzero.
If the function retrieves the WM_QUIT message, the return value is zero.
!!!?
If there is an error, the return value is -1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer. To get extended error information, call GetLastError.
Warning Because the return value can be nonzero, zero, or -1, avoid code like this:
while (GetMessage( lpMsg, hWnd, 0, 0)) ...
The possibility of a -1 return value means that such code can lead to fatal
application errors. Instead, use code like this:
BOOL bRet;
while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Regards,
Lingo
I havent had problems not testing for -1 myself.
MSG:
How does your pointer to an MSG structure become invalid? I don't think it can in a standard message pump unless there are some very serious issues going on, issues that will likely break any code that follows anyways (such as the stack being fubar)
HWND:
He isnt passing an hwnd to GetMessage(). I am guessing its hard for Null to be an invalid window handle since its a special cased on the other side of the GetMessage() call to get grab all messages belonging to the calling thread (regardless of their hwnd)
lingo, is it possible to get the full source code of your MMX message looper? Some parts appear to be missing.
Regards,
Seb
Its been my experience that if there are errors in the code, the app fails before it gets to the message loop independently of whether the message loop tests for -1 as well as zero. This is my preferred message loop.
keystroke equ <1> ; set to ZERO if no key processing
MsgLoop proc
LOCAL msg:MSG
push esi
push edi
xor edi, edi ; clear EDI
lea esi, msg ; Structure address in ESI
jmp jumpin
StartLoop:
IF keystroke
invoke TranslateMessage, esi
ENDIF
invoke DispatchMessage, esi
jumpin:
invoke GetMessage,esi,edi,edi,edi
test eax, eax
jnz StartLoop
mov eax, msg.wParam
pop edi
pop esi
ret
MsgLoop endp
Which disassembles to,
00401541 fn_00401541: ; Xref 004014D4
00401541 55 push ebp
00401542 8BEC mov ebp,esp
00401544 83C4E4 add esp,0FFFFFFE4h
00401547 56 push esi
00401548 57 push edi
00401549 33FF xor edi,edi
0040154B 8D75E4 lea esi,[ebp-1Ch]
0040154E EB0C jmp loc_0040155C
00401550 loc_00401550: ; Xref 00401567
00401550 56 push esi
00401551 E8F4040000 call jmp_TranslateMessage
00401556 56 push esi
00401557 E894040000 call jmp_DispatchMessageA
0040155C loc_0040155C: ; Xref 0040154E
0040155C 57 push edi
0040155D 57 push edi
0040155E 57 push edi
0040155F 56 push esi
00401560 E897040000 call jmp_GetMessageA
00401565 85C0 test eax,eax
00401567 75E7 jnz loc_00401550
00401569 8B45EC mov eax,[ebp-14h]
0040156C 5F pop edi
0040156D 5E pop esi
0040156E C9 leave
0040156F C3 ret
Seb,
"Some parts appear to be missing..."
OK, this works: :wink
.686
.mmx
;k3d
.xmm
.model flat, stdcall
option casemap:none,language:stdcall,dotname
assume fs:nothing
include c:\masm32\include\windows.inc
include c:\masm32\include\gdi32.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\Comctl32.inc
include c:\masm32\include\shell32.inc
include c:\masm32\include\advapi32.inc
includelib c:\masm32\lib\gdi32.lib
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\Comctl32.lib
includelib c:\masm32\lib\shell32.lib
includelib c:\masm32\lib\ole32.lib
includelib c:\masm32\lib\advapi32.lib
.data
MemArray dd 0,0,0,0,0,0,0,0,0,0,0,0 ;
hListView dd 0
hMemOrdLists dd 0
hImageList dd 0
hProcHeap dd 0
CaptionText db "Lingo's message loop",0
ClassName db "lingoclass", 0
wc dd sizeof WNDCLASSEX
dd CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW
dd offset WndProc, 0, 0, 400000h, 0, 0
dd COLOR_WINDOW +1, 0
dd offset ClassName, 0
align 8
MyStack dd offset TranslateMessage ; ret address for GetMessage
dd offset msg ; parameter for GetMessage
hWnd dd 0 ; hWnd ; parameter for GetMessage
dd 0 ; parameter for GetMessage
dd 0 ; parameter for GetMessage
dd offset DispatchMessage ; ret address for TranslateMessage
dd offset msg ; parameter for TranslateMessage
dd offset Mess_Loop ; ret address for DispatchMessage
dd offset msg ; parameter for DispatchMessage
dd offset GetMessage ;
dd 0 ; one dd more for 8 bytes allignment
msg MSG <>
.code
Start:
push IDI_APPLICATION
push 0
call LoadIcon
mov wc.WNDCLASSEX.hIcon, eax
mov wc.WNDCLASSEX.hIconSm, eax
push IDC_ARROW
push 0
call LoadCursor
mov wc.WNDCLASSEX.hCursor, eax
push offset wc
call RegisterClassEx ; Register Main Window Class
push 0 ; Create Main Window
push 400000h
push 0
push 0
push 680
push 800
push 2
push 180
push WS_OVERLAPPEDWINDOW
push offset CaptionText
push offset ClassName
push 0
call CreateWindowEx
mov hWnd, eax
push eax
push SW_SHOWNORMAL
push eax
call ShowWindow
call UpdateWindow
;
mov edi, dword ptr [GetMessage+2] ; edi->jmp address of GetMessage API
mov esi, offset MyStack
mov edi, [edi] ; edi->real address of GetMessage API
add esp, -6*4
movq MM3, [esi+8] ; esi-> offset MyStack
movq MM5, [esi+28]
pxor MM4, MM4
movq [esp], MM5
movq [esp+8], MM3
movq [esp+16],MM4
jmp edi ; edi->real address of GetMessage API
;Message Loop
Align 8
Mess_Loop:
add esp, -9*4 ;
movq MM3, [esi+0*8] ; esi-> offset MyStack
movq MM4, [esi+1*8] ;
movq MM5, [esi+2*8] ;
movq MM6, [esi+3*8] ;
movq MM7, [esi+4*8] ;
movq [esp+0*8], MM3 ;
movq [esp+1*8], MM4 ;
movq [esp+2*8], MM5 ;
movq [esp+3*8], MM6 ;
movq [esp+4*8], MM7 ;
jmp edi ; edi->real address of GetMessage API
;Exit Message Loop
MLoopExit:
;FreeMemArray ;
; mov ebx, MemArray ;
xor edi, edi ;
; mov ecx, [ebx] ;
@@: ;
; test ecx, ecx ;
; je @f ;
; mov [ebx], edi ; edi = 0
; add ebx, 4 ;
; invoke VirtualFree, ecx, edi, MEM_RELEASE
; mov ecx, [ebx]
; jmp @b
@@:
; invoke VirtualFree, hMemOrdLists, 0, MEM_RELEASE
; invoke ImageList_Destroy, hImageList
; invoke HeapFree, hProcHeap, edi, MemArray
push edi ; edi = 0
jmp ExitProcess ; Exit
ALign 16 ; [esp+0*4]->ret address, [esp+1*4]->hWnd, [esp+2*4]->uMsg
WndProc: ; [esp+3*4]->wParam, [esp+4*4]->lParam
cmp dword ptr [esp+2*4], WM_NOTIFY
je Lwm_notify
cmp dword ptr [esp+2*4], WM_SIZE
je Lwm_size
cmp dword ptr [esp+2*4], WM_DESTROY ; uMsg
jne DefWindowProc
je MLoopExit ; Exit
Lwm_size:
mov ecx, [esp+4*4] ; lParam
mov eax, ecx ;
and ecx, 0FFFFh ; ecx = height
shr eax, 16
;mov edx, hListView
;invoke MoveWindow, edx, 0, 0, ecx, eax, 1
jmp DefWindowProc
Lwm_notify:
mov edx, [esp+4*4] ; lParam
;mov ecx, hListView
;cmp ecx, [edx].LV_DISPINFO.hdr.hwndFrom
jne DefWindowProc
;....
;....
je DefWindowProc
End Start
Regards,
Lingo
[attachment deleted by admin]
Thank you very much, lingo! :U :clap:
Here is my contribution to useless programming for the day, an SSE message loop.
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
MsgLoop proc
.data
align 16
gma dq 0
mar dq 0
msg MSG <0>
.code
push esi
push edi
sub edi, edi ; clear EDI
mov esi, OFFSET msg ; Structure address in ESI
mov eax, OFFSET gma
mov [eax], esi ; load stack arguments into array
mov [eax+4], edi
mov [eax+8], edi
mov [eax+12], edi
movdqa xmm0, gma ; load aligned array into XMM register
mov eax, OFFSET mar
mov [eax], esi
mov [eax+4], esi
movq mm0, mar ; load array into MMX register
sub esp, 16
jmp jumpin
StartLoop:
sub esp, 24
movntq [esp], mm0
call TranslateMessage
call DispatchMessage
jumpin:
movntdq [esp], xmm0 ; write the 4 stack parameters
call GetMessage
test eax, eax ; test for zero
jnz StartLoop
mov eax, msg.wParam
pop edi
pop esi
ret
MsgLoop endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
You can run this one if the box is late enough wih the smug satisfaction that it may be a pico-second or two faster than a normal one. :bg It builds with any version of ML.EXE from 6.15 upwards.
[attachment deleted by admin]
you are really funny, why dont you use peekmessage instead of getmessage?
optimize the overhead on a proc like getmessage is invain, when all getmessage do is looping until it receives a message
magnus,
You are right but I warned that it was useless. With 3 API calls its a "who cares" with a message loop. :bg
Quote from: daydreamer on February 12, 2007, 05:38:35 AMall getmessage do is looping until it receives a message
Incorrect - GetMessage goes to ring0 immediately, and the thread uses no more than a few cycles on each timeslice (once every 10ms) it's been given - after it sees there're no messages available, it immediately forfeits its timeslice to another thread [actually the kernel suspends it until the thread receives a message, so the overhead of one extra thread-switching is eliminated in the task-scheduler]. You couldn't possibly optimize it further.
Using mmx without "emms" will slow-down or crash the app when an OS-managed GUI object uses the FPU. :toothy
Just to back-up my words, wrote a simple app
main proc
local msg:MSG
invoke GetMessage,addr msg,0,0,0
ret
main endp
start:
invoke main
invoke ExitProcess,0
end start
, and had it run for 15+ minutes. Every several minutes it'd do 4 context-switches in a flash (probably to check timeouts). Otherwise, the thread is constantly in Wait:WrUserRequest state (almost identical to suspended) .
(http://img368.imageshack.us/img368/2797/zzztest2hm5.png)
In comparison, an infinite loop in user-mode of a normal-priority thread takes 400 thread-switches per second, and 95%+ cpu. So, a PeekMessage custom implementation is not recommended for normal GUI apps :wink
I would agree with this comment (So, a PeekMessage custom implementation is not recommended for normal GUI apps) as GetMessage simply does not return if there is no message to be processed by the thread that it is called from. I think what Magnus had in mind was a games type message loop that hogged almost all of the processor to ensure that that games was not laggy or suffered interference from other processes while it was running.
There are probably better ways of doing this and among them is setting the process and/or thread priority but it would depend on theinternal architecture of the game as to how it sequenced events.
Quote from: hutch-- on February 12, 2007, 12:46:52 PM
I would agree with this comment (So, a PeekMessage custom implementation is not recommended for normal GUI apps) as GetMessage simply does not return if there is no message to be processed by the thread that it is called from. I think what Magnus had in mind was a games type message loop that hogged almost all of the processor to ensure that that games was not laggy or suffered interference from other processes while it was running.
There are probably better ways of doing this and among them is setting the process and/or thread priority but it would depend on theinternal architecture of the game as to how it sequenced events.
I shouldnt brought up that C++ cheat here when assembly is superior when it comes to clean alternatives, we should keep most api calls in the main section and put heavy processes in workerthreads, because a workerthread entirely without using calls we have all general 8 regs free to use
I don't under stand how the Lingo MessLoop is making it possible to jxx DefWindowProc four times and there is no label for DefWindowProc: to jump to ... but the code works and i can plainly see with the necked eye a faster Window popping up . Can someone tell me what's going on here. Where are these jumps going to and what is it doing under the hood ? Is there a DefWindowProc under there somewhere ?
ic2, take note of the stack - the parameters are already pushed (hWnd,msg,wParam,lParam), the return address-value points to inside the code of DispatchMessage - so it's better than pushing the same parameters and doing and extra "ret".
DefWindowProc is a Win32 API function.
Thanks Ultrano for explaining the un-seen.
I playing with it to understand what's going on with Olly. But it crash after Olly execute the window "only" when I run the mouse over the window tooltips buttons displaying Close, Maximize, Minimize it crash Windows98 completely. I don't have Olly experience. Maybe that what happen with any program after Olly execute it. Maybe that extra "ret" is needed. If not, I just thought I should mention it.
Anyway Olly do show DefWindowProc being used even when there is no label for it. But I think I see what you mean and will be exploring it all today. WoW ... this is going to be really interesting for my knowledge of ASM. When I come out of this i should really finally be on the ball a whole lot better :) Not to take away from others people efforts around here but Lingo style is .... Awesome!!! .... Thanks CoR
Or maybe it's just an effect of the lack of "emms" ? I don't have a win98 to test it on - could you try the same with a compilation, where there's emms after the group of mmx instructions?
Quotecould you try the same with a compilation, where there's emms after the group of mmx instructions?
I don't have a clue of where to start other than starting all over to learn about dos and work my way up to win32 asm as I should had done in the first place. That show you what i really know . ..
Anyway, if it's don't take much time for you to put it together I can't test it on Win98se and Win95b and post the results in minutes . It will be tested on a Intel Pentium(r) II, 128MB machine. If i see it than I have a idea of how to use it and modify it if i have a problem. Shame on me, all I am is a code monkey. Not a true coder in my mind as of yet... but i can usually modify like a pro when needed.
I did a search here to get an idea of how to use EMMS and see it's as simple as placing the line below the MMX code like you said. I never played with MMX code before and had no idea of what you meant by technical way of speaking. I thought it was a major operation...
Anyway, I put the line under "both" blocks of MMX code and now Olly don't crash anymore at the mouseover but it will now crash if i click the close button. What an amazing improvement. Hummmm going to try to find the other correct places to use EMMS to eliminate the close crash if possible... I may need more guidance, but i will make the effort to read about EMMS and the study the code that i found using it... in a few minutes...