howdy, what a nice place this board seems to be, anyway, I haven't programmed x86 assembly for *quite* some time, so I'll probably ask a lot of dumb questions while getting back into the game. First off, when I just started programming again now I have some questions about register preservation across api calls, basically which method is more faster/effective, say if I need to preserve a register between api calls, is it faster to push/pop or do a mov <reg>,

pseudo example:


mov ebx, (important data to preserve)
push ebx
invoke API_CALL_THAT_TRASHES_EBX
pop ebx
inc ebx
push ebx
invoke API_CALL_THAT_THRASHES_EBX
pop ebx


or is this faster/better/smarter?


mov ebx, (important data to preserve)
push ebx
invoke API_CALL_THAT_TRASHES_EBX
mov ebx,
inc ebx
invoke API_CALL_THAT_THRASHES_EBX
pop ebx


in this short example the pushing and poping of the first code won't do any difference, but while preserving a reg during a LONG line of API calls I wondered if it's not more efficient to just use mov <reg>, instead of pop/push between each api call.

sorry if this is really a dumb question, as I said I haven't programmed x86 for QUITE a while, in fact last time I programmed x86 was using the pmode dos extender (ahh those were the days!)

kind regards, BinarySoup
Posted on 2003-03-04 10:41:03 by BinarySoup
There's been a LOT said on register preservation. here's the summary:

1) All api functions will preserve ebx,edi,esi. In your example above you don't need to push/pop ebx on every function call.
2) If you alter ebx,edi,esi in your function then you must preserve them.
3) the other registers eax,ecx,edx are expendable.

mov <reg>, would be faster. I wouldn't get too smart if i were you. with each push/pop esp will change and you could very easily end up sitting in the corner with a pointy hat...;)
Posted on 2003-03-04 12:14:55 by MArtial_Code
mov <reg>, would be faster. I wouldn't get too smart if i were you. with each push/pop esp will change and you could very easily end up sitting in the corner with a pointy hat...;)

Not only that, the API may change parameters on the stack so the value is not guaranteed to be the same as before after the call.

Thomas
Posted on 2003-03-04 12:29:38 by Thomas
thankyou very much for the responce, as a reward here's more stupid questions ;)

MArtial_Code wrote:
If you alter ebx,edi,esi in your function then you must preserve them.

why? does the system need them preserved?? in the small programs I've written now I'm not preserving any regs between my own function calls (unless I need them preserved myself), and they seem to work, does the proc automatically preserve regs unless stated?

MArtial_Code wrote:
mov <reg>, would be faster. I wouldn't get too smart if i were you. with each push/pop esp will change and you could very easily end up sitting in the corner with a pointy hat...

that's what I wanted to know, big thanks, well back in the old days when one didn't have fancy things like INVOKE ;) I usually retrieved parameters from the stack using the basic , hopefully I remember the pitfalls, if not, I always
had a thing for pointy hats ;)
Posted on 2003-03-04 12:30:38 by BinarySoup
Thomas wrote:
Not only that, the API may change parameters on the stack so the value is not guaranteed to be the same as before after the call.


ehh, am I missing something here? you're saying that what I put at the top of the stack before a call may not be on the top of the stack after a API call??
Posted on 2003-03-04 12:34:54 by BinarySoup
I'm not sure what Thomas is saying...? I've not heard that before...

If you alter ebx,edi,esi in your function then you must preserve them.

Specifically when your callback function returns windows expects the above mentioned registers to be unaltered. Across your own functions the regiters which you preserve are at your discretion but in the world of windows programming, preserving ebx,edi,esi has been conventionalised.

wndproc proc .... ;called by windows

call FUNCTION_which_uses_ebx_edi_esi
ret
wndproc endp

FUNCTION_which_uses_ebx_edi_esi proc ...
push edi
push esi
push ebx

change edi,esi,ebx here

