News:

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

Side effects of print udword$

Started by llkooj, December 03, 2008, 05:21:09 AM

Previous topic - Next topic

llkooj

I am using print udwords$[EDX] but is overwriting the contents of EDX. What can I do to avoid this?

KeepingRealBusy


llkooj

I have thought of that, however, it is not workable solution for me. print affects EAX, ECX and EDX. Does not seem to affect EBX. Since I have more than one register to print I would only be able to save one register in EBX.  Also, I must print from registers, printing from variables will not work. Is there a function that prints without messing with register?

donkey

The rule for the Windows ABI (Application Binary Interface) is as follows...

EBP and ESP - Used for stack manipulation - do not use unless you know exactly what you are doing
EBX, ESI and EDI - Are always preserved by a procedure - the USES directive can do this for you
EAX, ECX and EDX - Are not guaranteed to be preserved and are considered volatile - do not ever count on them being preserved becasue they probably aren't or won't be in the next release

So no, you are not likely to find any function that will preserve EDX faithfully from version to version, also remember that registers even when preserved by a procedure are not guaranteed to remain stable during the life of that procedure only that they will have the same value on exit as on entry. You are probably best off to write your own function if you really need them preserved.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

donkey

By the way you can preserve them yourself

...
push eax
push edx
push ecx
print udword$(ecx),10,13
pop ecx
pop edx
pop eax
...


Or if time isn't a factor (though the actual difference in execution time is negligible) you can use pushad and popad instead of individual registers...

...
pushad
print udword$(ecx),10,13
popad
...
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

llkooj

Thanks for your replies. It worked  :dance:

KeepingRealBusy

I personally dislike the ABI very much. I hate unexpected changes and side effects. I fought this problem (side effects) for 40 years.

