News:

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

Question about console tutorial

Started by Crashish, May 10, 2010, 04:49:48 AM

Previous topic - Next topic

Crashish

masm32\tutorial\console\demo6

Hello all,

In the original "address.asm" example, 10 DWORDS are initialized, and their addresses are put into an array.
In short, the output in the original example is what I expected it would be.

Here's the original simplified a bit, but the output is the same (i.e., correct):
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
    ; --------------------------
    ; Initialize 10 DWORD values
    ; --------------------------
        itm0    dd 1
        itm1    dd 2
        itm2    dd 3
        itm3    dd 4
        itm4    dd 5
        itm5    dd 6
        itm6    dd 7
        itm7    dd 8
        itm8    dd 9
        itm9    dd 10
    ; ---------------------------------
    ; Put their addresses into an array
    ; ---------------------------------       
        array dd itm0,itm1,itm2,itm3,itm4
              dd itm5,itm6,itm7,itm8,itm9
             
        cnt     db 10
       
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
   
    mov esi, array
@@:
    mov edi, [esi]
   
    print str$(esi)
    print chr$(13,10)
   
    print str$(edi)
    print chr$(13,10,13,10) 

    add esi, 4
    dec cnt
    jnz @b

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


Output:
4206592
1

4206596
2

4206600
3

4206604
4

4206608
5

4206612
6

4206616
7

4206620
8

4206624
9

4206628
10

Press any key to exit...


Now, what if, I want to initialize 10 BYTES instead of 10 DWORDS and try this exercise again? I thought it would simply be a matter of changing line 41add esi, 1—and everything would work as expected. Turns out, it's not as simple as I first thought.

Let's give it a go...
I've only made 2 changes. Here they are:
    .data
    ; --------------------------
    ; Initialize 10 BYTE values
    ; --------------------------
        itm0    db 1
        itm1    db 2
        itm2    db 3
        itm3    db 4
        itm4    db 5
        itm5    db 6
        itm6    db 7
        itm7    db 8
        itm8    db 9
        itm9    db 10
...

.code

...

add esi, 1

...


Output:
4206592
67305985

4206593
84148994

4206594
100992003

4206595
117835012

4206596
134678021

4206597
151521030

4206598
168364039

4206599
657672

4206600
805308937

4206601
1076887562

Press any key to exit...


Verdammt!   This is not at all what I expected. The addresses look correct, but the values are garbage.
What's the deal here?
The first timemov edi, [esi] gets executed, it should simply copy 1 to edi, right?
To me, it seems like it would be no different than mov edi, 1 - mov edi, 2 - mov edi, 3 - etc.


This is the only way I could get the results I was looking for:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
    ; --------------------------
    ; Initialize 10 BYTE values
    ; --------------------------
        itm0    db 1
        itm1    db 2
        itm2    db 3
        itm3    db 4
        itm4    db 5
        itm5    db 6
        itm6    db 7
        itm7    db 8
        itm8    db 9
        itm9    db 10
    ; ---------------------------------
    ; Put their addresses into an array
    ; ---------------------------------       
        array dd itm0,itm1,itm2,itm3,itm4
              dd itm5,itm6,itm7,itm8,itm9
             
        cnt     db 10       ;Set counter variable to 10
       
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
   
    mov esi, array
@@:                         ;@@Label
    mov eax, [esi]
    push eax                ;Save eax
   
    print str$(esi)
    print chr$(13,10)
   
    pop eax                 ;Restore eax
    mov cl, al              ;Copy low byte of eax into low byte of ecx
    xor eax, eax            ;Clear eax
    movzx eax, cl           ;Zero-extend low byte of ecx, and copy into eax
    print str$(eax)
    print chr$(13,10,13,10) 

    add esi, 1              ;Add 1 to esi to get the next address
    dec cnt                 ;Decrement counter variable
    jnz @b                  ;Jump if not zero back to label

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


Output:
4206592
1

4206593
2

4206594
3

4206595
4

4206596
5

4206597
6

4206598
7

4206599
8

4206600
9

4206601
10

Press any key to exit...


Why doesn't simply changing add esi, 1 work?

Thanks for your help.


Ghandi

I think you mean ESI, not ESP. If you go playing with the stack pointer without taking care to return it to normal, it will give all sorts of problems from strange behaviour to outright crashes.

The example is working with DWORD values and if you look at the line:


mov edi, [esi]


it is loading a full DWORD to EDI, from the memory which ESI is pointing at.

If you want to access bytes only, you could use:


@@:
    movzx edi,byte ptr [esi]
   
    print str$(esi)
    print chr$(13,10)
   
    print str$(edi)
    print chr$(13,10,13,10) 

    add esi, 1
    dec cnt
    jnz @b


Your code loads a full DWORD still, so the first value is actually the first 4 bytes of your array, not one.

You could always keep them in a DWORD array which then lets you keep the code the same without changing it, but it wouldnt be a very efficient way of storing things.

