Dont get hung up on structures.. they are not a hassle at all...
The point of structures is yes, to localize alot of data under one premise
(or tag name). However, they are still a bit abstract from ASM programming,
which is why i suspect you have been having problems in the past. They are
simply a template to help organize data, but they are NOT data in
themselves. I better go to examples at this point:
I could say..
Point1_x dd 0
Point1_y dd 0
Point2_x dd 0
Point2_y dd 0 etc. etc.
And in using them i would have to reference each member directly:
mov eax, Point2_y
or
lea ebx, Point1_x
mov eax, [ebx + 12] ; 12'th byte after the first as defined above
This is ok, but it would be easier to use data if they are grouped together as
one type of data:
My_Point struc
x dd ?
y dd ?
My_Point ends
and in .data
Point1 My_Point <0,0> ; initialize x,y to 0,0
Point2 My_Point <2,0> ; initialize x,y to 2,0
or in .data?
Point3 My_Point <> ; uninitialized data ,?>
Point4 My_Point <>
The in the data segments, the stucture is used as a template to allocate the
amount of memory (dd's) in the order they appear in the template. This
is why i say stucts are NOT data in themselves. Now, if i were to
disassemble the data segment of this one or the pervious above, they
data would be in the same order (point1's x data, point1's y data,
point2's x data, point2's y data). The difference is by simply telling
MASM this will by a structured define, you can skip the redundancy of
typing it all out in order.
The code to use these are now:
mov eax, Point2.y
; y is the structure member that masm used to determin the offset
; this is the same as
; lea ebx, Point2
; mov eax, [ebx + 4] ; 4 bytes after the first (y data)
One of the benifits is the fact the data HAS to be grouped together as prescibed
in the structure template.. my origional example of separated data IS
grouped together, but doesnt has to be. It could be a random like such:
Point1_x dd 0
MyFlag dd 0
Point1_y dd 0
Point2_x dd 0
IsOn dd 0
Point2_y dd 0
If i were asked to have the address to a point in this setup, you couldnt do it,
because MyFlag is inbetween Point1_x and Point1_y:
invoke SomeFunct, addr Point1_x
will be used as:
mov ebx, Point1_x ; base address of a point
mov eax, [ebx] ; get the x data
mov eax, [ebx + 4] ; get the y data (assumed 4 bytes after the base)
The problem here is, 4 bytes after the first is MyFlag!!!! Using structures
insures this problem is always taken care of (assuming the structure is
defined in the correct order).
Taking this a step further, you can also NEST structures to build more
complex bounded data:
My_Circle sturct
Center My_Point <>
Radius dd ?
My_Circle ends
Colored_Circle struct
Circle My_Circle <>
Color dd ?
Colored_Circle ends
By simple defining in .data?
RGB_Circle Colored_Circle <>
i have allocated memory in the data segment in this order:
RGB_Circle: 4 bytes, Referenced as (this).Circle.Center.x
+4 bytes, Referenced as (this).Circle.Center.y
+4 bytes, Referenced as (this).Circle.Raduis
+4 bytes, Referenced as (this).Color
NOTE: (this) refers to the name of the defined data for 'this' structure type,
in this case it would be RGB_Circle.Circle.Center.x
ALSO NOTE: the 'SIZEOF Colored_Circle' command would return the above
evaluation = 16 bytes.
As a final thought, the use of stuctures are also advantageous when dealin with
them as pointer, by simply passing a simple 4 byte pointer, it can be
used to directly reference (in the above case) 16 bytes of data. (this
speeds up your function calling, because less is being pushed on the
stack).
; my fuction used a colored circle...
My_Fuction proc lpColoredCirlce:DWORD
...
mov ebx, lpColoredCircle
mov eax, (Colored_Circle PTR [ebx]).Circle.Center.x ;get x
mov edx, (Colored_Circle PTR [ebx]).Circle.Center.y ;get y
mov esi, (Colored_Circle PTR [ebx]).Radius ;get Rad
mov edi, (Colored_Circle PTR [ebx]).Color ;get Color
add edi, 20h
mov (Colored_Circle PTR [ebx]).Color, edi ;set Color
...
ret
My_Function endp
If your thinking there is still alot of keystrokes
involved here a simpler trick is to use the ASSUME directive on a
register as follows:
; my fuction used a colored circle...
My_Fuction proc lpColoredCirlce:DWORD
...
mov ebx, lpColoredCircle
ASSUME ebx:PTR Colored_Circle ; saves redundant typing
mov eax, [ebx].Circle.Center.x ;get x
mov edx, [ebx].Circle.Center.y ;get y
mov esi, [ebx].Radius ;get Rad
mov edi, [ebx].Color ;get Color
add edi, 20h
mov [ebx].Color, edi ;set Color
...
ASSUME EBX:NOTHING ; unassume ebx
ret
My_Function endp
Well there is my crash-course tutorial on Structures , im only a modest
programmer my self, so with the volume of info i tossed at you its
possible i overlooked some minor detail, but in essence this is it..
Hope it helps you out...
NaN
(( REPLY BY ERNIE ))
Everything Nan said he said well and true. However, a few thoughts
on using assume.
We all know, when you ASSUME something, you make an ASS of U and ME.
Trite, but still true.
If you assume a register, then forget to unassume it, you can get
strange results. Or, the assume could be so far from the line that
needs it the meaning is left unclear (to the human reading the code).
I'm not a big fan of weird "action at a distance" operators like this.
I am a devoted user of structures, they are just so handy. I used to
use the syntax Nan stated, until I found a slightly more compace (but
just as readable) form MASM accepts:
mov ebx, lpColoredCircle
mov eax, (Colored_Circle PTR [ebx]).Circle.Center.x ;get x
mov edx, (Colored_Circle PTR [ebx]).Circle.Center.y ;get y
mov esi, (Colored_Circle PTR [ebx]).Radius ;get Rad
mov edi, (Colored_Circle PTR [ebx]).Color ;get Color
may also be stated as:
mov ebx, lpColoredCircle
mov eax, [ebx].Colored_Circle.Circle.Center.x ;get x
mov edx, [ebx].Colored_Circle.Circle.Center.y ;get y
mov esi, [ebx].Colored_Circle.Radius ;get Rad
mov edi, [ebx].Colored_Circle.Color ;get Color
Ernie
(( REPLY BY MIRNO ))
The idea of a structure is purely for us the programmer.
All the computer sees is data, for example:
MyStruct STRUCT
a DWORD ?
b BYTE ?
c WORD ?
d BYTE 3 DUP (?)
MyStruct ENDS
As far as memory allocation is concerned this is 10 bytes, nothing else. To the
assembler, it is also a method of formating accesses to that data, and
it is possible to apply that formating to any piece of memory.
This allows the following code:
FourLetterWord STRUCT
FirstLetter BYTE ?
SecondLetter BYTE ?
ThirdLetter BYTE ?
FourthLetter BYTE ?
NullTerminator BYTE ?
FourLetterWord ENDS
.data
Word1 db "spam",0
Word2 db "clam",0
.code
start:
mov edx, ADDR Word1
mov (FourLetterWord PTR [edx]).FirstLetter, 'c'
invoke ExitProcess, 0
end start
Although we declared Word1 as a string of bytes, we can apply the formating of
the FourLetterWord structure to it.
So in the case of GlobalAlloc, you need to do something like:
invoke GlobalAlloc, ACCESS_TYPE, SIZEOF MyStruct * number_needed
This will create enough data to apply the format of MyStruct without causing
errors.
Mirno
(( REPLY BY NaN ))
Exactly, your simply allocating a bunch of bytes.. but if you keep
control of all your allocated bytes of data (ie, add proper offsets to
your global data pointer), then you can use the above methods with
pointers as Ernie outlined.
Structures are simply masks or templates to make your memory 'look'
organized... as Mirno demonstrates (good example BTW).
I could still allocate 5 bytes of heap memory, and use my above
structure (size 16 bytes templated) to reference it! The catch is, i
would only be able to access:
mov ebx, GlobalDataPointer
mov eax, (Colored_Circle PTR [ebx]).Cricle.Center.x
Because this is the first 4 bytes templated by my structure. To try to go to 'y'
would violate the heap space alocated, since in my example i have only
allocated 5 bytes in all. This is why Mirno suggests 'N * sizeof
Colored_Circle' where N is Integers > 1. That way you will always have
just enough data, to imply an array of N Colored_Cirlce structures,
and know you have enough data set asside for each memeber!
NaN
|