News:

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

Invalid operand for OFFSET

Started by unktehi, March 05, 2009, 05:22:40 AM

Previous topic - Next topic

unktehi

Can someone take a look at this? I'm not sure why I'm getting an error with 'OFFSET.'  I had this working when it wasn't in a separate procedure, but now that I've put it in a procedure and defined it with proto - I can't seem to figure out why it's not working.

----------------------
INCLUDE Irvine32.inc

Str_concat PROTO,
   sourceStr:PTR BYTE,      ; source string
   targetStr:PTR BYTE      ; target string

.data
string1 BYTE "out like a lamb.",0
string2 BYTE "In like a lion,                ",0

.code
main PROC

INVOKE Str_concat,
ADDR string1,
ADDR string2
   
   exit

main ENDP

Str_concat PROC,
   sourceStr: PTR BYTE,
   targetStr: PTR BYTE
   
   INVOKE Str_length, targetStr
   call WriteDec                  ;Verify lengthOf string2
   call crlf
   mov ecx, eax                  ;Place lengthOf string2 into ecx
   inc ecx
   mov esi, sourceStr
   mov edi, [targetStr+16]
   cld
   rep movsb
   
   ;INVOKE Str_copy,
   ;sourceStr,
   ;[targetStr+16]
   
   mov edx, OFFSET targetStr
   call WriteString
   call crlf
   
   ret   
   
Str_concat ENDP

END main

MichaelW

It's not working because the address of targetStr is not known at assembly time. You could for example use:

mov edx, OFFSET string1

And it would assemble correctly because the offset address of string1 is known at assembly time.

But in this case you don't need the address of targetStr, you need the address that is stored in targetStr, which is the address of string2. Since targetStr contains an address, to help prevent this sort of confusion it should probably be named pTargetStr (where the p means pointer), or something similar. Since pTargetStr would contain the address of the string you wish to display, you would need only:

mov edx, pTargetStr
eschew obfuscation

unktehi

Actually, it comes up with errors when I try to use:

mov edx, OFFSET string1

The error it comes up with is 'instruction operand must be the same size.'

That does make sense, but when I try simply using the 'targetStr' (without changing them to pTargetStr), it does not come up with any errors, but does not write anything either.  It spits out the length of string2, then goes on to say 'press any key to continue.' It throws no errors - just doesn't work.

BogdanOntanu

Quote from: unktehi on March 06, 2009, 12:54:24 AM
Actually, it comes up with errors when I try to use:

mov edx, OFFSET string1

The error it comes up with is 'instruction operand must be the same size.'

No it does not. OFFSET string1 is an address and in 32 bits flat mode this address is 32bits in size and this is exactly the size of the EDX register. You must be dreaming or there is another error in another location.

Quote
That does make sense,

No, it does not make sense at all... and the fact that you believe it might make sense shows that you did not yet understand pointers, parameters passing to procedures, use of OFFSET and ADDR operators, address and register sizes.

Quote
but when I try simply using the 'targetStr' (without changing them to pTargetStr), it does not come up with any errors, but does not write anything either.  It spits out the length of string2, then goes on to say 'press any key to continue.' It throws no errors - just doesn't work.

This is normal and expected... after all the CPU only does exactly what you instruct it to do.

The assembler can only find syntax errors but it can NOT find errors in your logic or in your understanding. IF you write code that is wrong in logic but is correct in form then the code will assemble but it will behave badly.

Behave badly does not mean that it will crash (but it might). Many times it will just fail silently or apparently "work" waiting for another day to bring havoc and frustration.

Learn to make small simple tests and to understand the basic things very well and in depth. Do not try to "make them work" and understand later ... otherwise you will be very frustrated in any programming languages but even more in ASM.

For example you can use the samples provided with MASM32 for simple tests.

In your procedure at this code:

   mov edi, [targetStr+16]
   cld
   rep movsb


What do you "think" that it is supposed to do?

