-----------------------------------------------------------------------------
7th April 1994
-----------------------------------------------------------------------------
Support Group Application Note
Number: 256
Issue: 1.00
Author:JR/JB
-----------------------------------------------------------------------------

Writing FIQ Code on RISC OS 3.5

-----------------------------------------------------------------------------
Applicable Hardware: 
                    The Risc PC range of computers

Related Application Notes:
                          None

-----------------------------------------------------------------------------
Copyright (C) 1994 Acorn Computers Limited 

Every effort has been made to ensure that the information in this leaflet is 
true and correct at the time of printing. However, the products described in
this leaflet are subject to continuous development and improvements and
Acorn Computers Limited reserves the right to change its specifications at
any time. Acorn Computers Limited cannot accept liability for any loss or
damage arising from the use of any information or particulars in this
leaflet. ACORN, ECONET and ARCHIMEDES are trademarks of Acorn Computers
Limited.
-----------------------------------------------------------------------------
Support Group
Acorn Computers Limited
Acorn House
Vision Park
Histon
Cambridge
CB4 4AE                                                  
-----------------------------------------------------------------------------

Introduction

For the majority of cases, Risc PC will run the ARM 610/700 in 26-bit mode 
for compatibility with previous ARM processors and the software written for 
them.  However, FIQ routines are entered with the ARM in 32-bit configuration.  
Programmers need to take the following notes into account when converting or 
writing FIQ code to run on a Risc PC.

The FIQ routine itself

Your FIQ routine will be entered in with the ARM in 32-bit configuration. In 
this configuration any instructions with the PC as destination and the S bit 
set will do the operation as specified and move the saved PSR to the current 
PSR. The effect this has is that these instructions:

TEQP, TSTP, CMPP, CMNP

become useless as the results get discarded and the mode drops back into 
whatever mode was interrupted to do the FIQ. These instructions have been 
defined to be not necessarily supported in future ARMs when running in 32-bit 
mode. These instructions:

<dataop><cc>S   pc, <reg>, <other>      (eg SUBS pc,lr,#4)
LDM<cc><dba>    <reg>, {<with PC>}^     (eg LDMIA {r0-r3 ,pc}^)

can only sensibly be used as returns from interrupt.

** do not enable interrupts in 32bit mode **

RISC OS 3.50 does not preserve the 32 bit status when returning from IRQs 
or other interrupts (eg SWI). This means that a piece of code executing in 
32 bit mode with IRQs enabled may, at any time, drop into 26 bit mode. 
Normally direct flag manipulation in 32-bit mode would be achieved using MRS 
and MSR instructions, but for your purposes you are unlikely to need or want 
to do this in a FIQ routine due to the amount of programming effort this 
would entail.

What does all this mean? 

- subtractions in 32 bit mode will tend to not preserve flags

- returning from FIQ would be achieved using SUBS pc,lr,#4


Installing your FIQ routine


The ARM processor prohibits direct poking of the processor vectors whilst 
in 26 bit mode. This is reasonable as it catches almost certainly OS-damaging 
26-bit code before it does any harm (in other words, it forces the authors 
of such code to rewrite the code in the light of the fact that this code will 
now be entered in 32-bit mode, not 26-bit mode as before).  As FIQ routines in 
RISC OS tend to be very small, they also tend to need no conversion for the 
ARM600.  Hence, to help with compatibility, RISC OS will detect a write to 
location &1C (the FIQ vector) and allow this to happen.  Other writes to the 
processor vectors are left as exceptions.  If you need rapid FIQ vector updates 
then your code must be altered to switch to 32 bit mode whilst doing the FIQ 
vector update.  ADFS does this and the relevant section of code is shown 
below :-

               ; Switch to _32 mode with IRQs and FIQs off
        ; Note must switch interrupts off before switching mode as
        ; there can be an interrupt after the msr instruction
        ; but before the following instruction.
        ; For non-32-bit processors this section reads:
        ; NOP
        ; Push "r1"
        ; ORR   r1, r1, #number
        ; NOP
        ; ORR   r1, r1, #number
        ; NOP

        mrs     AL, r1, CPSR_all
        Push    "r1"
        ORR     r1, r1, #I32_bit :OR: F32_bit
        msr     AL, CPSR_all, r1
        ORR     r1, r1, #2_10000
        msr     AL, CPSR_all, r1

        NOP
        MOV     LR, #FiqVector          ; FIQ vector address

        ; Copy handler
40      LDR     R1, [R0], #4            ; Get opcode
        TEQS    R1, #0                  ; All done?
        STRNE   R1, [LR], #4            ; No then copy to FIQ area
        BNE     %BT40                   ; And repeat

        ; And switch back - this bit reads as follows for non-32-bit processors:
        ; Pull  "r1"
        ; NOP
        Pull    "r1"
        msr     AL, CPSR_all, r1

*******  and from the headers: ******************

; ARM6 PSR transfer macros

; Condition code symbols

Cond_EQ *       0  :SHL: 28
Cond_NE *       1  :SHL: 28
Cond_CS *       2  :SHL: 28
Cond_HS * Cond_CS
Cond_CC *       3  :SHL: 28
Cond_LO * Cond_CC
Cond_MI *       4  :SHL: 28
Cond_PL *       5  :SHL: 28
Cond_VS *       6  :SHL: 28
Cond_VC *       7  :SHL: 28
Cond_HI *       8  :SHL: 28
Cond_LS *       9  :SHL: 28
Cond_GE *       10 :SHL: 28
Cond_LT *       11 :SHL: 28
Cond_GT *       12 :SHL: 28
Cond_LE *       13 :SHL: 28
Cond_AL *       14 :SHL: 28
Cond_   * Cond_AL
Cond_NV *       15 :SHL: 28

; New positions of I and F bits in 32-bit PSR

I32_bit *       1 :SHL: 7
F32_bit *       1 :SHL: 6
IF32_26Shift *  26-6

; Processor mode numbers

USR26_mode      *       2_00000
FIQ26_mode      *       2_00001
IRQ26_mode      *       2_00010
SVC26_mode      *       2_00011
USR32_mode      *       2_10000
FIQ32_mode      *       2_10001
IRQ32_mode      *       2_10010
SVC32_mode      *       2_10011
ABT32_mode      *       2_10111
UND32_mode      *       2_11011

; New register names

r13_abort       RN      13
r14_abort       RN      14
lr_abort        RN      14

r13_undef       RN      13
r14_undef       RN      14
lr_undef        RN      14

        MACRO
        mrs     $cond, $rd, $psrs
        LCLA    psrtype
psrtype SETA    -1
 [ "$psrs" = "CPSR" :LOR: "$psrs" = "CPSR_all"
psrtype SETA    0 :SHL: 22
 ]
 [ "$psrs" = "SPSR" :LOR: "$psrs" = "SPSR_all"
psrtype SETA    1 :SHL: 22
 ]
        ASSERT  psrtype <> -1
        ASSERT  $rd <> 15
        &       Cond_$cond :OR: 2_00000001000011110000000000000000 :OR: psrtype :OR: ($rd :SHL: 12)
        MEND

        MACRO
        msr     $cond, $psrl, $op2a, $op2b
        LCLA    psrtype
        LCLS    op2as
        LCLA    op
        LCLA    shift
psrtype SETA    -1
 [ "$psrl" = "CPSR" :LOR: "$psrl" = "CPSR_all"
psrtype SETA    (0:SHL:22) :OR: (1:SHL:19) :OR: (1:SHL:16)
 ]
 [ "$psrl" = "CPSR_flg"
psrtype SETA    (0:SHL:22) :OR: (1:SHL:19) :OR: (0:SHL:16)
 ]
 [ "$psrl" = "CPSR_ctl"
psrtype SETA    (0:SHL:22) :OR: (0:SHL:19) :OR: (1:SHL:16)
 ]
 [ "$psrl" = "SPSR" :LOR: "$psrl" = "SPSR_all"
psrtype SETA    (1:SHL:22) :OR: (1:SHL:19) :OR: (1:SHL:16)
 ]
 [ "$psrl" = "SPSR_flg"
psrtype SETA    (1:SHL:22) :OR: (1:SHL:19) :OR: (0:SHL:16)
 ]
 [ "$psrl" = "SPSR_ctl"
psrtype SETA    (1:SHL:22) :OR: (0:SHL:19) :OR: (1:SHL:16)
 ]
        ASSERT  psrtype <> -1
 [ ("$op2a" :LEFT: 1) = "#"
 ; Immediate operand

op2as   SETS    "$op2a" :RIGHT: ((:LEN: "$op2a")-1)
op      SETA    $op2as

  [ "$op2b" = ""
  ; Rotate not specified in immediate operand
shift   SETA    0
        WHILE   (op :AND: &FFFFFF00)<>0 :LAND: shift<16
op      SETA    ((op:SHR:30):AND:3):OR:(op:SHL:2)
shift   SETA    shift + 1
        WEND
        ASSERT  (op :AND: &FFFFFF00)=0
  |
  ; Rotate of immediate operand specified explicitly
        ASSERT  (($op2b):AND:&FFFFFFE1)=0
shift   SETA    ($opt2b):SHR:1
  ]
op      SETA    (shift :SHL: 8) :OR: op :OR: (1:SHL:25)
 |

 ; Not an immediate operand
  [ "$op2b" = ""
  ; Unshifted register
op      SETA    ($op2a) :OR: (0:SHL:25)
  |
        ! 1, "Shifted register not yet implemented in this macro!"
  ]
 ]
        &       Cond_$cond :OR: 2_00000001001000001111000000000000 :OR: op :OR: psrtype
        MEND

; SetMode newmode, reg1, regoldpsr
;
; Sets processor mode to constant value newmode
; using register reg1 as a temporary.
; If regoldpsr is specified, then this register
; on exit holds the old PSR before the mode change
; reg1 on exit always holds the new PSR after the mode change

        MACRO
        SetMode $newmode, $reg1, $regoldpsr
 [ "$regoldpsr"=""
        mrs     AL, $reg1, CPSR_all
        BIC     $reg1, $reg1, #&1F
        ORR     $reg1, $reg1, #$newmode
        msr     AL, CPSR_all, $reg1
 |
        mrs     AL, $regoldpsr, CPSR_all
        BIC     $reg1, $regoldpsr, #&1F
        ORR     $reg1, $reg1, #$newmode
        msr     AL, CPSR_all, $reg1
 ]
        MEND

