Programmers Model

Registers R0-R15, xPSR.

Mohith N
Updated: 19 March 2026
10 min read

The programmer's model of a processor defines exactly which registers are visible to software and how they behave during normal execution, exception handling, and privilege transitions. For ARM Cortex-M, this model is the foundational knowledge required before writing a single line of assembly, configuring an RTOS, or understanding how interrupt context switching works.

ARM Cortex-M Register SetGeneral PurposeR0 - Argument / ResultR1 - Argument / ResultR2 - ArgumentR3 - ArgumentR4-R11 - Local VariablesR12 - Intra-call scratchSpecial PurposeR13 / SP - Stack PointerMSP - Main Stack PointerPSP - Process Stack PointerR14 / LR - Link RegisterReturn address from functionR15 / PC - Program CounterNext instruction addressStatus Register xPSRAPSR - App flagsN Z C V Q bitsIPSR - ISR numberActive exception numberEPSR - Exec stateThumb bit T (must be 1)PRIMASK - IRQ disableFAULTMASKBASEPRI - IRQ priority maskCONTROL - SP select / privAll registers are 32-bit wide. xPSR = APSR | IPSR | EPSR combined view.
Figure 1: Complete ARM Cortex-M register set visible to programmer including special purpose and status registers

Core Concept Explanation

ARM Cortex-M has 16 general-purpose registers numbered R0 through R15, all of which are 32 bits wide. Registers R0 to R12 are general-purpose but have defined roles in the ARM Procedure Call Standard (AAPCS). R0 to R3 are used to pass the first four function arguments and hold return values. Registers R4 to R11 are callee-saved, meaning a function that uses them must preserve their original values. R12 serves as an intra-procedure call scratch register used by linker veneers.

Register R13 is the Stack Pointer (SP). The Cortex-M has two physically separate stack pointers: the Main Stack Pointer (MSP) and the Process Stack Pointer (PSP). Only one is active at a time, selected by bit 1 of the CONTROL register. MSP is used by exception handlers and by the main thread when running without an RTOS. PSP is used by RTOS tasks so that the task's stack is separate from the exception handler stack.

Register R14 is the Link Register (LR), which automatically receives the return address when a BL (Branch with Link) instruction is executed. When an exception occurs, LR is loaded with a special EXC_RETURN value that encodes which stack pointer to restore and whether to return to Thread or Handler mode. Register R15 is the Program Counter (PC) and always contains the address of the next instruction to be fetched. Writing to PC causes a branch, which is how many jump and return operations are implemented.

The xPSR Status Register

The xPSR (Program Status Register) is a 32-bit combined register that is internally split into three overlapping views: APSR, IPSR, and EPSR. The APSR (Application PSR) holds the four condition flags: N (Negative), Z (Zero), C (Carry), and V (Overflow). These flags are updated by arithmetic and logical instructions and are tested by conditional execution suffixes in Thumb-2.

The IPSR (Interrupt PSR) holds the number of the currently executing exception. A value of 0 means the processor is in Thread mode (no exception). Values 1 to 15 represent system exceptions (NMI, HardFault, SVC, etc.) and values 16 and above represent external peripheral interrupts. The EPSR (Execution PSR) holds the T bit, which must always be 1 on Cortex-M because the processor only supports Thumb-2 state. If T becomes 0, the processor generates a HardFault immediately.

Special Registers and Privilege Levels

The Cortex-M supports two privilege levels: Privileged and Unprivileged. Privileged mode has access to all registers and system configuration. Unprivileged mode cannot access the system control block, NVIC, or SysTick directly. The CONTROL register selects which mode the Thread mode executes in and which stack pointer is active. This two-level privilege model is fundamental to building secure RTOS-based systems where application tasks cannot corrupt the kernel.

PRIMASK, BASEPRI, and FAULTMASK are interrupt masking registers accessible only from privileged code. PRIMASK disables all maskable interrupts when set to 1, used for short critical sections. BASEPRI masks all interrupts with a priority number greater than or equal to the programmed value, enabling fine-grained interrupt shielding. FAULTMASK additionally disables all faults except NMI and is used only in fault recovery routines.

Mathematical Expression

