In m quest to learn assembly, I want to find out and try all I can. I don't use invoke anymore, and in my next step I would like to know how I would code not using procs.

I have the following:


Draw3DLine proc uses esi edi ebx hDc:DWORD, LineBegin:DWORD, LineEnd:DWORD, LineTop:DWORD
LOCAL MyPen:DWORD
LOCAL OldPen:DWORD

mov edi, [hDc]
mov esi, [LineTop]
mov ebx, [LineBegin]

push COLOR_BTNSHADOW
call GetSysColor

push eax
push 1
push PS_SOLID
call CreatePen
mov [MyPen], eax

push eax
push edi
call SelectObject
mov [OldPen], eax

push 0
push esi
push ebx
push edi
call MoveToEx

push esi
push [LineEnd]
push edi
call LineTo

push [OldPen]
push edi
call SelectObject

push [MyPen]
call DeleteObject

; Line 2
inc esi
push COLOR_BTNHIGHLIGHT
call GetSysColor

push eax
push 1
push PS_SOLID
call CreatePen
mov [MyPen], eax

push eax
push edi
call SelectObject
mov [OldPen], eax

push 0
push esi
push ebx
push edi
call MoveToEx

push esi
push [LineEnd]
push edi
call LineTo

push [OldPen]
push edi
call SelectObject

push [MyPen]
call DeleteObject

ret
Draw3DLine endp

and I call it with:


push 35
push 5
push 435
push [ps.hdc]
call Draw3DLine

and masm fixes the stack for me on return.

so would something like the following be correct?
move MyPen and OldPen to the .data? section.

create a label named Draw3DLine and execute like:


push 35
push 5
push 435
push [ps.hdc]
jmp Draw3DLine

and somewhere in the source:

Draw3DLine:
push edi
push esi
push ebx

push ebp
mov ebp, esp

mov edi, [ebp + 20]
mov esi, [ebp + 32]
mov ebx, [ebp + 24]

push COLOR_BTNSHADOW
call GetSysColor

push eax
push 1
push PS_SOLID
call CreatePen
mov [MyPen], eax

push eax
push edi
call SelectObject
mov [OldPen], eax

push 0
push esi
push ebx
push edi
call MoveToEx

push esi
push [ebp + 28]
push edi
call LineTo

push [OldPen]
push edi
call SelectObject

push [MyPen]
call DeleteObject

pop ebp
pop ebx
pop esi
pop edi

add esp, 16
ret

Can it be that simple? No, it feels like I am missing something or doing something wrong in the example...

Teach me!
Posted on 2003-05-15 12:22:16 by Gunner
I think first in your case access args at offsets EBP+14h to EBP+20h and seconds to exit properly use (the arglist is released after the return address)

pop ebp
pop ebx
pop esi
pop edi

add esp, 20
jmps
Posted on 2003-05-15 14:15:38 by _Servil_
ret 16
Posted on 2003-05-15 14:32:10 by iblis
It's more convenient to establish the stack frame first, so that the first parameter on the stack is always at the same offset.
proc1:

push ebp
mov ebp,esp
; first parameter (last stacked value) is at [ebp+8]

; first local DWORD is at [ebp-4]
sub esp,number_of_local_bytes ; allocate local variables

; if EBX, ESI, or EDI are used in this proc, save them here
; ...
; restore EBX, ESI, or EDI if they were saved

mov esp,ebp ; this will deallocate local variables
pop ebp
ret number_of_argument_bytes
You still need to use

call proc1

call is not a macro, it is a real x86 instruction.

If you pass values only in registers and don't create local variables, you only need a simple ret. The rest of the code wouldn't be needed. If this proc is never called directly from Windows, you can dictate what registers will or will not be saved.
Posted on 2003-05-15 15:59:41 by tenkey
Procs are usefull when you think its code could be reused without modification. If you dont think it could have any future value, use global variables or registers (or EQUates) to feed the "routine" as they used to be called.

Using the stack is prone to errors and just makes your code less readable for yourself when debugging. And you dont have to build stack frames, its cleanup, and everything associated with it. You know: the KISS principle!!!

Raymond
Posted on 2003-05-15 22:52:41 by Raymond
tenkey, I think is better to say
ret number_of_arguments*4
insead of
ret number_of_argument_bytes
since
We can simply say that everything in win32 thats being pushed to the stack becomes a dword (else the stack might very soon be dis-aligned, NT is said to generate an exception if not aliged).

