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

unktehi

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?


NightWare

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...

MichaelW

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.
eschew obfuscation

unktehi

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?

NightWare

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.

MichaelW

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.
eschew obfuscation

sinsi

  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?
Light travels faster than sound, that's why some people seem bright until you hear them.

unktehi

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..

NightWare

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...

MichaelW

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.
eschew obfuscation

unktehi

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

BogdanOntanu

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 .
Ambition is a lame excuse for the ones not brave enough to be lazy.
http://www.oby.ro

MichaelW

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:

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.

eschew obfuscation

ToutEnMasm

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.






unktehi

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!