Current Program State Register
The CPSR (Current Program State Register) is used to keep a note of some important characteristics of the result of an execution. For example if we wanted to implement a FOR loop in C++ we would invariably need a loop counter- i.for ( i=20; i=0; i--){
//some useful code
}
Here we can see that when i becomes equal to 0 the loop terminates, this is simple in C++ but in ARM assembly it is slightly more difficult- how do we know when i is zero?
This is where the CPSR is used, it keeps a note of the following elements implemented as status bits:
N Negative
Z Zero
C Carry
V Overflow
| N | Z | C | V |
|---|---|---|---|
| This is equal to the 32nd bit of the destination register. Remember that the binary values stored in an ARM register can be interpreted as 2s complement format, where a 0 in the MSB indicates +ve and 1 indicates -ve. | This is set to 1 when all 32-bits of the destination register are 0, else Z is 0. | This is set to 1 when the destination register, as the result of an addition for example, is equal to or greater than ( => ) 2^32. It has more complicated function which will be discussed later. | This can be quite complicated but it is generally used to indicate a signed overflow. This assumes however that the registers being used are interpreted as 2s complement. See below. |
Detailed Flag Description
The following table describes the most important conditions required to set the flags.| signed/unsigned | range | flags NZCV |
|---|---|---|
| unsigned | 2^32 | 001X |
| signed/unsigned | range | flags NZCV |
| unsigned | >2^32 | 001X |
| unsigned | (2^31)-1 < x < 2^32 | 1010 |
| unsigned/signed | 0 | 0100 |
Lets explain some cases in more detail.
Setting C (carry):
ADD R0, R1, R2
If the result in R0 is greater than (2^32)-1 given that both R1 and R2 are unsigned C will be set and NZV are 0. Although the ARM registers are 32-bit the highest unsigned value one can express is 2^31 because we start counting from 0.
R1 00000000000000000000111111111111
R2 11111111111111111111000000000000
R0 11111111111111111111111111111111 ADD
Now R0 is 2^31 + 2^30 + 2^29 ... = (2^32)-1, now it is clear to see why any value above this cannot be expressed in 32-bits. So C is set to 1 for values greater than (2^32) -1, try to think of C as the 33rd bit.
Setting C (carry):SIGNED VALUES
ADD R0, R1, R2
Where both R1 and R2 are signed. To say that a register is signed just means that we interpret as twos complement. Remenber that in twos complement the MSB is used indicate that the number is negative or positive. But this does mean that we now only have 31-bits to express the magnitude, so the range we can express in 32-bit twos complement is: -2^31< x >+(2^31)-1 . This now means that our maximum value that will trigger C to be set is (2^31)-1.
CMP - COMPARE
Most of the above condition codes compare two values, GT and LO for example. In order to compare a register to a number the CMP or compare instruction is used. The CMP instruction simply subtracts the second operand from the first:If after the CMP instruction the flags are:
- Z SET, this obviously means that they are both EQAUL because Rn - Rd = 0.
- N CLEAR, this must mean that Rn is POSITIVE because Rn - Rd > 0.
Conditional Execution
Most ARM assembly instructions can make use of conditional executuion by adding two letters to the end of the instruction as follows: example: ADD(cc) where cc means condition code. The following table describes the most important condition codes.
| Mnemonic | Flags required for execution | Description |
|---|---|---|
| GE | N == V | Greater than or equal to -Signed |
| GT | N == V & (Z Clear) | Greater Than -Signed |
| LT | N != V | Less Than -Signed |
| LE | (Z set) || (N != V) | Less Than -Signed |
| NE | Z clear | Not Equal to |
| HS | C set | Higher or equal -unsigned |
| HI | C set and Z clear | Higher than -unsigned |
| LS | (C clear) || (Z set) | Less than or equal to -unsigned |
| LO | C clear | Less than -unsigned |
| EQ | Z set | EQUAL |
| PL | N clear | Positive |
| CS | C set | Carry Set |
| CC | C Clear | Carry Clear |
| VS | V set | Overflow |
| VC | V clear | No Overflow |
| NV | N/A | NEVER -Probably best not to use |
| AL | N/A | Always |
To use these all we need to do is add them to the end of the normal instructions, for example:
CMP R0, #10
MOVEQ R1, #1
So here we can see that R0 is made equal to decimal 10, then it is compared to decimal 10, which actually means R0 - 10, which is obviously 0. Because of this the Z flag is set. MOVEQ means MOVE IF EQUAL TO, which if you look in the table above requires that the Z flag is SET. Which is exactly what has happened to us, as a result the register R1 is made equal decimal 1.
Another example is in order.
MOV R1, #1
CMP R0, #20
ADDGE R1, #1
Here R0 is again made equal to decimal 10 and R1 = decimal 1. Now the CMP instruction compares R0 (which is 10) and decimal 20, which actually again actually means R0 - 20 = -10. As a result the N flag is SET. ADDGE translates as ADD IF SIGNED GREATER THAN OR EQUAL . This requires that flags N == V, which is NOT the case here, only the N flag has been set so N != V. This addition does not, therefore go ahead.
The S bit
It is also possible to set the ARM flags using the S bit (which means set bits after execution) after and ADD or a SUB instruction. Example:SUBS R0, R0, #10
MOVEQ R1, #1
So R0 = 10. SUBS sets the flags after R0 - 10 which == 0, so Z flag SET. The MOVEQ executes because Z SET.
Some more Comparison Instructions
CMN -Is able to set NZCV by instead of subtracting Rn and Rd, but by adding them.TST - Is only able to set NZ by performing the digital logic operation AND on RN and Rd.
TEQ -Is able to set NZ by performing the XOR or EXCLUSIVE-OR operation on Rn and Rd.
ARM Assembly Language - an Introduction by J. R. Gibson
ARM Assembly by William Hohl