pop ebx
pop esi
pop edi
ret
FUNCTION_which_uses_ebx_edi_esi endp
Posted on 2003-03-04 12:55:36 by MArtial_Code
mov ,ebx
call function
mov ebx,
...

function:
push eax
...
pop eax
ret

This way you wouldn't get the same result in ebx after the function call, because eax is put at the same point in the stack as ebx. Alter esp as well if you want to use mov instead of push.
Posted on 2003-03-04 13:02:59 by _js_
MArtial_Code wrote:
If you alter ebx,edi,esi in your function then you must preserve them.

ok, then I have to ask, when defining a standard proc, not adding USES :, does it preserve these registers by default or? because I am changing ebx in basically every function I call and haven't manually preserved it, and yet everything works fine.


_js_ wrote:
This way you wouldn't get the same result in ebx after the function call, because eax is put at the same point in the stack as ebx. Alter esp as well if you want to use mov instead of push.


yes, but I start with a push ebx, your example starts with a mov , ebx, which simply overwrites first stack value.

push ebx
call function
mov ebx,
...

function:
push eax
...
pop eax
ret

would work right?

if pushing multiple values in a routine it gets a little more tricky

push ebx
call function
mov ebx,
push eax
call function
mov ebx,
mov eax,
...
...

but it's fully workable, or? I was more concerned wheter or not it was faster than doing a:

push ebx
call function
pop ebx
push ebx
call function
...

i'm sorry if I come off as a total dumbass, I installed hutch masm package last night and am trying to grasp the extended masm syntax as we speak, slowly, slowly my memory concerning assembly programming is surfacing, my brain is like a very slow tape backup.
Posted on 2003-03-04 13:24:34 by BinarySoup
masm won't generate any code to save registers if you don't use the uses directive.

If you've been altering ebx..etc without preserving them then consider yourself very lucky not to have run into problems with windows.

There have been many post on this board requesting help in solving random crashes. The solution to many of them turned out to be register preservation.

If you post a function for people to use then I'm fairly certain everyone here will expect that function to preserve ebx...ect if it uses them.

The code you've posted is worlable but why not create the space on the stack at the start of the routine. Then you won't have to keep altering the index to get to the same variable.

btw if you're going to access variables directly thru esp then you may as well turn off masm's automatic stack frame setup.

option PROLOGUE:NONE
option EPILOGUE:NONE

functions defined here will not have a stack frame
you're responsible for accessing function paramemters and
other local variables.


option PROLOGUE:PROLOGUEDEF
option EPILOGUE:EPILOGUEDEF
Posted on 2003-03-04 16:44:07 by MArtial_Code
Originally posted by BinarySoup
ehh, am I missing something here? you're saying that what I put at the top of the stack before a call may not be on the top of the stack after a API call??

Yup :):



foo proc param1:dword
add [param1], 100
mov eax, [param1]
ret
foo endp

This is a useless example but it shows that you can change parameters inside a function. You can't be sure a windows API function won't do this so don't assume anything pushed on the stack as parameters to an API is preserved after the API call.

Thomas
Posted on 2003-03-04 17:06:36 by Thomas
Quick side note: Why ebx, esi, edi?

The answer is simple. That is because Win32 APIs are built using MS C (not entirely, but most part of APIs), which has its own internal rule of register usage. Remember int 21h? Just think of APIs as C version of int 21h. There are differences, but the analogy will help you catch up quickly regarding register usage.
Posted on 2003-03-04 18:06:08 by Starless
you are great guys, like living encyclopedias of assembly, wish I'd found this place sooner.

anyway,

Tomas wrote:

You can't be sure a windows API function won't do this so don't assume anything pushed on the stack as parameters to an API is preserved after the API call.


sure, I know the stack parameters for the actual API call is not preserved, INVOKE is unless i'm mistaken nothing but a macro that pushes the parameters onto the stack for you, the actual API function will then pop them. However, if you push something onto the stack before a API call (using invoke), the API function must return with the stack pointer set at the position it was pre-invoke, else all is crazy, right?