(btw, KISS = Keep It Simple and Safe, (am I right about the "safe"?))
Posted on 2003-05-15 23:26:39 by scientica
Wow, lots o' info here... have to take it all in.

Thanks
Posted on 2003-05-15 23:48:16 by Gunner
I think you do not need call labels unless you want to remove the stack frame. Of course you can also use pops in your proc like



func:
pop eax
pop ecx;first parameter
push eax
...
ret
Posted on 2003-05-16 01:13:01 by roticv
scientica

KISS used to be an acronym for Keep It Simple Stupid. I would guess this was invented to refer to people complicating things just to give the impression of being smarter (such as some civil servants for example).

This has nothing to do with Gunner though.:stupid:

Nowadays, the "KISS principle" means to keep things as simple as you can, avoiding any complicated or superfluous detail.

Raymond
Posted on 2003-05-16 08:06:25 by Raymond

segment code class=code use32

..start:
push dword 4 ;ebp+28 or four argument
push dword 3 ;ebp+24 or third argument
push dword 2 ;ebp+20 or second argument
push dword 1 ;ebp+16 or first argument
jmp Draw3DLine


Draw3DLine:
push edi ;ebp+12
push esi ;ebp+8
push ebx ;ebp+4

push ebp ;<--here is where stack frame is created, ebp+0 or only ebp
mov ebp, esp ;and here

;first param
mov dword, 11
;second
mov dword, 21
;third
mov dword, 31
;four
mov dword, 41
pop ebp
pop ebx
pop esi
pop edi

add esp, 16
ret


you are trying use a combination of stack frame and the other technique only with the use of esp without stack frame, i recomend this post Locals without stack frame i think is really complete ;) aned i learn a lot there, but here you are triying to mix both techniques.

the first technique with stack frame: use the pair enter/leave in the start/end of the function and if you allocate space for local bars it dosent matter, only keep the track of push/pop pairs

the second technique is a little short (normally two instructions less ), here you only use a reference from esp and dont lost time in pushing ebp and mov ebp, esp

i check the code above and is the correct, with the olly, but i sugest that try first like tenkey say, try first establish the stack frame


segment code class=code use32

..start:
push dword 4 ;ebp+16
push dword 3 ;ebp+12
push dword 2 ;ebp+8
push dword 1 ;ebp+4
jmp Draw3DLine


Draw3DLine:
push ebp ;<--here is where stack frame is created, ebp+0 or only ebp
mov ebp, esp ;and here
push edi ;ebp-4
push esi ;ebp-8
push ebx ;ebp-12
;here you can allocate space for local vars with sub esp, size
;first param
mov dword, 11
;second
mov dword, 21
;third
mov dword, 31
;four
mov dword, 41
pop ebx
pop esi
pop edi
pop ebp
add esp, 16 ;+size here you can liberate the space of the posible local vars
ret


But the first idea is correct, like you see, the stack frame let you reference arguments and local vars from it place

I think only is maybe or maybe not more readable the last one.
Posted on 2003-05-18 17:02:53 by rea
I'm on holiday at the moment, but I just noticed your message about not using PROC.

I wrote a tutorial on this ("Understand the Stack - Part 2") which you can get from GoDevTool.com and there is also some sample code in my demo program TestBug.

My assembler GoAsm uses the word FRAME instead of PROC, to emphasise the fact that you are making a stack frame when you use this assembler directive, something which I believe is not always understood.

In normal use (no local data required) you would not need to make a stack frame, although it is convenient to do this to get parameters (arguments).
Posted on 2003-05-19 20:41:21 by jorgon
hgb,

I think you should replace the jmp Draw3DLine with call Draw3DLine, since ret expects the return address to be push onto the stack.
Posted on 2003-05-19 21:15:18 by roticv
thx for remember me that.. that a call is like two instruction push the next addres ( i miss this, sorry), and jmp to the proc/funct, also i correct now that...

in the first form (Gunner original) is


segment code class=code use32

..start:
push dword 4 ;ebp+32
push dword 3 ;ebp+28
push dword 2 ;ebp+24
push dword 1 ;ebp+20
push dword $+10 ;ebp+16
jmp Draw3DLine
add esp, 16
push dword 0
extern ExitProcess
call [ExitProcess]

Draw3DLine:
push edi ;ebp+12
push esi ;ebp+8
push ebx ;ebp+4

