1.9 Some Basic HLA Control Structures


1.9 Some Basic HLA Control Structures

The mov, add, and sub instructions, while valuable, aren't sufficient to let you write meaningful programs. You will need to complement these instructions with the ability to make decisions and create loops in your HLA programs before you can write anything other than a trivial program. HLA provides several high level control structures that are very similar to control structures found in high level languages. These include if..then..elseif..else..endif, while..endwhile, repeat..until, and so on. By learning these statements you will be armed and ready to write some real programs.

Before discussing these high level control structures, it's important to point out that these are not real 80x86 assembly language statements. HLA compiles these statements into a sequence of one or more real assembly language statements for you. Later in this text, you'll learn how HLA compiles the statements, and you'll learn how to write pure assembly language code that doesn't use them. However, there is a lot to learn before you get to that point, so we'll stick with these high level language statements for now.

Another important fact to mention is that HLA's high level control structures are not as high level as they first appear. The purpose behind HLA's high level control structures is to let you start writing assembly language programs as quickly as possible, not to let you avoid the use of assembly language altogether. You will soon discover that these statements have some severe restrictions associated with them, and you will quickly outgrow their capabilities. This is intentional. Once you reach a certain level of comfort with HLA's high level control structures and decide you need more power than they have to offer, it's time to move on and learn the real 80x86 instructions behind these statements.

The following sections assume that you're familiar with at least one high level language. They present the HLA control statements from that perspective without bothering to explain how you actually use these statements to accomplish something in a program. One prerequisite this text assumes is that you already know how to use these generic control statements in a high level language; you'll use them in HLA programs in an identical manner.

1.9.1 Boolean Expressions in HLA Statements

Several HLA statements require a boolean (true or false) expression to control their execution. Examples include the if, while, and repeat..until statements. The syntax for these boolean expressions represents the greatest limitation of the HLA high level control structures. This is one area where your familiarity with a high level language will work against you: You'll want to use the fancy expressions you use in a high level language, yet HLA only supports some basic forms.

HLA boolean expressions always take the following forms:[9]

 flag_specification !flag_specification register !register Boolean_variable !Boolean_variable mem_reg relop mem_reg_const register in LowConst..HiConst register not in LowConst..HiConst 

A flag_specification may be one of the symbols that are described in Table 1-2.

Table 1-2: Symbols for flag_specification

Symbol

Meaning

Explanation


@c

Carry

True if the carry is set (1), false if the carry is clear (0).

@nc

No carry

True if the carry is clear (0), false if the carry is set (1).

@z

Zero

True if the zero flag is set, false if it is clear.

@nz

No zero

True if the zero flag is clear, false if it is set.

@o

Overflow

True if the overflow flag is set, false if it is clear.

@no

No overflow

True if the overflow flag is clear, false if it is set.

@s

Sign

True if the sign flag is set, false if it is clear.

@ns

No

sign True if the sign flag is clear, false if it is set.

The use of the flag values in a boolean expression is somewhat advanced. You will begin to see how to use these boolean expression operands in the next chapter.

A register operand can be any of the 8-bit, 16-bit, or 32-bit general purpose registers. The expression evaluates false if the register contains a zero; it evaluates true if the register contains a non-zero value.

If you specify a boolean variable as the expression, the program tests it for zero (false) or non-zero (true). Because HLA uses the values zero and one to represent false and true, respectively, the test works in an intuitive fashion. Note that HLA requires such variables be of type boolean. HLA rejects other data types. If you want to test some other type against zero/not zero, then use the general boolean expression discussed next.

The most general form of an HLA boolean expression has two operands and a relational operator. Table 1-3 lists the legal combinations.

Table 1-3: Legal Boolean Expressions

Left Operand

Relational Operator

Right Operand


Memory variable

= or ==

Memory variable,

<> or !=

or

<

Register

<=

Register,

>

or

>=

Constant

Note that both operands cannot be memory operands. In fact, if you think of the right operand as the source operand and the left operand as the destination operand, then the two operands must be the same that add and sub allow.

Also like the add and sub instructions, the two operands must be the same size. That is, they must both be byte operands, they must both be word operands, or they must both be double word operands. If the right operand is a constant, its value must be in the range that is compatible with the left operand.

There is one other issue: If the left operand is a register and the right operand is a positive constant or another register, HLA uses an unsigned comparison. The next chapter will discuss the ramifications of this; for the time being, do not compare negative values in a register against a constant or another register. You may not get an intuitive result.