push foo (is now on top of stack, )
invoke foofunction (pushes 3 dword parameters, foo is now at )
...

foofunction (pops 3 dwords, foo is back at )
ret
....
mov foo,

you probably thought I was wondering about reusing API parameters from stack after a API call, that's not what I meant, it was simply if it was faster to do:

push eax
invoke API_BLABLA (trashes eax)
mov eax,
... use eax for something
invoke API_BLABLA (trashes eax)
mov eax,
... use eax for something
invoke API_BLABLA (trashes eax)
...
pop eax

than:

push eax
invoke API_BLABLA (trashes eax)
pop eax
... use eax for something
push eax
invoke API_BLABLA (trashes eax)
pop eax
... use eax for something
push eax
invoke API_BLABLA (trashes eax)
...
pop eax

basically, is it faster to do a mov reg, than doing a pop reg + push reg.


also, while I have you supercoders attention, I just wrote a little test program with a linked list, and I wondered if something constitutes as possible bug.

here goes, it's dirty I know, just trying to remember asm and understand windows api structure.

this routine de allocates all items in list (struct command in MASM, I LOVE IT!).

assume eax: ptr testdata
mov eax, list
.while true
.break .if eax == NULL
push eax
invoke GlobalUnlock, .MemoryHandle
mov eax,
invoke GlobalFree, .MemoryBlock ;Now block is free for use in win?
pop eax
mov eax, .Next ; But I use a value from the deallocated block here, is it
; dangerous? not very likely that another program
; can allocate this block and fill it during this short
; time right? or is it bad practice?
.endw

thanks for all your help guys, you rock!! must be a pain with people like me asking tons about the basics...
Posted on 2003-03-04 21:59:44 by BinarySoup
Originally posted by BinarySoup

basically, is it faster to do a mov reg, than doing a pop reg + push reg.

True, at the cost of one byte.

assume eax: ptr testdata
mov eax, list
.while true
.break .if eax == NULL
push eax
invoke GlobalUnlock, .MemoryHandle
mov eax,
invoke GlobalFree, .MemoryBlock ;Now block is free for use in win?
pop eax
mov eax, .Next ; But I use a value from the deallocated block here, is it
; dangerous? not very likely that another program
; can allocate this block and fill it during this short
; time right? or is it bad practice?
.endw

Why would that be dangerous? .MemoryBlock was freed, not eax. You know it from a rudimentary linked list class (or, an article if you taught yourself). But, if you also allocated the list element (not the list data you freed in your code), this code suffers memory leak.
Posted on 2003-03-04 22:15:05 by Starless
Starless wrote:
.MemoryBlock was freed, not eax


yes I know that, what could be dangerous was if between the:

invoke GlobalFree, .MemoryBlock

and the:

mov eax, .Next

another program could possibly allocate the memory and corrupt it so that .Next which is a pointer to next element in list would now have been filled with something else, however, I find it unlikely that another program would actually be able to allocate the memory and fill it in this short time no matter how multitasking windows is.

Starless wrote:
But, if you also allocated the list element (not the list data you freed in your code), this code suffers memory leak.


ehh, don't follow you here, each item in the list is an allocated block, the pointers to the next item are contained in that block, then of course there is a dword defined in the .data section that holds a pointer to the first item in the list.

simple example:

.data?
ItemList dd ? ; pointer to first item in list, null if none.

Item struct
MemoryHandle dd ?
MemoryBlock dd ?
NextItem dd ? ; next item in list, null if end
Item ends

basic oneway linked list...

allocation of a item would be something like:

invoke GlobalAlloc, GMEM_MOVEABLE, SIZEOF Item
push eax
invoke GlobalLock, eax
assume eax : ptr Item
mov .MemoryBlock, eax
pop ecx
mov .MemoryHandle, ecx
mov .NextItem, null

then we'd traverse the list until we find an item with NextItem = null and attach pointer to this Item.

