Section 12.1 Repetition
The algorithms we choose when programming interact closely with the data storage structure. As you probably know, a string of characters is stored in an array. Each element of the array is of type char
, and in C the end of the data is signified with a sentinel value, the NUL
character (see Table 2.13.1).
Array processing is usually a repetitive task. The processing of a character string is a good example of repetition. Consider the C program in Listing 12.1.1.
The while
statement:
while (*aString != '\0') { ... }
controls the execution of the statements within the {...}
block:
It evaluates the Boolean expression
*aString != '\0'
.If the Boolean expression evaluates to false, program flow jumps to the statement immediately following the
{...}
block.If the Boolean expression evaluates to true, program flow enters the
{...}
block and executes the statements there in sequence.At the end of the
{...}
block program flow jumps back up to the evaluation of the Boolean expression.
The pointer variable is incremented with the
aString++;
statement. Notice that this variable must be changed inside the {...}
block. Otherwise, the Boolean expression will always evaluate to true, giving an “infinite” loop.
It is important that you identify the variable the while
construct uses to control program flow—the Loop Control Variable (LCV). Make sure that the value of the LCV is changed within the {...}
block. There may be more than one LCV.
The way that the while
construct controls program flow can be seen in the flow chart in Figure 12.1.2.
This flow chart shows that we need the following assembly language tools to construct a while
loop:
Instruction(s) to evaluate Boolean expressions.
An instruction that conditionally transfers control (branches) to another location in the program. This is represented by the large diamond, which shows two possible paths.
An instruction that unconditionally transfers control to another location in the program. This is represented by the line that leads from “Execute body of
while
loop” back to the top.
We will explore instructions that provide these tools in the next three subsections.
Subsection 12.1.1 Comparison Instructions
Although most ARM instructions can optionally affect the condition flags, there are two instructions specfically intended to compare two values. They always set the condition flags, but do not change either of the compared values.
CMP
-
Does an arithmetic comparison of two values and sets condition flags according to the result.
CMP{<c>} <Rn>, #<const> % immediate CMP{<c>} <Rn>, <Rm> {,<shift>} % register CMP{<c>} <Rn>, <Rm>, <type> <Rs> % register-shifted register
<c>
is the condition code, Table 9.2.1.<Rn>
and<Rm>
and<Rn>
are registers to compare.<Rs>
contains the shift amount in the “register-shifted register” form.\(-257 \le const \le +256\text{,}\) or \(const = +256, +260, +264, \ldots, +65280\text{,}\) or \(const = -261, -265, \ldots, -65281\text{.}\) This odd sequence of values will be explained in Section 11.3.3
<shift>
and<type>
are explained in Section 9.2.3
The values in
<Rm>
,<Rn>
, and<Rs>
are unchanged. Only the condition codes in theCPSR
register are changed to show the result of a subtraction. In the “immediate” form,<const>
is subtracted from the value in<Rn>
. In the “register” and “register-shifted register” forms, the value in<Rm>
is subtracted from the value in<Rn>
. If a shift is specified, the value in<Rm>
is shifted by the specified amount before the subtraction is performed. TST
-
Does a logical comparison of two values and sets condition flags according to the result.
TST{<c>} <Rn>, #<const> % immediate TST{<c>} <Rn>, <Rm> {,<shift>} % register TST{<c>} <Rn>, <Rm>, <type> <Rs> % register-shifted register
<c>
is the condition code, Table 9.2.1.<Rn>
and<Rm>
and<Rn>
are registers to compare.<Rs>
contains the shift amount in the “register-shifted register” form.\(-257 \le const \le +256\text{,}\) or \(const = +256, +260, +264, \ldots, +65280\text{,}\) or \(const = -261, -265, \ldots, -65281\text{.}\) This odd sequence of values will be explained in Section 11.3.3
<shift>
and<type>
are explained in Section 9.2.3
The values in
<Rm>
,<Rn>
, and<Rs>
are unchanged. Only the condition codes in theCPSR
register are changed to show the result of a bitwise AND. In the “immediate” form, a bitwise AND is performed between<const>
and the value in<Rn>
. In the “register” and “register-shifted register” forms, the bitwise AND is performed between the value in<Rm>
and the value in<Rn>
. If a shift is specified, the value in<Rm>
is shifted by the specified amount before the AND is performed.
Subsection 12.1.2 while
Loop
We are now prepared to look at how a while
loop is constructed at the assembly language level. As usual, we begin with the assembly language generated by the gcc
compiler for the program in Listing 12.1.1, which is shown in Listing 12.1.3 with comments added.
We see another new instruction here, ldrb
, which you can probably guess operates like ldr
(Section 9.2) but loads a byte from memory instead of an entire word.
The compiler has created code that starts at the bottom of the while
loop instead of the top:
b .L2
This improves the efficiency of the algorithm, perhaps at the expense of readability.
My assembly language solution checks the loop control variable at the top of the loop, as shown in Listing 12.1.4.
Subsection 12.1.3 for
Loop
A while
seems natural for processing an array that is terminated with a sentinel value, but many programmers prefer a for
loop for processing a known number of array elements. A C version of a “Hello world” program using a for
loop is shown in Listing 12.1.5.
Comparing the assembly language for the for
loop in Listing 12.1.6 with the assembly language for the while
loop in Listing 12.1.5, we see that there is very little difference. The main difference is that we asked that the compiler use registers for local variables in the for
loop version.
Listing 12.1.7 shows my version of the for
loop.
The message here is that there is no difference between a for
loop and a while
loop at the machine code level. You should use the construct in your high level language that is more appropriate for your specific application. In general, use a while
loop for a sentinel-controlled loop, and a for
loop for a count-controlled loop.