RecordsUnionsNamespaces.pdf

(240 KB) Pobierz
AoA.book
Records, Unions, and Namespaces
Recor
ds, Unions, and Name Spaces
Chapter Five
5.1
Chapter Overview
This chapter discusses ho
w to declare and use record (structures), unions, and name spaces in your pro
-
grams.
After strings and arrays, records are among the most commonly used composite data types; indeed,
records are the mechanism you use to create user
-defi
ned composite data types. Man
y assembly language
programmers ne
v
er bother to learn ho
w to use records in assembly language, yet w
ould ne
v
er consider not
using them in high le
v
el language programs.
This is some
what inconsistent since records (structures) are
just as useful in assembly language programs as in high le
v
el language programs. Gi
v
en that you use
records in assembly language (and especially HLA) in a manner quite similar to high le
el languages, there
really is no reason for e
xcluding this important tool from your programmer’
s tool chest.
Although you’
ll use
unions and name spaces f
ar less often than records, their presence in the HLA language is crucial for man
y
adv
anced applications.
This brief chapter pro
vides all the information you need to successfully use records,
unions, and name spaces within your HLA programs.
5.2
Records
Another major composite data structure is the P
ascal
ecor
d
or C/C++
structur
e
1
.
The P
ascal terminol
-
ogy is probably better
, since it tends to a
v
oid confusion with the more general term
data structur
e
. Since
HLA uses the term “record” we’
ll adopt that term here.
Whereas an array is homogeneous, whose elements are all the same, the elements in a record can be of
an
y type.
Arrays let you select a particular element via an inte
ger inde
x.
W
ith records, you must select an
element (kno
wn as a
eld
) by name.
The whole purpose of a record is to let you encapsulate dif
ferent, b
ut logically related, data into a single
package.
The P
ascal record declaration for a student is probably the most typical e
xample:
student =
record
Name: string [64];
Major: integer;
string[11];
Midterm1: integer;
Midterm2: integer;
Final: integer;
Homework: integer;
Projects: integer;
end;
SSN:
Most P
ascal compilers allocate each fi
eld in a record to contiguous memory locations.
This means that
2
ascal will reserv
e the fi
rst 65 bytes for the name
, the ne
xt tw
o bytes hold the major code, the ne
xt 12 the
Social Security Number
, etc.
In HLA, you can also create structure types using the RECORD/ENDRECORD declaration.
Y
ou w
ould
encode the abo
v
e record in HLA as follo
ws:
type
student:
record
Name: char[65];
Major: int16;
SSN: char[12];
1. It also goes by some other names in other languages, but most people recognize at least one of these names.
2. Strings require an extra byte, in addition to all the characters in the string, to encode the length.
Beta Draft - Do not distribute
© 2001, By Randall Hyde
Page
483
v
r
P
286680857.001.png
Chapter Five
Volume Three
Midterm1: int16;
Midterm2: int16;
Final: int16;
Homework: int16;
Projects: int16;
endrecord;
ery similar to the Pascal declaration. Note that, to be true to the
Pascal declaration, this example uses character arrays rather than strings for the
(U.S Social
Security Number) fields. In a real HLA record declaration you’d probably use a string type for at least the
name (keeping in mind that a string variable is only a four byte pointer).
The field names within the record must be unique. That is, the same name may not appear two or more
times in the same record. However, all field names are local to that record. Therefore, you may reuse those
field names elsewhere in the program.
The RECORD/ENDRECORD type declaration may appear in a variable declaration section (e.g.,
STATIC or VAR) or in a TYPE declaration section. In the previous example the
Name
and
SSN
Student
declaration appears
in the
TYPE section, so this does not actually allocate an
y storage for a
Student
v
ariable. Instead, you ha
v
e
to e
xplicitly declare a v
ariable of type
Student
.
The follo
wing e
xample demonstrates ho
w to do this:
var
John: Student;
This allocates 81 bytes of storage laid out in memory as sho
wn in
Figure 5.1
.
Name
(65 bytes)
SSN
(12 bytes)
Mid 2
(2 bytes)
Homework
(2 bytes)
John
Major
(2 bytes)
Mid 1
(2 bytes)
Final
(2 bytes)
Projects
(2 bytes)
Figure 5.1
Student Data Structure Storage in Memory
If the label
John
corresponds to the
base address
of this record, then the
Name
field is at offset
John+0
, the
, etc.
To access an element of a structure you need to know the offset from the beginning of the structure to
the desired field. For example, the
field is at offset
John+65
, the
SSN
field is at offset
John+67
Major
eld in the v
ariable
J
ohn
is at of
fset 65 from the base address of
J
ohn
.
Therefore, you could store the v
alue in
AX into this fi
eld using the instruction
mov( ax, (type word John[65]) );
Unfortunately, memorizing all the offsets to fields in a record defeats the whole purpose of using them in the
first place. After all, if you’ve got to deal with these numeric offsets why not just use an array of bytes
instead of a record?
Well, as it turns out, HLA lets you refer to field names in a record using the same mechanism C/C++
and Pascal use: the dot operator. To store AX into the Major field, you could use “mov( ax, John.Major );”
instead of the previous instruction. This is much more readable and certainly easier to use.
Note that the use of the dot operator does not introduce a new addressing mode. The instruction
“mov( ax, John.Major );” still uses the displacement only addressing mode. HLA simply adds the base
address of John with the offset to the Major field (65) to get the actual displacement to encode into the
instruction.
Like any type declaration, HLA requires all record type declarations to appear in the program before
you use them. However, you don’t have to define all records in the TYPE section to create record variables.
You can use the RECORD/ENDRECORD declaration directly in a variable declaration section. This is con-
Page
484
© 2001, By Randall Hyde
Beta Draft - Do not distribute
As you can see, the HLA declaration is v
Major
286680857.002.png
Records, Unions, and Namespaces
venient if you have only one instance of a given record object in your program. The following example dem-
onstrates this:
storage
OriginPoint: record
x: uns8;
y: uns8;
z: uns8;
endrecord;
5.3 Record Constants
HLA lets you define record constants. In fact, HLA supports both symbolic record constants and literal
record constants. Record constants are useful as initializers for static record variables. They are also quite
useful as compile-time data structures when using the HLA compile-time language (see the chapters on
Macros and the HLA Compile-Time Language). This section discusses how to create record constants.
A record literal constant takes the following form:
RecordTypeName :[ List_of_comma_separated_constants ]
The RecordTypeName is the name of a record data type you’ve defined in an HLA TYPE section prior to
this point. To create a record constant you must have previously defined the record type in a TYPE section
of your program.
The constant list appearing between the brackets are the data items for each of the fields in the specified
record. The first item in the list corresponds to the first field of the record, the second item in the list corre-
sponds to the second field, etc. The data types of each of the constants appearing in this list must match their
respective field types. The following example demonstrates how to use a literal record constant to initialize
a record variable:
type
point: record
x:int32;
y:int32;
z:int32;
endrecord;
static
Vector: point := point:[ 1, -2, 3 ];
This declaration initializes Vector.x with 1, Vector.y with -2, and Vector.z with 3.
You can also create symbolic record constants by declaring record objects in the CONST or VAL sec-
tions of your program. You access fields of these symbolic record constants just as you would access the
field of a record variable, using the dot operator. Since the object is a constant, you can specify the field of a
record constant anywhere a constant of that field’s type is legal. You can also employ symbolic record con-
stants as record variable initializers. The following example demonstrates this:
type
point: record
x:int32;
y:int32;
z:int32;
endrecord;
const
PointInSpace: point := point:[ 1, 2, 3 ];
static
Vector: point := PointInSpace;
Beta Draft - Do not distribute
© 2001, By Randall Hyde
Page 485
Chapter Five
Volume Three
XCoord: int32 := PointInSpace.x;
.
.
.
stdout.put( “Y Coordinate is “, PointInSpace.y, nl );
.
.
.
5.4 Arrays of Records
It is a perfectly reasonable operation to create an array of records. To do so, you simply create a record
type and then use the standard array declaration syntax when declaring an array of that record type. The fol-
lowing example demonstrates how you could do this:
type
recElement:
record
<< fields for this record >>
endrecord;
.
.
.
static
recArray: recElement[4];
To access an element of this array you use the standard array indexing techniques found in the chapter
on arrays. Since recArray is a single dimension array, you’d compute the address of an element of this array
using the formula “baseAddress + index*@size(recElement).” For example, to access an element of recAr-
ray you’d use code like the following:
// Access element i of recArray:
intmul( @size( recElement ), i, ebx ); // ebx := i*@size( recElement )
mov( recArray.someField[ebx], eax );
Note that the index specification follows the entire variable name; remember, this is assembly not a high
level language (in a high level language you’d probably use “recArray[i].someField”).
Naturally, you can create multidimensional arrays of records as well. You would use the standard row or
column major order functions to compute the address of an element within such records. The only thing that
really changes (from the discussion of arrays) is that the size of each element is the size of the record object.
static
rec2D: recElement[ 4, 6 ];
.
.
.
// Access element [i,j] of rec2D and load “someField” into EAX:
intmul( 6, i, ebx );
add( j, ebx );
intmul( @size( recElement ), ebx );
mov( rec2D.someField[ ebx ], eax );
Page 486
© 2001, By Randall Hyde
Beta Draft - Do not distribute
Records, Unions, and Namespaces
5.5 Arrays/Records as Record Fields
Records may contain other records or arrays as fields. Consider the following definition:
type
Pixel:
record
Pt: point;
color: dword;
endrecord;
The definition above defines a single point with a 32 bit color component. When initializing an object of type
Pixel, the first initializer corresponds to the Pt field, not the x-coordinate field . The following definition is
incorrect:
static
ThisPt: Pixel := Pixel:[ 5, 10 ]; // Syntactically incorrect!
The value of the first field (“5”) is not an object of type point . Therefore, the assembler generates an error
when encountering this statement. HLA will allow you to initialize the fields of Pixel using declarations like
the following:
static
ThisPt: Pixel := Pixel:[ point:[ 1, 2, 3 ], 10 ];
ThatPt: Pixel := Pixel:[ point:[ 0, 0, 0 ], 5 ];
Accessing Pixel fields is very easy. Like a high level language you use a single period to reference the Pt
field and a second period to access the x , y , and z fields of point :
stdout.put( “ThisPt.Pt.x = “, ThisPt.Pt.x, nl );
stdout.put( “ThisPt.Pt.y = “, ThisPt.Pt.y, nl );
stdout.put( “ThisPt.Pt.z = “, ThisPt.Pt.z, nl );
.
.
.
mov( eax, ThisPt.Color );
You can also declare arrays as record fields. The following record creates a data type capable of repre-
senting an object with eight points (e.g., a cube):
type
Object8:
record
Pts: point[8];
Color: dword;
endrecord;
This record allocates storage for eight different points. Accessing an element of the Pts array requires that
you know the size of an object of type point (remember, you must multiply the index into the array by the
size of one element, 12 in this particular case). Suppose, for example, that you have a variable CUBE of type
Object8 . You could access elements of the Pts array as follows:
// CUBE.Pts[i].x := 0;
mov( i, ebx );
intmul( 12, ebx );
mov( 0, CUBE.Pts.x[ebx] );
The one unfortunate aspect of all this is that you must know the size of each element of the Pts array.
Fortunately, HLA provides a built-in function that will compute the size of an array element (in bytes) for
you: the @size function. You can rewrite the code above using @size as follows:
// CUBE.Pts[i].x := 0;
mov( i, ebx );
Beta Draft - Do not distribute
© 2001, By Randall Hyde
Page 487
Zgłoś jeśli naruszono regulamin