targetStr is your procedure's argument... it contains a pointer to a string ... but how do you dare to add 16 to your argument?
What value do you obtain in EDI in this case?


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

unktehi

Your right, my bad. I'm not sure what was causing that error because I can't get it to come up now.

Either way, it's working, other than spitting out the concatenated string.

The issue is with [targetStr+16] because if I change it to just [targetStr] or just targetStr it works - to replace string2 with string1 just copying that one string into the other.  But what I'm trying to do is add one string to another, the first string needs to be added to the other at bit 16. I know adding numbers such as this [targetStr+2] is generally used when the type of data you are working with are different types such as +2 for words, +4 for doublewords, etc, but if we're dealing with bytes - can you not add 16 to have the copying begin at the 16th bit in string2 like [target+16]?

unktehi

In your procedure at this code:

   mov edi, [targetStr+16]
   cld
   rep movsb


What do you "think" that it is supposed to do?

targetStr is your procedure's argument... it contains a pointer to a string ... but how do you dare to add 16 to your argument?
What value do you obtain in EDI in this case?


This is exactly what I'm confused with.  I was understanding this would take string2 and start copying it to the pointer at bit 16 on string1. 



Quote

BogdanOntanu

Quote from: unktehi on March 06, 2009, 03:31:22 AM
Your right, my bad. I'm not sure what was causing that error because I can't get it to come up now.

Either way, it's working, other than spitting out the concatenated string.

Define "working" :P

Quote
The issue is with [targetStr+16]

Yes, this is horribly wrong... wrong from a conceptual point of view. If shows that you did not understand the concepts BUT you are trying to "fix the form only".

IF I would be your teacher you will loose the exam instantly for such an mistake no matter how well you did in all other tests or during the year.

Quote
because if I change it to just [targetStr] or just targetStr it works

This is changing the form without no understanding...

Again how do you define "it works"? You mean it does compile (assemble) without errors... but for sure it does not work in the sense that it does not perform the required actions as "intended".

Quote
- to replace string2 with string1 just copying that one string into the other.  But what I'm trying to do is add one string to another, the first string needs to be added to the other at bit 16.

I can understand what you are trying to achieve... It is wrong and non elegant but maybe it can be used as a test... maybe.

This is not the problem now but IMHO you should not define a place in string2 where to insert string1. You should have 2 strings and concatenate them into a big enough temporary buffer or have another argument that is a pointer to the result buffer :P .

And again is it "bit 16" OR "byte 16" ? Careful here because bits are not the same as bytes. One byte contains 8 bits.

Anyway...

Quote
I know adding numbers such as this [targetStr+2] is generally used when the type of data you are working with are different types such as +2 for words, +4 for doublewords, etc,

No kidding? Where from do you "know" this? Hearsay?

Again do not forget that even things that you "hear" here can and will be wrong at times (for various reasons)
Hence you should only trust your own testing and understanding 100%

Quote
but if we're dealing with bytes - can you not add 16 to have the copying begin at the 16th bit in string2 like [target+16]?

Again a confusing between bits and bytes in the very same sentence...

We do take the time hare to consider your problems and write answers... Please also take the time to:
1) Inspect your sentences for errors
2) to format your code properly to make it easy to read
3) to make small tests using MASM32 examples in order to understand where the problem really is...
4) Never consider that you are right, always test and try to understand

Now as for your error:


mov edi,[targetStr+16]


THINK about it....

Question: What is targetStr?
Answer: it is an argument of your procedure

Q: What does it mean to ADD 16 to an argument?
Answer: To "probe" the memory for random results because you procedure only has 2 arguments and in fact "targetStr" is the last argument and adding 16 to it only goes beyond into "nowhere"

Now I know that your argument is supposed to CONTAIN an pointer to a string and my guess is that you wanted to ADD 16 to that pointer value and not to "targetStr".

