4.4 HLA Constant and Value Declarations


4.4 HLA Constant and Value Declarations

HLA's const and val sections let you declare symbolic constants. The const section lets you declare identifiers whose value is constant throughout compilation and runtime; the val section lets you declare symbolic constants whose values can change at compile time, but whose values are constant at runtime (that is, the same name can have a different value at several points in the source code, but the value of a val symbol at a given point in the program cannot change while the program is running).

The const section appears in the same declaration section of your program that contains the static, readonly, storage, and var sections. It begins with the const reserved word and has a syntax that is nearly identical to the readonly section; that is, the const section contains a list of identifiers followed by a type and a constant expression. The following example will give you an idea of what the const section looks like:

 const      pi:                real32  := 3.14159;      MaxIndex:          uns32   := 15;      Delimiter:         char    := '/';      BitMask:           byte    := $F0;      DebugActive:       boolean := true; 

Once you declare these constants in this manner, you may use the symbolic identifiers anywhere the corresponding literal constant is legal. These constants are known as manifest constants. A manifest constant is a symbolic representation of a constant that allows you to substitute the literal value for the symbol anywhere in the program. Contrast this with readonly variables; a readonly variable is certainly a constant value because you cannot change such a variable at runtime. However, a memory location associated with readonly variables and the operating system, not the HLA compiler, enforces the read-only attribute at runtime. Although it will certainly crash your program when it runs, it is perfectly legal to write an instruction like "mov( eax, ReadOnlyVar );". On the other hand, it is no more legal to write "mov( eax, MaxIndex );" (using the declaration above) than it is to write "mov( eax, 15 );". In fact, both of these statements are equivalent because the compiler substitutes "15" for MaxIndex whenever it encounters this manifest constant.

If there is absolutely no ambiguity about a constant's type, then you may declare a constant by specifying only the name and the constant's value, omitting the type specification. In the example earlier, the pi, Delimiter, MaxIndex, and DebugActive constants could use the following declarations:

 const      pi                := 3.14159;   // Default type is real80.      MaxIndex          := 15;        // Default type is uns32.      Delimiter         := '/';       // Default type is char.      DebugActive       := true;      // Default type is boolean. 

Symbolic constants that have an integer literal constant are always given the smallest possible unsigned type if the constant is zero or positive, or the smallest possible integer type (int8, int16, and so on) if the value is negative.

Constant declarations are great for defining "magic" numbers that might possibly change during program modification. The program in Listing 4-3 provides an example of using constants to parameterize "magic" values in the program.

Listing 4-3: Data Alignment Program Rewritten Using CONST Definitions.

start example
 program ConstDemo; #include( "stdlib.hhf" ); const      MemToAllocate   := 4_000_000;      NumDWords       := MemToAllocate div 4;      MisalignBy      := 62;      MainRepetitions := 1000;      DataRepetitions := 999_900;      CacheLineSize   := 16; begin ConstDemo;      //console.cls();      stdout.put      (           "Memory Alignment Exercise",nl,           nl,           "Using a watch (preferably a stopwatch), time the execution of", nl           "the following code to determine how many seconds it takes to", nl           "execute.", nl           nl           "Press Enter to begin timing the code:"      );      // Allocate enough dynamic memory to ensure that it does not      // all fit inside the cache. Note: the machine had better have      // at least four megabytes free or virtual memory will kick in      // and invalidate the timing.      malloc( MemToAllocate );      // Zero out the memory (this loop really exists just to      // ensure that all memory is mapped in by the OS).      mov( NumDWords, ecx );      repeat           dec( ecx );           mov( 0, (type dword [eax+ecx*4]));      until( !ecx ); // Repeat until ECX = 0.      // Okay, wait for the user to press the Enter key.      stdin.readLn();      // Note: as processors get faster and faster, you may      // want to increase the size of the following constant.      // Execution time for this loop should be approximately      // 10-30 seconds.      mov( MainRepetitions, edx );      add( MisalignBy, eax ); // Force misalignment of data.      repeat           mov( DataRepetitions, ecx );           align( CacheLineSize );           repeat                sub( 4, ecx );                mov( [eax+ecx*4], ebx );                mov( [eax+ecx*4], ebx );                mov( [eax+ecx*4], ebx );                mov( [eax+ecx*4], ebx );           until( !ecx );           dec( edx );      until( !edx ); // Repeat until EAX is zero.      stdout.put( stdio.bell, "Stop timing and record time spent", nl, nl );      // Okay, time the aligned access.      stdout.put      (           "Press Enter again to begin timing access to aligned variable:"      );      stdin.readLn();      // Note: if you change the constant above, be sure to change      // this one, too!      mov( MainRepetitions, edx );      sub( MisalignBy, eax ); // Realign the data.      repeat           mov( DataRepetitions, ecx );           align( CacheLineSize );           repeat                sub( 4, ecx );                mov( [eax+ecx*4], ebx );                mov( [eax+ecx*4], ebx );                mov( [eax+ecx*4], ebx );                mov( [eax+ecx*4], ebx );           until( !ecx );           dec( edx );      until( !edx ); // Repeat until EAX is zero.      stdout.put( stdio.bell, "Stop timing and record time spent", nl, nl );      free( eax ); end ConstDemo; 
