The Art of
ASSEMBLY LANGUAGE PROGRAMMING

Chapter Ten (Part 2)

Table of Content

Chapter Ten (Part 4)

CHAPTER TEN:
CONTROL STRUCTURES (Part 3)
10.6 - Loops
10.6.1 - While Loops
10.6.2 - Repeat..Until Loops
10.6.3 - LOOP..ENDLOOP Loops
10.6.4 - FOR Loops
10.7 - Register Usage and Loops
10.6 Loops

Loops represent the final basic control structure (sequences decisions and loops) which make up a typical program. Like so many other structures in assembly language you'll find yourself using loops in places you've never dreamed of using loops. Most HLLs have implied loop structures hidden away. For example consider the BASIC statement IF A$ = B$ THEN 100. This if statement compares two strings and jumps to statement 100 if they are equal. In assembly language you would need to write a loop to compare each character in A$ to the corresponding character in B$ and then jump to statement 100 if and only if all the characters matched. In BASIC there is no loop to be seen in the program. In assembly language this very simple if statement requires a loop. This is but a small example which shows how loops seem to pop up everywhere.

Program loops consist of three components: an optional initialization component a loop termination test and the body of the loop. The order with which these components are assembled can dramatically change the way the loop operates. Three permutations of these components appear over and over again. Because of their frequency these loop structures are given special names in HLLs: while loops repeat..until loops (do..while in C/C++) and loop..endloop loops.

10.6.1 While Loops

The most general loop is the while loop. It takes the following form:

	WHILE boolean expression DO statement;

There are two important points to note about the while loop. First the test for termination appears at the beginning of the loop. Second as a direct consequence of the position of the termination test the body of the loop may never execute. If the termination condition always exists the loop body will always be skipped over.

Consider the following Pascal while loop:

	I := 0;
WHILE (I<100) do I := I + 1;

I := 0; is the initialization code for this loop. I is a loop control variable because it controls the execution of the body of the loop. (I<100) is the loop termination condition. That is the loop will not terminate as long as I is less than 100. I:=I+1; is the loop body. This is the code that executes on each pass of the loop. You can convert this to 80x86 assembly language as follows:

                mov     I
0
WhileLp:        cmp     I
100
jge     WhileDone
inc     I
jmp     WhileLp

WhileDone:

Note that a Pascal while loop can be easily synthesized using an if and a goto statement. For example the Pascal while loop presented above can be replaced by:

        I := 0;
1:      IF (I<100) THEN BEGIN
I := I + 1;
GOTO 1;
END;

More generally any while loop can be built up from the following:

        optional initialization code
1:      IF not termination condition THEN BEGIN
loop body
GOTO 1;
END;

Therefore you can use the techniques from earlier in this chapter to convert if statements to assembly language. All you'll need is an additional jmp (goto) instruction.

10.6.2 Repeat..Until Loops

The repeat..until (do..while) loop tests for the termination condition at the end of the loop rather than at the beginning. In Pascal the repeat..until loop takes the following form:

        optional initialization code
REPEAT
loop body
UNTIL termination condition

This sequence executes the initialization code the loop body then tests some condition to see if the loop should be repeated. If the boolean expression evaluates to false the loop repeats; otherwise the loop terminates. The two things to note about the repeat..until loop is that the termination test appears at the end of the loop and as a direct consequence of this the loop body executes at least once.

Like the while loop the repeat..until loop can be synthesized with an if statement and a goto . You would use the following:

        initialization code
1:              loop body
IF NOT termination condition THEN GOTO 1

Based on the material presented in the previous sections you can easily synthesize repeat..until loops in assembly language.

10.6.3 LOOP..ENDLOOP Loops

If while loops test for termination at the beginning of the loop and repeat..until loops check for termination at the end of the loop the only place left to test for termination is in the middle of the loop. Although Pascal and C/C++[4] don't directly support such a loop the loop..endloop structure can be found in HLL languages like Ada. The loop..endloop loop takes the following form:

        LOOP
loop body
ENDLOOP;

Note that there is no explicit termination condition. Unless otherwise provided for the loop..endloop construct simply forms an infinite loop. Loop termination is handled by an if and goto statement[5]. Consider the following (pseudo) Pascal code which employs a loop..endloop construct:

        LOOP