This "targetStr" is just a nice name for an procedure argument, in fact it is an address somewhere on the stack and hence very temporary in nature.

Yes, it does contain a pointer to something but it is NOT the pointed stuff itself. Hence when you add 16 to "targetStr" you DO NOT move the pointer inside the string BUT instead you move the argument somewhere in the "list of arguments" towards and illegal position because your procedure only has 2 arguments anyway.

You must understand the difference between CONTAINS and IT IS. The difference between a POINTER towards a variable and the actual variable address and the actual content of a variable. And you should understand that procedure arguments are not the same as their values.

Those are important basic concepts and without understanding them you are building your house on THIN ICE.

Hence what you "intended" was:

mov edi,[targetStr] ; get the pointer to target string from the procedure's argument
add edi,16  ;move pointer 16 bytes forward


Not a very nice solution but it "should work" for very specific strings.


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

unktehi

I'm really sorry.  I know this is probably just as frustrating for you as this is for me.

Quote
Define "working" Tongue

I know I really need to work on how I phrase things and clarify what I'm trying to say. What I meant by this, is that it works, assembles without errors and I am able to move sourceStr into targetStr successfully.  The problem I had was trying to understand how to add the source string to the end of the target string - after BIT 16 - not byte.

Quote
This is not the problem now but IMHO you should not define a place in string2 where to insert string1. You should have 2 strings and concatenate them into a big enough temporary buffer or have another argument that is a pointer to the result buffer Tongue .

Strangely enough, I asked my professor about this and he said that the easiest way to do it is to add enough spaces to accommodate the string.

I'm sorry, I know I need to be much more careful with my wording and terms, I'll try to be more critical and careful in the future.

I will definitely go back, again, and revisit my understanding of pointer, address, contents of variables, etc.


MichaelW

The normal behavior for string concatenation is to append one string to the end of the other. The destination string must be long enough to accommodate both strings, so if you defined it something like this:

dest db "In like a lion,", 50 dup(0)

Then your Str_concat procedure could find the end of the destination string by searching for the first null byte (the first of 50 in this case), and copy the source string into the destination string starting at that address.
eschew obfuscation

BogdanOntanu

Yes, MichaelW is right, the most common solution is to reserve "enough" space at string1 and then concatenate the string2 to it.

My only observation is that I would name this kind of operation "Str_Add" because what it does is in fact string1 = string1 + string2
However this "wording" is not of the essence.

Do you remember the discussion about null terminated strings ?

That is why the NULL (zero) byte is so useful as a string terminator: because it marks the end of string1 and you know that right there where you find a zero byte you can start writing string2. This way you do not have to count string chars "by hand" and use special offsets like that "16" ...

BTW, are you sure that it is "bit 16" and not "byte 16"? ... because 16 bits would mean 2 bytes and in your error you did add 16 bytes not 16 bits.

Anyway take care at the statements in the problem's text because they could be misleading.

I hope that we will not find later on that your teacher wanted this string concatenation to be done in the "16 BITS" mode of the CPU... because then it is slightly different in implementation (but the concepts will remain the same)
Ambition is a lame excuse for the ones not brave enough to be lazy.
http://www.oby.ro

Mark Jones

I always recommend this reading, as it is absolutely critical in understanding the basics of assembler: \masm32\help\asmintro.chm
Read it... read it again... all of it... commit it to memory. :bg

ADDR, OFFSET, xxx PTR, [], and indirect addressing modes are some of the trickier concepts in MASM, so take your time. Don't try to learn everything at once, or it will all end up a jumbled mess. :wink
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

RuiLoureiro

Quote from: unktehi on March 05, 2009, 05:22:40 AM
Can someone take a look at this?
...
.data
string1 BYTE "out like a lamb.",0
string2 BYTE "In like a lion,                ",0

.code
main PROC

INVOKE Str_concat,
ADDR string1,
ADDR string2   
   exit
main ENDP

