News:

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

Error checking and returns

Started by BlackVortex, June 30, 2009, 11:13:27 PM

Previous topic - Next topic

BlackVortex

I need some advice on goasm coding style.

OK, I had this code as an exported function from my dll, it just calls some APIs, this is pseudocode :

Export_1 FRAME param1,param2
USES EBX,ESI,EDI

invoke API1,  blah, blah
invoke API1,  blah, blah
invoke API1,  blah, blah
invoke API1,  blah, blah

RET
ENDF


Then I wanted this export function to check after API for errors and return with eax = error code
1 if the first API fails,2 if the second fails etc.

I ended up with this, which seems to work great, but doesn't look too good, kinda convoluted :
Export_1 FRAME param1,param2
USES EBX,ESI,EDI

invoke API1,  blah, blah
or eax,eax
jnz >
mov eax, 1
jmp @return
:

invoke API1,  blah, blah
or eax,eax
jnz >
mov eax, 2
jmp @return
:

invoke API1,  blah, blah
or eax,eax
jnz >
mov eax, 3
jmp @return
:

invoke API1,  blah, blah
or eax,eax
jnz >
mov eax, 4
:
@return:
RET
ENDF

So, if successful, eax = a handle from the last API call, otherwise if an error happened, it's 1,2,3 or 4
Notice I'm using jumps to the epilogue to avoid multiple epilogue code generation.

1) Any remarks or advice for improvement ? (I haven't looked into macroing)
2) Is my choice of "or eax,eax" correct/the best ?

BlackVortex

With a method jj2007 showed me now the code is like this :
Export_1 FRAME param1,param2
USES EBX,ESI,EDI
xor ebx, ebx   ; error counter
invoke API1,  blah, blah
inc ebx
test eax, eax
jz > @returnERR

invoke API1,  blah, blah
inc ebx
test eax, eax
jz > @returnERR

invoke API1,  blah, blah
inc ebx
test eax, eax
jz > @returnERR

invoke API1,  blah, blah
inc ebx
test eax, eax
jnz > @returnOK

@returnERR:
mov eax, ebx      ; return the error code

@returnOK:
RET
ENDF

Tested on my real code and working fine   :U

Improvement, since it got rid of some labels and some jumps   :bg

jorgon

Hi BlackVortex

In your amended source, the code labels @returnERR and &returnOK are public symbols (in the help file, GoAsm calls them "unique labels").  They end up in the symbol table and can be accessed by other functions.  This also means you can only use them once in your prog too, which may not be the intention.

Instead you can use locally scoped labels which begin with a dot for example .returnERR: or .returnOK:
In your function these would end up in the symbol table as "Export_1.returnERR" and "Export_1.returnOK".  Such labels are useful for clarity in your source code and for debugging.  You can also refer to these labels from outside the function if you wish.

A third alternative is to use "unscoped re-usable labels", which are just digits or a single character and a digit, for example L1: or L2: or Z56.44 or, as in your first example, just a colon.  These are not put in the symbol table at all and can be repeated anywhere in your source.

Author of the "Go" tools (GoAsm, GoLink, GoRC, GoBug)

BlackVortex

I guess I will replace all occurences of the 2 labels with L1: and L2:  ? If I have problems I will report back.

BlackVortex

Hehe, now I remembered why I skipped the "Labels: unique, re-usable and scoped" chapter of the documentation. Kinda hard  :wink

And I noticed my previous code used long jumps everywhere ! Also, it refused to compile because the first jump was 7 bytes more "distant" from the label, so I had to force it to long.

Shouldn't goasm automatically use short when possible, and long where needed ?

Anyway, now my code looks like :
Export_1 FRAME param1,param2
USES EBX,ESI,EDI
xor ebx, ebx   ; error counter
invoke API1,  blah, blah
inc ebx
test eax, eax
jz long > L1          ; forced long jump to avoid assembly error

invoke API1,  blah, blah
inc ebx
test eax, eax
jz > L1

invoke API1,  blah, blah
inc ebx
test eax, eax
jz > L1

invoke API1,  blah, blah
inc ebx
test eax, eax
jnz >

L1:
mov eax, ebx      ; return the error code

:
RET
ENDF

jorgon

QuoteShouldn't goasm automatically use short when possible, and long where needed?

GoAsm does this automatically with backward jumps.  But in the case of forward jumps, as a single-pass assembler GoAsm cannot do this, because at the time of coding it does not know how far ahead the label being jumped to is going to be.

By the way, instead of using the word "long" you can double-up the direction indicators, eg.

JZ >>L1

which you might think looks a little better.  Optionally you can also use the backward direction indicators "<" and "<<" to make it easier to follow the direction of code execution upon the various conditions in the case of backward jumps.

If you find you need a lot of long jumps in your code, you can consider putting some of the code into a function, and then calling that function from the main code.  Personally I find this is quite a good discipline which helps to make the source code easier to read.
Author of the "Go" tools (GoAsm, GoLink, GoRC, GoBug)