Program Status Register
&
conditional execution

 

Go here for details of R15/PSR in 32-bit mode.

Register 15 (26-bit mode):

R15 is built up as follows:
  Bit  31  30  29  28  27  26  25------------2  1  0

       N   Z   C   V   I   F   Program Counter  S1 S0
The 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.
Download example: currmode.basic

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,
  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.
But don't worry. The instruction 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 PC
And to return to the original mode:
   TEQP    R6, #0          ; Write previous mode flags (in R6) to PC
Download example: setmode.basic

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.

 

 

Conditional execution:

A very special feature of the ARM processor is its conditional execution. We are not talking your basic 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 Than
There are two more codes, The crunch comes, however, when you realise that all of the Bxx instructions are actually the same instruction. Then you will think, if you can do all that to a branch instruction, can it be done to, say, a register load instruction?
The answer is yes.

 

 

Here follows a list of available conditional codes:

EQ : Equal
If the Z flag is set after a comparison.

NE : Not Equal
If the Z flag is clear after a comparison.

VS : Overflow Set
If the V flag is set after an arithmetical operation, the result of which will not fit into a 32bit destination register.

VC : Overflow Clear
If the V flag is clear, the reverse of VS.

HI : Higher Than (unsigned)
If after a comparison the C flag is set AND the Z flag is clear.

LS : Lower Than or Same (unsigned)
If after a comparison the C flag is clear OR the Z flag is set.

PL : Plus
If the N flag is clear after an arithmetical operation. For the purposes of defining 'plus', zero is positive because it isn't negative...

MI : Minus
If the N flag is set after an arithmetical operation.

CS : Carry Set
Set if the C flag is set after an arithmetical operation OR a shift operation, the result of which cannot be represented in 32bits. You can think of the C flag as the 33rd bit of the result.

CC : Carry Clear
The reverse of CS.

GE : Greater Than or Equal (signed)
If after a comparison...
the N flag is set AND the V flag is set
or...
the N flag is clear AND the V flag is clear.

GT : Greater Than (signed)
If after a comparison...
the N flag is set AND the V flag is set
or...
the N flag is clear AND the V flag is clear
and...
the Z flag is clear.

LE : Less Than or Equal To (signed)
If after a comparison...
the N flag is set AND the V flag is clear
or...
the N flag is clear AND the V flag is set
and...
the Z flag is set.

LT : Less Than (signed)
If after a comparison...
the N flag is set AND the V flag is clear
or...
the N flag is clear AND the V flag is set.

AL : Always
The default condition, so does not need to be explicity stated.

NV : Never
Not particularly useful, it states that the instruction should never be executed. A kind of Poor Mans' NOP.
NV was included for completeness (as the reverse of AL), but you should not use it in your own code.
There is a final conditional code which works in the reverse way. 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, R1
The 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 caller
Notes:

 

 

The 32 bit PSR

As described in this document, processors after the ARM 3 provide a 32 bit addressing space by moving the PSR out of R15 and giving R15 a full 32 bits in which to store the address of the current position.
Currently, RISC OS works in 26 bit mode, except for a few special cases which is unlikely to be encountered.
The 32 bit mode is important because 26 bits (as in the old PSR) restricts the maximum amount of addressable memory per-application to 28Mb. That is why you can't drag the Next slot beyond 28Mb irrespective of how much memory you have installed.

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 R0
You 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).
Each privileged mode has its own PSR, so the total available selection of PSR is: It appears as if you cannot explicitly specify to save the current PSR in, say, SPSR_fiq. Instead, you should change to FIQ mode and then save to SPSR. In other words, you can only alter the SPSR of the mode you are in.
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!

To set the V flag:

  MSR     CPSR_flg, #&10000000
This 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<<31
Clever, 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
Download this example

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?

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 Iyonix; 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 26bit or 32bit...

 

The original version of this document, and the original ARM assembler guide includes...

This is incorrect. The extended multiply can be used from 26 bit mode; it's how the MP3 decoders use it!

 

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.

 

 


Return to assembler index
Copyright © 2004 Richard Murray