News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

error msg?

Started by shankle, June 26, 2012, 12:49:14 AM

Previous topic - Next topic

shankle

; MASM32 This code is working in MASM32
WndProc PROC  hWnd:HWND, iMsg:UINT, wParam:WPARAM, lParam:LPARAM
       mov   eax,iMsg
       .IF eax==WM_CREATE
       
;-------------------------
; This code does not work in GoAsm
WndProc FRAME  hWnd,iMsg,wParam,lParam       
;   test begin
;TM8              db   'begin of wndproc',0
      pushad
      invoke MessageBox, NULL,addr TM8,addr TM8,MB_YESNO     
      popad
;   test end
      mov   eax,[iMsg]
      uses ebx,edi,esi
      Local hMemory,stm:SYSTEMTIME,ps,hdc,hdcPrn

;.WM_CREATE
        cmp D[eax],WM_CREATE
         jne >>.WM_CHAR         
; ----------------------         
        cmp D[eax],WM_CHAR 
        jne >>.WM_PAINT     
;--------------------------       
.WM_PAINT
        cmp D[eax],WM_PAINT 
        jne >>.WM_DESTROY
;------------------------------         
.WM_DESTROY         
        cmp D[eax],WM_DESTROY 
         jne >

What happens is it never gets to WM_Create.
Then gives windows error message: MyProg has stopped working.

If I take my test message out then it gets to WM_create and then gives
a Windows error message : MyProg has stopped working.

If I change cmp D[eax],WM_CREATE to cmp D[iMsg], WM_Create then
it goes into a loop in Wndproc. Have to power down the puter to get out if it.

wjr

Getting there... the code for your procedure should be placed after USES and LOCAL (not sure if your ps should be ps:PAINTSTRUCT). Once you have the value of the message in eax, for the comparisons use eax and not D[eax].

WndProc FRAME hWnd,iMsg,wParam,lParam       
USES ebx,edi,esi
LOCAL hMemory,stm:SYSTEMTIME,ps,hdc,hdcPrn

mov eax,[iMsg]
.WM_CREATE
cmp eax,WM_CREATE
jne >>.WM_CHAR


When you tried cmp D[iMsg], WM_CREATE that was actually correct. Unfortunately, that means there are still some corrections after this point to track down...

satpro


shankle,
I noticed a few things.  GoAsm doesn't use if/then/else syntax.  It is only for conditional assembly.  Using a combination of compares and/or testing flags is the way you have to go.  Anyway, it'll keep you much more focused on the state of the machine.  Knowing the zero, carry, and sign flags inside-out is a must.  Also, please read the GoAsm manual section on stack frames/local variables.  There is a specific order, e.g. FRAME, USES, LOCAL, CODE,RET,ENDF / ENDU.
I tried to see if I could fix your WndProc--it was just too much for me--sorry.  It needs to be simpler--much simpler.  So, I've included the bare bones of one I use.  It is also originally from the manual.  I hope I didn't rip too much out, but it should basically work for you.  The "message compares" use a table with the messages and the addresses of the message handler routines.  If you want to be good at this you'll start using tables for much of what you do, especially math.  Tables are magnitudes faster than anything else because you can branch on flags--and they will always work better than a bunch of slow, sloppy compares that you find in if/then statements.  Sometimes (multiplication) tables can be big.  But so what?  Always better than slow.  And you'll hear guys argue against that point.  They probably didn't program 1 mHz 8-bit computers in the 70s & 80s, when every byte (and every cycle) mattered.  Good assembly language is an art, no doubt about it.  That stuff still matters.  So, be an artist.  Let the "C" guys rely on the fact they have (and need) a 3 gHz machine to make complicated stuff fly.
About saving registers:  if you don't modify them in a procedure don't bother to push/pop them.  Blindly pushing all the registers makes for slow code.  Eventually you'll even start to notice the difference in execution speed.  But, if you "use" them, then by all means you should "USES" them.  That's the good thing about "USES".  You just look through that particular procedure and code a "USES reg,reg,reg" etc for each register used IN THAT PROCEDURE.  The exception is if you return something in, say eax.  You'll wind up losing it if you "USES" it  ;) .  (Did that on purpose)
So, I hope you can use this.  Study how that message table works.  It's old-school, quality assembly--and it's FAST.  And simple.  It's Jeremy Gordon code...


