News:

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

INVOKE / arguments error?

Started by unktehi, February 27, 2009, 02:59:10 AM

Previous topic - Next topic

jj2007

#15
Quote from: ToutEnMasm on February 28, 2009, 07:41:10 AM
Testing for signed numbers ,you must declare them as signed (SDWORD),if you don't do,the ".IF Sign? " don't work.

Sign? is not dependent of the variable type, see snippet below. What you probably mean is the usage of

.if MyVar<0

A popular error is

.if eax<0

This condition is never true, because registers are by definition unsigned, i.e. -1 is ffffffffh, a high positive number. You can avoid such trouble by using an equate (somewhere in the .const segment, e.g. after the PROTOs):

signed   EQU sdword ptr

.if signed eax<0

This works fine.

include \masm32\include\masm32rt.inc

SignTest PROTO: DWORD, :SDWORD

.code

start:
invoke SignTest, 0, 0
getkey
exit ; short form of invoke ExitProcess, 0

SignTest proc v1unsigned:DWORD, v2signed:SDWORD

dec v1unsigned ; set flags
.if Sign?
print "v1 sign", 13, 10
.else
print "v1 no sign", 13, 10
.endif

dec v2signed ; set flags
.if Sign?
print "v2 sign", 13, 10
.else
print "v2 no sign", 13, 10
.endif
ret
SignTest endp

unktehi

Ok, I THINK I'm understanding more - either that or am more confused than ever.  BUT, I made a few modifications:

1) I took out:

   push ebp       
   mov ebp, esp

if using STDCALL shouldn't this be automatically written?       

2) I changed valueY:DWORD, valueX:DWORD to valueY:SDWORD, valueX:SDWORD.  In addition, I changed their declarations in the .data section to also reflect signed double words.

However, now I'm getting an error that I have a conflicting parameter definition with valueX and valueY - and I can't see where it's coming from if I changed it both in the data section and in the argument types of the procedure. Any ideas on this?

Here are a few questions I still don't seem to understand:

1. I'm using the arguments/parameters of valueX and valueY - but being I need to modify those values, do I need to create local variables with the LOCAL directive, in addition to having those arguments/parameters in the PROC call?

Thanks for your help.  I'm sorry if I seem so ignorant.  I'm really (obviously) struggling with this.

jj2007

Honestly, you should not waste your time here posting questions and waiting hours until an answer arrives. There are excellent manuals on the web: Webster, Microsoft and the Masm reference here on the Forum. You will see that it helps.

PBrennick

When you define data in the data section you give them unique names. In your case you chose ValueY and ValueX. So far so good. Now you can send those values as declaractions to a procedure using:

invoke GCD, ADDR valueX, ADDR valueY

Which is what I assume you are doing. Again, so far so good. Now, the problem you cannot do this:

GCD PROC valueY:SDWORD, valueX:SDWORD

You need to make different names in the proc statement such as:

GCD PROC MyvalueY:SDWORD, MyvalueX:SDWORD

Then in the procedure, you will use MyvalueX and MyvalueY as Pointers to access valueX and valueY. If you use those same names twice you will get a re-definition type error. Remember, also that Pointers are already pointing at the real addresses, so you will not use addr of offset in the procedure itself, you would use:

    mov eax, MyvalueX                ; to get the value that is stored in valueX.

or

    mov eax, MyvalueY                ; to get the value that is stored in valueY.

To access the data directly instead of through the proc, THEN you would do this:

    push ebx
    mov  ebx, offset valueX
    mov  eax, [ebx]
    pop  ebx

or

    mov eax, [MyvalueX]

for instance. You would use that method IF you were not declaring this stuff in the proc statement and was using the following instead:

GCD PROC

or

call GCD

Pointers are a VERY useful part of the invoke statement. You just need to understand that the procedure is declaring pointers to addresses and NOT addresses.

Paul


The GeneSys Project is available from:
The Repository or My crappy website

unktehi

Actually, I am declaring them in the data section different as so:

value1 SDWORD ?
value2 SDWORD ?

then in my main method, it's calling the PROC with this:

invoke GCD, value1, value2

So, when the actual procedure is called, it is called like this:

GetCommonDivisor PROC uses ecx valueY:SDWORD, valueX:SDWORD

Within my procedure I am not referring to them as value1 or value2, but rather valueX and valueY.

