During execution, your program may refer to record objects indirectly using a pointer. When you use a pointer to access fields of a structure, you must load one of the 80x86's 32-bit registers with the address of the desired record. Suppose you have the following variable declarations (assuming the Object8
structure from an earlier section):
static Cube: Object8; CubePtr: pointer to Object8 := &Cube;
CubePtr
contains the address of (that is, it is a pointer to) the Cube
object. To access the Color
field of the Cube
object, you could use an instruction like mov( Cube.Color, eax );
. When accessing a field via a pointer, you first need to load the address of the object into a 32-bit register such as EBX. The instruction mov( CubePtr, ebx );
will do the trick. After doing so, you can access fields of the Cube
object using the [ebx+offset]
addressing mode. The only problem is, "How do you specify which field to access?" Consider briefly the following incorrect code:
mov( CubePtr, ebx ); mov( [ebx].Color, eax ); // This does not work!
Because field names are local to a structure and it's possible to reuse a field name in two or more structures, how does HLA determine which offset Color
represents? When accessing structure members directly (e.g., mov( Cube.Color, eax )
;), there is no ambiguity because Cube
has a specific type that the assembler can check. [ebx]
, on the other hand, can point at anything. In particular, it can point at any structure that contains a Color
field. So the assembler cannot, on its own, decide which offset to use for the Color
symbol.
HLA resolves this ambiguity by requiring that you explicitly supply a type. To do this, you must coerce [ebx]
to type Cube
. Once you do this, you can use the normal dot operator notation to access the Color
field:
mov( CubePtr, ebx ); mov( (type Cube [ebx]).Color, eax );
If you have a pointer to a record and one of that record's fields is an array, the easiest way to access elements of that field is by using the base-plus-indexed addressing mode. To do so, you just load the pointer's value into one register and compute the index into the array in a second register. Then you combine these two registers in the address expression. In the example above, the Pts
field is an array of eight point
objects. To access field x
of the i
th element of the Cube.Pts
field, you'd use code like the following:
mov( CubePtr, ebx ); intmul( @size( point ), i, esi ); // Compute index into point array. mov( (type Object8 [ebx]).Pts.x[ esi*4 ], eax );
If you use a pointer to a particular record type frequently in your program, typing a coercion operator like (type Object8 [ebx])
can get old very quickly. One way to reduce the typing needed to coerce EBX is to use a text
constant. Consider the following statement:
const O8ptr: text := "(type Object8 [ebx])";
With this statement at the beginning of your program, you can use O8ptr
in place of the type coercion operator, and HLA will automatically substitute the appropriate text. With a text constant like the above, the former example becomes a little more readable and writable:
mov( CubePtr, ebx ); intmul( @size( point ), i, esi ); // Compute index into point array. mov( O8Ptr.Pts.x[ esi*4 ], eax );