The Application Program Status Register (APSR) is one of the key registers in the ARM Cortex series of processors. It contains important status and control information related to the execution state of an application program. Directly accessing the APSR can provide valuable insights into the current state of the processor and allow modifying certain control bits to influence application behavior. This article will provide a comprehensive guide on how to access the APSR register on ARM Cortex devices.
Overview of the APSR Register
The APSR is a 32-bit read/write register which contains the current state of the condition flags, the interrupt disable bit, and the Q flag for saturation arithmetic. It is accessible in all modes – user, FIQ, IRQ, supervisor, abort, undefined and system. The register can be broken down into the following bit fields:
- Bits 31-28: Negative/Less than/Zero/Overflow flags
- Bit 27: Q flag for saturation arithmetic
- Bit 9: Interrupt disable bit
- Bits 8-0: Reserved
The N, Z, C, and V bits (bits 31 to 28) hold the state of the condition flags which are used for conditional execution in ARM code. These get updated automatically based on the results of data processing instructions. The Q flag (bit 27) indicates saturation on overflow during signed arithmetic. The I bit (bit 9) can be used to disable IRQ interrupts when set to 1. The remaining bits are unused and reserved for future expansion.
Reading the APSR Value
There are two ways to read the current value in the APSR register – directly accessing the register using LDR instructions or transferring a copy of it to a general purpose register using MRS instructions.
Using LDR Instructions
Any word aligned LDR instruction can directly access the APSR as it is memory mapped to the coprocessor space. For example: LDR R0, =CPSR @ Load APSR into R0 LDR R1, [CPSR] @ Load APSR into R1
Here the CPSR label points to the memory address of the APSR register. So the LDR instruction loads the value from that memory location into the destination register R0 or R1. This allows you to read the APSR in a single instruction.
Using MRS Instructions
The Move to Register from Special register (MRS) instruction can be used to transfer a copy of the APSR contents to a general purpose register. The syntax for MRS is: MRS Rd, APSR
For example: MRS R2, APSR @ Copy APSR to R2
The MRS instruction copies the value in APSR to register R2. While MRS requires 2 instructions – one to do the transfer and one to use the transferred contents, it has the benefit of not directly accessing memory. The LDR method reads the actual APSR location whereas MRS just gets a snapshot copy of it.
Writing to the APSR Register
Updates to the APSR register are possible using the MSR (Move to Special Register) instruction. Using MSR you can directly modify the N, Z, C, V flags to values of your choosing. However, writing to the APSR does come with some caveats:
- You can only modify the condition flags (bits 31 to 28)
- Modifying the flags does not actually update processor state
- Changing I bit state using MSR is deprecated
Let’s look at some examples of using MSR to write to APSR: MSR APSR_nzcvq, R5 @ Set flags using R5 MSR APSR_nzcvq, #0xF0000000 @ Set flags directly
Here APSR_nzcvq specifies that we want to modify only the condition flags portion of APSR. In the first example, the value in R5 is used to update the flags. In the 2nd example, an immediate value sets the flags to known values.
Important: Though MSR allows you to directly configure the flags, this won’t actually update the processor state. The flags will be overwritten by the next instruction that does a comparison or arithmetic operation. To preserve custom flag settings, you have to prevent any updating of flags in subsequent instructions.
Preserving APSR Flag Settings
As noted earlier, directly modifying the APSR condition flags using MSR does not actually update ARM processor state. So how do you keep the custom flag settings from getting overwritten? One method is to avoid flag-setting instructions immediately after MSR. For example: MSR APSR_nzcvq, R5 @ Set custom flags AND R2, R3, #1 @ Bitwise AND – does not affect flags LSL R1, R2, #3 @ Logical shift – does not affect flags
The AND and LSL instructions here do not update flags, so the changes made by MSR are preserved. An easier method is to make use of the IT instruction which can conditionally disable flag updates: MSR APSR_nzcvq, R5 @ Set custom flags IT NE @ Next 2 instructions do not update flags AND R2, R3, #1 LSL R1, R2, #3
The IT NE instruction disables flag updates for the next two instructions inside the IT block. This causes the AND and LSL to not modify flags, preserving our custom APSR values.
Use Cases for Direct APSR Access
Why would you want to directly access and modify the APSR contents? Here are some example use cases:
Conditional Code Execution
By setting the condition flags in APSR, you can force conditional code to execute on the opposite condition. For example: MSR APSR_nzcvq, #0xF0000000 @ Set N,C,V flags = 1 BEQ label @ Will branch now
Even though the BEQ checks for Z=1, we force it to branch by setting N,C,V = 1 via MSR.
Setting the I bit in APSR will disable IRQ interrupts. This can be done via: MRS R1, APSR @ Read APSR ORR R1, #0x200 @ Set I bit MSR APSR_nzcvq, R1 @ Write back
However, as noted earlier, directly changing the I bit via MSR is deprecated on Cortex-M devices. The recommended method is to use the PRIMASK register instead for interrupts.
The Q flag in APSR can be checked after arithmetic to determine if saturation happened. This is useful when doing fixed point math. SMLAWB R1, R2, R3, R4 @ Signed multiply accumulate MRS R5, APSR @ Check Q flag TST R5, #0x08000000 BEQ no_saturation @ Branch if no saturation
Accessing APSR during debugging can help view the current processor state. The flags provide an indication of previous comparison results. Examining APSR when a crash occurs gives insight into the cause.
APSR Usage Guidelines
While APSR provides useful status and control functionality, direct access should be used carefully. Keep the following guidelines in mind:
- Avoid direct APSR access in interrupt handlers
- Minimize read-modify-write sequences to APSR
- Use MRS/MSR for readability even if LDR is faster
- Ensure MSR of flags is followed by non flag-setting instructions
- Preference PRIMASK over I bit for interrupt disable
- Check Q flag for saturation arithmetic if required
Differences Between Cortex-M and Cortex-A
The Cortex-M and Cortex-A series of ARM processors have some differences when accessing APSR:
- Cortex-M – APSR always accessible in handler mode
- Cortex-A – APSR access limited to specific modes
- Cortex-M – MSR to I bit deprecated, use PRIMASK instead
- Cortex-A – Can disable IRQ via MSR to I bit
- Cortex-M – LDR of APSR does an actual memory read
- Cortex-A – LDR of APSR just returns the current value
So code that accesses APSR directly may need to be adjusted when porting between the two architecture families.
Interactions with EXC_RETURN
The EXC_RETURN register holds info about the stack frame when exception handlers are invoked. The lowest 4 bits of EXC_RETURN mirror the condition flags: EXC_RETURN[3:0] = APSR[31:28]
This means when modifying APSR flags, the lower 4 bits of EXC_RETURN also get updated. The reverse applies too – changing the low nybble of EXC_RETURN results in the flags being updated.
Accessing APSR in Thumb vs ARM Mode
In Thumb mode, the APSR is accessible via the special register number 0x400F. In ARM mode, it uses the specific register name CPSR instead. For example: Thumb mode: MRS R1, APSR MSR APSR_nzcvq, R2 ARM Mode: MRS R1, CPSR MSR CPSR_f, R2
So code accessing APSR directly needs to be aware of the instruction set state.
Interaction with Execution Priority
The ARM architecture has finely grained control over execution priority using registers like BASEPRI, FAULTMASK and PRIMASK. These work together with APSR’s I bit to allow priority-based preemption of exceptions and interrupts. Setting BASEPRI prevents low priority exceptions from interrupting the current execution context. FAULTMASK prevents faults from escalating to hard faults in certain cases. PRIMASK disables all exceptions below a certain priority level. These registers work with the I bit to provide flexible prioritized interruption control.
Accessing APSR in Privileged vs Unprivileged Code
The APSR can be accessed freely in privileged code without restrictions. But in unprivileged user mode, there are some limitations imposed by the architecture:
- MRS can only transfer APSR_nzcvq (just the condition flags)
- MSR can only write to APSR_nzcvq
- Direct LDR/STR access to APSR is prohibited
So unprivileged code cannot read/write the full APSR value or the interrupt disable bit. This prevents user applications from arbitrarily modifying the processor state.
Using APSR Safely
Directly accessing APSR can be useful but also dangerous if done carelessly. Some tips for using APSR safely:
- Avoid APSR manipulation in interrupt handlers
- Keep read-modify-write of APSR to a minimum
- Do not overwrite I bit, use PRIMASK register instead
- Ensure exception return behavior is understood if changing flags
- Prevent conditional execution from straying far on false condition
- Restrict APSR access in unprivileged code as per architecture
With careful usage, the APSR can provide helpful insight into processor execution state and greater control over program flow.
The Application Program Status Register in ARM Cortex processors packs useful status and control bits related to the current execution context. Understanding how to properly read, modify and utilize the APSR can help developers optimize program behavior. This guide covered the key methods for accessing APSR – using LDR for direct reads, MRS/MSR for transfers and the intricacies around changing the condition flags and interrupt disable bits. With the power of APSR access comes great responsibility, so adhere to the architectural guidelines and use it judiciously. Refer to this article whenever you need deeper insight or more control around your Cortex-M application via the APSR.