The in and not in operators let you test a register to see if it is within a specified range. For example, the expression "EAX in 2000..2099" evaluates true if the value in the EAX register is between 2000 and 2099 (inclusive). The not in (two words) operator checks to see if the value in a register is outside the specified range. For example, "AL not in ‘a’..‘z’" evaluates true if the character in the AL register is not a lower case alphabetic character.

Here are some examples of legal boolean expressions in HLA:

 @c Bool_var al ESI EAX < EBX EBX > 5 i32 < -2 i8 > 128 al < i8 eax in 1..100 ch not in 'a'..'z' 

1.9.2 The HLA IF..THEN..ELSEIF..ELSE..ENDIF Statement

The HLA if statement uses the syntax shown in Figure 1-10.

click to expand
Figure 1-10: HLA IF Statement Syntax.

The expressions appearing in an if statement must take one of the forms from the previous section. If the boolean expression is true, the code after the then executes, otherwise control transfers to the next elseif or else clause in the statement.

Because the elseif and else clauses are optional, an if statement could take the form of a single if..then clause, followed by a sequence of statements and a closing endif clause. The following is such a statement:

 if( eax = 0 ) then       stdout.put( "error: NULL value", nl ); endif; 

If, during program execution, the expression evaluates true, then the code between the then and the endif executes. If the expression evaluates false, then the program skips over the code between the then and the endif.

Another common form of the if statement has a single else clause. The following is an example of an if statement with an optional else clause:

 if( eax = 0 ) then       stdout.put( "error: NULL pointer encountered", nl ); else       stdout.put( "Pointer is valid", nl ); endif; 

If the expression evaluates true, the code between the then and the else executes; otherwise the code between the else and the endif clauses executes.

You can create sophisticated decision-making logic by incorporating the elseif clause into an if statement. For example, if the CH register contains a character value, you can select from a menu of items using code like the following:

 if( ch = 'a' ) then       stdout.put( "You selected the 'a' menu item", nl ); elseif( ch = 'b' ) then       stdout.put( "You selected the 'b' menu item", nl ); elseif( ch = 'c' ) then       stdout.put( "You selected the 'c' menu item", nl ); else       stdout.put( "Error: illegal menu item selection", nl ); endif; 

Although this simple example doesn't demonstrate it, HLA does not require an else clause at the end of a sequence of elseif clauses. However, when making multiway decisions, it's always a good idea to provide an else clause just in case an error arises. Even if you think it's impossible for the else clause to execute, just keep in mind that future modifications to the code could void this assertion, so it's a good idea to have error reporting statements in your code.

1.9.3 Conjunction, Disjunction, and Negation in Boolean Expressions

Some obvious omissions in the list of operators in the previous sections are the conjunction (logical AND), disjunction (logical OR), and negation (logical NOT) operators. This section describes their use in boolean expressions (the discussion had to wait until after describing the if statement in order to present realistic examples).

HLA uses the "&&" operator to denote logical AND in a run-time boolean expression. This is a dyadic (two-operand) operator and the two operands must be legal run-time boolean expressions. This operator evaluates true if both operands evaluate to true. Example:

 if( eax > 0 && ch = 'a' ) then       mov( eax, ebx );       mov( ' ', ch ); endif; 

The two mov statements above execute only if EAX is greater than zero and CH is equal to the character ‘a’. If either of these conditions is false, then program execution skips over these mov instructions.

Note that the expressions on either side of the "&&" operator may be any legal boolean expression; these expressions don't have to be comparisons using the relational operators. For example, the following are all legal expressions:

 @z && al in 5..10 al in 'a'..'z' && ebx boolVar && !eax 

HLA uses short-circuit evaluation when compiling the "&&" operator. If the leftmost operand evaluates false, then the code that HLA generates does not bother evaluating the second operand (because the whole expression must be false at that point). Therefore, in the last expression above, the code will not check EAX against zero if boolVar contains false.

Note that an expression like "eax < 10 && ebx <> eax" is itself a legal boolean expression and, therefore, may appear as the left or right operand of the "&&" operator. Therefore, expressions like the following are perfectly legal:

 eax < 0 && ebx <> eax && !ecx 

The "&&" operator is left associative, so the code that HLA generates evaluates the expression above in a left-to-right fashion. If EAX is less than zero, the CPU will not test either of the remaining expressions. Likewise, if EAX is not less than zero but EBX is equal to EAX, this code will not evaluate the third expression because the whole expression is false regardless of ECX's value.