For my procedures (I do not use functions), I always save ALL registers and flags, including eax. I only pass back status flags (sign zero carry). If something needs to be returned, there is a parameter which is an OFFSET to some location in which to store the return value or structure. I preserve the direction flag (Windows API doesn't always do this).

To insure that I always know when a function name is an API, I define all APIs as APIxxxx and then equate APIxxxx to xxxx.

I build wrappers for all of the APIs:


    CreateAFile      PROTO,
                         szFilename:PBYTE,
                         pHandle:PDWORD
;Returns status flags, jz good, jnz bad

    APICreateFile    PROTO,
                         szFilename:PBYTE,
                         dAccess:DWORD,
                         dShareMode:DWORD,
                         pSecurity:DWORD,
                         dCreationDisposition:DWORD,
                         dFlagsAndAttributes:DWORD,
                         hTemplate:DWORD
;Returns handle
                         

    CreateAFile PROC USES EAX EDI,
                    pName:PBYTE,
                    pHandle:PDWORD

    PUSHAD
    PUSHFD

    INVOKE APICreateFile,param,param...
    mov     dAPIReturn,eax

    POPFD
    POPAD

    mov    eax,dAPIReturn
    cmp    eax,INVALID_HANDLE
    jz       Bad
    mov   edi,pHandle
    mov   [edi],eax
    mov    dError,ERROR_DEBUG_DONE
    jmp    Exit
Bad:

    PUSHAD
    PUSHFD

    INVOKE APIGetLastError
    mov    dLastAPIError,eax

    POPFD
    POPAD

    mov    dError,ERROR_API_ERROR
Exit:
    INVOKE SetLastDebugError,dError ;saves last error in dLastDebugError and compares to ERROR_DEBUG_DONE, returns compare flags
    ret
APICreateFile ENDP

;for a call:

    INVOKE CreateAFile,OFFSET szName,OFFSET hHandle
    jnz        error
    jz          good


I also find it disconcerting that INVOKE will destroy eax if passing a parameter as a byte i.e. INVOKE func,edx,bl. I make it a habit of always defining procedures that need a byte value as requiring the low byte of a dword, thus all my INVOKE parameters preserve eax (eax just doesn't get used).

I know that everyone will hate my coding style, but I can pick up my code that I wrote in the early 80's and understand what I was doing, so I can convert it to something different.

Dave.

donkey

Well, "hate" is a strong word, I don't particularly agree that there is a necessity for preserving volatile registers or for explicitly preserve those that are non-volatile. I am not sure which API function modifies the direction flag and does not restore it to its original state (or at least clear it on exit), or one that does not restore the non-volatile registers. An example here would be useful though after all this time if I haven't run into them the APIs must be quite esoteric indeed (could be that I have heard of them before but since I don't use them I filed the knowledge under "Britanny Spears" that is "noted and promptly forgotten").

As for the INVOKE issues, they are an artifact of MASM and since I use GoAsm are not issues that I have to deal with, GoAsm does not use EAX in INVOKE when using a stack based address as a parameter, rather it does the math directly on the stack using EBP/ESP and avoids the use of EAX.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Tedd

I think the point is simply that some set of rules is to be followed.
Windows prescribes one set, KeepingRealBusy prefers another, and others may choose yet another. The argument of which is better is quite pointless. It doesn't really matter as long as it's consistent (and documented.)
Many will follow the windows 'rules' for no better reason than because it's there and they already must follow it to use the windows api. KeepingRealBusy's rules aim for purity (preserve state and avoid side effects.)



Quote from: KeepingRealBusy on December 03, 2008, 06:09:49 PM
I personally dislike the ABI very much. I hate unexpected changes and side effects. I fought this problem (side effects) for 40 years.
The ABI contains a consistent set of rules - you know in advance what will be changed and what will not, these changes aren't unexpected (if some functions didn't follow that then it's a problem, but the ABI itself is still correct.) It's only a problem if you choose to treat it as one - something must be changed somewhere, otherwise the cpu does nothing, the only question is at what level that occurs. The windows ABI was actually formed for the sake of C compilers (another reason why it must be consistent and predicatable.) It's simply a trade-off between preserve-nothing and preserve-everything (both of which are unnecessary extremes.)

As for 'invoke' - that's an masm helper, it's not part of the abi - it uses eax as an intermediate because functions return a value in eax, so it's supposedly harmless when the function itself will corrupt that register anyway.

QuoteI know that everyone will hate my coding style, but I can pick up my code that I wrote in the early 80's and understand what I was doing, so I can convert it to something different.
That's simply a consistency issue. As long as some set of rules is followed, you can do this with any code and any 'abi.'
No snowflake in an avalanche feels responsible.

jj2007

Quote from: donkey on December 03, 2008, 06:54:05 PMI am not sure which API function modifies the direction flag and does not restore it to its original state

I am also eager to see examples of such esoteric API's, but it sounds really like hearsay. The quote below concerns a rare condition - an exception in a function where the flag is set, followed by an exception handler. While such handlers are a question of personal taste (I prefer a neat crash), if you really need one, start it with a cld...

FIX: Direction Flag Is Not Cleared When an Exception Occurs
All C run-time functions correctly clear DF upon termination. However, if an exception occurs before a function has a chance to clear DF, the flag will still be set when the exception handler is executed. This will cause code in the handler (which assumes that DF is 0) to fail.

KeepingRealBusy

JJ,

QuoteQuote from: donkey on December 03, 2008, 11:54:05 am
I am not sure which API function modifies the direction flag and does not restore it to its original state

My point exactly. If the DF was set on entry, and reset on return, then by definition, it was changed. Allowable by ABI, but that is why I always preserve it.

Dave.

Vortex

Quote from: donkey on December 03, 2008, 06:54:05 PM
As for the INVOKE issues, they are an artifact of MASM and since I use GoAsm are not issues that I have to deal with, GoAsm does not use EAX in INVOKE when using a stack based address as a parameter, rather it does the math directly on the stack using EBP/ESP and avoids the use of EAX.

GoAsm pushes EBP and modifies ESP to reflect the correct address of the parameter pushed onto the stack.

An attempt to simulate this with Masm :


.386
.model flat,stdcall
option casemap:none

include     \masm32\include\windows.inc
include     \masm32\include\kernel32.inc
include     \masm32\include\masm32.inc

includelib  \masm32\lib\kernel32.lib
includelib  \masm32\lib\masm32.lib

main        PROTO
uppercase   PROTO :DWORD

.code

start:

    call    main
    invoke  ExitProcess,0

main PROC

LOCAL string[12]:BYTE
LOCAL dummy:DWORD

    mov     DWORD PTR [esp+4],'tset'
    mov     DWORD PTR [esp+8],'rts '
    mov     DWORD PTR [esp+12],'gni'
    push    ebp
    add     DWORD PTR [esp],-3*4    ; correct the value of [esp] to point "string"
    call    uppercase               ; convert the string to upper case
    invoke  StdOut,eax
    ret

main ENDP

END start

[attachment deleted by admin]