end example

4.4.1 Constant Types

Manifest constants can be any of the HLA primitive types plus a few of the composite types this chapter discusses. Chapters 1, 2, and 3 discussed most of the primitive types; the primitive types include the following:[4]

  • Boolean constants (true or false).

  • Uns8 constants (0..255).

  • Uns16 constants (0..65535).

  • Uns32 constants (0..4,294,967,295).

  • Int8 constants (-128..+127).

  • Int16 constants (-32768..+32767).

  • Int32 constants (-2,147,483,648..+2,147,483,647).

  • Char constants (any ASCII character with a character code in the range 0..255).

  • Byte constants (any eight-bit value including integers, booleans, and characters).

  • Word constants (any 16-bit value).

  • Dword constants (any 32-bit value).

  • Real32 constants (floating point values).

  • Real64 constants (floating point values).

  • Real80 constants (floating point values).

In addition to the constant types appearing above, the const section supports six additional constant types:

  • String constants

  • Text constants

  • Enumerated constant values

  • Array constants

  • Record/Union constants

  • Character set constants

These data types are the subject of this chapter, and the discussion of most of them appears a little later. However, the string and text constants are sufficiently important to warrant an early discussion of these constant types.

4.4.2 String and Character Literal Constants

HLA, like most programming languages, draws a distinction between a sequence of characters, a string, and a single character. This distinction is present both in the type declarations and in the syntax for literal character and string constants. Until now, this text has not drawn a fine distinction between character and string literal constants; now is time to do so.

String literal constants consist of a sequence of zero or more characters surrounded by the ASCII quote characters. The following are all examples of legal literal string constants:

      "This is a string"      // String with 16 characters.      ""                      // Zero length string.      "a"                     // String with a single character.      "123"                   // String of length three. 

A string of length one is not the same thing as a character constant. HLA uses two completely different internal representations for character and string values. Hence, "a" is not a character value; it is a string value that just happens to contain a single character.

Character literal constants take a couple forms, but the most common form consists of a single character surrounded by ASCII apostrophe characters:

     '2'           // Character constant equivalent to ASCII code $32.     'a'           // Character constant for lower case 'A'. 

As noted above, "a" and ‘a’ are not equivalent.

Those who are familiar with C, C++, or Java probably recognize these literal constant forms, because they are similar to the character and string constants in C/C++/Java. In fact, this text has made a tacit assumption to this point that you are somewhat familiar with C/C++ insofar as examples appearing up to this point use character and string constants without an explicit definition of them.[5]

Another similarity between C/C++ strings and HLAs is the automatic concatenation of adjacent literal string constants within your program. For example, HLA concatenates the two string constants

     "First part of string, " "second part of string" 

to form the single string constant

     "First part of string, second part of string" 

Beyond these few similarities, however, HLA strings and C/C++ strings differ. For example, C/C++ strings let you specify special character values using the escape character sequence consisting of a backslash character followed by one or more special characters; HLA does not use this escape character mechanism. HLA does provide, however, several other ways to insert special characters into a string or character constant.

Because HLA does not allow escape character sequences in literal string and character constants, the first question you might ask is "How does one embed quote characters in string constants and apostrophe characters in character constants?" To solve this problem, HLA uses the same technique as Pascal and many other languages: You insert two quotes in a string constant to represent a single quote or you place two apostrophes in a character constant to represent a single apostrophe character, e.g.,

     "He wrote a ""Hello World"" program as an example." 