please don't tell me I screwed up basic stuff like this :(
Posted on 2003-03-04 23:04:14 by BinarySoup
(Scratching on my head...) Your code is quite different than what I would do. You might have some specific needs which I fail to understand.

So, here is what I would do (unless you consider yourself clueless, you can ignore the rest).

1. Search the forum about linked list.
There is a library posted by a member written for MASM. Unless you are testing your skill, having a library as a reference is helpful.

2. Search the forum about HeapAlloc(), GlobalAlloc(), LocalAlloc() and VirtualAlloc().
Not long ago there was a long thread debating the differences between those memory management APIs. OTOH, SDK document strongly discourages the use of LocalAlloc() and GlobalAlloc() unless required by some APIs (carried-over from Windows 3.1). Esp., GMEM_MOVEABLE flag is marked as obsolete.

3. If I write a linked-list routine, I would do:


LISTSTRUCT STRUCT
next dd ?
somedata dd 10 dup(?)
LISTSTRUCT ENDS

.data
list_head dd 0

; either from HeapCreate()
; or from GetProcessHeap()
hHeap dd ?

.code
...
; new node which happens to be the head.
invoke HeapAlloc,hHeap,0,sizeof LISTSTRUCT
mov list_head,eax
mov [eax].next,0
; and data copy here

...

; new node
invoke HeapAlloc,hHeap,0,sizeof LISTSTRUCT
mov edx,list_head
@@:
mov ecx,[edx].next
test ecx,ecx
jz @F
mov edx,ecx
jmp @B
@@:
mov [edx].next,eax
mov [eax].next,0
; data copy here

...

; destruction
push ebx
mov eax,list_head
@@:
mov ebx,[eax].next
; assuming a node does not
; have any other allocated mem.
invoke HeapFree,hHeap,0,eax
mov eax,ebx
test eax,eax
jnz @B
pop ebx

Then, again, this kind of code was written to suit my need. So this may not be what you want.
Posted on 2003-03-05 00:01:18 by Starless
thanks for all the input!

well, the linked list stuff I wrote was just for testing structs and some memory allocation de-allocation, it was the simplest possible.

I haven't written anything "serious" as of yet, just trying to get back into x86 assembly, was about 5 years since I did anything in it, and then it was graphics/demo stuff in DOS pmode using VBE, a couple of cpu emulators etc, so the biggest hurdle will most likely be adjusting to the windows API.

as for the different memory allocation schemes that is definately something I need to read up on, as I am totally lost there and memory allocation is a "kinda important" part of programming, thanks for the tip!

I must say I really like the .IF .ELSE .BREAK etc constructs, takes away the need of all those pesky local labels, also the struct is great!

well back to writing ugly pointless code and hopefully learn/remember something!

this forum is GREAT!
Posted on 2003-03-05 00:33:42 by BinarySoup
you probably thought I was wondering about reusing API parameters from stack after a API call, that's not what I meant, it was simply if it was faster to do:

Yes you are right, I should have read your code better :rolleyes:. In that case you can just use mov eax, instead of pop+push.

Thomas
Posted on 2003-03-05 01:35:41 by Thomas
Thomas that example is deceptive:

foo proc param1:dword
add , 100
mov eax,
ret
foo endp

There's really no confusion since it would make more sense if you renamed the parameter:
foo proc pParam1:dword
...
foo endp

since to call that function you would write either
invoke foo, addr changethisvariable
or
invoke foo, thisvariablesvalue
which wouldn't change anything but the local parameter param1.

Also the convention with register preservation is really very simple:
eax, ecx, edx are never guaranteed to be preserved accross function calls
esi, edi, ebx are
So really all you have to keep in mind is to preserve esi, edi, ebx in windows callback functions (EnumWindowsProc proc uses ebx, esi ...)
And it is stupid not to follow the same approach and preserve esi, edi, ebx in your own exported functions.

Shouldn't it be a FAQ too?
Posted on 2003-03-05 02:21:22 by grv575