The N, Z, C, V flags in APSR are updated after each arithmetic instruction. For a subtraction operation A minus B implemented as A + (~B) + 1 (two's complement addition), the carry flag C indicates unsigned borrow (C = 0 means borrow occurred). The overflow flag V is set when the signed result exceeds the 32-bit signed range. A common GATE question derives the relationship: for an N-bit subtraction, borrow = NOT(C) when the ARM carries out subtraction via addition of two's complement.

Practical Understanding

In an RTOS like FreeRTOS running on Cortex-M, each task has its own stack and uses PSP. When the SysTick timer triggers a context switch, the kernel saves R4-R11 (since R0-R3, R12, LR, PC, xPSR are hardware-saved automatically on exception entry), switches PSP to point to the new task's stack, restores R4-R11 from the new task's stack, then executes BX LR using the EXC_RETURN value to return to Thread mode using PSP. Understanding this register map is the minimum knowledge required to write or debug an RTOS port.

Example
Given:
R0 = 0x00000050 (decimal 80)
R1 = 0x00000090 (decimal 144)
Instruction: SUBS R2, R0, R1  (R2 = R0 - R1 = 80 - 144)

Why this formula applies:
SUBS updates APSR flags. Subtraction on ARM = A + (~B) + 1

Formula:
Result = R0 + NOT(R1) + 1 = 80 + (NOT 144) + 1 = 80 - 144 = -64

Substitution:
Result (unsigned) = 0x00000050 + 0xFFFFFF6F + 1 = 0xFFFFFFC0

Calculation:
R2 = 0xFFFFFFC0 (signed = -64)
N flag = 1 (MSB is 1, result is negative)
Z flag = 0 (result is not zero)
C flag = 0 (borrow occurred, since 80 < 144 unsigned)
V flag = 0 (no signed overflow, -64 is within range)

Final Answer:
R2 = 0xFFFFFFC0, N=1, Z=0, C=0, V=0. Borrow = NOT(C) = 1 confirms unsigned underflow.
Exam Tip: On ARM Cortex-M, borrow from a subtraction is the logical NOT of the Carry flag (C=0 means borrow occurred). This is opposite to the convention used in x86. GATE questions on flag computation after SUB instructions frequently test this specific difference.

Mechanism: Register Usage in Function Calls

AAPCS Register Usage in Function CallCaller FunctionLoad args into R0,R1,R2,R3BL function_name (LR = return addr)After return: result in R0R4-R11 unchanged (callee-saved)Caller-saved: R0-R3, R12(may be corrupted by callee)Callee-saved: R4-R11, LR(must be restored before return)Callee FunctionPUSH {R4-R7, LR} on entryUse R0-R3 as input argsCompute, use R4-R7 for localsStore result in R0POP {R4-R7, PC} to returnPOP into PC restores returnaddress from saved LRBLRET
Figure 2: ARM Procedure Call Standard register roles during a function call and return sequence
  • R0-R3 hold function arguments and the return value. The caller may freely use these after a call without saving them. If a function needs more than four arguments, the extra ones are passed on the stack.
  • R4-R11 are preserved across calls. The callee must save these to the stack using PUSH on entry and restore them using POP on return.
  • LR holds the return address. For a leaf function that makes no further calls, it can directly BX LR. For non-leaf functions, LR must be pushed to stack and popped into PC on return.
  • The T bit in EPSR must always remain 1. Branching to an address with bit 0 clear would clear T and cause an immediate HardFault exception.
  • On exception entry, hardware automatically saves R0-R3, R12, LR, PC, and xPSR onto the active stack. Software must separately save R4-R11 if the ISR uses those registers.

Quick Revision

  • 16 registers total, all 32-bit. R0-R12 general purpose, R13 = SP, R14 = LR, R15 = PC.
  • Two stack pointers: MSP (exceptions, privileged) and PSP (RTOS tasks). CONTROL register bit 1 selects active SP.
  • xPSR = APSR (N,Z,C,V,Q flags) + IPSR (exception number) + EPSR (T bit). T bit must always be 1.
  • Caller-saved: R0-R3, R12. Callee-saved: R4-R11. Return value in R0.
  • Hardware auto-saves R0-R3, R12, LR, PC, xPSR on exception entry. ISR only needs to save R4-R11 if used.
  • Exam trap: On ARM, borrow = NOT(C). C=0 after SUBS means unsigned underflow, opposite to x86 borrow convention.
  • PRIMASK=1 disables all IRQs. BASEPRI masks IRQs at or below a priority level. FAULTMASK masks all faults except NMI.

ARM Programmers Model

Test your knowledge on this topic!

Question 1 of 3

Q1.Which core register serves as the Program Counter (PC) in the Cortex-M architecture?