4.30 Pointers to Records

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 ith 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 );