HR,
Ghandi

sinsi

Isn't the original tutorial wrong?
  mov esi,array ;wrong, surely? it should be
  mov esi,offset array

It's just by luck that the original loads the address of the first dword and increments by 4, flukily pointing to the next dword...
Trying to treat them as bytes and incrementing by 1 shows the problem.


    mov edi, [esi]              ; dereference it into EDI
    mov edi,[edi]               ;to get the address properly instead of a fluke!
    ;movzx edi,byte ptr [edi]   ;or this for the bytes

Light travels faster than sound, that's why some people seem bright until you hear them.

Crashish

Thanks so much for the quick replies.

@Ghandi
QuoteI think you mean ESI, not ESP.
Yes, you're right. I don't know why I wrote esp over and over.
I edited my original post, and put esi instead of esp.

movzx edi,byte ptr [esi]
Thank you for this.

@sinsi
QuoteIsn't the original tutorial wrong?
More than likely, it's just me that's wrong. But, I can't be sure, because I over-wrote the original asm file.
If someone could upload and attach the original, I'd really appreciate it. Or, if you could tell me a way to restore it without reinstalling the entire masm package again.

jj2007


Crashish


Crashish

QuoteIsn't the original tutorial wrong?
Code:
  mov esi,array ;wrong, surely? it should be
  mov esi,offset array

It's just by luck that the original loads the address of the first dword and increments by 4, flukily pointing to the next dword...
Trying to treat them as bytes and incrementing by 1 shows the problem.

Code:
    mov edi, [esi]              ; dereference it into EDI
    mov edi,[edi]               ;to get the address properly instead of a fluke!
    ;movzx edi,byte ptr [edi]   ;or this for the bytes

Thanks to jj2007, I now have the original numbers.asm:
    mov esi, array              ; put array address into ESI

