I've been just starting to learn assembly for a few weeks now and I've been trying to just make some random programs (mostly reinventing wheels), but I've been having a problem with pushad and popad.
One of the first things I did was make my own multiply procedure:
Mult PROC num1:PTR DWORD, num2:DWORD
pushad
mov eax, 0
mov ebx, num2
mov ecx, 32
mov esi, num1
@@MultLoop:
shl ebx, 1
.IF CARRY?
mov edx, [esi]
sub ecx, 1
shl edx, cl
add ecx, 1
add eax, edx
.ENDIF
LOOP @@MultLoop
mov [esi], eax
popad
ret
Mult ENDP
Which I've tested fairly throughly and seems to work fine. Now I only mention this because I use the mult procedure in my string to decimal procedure. However, something weird has been happening. When I call the procedure with the following code:
call DumpRegs
INVOKE mult, ADDR temp, temp2
call DumpRegs
I get the output:
EAX=00000000 EBX=00404003 ECX=00000001 EDX=00000004
ESI=00000007 EDI=77F517E6 EBP=0012FFB4 ESP=0012FF84
EIP=0040109B EFL=00000202 CF=0 SF=0 ZF=0 OF=0
EAX=0012FFAC EBX=00404003 ECX=00000001 EDX=00000004
ESI=00000007 EDI=77F517E6 EBP=0012FFB4 ESP=0012FF84
EIP=004010AC EFL=00000202 CF=0 SF=0 ZF=0 OF=0
Which doesn't make any sense because EAX has changed values. Where am I going wrong?
You've been burnt by the INVOKE command. If you use ADDR, it wipes out the value in EAX. One of the annoyances that caused me to use a completely different approach (including generating some ugly code, sometimes) with HLA. BTW, EAX gets modified *before* the call (and, hence, before the pushad).
Cheers,
Randy Hyde
Man, spent a good 3 hours trying to debug that thing....
Thanks, but I'm still a bit confused. You said that ADDR changes the value of EAX. Why exactly does it do this? And more importantly, why does my book (Assebly Language for Intel-Based Computers) not mention it...
Is there any way to make this work while still just working with plain MASM (i.e. not HLA)? Two obvious work arounds are to use push/pop before and after the INVOKE, and just not use eax when using INVOKE, but I would imagine there is something better... no?
rshadarack,
Welcome on board. Just a word of wisdom that will save you problems in the future. Learn the Windows specified convention on register preservation and it will help you to generate far more reliable code that is reusable in any context.
PUSHAD and POPAD are in fact useful in some debugging circumstances but they are slow and cover up something that can be solved easily. If you are using a normal stack frame procedure that already handles ESP and EBP try tis method out.
MyProc proc args etc ......
push ebx
push esi
push edi
; write your code here
pop edi
pop esi
pop ebx
ret
MyProc endp
This is the register convention that ALL of the Windows API functions assume and the operaing system uses at the programming interface level. If you don't modify any of the three registers in this context, then you can remove the PUSH/POP pairs for that register. Now there is an exact reciprocal in a procedure where you call another that uses this convention, the EAX ECX and EDX registers can be modified by any procedure that uses the system convention so if you have values loaded in EAX ECX or EDX, you must preserve and restore them around an API call or other that uses this convention.
The real trick is to use EBX ESI and EDI for non changable registers where you can so you don't have to do the additional work around each API or similar call.
In code that you write further up a call tree, you can in fact preserve the system defined registers and then use all of them in any way you like but this code cannot interact with the OS as it is deviant in register usage.
Just as an aside when you are debugging code, you can use both register and flag preservation instructions that allow you to display txt at the console or show a message box so you know what is going on in an algo.
pushad
pushfd
; do what you like here
popfd
popad
Quote from: rshadarack on August 01, 2005, 02:37:47 AM
Man, spent a good 3 hours trying to debug that thing....
Thanks, but I'm still a bit confused. You said that ADDR changes the value of EAX. Why exactly does it do this? And more importantly, why does my book (Assebly Language for Intel-Based Computers) not mention it...
Is there any way to make this work while still just working with plain MASM (i.e. not HLA)? Two obvious work arounds are to use push/pop before and after the INVOKE, and just not use eax when using INVOKE, but I would imagine there is something better... no?
it depends on the arguments you are using for the invoke. usually when you want to pass the "ADDR" of a stack based variable, MASM will use the eax register to
LEA the address. if you look at your listing, it would look something like this:
;INVOKE mult, ADDR temp, temp2
PUSH temp2
LEA eax,temp
PUSH eax
CALL mult
one of the reasons why INVOKE can be unreliable at times when you need all registers preserved. there's many ways to get around this manually.
In addition to Hutch's comments, when in "sticky situations" where EAX,ECX,and EDX are being overwritten, one could tell MASM to make the procedure itself preserve EBX,ESI, and EDI so they can be used safely in the entire procedure. Like this:
MyProc proc uses ebx esi ; ebx and esi are automatically preserved
; code here
ret
MyProc endp
i like to restore all register after a call. so i often use this style:
something proc
LOCAL retvalue:dword
pushad
;...code here
popad
mov eax,retvalue
ret
something endp
QuoteIn code that you write further up a call tree, you can in fact preserve the system defined registers and then use all of them in any way you like but this code cannot interact with the OS as it is deviant in register usage.
Does this mean, that code like this can cause problems?
AnyProc PROC USES ebx hWnd:DWORD,bSpin:DWORD
Invoke GetWindowLong,hWnd,0
mov ebx,eax
ASSUME ebx:PTR CpuPgBar
' Some code here
mov [ebx].dwValue,eax
sub SDWORD PTR eax,[ebx].dwMinVal
mov [ebx].dwRelPos,eax
Invoke dwtoa,[ebx].dwValue,ADDR [ebx].szBuffer
Invoke StrLen,ADDR [ebx].szBuffer
mov [ebx].dwStrLen,eax
Invoke MultiByteToWideChar,CP_ACP,0,ADDR [ebx].szBuffer,-1,ADDR [ebx].szWBuffer,[ebx].dwStrLen
' Some code here
ASSUME ebx:NOTHING
ret
AnyProc EndP
More exactly: if i use ebx in this context, and there are API-calls like MultiByteToWideChar while ebx is the base address of a structure, can this cause problems? I did never notice any problems with this.... but i'm a little confused now... :eek
QuoteThe real trick is to use EBX ESI and EDI for non changable registers where you can so you don't have to do the additional work around each API or similar call.
Ah, ok, I see now. So I should save all my data in either the memory or ebx, esi, and edi registers when calling functions, as I can't assume eax, ecx, and edx won't be changed.
Thanks for the help guys, I'll probably be back for more shortly.
Also make sure your callback procs preserve EBX, ESI and EDI. For example window procedures.
The direction flag can cause trouble as well, if it's not cleared weird things may happen. String APIs usually work backwards, and some other calls generate GPFs.
Quote from: QvasiModo on August 03, 2005, 09:58:51 PMThe direction flag can cause trouble as well, if it's not cleared weird things may happen. String APIs usually work backwards, and some other calls generate GPFs.
quick question on that, would it be safe to assume that the direction flag will ALWAYS be cleared after using API functions or anything else other than manually setting it or do functions that would end up setting it exist?
Thats the theory Jeff but I would not trust any mixed code to reliably do i. For the cost of a single CLD you will never have a problem.
Quote from: QvasiModo on August 03, 2005, 09:58:51 PM
The direction flag can cause trouble as well, if it's not cleared weird things may happen. String APIs usually work backwards, and some other calls generate GPFs...
So a misplaced CLD could affect, say, lstrcat?
Quote from: Mark Jones on August 04, 2005, 03:07:06 AMSo a misplaced CLD could affect, say, lstrcat?
Yes, if the direction flag is set, and you call lstrcat for example without using
cld, then the output will very likely be affected.
Personally i never use the cmps and other string instructions, so i no need to worry about this.
QvasiModo,
Just a point of clarification for people who need to properly understand this stuff.
> Also make sure your callback procs preserve EBX, ESI and EDI. For example window procedures
I gather here you mean preserve in the sense of IF you modify any of these registers THEN you need to preserve them first and restore them after ? I gather that you don't intend to blindly push and pop registers for no purpose.
Quote from: hutch-- on August 04, 2005, 07:24:02 AM
QvasiModo,
Just a point of clarification for people who need to properly understand this stuff.
> Also make sure your callback procs preserve EBX, ESI and EDI. For example window procedures
I gather here you mean preserve in the sense of IF you modify any of these registers THEN you need to preserve them first and restore them after ? I gather that you don't intend to blindly push and pop registers for no purpose.
Yes, that's exactly what I mean. Only push registers that your procedure actually modifies. :)