Str_concat PROC,
   sourceStr: PTR BYTE,
   targetStr: PTR BYTE
   

   inc ecx
   mov esi, sourceStr
   mov edi, [targetStr+16]
   cld
   rep movsb
   
...   
   ret   
   
Str_concat ENDP

END main

Hi,
              It seems you want to pass pointers to Str_concat  so sourceStr should be dword and ...
              It seems you want a string2 like this

string3     db "In like a lion, out like a lamb.", 0

              so you want to begin at the address   targetStr+16 and you dont want the dword
              pointed to [targetStr+16] in edi.
              So, mov    edi, targetStr and   
                    add     edi, 16
                    ... etc.
Try to read and understand this example:
;
Quote
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
    .586
;»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
AddStrStr   proto  :DWORD,:DWORD
;»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
.data
string1     db "out like a lamb.",0

string2     db "In like a lion, ", 0
              db 16 dup (0)
              db 0
           
string3     db "In like a lion, out like a lamb.", 0
;»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»»
.code

; output:
;           ECX = string length
;
GetStrLen   proc                     

            mov   ecx, 0              ; length = 0
            mov   edx, dword ptr [esp + 4]
@@:         cmp   byte ptr [edx], 0
            je    short @F            ; 0 was found => exit
            ;
            inc   edx         
            inc   ecx
            jnz   short @B
                         
@@:         ret   4
GetStrLen   endp
;-------------------------------------------------
; Input:
;             source and target are string pointers
;
AddStrStr proc  source:DWORD, target:DWORD

          push   source   
          call   GetStrLen
         
          inc    ecx            ; copy 0 too
         
          mov    esi, source
          mov    edi, target
          add    edi, 16        ; begin at position +16
          cld
          rep    movsb

          ret     
AddStrStr endp
;-------------------------------------------------
start:
        ;***********************
        print   offset string1, 13, 10
        print   offset string2, 13, 10
        inkey   "strings 1,2. press a key..."       
        ;***********************

        ;***********************
        print   chr$(13, 10)
        print   addr string3, 13, 10
        inkey   "string3. press a key..."
        ;***********************

        ;push    offset string2
        ;push    offset string1
        ;call    AddStrStr

        ;++++++++++++++++++++++++++++++++++++++++++++
        invoke  AddStrStr, addr string1, addr string2
        ;++++++++++++++++++++++++++++++++++++++++++++

        ;*************************************
        print   chr$(13, 10)       
        print   offset string2, 13, 10
        inkey   "new string2. press a key..."
        ;************************************
        exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
I hope it help you
Rui
         


unktehi

For the sake of trying to understand how you find the pointer in a string to find a 0-valued byte (not a ASCII character), I decided to go back and try to write a simple application:

.data
string2 BYTE "In like a lion,", 50 dup(0)   ; string to look at
string3 BYTE "No 0s were found",0               ; message to show if no 0s are found

.code
main PROC            ; main procedure

