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 41—add 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.
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
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
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.
Here it is.
Thank you very much jj2007.
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.