Prg2.s performs a loop to add up the numbers from 1 to 5 and returns the result as its exit code. Register 0 (R0) holds the sum, and R1 holds the value to add into the sum. The mov instructions on lines 4 and 5 initialize them to 0 and 1 respectively.
Skipping lines 7-9 for the moment, let's look at lines 11-12. These are add instructions; they add their two rightmost operands and put the result into the left operand, as the comments indicate. Like mov, they're limited in their operands. The target, and the first of the two right hand operands, must be registers. The final operand may be either a register or a constant, or several other possibilities we'll look at later.
The most interesting new feature of Prg2.s, however, is the loop. In order to add the values 1 through 5 into R0, the program must repeat lines 11-12 over and over, adding increasingly higher values of R1 into R0, until R1 exceeds 5.
Assembly language includes no "while", "for" or other loop keywords. It offers only branch instructions, which jump from one point in the code to another. For instance, the branch instruction (opcode b) on line 14 makes the program jump back to line 8 (labelled as loopTop). It thus repeats lines 8-12 over and over.
But, those lines should only repeat as long as R1 is less than 6, since we're adding just the values 1 through 5. Lines 8 and 9 take care of this.
Line 8 is a compare instruction (opcode cmp). It compares two operand values, in this case R1 and 6. That's all it does; any action based on the result of the comparison requires a subsequent instruction. The CPU "remembers" the results of a cmp until the next cmp (or until some other instructions are executed -- more on this later). Line 9's bge instruction is a conditional branch. It jumps the program to its target location allDone only if the result of the prior cmp was "greater than or equal", i.e. if R1's value is greater than or equal to 6.
Line 9 is thus deciding when to exit the loop. When R1 reaches 6, line 9 jumps past line 14, so the program can finish and exit. The final two lines set up and perform the swi instruction as in Prg1. (Note that the sum is already in R1, as the exit code.)
To be sure that's clear, let's follow the first several loop iterations. We start with R0 and R1 at their initial values of 0 and 1. The cmp/bge instruction pair does not jump, and we proceed to the two add instructions, adding R1 into R0, and incrementing R1. Then we branch back to loopTop to do another loop iteration. Again, the cmp/bge pair does not jump, and the subsequent add instructions increase R0 to 3, and R1 to 3. Loop iterations continue like this until R1 reaches 6, at which point R0 will be 15. Then the cmp/bge pair does jump to allDone, and the program exits with code 15.
I'll assemble and run the program here, so we can see the final exit code of 15, confirming that it works.
There are other forms of conditional branch than bge, testing for things other than greater-than-or-equal. Look these up in the course resources and name the opcodes for the various different relational branch tests.
Answer 1
Lines 8, 9, and 14 are the way you do a while-loop in assembly. You start with a test to see if the loop should end, and you branch past the end of the loop if that test is true. After that, you put the loop body, with an unconditional branch at the end to go back up and repeat the test. In the same way, any higher-language control structure, e.g. if-else, do-while, for, can be imitated with the right pattern of cmp, b, and bxx instructions, where "xx" is a test like ge.
bxx instruction can make is to jump around code, tests must often be negated. Instead of "If test X is true, do this code", we write "If test X is false, skip this code". The "true" case is often the "don't branch" option. This takes some getting used to for those accustomed to higher level languages.
Revise Prg2.s so that the loop decrements R1 from 5 down to 1. You will need to change 4 instructions, and you'll need to research and use a new opcode sub, which does subtraction instead of addition.
Answer 2
mov R1, #5 @ R1 = 5 loopTop: cmp R1, #0 @ while (R1 >= 0) ble allDone add R0, R0, R1 @ R0 = R0 + R1 sub R1, R1, #1 @ R1 = R1 - 1 b loopTop @ back to while header allDone: mov R7, #1 swi 0
Write the assembly that would perform an if-statement like this:
if (R0 > R1) R0 = R1
Answer 3
cmp R0, R1 ble after mov R0, R1 after: .... Again, we do a negative test, to see if R0 <= R1, and branch around the mov R0, R1 instruction if so. If R0 > R1, we won't branch, and the mov gets performed.
The idea of jumping from point to point in an assembly program implies some means of tracking "where we are" -- what the current assembly instruction is. The current assembly instruction is at some location in memory, so a 32-bit value will tell the processor what instruction is being executed, and what is next. Every CPU has a register holding this location, generally termed the program counter or PC. In an ARM process, R15 is the PC. R15 cannot be used for mathematical operations; it always stores the current instruction location.
b instruction can use a numerical address, instead of a label, e.g.
b #160
to jump to memory address 160. Write a mov instruction that does exactly the same thing.
Answer 4
An ARM CPU "remembers" the outcome of a cmp instruction by storing information on the result of the comparison in a register we haven't discussed yet: the Program Status Register or PSR. We'll discuss it in more detail later, but briefly, the PSR stores a number of bits of information about the program, including the outcome of a cmp.
A cmp instruction actually subtracts its second operand from the first, but doesn't store the difference. The PSR, however, does devote several of its bits to record whether the result is zero, negative, and several other conditions we'll discuss in an upcoming segment. These one-bit flags in the PSR are called condition codes. So for instance:
cmp R1, #6
computes R1 - 6, but doesn't store the difference. But condition codes in the PSR remember whether the result was zero, or negative. And then:
bge allDone
checks the PSR condition codes to see if the result was either zero, or not negative. Either case would imply that R1 >= 6, and the bge branches if so.
The PSR only stores information about one instruction. A new cmp wipes out any results from prior cmps. The PSR can also store information about other instructions, e.g. an add or sub, but only when they are specially marked to do so. The PSR always stores information about a cmp.
Any instruction can check the PSR condition codes, and execute only if they satisfy some criteria. Later we will see cases where nonbranch instructions do this. But by far the most common case is for branch instructions to execute conditionally based on condition codes in the PSR, resulting usually from a prior cmp instruction.