In this case, I'm assuming value1 is being placed into valueX and value2 is being placed in valueY.  Is that correct?



BogdanOntanu

Quote from: unktehi on February 28, 2009, 07:54:55 PM
Actually, I am declaring them in the data section different as so:

value1 SDWORD ?
value2 SDWORD ?

First of all you do NOT need to declare them as being signed. Basically the Euclidian GCD algorithm works with unsigned numbers. Hence you just need to make sure that you invoke the GCD procedure with unsigned numbers.

Quote
then in my main method, it's calling the PROC with this:

invoke GCD, value1, value2

What is the use of word "method" in here? Method means something in Object Oriented Programming and hence the use of method instead of procedure is confusing.

Then,

Procedures have arguments and local variables. You do not need local variables in this simple case but you do have two arguments.
Arguments can be sent to procedures in two ways:
1) by value
2) by pointer

Again in this case you do NOT need to concern yourself with sending arguments by pointers. You only have 2 values to send to you procedure and this is fine.

Hence when you write:
invoke GCD, value1, value2

Then the contents of the variable named value1 (let us say 54) and the contents of the variable named value2 (let us say 18) are sent to your procedure by the assembler. For a STDCALL kind of procedure what happens behind the scenes is in fact this:

push [value1]
push [value2]
call GCD


Observe the very poor choice of your variable names... in my above text I was going to say: " the value at variable value1 is sent..." and this can be confusing.

Quote
So, when the actual procedure is called, it is called like this:

GetCommonDivisor PROC uses ecx valueY:SDWORD, valueX:SDWORD

Again try to be careful with wording here. What you write above is in fact the definition of the procedure NOT the way that the procedure is called. Again this can confuse people trying to help you.

As I have said before you do not need signed dwords here as the algorithm works with unsigned values.

Another advice is to keep the procedures very simple. Use only the minimal needed code. This helps understanding.

Beside there is no purpose to test for sign and convert to unsigned at every invocation of this procedure. This test should be done into another procedure that would handle getting input from the user and converting it to unsigned or in the main procedure or area of your program but outside and before invoking GetCommonDivisor.

Using my previously simple GCD example "your" GCD procedure should look something like this:

GetCommonDivisor PROC uses ecx edx valueY:DWORD, valueX:DWORD

xor edx,edx
mov eax,[valueY]
mov ecx,[valueX]
div ecx

.if edx == 0
mov eax,[valueX]
ret
.else
invoke GetCommonDivisor,[valueX],edx
.endif

ret
GetCommonDivisor ENDP


This was written for ease of reading and understanding and was not optimized ;)

Now if you invoke this procedure like this:
invoke GetCommonDivisor, 54, 18

Then you should obtain the result 6 in EAX register.

Of course this is only a simple test and since you do not want constants in you code you should invoke it like this:
invoke GetCommonDivisor, [value1], [value2]

Please observe my personal preference for using [] around variables in order to signal "the contents of variable" as opposed to "the address of variable". However In MASM syntax this is not a common practice because you can use the plain variable name for the same thing and MASM will add the [] in code but I consider this confusing for beginners.

In  MASM when you do need the pointer to a variable (the address of a variable)  then you must write either "OFFSET my_variable" or "ADDR my_variable".

ADDR is commonly used for LOCAL variables inside of a procedure (you have none in this example) while OFFSET is commonly used for global variables defined in the DATA or DATA? sections (for example your value1 and value2 variables). However in this example you do not need to use pointers.

Quote
Within my procedure I am not referring to them as value1 or value2, but rather valueX and valueY.

This is normal.

Both arguments and local variables inside a procedure are automatically created by the assembler at runtime and destroyed when the procedure ends (this is on of the use of stack frames). Those variables are valid only inside a procedure and exist only during the procedure code.

One advantage is that unlike global variables there can be multiple variables with the exact same name as long as they reside in different procedures. Another advantage is that locals are usually thread safe.

It is common practice to reduce the usage of global variables to a minimum and use local variables inside procedures for most needs. However for a beginner it is better that he/she does master the use of globals before advances to locals. Again in your example there is no need for a local variable.

Quote
In this case, I'm assuming value1 is being placed into valueX and value2 is being placed in valueY.  Is that correct?

Oh well I sense a confusion here...

