News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Is EDX or EAX more volatile?

Started by CCurl, October 17, 2015, 12:31:37 AM

Previous topic - Next topic

CCurl

Sorry about all the newbie questions ...

Between EAX and EDX, which is more volatile? In other words, by convention, which one is considered to be more of a "don't expect it to be preserved" register?

I am guessing the answer is EDX, but wanted to get your views.

gelatine1

Eax contains the return value for any procedure and thus will be altered quite often. Edx will be changed quite often too but not as much as Eax. I hope that's what you were wondering?

jj2007

eax, ecx and edx get changed by WinAPI calls.

In a sense, eax is more "valuable" because, as Gelatine pointed out, it returns values that you still may need.

MasmBasic functions leave ecx intact because it's a somewhat special register used for several unique opcodes:
  jecxz @F
  loop
  shl eax, cl

Unfortunately Microsoft chose to make it trashable, so ordinary WinAPI calls destroy ecx.

In short: edx is real trash, consider caring for ecx through a push ecx ... do stuff ... pop ecx.

CCurl


In short: EDX is real trash, consider caring for ECX through a push ECX ... do stuff ... pop ECX.



EAX contains the return value for any procedure and thus will be altered quite often.


This is exactly the sort of information I was looking for. Rules I will strive to live by:

1) I will use EAX as the return value when appropriate.
2) If I need EDX to survive, I will PUSH and POP it before and after calling a subroutine.
3) My subroutines will NOT worry about protecting EDX.
4) My subroutines WILL protect registers other than EDX (and EAX when returning something).

Thanks!

jj2007

> If I need EDX to survive

You meant ecx, I suppose.

Windows functions (almost) always return their results in eax. If you look under the hood of a C/C++ program, return somevar means "put the value of that local var into eax, then return to the caller".

In assembler, you enjoy a luxury: You can return more than one value. For example, Instr_("Sometest", "test") returns, not surprisingly, 5, in the sense that Print Str$(Instr_("Sometest", "test") would show 5 in the console. However, under the hood (i.e. with a debugger like Olly) you could see that the 5 are actually returned in edx, while eax holds a pointer to "test". Therefore,
.If Instr_("Sometest", "test")
  Print eax
.endif

is a perfectly valid use, it will print test.

Another very flexible option to return multiple values is to pass a pointer to a structure in memory, and to fill that structure in your proc.

CCurl

Quote from: jj2007 on October 17, 2015, 05:42:49 AM
> "If I need EDX to survive" ..
You meant ecx, I suppose.
No, I actually meant EDX. Preserving ECX is covered under rule #4. :)

Good info about the ability to return >1 result.

hutch--

CCurl,

The use of registers in Windows 32 bit conforms to what is called the "Intel Application Binary Interface" (ABI for short) and to successfully write 32 bit Windows software you need to both understand it and write software that is compatible with it.

The specification is that EAX ECX & EDX are transient registers that any procedure can modify while the operating system requires that you preserve the rest between calls. Generally this means you can use EBX ESI and EDI in normal procedures as long as you preserve them. EBP is used as the base pointer and if you write a procedure without a stack frame you can preserve and use EBP as well. The stack pointer ESP is best left alone unless you know exactly what you are doing. In some very unusual contexts you can save ESP to memory or an MMX or XMM register but its really easy to make a mess of it.

The distinction worth understanding is between the caller and what is called. If the procedure you are writing is only called by another, you can trash EAX ECX and EDX and not have to bother about them but if the procedure you are writing calls another procedure AND it uses any of EAX ECX and EDX, then they also can be overwritten by the procedure that is called so if your own procedure uses them you then must preserve them yourself.

As mentioned before, EAX is by convention used as a single return value but you can also use ECX and EDX for return values which is very handy in assembler. If you have to return much more data you have memory based options, global variables are useful here and this is an old technique from the Win3.? era with DLLs that had very limited stack space. Probably the most common approach for multiple return values is to pass the address of a structure to the called procedure.

The "Intel Application Binary Interface" may sound complicated but in practice its easy enough to manage and it is also very efficient.

CCurl

OK ... new rules:

1) I will use EAX as the return value when my subroutine returns a single value. Other options exist if returning >1 value.
2) If my caller needs any of (EAX, ECX, or EDX) to survive a subroutine call, I will PUSH them before and POP them after calling the subroutine.
3) My subroutines do NOT need to worry about protecting EAX, ECX, or EDX.
4) My subroutines WILL protect registers other than those identified in Rule #3.

I am on board with this, and life is good.