News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

PopAD and PushAD

Started by rshadarack, August 01, 2005, 12:21:40 AM

Previous topic - Next topic

rshadarack

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?

Randall Hyde

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

rshadarack

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?

hutch--

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
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Jeff

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.

Mark Jones

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
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

diablo2oo2

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

Phoenix

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

rshadarack

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.

QvasiModo

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.

Jeff

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?

hutch--

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.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Mark Jones

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?
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

Petroizki

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.

hutch--

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.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php