READ(ch)
IF ch = '.' THEN BREAK;
WRITE(ch);
ENDLOOP;

In real Pascal you'd use the following code to accomplish this:

1:
READ(ch);
IF ch = '.' THEN GOTO 2; (* Turbo Pascal supports BREAK! *)
WRITE(ch);
GOTO 1
2:

In assembly language you'd end up with something like:

LOOP1:  getc
cmp     al
'.'
je      EndLoop
putc
jmp     LOOP1
EndLoop:
10.6.4 FOR Loops

The for loop is a special form of the while loop which repeats the loop body a specific number of times. In Pascal the for loop looks something like the following:

	FOR var := initial TO final DO stmt
or
FOR var := initial DOWNTO final DO stmt

Traditionally the for loop in Pascal has been used to process arrays and other objects accessed in sequential numeric order. These loops can be converted directly into assembly language as follows:

In Pascal:

FOR var := start TO stop DO stmt;

In Assembly:

                mov     var
start
FL:             mov     ax
var
cmp     ax
stop
jg      EndFor

; code corresponding to stmt goes here.

inc     var
jmp     FL
EndFor:

Fortunately most for loops repeat some statement(s) a fixed number of times. For example

        FOR I := 0 to 7 do write(ch);

In situations like this it's better to use the 80x86 loop instruction (or corresponding dec cx/jne sequence) rather than simulate a for loop:

                mov     cx
7
LP:             mov     al
ch
call    putc
loop    LP

Keep in mind that the loop instruction normally appears at the end of a loop whereas the for loop tests for termination at the beginning of the loop. Therefore you should take precautions to prevent a runaway loop in the event cx is zero (which would cause the loop instruction to repeat the loop 65 536 times) or the stop value is less than the start value. In the case of

        FOR var := start TO stop DO stmt;

assuming you don't use the value of var within the loop you'd probably want to use the assembly code:

                mov     cx
stop
sub     cx
start
jl      SkipFor
inc     cx
LP:             stmt
loop    LP
SkipFor:

Remember the sub and cmp instructions set the flags in an identical fashion. Therefore this loop will be skipped if stop is less than start. It will be repeated (stop-start)+1 times otherwise. If you need to reference the value of var within the loop you could use the following code:

                mov     ax
start
mov     var
ax
mov     cx
stop
sub     cx
ax
jl      SkipFor
inc     cx
LP:             stmt
inc     var
loop    LP
SkipFor:

The downto version appears in the exercises.

10.7 Register Usage and Loops

Given that the 80x86 accesses registers much faster than memory locations registers are the ideal spot to place loop control variables (especially for small loops). This point is amplified since the loop instruction requires the use of the cx register. However there are some problems associated with using registers within a loop. The primary problem with using registers as loop control variables is that registers are a limited resource. In particular there is only one cx register. Therefore the following will not work properly:

                mov     cx
8
Loop1:          mov     cx
4
Loop2:          stmts
loop    Loop2
stmts
loop    Loop1

The intent here of course was to create a set of nested loops that is one loop inside another. The inner loop (Loop2) should repeat four times for each of the eight executions of the outer loop (Loop1). Unfortunately both loops use the loop instruction. Therefore this will form an infinite loop since cx will be set to zero (which loop treats like 65 536) at the end of the first loop instruction. Since cx is always zero upon encountering the second loop instruction control will always transfer to the Loop1 label. The solution here is to save and restore the cx register or to use a different register in place of cx for the outer loop:

                mov     cx
8
Loop1:          push    cx
mov     cx
4
Loop2:          stmts
loop    Loop2
pop     cx
stmts
loop    Loop1

or:
mov     bx
8
Loop1:          mov     cx
4
Loop2:          stmts
loop    Loop2
stmts
dec     bx
jnz     Loop1

Register corruption is one of the primary sources of bugs in loops in assembly language programs always keep an eye out for this problem.


[4] Technically C/C++ does support such a loop. "for(;;)" along with break provides this capability.

[5] Many high level languages use statements like NEXT BREAK CONTINUE EXIT and CYCLE rather than GOTO; but they're all forms of the GOTO statement.

Chapter Ten (Part 2)

Table of Content

Chapter Ten (Part 4)

Chapter Ten: Control Structures (Part 3)
27 SEP 1996