Assembler Control
Home Up Assembler Control ONEPAGE.ASM STARS.ASM NOECHO.ASM TAIL.ASM FIRST.ASM ADDTWO.ASM SERIES.ASM GETSHOW.ASM Trace: PUSH/POP Trace: CALL Trace: INT Trace: small prog
Updated:
2003-05-04 01:19

Coding Assembler Control Flow

Most programs contain some form of conditional logic, e.g. IF statements that affect the usual linear flow of control in the program.  Programming conditional logic means turning the pseudo-code logic into statements written in mnemonic form for the Assembler.  The CPU has no IF, WHILE, FOR or other structured statements; we must use the control flow instructions available to us to implement structured code.

Conditional statements contain a test expression that returns a TRUE or FALSE, e.g. IF (count == 0), WHILE (sum >= MAX), etc.  The simplest test expression is a test that compares two quantities, e.g. A<B.  Let's name these two quantities with variable names A and B and work out the translation of two-variable test expressions to Assembler instructions.  More complex expressions can be built using the techniques developed here.

IF and WHILE Statements

Note that the test in an IF statement and the test in a WHILE statement have exactly the same structure; both expressions return TRUE or FALSE and alter the flow of control around an enclosed body of statements:

if ( a == b ) then
    a = a + b    // TRUE body
endif
while ( a == b ) do
    a = a + b    // TRUE body
endwhile

In both the above code fragments, the control flow enters the TRUE body only if the test expression comparing A and B for equality is true. If we know how to code one test in Assembler instructions, we know how to code the other.  We need only focus on the code for the test expression itself.

Basic IF Structure

The basic Assembler instructions that alter the flow of control are JUMP statements. The jumps can either be unconditional, e.g. JMP, or they can be conditional on the state of the status flags, e.g. JGT, JA, JLE, JBE.   We must use these instructions to implement our pseudocode IF and WHILE control flow statements.  The pseudocode structure for a simple IF statement (no ELSE clause) is as follows:

	// Simple IF statement structure pseudocode:
	Evaluate the test expression using A and B
	CONDITIONALLY JUMP to ENDIF (the end of the IF statement)
	   Perform the many
	   statements that are
	   the body of the IF here...
ENDIF:	Continue with rest of program

Note the placement of the Conditional JUMP instruction, above.  The function of the JUMP is to get around the body of the IF statement.  We must code a test expression and JUMP instruction that let the flow of control go into the body of the IF only if the test expression is TRUE.  If the test expression is FALSE, the JUMP should take control around the body of the IF statement. Pay careful attention to this detail: The jump must take place if the test expression is FALSE, not if it is TRUE.

Mathematics and Status Flags

Each type of JUMP instruction checks the settings of the various status flags: Negative, Zero, Positive, Carry, etc.  Only mathematics, e.g. ADD, SUBTRACT, COMPARE, sets the status flags; therefore, to use conditional JUMP statements to alter the flow of control in our program, we must first perform some mathematics using our two variables A and B.  The mathematics will set the CPU status flags based on the relationship between A and B, and we can then JUMP according to the setting of the flags.

For example, consider the following code fragment:

if ( a == b ) then
    a = a + b    // TRUE body
endif
a = a + 1

We have written an expression that causes control flow to enter the TRUE body only if A is equal to B.  Using the IF statement structure given above, we need to pick some mathematics to set the flag lights and follow the math with a a JUMP instruction that branches around the IF body if the test expression is FALSE.

If we want to test for equality, A == B, we must arrange to JUMP around the IF body on the inverse condition, that is, when A != B. So, we always pick a conditional JUMP that is the opposite of the test condition we are using.  If the test is "equality", the JUMP is "jump if not equal".  If the test is "less than", the JUMP is "jump if greater than or equal to", etc.

Using the above IF structure, here is a translation of the above pseudocode into Assembler instructions:

	MOV	DX,A	; one operand must be in a register
	CMP	DX,B	; do math on A and B to set the status flags
	JNE	ENDIF	; A is not equal to B - jump around
	MOV	CX,B	;   body of IF... (one op in register)
	ADD	A,CX	;   body of IF... (a = a + b)
ENDIF:	INC	A	; end of IF statement - continue on
If A and B are equal, comparing them will cause the Zero flag to come on.  The JNE instruction operates only if the Zero flag is off, so no jump will take place and the body of the IF statement will execute. If A and B are not equal, the Zero flag will not come on, and the JNE will then operate to skip over the body of the IF statement.  We always invert the jump condition because it must must jump around the body of the IF statement.

All Assembler flow control statements (IF, WHILE) can be programmed using the above structure:

  1. Do some mathematics using A and B to set the status flags.
  2. Use a conditional JUMP instruction to jump around the body.
    (Remember to jump on the inverse of the test.)

How do we choose what mathematics to perform, and which JUMP instruction to use?  We will examine that later in this document.

IF with an ELSE clause

IF statements with ELSE clauses are only slightly more complex than simple IF statements.  Here is a pseudocode program fragment using an IF/ELSE statement:

1. if ( a == b ) then
2.     a = a + b
3. else
4.     b = b - a
5. endif
6. b++

Here is a translation of the above pseudocode fragment into Assembler, with the statement numbers inserted as comments:

	MOV	DX,A	; 1: one operand must be in a register
	CMP	DX,B	;    do math on A and B to set flags
	JNE	ELSE
	MOV	AX,B	; 2: one operand must be in a register
	ADD	A,AX	;    a = a + b
	JMP	ENDIF
ELSE:	MOV	CX,A	; 4: one operand must be in a register
	SUB	B,CX	;    b = b - a
ENDIF:	INC	B	; 6: ENDIF; after IF
Compare the above Assembler code with that used in the simple IF statement.  Note the identical placement of the test expression mathematics and JUMP instruction on line 1, above.  The function of the first JUMP is still to get around the TRUE body of the IF statement; however, instead of jumping to the end of the IF statement, it now jumps to the first statement in the ELSE clause.  (If the test expression of an IF/ELSE statement is FALSE, control passes over the TRUE body and into the FALSE body.)

A new "JMP ENDIF" instruction has been added to the end of the TRUE body of the IF statement (just before the ELSE label) to jump around the FALSE body that makes up the ELSE part of the IF.  If this jump were not there, the flow of control would run from the statements at the end of the TRUE body into the statements at the beginning of the FALSE body.

A WHILE Statement

The following pseudocode fragment is almost identical to the simple IF example used earlier; only the keywords IF have been changed to WHILE:

while ( a == b ) then
    a = a + b    // TRUE body
endwhile
a = a + 1

Again, we have written an expression that causes control flow to enter the TRUE body only if A is equal to B.  We need to pick some mathematics to set the flag lights and use a conditional JUMP instruction to jump around the WHILE body.  We use almost the same statement structure as for the simple IF statement.  Here is a translation of the above pseudocode into Asembler instructions:

TEST:	MOV	DX,A	; one operand must be in a register
	CMP	DX,B	; do math on A and B to set the status flags
	JNE	ENDWH	; A is not equal to B - jump around
	MOV	CX,B	;   body of WHILE... (one op in register)
	ADD	A,CX	;   body of WHILE... (a = a + b)
	JMP	TEST	; WHILE is a loop - jump up to test again
ENDWH:	INC	A	; end of WHILE statement - continue on
The analysis of the above Asssembler code is similar to the analysis of the code for the simple IF statement:  If A and B are equal, subtracting them will cause the Zero flag to come on.  The JNE instruction needs the Zero flag off, so it does not jump and we enter the WHILE body. If A and B are not equal, the Zero flag will not come on, and the JNE will jump around the body of the WHILE statement.

The key difference between the above code and the code for the simple IF statement is the addition of a "JMP TEST" instruction at the bottom of the body of the WHILE statement.  WHILE statements need to loop; IF statements do not.  The jump returns control to the top of the WHILE loop, which is to the test expression that begins the loop.

FOR Statements

FOR statements are simply a shorthand way of writing the equivalent WHILE statements.  To code a FOR statement, turn it into the equivalent WHILE statement and then translate that into assembler:

	// the original FOR statement
	for ( i=0; i < 10; i++ ) do
	    x = x + i
	endfor
	// translated into a WHILE statement
	i = 0
	while ( i < 10 ) do
	    x = x + i
	    i = i + 1
	endwhile

	; turned into Intel assembler
	MOV	I,0
TEST:	CMP	I,10	; do mathematics to set the status flags
	JGE	ENDWH	; if I is >= 10 - jump around
	MOV	AX,I	; one operand must be in a register
	ADD	X,AX	; x = x + i
	INC	I	; i = i + 1
	JMP	TEST	; WHILE is a loop - start over
ENDWH:	...		; end of WHILE statement - continue on

The Initialization clause of the FOR loop precedes the WHILE statement.  The body of the WHILE loop ends with the Increment clause of the FOR loop, just before jumping back to the top of the loop.  The WHILE loop itself has exactly the same structure as before:

  1. Do some mathematics using A and B to set the status flags.
  2. Use a conditional JUMP instruction to jump around the body.
    (Remember to jump on the inverse of the test.)

A common error when programming FOR loops is to mis-code the jump to the top of the loop.  Some people mistakenly code the jump to go to the first statement of the Initialization clause.  The correct jump is to the first statement of the Test part of the WHILE loop, not to the Initialization clause.

Choosing the JUMP

All of the above control structures (IF, IF/ELSE, WHILE, FOR) have the same structure for their test conditions:

  1. Do some mathematics using A and B to set the status flags.
  2. Use a conditional JUMP instruction to jump around the body.
    (Remember to jump on the inverse of the test.)

How do we choose what mathematics to perform using A and B, and which JUMP instruction to use?

Since we are testing the relationship between A and B - equal, less than, greater than, etc. - the operation that works best to set the CPU status flags is SUBTRACT.  Subtracting A from B or B from A sets the flags according to the relative values of A and B; ADDING would not have the same effect. 

