Chapter 4. CONSTANTS, VARIABLES, AND DATA TYPES

CONSTANTS, VARIABLES, AND DATA TYPES

Chapter 2 discussed the basic format for data in memory. Chapter 3 covered how a computer system physically organizes that data in memory. This chapter finishes the discussion by connecting the concept of data representation to its actual physical representation. As the title implies, this chapter concerns itself with three main topics: constants, variables, and data structures. This chapter does not assume that you've had a formal course in data structures, though such experience would be useful.

This chapter discusses how to declare and use constants, scalar variables, integers, data types, pointers, arrays, records/structures, unions, and namespaces. You must master these subjects before going on to the next chapter. Declaring and accessing arrays, in particular, seems to present a multitude of problems to beginning assembly language programmers. However, the rest of this text depends on your understanding of these data structures and their memory representation. Do not try to skim over this material with the expectation that you will pick it up as you need it later. You will need it right away, and trying to learn this material along with later material will only confuse you more.

This chapter introduces arrays and other concepts that will require the expansion of your 80x86 instruction set knowledge. In particular, you will need to learn how to multiply two values; hence the first instruction we will look at is the intmul (integer multiply) instruction. Another common task when accessing arrays is to check to see if an array index is within bounds. The 80x86 bound instruction provides a convenient way to check a register's value to see if it is within some range. Finally, the into (interrupt on overflow) instruction provides a quick check for signed arithmetic overflow. Although into isn't really necessary for array (or other data type) access, its function is very similar to bound; hence the presentation of it at this point.

The intmul instruction takes one of the following forms:

// The following compute destreg = destreg * constant

          intmul( constant, destreg16 );
          intmul( constant, destreg32 );

          // The following compute dest = src * constant

          intmul( constant, srcreg16, destreg16 );
          intmul( constant, srcmem16, destreg16 );

          intmul( constant, srcreg32, destreg32 );
          intmul( constant, srcmem32, destreg32 );

          // The following compute dest = src * constant

          intmul( srcreg16, destreg16 );
          intmul( srcmem16, destreg16 );
          intmul( srcreg32, destreg32 );
          intmul( srcmem32, destreg32 );

Note that the syntax of the intmul instruction is different from that of the add and sub instructions. In particular, the destination operand must be a register (add and sub both allow a memory operand as a destination). Also note that intmul allows three operands when the first operand is a constant. Another important difference is that the intmul instruction allows only 16-bit and 32-bit operands; it does not multiply 8-bit operands.

intmul computes the product of its specified operands and stores the result into the destination register. If an overflow occurs (which is always a signed overflow, because intmul multiplies only signed integer values), then this instruction sets both the carry and overflow flags. intmul leaves the other condition code flags undefined (so, for example, you cannot meaningfully check the sign flag or the zero flag after executing intmul).

The bound instruction checks a 16-bit or 32-bit register to see if it is between two values. If the value is outside this range, the program raises an exception and aborts. This instruction is particularly useful for checking to see if an array index is within a given range. The bound instruction takes one of the following forms:

bound( reg16, LBconstant, UBconstant );
bound( reg32, LBconstant, UBconstant );

bound( reg16, Mem16[2] );
bound( reg32, Mem32[2] );

The bound instruction compares its register operand against an unsigned lower bound value and an unsigned upper bound value to ensure that the register is in the range:

lower_bound <= register <= upper_bound

The form of the bound instruction with three operands compares the register against the second and third parameters (the lower bound and upper bound, respectively).[47] The bound instruction with two operands checks the register against one of the following ranges:

Mem16[0] <= register16 <= Mem16[2]
Mem32[0] <= register32 <= Mem32[4]

If the specified register is not within the given range, then the 80x86 raises an exception. You can trap this exception using the HLA try..endtry exception-handling statement. The excepts.hhf header file defines an exception, ex.BoundInstr, specifically for this purpose. The program in Example 4-1 demonstrates how to use the bound instruction to check some user input.

The into instruction, like bound, also generates an exception under certain conditions. Specifically, into generates an exception if the overflow flag is set. Normally, you would use into immediately after a signed arithmetic operation (e.g., intmul) to see if an overflow occurs. If the overflow flag is not set, the system ignores into; however, if the overflow flag is set, then the into instruction raises the ex.IntoInstr exception. The program in Example 4-2 demonstrates the use of the into instruction.



[47] This form isn't a true 80x86 instruction. HLA converts this form of the bound instruction to the two-operand form by creating two readonly memory variables initialized with the specified constants.