CONST SECTION


;   ******************** Window message table **********************
;
MESSAGES DD WM_CREATE,    CREATE       ;the message then the code address
         DD WM_DESTROY,   DESTROY
         DD WM_PAINT,         PAINT
         DD WM_KEYDOWN,  KEYDOWN
         DD WM_TIMER,        TIMER


CODE SECTION

WndProc:
FRAME hwnd, Msg, wParam, lParam     ;establish stack frame, get params


;--------------------------------------
;FIND THE A MESSAGE WE'RE INTERESTED IN
;--------------------------------------
mov eax, [Msg]                                 ;get in eax message sent by Windows
mov ecx, SIZEOF MESSAGES/8        ;get number of messages to look at
mov edx, ADDR MESSAGES


P1:
dec ecx                                             ;count down to -1
js >.notfound                                     ;if = -1 then call default win procedure


cmp [edx+ecx*8],eax                        ;check table if entry is correct message
jnz <P1                                            ;no, so loop back for next table entry


call [edx+ecx*8+4]                           ;call the correct procedure for the message
jnc >.exit


;----------------------------------------
;NO MATCH, SO PASS PARAMS BACK TO WINDOWS
;USE DEFAULT WINDOWS PROCEDURE
;----------------------------------------
.notfound
push [lParam]
push [wParam]
push [Msg]
push [hwnd]
call DefWindowProc


;------------------------------
;DESTROY STACK FRAME, EXIT BACK TO WINDOWS HERE
;------------------------------
.exit
ret
ENDF


;_________________________________________________________________________
;_________________________________________________________________________


PAINT:                      ;       RE-DRAW WINDOW
USEDATA WndProc                ;use parameters sent to WndProc
USES ebx,ecx,edx,esi,edi    ;only save registers that you use in this procedure
;-------------------------------------------------------------


;---------------
;CALL BEGINPAINT
;---------------
invoke BeginPaint, [hwnd], ADDR ps
mov [hdc], eax


;updating/refreshing the window goes here


;-------------
;CALL ENDPAINT
;-------------
invoke EndPaint, [hwnd], ADDR ps


;---------
;EXIT HERE
;---------
xor eax,eax                 ;return not carry and eax = 0
ret
ENDU                        ;finished using parameters sent to WndProc


;_________________________________________________________________________
;_________________________________________________________________________


CREATE:                     ;       PROGRAM INITIALIZATION
USEDATA WndProc                  ;use parameters sent to WndProc
USES ebx,ecx,edx,esi,edi      ;only save registers that you use in this procedure
;-------------------------------------------------------------



;program init goes here


;---------
;EXIT HERE
;---------
xor eax,eax                 ;return not carry and eax = 0
ret
ENDU                        ;finished using parameters sent to WndProc


;_________________________________________________________________________
;_________________________________________________________________________


DESTROY:                    ;       PROGRAM CLEAN-UP
USEDATA WndProc                            ;use parameters sent to WndProc
USES ebx,ecx,edx,esi,edi                ;only save registers that you use in this procedure
;-------------------------------------------------------------


invoke PostQuitMessage, NULL            ;kill the application


;---------
;EXIT HERE
;---------
xor eax,eax                 ;return not carry and eax = 0
ret
ENDU                        ;finished using parameters sent to WndProc


;_________________________________________________________________________
;_________________________________________________________________________


KEYDOWN:                    ;       PROCESS KEYPRESSES
USEDATA WndProc                            ;use parameters sent to WndProc
USES ebx,ecx,edx,esi,edi                ;only save registers that you use in this procedure
;-------------------------------------------------------------