Since we are often only performing the SUBTRACT to set the status flags - we don't actually want to use the value of the subtraction - most CPUs have a COMPARE (CMP) instruction that sets the flags as if the subtraction had been done, but it doesn't change any of the operands and doesn't save the results of the subtraction.

The only issue to resolve is which of the many JUMP instructions we need to use.

Greater vs. Above, Less vs. Below

Intel CPUs provide JUMP instructions for both unsigned binary mathematics and for signed, 2's complement mathematics.

When comparing 16-bit quantities, FFFFh is said to be above 0001h when considered as unsigned values. (FFFFh is a much bigger unsigned number.)  However, when considered as signed, 2's complement values, FFFFh is negative, so FFFFh is said to be less than 0001h. The Intel JUMP instructions reflect this distinction:

Opcodes Type of conditional JUMP
JA, JAE Above, Above or Equal; unsigned
JB, JBE Below, Below or Equal; unsigned
JGT, JGE Greater Than, Greater Than or Equal; signed
JLT, JLE Less Than, Less Than or Equal; signed
JE, JZ Equal, Zero
JNE, JNZ Not Equal, Not Zero
After comparing two quantities, select the JUMP instruction that is appropriate for whether the two quantities are signed (e.g. JGT) or unsigned (e.g. JA).

Examples

Given this pseudocode fragment, code it as both a signed and and unsigned test:

if ( a > b ) then ...
As a signed test (remember that we always invert the JUMP condition):
	MOV	CX,A
	CMP	CX,B
	JLE	ENDIF	; skip the body if A <= B (signed)
	...	...
As an unsigned test:
	MOV	CX,A
	CMP	CX,B
	JBE	ENDIF	; skip the body if A <= B (unsigned)
	...	...
As programmer, you are expected to choose the appropriate conditional JUMP.

Nested AND Logic

Here is a more complex example using two test expressions joined by AND:

if ( a != b && c >= d ) then
    ... body goes here ...
endif

The above test expression is not a simple one involving two quantities, so the methods we have been using so far will not work directly.  However, we can rewrite the above compound test expression into two nested IF statements, each of which uses a simple A/B-type expression that we know how to translate:

if ( a != b ) then
    if ( c >= d ) then
        ... IF body goes here ...
    endif
endif
Since the IF statements are nested, the body will not be executed unless both the outer AND inner IF conditions are both true.  We can now translate these two IF statements directly into LMC assembler:
	MOV	DX,A	; one operand must be in a register
	CMP	DX,B	; this is the outer IF test
	JE	ENDIF1	; skip the body if A == B
	MOV	DX,C	; one operand must be in a register
	CMP	DX,D	; this is the inner IF test
	JLT	ENDIF2	; skip the body if C < D
	... IF body goes here ...
ENDIF2:
ENDIF1:

Both ENDIF1 and ENDIF2 are actually labels for the same location; jumping to either label jumps to the same code at the end of the IF statement.  We might therefore simplify the code and use only a single ENDIF label for both jumps.

Nested OR Logic

Here is another complex example, this time using two expressions joined by OR:

if ( a != b || c >= d ) then
    ... IF body goes here ...
endif

The above two conditional test expressions are joined by OR, so we cannot implement a solution using two nested IF statements as we did when they were joined by AND.  With a bit of help from deMorgan's theorem in Boolean logic, we can invert the condition so that it becomes one containing AND instead of OR, and then use the inverted condition to branch around the loop body:

First, we must apply deMorgan's theorem to invert the condition to an opposite condition that uses AND instead of OR:

original condition:  a != b || c >= d
inverted condition:  !(a != b || c >= d)
deMorgan applied:    !(a != b) && !(c >= d)
simplified:          a == b && c < d
 We then use that new inverted condition to branch around the loop body:
	if ( a == b && c < d ) then
    	    goto around
	endif
	... IF body goes here ...
around:	... linear code resumes here ...
We now have a compound condition joined by AND, a problem we can solve with nested IF statements, as we did in the previous section.  The GOTO becomes a simple JUMP statement in Assembler:
	MOV	DX,A	; one operand must be in a register
	CMP	DX,B	; this is the outer IF test
	JNE	DOBODY	; go to the body if A != B
	MOV	DX,C	; one operand must be in a register
	CMP	DX,D	; this is the inner IF test
	JGE	DOBODY	; go to the body if C >= D
	JMP	AROUND	; skip the body; deMorgan inverted test
DOBODY:	... IF body goes here ...
AROUND:	... linear code resumes here ...
Unlike previous examples, where the JUMPs branched around the IF body, the above code uses a deMorgan inverted condition and the DOBODY labels lead to the IF body to implement the OR functionality.

Web Author: Ian! D. Allen idallen@idallen.ca      Updated: 2003-05-04 01:19

Internet Free Zone Level 1 logo Support free and non-commercial Internet.

Any Browser logo This site works best in Any Browser, a campaign for non-specific WWW.

Creative Commons License logo This work is licensed under a Creative Commons License.