mov ebx, OFFSET string2                             ; mov the OFFSET of string2 into ebx (isn't this the address of where string2 resides?)
mov ecx, LENGTHOF string2                         ; mov the length of string2 into ecx - for loop

mov edx, OFFSET string2                             ; move the OFFSET of string2 into edx (isn't this the address of where string2 resides?)
call WriteString                                           ; Write string to console to verify string contents
call crlf

mov eax, ecx                                             ; move the length of value into eax
call WriteInt                                               ; write out the length of value to verify it is seeing the DUP contents on end of string
call crlf

;COMMENT !

L1:
cmp BYTE PTR [ebx], 0                              ; compare the first value (on first loop) in byte 1 of ebx to 0
jz found                                                    ; If a zero value is found go to 'found'
inc ebx                                                     ; Otherwise, increment ebx by 1 byte
loop L1                                                     ; Loop back up to L1 and repeat
jmp notfound                                            ; If the value of ecx gets to 0 and no 0s were found, jump to 'notfound'

found:                                                     
mov edx, BYTE PTR [ebx]                           ; THIS LINE COMES UP WITH ERRORS - it was my intention to pass the value of [ebx] to edx
call WriteString                                           ; Write value of edx to console
jmp exitme                                                ; Then skip over 'notfound' and jump to 'exitme'

notfound:
mov edx, OFFSET string3                             ; Place OFFSET of string3 (isn't this the address of string3?) into edx
call WriteString                                           ; Write string3 to console

;!

exitme:                                                     ; end
   
   exit

main ENDP

END main


Now, I'm not sure what I'm missing here because I thought that the line 'mov edx, BYTE PTR [ebx]' would pass the value of [ebx], into edx allowing me to write the value to the console. From my understanding that value should be '0' - however, I'm not even sure if a value of 0 would print out anything. I did, however, change the '0' in the cmp instruction to "k" to see if it would make a difference. Regardless, when I try to assemble the code the line ''mov edx, BYTE PTR [ebx]'' comes up with the error of 'instruction operands must be the same size.'   

If, however, I change that line to "mov edx, ebx" - it is able to read and write string2 out to the console and it also recognizes and writes out that there are 65 bytes (with the 50 dup on the end) in the string. So, I believe that the error thrown prior to my modification does deal with the line "mov edx, BYTE PTR[ebx]" but I'm not sure why - if that is pointing to the byte in the string that contains a 0-valued byte (not a ASCII character).  Can someone help me understand why this error is happening?

NightWare

Quote from: unktehi on March 08, 2009, 03:21:56 AM
mov edx, BYTE PTR [ebx]
mean : move byte pointed by the address in ebx, to edx

but you have defined that it's BYTEs in your data... and edx has a DWORD size... so 2 possibility :
mov dl,byte ptr [ebx] ; <- but here you must clean the 3 superiors bytes before (just in case)
movzx edx,byte ptr [ebx] ; <- here you must use movsx if it's a signed value

RuiLoureiro

#14
Quote from: unktehi on March 08, 2009, 03:21:56 AM
[Q1]
.. because I thought that the line 'mov edx, BYTE PTR [ebx]' would pass the value of [ebx], into edx allowing me to write the value to the console.
[A1]
       you thought that ... but you cannot move a BYTE to a DWORD in that way.
       you must use instead movzx    edx, byte ptr [ebx] or  mov   dl, byte ptr [ebx].
       Ok ?

Quote from: unktehi on March 08, 2009, 03:21:56 AM
[Q2]
Regardless, when I try to assemble the code the line ''mov edx, BYTE PTR [ebx]'' comes up with the error of 'instruction operands must be the same size.'   
[A2]
            yes, 'instruction operands must be the same size, so it must be mov   edx, dword ptr [ebx]
            but it is what you dont want you want a byte, so you must use mov  dl, byte ptr [ebx]
            and then you use DL not edx or you can use movzx   edx, byte ptr [ebx]
Quote from: unktehi on March 08, 2009, 03:21:56 AM
[Q3]
If, however, I change that line to "mov edx, ebx" - it is able to read and write string2 out to the console and it also recognizes and writes out that there are 65 bytes (with the 50 dup on the end) in the string.
[A3]
       IF you change to mov   edx, ebx you are saying turn edx equal to ebx and if ebx is the address
       of string2 then now edx is the address of string2 too and there is no error, but it doesnt do what you   
       want, no ?
Quote
          EDIT: i read your problem again. It seems that you want to pass the address where you found
                   the 0
. So it should be: (note that i dont know how WriteString works !)
found:                                                     
          mov edx, ebx                          ; edx = ebx = address of string2 where we found 0
          call WriteString                        ; Write string2 begining at address EBX=EDX
          jmp exitme                             ; Then skip over 'notfound' and jump to 'exitme'
One question to you: WriteString and crlf preserves ECX ?
Rui