The previous is equivalent to:

     He wrote a "Hello World" program as an example.     '''' 

The following is equivalent to a single apostrophe character.

HLA provides a couple of other features that eliminate the need for escape characters. In addition to concatenating two adjacent string constants to form a longer string constant, HLA will also concatenate any combination of adjacent character and string constants to form a single string constant:

     '1' '2' '3' // Equivalent to "123"     "He wrote a " '"' "Hello World" '"' " program as an example." 

Note that the two "He wrote " strings in the these examples are identical to HLA.

HLA provides a second way to specify character constants that handles all the other C/C++ escape character sequences: the ASCII code literal character constant. This literal character constant form uses the syntax:

 #integer_constant 

This form creates a character constant whose value is the ASCII code specified by integer_constant. The numeric constant can be a decimal, hexadecimal, or binary value, e.g.,

 #13      #$d      #%1101      // All three are the same character,                               // a carriage return. 

Because you may concatenate character literals with strings, and the #constant form is a character literal; the following are all legal strings:

     "Hello World" #13 #10      // #13 #10 is the Windows newline sequence                                // (carriage return followed by line feed).     "Error: Bad Value" #7      // #7 is the bell character.     "He wrote a " #$22 "Hello World" #$22 " program as an example." 

Because $22 is the ASCII code for the quote character, this last example is yet a third form of the "He wrote " string literal.

4.4.3 String and Text Constants in the CONST Section

String and text constants in the const section use the following declaration syntax:

 const      AStringConst:         string := "123";      ATextConst:           text   := "123"; 

Other than the data type of these two constants, their declarations are identical. However, their behavior in an HLA program is quite different.

Whenever HLA encounters a symbolic string constant within your program, it substitutes the string literal constant in place of the string name. So a statement like "stdout.put( AStringConst );" prints the string "123" (without quotes, of course) to the display. No real surprise here.

Whenever HLA encounters a symbolic text constant within your program, it substitutes the text of that string (rather than the string literal constant) for the identifier. That is, HLA substitutes the characters between the delimiting quotes in place of the symbolic text constant. Therefore, the following statement is perfectly legal given the previous declarations:

      mov( ATextConst, al ); // equivalent to mov( 123, al ); 

Note that substituting AStringConst for ATextConst in this example is illegal:

      mov( AStringConst, al ); // equivalent to mov( "123", al ); 

This latter example is illegal because you cannot move a string literal constant into the AL register.

Whenever HLA encounters a symbolic text constant in your program, it immediately substitutes the value of the text constant's string for that text constant and continues the compilation as though you had written the text constant's value rather than the symbolic identifier in your program. This can save some typing and help make your programs a little more readable if you often enter some sequence of text in your program. For example, consider the nl (newline) text constant declaration found in the HLA stdio.hhf library header file:

 const      nl: text := "#$d #$a"; // Windows version. Linux is just a line feed. 

Whenever HLA encounters the symbol nl, it immediately substitutes the value of the string "#$d #$a" for the nl identifier. When HLA sees the #$d (carriage return) character constant followed by the #$a (line feed) character constants, it concatenates the two to form the string containing the Windows newline sequence (a carriage return followed by a line feed). Consider the following two statements:

      stdout.put( "Hello World", nl );      stdout.put( "Hello World" nl ); 

(Notice that the second statement above does not separate the string literal and the nl symbol with a comma.) In the first example, HLA emits code that prints the string "Hello World" and then emits some additional code that prints a newline sequence. In the second example, HLA expands the nl symbol as follows:

      stdout.put( "Hello World" #$d #$a ); 

Now HLA sees a string literal constant ("Hello World") followed by two character constants. It concatenates the three of them together to form a single string and then prints this string with a single call. Therefore, leaving off the comma between the string literal and the nl symbol produces slightly more efficient code. Keep in mind that this only works with string literal constants. You cannot concatenate string variables, or a string variable with a string literal, by using this technique.

Linux users should note that the Linux end-of-line sequence is just a single line feed character. Therefore, the declaration for nl is slightly different in Linux (to always guarantee that nl expands to a string constant rather than a character constant).

In the constant section, if you specify only a constant identifier and a string constant (i.e., you do not supply a type), HLA defaults to type string. If you want to declare a text constant you must explicitly supply the type.

 const      AStrConst := "String Constant";      ATextConst: text := "mov( 0, eax );"; 

4.4.4 Constant Expressions

Thus far, this chapter has given the impression that a symbolic constant definition consists of an identifier, an optional type, and a literal constant. Actually, HLA constant declarations can be a lot more sophisticated than this because HLA allows the assignment of a constant expression, not just a literal constant, to a symbolic constant. The generic constant declaration takes one of the following two forms:

      Identifier : typeName := constant_expression ;      Identifier := constant_expression ; 

Constant expressions take the familiar form you're used to in high level languages like C/C++ and Pascal. They may contain literal constant values, previously declared symbolic constants, and various arithmetic operators. The following lists some of the operations possible in a constant expression:

 Arithmetic Operators      -      (unary negation) Negates the expression immediately following the             "-".      *      Multiplies the integer or real values around the asterisk.      div    Divides the left integer operand by the right integer operand             producing an integer (truncated) result.      mod    Divides the left integer operand by the right integer operand             producing an integer remainder.      /      Divides the left numeric operand by the second numeric operand             producing a floating point result.      +      Adds the left and right numeric operands.      -      Subtracts the right numeric operand from the left numeric operand. Comparison Operators      =, ==  Compares left operand with right operand. Returns TRUE if equal.      <>, != Compares left operand with right operand. Returns TRUE if not equal.      <   Returns true if left operand is less than right operand.      <=  Returns true if left operand is <= right operand.      >   Returns true if left operand is greater than right operand.      >=  Returns true if left operand is >= right operand. Logical Operators:[6]      &  For boolean operands, returns the logical AND of the two operands.      |      For boolean operands, returns the logical OR of the two operands.      ^      For boolean operands, returns the logical exclusive-OR.      !      Returns the logical NOT of the single operand following "!". Bitwise Logical Operators:      &  For integer numeric operands, returns bitwise AND of the operands.      |      For integer numeric operands, returns bitwise OR of the operands.      ^      For integer numeric operands, returns bitwise XOR of the operands.      !      For an integer numeric operand, returns bitwise NOT of the operand. String Operators:      '+'    Returns the concatenation of the left and right string operands. 

The constant expression operators follow standard precedence rules; you may use the parentheses to override the precedence if necessary. See the HLA Reference Manual on the CD-ROM for the exact precedence relationships between the operators. In general, if the precedence isn't obvious, use parentheses to exactly state the order of evaluation. HLA actually provides a few more operators than these, though the ones above are the ones you will most commonly use; the HLA documentation provides a complete list of constant expression operators.

If an identifier appears in a constant expression, that identifier must be a constant identifier that you have previously defined in your program. You may not use variable identifiers in a constant expression; their values are not defined at compile time when HLA evaluates the constant expression. Also, don't confuse compile time and run-time operations:

 // Constant expression, computed while HLA is compiling your program: const           x           := 5;           y           := 6;           Sum         := x + y; // Run-time calculation, computed while your program is running, long after // HLA has compiled it:      mov( x, al );      add( y, al ); 

HLA directly interprets the value of a constant expression during compilation. It does not emit any machine instructions to compute "x+y" in the constant expression above. Instead, it directly computes the sum of these two constant values. From that point forward in the program, HLA associates the value 11 with the constant Sum just as if the program had contained the statement "Sum := 11;" rather than "Sum := x+y;" On the other hand, HLA does not precompute the value 11 in AL for the mov and add instructions above;[7] it faithfully emits the object code for these two instructions and the 80x86 computes their sum when the program is run (sometime after the compilation is complete).

In general, constant expressions don't get very sophisticated in assembly language programs. Usually, you're adding, subtracting, or multiplying two integer values. For example, the following const section defines a set of constants that have consecutive values:

 const      TapeDAT           := 1;      Tape8mm           := TapeDAT + 1;      TapeQIC80         := Tape8mm + 1;      TapeTravan        := TapeQIC80 + 1;      TapeDLT           := TapeTravan + 1; 

The constants above have the following values: TapeDAT = 1, Tape8mm = 2, TapeQIC80 = 3, TapeTravan = 4, and TapeDLT = 5.

4.4.5 Multiple CONST Sections and Their Order in an HLA Program

Although const sections must appear in the declaration section of an HLA program (e.g., between the "program pgmname;" header and the corresponding "begin pgmname;" statement), they do not have to appear before or after any other items in the declaration section. In fact, like the variable declaration sections, you can place multiple const sections in a declaration section. The only restriction on HLA constant declarations is that you must declare any constant symbol before you use it in your program.

Some C/C++ programmers, for example, are more comfortable writing their constant declarations as follows (because this is closer to C/C++'s syntax for declaring constants):

 const      TapeDAT           :=   1; const      Tape8mm           :=   TapeDAT + 1; const      TapeQIC80         :=   Tape8mm + 1; const      TapeTravan        :=   TapeQIC80 + 1; const      TapeDLT           :=   TapeTravan + 1; 

The placement of the const section in a program seems to be a personal issue among programmers. Other than the requirements of defining all constants before you use them, you may feel free to insert the constant declaration section anywhere in the declaration section. Some programmers prefer to put all their const declarations at the beginning of their declaration section, some programmers prefer to spread them throughout declaration section, defining the constants just before they need them for some other purpose. Putting all your constants at the beginning of an HLA declaration section is probably the wisest choice right now. Later in this text you'll see reasons why you might want to define your constants later in a declaration section.

4.4.6 The HLA VAL Section

You cannot change the value of a constant you define in the const section. While this seems perfectly reasonable (constants after all, are supposed to be, well, constant), there are different ways we can define the term "constant" and const objects only follow the rules of one specific definition. HLA's val section lets you define constant objects that follow slightly different rules. This section will discuss the val section and the difference between val constants and const constants.

The concept of "const-ness" can exist at two different times: while HLA is compiling your program and later when your program executes (and HLA is no longer running). All reasonable definitions of a constant require that a value not change while the program is running. Whether or not the value of a "constant" can change during compilation is a separate issue. The difference between HLA const objects and HLA val objects is whether the value of the constant can change during compilation.

Once you define a constant in the const section, the value of that constant is immutable from that point forward both at runtime and while HLA is compiling your program. Therefore, an instruction like "mov( SymbolicCONST, EAX );" always moves the same value into EAX, regardless of where this instruction appears in the HLA main program. Once you define the symbol SymbolicCONST in the const section, this symbol has the same value from that point forward.

The HLA val section lets you declare symbolic constants, just like the const section. However, HLA val constants can change their value throughout the source code in your program. The following HLA declarations are perfectly legal:

 val      InitialValue      := 0; const    SomeVal           := InitialValue + 1;     // = 1 const    AnotherVal        := InitialValue + 2;     // = 2 val      InitialValue      := 100; const    ALargerVal        := InitialValue;         // = 100 const    LargeValTwo       := InitialValue*2;       // = 200 

All of the symbols appearing in the const sections use the symbolic value InitialValue as part of the definition. Note, however, that InitialValue has different values at various points in this code sequence; at the beginning of the code sequence InitialValue has the value zero, while later it has the value 100.

Remember, at runtime a val object is not a variable; it is still a manifest constant and HLA will substitute the current value of a val identifier for that identifier.[8] Statements like "mov( 25, InitialValue );" are no more legal than "mov( 25, 0 );" or "mov( 25, 100 );".

4.4.7 Modifying VAL Objects at Arbitrary Points in Your Programs

If you declare all your val objects in the declaration section, it would seem that you would not be able to change the value of a val object between the begin and end statements of your program. After all, the val section must appear in the declaration section of the program, and the declaration section ends before the begin statement. Later, you will learn that most val object modifications occur between the begin and end statements; hence, HLA must provide some way to change the value of a val object outside the declaration section. The mechanism to do this is the "?" operator.

Not only does HLA allow you to change the value of a val object outside the declaration section, it allows you to change the value of a val object almost anywhere in the program. Anywhere a space is allowed inside an HLA program, you can insert a statement of the form:

 ? ValIdentifier := constant_expression ; 

This means that you could write a short program like the one appearing in Listing 4-4.

Listing 4-4: Demonstration of VAL Redefinition Using "?" Operator.

start example
 program VALdemo; #include( "stdlib.hhf" ) val      NotSoConstant := 0; begin VALdemo;      mov( NotSoConstant, eax );      stdout.put( "EAX = ", (type uns32 eax ), nl );      ?NotSoConstant := 10;      mov( NotSoConstant, eax );      stdout.put( "EAX = ", (type uns32 eax ), nl );      ?NotSoConstant := 20;      mov( NotSoConstant, eax );      stdout.put( "EAX = ", (type uns32 eax ), nl );      ?NotSoConstant := 30;      mov( NotSoConstant, eax );      stdout.put( "EAX = ", (type uns32 eax ), nl ); end VALdemo; 
end example

You probably won't have much use for val objects at this time. When this text discusses HLA's macros and compile time language, you'll see how useful val objects can be to you.

[4]This is not a complete list. HLA also supports 64-bit and 128-bit data types. We'll discuss those later.

[5]Apologies are due to those of you who do not know C/C++/Java or a language that shares these string and constant definitions.

[6]Note to C/C++ and Java users. HLA's constant expressions use complete boolean evaluation rather than short-circuit boolean evaluation. Hence, HLA constant expressions do not behave identically to C/C++/Java expressions.

[7]Technically, if HLA had an optimizer it could replace these two instructions with a single "mov( 11, al );" instruction. HLA 1.x, however, does not do this.

[8]In this context, current means the value last assigned to a val object looking backward in the source code.




The Art of Assembly Language
The Art of Assembly Language
ISBN: 1593272073
EAN: 2147483647
Year: 2005
Pages: 246
Authors: Randall Hyde

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net