The Status register

From ARMwiki
Jump to: navigation, search

While the ARM has always been a 32 bit processor, the original design had the Program Counter and Processor Status Register both sharing R15. For this reason, older processors are often referred to as "being 26 bit". This refers to the width of the Program Counter.

A Program Counter of 26 bits allows addressing only within a 64MiB range, so this really only pertains to older RISC OS computers (which typically offered 4MiB or sometimes 8MiB onboard). Contemporary ARM processors offer a full 32 bit Program Counter allowing access to up to 4Gb of directly addressable memory, along with separate status registers.


Contemporary processors

There is a Current Processor Status Register (CPSR) which is the same across all processor modes. Then there is the Saved Processor Status Register (SPSR) which is specific to each mode, with the exception of User mode and System mode (the two least privileged modes) which do not have a SPSR.

Bit allocation

The allocation of the bits within the CPSR (and SPSR) is:

31 30 29 28 27 24 19 … 16 9 8 7 6 5 4 … 0
N Z C V Q J GE[3:0] E A I F T M[4:0]

Though note that some of these allocations may differ depending on the ARM family device you are using. Please refer to the appropriate datasheet for specifics.

The Condition flags are as follows:

  • Negative: is set to bit 31 of the result, so N is 1 if the signed value is negative, and cleared if the result is positive or zero.
  • Zero: is set if the result is zero; this is usual to denote an equal result from a comparison. If the result is non-zero, this flag is cleared.
  • Carry: Is more complex:
    • With the instructions ADC, ADD, and CMN, this flag is set if the result would produce an unsigned overflow.
    • With the instructions CMP, SBC, and SUB, this flag is set if the result would produce an unsigned underflow (a borrow).
    • For other instructions that use shifting, this flag is set to the value of the last bit shifted out by the shifter.
    • Other instructions usually leave this flag alone.
  • oVerflow: for addition and subtraction, this flag is set if a signed overflow occurred. Otherwise, it is generally left alone. Note that some API conventions may specifically set oVerflow to flag an error condition.

