The MASM Forum Archive 2004 to 2012

General Forums => The Campus => Topic started by: unktehi on February 27, 2009, 02:59:10 AM

Title: INVOKE / arguments error?
Post by: unktehi on February 27, 2009, 02:59:10 AM
I know I'm missing something here even though my program works....generally.

I have written a procedure that is called by INVOKE GCD, value1, value2.  Here is the procedure:


GCD PROC valueX:DWORD, valueY:DWORD

or esi,value1 ; Check sign of value 1
.IF Sign? ; If integer is negative convert it to positive
Neg esi
.EndIf
mov value1, esi ; Restore esi to value1

or edi,value2 ; Check sign of value 2
.IF Sign? ; If integer is negative convert it to positive
Neg edi
.EndIf
mov value2, edi ; Restore edi to value2


mov eax, esi ; Move esi(value1) into eax
mov ebx, edi ; Move edi(value2) into ebx
sub edx, edx ; Clear upper area (sign) of edx:aex
idiv ebx ; Divide eax/ebx. Quotient is in eax. Remainder is in edx.
mov esi, edi ; Move edi(value2) into esi(was value1)
mov edi, edx ; Move edx(remainer of idiv) into edi(was value2)

.if edi > 0
mov value1, esi
mov value2, edi
call GCD
.else
mov common_divisor, esi
.endif

ret


When the entire program runs, it works as expected most of the time, however I am getting errors in regards to the first line of the procedure:
procedure argument or local not referenced : valueX
procedure argument or local not referenced : valueY

I guess I'm still not clear how value1 and value2 should relate to valueX and valueY. Does the first one passed in take on the first local variable name?

Title: Re: INVOKE / arguments error?
Post by: NightWare on February 27, 2009, 04:15:31 AM
1. you must use the parameters of the function, here valueX and valueY
2. or esi,valueX will not work always ! you have to add xor esi,esi before, or use mov esi,valueX and test esi,esi to fix the sign flag, instead...
Title: Re: INVOKE / arguments error?
Post by: MichaelW on February 27, 2009, 04:37:56 AM
unktehi,

If the code is assembling, then you must have defined value1 and value2 in the data section, and since items defined in the data section are visible throughout the module, the procedure is accessing value1 and value2 directly. If you intend to use invoke to pass value1 and value2 to the procedure in the valueX and valueY parameters, you will need to make substantial changes to the procedure and the invoke statement.

The current statement:

INVOKE GCD, value1, value2

Will pass copies of value1 and value2 on the stack, but since the procedure needs to update value1 and value2, passing copies will not work because the procedure will have access only to the copies. You can get around this problem by passing the addresses of value1 and value2 to the procedure:

INVOKE GCD, ADDR value1, ADDR value2

But the code in the procedure will need to be modified to access value1 and value2 through the passed addresses (pointers).

And the recursive call (GCD calling GCD) will also need to be done with invoke:

INVOKE GCD, ADDR value1, ADDR value2

Also, your code would be easier to read and copy if you would place it between code tags.  Since I made the change to your post above, if it's not clear how to do this, click the Modify button and take a look at the changes I made.
Title: Re: INVOKE / arguments error?
Post by: unktehi on February 27, 2009, 05:07:29 AM
NightWare - In regards to 'using' the parameters of the function: valueX and valueY, would that mean that in all instances where I have value1 and value2 they should be changed to valueX and valueY? I'm not sure how to 'use' the parameters in a function - hence the confusion.

MichaelW - That makes sense that I should be invoking the address of the passed variables rather than the copy of them. Is it correct to assume that anytime a passed variable will be modified that the procedure should be passed an address?  When would be an appropriate time to use the variable - when it's a constant?
Title: Re: INVOKE / arguments error?
Post by: NightWare on February 27, 2009, 05:38:32 AM
yes and no,

yes, if you define invoke GDC,value1,value2 you put the values in valueX and valueY. BUT, if you need returned values/results, you must put them in unpreserved registers (eax/ecx/edx).


no, if, like pointed by MichaelW, you define invoke GDC,ADDR value1,ADDR value2 then you put the memory location in valueX and valueY, but in this case you need to understand that you MUST read/write the values from those address. and no need to return values/results here.
Title: Re: INVOKE / arguments error?
Post by: MichaelW on February 27, 2009, 06:18:22 AM
QuoteIs it correct to assume that anytime a passed variable will be modified that the procedure should be passed an address?