HLA uses the "||" operator to denote disjunction (logical OR) in a run-time boolean expression. Like the "&&" operator, this operator expects two legal runtime boolean expressions as operands. This operator evaluates true if either (or both) operands evaluate true. Like the "&&" operator, the disjunction operator uses short-circuit evaluation. If the left operand evaluates true, then the code that HLA generates doesn't bother to test the value of the second operand. Instead, the code will transfer to the location that handles the situation when the boolean expression evaluates true. Examples of legal expressions using the "||" operator:

 @z || al = 10 al in 'a'..'z' || ebx !boolVar || eax 

As for the "&&" operator, the disjunction operator is left associative so multiple instances of the "||" operator may appear within the same expression. Should this be the case, the code that HLA generates will evaluate the expressions from left to right, e.g.,

 eax < 0 || ebx <> eax || !ecx 

The code above executes if either EAX is less than zero, EBX does not equal EAX, or ECX is zero. Note that if the first comparison is true, the code doesn't bother testing the other conditions. Likewise, if the first comparison is false and the second is true, the code doesn't bother checking to see if ECX is zero. The check for ECX equal to zero only occurs if the first two comparisons are false.

If both the conjunction and disjunction operators appear in the same expression then the "&&" operator takes precedence over the "||" operator. Consider the following expression:

 eax < 0 || ebx <> eax && !ecx 

The machine code HLA generates evaluates this as

 eax < 0 || (ebx <> eax && !ecx) 

If EAX is less than zero, then the code HLA generates does not bother to check the remainder of the expression, the entire expression evaluates true. However, if EAX is not less than zero, then both of the following conditions must evaluate true in order for the overall expression to evaluate true.

HLA allows you to use parentheses to surround sub expressions involving "&&" and "||" if you need to adjust the precedence of the operators. Consider the following expression:

 (eax < 0 || ebx <> eax) && !ecx 

For this expression to evaluate true, ECX must contain zero and either EAX must be less than zero or EBX must not equal EAX. Contrast this to the result the expression produces without the parentheses.

HLA uses the "!" operator to denote logical negation. However, the "!" operator may only prefix a register or boolean variable; you may not use it as part of a larger expression (e.g., "!eax < 0"). To achieve the logical negative of an existing boolean expression you must surround that expression with parentheses and prefix the parentheses with the "!" operator, e.g.,

 !( eax < 0 ) 

This expression evaluates true if EAX is not less than zero.

The logical not operator is primarily useful for surrounding complex expressions involving the conjunction and disjunction operators. While it is occasionally useful for short expressions like the one above, it's usually easier (and more readable) to simply state the logic directly rather than convolute it with the logical not operator.

Note that HLA also provides the "|" and "&" operators, but these are distinct from "||" and "&&" and have completely different meanings. See the HLA reference manual for more details on these (compile time) operators.

1.9.4 The WHILE..ENDWHILE Statement

The while statement uses the basic syntax shown in Figure 1-11.

click to expand
Figure 1-11: HLA WHILE Statement Syntax.

This statement evaluates the boolean expression. If it is false, control immediately transfers to the first statement following the endwhile clause. If the value of the expression is true, then the CPU executes the body of the loop. After the loop body executes, control transfers back to the top of the loop where the while statement retests the loop control expression. This process repeats until the expression evaluates false.

Note that the while loop, like its high level language counterpart, tests for loop termination at the top of the loop. Therefore, it is quite possible that the statements in the body of the loop will not execute (if the expression is false when the code first executes the while statement). Also note that the body of the while loop must, at some point, modify the value of the boolean expression or an infinite loop will result.

Example of an HLA while loop:

 mov( 0, i ); while( i < 10 ) do       stdout.put( "i=", i, nl );       add( 1, i ); endwhile; 

1.9.5 The FOR..ENDFOR Statement

The HLA for loop takes the following general form:

 for( Initial_Stmt; Termination_Expression; Post_Body_Statement ) do       << Loop Body >> endfor; 

This is equivalent to the following while statement:

 Initial_Stmt; while( Termination_expression ) do       << loop_body >>       Post_Body_Statement; endwhile; 