mov eax, [wParam]


;----------------------
;COMPARE TO ESCAPE KEY?
;----------------------
cmp eax, VK_ESCAPE 
jne >E9


invoke PostQuitMessage, NULL            ;kill the application


;---------
;EXIT HERE
;---------
E9:
xor eax,eax                 ;return not carry and eax = 0
ret
ENDU                        ;finished using parameters sent to WndProc


;_________________________________________________________________________
;_________________________________________________________________________




TIMER:                    ;       PROCESS TIMERS
USEDATA WndProc                                 ;use parameters sent to WndProc
USES ebx,ecx,edx,esi,edi                ;only save registers that you use in this procedure
;-------------------------------------------------------------


;code if you process timers
;I always use timer proc outside the window


;---------
;EXIT HERE
;---------
xor eax,eax                 ;return not carry and eax = 0
ret
ENDU                        ;finished using parameters sent to WndProc




jj2007


dedndave

yah - but you're bloating CPU time that might be used for some other process, nonetheless

satpro

Quote from: jj2007 on July 14, 2012, 06:24:46 PM
Quote from: satpro on July 14, 2012, 12:54:06 PM
Blindly pushing all the registers makes for slow code.

Sounds familiar :biggrin:
And it wasn't that long ago, was it?  I realized (while) you all were having that discussion the answer was basically rule #1 in assembly:  "Preserve that and only that which you use."  It's actually quite simple, and the same whether we're talking 65x, 68x, or x86.  At the time I thought there might be some hard and fast rule among the hard-cores here, so I decided to ask.  Turns out there wasn't.  My simple question turned into quite a lengthy discussion, didn't it?

jj2007

There is no need for a lengthy discussion, there is a need for empiric evidence instead of religious beliefs. Tell me by how much % a "uses esi edi ebx" slows down your proggie, and I'll keep my mouth shut.

satpro

Well, there was a lengthy discussion about the topic, and quite a few of us were part of it.  If you add six (or more) needless cycles (and bytes) to every procedure, especially the Window Procedure, running over and over, it's not just a percentage--it's a hard count.  That stuff adds up.  Maybe some guy is still using an old laptop with 1 cpu @ 800 mHz, trying to play music in the background.  That style of programming will cost HIM up to 16x.  You don't think he'll notice?


So, save those registers for no reason.  Freedom of expression--I don't imagine anyone will stand in your way.  But if we are talking assembly language technique (religious belief?) then it's a different story, especially for someone like shankle who might be new to this and only wants to learn good, solid programming.  Yes, of course it's safe to push ebx, esi, and edi, or to push all of them for that matter.  It's also borderline sloppy.  Why do it if it's not necessary?


I just hope it's not because  "... otherwise Hutch & me & a few others will eat you alive :greensml:  ."   ->Incidentally, Hutch excluded his name from that line...he put it this way:

" :biggrin:

This is actually BAD advice."

jj2007

Quote from: satpro on July 15, 2012, 07:31:18 AMit's not just a percentage--it's a hard count.

Show us. Express it in nanoseconds, cycles, percent, whatever, but quantify the "hard count". Show us how much the guy with the 800 MHz CPU will suffer.

(no need to indulge in precision, a back-of-the-envelope formula will be sufficient)

satpro

I made my point.


But ok... against today's 4x cpu, 3.2 gHz machine.  BTW, how does one expect nanoseconds and cycles, yet precision is not necessary?

jj2007

Quote from: satpro on July 15, 2012, 07:45:03 AMBTW, how does one expect nanoseconds and cycles, yet precision is not necessary?

jj: "Most [messages] are WM_MOUSEMOVE - a particularly fast one with only 600 cycles. 3 push/pop pairs delay WM_MOUSEMOVE by 0.00008 milliseconds - how significant is that delay?"

