Program Status Register
|
Go here for details of R15/PSR in 32-bit mode.
Bit 31 30 29 28 27 26 25------------2 1 0 N Z C V I F Program Counter S1 S0The flags mean:
N Negative Set if result is negative Z Zero Set if result is zero C Carry Set if carry occurs O Overflow Set if overflow occurs I IRQ Interrupt disable F FIQ Fast Interrupt disable S1 and S0 are processor mode flags: S1 S0 Mode 0 0 USR - User mode 0 1 FIQ - Fast Interrupt mode 1 0 IRQ - Interrupt mode 1 1 SVC - Supervisor mode
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
(R15 and PC mean the same thing to the BASIC assembler)
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 example is not 32-bit compliant. Refer to the section below for how to read the current mode in the 32-bit environment.
new_state = old_state EOR (1 << 28)
could be pseudocode for changing the state of the oVerflow flag. But we cannot do a simple
EORS
operation as the pipeline would cause the following two instructions to be
skipped.TEQ
does a pretend EOR (the results are not
stored anywhere). 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.
This example is not 32-bit compliant. Refer to the section
below for how to change modes in the 32-bit environment.
This can be expanded to change processor mode. For example, to enter SVC mode you would:
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 PCAnd to return to the original mode:
TEQP R6, #0 ; Write previous mode flags (in R6) to PC
After changing the mode, you should perform a null operation to allow the registers to settle.
Something like MOV R0, R0
should be okay. The use of NV suffixed
instructions has been deprecated.
Branch if Carry Set
, the ARM takes this a logical stage further
to mean XXX if carry set
- where XXX is just about anything.
By way of example, here is a list of branch instructions understood by the Intel 8086 processor:
JA Jump if Above JAE Jump if Above or Equal JB Jump if Below JBE Jump if Below or Equal JC Jump if Carry JCXZ Jump if CX Zero (CX is a register that can be used for loop counts) JE Jump if Equal JG Jump if Greater than JGE Jump if Greater than or Equal JL Jump if Less than JLE Jump if Less Than or Equal JMP JuMP (unconditional) JNA Jump if Not Above JNAE Jump if Not Above or Equal JNB Jump if Not Below JNBE Jump if Not Below or Equal JNC Jump if No Carry JNE Jump if Not Equal JNG Jump if Not Greater than JNGE Jump if Not Greater than or Equal JNL Jump if Not Less than JNLE Jump if Not Less than or Equal JNO Jump if Not Overflow JNP Jump if Not Parity JNS Jump if Not Sign JNZ Jump if Not Zero JO Jump if Overflow JP Jump if Parity JPE Jump if Parity Even JPO Jump if Parity Odd JS Jump if Sign JZ Jump if Zero And the 80386 added: JCXZ Jump if CX Zero JECXZ Jump if ECX Zero Not forgetting the closely related: LOOP Decrement CX and loop if not zero LOOPE/LOOPZ Decrement (E)CX and loop if not zero and ZF = 1 LOOPNE/LOOPNZ Decrement (E)CX and loop if not zero and ZF = 0 Added caveat: JZ far_address is illegal, you must use: JNZ skip_jump JMP far_address skip_jump: which messes up the pipeline as only JMP can jump to a 'far' address.
And by contrast, the ARM processor offers a whopping...uh...
B Branch BL Branch with Link
But the ARM is not limited by this seemingly inflexible approach due to conditional execution which offers you:
BEQ Branch if EQual BNE Branch if Not Equal BVS Branch if oVerflow Set BVC Branch if oVerflow Clear BHI Branch if HIgher BLS Branch if Lower or the Same BPL Branch if PLus BMI Branch if MInus BCS Branch if Carry Set BCC Branch if Carry Clear BGE Branch if Greater than or Equal BGT Branch if Greater Than BLE Branch if Less than or Equal BLT Branch if Less Than BLEQ Branch with Link if EQual .... BLLT Branch with Link if Less ThanThere are two more codes,
AL
- ALways, the default condition so doesn't need to be specified
NV
- NeVer, so very useful. You should not use this code anyway...
Here follows a list of available conditional codes:
S
, when applied
to an instruction, causes the status flags to be updated. This does not happen
automatically - except for those instructions whose purpose is to set the status.
For example:
ADD R0, R0, R1 ADDS R0, R0, R1 ADDEQS R0, R0, R1The first example shows us a basic addition (adding the value of R1 to R0) which does not affect the status registers.
The second example shows us the same addition, only this time it will cause the status registers to be updated.
The last example shows us the addition again, updating the status registers. The difference here is that it is a conditional instruction. It will only be executed if the result of a previous operation was EQual (if the Z flag is set).
Here is an example of conditional execution at work. You want to compare register zero with the contents of something stored in register ten. If not equal to R10, then call a software interrupt, increment and branch back to do it again. Otherwise clear R10 and return to a calling piece of code (whose address is stored in R14).
\ An example of conditional execution .loop ; Mark the loop start position CMP R0, R10 ; Compare R0 with R10 SWINE &40017 ; Not equal: Call SWI &40017 ADDNE R0, R0, #1 ; Add 1 to R0 BNE loop ; Branch to 'loop' MOV R10, #0 ; Equal : Set R10 to zero LDMFD R13!, {R0-R12,PC} ; Return to callerNotes:
LDMFD
before, it loads multiple registers from the stack.
In this example, we are loading R0 to R12 and R14 from a fully descending stack. Refer to
str.html for more on register loading and storing.
LDMFD R13!, {R0-R12,R14}
MOV PC,
R14
MOV
statement.
The allocation of the bits within the CPSR (and the SPSR registers to which it is saved) is:
31 30 29 28 --- 7 6 - 4 3 2 1 0 N Z C V I F M4 M3 M2 M1 M0 0 0 0 0 0 User26 mode 0 0 0 0 1 FIQ26 mode 0 0 0 1 0 IRQ26 mode 0 0 0 1 1 SVC26 mode 1 0 0 0 0 User mode 1 0 0 0 1 FIQ mode 1 0 0 1 0 IRQ mode 1 0 0 1 1 SVC mode 1 0 1 1 1 ABT mode 1 1 0 1 1 UND mode
Typically, the processor will be operating in User26, FIQ26, IRQ26 or SVC26 mode. It is possible
to enter a 32 bit mode, but extreme care must be taken. RISC OS won't expect it, and will sulk
greatly if it finds itself in it!
(except RISC OS 5 which works totally in 32bit mode - and you cannot enter 26bit as the processor
doesn't have that anymore...)
You cannot use MOVS PC, R14
or LDMFD R13!, {registers, PC}^
in
32 bit code. Neither can you use ORRS PC, R14, #1<<28
to set the V flag.
All of this is now possible using MRS
and MSR
.
Copy a register into the PSR MSR CPSR, R0 ; Copy R0 into CPSR MSR SPSR, R0 ; Copy R0 into SPSR MSR CPSR_flg, R0 ; Copy flag bits of R0 into CPSR MSR CPSR_flg, #1<<28 ; Copy flag bits (immediate) into CPSR
Copy the PSR into a register MRS R0, CPSR ; Copy CPSR into R0 MRS R0, SPSR ; Copy SPSR into R0You have two PSRs -
CPSR
which is the Current Program Status Register and
SPSR
which is the Saved Program Status Register (the previous processor mode's PSR).
_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!
To set the V flag:
MSR CPSR_flg, #&10000000This sets the V flag and doesn't affect the control bits.
Here, for your delectation, is a way to set the V flag on any ARM processor:
CMP R0, #1<<31 CMNVC R0, #1<<31Clever, huh?
To change mode:
MRS R0, CPSR_all ; Copy the PSR BIC R0, R0, #&1F ; Clear the mode bits ORR R0, R0, #new_mode ; Set bits for new mode MSR CPSR_all, R0 ; write PSR back, changing mode
IMPORTANT: Your assembler may complain that 'CPSR_all' is not recognised, or that you need some weird syntax such as 'CPSR_nvf'; refer to the documentation supplied with your assembler to figure out exactly what it's MSR/MRS syntax is...
What we shall do now is to enter the SVC32 mode and set the Z flag. Then we'll return to SVC26
mode and 'test' if the Z flag is set.
RISC OS will not be expecting to find itself in 32 bit mode, so we'll disable all interrupts and
keep them that way. Though this code should execute fast enough, and we don't want to take any
chances...
Before we begin, I just want to say that you will need Darren Salt's
ExtBASICasm
module to allow BASIC to assemble the MSR/MRS instructions. It is
entirely possible to insert these instructions using EQUD, but it is messy and hard to follow.
If you use !Zap, you'll find it as !Zap.Code.Extensions.ExtAsm, in the v1.40 distribution.
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 Downloaded from http://www.heyrick.co.uk/assembler/ 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 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
You might have got the idea that 32 bit code is not terribly useful. Under all older versions of RISC OS, this is true. In fact, as far as I'm aware, the only thing that 32 bit mode will give you on an older machine (i.e. RiscPC/IBX-100/Mico...) is:
HOWEVER, if you intend to write software that will run on the Iyonix (and we'll have Ron
after you if you refuse!), you will need to take 32bit into account.
Actually, it is pretty simple. I have written a number of modules (in assembler) that run okay on
an Iyonix that I don't have... How?
MOVS PC, Rxx
LDMFD R13!, {Rxx-Rxx, PC}^
[ {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 26bit or 32bit...
The original version of this document, and the original ARM assembler guide includes...
To conclude:
The change to 32bit may not sound like much, but given that all references to
amending/altering the PSR bits in PC have to be changed to refer to a separate PSR not in R15,
it suddenly becomes a much bigger issue. Also, you can no longer restore the PSR and branch back
to the caller in one instruction, as these are now two separate operations. For that reason,
code must be rewritten.