Another major composite data structure is the Pascal record or C/C++/C# structure.[65] The Pascal terminology is probably better, because it tends to avoid confusion with the more general term data structure. Because HLA uses the term record, we'll adopt that term here.
Whereas an array is homogeneous, whose elements are all the same type, the elements in a record can have different types. Arrays let you select a particular element via an integer index. With records, you must select an element (known as a field) by name.
The whole purpose of a record is to let you encapsulate different, though logically related, data into a single package. The Pascal record declaration for a student is a typical example:
student = record Name: string[64]; Major: integer; SSN: string[11]; Midterm1: integer; Midterm2: integer; Final: integer; Homework: integer; Projects: integer; end;
Most Pascal compilers allocate each field in a record to contiguous memory locations. This means that Pascal will reserve the first 65 bytes for the name,[66] the next 2 bytes hold the major code, the next 12 bytes hold the Social Security number, and so on.
In HLA, you can also create record types using the record
/endrecord
declaration. You would encode the above record in HLA as follows:
type student: record Name: char[65]; Major: int16; SSN: char[12]; Midterm1: int16; Midterm2: int16; Final: int16; Homework: int16; Projects: int16; endrecord;
As you can see, the HLA declaration is very similar to the Pascal declaration. Note that, to be true to the Pascal declaration, this example uses character arrays rather than strings for the Name
and SSN
(US 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 4-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 or in different records.
The record
/endrecord
declaration may appear in a variable declaration section (e.g., static
or var
) or in a type
declaration section. In the previous example the Student
declaration appears in the type
section, so this does not actually allocate any storage for a Student
variable. Instead, you have to explicitly declare a variable of type Student
. The following example demonstrates how to do this:
var John: Student;
This allocates 81 bytes of storage laid out in memory as shown in Figure 4-10.
If the label John
corresponds to the base address of this record, then the Name
field is at offset John+0
, the Major
field is at offset John+65
, the SSN
field is at offset John+67
, and so on.
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 Major
field in the variable John
is at offset 65 from the base address of John
. Therefore, you could store the value in AX into this field 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 have to deal with these numeric offsets, why not just use an array of bytes instead of a record?
Fortunately, HLA lets you refer to field names in a record using the same mechanism C/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 convenient if you have only one instance of a given record object in your program. The following example demonstrates this:
storage OriginPoint: record x: uns8; y: uns8; z: uns8; endrecord;