push ebp ;<--here is where stack frame is created
mov ebp, esp ;and here

;first param
mov dword[ebp + 20], 11
;second
mov dword[ebp + 24], 21
;third
mov dword[ebp + 28], 31
;four
mov dword[ebp + 32], 41
pop ebp
pop ebx
pop esi
pop edi
ret


and in the "form" i sugest is


push dword 4 ;ebp+32
push dword 3 ;ebp+28
push dword 2 ;ebp+24
push dword 1 ;ebp+20
push dword $+10 ;ebp+16
jmp Draw3DLine
add esp, 16
push dword 0
extern ExitProcess
call [ExitProcess]

Draw3DLine:
push edi ;ebp+12
push esi ;ebp+8
push ebx ;ebp+4

push ebp ;<--here is where stack frame is created
mov ebp, esp ;and here

;first param
mov dword[ebp + 20], 11
;second
mov dword[ebp + 24], 21
;third
mov dword[ebp + 28], 31
;four
mov dword[ebp + 32], 41
pop ebp
pop ebx
pop esi
pop edi
ret


like you see, i "balance" the stack after the call, in the next instruction and not inside, this is only if you want use ret, but remember that balance the stack, and push pop dont destroy the values, only if you move data there directly, or make another push over a pop... ok.. what i am saying.,.. is that you dont need balance the stack after the call, you can do inside of your function.. and instead of using ret instruction, you can use some like _Servil_ say, is correct use a jump in this case... is like this...



segment code class=code use32

..start:
push dword 4 ;ebp+20
push dword 3 ;ebp+16
push dword 2 ;ebp+12
push dword 1 ;ebp+8
push dword $+10 ;ebp+4
jmp Draw3DLine
push dword 0
extern ExitProcess
call [ExitProcess]

Draw3DLine:
push ebp ;<--here is where stack frame is created
mov ebp, esp ;and here
push edi ;ebp-4
push esi ;ebp-8
push ebx ;ebp-12


;first param
mov dword[ebp + 8], 11
;second
mov dword[ebp + 12], 21
;third
mov dword[ebp + 16], 31
;four
mov dword[ebp + 20], 41
pop ebp
pop ebx
pop esi
pop edi
;here balance the stack inside
;and not use ret instruction
;edit.. and remember the values still there in memory you can overwrite this value only if you move data there directly or push a value there
add esp, 16
jmp [esp-16]


thank all rotvic for correct me... amm i canot sa. i saw texting the knowledge.. i was wrong from the start... thx _ervil_ for the idea of the jump, the starter Gunner and thx for all the other people, this let me remember what is a call, understand and learn a little more :D



I check now all the trhee examples, works ok... with the olly.. the thing what append that i assume in my other examples.. was ok.. is tha t the return address.. waas exactly the call to terminateThread.. or some like that :S lol


Have nice day.:alright:
Posted on 2003-05-19 22:36:21 by rea
Remove stack frame and do not use ebx, esi, edi (Thus no need to preserve them)


push ..
push ..
push ..
push ..
call Draw3DLine
..

Draw3DLine:
sub esp,8
invoke GetSysColor, COLOR_BTNSHADOW
invoke CreatePen, PS_SOLID,1,eax
mov [esp],eax
push eax
push [esp+8+4]
call SelectObject
mov [esp+4],eax
push 0
push [esp+8+16]
push [esp+8+8]
push [esp+8+4]
call MoveToEx
push [esp+8+16]
push [esp+8+12]
push [esp+8+4]
call LineTo
push [esp+4]
push [esp+8+4]
call SelectObject
push [esp]
call DeleteObject
invoke GetSysColor, COLOR_BTNHIGHLIGHT
invoke CreatePen, PS_SOLID, 1 ,eax
mov [esp],eax
push eax
push [esp+8+4]
call SelectObject
mov [esp+8],eax
push 0
inc dword ptr[esp+8+16]
push [esp+8+16]
push [esp+8+8]
push [esp+8+4]
call MoveToEx
push [esp+8+16]
push [esp+8+12]
push [esp+8+4]
call LineTo
push [esp+4]
push [esp+8+4]
call SelectObject
push [esp]
call DeleteObject
add esp,8
ret 16

*Note: Untested
Anyway It would be easier to work with invoke and stuffs like that for gdi.
hehe... seems like my code is longer and more confusing (Kids do not try this at home :) )
Posted on 2003-05-20 00:31:46 by roticv