Initial_Stmt can be any single HLA/80x86 instruction. Generally this statement initializes a register or memory location (the loop counter) with zero or some other initial value. Termination_expression is an HLA boolean expression (same format that while allows). This expression determines whether the loop body executes. The Post_Body_Statement executes at the bottom of the loop (as shown in the while example above). This is a single HLA statement. Usually it is an instruction like add that modifies the value of the loop control variable.

The following gives a complete example:

 for( mov( 0, i ); i < 10; add(1, i )) do       stdout.put( "i=", i, nl ); endfor; // The above, rewritten as a while loop, becomes: mov( 0, i ); while( i < 10 ) do       stdout.put( "i=", i, nl );       add( 1, i ); endwhile; 

1.9.6 The REPEAT..UNTIL Statement

The HLA repeat..until statement uses the syntax shown in Figure 1-12. C/C++/C# and Java users should note that the repeat..until statement is very similar to the do..while statement.

click to expand
Figure 1-12: HLA REPEAT..UNTIL Statement Syntax.

The HLA repeat..until statement tests for loop termination at the bottom of the loop. Therefore, the statements in the loop body always execute at least once. Upon encountering the until clause, the program will evaluate the expression and repeat the loop if the expression is false[10] (that is, it repeats while false). If the expression evaluates true, the control transfers to the first statement following the until clause.

The following simple example demonstrates the repeat..until statement:

 mov( 10, ecx ); repeat       stdout.put( "ecx = ", ecx, nl );       sub( 1, ecx ); until( ecx = 0 ); 

If the loop body will always execute at least once, then it is usually more efficient to use a repeat..until loop rather than a while loop.

1.9.7 The BREAK and BREAKIF Statements

The break and breakif statements provide the ability to prematurely exit from a loop. Figure 1-13 provides the syntax for these two statements.

click to expand
Figure 1-13: HLA BREAK and BREAKIF Syntax.

The break statement exits the loop that immediately contains the break; the breakif statement evaluates the boolean expression and exits the containing loop if the expression evaluates true.

Note that the break and breakif statements do not allow you to break out of more than one nested loop. HLA does provide statements that do this, the begin..end block and the exit/exitif statements. Please consult the HLA Reference Manual for more details. HLA also provides the continue/continueif pair that let you repeat a loop body. Again, see the HLA reference manual for more details.

1.9.8 The FOREVER..ENDFOR Statement

Figure 1-14 shows the syntax for the forever statement.

click to expand
Figure 1-14: HLA FOREVER Loop Syntax.

This statement creates an infinite loop. You may also use the break and breakif statements along with forever..endfor to create a loop that tests for loop termination in the middle of the loop. Indeed, this is probably the most common use of this loop as the following example demonstrates:

 forever       stdout.put( "Enter an integer less than 10: ");       stdin.get( i );       breakif( i < 10 );       stdout.put( "The value needs to be less than 10!", nl ); endfor; 

1.9.9 The TRY..EXCEPTION..ENDTRY Statement

The HLA try..exception..endtry statement provides very powerful exception handling capabilities. The syntax for this statement appears in Figure 1-15.

click to expand
Figure 1-15: HLA TRY..EXCEPTION..ENDTRY Statement Syntax.

The try..endtry statement protects a block of statements during execution. If the statements between the try clause and the first exception clause (the protected block), execute without incident, control transfers to the first statement after the endtry immediately after executing the last statement in the protected block. If an error (exception) occurs, then the program interrupts control at the point of the exception (that is, the program raises an exception). Each exception has an unsigned integer constant associated with it, known as the exception ID. The excepts.hhf header file in the HLA Standard Library predefines several exception IDs, although you may create new ones for your own purposes. When an exception occurs, the system compares the exception ID against the values appearing in each of the exception clauses following the protected code. If the current exception ID matches one of the exception values, control continues with the block of statements immediately following that exception. After the exception handling code completes execution, control transfers to the first statement following the endtry.

If an exception occurs and there is no active try..endtry statement, or the active try..endtry statements do not handle the specific exception, the program will abort with an error message.

The following code fragment demonstrates how to use the try..endtry statement to protect the program from bad user input:

 repeat       mov( false, GoodInteger ); // Note: GoodInteger must be a boolean var.       try       stdout.put( "Enter an integer: " );       stdin.get( i );       mov( true, GoodInteger );    exception( ex.ConversionError );       stdout.put( "Illegal numeric value, please re-enter", nl );    exception( ex.ValueOutOfRange );       stdout.put( "Value is out of range, please re-enter", nl );    endtry; until( GoodInteger ); 