Yes.

QuoteWhen would be an appropriate time to use the variable - when it's a constant?

I don't understand this one.
Title: Re: INVOKE / arguments error?
Post by: sinsi on February 27, 2009, 06:26:12 AM
  call GCDshould be  invoke GCD,esi,ediotherwise your stack gets mucked up.

Get a book about asm basics or download the intel programming docs.
Do you know any other programming languages?
Title: Re: INVOKE / arguments error?
Post by: unktehi on February 27, 2009, 05:11:13 PM
MichaelW -

When I said "When would be an appropriate time to use the variable - when it's a constant?" - I meant when would it be an appropriate time to use the variable name without the ADDR instruction with INVOKE? Only when the values passed don't change? Would it be better practice to always use ADDR when invoking procedures?

Sinsi -

I do have a book, but I don't understand the way that it tries to explain many of the concepts or the limited examples that are given.  That's why I'm trying to clarify.  I know Javascript, html, css, dhtml, etc - but those aren't really high-level languages.  Currently I'm learning Java..
Title: Re: INVOKE / arguments error?
Post by: NightWare on February 27, 2009, 11:19:11 PM
Quote from: unktehi on February 27, 2009, 05:11:13 PM
When would be an appropriate time to use the variable - when it's a constant?
it depends of you/your need, if the procedure is supposed to be a general function, or an app specific procedure. if values are only read or written. if you need to return a single value/result or need to alterate an area. you should look at masm32 examples, try to understand what's done there...
Title: Re: INVOKE / arguments error?
Post by: MichaelW on February 28, 2009, 04:24:21 AM
QuoteI meant when would it be an appropriate time to use the variable name without the ADDR instruction with INVOKE?

Whenever the procedure needs only the variable value. If you pass a variable address, to get the variable value the procedure must first load the address into a register and then use the register in an indirect memory operand. If you pass the variable value then the procedure can access the value directly, and there can be no problem with accidentally changing the value of the original variable, because the procedure can access only the copy on the stack.
Title: Re: INVOKE / arguments error?
Post by: unktehi on February 28, 2009, 04:43:42 AM
Nightware - It looks like I have to download MASM32 to view any examples? Are there any examples I can look out without downloading that?

However, I'm beginning to understand, I think.  I am getting no errors now, but the value I'm getting returned is not the greatest common factor...it's 7 digits long. Do I have something backwards?

Here is my modified invoke call from main:
invoke GCD, ADDR value1, ADDR value2

Here is my modified procedure:
GCD PROC valueY:DWORD, valueX:DWORD

   ; value2 is pushed on stack through INVOKE statement = valueY <- not sure if this is right
   ; value1 is pushed on stack through INVOKE statement = valueX  <- not sure if these are reversed

   push ebp            ; return address is pushed on stack = EBP+4
   mov ebp, esp         ; EBP is set to base pointer of stack
   
   xor esi, esi            ; Set esi (and sign bit) to 0
   or esi, valueX         ; Check sign of value1
   .IF Sign?            ; If integer is negative convert it to positive
   Neg esi
   .EndIf
   mov valueX, esi         ; Restore esi to value1
      
   xor esi, esi            ; Set esi (and sign bit) to 0
   or edi,valueY         ; Check sign of value2
   .IF Sign?            ; If integer is negative convert it to positive
   Neg edi
   .EndIf
   mov valueY, edi         ; Restore edi to value2
   
   
   mov eax, esi         ; Move esi(value1) into eax
   mov ebx, edi         ; Move edi(value2) into ebx
   sub edx, edx         ; Clear upper area (sign) of edx:aex
   idiv ebx            ; Divide eax/ebx. Quotient is in eax. Remainder is in edx.
   mov esi, edi         ; Move edi(value2) into esi(was value1)
   mov edi, edx         ; Move edx(remainer of idiv) into edi(was value2)
   
   .if edi > 0
   mov value1, esi
   mov value2, edi
   invoke GCD, ADDR value1, ADDR value2   ; Recursive call back to GCD
   .else
   mov eax, esi                                               ; Placing the 'new' value1 into eax
   pop ebp                                                    ; Popping eax to be used by main after return
   .endif
   
   ret            ; return to main procedure             
   
