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