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:
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:
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 REM Short example to go into 32 bit mode, set the REM Z flag, and return to a conditional based upon REM this. REM REM From: http://www.heyrick.co.uk/armwiki/ REM 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? ON ERROR PRINT REPORT$+" at "+STR$(ERL/10) : END 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} .zunset EQUS " Z flag is not set (this ain't right!)" EQUB 0 ALIGN .zset EQUS " Z flag is set (as expected!)" EQUB 0 ALIGN .entering EQUS " About to enter 32 bit mode, cross your fingers!" EQUB 0 ALIGN .exiting EQUS " Returned to 26 bit mode. Phew!" EQUB 0 ALIGN ] NEXT PRINT "Executing code" CALL code% PRINT "Finished" END