Originally posted by hutch--
You have actually described the convention the wrong way around. Try and connect this much, when you write your OWN procedure, IF you use any of EBX ESI EDI ESP EBP you either preserve it or crash the application most of the time.
You have actually described the convention the wrong way around. Try and connect this much, when you write your OWN procedure, IF you use any of EBX ESI EDI ESP EBP you either preserve it or crash the application most of the time.
No it won't:
.586
.model flat, stdcall
option casemap:none
include <windows.inc>
include <kernel32.inc>
include <user32.inc>
includelib <kernel32.lib>
includelib <user32.lib>
.data
msgText db "text",0
.code
func2 proc param:dword
mov ebx, 1
mov esi, param
invoke MessageBox, NULL, addr msgText, addr msgText, MB_OK
mov edi, 2
ret
func2 endp
func1 proc
invoke func2, 100
mov ebx, 12345
ret
func1 endp
start:
invoke func1
invoke ExitProcess, eax
end start
Both func1 and func2 trash some of those register but it won't crash.
How does this effect a person who is writing an application ? Simple, if they are going to use any of the registers that require preservation they do so and if they are going to call API functions, they MUST protect EAX ECX EDX if they are being used in the procedure.
Of course you must preserve those since they will be trashed by windows. But that does not mean you can't trash ebx esi edi in your own proc.
Now to address WHAT a callback is under Windows, it is the starting address of a procedure which depending and the type of callback that it is is designed specifically to receive a set number of parameters.
The most important property of a callback is that it's called by windows and that's the only reason you need to adhere to the ebx/esi/edi register preservation.
Now as you choose to try and correct me in the advice I have given to a member, explain to anyone here why EBX ESI EDI must be preserved when they are not used here. You will notice that ESP and EBP are preserved in the normal manner for MASM.
I never said you should save registers if you don't use them.
The simple fact is that EBX ESI EDI are NOT used in this standard system callback so unless your own code uses them, extra preservation is bad code design that serves no purpose.
I would stick to the *convention* as well, but again, it's not a requirement. If you want you don't have to save them. It's your choice.
I will put it to you that feeding new member folklore is incorrect, the convention for register preservation under Windows is well known and not subject to personal opinion.
I just want to make clear that it isn't a requirement, just a convention. It's not *wrong* what I'm saying, but I wouldn't advice not preserving them.
While I support you writing code in any manner that you like, trying to inflict a mistake on other members looking for information is not consistent with the responsibilities you have here.
It's not a mistake..
Thomas
P.S. I might not be able to answer any posts the next couple of days so my reply may be a bit late..
Thomas,
I guess we will hear from you when you get back.
The assembler dump is unconditional proof that there is no need to preserve the registers in a callback procedure if they are not used. It is like that because it DOES conform to the Windows convention of preserving the registers that are used and NOT preserving those that are not.
Now it appears that you take the same line as f0dder in register preservation being optional but I will put it to you that while you may get something to run on one version of Windows, it has just as much chance to fail on another if you do not properly protect the registers required.
Thats what the convention is for, to make sure that code is reliable, not a russian roulette depending on the version you run.
===============================
I never said you should save registers if you don't use them.
===============================
Then what are you arguing about ? Either you conform to the convention or you do not but there is no half way.
===============================
I would stick to the *convention* as well, but again, it's not a requirement. If you want you don't have to save them. It's your choice.
===============================
No its not a requirement unless you want to write reliable code that runs on different versions of Windows and again this is what the convention is for, to reliable manage registers within an operating system that specifies its register convention.
===============================
I just want to make clear that it isn't a requirement, just a convention. It's not *wrong* what I'm saying, but I wouldn't advice not preserving them.
===============================
The problem is with your assertion that you have no way of predicting whether Windows uses the expendable registers or not if you do not comform to the convention. It is there for exactly the reason that it IS predictable and if you DO comform to it your code will run properly.
I have picked you up for the same reason as I did with f0dder, you can write anything you like and it is not my business but when you support technical information that is incorrect to a member looking for information you have misadvised them and lead them astray.
What you supported was the assertion made by f0dder that a callback has some special condition that other procedures do not when it comes to preserving registers, the dump of the WndProc proves that this assertion is wrong.
Regards,
hutch@movsd.com
I guess we will hear from you when you get back.
The assembler dump is unconditional proof that there is no need to preserve the registers in a callback procedure if they are not used. It is like that because it DOES conform to the Windows convention of preserving the registers that are used and NOT preserving those that are not.
Now it appears that you take the same line as f0dder in register preservation being optional but I will put it to you that while you may get something to run on one version of Windows, it has just as much chance to fail on another if you do not properly protect the registers required.
Thats what the convention is for, to make sure that code is reliable, not a russian roulette depending on the version you run.
===============================
I never said you should save registers if you don't use them.
===============================
Then what are you arguing about ? Either you conform to the convention or you do not but there is no half way.
===============================
I would stick to the *convention* as well, but again, it's not a requirement. If you want you don't have to save them. It's your choice.
===============================
No its not a requirement unless you want to write reliable code that runs on different versions of Windows and again this is what the convention is for, to reliable manage registers within an operating system that specifies its register convention.
===============================
I just want to make clear that it isn't a requirement, just a convention. It's not *wrong* what I'm saying, but I wouldn't advice not preserving them.
===============================
The problem is with your assertion that you have no way of predicting whether Windows uses the expendable registers or not if you do not comform to the convention. It is there for exactly the reason that it IS predictable and if you DO comform to it your code will run properly.
I have picked you up for the same reason as I did with f0dder, you can write anything you like and it is not my business but when you support technical information that is incorrect to a member looking for information you have misadvised them and lead them astray.
What you supported was the assertion made by f0dder that a callback has some special condition that other procedures do not when it comes to preserving registers, the dump of the WndProc proves that this assertion is wrong.
Regards,
hutch@movsd.com
If you don't use a register in a routine then you, in effect, have preserved it, since it's the same value on exit as it was on entry.
I would like to see some documented authority on this issue, preferrably from MSDN. Unfortunately I searched there and turned up nothing. Anybody?
I would like to see some documented authority on this issue, preferrably from MSDN. Unfortunately I searched there and turned up nothing. Anybody?
iblis,
it is very hard to get in print, I have seen it specified in Borland documentation some time ago and I think it was in the C++ builder version 3.
It tends to be one of those things that is well known by compiler writers and assembler programmers over many years but there is little printed or internet suport for low level programming from major vendors so I don't know where to point you here.
Apart from the register convention, the only other ones I remember is keeping the direction flag clear and popping all of the FP registers on exit.
Let us know if you find some formal specifications on this from any of the major vendors as it may be useful as reference material.
Regards,
hutch@movsd.com
it is very hard to get in print, I have seen it specified in Borland documentation some time ago and I think it was in the C++ builder version 3.
It tends to be one of those things that is well known by compiler writers and assembler programmers over many years but there is little printed or internet suport for low level programming from major vendors so I don't know where to point you here.
Apart from the register convention, the only other ones I remember is keeping the direction flag clear and popping all of the FP registers on exit.
Let us know if you find some formal specifications on this from any of the major vendors as it may be useful as reference material.
Regards,
hutch@movsd.com
Hutch, I hope you understand that if Windows does not call your procedure, and it doesn't call Windows, then it doesn't matter what version of Windows your program runs on, because Windows will not actively search for call instructions in your code, place breakpoints on them and deliberately modify eax, ecx and edx when they are encountered. That would break most existing applications, and I trust Microsoft not to do crazy and purposeless things like that. Therefore you can have routines that return something in esi and ebp, but don't change edx, and they will still work forever.
Thanks all.
I have only tonight returned, and am very thankful for the attention. Actually, the reason for the question is a palindrome 196 test program I have been writing in HLA mode...
;**********
repeated addition of a number to its reverse with testing for the yielding of a palindrome, eg 121, 8448, 14641, etc
when we start with 196+691=887.... 887+788=1675... 1675+5761=7436... it seems as though the addition never yields a palindrome...
I just wanted to add my program to the countless others which perform the test.
;**********
I am using BCD additions and in this sense I am doing something similar to locke (in another thread)
I tested the routines in NASM programmed for DOS unreal mode - I had working programs (I developed several algorithms that all work - just at different speeds)...
Unfortunately, since the 32bit accesses (EAX instead of AX, AH) require a prefix byte in unreal mode, and segment overrides also require a prefix byte; the routines are severely slowed down compared to what I estimate max speed would be ... Furthermore, since the Pentium 4 seems to have a crippled handling of such prefixes, a 1.8 Ghz Pentium 4 operates at about the speed of a 166MHz Pentium MMX in unreal mode!!!!!
I ported the routines to HLA, but the program runs into some kind of error when I reach an 8 byte boundary in the addition. I suspect I am doing something wrong, and I should start a new post for special help in that project...
I suppose I may need a good HLA programmer to help me diagnose my problem if I cannot get any further with what you have said so far.
But I wanted to get general info on what to avoid in Win32 ASM programming...
Again thanks all.
I have only tonight returned, and am very thankful for the attention. Actually, the reason for the question is a palindrome 196 test program I have been writing in HLA mode...
;**********
repeated addition of a number to its reverse with testing for the yielding of a palindrome, eg 121, 8448, 14641, etc
when we start with 196+691=887.... 887+788=1675... 1675+5761=7436... it seems as though the addition never yields a palindrome...
I just wanted to add my program to the countless others which perform the test.
;**********
I am using BCD additions and in this sense I am doing something similar to locke (in another thread)
I tested the routines in NASM programmed for DOS unreal mode - I had working programs (I developed several algorithms that all work - just at different speeds)...
Unfortunately, since the 32bit accesses (EAX instead of AX, AH) require a prefix byte in unreal mode, and segment overrides also require a prefix byte; the routines are severely slowed down compared to what I estimate max speed would be ... Furthermore, since the Pentium 4 seems to have a crippled handling of such prefixes, a 1.8 Ghz Pentium 4 operates at about the speed of a 166MHz Pentium MMX in unreal mode!!!!!
I ported the routines to HLA, but the program runs into some kind of error when I reach an 8 byte boundary in the addition. I suspect I am doing something wrong, and I should start a new post for special help in that project...
I suppose I may need a good HLA programmer to help me diagnose my problem if I cannot get any further with what you have said so far.
But I wanted to get general info on what to avoid in Win32 ASM programming...
Again thanks all.
Sephiroth3,
You are correct in that if you purely run your own code once you have the application up and running, you don't have to observe the convention once you have protected the registers in the first place.
Once this is done, you can run procedures with no stack frame, pass information any way you want, use the base and stack pointer registers in non standard manners etc ...
This is fine and commonly known but you must in the first place set up the running program which involves interacting with the operating system and it is in terms of entry and exit that you must not make a mess of it. If you use an operating system function on the way through you then need to watch what you are doing as its own functions are written observing the convention.
Regards,
hutch@movsd.com
You are correct in that if you purely run your own code once you have the application up and running, you don't have to observe the convention once you have protected the registers in the first place.
Once this is done, you can run procedures with no stack frame, pass information any way you want, use the base and stack pointer registers in non standard manners etc ...
This is fine and commonly known but you must in the first place set up the running program which involves interacting with the operating system and it is in terms of entry and exit that you must not make a mess of it. If you use an operating system function on the way through you then need to watch what you are doing as its own functions are written observing the convention.
Regards,
hutch@movsd.com
Ah, I have now read the replies (No, I am not thoroughly confused).
Hutch, MASM32 is way above me right now.... I'm using HLA to program the palindrome thing in console mode. It has simple enough (for me) print, locate and memory functions...
But, the principles you gave above are what I was asking for. Is there any webpage I can visit to get more warnings?
At any rate, thanks much, all.
Here's my question, then...
Apart from API calls through HLA functions like print, etc, I am not using any procedures, subroutines, etc... The program does:
pal: print messages
add the entire number
print messges
test for palindrome
loopne pal...
ad infinitum.
Do I need to be avoiding any particular registers? But when I ported this to Windows, it is broken when I cross the first 8 byte boundary (decrementing ESI, EDI).
I do not save these registers anywhere, since the program is linear, and when I need values I just get them from the variables. I will start a new thread for this program, but am I doing anything wrong from what you can see?
Hutch, MASM32 is way above me right now.... I'm using HLA to program the palindrome thing in console mode. It has simple enough (for me) print, locate and memory functions...
But, the principles you gave above are what I was asking for. Is there any webpage I can visit to get more warnings?
At any rate, thanks much, all.
Here's my question, then...
Apart from API calls through HLA functions like print, etc, I am not using any procedures, subroutines, etc... The program does:
pal: print messages
add the entire number
print messges
test for palindrome
loopne pal...
ad infinitum.
Do I need to be avoiding any particular registers? But when I ported this to Windows, it is broken when I cross the first 8 byte boundary (decrementing ESI, EDI).
I do not save these registers anywhere, since the program is linear, and when I need values I just get them from the variables. I will start a new thread for this program, but am I doing anything wrong from what you can see?
V Coder,
If you are working with Randy Hyde's HLA probably the best person to ask is Randy himself as he has a forum here as well so it may be a good idea to post a question in there.
Randy has heaps of expertise and has been writing assembler for a long time and designed HLA from scratch so he is the right guy to ask.
Good luck and I hope you do OK with the project.
Regards,
hutch@movsd.com
If you are working with Randy Hyde's HLA probably the best person to ask is Randy himself as he has a forum here as well so it may be a good idea to post a question in there.
Randy has heaps of expertise and has been writing assembler for a long time and designed HLA from scratch so he is the right guy to ask.
Good luck and I hope you do OK with the project.
Regards,
hutch@movsd.com
Hutch, would you please care to read this post before you reply?
*) I have not been saying you should push/pop registers if you're not using them. I've been saying you should preserve them. Preserve, as I see it, mean push/pop (or save in some other way), if you're actually changing them.
*) The reason I've been saying "callbacks" is for the same reasons Thomas have stated as well: only in callbacks will you have to do the register preservation. Of course you will have to be prepared for called functions trashing registers, but that's an implicit "Duh!".
*) As said before, you're free to do anything you wish, in your own internal functions. Don't care to preserve registers? Don't. But document what you're doing. And again, I'm not advising people to do this in general.
At least Thomas, iblis and Sephiroth3 seem to have understood what I'm writing, and agree. Too bad you once again poisoned a thread with your endless crusades. (I'm sure you'll read _that_ part).
*) I have not been saying you should push/pop registers if you're not using them. I've been saying you should preserve them. Preserve, as I see it, mean push/pop (or save in some other way), if you're actually changing them.
*) The reason I've been saying "callbacks" is for the same reasons Thomas have stated as well: only in callbacks will you have to do the register preservation. Of course you will have to be prepared for called functions trashing registers, but that's an implicit "Duh!".
*) As said before, you're free to do anything you wish, in your own internal functions. Don't care to preserve registers? Don't. But document what you're doing. And again, I'm not advising people to do this in general.
At least Thomas, iblis and Sephiroth3 seem to have understood what I'm writing, and agree. Too bad you once again poisoned a thread with your endless crusades. (I'm sure you'll read _that_ part).
f0dder,
I will put aside some of the other implied content of your postings and remind you of what you have posted.
=============================================
Register preservation (as hutch) stated in CALLBACKS. And remember that any function you call can trash the registers (except for the regs you have to preserve).
=============================================
This is what you have said.
Sparing myself the various forms of posturing that have occurred in this thread, I refer you back to the dump of the WndProc procedure that I posted.
When you can find any of the registers EBX ESI & EDI, you have an argument to make for special treatment of a system callback. As those registers are NOT in the procedure, you are simply wrong.
I have picked you up before for exactly the same mistake of claiming that a system callback requires different register preservation rules to any other procedure. The assembler dump is not ambiguous, either the registers are there or they are not and as the exhaustive proof that the original windows convention is the only one that applies, the registers that are not used are not preserved.
It may suit your interests to keep flogging the same mistakes over and over again but it is unreasonable conduct to do this at the expense of another member who has posted a question to learn something basic about writing windows software.
To sum up what has been said here,
1. You have made an assertion that is wrong while attempting to correct advice that I gave to another member.
2. The assembler dump is exhaustive proof that you are wrong in having done so.
3. Reinterpretation of what you have said does not address the original mistake in what you have chosen to argue.
4. You have again used a thread posted by a member looking for information to start another argument.
Regards,
hutch@movsd.com
I will put aside some of the other implied content of your postings and remind you of what you have posted.
=============================================
Register preservation (as hutch) stated in CALLBACKS. And remember that any function you call can trash the registers (except for the regs you have to preserve).
=============================================
This is what you have said.
Sparing myself the various forms of posturing that have occurred in this thread, I refer you back to the dump of the WndProc procedure that I posted.
When you can find any of the registers EBX ESI & EDI, you have an argument to make for special treatment of a system callback. As those registers are NOT in the procedure, you are simply wrong.
I have picked you up before for exactly the same mistake of claiming that a system callback requires different register preservation rules to any other procedure. The assembler dump is not ambiguous, either the registers are there or they are not and as the exhaustive proof that the original windows convention is the only one that applies, the registers that are not used are not preserved.
It may suit your interests to keep flogging the same mistakes over and over again but it is unreasonable conduct to do this at the expense of another member who has posted a question to learn something basic about writing windows software.
To sum up what has been said here,
1. You have made an assertion that is wrong while attempting to correct advice that I gave to another member.
2. The assembler dump is exhaustive proof that you are wrong in having done so.
3. Reinterpretation of what you have said does not address the original mistake in what you have chosen to argue.
4. You have again used a thread posted by a member looking for information to start another argument.
Regards,
hutch@movsd.com
The register preservation rules are if you use them in a procedure, preserve EBX ESI & EDI (ESP and EBP if you are writing your own proc entry) while being able to freely modify EAX ECX & EDX and remember that another procedure can do the same thing.
Question.
My program does not have procedures per se. However, I make several API calls. Does Windows require certain registers to be the same value? Should I save the values of ESI EDI EBX at the start of the program, and after each system call, and restore them before the next system call?
V,
Just have a look at the PM I sent back to you. It save the other hassles that have occurred in here.
Regards,
hutch@movsd.com
Just have a look at the PM I sent back to you. It save the other hassles that have occurred in here.
Regards,
hutch@movsd.com
1. You have made an assertion that is wrong while attempting to correct advice that I gave to another member.
Nope, I wanted to expand upon it, not correcting it.
2. The assembler dump is exhaustive proof that you are wrong in having done so.
The assembler dump doesn't change anything. Let me quote iblis, since he was better able to phrase what I meant:
If you don't use a register in a routine then you, in effect, have preserved it, since it's the same value on exit as it was on entry.
3. Reinterpretation of what you have said does not address the original mistake in what you have chosen to argue.
If you have misinterpreted what I wrote, of course I have to try and rephrase it.
4. You have again used a thread posted by a member looking for information to start another argument.
Sorry, but it's your fault. I didn't want to start a war, I wanted to add some of the "dos and donts of win32 programming", as a bunch of these rules aren't really documented anywhere official. You misunderstood my post, I tried correcting, and as often before you went bonanza.
VCoder:
My program does not have procedures per se. However, I make several API calls. Does Windows require certain registers to be the same value? Should I save the values of ESI EDI EBX at the start of the program, and after each system call, and restore them before the next system call?
In a callback (wndproc, dlgproc, findwindows callback, windows hook, etc), you are required to preserve the values ebx,esi,edi,ebp,esp. To avoid hutch nitpicking, preserve means leave the callback with the same value as on entry. This means saving them if you modify them, otherwise nada.
Across your code, assume that any win32 function will trash eax,ecx,edx, but leave the other registers intact. You don't have to do any saving of ebx,esi,edi,esp,ebp throughout the "normal" code - you do have to make sure ESP remains 4-aligned though.
Oh, there's a few things other than that. Don't mess with selectors (cs,ds,es,fs,gs,ss) - those _will_ have to be restored before API calls if you change them. Also be sure to keep direction flag clear if you modify it (if you don't, don't worry).
f0dder,
Ther is no second prize for misadvising members, You added a condition to a well known convention, the dump proved you wrong. That is the end of the argument.
======================================
You misunderstood my post, I tried correcting, and as often before you went bonanza.
======================================
This is a matter you need to take up with the administration as I am not interested in your mistakes.
======================================
To avoid hutch nitpicking, preserve means leave the callback with the same value as on entry. This means saving them if you modify them, otherwise nada.
======================================
Likewise.
If you wish to start an argument, be careful of what you say, particularly as you are mistaken, if you want to make comments of this type, refer to the administration as I am not interested.
Regards,
hutch@movsd.com
Ther is no second prize for misadvising members, You added a condition to a well known convention, the dump proved you wrong. That is the end of the argument.
======================================
You misunderstood my post, I tried correcting, and as often before you went bonanza.
======================================
This is a matter you need to take up with the administration as I am not interested in your mistakes.
======================================
To avoid hutch nitpicking, preserve means leave the callback with the same value as on entry. This means saving them if you modify them, otherwise nada.
======================================
Likewise.
If you wish to start an argument, be careful of what you say, particularly as you are mistaken, if you want to make comments of this type, refer to the administration as I am not interested.
Regards,
hutch@movsd.com
This will be the last post of mine in this thread about this matter, as it seems to be pointless.
Hutch, I didn't post any wrong information in this thread. I should think others will agree.
I must, however, admit that what I posted wasn't 100% explicit cut-in-cardboard. It was obviously possible to misinterpret it - at least you did. If you're saying I'm trying to wiggle out of it by "reinterpreting what I have said", you're calling me a liar.
If you can find anything technically wrong with the following block of text, be sure to let me know. I think that's about the most explicit I can get.
In a callback (wndproc, dlgproc, findwindows callback, windows hook, etc), you are required to preserve the values ebx,esi,edi,ebp,esp. To avoid hutch nitpicking, preserve means leave the callback with the same value as on entry. This means saving them if you modify them, otherwise nada.
Across your code, assume that any win32 function will trash eax,ecx,edx, but leave the other registers intact. You don't have to do any saving of ebx,esi,edi,esp,ebp throughout the "normal" code - you do have to make sure ESP remains 4-aligned though.
Oh, there's a few things other than that. Don't mess with selectors (cs,ds,es,fs,gs,ss) - those _will_ have to be restored before API calls if you change them. Also be sure to keep direction flag clear if you modify it (if you don't, don't worry).
Hutch, I didn't post any wrong information in this thread. I should think others will agree.
I must, however, admit that what I posted wasn't 100% explicit cut-in-cardboard. It was obviously possible to misinterpret it - at least you did. If you're saying I'm trying to wiggle out of it by "reinterpreting what I have said", you're calling me a liar.
If you can find anything technically wrong with the following block of text, be sure to let me know. I think that's about the most explicit I can get.
In a callback (wndproc, dlgproc, findwindows callback, windows hook, etc), you are required to preserve the values ebx,esi,edi,ebp,esp. To avoid hutch nitpicking, preserve means leave the callback with the same value as on entry. This means saving them if you modify them, otherwise nada.
Across your code, assume that any win32 function will trash eax,ecx,edx, but leave the other registers intact. You don't have to do any saving of ebx,esi,edi,esp,ebp throughout the "normal" code - you do have to make sure ESP remains 4-aligned though.
Oh, there's a few things other than that. Don't mess with selectors (cs,ds,es,fs,gs,ss) - those _will_ have to be restored before API calls if you change them. Also be sure to keep direction flag clear if you modify it (if you don't, don't worry).
Thanks guys.
I now understand (before I THOUGHT I understood).
I'm still unsuccessfully debugging my program (I don't think I am breaking any Win32 rules...). It is written in HLA but I don't think the problem is HLA, so I will post it in a new thread. Actually, I have it written first NASM for DOS unreal mode as well. I will post it in that form and HLA for Win32. Perhaps someone can help me convert it to Win32 ASM, or an HLA programmer may be able to detect my bugs.
I now understand (before I THOUGHT I understood).
I'm still unsuccessfully debugging my program (I don't think I am breaking any Win32 rules...). It is written in HLA but I don't think the problem is HLA, so I will post it in a new thread. Actually, I have it written first NASM for DOS unreal mode as well. I will post it in that form and HLA for Win32. Perhaps someone can help me convert it to Win32 ASM, or an HLA programmer may be able to detect my bugs.
I have followed the discussion with interest. I think I understand all except one point that has always bugged me.
Is register preservation (notably ebx, esi, edi) required at all (by Windows) outside of procedures? I believe "no"?
Greg
Is register preservation (notably ebx, esi, edi) required at all (by Windows) outside of procedures? I believe "no"?
Greg
bushpilot,
ebx/esi/edi/esp/ebp is required in callbacks. (And you must keep in mind that any win32 api you call can destroy the other registers, and other functions registers according to their calling convention - luckily, just about everything follows win32 calling convention).
certain bits of eflags, segment registers, and perhaps a few more things must be preserved "always". That is, if you modify them, you must restore them before going out of scope of your own code (this include calling the win32 api). Furthemore, esp must always be 4byte aligned.
Again, note that "preserve" doesn't mind "blindy pushing/popping", but saving+restoring if you change them inbetween.
ebx/esi/edi/esp/ebp is required in callbacks. (And you must keep in mind that any win32 api you call can destroy the other registers, and other functions registers according to their calling convention - luckily, just about everything follows win32 calling convention).
certain bits of eflags, segment registers, and perhaps a few more things must be preserved "always". That is, if you modify them, you must restore them before going out of scope of your own code (this include calling the win32 api). Furthemore, esp must always be 4byte aligned.
Again, note that "preserve" doesn't mind "blindy pushing/popping", but saving+restoring if you change them inbetween.
v coder I think you can make hla output intermediate masm/fasm code so if there's any problem it can be looked at like that.