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.while
loops, repeat..until
loops (do..while
in C/C++), and loop..endloop
loops.
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.
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.
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:
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.
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.