|
Although HLA is fairly loose when it comes to type checking, HLA does ensure that you specify appropriate operand sizes to an instruction. For example, consider the following (incorrect) program:
program hasErrors; static i8: int8; i16: int16; i32: int32; begin hasErrors; mov( i8, eax ); mov( i16, al ); mov( i32, ax ); end hasErrors;
HLA will generate errors for these three mov instructions. This is because the operand sizes are incompatible. The first instruction attempts to move a byte into EAX, the second instruction attempts to move a word into AL and the third instruction attempts to move a double word into AX. The mov instruction, of course, requires both operands to be the same size.
While this is a good feature in HLA,[12] there are times when it gets in the way. Consider the following code fragments:
static byte_values: byte; @nostorage; byte 0, 1; ... mov( byte_values, ax );
In this example let's assume that the programmer really wants to load the word starting at the address of byte_values into the AX register because they want to load AL with 0 and AH with 1 using a single instruction. HLA will refuse, claiming there is a type mismatch error (because byte_values is a byte object and AX is a word object). The programmer could break this into two instructions, one to load AL with the byte at address byte_values and the other to load AH with the byte at address byte_values[1]. Unfortunately, this decomposition makes the program slightly less efficient (which was probably the reason for using the single mov instruction in the first place). Somehow, it would be nice if we could tell HLA that we know what we're doing and we want to treat the byte_values variable as a word object. HLA's type coercion facilities provide this capability.
Type coercion[13] is the process of telling HLA that you want to treat an object as an explicit type, regardless of its actual type. To coerce the type of a variable, you use the following syntax:
(type newTypeName addressExpression)
The newTypeName item is the new type you wish to associate with the memory location specified by addressExpression. You may use this coercion operator anywhere a memory address is legal. To correct the previous example, so HLA doesn't complain about type mismatches, you would use the following statement:
mov( (type word byte_values), ax );
This instruction tells HLA to load the AX register with the word starting at address byte_values in memory. Assuming byte_values still contains its initial values, this instruction will load zero into AL and one into AH.
Type coercion is necessary when you specify an anonymous variable as the operand to an instruction that directly modifies memory (e.g., neg, shl, not, and so on). Consider the following statement:
not( [ebx] );
HLA will generate an error on this instruction because it cannot determine the size of the memory operand. The instruction does not supply sufficient information to determine whether the program should invert the bits in the byte pointed at by EBX, the word pointed at by EBX, or the double word pointed at by EBX. You must use type coercion to explicitly specify size of anonymous references with these types of instructions:
not( (type byte [ebx]) ); not( (type dword [ebx]) );
Caution | Do not use the type coercion operator unless you know exactly what you are doing and fully understand the effect it has on your program. Beginning assembly language programmers often use type coercion as a tool to quiet the compiler when it complains about type mismatches without solving the underlying problem. Consider the following statement (where byteVar is an 8-bit variable): mov( eax, (type dword byteVar) ); |
Without the type coercion operator, HLA complains about this instruction because it attempts to store a 32-bit register into an 8-bit memory location. A beginning programmer, wanting their program to compile, may take a shortcut and use the type coercion operator as shown in this instruction; this certainly quiets the compiler — it will no longer complain about a type mismatch. So the beginning programmer is happy. But the program is still incorrect; the only difference is that HLA no longer warns you about your error. The type coercion operator does not fix the problem of attempting to store a 32-bit value into an 8-bit memory location — it simply allows the instruction to store a 32-bit value starting at the address specified by the 8-bit variable. The program still stores four bytes, overwriting the three bytes following byteVar in memory. This often produces unexpected results including the phantom modification of variables in your program.[14] Another, rarer, possibility is for the program to abort with a general protection fault. This can occur if the three bytes following byteVar are not allocated in real memory or if those bytes just happen to fall in a read-only segment in memory. The important thing to remember about the type coercion operator is this: "If you cannot exactly state the affect this operator has, don't use it."
Also keep in mind that the type coercion operator does not perform any translation of the data in memory. It simply tells the compiler to treat the bits in memory as a different type. It will not automatically sign extend an 8-bit value to 32 bits nor will it convert an integer to a floating point value. It simply tells the compiler to treat the bit pattern of the memory operand as a different type.
[12]After all, if the two operand sizes are different this usually indicates an error in the program.
[13]Also called type casting in some languages.
[14]If you have a variable immediately following byteVar in this example, the mov instruction will surely overwrite the value of that variable, whether or not you intend for this to happen.
|