Program Status Register
The ARM Cortex-M architecture contains a status register (Program Status Register) that stores information about a previously executed instruction. The PSR is a combination of status registers: Application Program Status Register (APSR), Interrupt Program Status Register (IPSR), and the Exception Program Status Register (EPSR). For now, we will concern ourselves only with the upper 4 bits of the APSR. These 4 status bits provide a mechanism to conditionally execute instruction(s) based on the results of a previous instruction. The figure below shows the contents of the APSR.
The APSR contains the following condition flags:
N Set to 1 when the result of the operation was negative, cleared to 0 otherwise.
Z Set to 1 when the result of the operation was zero, cleared to 0 otherwise.
C Set to 1 when the operation resulted in a carry, cleared to 0 otherwise.
V Set to 1 when the operation caused overflow, cleared to 0 otherwise.
When a Carry Occurs
- if the result of an addition is greater than or equal to 2^32
- if the result of a subtraction is positive or zero
- as the result of an inline barrel shifter operation in a move or logical instruction.
When Overflow Occurs
Overflow occurs when the sign of the result, in bit, does not match the sign of the result had the operation been performed at infinite precision, for example:
- if adding two negative values results in a positive value
- if adding two positive values results in a negative value
- if subtracting a positive value from a negative value generates a positive value
- if subtracting a negative value from a positive value generates a negative value.
It is important to note that the APSR is NOT updated after every instruction. There are two ways to set the status bits in the APSR: use a special suffix at the end of an instruction or use one of several instructions that always update the APSR.
Dedicated Comparison Instructions
Lets take a look at a few different ways to set the APSR. The first method to set the APSR is to use instructions that always update the APSR. There are four commonly used instructions that always set the flags bits: CMP, CMN, TST, and TEQ. Each of these instructions will update only the APSR. They do not modify the contents of either register used in the comparison.
;CMP (Compare) Subtracts the value of Operand2 ;CMN (Compare Negative) Adds the value of Operand2 ;TST (Test Bits) Bitwise AND operation with Operand2 ;TEQ (Test Equivalent) Bitwise XOR operation with Operand2 ; Initialize R0, R1, and R2 MOV R0, #10 MOV R1, #10 MOV R2, #20 ; Non-destructive subtract (R0, R1, R2 are not modified), update flags registers CMP R0, #10 ; N=0, Z=1, C=1, V=0 CMP R0, R1 ; N=0, Z=1, C=1, V=0 CMP R0, R2 ; N=1, Z=0, C=0, V=0
Appending S to an Instruction
In addition to dedicated comparison operations, you can also use the results of any of our arithmetic/logical instructions to set the APSR. All you have to do is append an ‘S’ to the end of the instruction. The major difference between this method is that the result of the the ALU instruction is stored into one of the General Purpose Registers. Lets take a look at a few examples.
MOV32 R0, #0x7FFFFFFE ; Initialize R0 to a very large value ADD R0, R0, #1 ; Flags are not updated since there is no 'S' ADDS R0, R0, #1 ; Flags ARE updated. N=1, Z=0, C=0, V=1 MOV32 R0, #0x7FFFFFFF ; Initialize R0 to a very large value AND R0, R0, #0xFF ; Flags are not updated ANDS R1, R0, #0x03 ; N=0, Z=0, C=0, V=1
It is important to note is that even with the ‘S’ suffix, an instructions may not update all of the flag bits. The last instruction in the example above states that V is equal to 1. This is because most logical instructions do not update the overflow bit. Make sure to examine the ARM Instruction Referenceto see which flags a given instruction will modify.
Using the status flags in the APSR, you can write assembly instructions that will conditionally execute. Conditional instructions allow us to implement high level language constructs like if/else and for loops. So what instructions are conditional in the Cortex-M architecture? The answer is that all instructions can be conditional. The Cortex-M architecture supports a variety of condition codes that can be appended to any ARM assembly instruction. If the flags in the APSR match the given condition code, the instruction is executed as normal. If the condition code is not met, the instruction becomes a NO OP and has no effect.
Let’s examine the EQ suffix. The EQ suffix is used to determine if the result of a prior instruction resulted in a value that was equal to zero. If the result was equal to zero, the instruction would execute as normal. If the result was non-zero, the instruction would become a NO OP.
The GE suffix is used to determine if a prior instruction resulted in a value that is greater than or equal to 0.
Appending S to MOV
MOVS R1, #-1 MOV R0, #1 MOVEQ R0, #0 MOVMI R0, #-1 MOVGT R0, #1
(1) R1 is set to 0xFFFFFFFF and flags are updated (N=1, Z=0, C=0, V=0). This is the only instruction that updates the flags register in this sequence since it is the only instruction with S appended to it.
(2) R0 is set to 0x1. Flags are not updated
(3) Acts as a NO OP since Z = 0
(4) R0 is set to 0xFFFFFFFF since N = 1
(5) Acts as a NO OP. The value in instruction 1 was a negative value, so it cannot be greater than 0.
The ARM assembly below shows how to implement an IF/ELSE statement without using branch statements. You could have used a branch statement to implement this code fragment, but it would have been a less efficient implementation. When a branch statement is issued, the 3-stage pipeline has to flush the two instructions following the branch instruction since they should not execute. This results in a two clock cycles where the processor effectively does nothing. By using the conditional instructions below, only a single operation is turned into a NO OP, so only a single clock cycle is lost.
i = 10; if(i>5) i = 7; else i = 0;
; Initialize R0 MOV R0, #10 ; If Else CMP R0, #5 MOVGT R0, #7 MOVLE R0, #0
Lets take a look at an if statement with multiple conditions
MOV32 R0, 0xDEADBEEF ; If bit0 AND bit 31 are both 1, set R1 to 1, else set R1 to 0 MOV R1, #1 TST R0, #0x80000000 MOVEQ R1, #0 TST R0, #0x00000001 MOVEQ R1, #0