What happens is that:
1) the contents of value1 is placed and made available at valueX argument
2) the contents of value1 is placed and made available at valueX argument

However there is no magical link in between the two (variable and arguments). Hence if you modify the argument named valueX then the variable named value1 remains not changed.

I do suspect that you wanted to actually change the value1 global variable from inside you procedure and this is somehow wrong and because of this the discussion took the "wrong turn" about using pointers to variable1.

Again you do NOT need to perform this action for this calculation example and you can perform the sign test and change the value to unsigned outside of the GCD procedure.

But if you would have wanted to use pointers towards variable1 and variable2 then you should have dereferenced the pointers before accessing the contents  :D

Something like this:

GetCommonDivisor_by_PTR PROC uses ecx edx esi edi var1_ptr:DWORD, val2_ptr:DWORD
LOCAL tmp_var1:dword, tmp_var2:DWORD

xor edx,edx

mov esi,[var1_ptr] ; get pointer1
mov eax,[esi] ; get value at pointer1 (dereference)

mov edi,[var2_ptr] ; get pointer2
mov ecx,[edi] ; get value at pointer2 (dereference)

div ecx


.if edx == 0
mov eax,ecx
ret
.else
mov [tmp_var1],ecx
mov [tmp_var2],edx

invoke GetCommonDivisor,ADDR tmp_var1,ADDR tmp_var2
.endif

ret
GetCommonDivisor_by_PTR ENDP


And invoke it like this:

invoke GetCommonDivisor_by_PTR, offset variable1, offset variable2


However this is pathetically more complicated, uses more memory, it is longer and it serves no sane purpose other than to  show some wrong example of using local variables, sending arguments by pointers and usage of OFFSET and ADDR :P


Ambition is a lame excuse for the ones not brave enough to be lazy.
http://www.oby.ro

unktehi

THANK YOU!! THANK YOU!!!

That really cleared things up and I found out I was right the first time in many instances.

I got it to work perfectly - thank you! Can I bake you cookies or something?

unktehi

P.S. I'm sorry for my confusing way of referring to things (ie procedure calls, etc.  I will know how to refer to things better in the future!

BogdanOntanu

Quote from: unktehi on February 28, 2009, 10:58:38 PM
THANK YOU!! THANK YOU!!!

That really cleared things up and I found out I was right the first time in many instances.

You are welcome ;)

Quote
I got it to work perfectly - thank you! Can I bake you cookies or something?

Sure ;)
Ambition is a lame excuse for the ones not brave enough to be lazy.
http://www.oby.ro

MichaelW

For what it's worth, here's a version the closely follows this C example:

int gcd(int a, int b)
{
   return ( b == 0 ? a : gcd(b, a % b) );
}


Without using any high-level syntax, and with a version that displays the intermediate values:

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc

    amodb MACRO a, b
        xor edx, edx
        mov eax, a
        mov ecx, b
        div ecx
        EXITM <edx>
    ENDM
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

;--------------------------------------------------
; Algorithm description from Wikipedia:
; Given two natural numbers a and b: check if b is
; zero; if yes, a is the gcd. If not, repeat the
; process using, respectively, b, and the remainder
; after dividing a by b.
;--------------------------------------------------

gcd proc a:DWORD, b:DWORD

    cmp b, 0
    jne @F
    mov eax, a
    ret
  @@:
    mov eax, a
    cdq
    mov ecx, b
    div ecx
    invoke gcd, b, edx
    ret

gcd endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

_gcd proc a:DWORD, b:DWORD

    print "a="
    print ustr$(a),9
    .IF b               ; must avoid divide by zero
      print "b="
      print ustr$(b),9
      print "a%b="
      mov eax, a
      cdq
      mov ecx, b
      div ecx
      print ustr$(edx),13,10
    .ELSE
      print "b="
      print ustr$(b),13,10
    .ENDIF

    cmp b, 0
    jne @F
    mov eax, a
    ret
  @@:
    mov eax, a
    cdq
    mov ecx, b
    div ecx
    invoke _gcd, b, edx
    ret

_gcd endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

    invoke gcd, 28, 6
    print ustr$(eax),13,10,13,10

    invoke _gcd,124, 568
    print ustr$(eax),13,10,13,10

    inkey "Press any key to exit..."
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start

eschew obfuscation