The Interrupt flags are as follows:

  • I: when set, disables IRQ interrupts
  • F: when set, disables FIQ interrupts
  • A: [ARMv6 and later] when set, disables imprecise aborts (this is an abort on a memory write that has been held in a write buffer in the processor and not written to memory until later, perhaps after another abort or interrupt is in progress.

The Instruction set flags are as follows:

  • Thumb
  • Jazelle

These are set in the following pattern:

J T Instruction set
0 0 ARM (standard)
0 1 Thumb
1 0 Jazelle
1 1 Reserved

The T bit exists on Thumb capable processors (ARMv4 and later, according to capabilities), the J bit exists on Jazelle capable processors (ARMv5 and later, according to capabilities). Both J and T bits exist in the ARMv6 processors.

The other bits (depending on processor) are as follows:

  • Q: this flag is set in E variants of of ARMv5 and above to indicate underflow and/or saturation is used in instructions intended to assist DSP operations.
  • GE[3:0]: these flags, in ARMv6, control the Greater than or Equal behaviour in SIMD instructions. For halfword instructions, if bits 3:2 are set, the upper halfword is used; and if bits 1:0 are set, the lower halfword is set. Similarly, for byte opeations, if bit 3 is set, the top byte is used; if bit 0 is set, the bottom byte is used; and bits/bytes 2 and 1 in the same fashion.
  • E: is a flag in ARMv6 that controls the 'endianness' for data handling. Instruction fetches are unchanged, little-endian and word aligned (halfword for Thumb).

In little-endian formats, the first byte at a word-aligned address is the least significant byte. Thus, the hex value 0xDEADBEEF would appear as EF, BE, AD, DE; while big-endian formats would have the first byte as the most significant, leading to the memory holding DE, AD, BE, EF.
Actually setting up endian support requires co-operation between the status register and flags in the CP15 processor configuration, and is therefore beyond the scope of this document.

There may be more, please refer to the appropriate datasheet.

Processor mode bits are as follows:

M[4:0] Mode Register Set
 %10000 User R0-R14, CPSR, PC
 %10001 FIQ R0-R7, R8_fiq-R14_fiq, CPSR, SPSR_fiq, PC
 %10010 IRQ R0-R12, R13_irq, R14_irq, CPSR, SPSR_irq, PC
 %10011 SVC (supervisor) R0-R12, R13_svc R14_svc CPSR, SPSR_irq, PC
 %10111 Abort R0-R12, R13_abt R14_abt CPSR, SPSR_abt PC
 %11011 Undefined R0-R12, R13_und R14_und, CPSR, SPSR_und PC
 %11111 System (ARMv4+) R0-R14, CPSR, PC

Older processors, namely the ARM6, ARM7, and StrongARM (plus related devices, such as the ARM7500FE) offered legacy support for the 26 bit mode, as was used by RISC OS at the time.

The processor mode choices thus were as above, but also included the following:

Processor Mode options, legacy support

It is also possible, in this configuration, to select 26 bit modes by using the older PC+PSR method.

The Processor Modes and their use/behaviour are defined separately.

Manipulating the PSR

To copy a register into the PSR:

  MSR     CPSR, R0                ; Copy R0 into CPSR
  MSR     SPSR, R0                ; Copy R0 into SPSR
  MSR     CPSR_f, R0              ; Copy flag bits of R0 into CPSR
  MSR     CPSR_f, #1<<28          ; Copy flag bits (immediate) into CPSR

To copy the PSR into a register:

 MRS     R0, CPSR                ; Copy CPSR into R0
 MRS     R0, SPSR                ; Copy SPSR into R0

You have two PSRs - the CPSR which is the Current Program Status Register and SPSR which is the Saved Program Status Register (the previous processor mode's PSR). Each privileged mode has its own PSR, however USR and SYS modes do not have an SPSR.

You can only alter the SPSR of the mode you are currently in, thus if you are in SVC mode, you cannot MRS to update SPSR_fiq. The way to do this, should it be necessary, is to temporarily enter the mode relating to the SPSR you wish to update it, and do it that way. Using the _flg suffix allows you to alter the flag bits without affecting the control bits.

In User(32) mode, the control bits of CPSR are protected, you can only alter the condition flags. In other modes, the entire CPSR is available. You should not specify R15 as a source or destination register. And finally, you must not attempt to access the SPSR in User(32) mode as it doesn't exist!

When writing the PSR (either CPSR or SPSR), the register is broken into four sections as follows:

  • c Control bits (0-7): processor mode, I/F interrupt disable, and T on ARMv4T.
  • x eXtension bits (8-15)
  • s Status bits (16-23), don't confuse this with the status flags.
  • f Flag bits (24-31), NZCV in bits 28-31.


  MSR     CPSR_c, xxxx      sets the control bits
  MSR     CPSR_f, xxxx      sets the flag bits
  MSR     CPSR_cxsf, xxxx   sets everything

Note that you should modify only what is necessary to modify, as the processor families behave slightly differently regarding updating parts of the PSR. For example an ARM9 needs more cycles than an ARM7 to execute an MSR if you are updating the control bits (due to pipeline propagation).

To set the V flag (and clear the others) without affecting the other parts of the CPSR:

  MSR     CPSR_f, #&10000000

Alternatively, here is a clever way to set the V flag on any ARM processor:

  CMP     R0, #1<<31
  CMNVC   R0, #1<<31

To change mode:

 MRS     R0, CPSR                ; Copy the PSR
 BIC     R0, R0, #&1F            ; Clear the mode bits
 ORR     R0, R0, #new_mode       ; Set bits for new mode
 MSR     CPSR_c, R0              ; write PSR back, changing only the control bits

Obsolete versions of the CPSR

Older assemblers may have used different ways of referring to the PSR:

  • cpsr_all Control and Flags fields.
  • cpsr_flg Flags field.
  • cpsr_ctl Control field.

This older syntax should be considered obsolete. Use CPSR_cf, CPSR_f, or CPSR_c respectively.

Transitioning to 32 bit mode

For old time RISC OS coders, here's a quick résumé of things to avoid when writing 32 bit code:

  • Never use MOVS PC, Rxx
  • Never use LDMFD R13!, {Rxx-Rxx, PC}^
  • Specify your routines as corrupting processor flags, and make no attempt to preserve them, let the caller do that
  • Don't assume anything else preserves the processor flags - do it yourself before calling functions that could corrupt flags

Things are a little more complicated with APCS, but since the new C compiler can make code that runs on older machines as well as the newer ones (Iyonix, BeagleBoard, Pi, etc); why not convert your projects to be 26/32 neutral? This seems like the easiest way; though all that is really required in assembler is:

  [ {CONFIG=26}
    ; 26bit return
    MOVS    PC, R14     ; or LDMFD xxxx^
    ; 32bit return
    MOV     PC, R14     ; or LDMFD xxxx

objasm will then sort out which code to use depending on whether it is assembling for 26 bit or 32 bit...

If you are interworking with 32 bit neutral C, the C compiler (APCS-32) no longer expects flags to be restored, so you can change assembler routines to the 32 bit style exit and use the code on all RISC OS machines

Legacy processors (26 bit)

26 bit mode is a mostly obsolete method that is used in the original RISC OS machines and all versions of RISC OS from RISC OS 2 to RISC OS 4 (Select/Adjust) inclusive.
Specifically, the ARM2, ARM250, and ARM3 support only this mode of operation. The ARM6(10), ARM7(10), ARM7500(FE), and StrongARM support dual 26/32 operation for use with original versions of RISC OS.


In the original mode, the Status Register and Program Counter were combined in a single register, as follows:

PC+PSR, 26 bit

As the Program Counter was only expected to address 64MiB, it only needed to be 26 bits wide, the upper six bits being the four flags (NZCV) and the IRQ/FIQ disable bits. Furthermore, as every address is word aligned, the bottom two bits of the address would always be zero, so they could be used for a different purpose in the PC+PSR arrangement. Specifically, these bits held the current Processor Mode as follows:

Processor Mode options, 26 bit

When R15 is used as the first operand in an instruction, only the Program Counter part of it is available. Thus, the following instruction will copy PC out to a register and add 256 to it:

  ADD    R0, R15, #256

When R15 is used as the second operand, all 32 bits are accessible: the Program Counter, the flags, and the status. The following code segment will identify the current processor mode:

   MOV     R0, #3          ; Load a bit mask (%11) into R0
   AND     R0, R0, PC      ; AND R15 into R0, to get the mode status
   CMP     R0, #3          ; Compare mode with '3' (SVC)
   BEQ     svc             ; If SVC mode, branch to 'svc'
   CMP     R0, #2          ; Compare mode with '2' (IRQ)
   BEQ     irq             ; If IRQ mode, branch to 'irq'
   CMP     R0, #1          ; Compare mode with '1' (FIQ)
   BEQ     fiq             ; If FIQ mode, branch to 'fiq'
   CMP     R0, #0          ; Compare mode with '0' (USR)
   BEQ     usr             ; If USR mode, branch to 'usr'
   ; This is just an example, for it might be better to
   ; fall-through for the final case instead of branching.

You might ask why it wouldn't be possible to do AND R0, PC, #3 to save an instruction. This won't work as when PC is the first operand, only the PC part is visible. It must be the final operand in order to have the mode and flags visible.

Changing processor status

In order to change the processor mode, or indeed any of the flags, we need to EOR the desired flag with the status flags. There is pseudocode for changing the changing the state of the oVerflow flag:

  new_state = old_state EOR (1 << 28)

But we cannot do a simple EORS operation as writing back to R15 would affect the pipeline and cause the following two instructions to be skipped.

But don't worry. The instruction TEQ does a pretend EOR (the results are not stored anywhere, we don't need them). Combine this with the P suffix, which writes bits 0, 1, and 26 to 31 of the result directly to bits 0, 1, and 26 to 31 of R15 giving you an easy way to change the flags:

  TEQP   R15, bit_mask

You can only change a flag if you are in a processor mode which allows you to set that flag, namely you cannot sit in USR mode and instruct the processor to enter SVC mode.

  MOV     R6, PC          ; Store original state of PC in R6
  ORR     R7, R6, #3      ; Set SVC mode
  TEQP    R7, #0          ; Write mode flags (in R7) to PC

And to return to the original mode:

  TEQP    R6, #0          ; Write previous mode flags (in R6) back to PC

After changing the mode, you should perform a null operation to allow the registers to settle. The standard NOP is MOV R0, R0.

Personal tools