The ARM architecture supports both conditional and unconditional branches to alter the flow of program execution. Conditional branches test a condition code and branch if the condition is true, while unconditional branches always branch. The processor mode determines which instructions are available and what privileges the code has access to. This article will explore conditional versus unconditional branches, explain the available processor modes, and discuss how branches and modes interact on ARM processors.
Conditional Branches
Conditional branches allow branching based on the result of a comparison or test. ARM supports a set of condition codes that represent possible results of a comparison, like equals, greater than, less than, etc. Conditional branch instructions check the condition code and branch if the condition is true.
For example, the branch if equal instruction (BEQ) checks if the zero bit is set in the condition flags register. If the last comparison was an equality check that set the flags, BEQ branches. Otherwise, it falls through. Other conditional branch instructions check other condition codes. This allows efficient conditional program flow without explicitly testing a condition in code.
Here is an example pseudo-code using BEQ: CMP R0, R1 // Compare R0 and R1 BEQ label // Branch if equal … label: // Do something
Conditional branches are powerful because they avoid the need to explicitly evaluate conditions with additional instructions. The processor handles the test automatically.
Condition Codes
The condition codes checked by conditional branch instructions indicate the result of the last comparison, which sets the flag bits in the Application Program Status Register (APSR). There are several condition codes that can be tested:
- Equal (Z set) – BEQ
- Not equal (Z clear) – BNE
- Unsigned higher or same (C set) – BHS
- Unsigned lower (C clear) – BLO
- Signed greater than or equal (N==V) – BGE
- Signed less than (N!=V) – BLT
- Signed greater than (Z==0, N==V) – BGT
- Signed less than or equal (N!=V) – BLE
The conditional branch checks the flags set by the previous instruction that set the condition codes. This avoids explicit comparison logic before the branch.
Range of Branch Offsets
The branch instructions specify a label to branch to. This label is encoded as a relative offset from the branch instruction’s own address. Different conditional branch instructions support different sized offsets:
- BEQ, BNE – +/- 16MB offset
- BGE, BLT – +/- 1MB offset
- BGT, BLE – +/- 32KB offset
This relative offset range determines how far away the label target can be placed. For long distance branches, it may be necessary to use multiple branches.
Unconditional Branches
Unconditional branches always branch to the target location, regardless of flags or condition codes. These include direct branches (B) and indirect branches (BLX).
The B instruction branches to a label at a relative offset. BLX branches to an address in a register. Both happen unconditionally.
For example: B label // Always branches to label BLX R3 // Branches to address in R3
Unconditional branches are used when you want to redirect execution flow without testing a condition. They are simpler than conditional branches but less flexible.
Range of Unconditional Branch Offsets
The ARM B instruction supports a relative offset of +/- 32MB, allowing branches over a large region of code. BLX uses an address stored in a register, so the destination can be anywhere within the 32-bit address space.
Interactions Between Branches and Processor Modes
ARM processors support different processor modes with varying levels of privilege. The mode determines what instructions can execute and what system resources are accessible. The branches available and their behavior depends on the current mode.
ARM Processor Modes
The key ARM processor modes are:
- User – Low privilege, cannot access system resources
- FIQ – Supports fast interrupts, has some privilege
- IRQ – Used for general interrupts, has some privilege
- Supervisor – Protected mode for OS kernels, high privilege
- Abort mode – Entered on data or instruction aborts
- Undefined mode – Entered on attempt to execute undefined instructions
- System mode – Highest privilege, can execute any instruction
The current mode is defined in the Current Program Status Register (CPSR). Code can switch between modes using special instructions like MOV, MSR, OR MRS when privilege allows.
Branching Between Modes
Branches can target code in the same or different mode. However, branching from a low privilege mode to code in a higher privilege mode is prohibited. Attempting an illegal branch raises an exception.
For example, code running in User mode cannot branch directly to Supervisor mode code. But Supervisor mode code can branch freely. Restricting branches prevents User mode from accessing protected system resources.
Mode-Specific Branch Instructions
Some branch instructions are only available in specific modes due to privilege restrictions. For example:
- RFE – Return from exception, only in exception modes
- ERET – Return from exception ERET, only in exception modes
- SRS – Store return state, only in non-User modes
- BXJ – Branch exchange with link to Java state, only in Jazelle state
Attempting to execute these branches in the wrong mode will trigger an Undefined Instruction exception.
Typical Uses of Branches
Branches are used extensively in ARM code to implement conditional flows and redirects. Some typical examples include:
- Multi-way conditional code – Use a series of conditional branches to check multiple conditions.
- Complex conditionals – Combine multiple conditional branches to test compound conditions.
- Looping constructs – Branches can implement loops of various types.
- Subroutine returns – Return to the calling code after a procedure.
- Interrupt handling – Branch to interrupt handlers based on interrupt triggers.
- State machines – Branches allow compact state machine implementations.
- Error handling – Branch to error handling code when exceptions occur.
Both conditional and unconditional branches are used extensively for these purposes. Using the appropriate branches and managing the mode transitions properly is key to efficient ARM programming.
Example: Branching in a Function Call
Here is an example of how branches are used to implement a simple C function call in ARM assembly: // Function declaration int myFunction(int arg1, int arg2); // Calling code MOV R0, #1 // Set first argument MOV R1, #2 // Set second argument BL myFunction // Call function // Function definition myFunction: PUSH {R4, LR} // Save registers … // Do function work MOV R0, #10 // Return value in R0 POP {R4, LR} // Restore registers BX LR // Return to caller
This shows both an unconditional BL branch to perform the call, and a conditional BX return branch. The LR register is used to hold the return address. Pushing and popping LR around the function body preserves the right return location.
The branches allow easy redirection of code flow to implement the call and return. The conditional BX makes sure to return properly after the function work completes.
Conclusion
In summary, ARM supports conditional and unconditional branches to alter program flow. Conditional branches test condition codes set by previous instructions while unconditional branches always redirect execution. The processor mode determines what branches are available and what modes code can branch between. Using the appropriate branches allows implementing conditional logic, loops, function calls, interrupts and more. Proper use of branches and managing mode changes are key skills for ARM developers.