The repeat..until loop repeats this code as long as there is an error during input. Should an exception occur because of bad input, control transfers to the exception clauses to see if a conversion error (e.g., illegal characters in the number) or a numeric overflow occurs. If either of these exceptions occur, then they print the appropriate message and control falls out of the try..endtry statement and the repeat..until loop repeats because the code will not have set GoodInteger to true. If a different exception occurs (one that is not handled in this code), then the program aborts with the specified error message.[11]

Table 1-4 on the following page lists the exceptions provided in the excepts.hhf header file as this was being written. Please see the excepts.hhf header file provided with HLA for the most current list of exceptions.

Table 1-4: Exceptions Provided in Excepts.hhf

Exception

Description


ex.StringOverflow

Attempt to store a string that is too large into a string variable.

ex.StringIndexError

Attempt to access a character that is not present in a string.

ex.ValueOutOfRange

Value is too large for the current operation.

ex.IllegalChar

Operation encountered a character code whose ASCII code is not in the range 0..127.

ex.ConversionError

String-to-numeric conversion operation contains illegal (non-numeric) characters.

ex.BadFileHandle

Program attempted a file access using an invalid file handle value.

ex.FileOpenFailure

Operating system could not open file (file not found).

ex.FileCloseError

Operating system could not close file.

ex.FileWriteError

Error writing data to a file.

ex.FileReadError

Error reading data from a file.

ex.DiskFullError

Attempted to write data to a full disk.

ex.EndOfFile

Program attempted to read beyond the end of file.

ex.MemoryAllocationFailure

Insufficient system memory for allocation request.

ex.AttemptToDerefNULL

Program attempted to access data indirectly using a NULL pointer.

ex.CannotFreeMemory

Memory free operation failure.

ex.WidthTooBig

Format width for numeric to string conversion was too large.

ex.TooManyCmdLnParms

Command line contains too many arguments for processing by arg.c and arg.v.

ex.ArrayShapeViolation

Attempted operation on two arrays whose dimensions don't match.

ex.ArrayBounds

Attempt to access an element of an array, but the index was out of bounds.

ex.InvalidDate

Attempted date operation with an illegal date.

ex.InvalidDateFormat

Conversion from string to date contains illegal characters.

ex.TimeOverflow

Overflow during time arithmetic.

ex.AssertionFailed

ASSERT statement encountered a failed assertion.

ex.ExecutedAbstract

Attempt to execute an abstract class method.

ex.AccessViolation

Attempt to access an illegal memory location.

ex.Breakpoint

Program executed a breakpoint instruction (INT 3).

ex.SingleStep

Program is operating with the trace flag set.

ex.PrivInstr

Program attempted to execute a kernel-only instruction.

ex.IllegalInstr

Program attempted to execute an illegal machine instruction.

ex.BoundInstr

Bound instruction execution with "out of bounds" value.

ex.IntoInstr

Into instruction execution with the overflow flag set.

ex.DivideError

Program attempted division by zero or other divide error.

ex.fDenormal

Floating point exception (see the chapter on arithmetic).

ex.fDivByZero

Floating point exception (see the chapter on arithmetic).

ex.fInexactResult

Floating point exception (see the chapter on arithmetic).

ex.fInvalidOperation

Floating point exception (see the chapter on arithmetic).

ex.fOverflow

Floating point exception (see the chapter on arithmetic).

ex.fStackCheck

Floating point exception (see the chapter on arithmetic).

ex.fUnderflow

Floating point exception (see the chapter on arithmetic).

ex.InvalidHandle

OS reported an invalid handle for some operation.

ex.StackOverflow

OS reported a stack overflow.

Most of these exceptions occur in situations that are well beyond the scope of this chapter. Their appearance here is strictly for completeness. See the HLA Reference Manual, the HLA Standard Library documentation, and the HLA Standard Library source code for more details concerning these exceptions. The ex.ConversionError, ex.ValueOutOfRange, and ex.StringOverflow exceptions are the ones you'll most commonly use.

We'll return to the discussion of the try..endtry statement a little bit later in this chapter. First, however, we need to cover a little more material.

[9]There are a few additional forms that we'll cover in later sctions and later chapters.

[10]Note that this condition is the opposite of the do..while loop found in C/C++ and Java.

[11]An experienced programmer may wonder why this code uses a boolean variable rather than a breakif statement to exit the repeat..until loop. There are some technical reasons for this that you will learn about later in this text.




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