That is admittedly not very precise, it could be 0.00007 milliseconds or 0.00009 milliseconds, but it still holds much more information than a bold "unnecessary pushing costs a lot of CPU power" statement. Remember your CPU does approx. 2500000000 cycles per second. If you want to get it sweating, you need something much more serious than a little pushin' and poppin' in an idle WndProc, e.g.
xor ecx, ecx
@@: dec ecx
  jne @B
is a good exercise for your lazy CPU :bgrin:

I understand that you like the idea of being a hardcore assembly programmer, but what you claim is simply not relevant in a WndProc, because a) these messages don't get called a Million times per second and b) the extra 3 cycles are statistical noise when def-processing the message takes many hundreds of cycles.

Your line of reasoning does hold in an innermost loop which gets called a Million times (and yes, I mean a Million, not less). And of course, you would never call a subproc from within an innermost loop, so the push & pop argument is completely irrelevant anyway.

satpro

jj,


I get where you're coming from, and for the most part assembly corrects all of C/C++s speed issues, but I really want to get back to shankle's problems learning GoAsm.  He deserves a fair shot at this.  I think I was responding initially to the sarcasm aimed at me regarding the question I asked a couple of weeks ago just looking for an informed opinion about register preservation.  I'm not some newbie--quite the opposite.  Just never really heard anyone talking about it before.  So I asked.


If you want to make the cpu sweat, simply switch over to PeekMessage.  That'll do it.  As an aside, I still have one of those 800 mHz laptops that I use when I'm away from this thing and my wife's is in her hands.  That machine sweats everything--except for well-written assembly programs like qeditor or GoAsm.  I can literally see mouse activity in the task manager.  So yes, I do believe everything is relevant, that's all.  Hardcore?  Probably.  But I'm okay if you have a different programming style.  We all do.

jj2007

Quote from: satpro on July 15, 2012, 09:09:52 AMI can literally see mouse activity in the task manager.

Hardcore assembler coders measure it, so give us figures: Is this CPU weak enough to turn an extra 0.00008 milliseconds per WM_MOUSEMOVE into "activity" in the task manager?

So go and test it with one of the standard Windows apps that you find in \masm32\examples, then we have something real to discuss.

Just in case your task manager doesn't notice your frenetic attempts: A WM_MOUSEMOVE per se needs 600 cycles, plus 3 for a little push'n and popp'n. But there are apps out there in the evil Microsoft & Adobe etc world which do, in the WM_MOUSEMOVE handler, incredibly slow things that may take ten-thousands of cycles... and that rubbish will indeed show as "activity" in the task manager".

shankle

This is not about my main problem but am curious why this messagebox is causing problems.
This code causes the messagebox to go into a loop from which I have to
reboot the puter to get out of it. It never goes to WM_CREATE.
WndProc:
        FRAME  hWnd,iMsg,wParam,lParam
        USES ebp,ebx,edi,esi
        Local hMemory,stm:SYSTEMTIME,ps:PAINTSTRUCT,hdc,hdcPrn
       
;   USED ONLY DURING TESTING PHASE
;TM8              db   'begin of wndproc',0
      pushad
      invoke MessageBox, NULL,addr TM8,NULL,MB_OK 
      popad   
;   test end

;.WM_CREATE
        cmp D[iMsg],WM_CREATE
         jne >>.WM_CHAR         
;.........................................       
This code does execute WM_CREATE and then goes back to my original problem
which I'm still trying to resolve.
WndProc:
        FRAME  hWnd,iMsg,wParam,lParam
        USES ebp,ebx,edi,esi
        Local hMemory,stm:SYSTEMTIME,ps:PAINTSTRUCT,hdc,hdcPrn         

;.WM_CREATE
        cmp D[iMsg],WM_CREATE
         jne >>.WM_CHAR         

jj2007

Maybe GoAsm is different, but with Masm a uses ebp causes serious problems when local variables are present, because ebp gets automatically saved by the frame.