As you can, there is no "offset" in there. And, although the comment "; put array address into ESI" seems wrong to me... (since "array's" address is not put into ESI, "itm0's address is put into ESI; I think it should say, "put address of first element (i.e., itm0) into ESI."), I don't see how it's wrong or a "flukily" way of doing things.

In the Introduction To Assembler: Data Types In Registers, this example is given:
mov edx, lpMemvar          ; copy variable address into EDX
Doesn't "lp" mean long pointer (i.e., an artifact of 16-bit Windows), which is still used today, and simply denotes a pointer?  It's surely not copying it's own address into edx, it's copying it's "content" or address of Memvar into edx.
If it was copying it's own address into edx, wouldn't it say something like:
mov edx, offset lpMemvar     ; or...
lea edx, lpMemvar

This seems to be no accident; but rather, done on purpose.

Take a look at these two variables:
.data
        memVar      dd 123456789
        lpmemVar    dd memVar

Really, all variables are the same. That is, they all have the same basic thing in common: they all have a "value" or "content". This content/value can be an address (which is of course a number), number, string literal (like 'a') etc..
Yes, there are different types/sizes, but lpmemVar is still just a variable the same as memVar is; they both have a value. It just so happens (purposely) that lpmemVar's value is the address of memVar. We just try to name variables accordingly, to keep things understandable.

To me, it seems the only difference between mov esi, array and lea esi, array (or: mov esi, offset array) is that there's one less step involved to get what we want/need when talking about "dereferencing."

Example 1: (one less step involved)
    .data
        itm0    dd 0
        itm1    dd 1
        itm2    dd 2
        ...     ...
       
    .code
       
        mov esi, array                      ; esi now contains the value/contents of "array," or more accurately , the *address* of "itm0", which is, of course, the first element (i.e., first dword/4 bytes), not the address of "array"
        mov edi, [esi]                      ; dereference esi into edi; edi now contains the value/contents of "itm0"

        ...     ...


Example 2: (one additional step involved)
    .data
        itm0    dd 0
        itm1    dd 1
        itm2    dd 2
        ...     ...
       
    .code
       
        lea esi, array                     ; load effective address of array into esi; esi now contains the address of "array", not the address of "itm0"
        mov edi, [esi]                     ; dereference esi into edi; edi now contains the value/contents of "array," or more accurately, the *address* of "itm0"
                                           ; ** additional step involved **
        mov edi, [edi]                     ; dereference edi back into edi;  edi now contains value/contents  of "itm0"

        ...     ...


Now, one last example.
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
   
        MemVar  dd 123456789                                ;value = 123456789       
        pMemVar dd MemVar                                   ;value = address (of MemVar)  "p" stands for pointer
        p2pMemVar dd pMemVar                                ;value = address (of pMemVar) "p2p" stands for pointer to pointer
        p2p2pMemVar dd p2pMemVar                            ;value = address (of p2pMemVar)"p2p2p" stands for pointer to pointer to pointer
       
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

    mov esi, MemVar                                         ;copy MEMORY operand into esi
    print str$(esi)                                         ;     "DIRECT reference"
    print chr$(" = value/contents of MemVar",13,10,13,10)

    mov esi, 123456789                                      ;copy IMMEDIATE operand into esi
    print str$(esi)
    print chr$(" = value/contents of MemVar",13,10,13,10)

    mov edi, esi                                            ;copy REGISTER operand esi into edi
    print str$(edi)
    print chr$(" = value/contents of MemVar",13,10,13,10)

    lea esi, MemVar                                         ;(l)oad (e)ffective (a)ddress of MemVar (here, it's just for example purposes)
    print str$(esi)                                         ;same as: mov esi, OFFSET Memvar
    print chr$("   = address of MemVar",13,10)
    mov esi, [esi]                                          ;dereference (address of MemVar, which leaves us with the "content" of MemVar or "value" of MemVar 123456789) esi; copy back into esi
                                                            ;by a technique called "DEREFERENCING"
                                                            ;we now have "direct reference" to MemVar's value/contents   
    print str$(esi)
    print chr$(" = value/contents of MemVar",13,10,13,10)

    lea esi, pMemVar                                        ;(l)oad (e)ffective (a)address of pMemVar
    print str$(esi)
    print chr$("   = address of pMemVar",13,10)
    mov esi, [esi]                                          ;dereference (address of pMemVar) esi; copy back into esi                      -- First level of indirection
    mov esi, [esi]                                          ;dereference (address of MemVar) esi; copy back into esi                       -- Second level of indirection
                                                            ;we now have "direct reference" to MemVar's value/contents
    print str$(esi)                                         ;two levels of "INDIRECTION"
    print chr$(" = value/contents of MemVar",13,10,13,10)

    lea esi, p2pMemVar                                      ;(l)oad (e)ffective (a)ddress of p2pMemVar
    print str$(esi)
    print chr$("   = address of p2pMemVar",13,10)
    mov esi, [esi]                                          ;dereference (address of p2pMemVar) esi; copy back into esi                 -- First level of indirection
    mov esi, [esi]                                          ;dereference (address of pMemVar) esi; copy back into esi                   -- Second level of indirection
    mov esi, [esi]                                          ;dereference (address of MemVar, which leaves us with the value 123456789)  -- Third level of indirection
                                                            ;we now have "direct reference" to MemVar's value/contents     
    print str$(esi)
    print chr$(" = value/contents of MemVar",13,10,13,10)

    lea esi, p2p2pMemVar                                    ;(l)oad (e)ffective (a)ddress of p2p2pMemVar
    print str$(esi)
    print chr$("   = address of p2p2pMemVar",13,10)
    mov esi, [esi]                                          ;dereference (address of p2p2pMemVar) esi; copy back into esi               -- First level of indirection
    mov esi, [esi]                                          ;dereference (address of p2pMemVar) esi; copy back into esi                 -- Second level of indirection
    mov esi, [esi]                                          ;dereference (address of pMemVar) esi; copy back into esi                   -- Third level of indirection
    mov esi, [esi]                                          ;dereference (address of MemVar) esi; copy back into esi                    -- Fourth level of indirection
                                                            ;we now have "direct reference" to MemVar's value/contents
    print str$(esi)
    print chr$(" = value/contents of MemVar",13,10,13,10)

    mov esi, p2p2pMemVar                                    ;copy value/contents of p2p2pMemVar, which is the address of "p2pMemVar"
    print str$(esi)
    print chr$("   = address of p2pMemVar, not **p2p2pMemVar**",13,10)
    mov esi, [esi]                                          ;dereference (address of p2pMemVar) esi; copy back into esi                 -- First level of indirection
    mov esi, [esi]                                          ;dereference (addresss of pMemVar) esi; copy back into esi                  -- Second level of indirection
    mov esi, [esi]                                          ;dereference (address of MemVar) esi; copy back into esi                    -- Third level of indirection
                                                            ;we now have "direct reference" to MemVar's value/contents
    print str$(esi)                     
    print chr$(" = value/contents of MemVar",13,10,13,10)
                                                         
    inkey "Press any key to exit..."   
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start

Output:
123456789 = value/contents of MemVar

123456789 = value/contents of MemVar

123456789 = value/contents of MemVar

4206592   = address of MemVar
123456789 = value/contents of MemVar

4206596   = address of pMemVar
123456789 = value/contents of MemVar

4206600   = address of p2pMemVar
123456789 = value/contents of MemVar

4206604   = address of p2p2pMemVar
123456789 = value/contents of MemVar

4206600   = address of p2pMemVar, not **p2p2pMemVar**
123456789 = value/contents of MemVar

Press any key to exit...


Sorry for such a long post, but I really want to understand why using mov esi, array is wrong, and why mov esi, offset array/lea esi, array is correct.
I know it's a long post, but I do hope you read it to understand what I'm saying. Which is, it doesn't seem wrong to me.