From ARMwiki
Jump to: navigation, search
Processor registers, simple.
The ARM offers, ostensibly, sixteen registers, R0 to R15. All registers are 32 bit values.

Of these registers, only two have specific purposes.

  • R15 is the Program Counter.
  • R14 is set to the return address upon executing a BL instruction.

Various conventions exist which place other specific uses on registers (such as R13 being the Stack Pointer), however the ARM itself does not place any requirements onto other registers. Indeed, if you preserve (i.e. push to stack) the return address in R14, you can then use it as a general purpose register.

If you are writing user mode programs: this, plus the applicable convention, is about all you need to know.

The complete register set

The reality, however, is that the ARM offers thirty seven registers. Thirty of these are the general purpose registers, named R0 to R14. This is possible because which registers you see depends upon what processor mode you are in. Then there is R15, the Program Counter. The remaining six registers are status registers, and their visibility depends upon the current processor mode.

In general, R0 to R12 are the same, except when in FIQ mode where R8 upwards are shadow registers.
R13 and R14 change depending upon the processor mode, with the exception of the two lowest privileged (User and System). This register is banked across all modes (except System) to permit each mode to have a private stack set up.
R14 is banked similarly, so if a User mode program calls a System mode function when an Interrupt happens that is itself interrupted by an FIQ... all would nominally place their return addresses into R14, however since in each case it is the banked R14, things don't fall apart.
For FIQ handling, R8 upwards are private so registers can be set up and maintained across FIQs and processing can begin without needing to load registers from memory.

The status register CPSR is the same across all modes, however all modes except User and System introduce a SPSR status register which is specific to that mode.

A diagram should make this clearer:

Processor registers, complete.

APCS register naming

The Arm Procedure Call Standard defines names for the various registers, as follows:

Register APCS name Purpose
R0 a1 Argument registers, passing values, don't need to be preserved,
results are usually returned in R0
R1 a2
R2 a3
R3 a4
R4 v1 Variable registers, used internally by functions, must be preserved if used
R5 v2
R6 v3
R7 v4
R8 v5
R9 v6
R10 sl Stack Limit / stack chunk handle
R11 fp Frame Pointer, contains zero, or points to stack backtrace structure
R12 ip Procedure entry temporary workspace
R13 sp Stack Pointer, fully descending stack, points to lowest free word
R14 lr Link Register, return address at function exit
R15 pc Program Counter

Upon function return (by pushing lr into pc), v1-v6, sl, fp, and sp must be the same as they were on entry.

The original APCS standard is defined in the RISC OS Programmers Reference Manuals on p4-399.

The current APCS standard is defined by ARM Ltd in this PDF document.

26 bit operation

The original ARMs (ARM2, ARM3) were simpler, and supported only four processor modes with correspondingly fewer registers.

The "26 bit" in the name is defined from the Program Counter being only 26 bits wide, thus permitting R15 to hold also the status flags. While this system worked well, and allowed one simple instruction to restore all stacked registers and exit the function, it was greatly limited by the fact that 26 bits only permits addressing of 64MiB. When the ARM was designed in the late '80s, this was a massive amount of memory - in fact, no MEMC (memory controller chip)) is capable of addressing more than 4MiB, and since using multiple MEMCs was often quirky, the majority of Archimedes machines contain no more than 4MiB, and I have not seen any with more than 8MiB. The maximum possible is 16MiB (with four MEMCs), both due to hardware constraints and also due to the MEMC's view of the world splits the 64MiB memory allocation into 32MiB of logically-mapped memory, 16MiB of physically mapped memory, and 16MiB of 'other' (I/O, and Read:ROM, Write:VIDC/MEMC/AddressTranslation).

For RISC OS machines of Acorn era, the register set is as follows:

Processor registers, older 26 bit.

Because the RiscPC processors work in 26 bit and 32 bit mode (to allow more than 64MiB to be used), things are somewhat more complicated. However as an end user, you can assume that RISC OS versions lower than RISC OS 5 will appear to function in a 26 bit way (with individual tasks limited in the amount of memory that can be addressed). You will, however, need to read the relevant Technical Reference Manual if you plan to write IRQ or FIQ mode.

  • RISC OS 5 (Iyonix, Beagle Board, etc) works in a 32 bit environment, so this section does not apply. Likewise, I believe RISC OS 6 to be similar. Note that RISC OS 6 is an evolution of RISC OS Ltd's RISC OS 4 and is a different branch and thus unrelated to Castle's RISC OS 5. It is RISC OS 5 that has been open sourced.

By way of example, here is a quirky little program to enter 32 bit mode (under a mostly 26 bit RISC OS), set the Z flag, return back to 26 bit mode, and test if the Z flag is set. This should be run on a RiscPC, A7000(+), Mico, etc running RISC OS version 3.5, 3.6, 3.7 or one of the RISC OS 4 Select/Adjust family. It will not work on the Iyonix or similar/later.

You will need Darren Salt's assembler extension "ExtBasAsm", and while you are there you might like to pick up "DebuggerPlus".

  REM >32bittest
  REM Short example to go into 32 bit mode, set the
  REM Z flag, and return to a conditional based upon
  REM this.
  REM From:
  REM We need to ensure the required hardware is present...
  emsg$ = "Sorry, you'll need a RiscPC or later, with a 32 bit capable "
  emsg$+= "processor in order to use this software."
  ON ERROR SYS "OS_PrettyPrint", emsg$ : PRINT : END
  SYS "OS_Memory", 6
  REM ?? how to detect and fault too-late machines ie Iyonix?
  DIM code% 1024
  PRINT "Assembling code"
  FOR l% = 0 TO 2 STEP 2
    P% = code%
    [  OPT l%
       EXT 1                      ; Enable ExtBASICAsm
       \ Preserve our R14 and then enter SVC mode
       STMFD   R13!, {R14}
       SWI     "OS_EnterOS"
       ADR     R0, entering
       SWI     "OS_Write0"
       SWI     "OS_NewLine"
       \ Turn off all interrupts, because RISC OS won't be expecting 32 bit mode!
       SWI     "OS_IntOff"
       \ User32 code
       MOV     R0, #%10011
       MSR     CPSR_all, R0       ; Select CPSR mode, clear all the bits
       MOV     R0, R0
       MRS     R0, CPSR_all       ; Read CPSR
       BIC     R0, R0, #&F0000000 ; Clear the flag bits
       BIC     R0, R0, #&1F       ; Clear the mode bits
       ORR     R0, R0, #1<<30     ; Set the Z flag
       ORR     R0, R0, #%11       ; Set SVC26 mode
       MSR     CPSR_all, R0       ; Do it!
       MOV     R0, R0
       \ Turn interrupts back on again.
       SWI     "OS_IntOn"
       ADR     R0, exiting
       SWI     "OS_Write0"
       SWI     "OS_NewLine"
       \ If Z set, print Z set else print Z unset
       ADR     R0, zunset
       ADREQ   R0, zset
       SWI     "OS_Write0"
       SWI     "OS_NewLine"
       \ Return to USR mode
       BIC     R14, PC, #3
       TEQP    R14, #0
       MOV     R0, R0
       \ And get out of here.
       LDMFD   R13!, {PC}
       EQUS    "  Z flag is not set (this ain't right!)"
       EQUB    0
       EQUS    "  Z flag is set (as expected!)"
       EQUB    0
       EQUS    "  About to enter 32 bit mode, cross your fingers!"
       EQUB    0
       EQUS    "  Returned to 26 bit mode. Phew!"
       EQUB    0
  PRINT "Executing code"
  CALL code%
  PRINT "Finished"
Personal tools