GCD ENDP
Title: Re: INVOKE / arguments error?
Post by: BogdanOntanu on February 28, 2009, 06:58:42 AM
Quote
... It looks like I have to download MASM32 to view any examples? Are there any examples I can look out without downloading that?

MASM32 is a very small but very useful download.
If you are not willing to download it and take the time to learn / look/ understand from it's many examples then...

Anyway, here is my simple GCD procedure:

PROC Calculate_GCD stdcall
USES ecx,edx
ARG divident, divisor

xor edx,edx
mov eax,[divident]
mov ecx,[divisor]
div ecx

.if edx == 0
; found our GCD
; result is returned in eax register
mov eax,[divisor]
ret
.else
invoke Calculate_GCD,[divisor],edx
.endif

ret
endp


This example is in Sol_ASM syntax (my assembler) not in MASM syntax. However SOL_ASM  is relatively close in syntax to MASM.

The task of conversion to MASM syntax, adding tests and input/output routines is left to the willing to learn and understand student :P .
Title: Re: INVOKE / arguments error?
Post by: MichaelW on February 28, 2009, 07:15:54 AM
After thinking more about this, sinsi was on the right track and I was on the wrong one. It is not necessary to pass addresses, in the initial call or the recursive calls. Only values need be passed on the stack, as Bogdan's example shows. I implemented the C example from  here (http://en.wikipedia.org/wiki/Euclidean_algorithm):

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


In MASM code, without any high level syntax, in 13 lines.

Title: Re: INVOKE / arguments error?
Post by: ToutEnMasm on February 28, 2009, 07:41:10 AM
Hello,
Two things if you want to write your own GCD proc
Quote
Good declare
GCD PROC uses esi edi ebx valueX:SDWORD, valueY:SDWORD

Writing a proc you preserve esi edi and ebx,if you use it.
Testing for signed numbers ,you must declare them as signed (SDWORD),if you don't do,the ".IF Sign? " don't work.
Bad practice also,using esi edi as data register . use variable in data instead,and declare them as signed (just add a "S" to is declare).
common use of esi edi ebx is pointer.





Title: Re: INVOKE / arguments error?
Post by: unktehi on February 28, 2009, 08:16:32 AM
BogdanOntanu - I will download the examples and take a look.  I just wasn't sure if he was referring to a page or the package to download (because there was no link).  So if the examples are in the download package, I certainly will take a look at it!

ToutEnMasm - Thanks for pointing that out - I totally didn't even think about changing the dwords to sdwords - but I absolutely should!
Title: Re: INVOKE / arguments error?
Post by: jj2007 on February 28, 2009, 09:05:32 AM
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
Title: Re: INVOKE / arguments error?
Post by: unktehi on February 28, 2009, 06:16:49 PM
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.
Title: Re: INVOKE / arguments error?
Post by: jj2007 on February 28, 2009, 07:07:58 PM
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 (http://webster.cs.ucr.edu/Page_TechDocs/MASMDoc/), Microsoft (http://msdn.microsoft.com/en-us/library/afzk3475(VS.71).aspx) and the Masm reference here on the Forum (http://msdn.microsoft.com/en-us/library/afzk3475(VS.71).aspx). You will see that it helps.
Title: Re: INVOKE / arguments error?
Post by: PBrennick on February 28, 2009, 07:47:25 PM
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


Title: Re: INVOKE / arguments error?
Post by: 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 ?

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?


Title: Re: INVOKE / arguments error?
Post by: BogdanOntanu on February 28, 2009, 09:40:10 PM
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


Title: Re: INVOKE / arguments error?
Post by: 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.

I got it to work perfectly - thank you! Can I bake you cookies or something?
Title: Re: INVOKE / arguments error?
Post by: unktehi on February 28, 2009, 11:07:36 PM
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!
Title: Re: INVOKE / arguments error?
Post by: BogdanOntanu on February 28, 2009, 11:49:40 PM
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 ;)
Title: Re: INVOKE / arguments error?
Post by: MichaelW on March 01, 2009, 02:00:52 AM
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