; Copyright 1999 Pace Micro Technology plc
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
        SUBT    Generic 32-bit CPU Specific Definitions

OldOpt  SETA    {OPT}
        OPT     OptNoList+OptNoP1List

 [ :LNOT: :DEF: Included_Hdr_CPU_Generic32
        GBLL    Included_Hdr_CPU_Generic32
Included_Hdr_CPU_Generic32 SETL {TRUE}

; ***********************************
; ***    C h a n g e   L i s t    ***
; ***********************************
;
; Date       Name          Description
; ----       ----          -----------
; 05-Nov-99  KBracey       Moved from ARM600.
;                          32-bit versions of Generic26 macros created.

 [ :LNOT: :DEF: Included_Hdr_Machine_Machine
        GET     Hdr:Machine.<Machine>
 ]

; 32-bit PSR transfer macros

; New positions of bits in 32-bit PSR

Q32_bit *       1 :SHL: 27
IT32_bits *     &0600FC00
J32_bit *       1 :SHL: 24
GE3_bit *       1 :SHL: 19
GE2_bit *       1 :SHL: 18
GE1_bit *       1 :SHL: 17
GE0_bit *       1 :SHL: 16
GE32_bits *     2_1111 :SHL: 16
E32_bit *       1 :SHL: 9
A32_bit *       1 :SHL: 8
I32_bit *       1 :SHL: 7
F32_bit *       1 :SHL: 6
T32_bit *       1 :SHL: 5
M32_bits *      2_11111

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
MON32_mode      *       2_10110
ABT32_mode      *       2_10111
HYP32_mode      *       2_11010
UND32_mode      *       2_11011
SYS32_mode      *       2_11111

; New register names

r13_mon         RN      13
r14_mon         RN      14
lr_mon          RN      14

r13_abort       RN      13
r14_abort       RN      14
lr_abort        RN      14

r13_abt         RN      13
r14_abt         RN      14
lr_abt          RN      14

r13_undef       RN      13
r14_undef       RN      14
lr_undef        RN      14

r13_und         RN      13
r14_und         RN      14
lr_und          RN      14

; STREX results

STREX_succeeded * 0
STREX_failed    * 1

        GBLA    CPU32_bits
        GBLA    CPU32_set
        GBLA    CPU32_clr

; ***************************************************
; ***  IFto32 - Convert I/F/IF/FI string to       ***
; ***  I32_bit+F32_bit flags in variable psr32    ***
; ***************************************************
        MACRO
$psr32  IFto32 $if
        LCLS   upper
upper   SETS   :UPPERCASE: "$if"
      [ "$upper" = "I"
$psr32  SETA   I32_bit
      ELIF "$upper" = "F"
$psr32  SETA   F32_bit
      ELIF "$upper" = "IF" :LOR: "$upper" = "FI"
$psr32  SETA   I32_bit+F32_bit
      |
        ! 1, "Unsupported interrupt flags"
      ]
        MEND

 [ :LNOT: No32bitCode

        ; 32 bit versions of the macros in Generic26

; ***************************************************
; ***  PSRto32 - Convert a PSR constant to a      ***
; ***  32-bit PSR value in variable psr32         ***
; ***  Shifts I and F into their 32-bit positions ***
; ***************************************************
        MACRO
$psr32  PSRto32 $psr
        [ (($psr) :AND: (:NOT: ARM_CC_Mask)) <> 0
	! 1, "Illegal PSR bits"
        ]
$psr32  SETA    (($psr) :AND: :NOT: (I_bit:OR:F_bit)) :OR: ((($psr) :AND: (I_bit:OR:F_bit)) :SHR: IF32_26Shift)
        MEND

; ************************************************
; ***  CLC - Clear carry flag - will set nzcv  ***
; ************************************************
        MACRO
$label  CLC     $cond
    [ NoARMv3
$label
      [ "$cond"<>"" :LAND: "$cond"<>"AL"
        LCLS    rcc
rcc     SETS    :REVERSE_CC:"$cond"
        B$rcc   %FT01                   ; Go round when $cond-itional
      ]
        CMN     pc, #0                  ; CLC in 26 bit modes
        MSR     CPSR_f, #0              ; ARMv3+ does CLC, NOP on ARMv2
01
    |
$label  MSR$cond CPSR_f, #0
    ]
        MEND

; ***********************************************
; ***  CLRPSR - Clear bits in PSR from the    ***
; ***  mask in $bits, using register $regtmp  ***
; ***********************************************
        MACRO
$label  CLRPSR  $bits, $regtmp, $cond, $oldpsr
$label  SCPSR   0, $bits, $regtmp, $cond, $oldpsr
        MEND

; **************************************************
; *** CLRV - Clear overflow flag - will set nzCv ***
; **************************************************
        MACRO
$label  CLRV    $cond
    [ NoARMv3
$label
      [ "$cond"<>"" :LAND: "$cond"<>"AL"
        LCLS    rcc
rcc     SETS    :REVERSE_CC:"$cond"
        B$rcc   %FT01                   ; Go round when $cond-itional
      ]
        CMP     pc, #0                  ; CLRV in 26 bit modes
        MSR     CPSR_f, #C_bit          ; ARMv3+ does CLRV, NOP on ARMv2
01
    |
$label  MSR$cond CPSR_f, #C_bit
    ]
        MEND

; **********************************************************************************
; ***  PHPSEI - Disable IRQs, saving an old interrupt state indicator in a       ***
; ***  register, default R14.  Note that this code preserves the C and V flags.  ***
; ***  Don't have to supply regtmp, but if you do, we save an instruction.       ***
; **********************************************************************************
        MACRO
$label  PHPSEI  $register=R14, $regtmp
        LCLS    usereg
  [ "$register" = ""
usereg  SETS    "R14"
  |
usereg  SETS    "$register"
  ]
$label
      [ NoARMv3
        TEQ     pc, pc                         ; Can only PHPSEI in non user mode
        BEQ     %FT01
        MOV     $usereg, #I_bit
        TST     $usereg, PC                    ; is I_bit set ?
        TEQEQP  $usereg, PC                    ; no, then set it (and $register = I_bit)
        MOVNE   $usereg, #0                    ; yes, then leave alone (and $register = 0)
        B       %FT02
01
      ]
  [ :LNOT: NoARMv6
        ; CPS is quicker than MSR
        ; If there's a high probability of IRQs already being disabled, we can
        ; save even more time by branching over the CPS. But for now assume IRQs
        ; will mostly be on.
        MRS     $usereg, CPSR
        CPSID   i
  ELIF "$regtmp" = "" :LOR: StrongARM_MSR_bug
        MRS     $usereg, CPSR
        TST     $usereg, #I32_bit              ; is I32_bit set?
        ORREQ   $usereg, $usereg, #I32_bit     ; no, then set it
        mymsr   EQ, CPSR_c, $usereg, , safe
        BICEQ   $usereg, $usereg, #I32_bit     ; $register contains original PSR
  |
        MRS     $usereg, CPSR
        TST     $usereg, #I32_bit              ; is I32_bit set?
        ORREQ   $regtmp, $usereg, #I32_bit     ; no, then set it
        mymsr   EQ, CPSR_c, $regtmp            ; $register contains original PSR
  ]
02
        MEND

; **************************************************************************
; ***  PLP - Restore IRQ state from the indicator in a register (set up  ***
; ***  by PHPSEI).  Note that this code preserves the C and V flags.     ***
; **************************************************************************
        MACRO
$label  PLP     $register=R14
        LCLS    usereg
  [ "$register" = ""
usereg  SETS    "R14"
  |
usereg  SETS    "$register"
  ]
      [ NoARMv3
$label  TEQ     pc, pc                         ; Can only PLP in non user mode
        mymsr   EQ, CPSR_c, $usereg, , unsafe  ; Is a NOP pre ARMv3
        TEQNEP  $usereg, PC
        NOP
      |
$label  MSR     CPSR_c, $usereg
      ]
        MEND

; ******************
; ***  RETURNVC  ***
; ******************
        MACRO
$label  RETURNVC  $cond
$label
 [ NoARMv3
$label
      [ "$cond"<>"" :LAND: "$cond"<>"AL"
        LCLS    rcc
rcc     SETS    :REVERSE_CC:"$cond"
        B$rcc   %FT01                   ; Go round when $cond-itional
      ]
        TEQ     r0, r0                  ; Incase in user mode
        TEQ     pc, pc
        BICNES  pc, lr, #V_bit
        MSR     CPSR_f, #0
        MOV     pc, lr
01
 |
  [ "$cond" = "NV"
        ! 1, "Deprecated use of NV condition code in RETURNVC"
  ]
  [ "$cond" = "VC"
        MOVVC   pc, lr
  ]
  [ "$cond" = "NE" :LOR: "$cond"="CC" :LOR: "$cond"="LO" :LOR: "$cond"="PL" :LOR: "$cond"="LS" :LOR: "$cond"="GE" :LOR: "$cond"="GT" :LOR: "$cond"="AL" :LOR: "$cond"=""
        MSR$cond CPSR_f, #0
        MOV$cond pc, lr
  ]
  [ "$cond" = "EQ" :LOR: "$cond"="CS" :LOR: "$cond"="HS" :LOR: "$cond"="MI" :LOR: "$cond"="LT" :LOR: "$cond"="LE"
        MSR$cond CPSR_f, #N_bit + Z_bit + C_bit
        MOV$cond pc, lr
  ]
  [ "$cond" = "HI"
        MSR$cond CPSR_f, #C_bit
        MOV$cond pc, lr
  ]
  [ "$cond" = "VS"
        BVC     %FT01                   ; Skip on opposite condition
        MSR     CPSR_f, #0
        MOV     pc, lr
01
  ]
 ]
        MEND

; ******************
; ***  RETURNVS  ***
; ******************
        MACRO
$label  RETURNVS  $cond
$label
 [ NoARMv3
$label
      [ "$cond"<>"" :LAND: "$cond"<>"AL"
        LCLS    rcc
rcc     SETS    :REVERSE_CC:"$cond"
        B$rcc   %FT01                   ; Go round when $cond-itional
      ]
        TEQ     r0, r0                  ; Incase in user mode
        TEQ     pc, pc
        ORRNES  pc, lr, #V_bit
        MSR     CPSR_f, #V_bit
        MOV     pc, lr
01
 |
  [ "$cond" = "NV"
        ! 1, "Deprecated use of NV condition code in RETURNVC"
  ]
  [ "$cond" = "VS"
        MOVVS   pc, lr
  ]
  [ "$cond" = "NE" :LOR: "$cond"="CC" :LOR: "$cond"="LO" :LOR: "$cond"="PL" :LOR: "$cond"="LS" :LOR: "$cond"="AL" :LOR: "$cond"=""
        MSR$cond CPSR_f, #V_bit         ; Condition is still satisfied
        MOV$cond pc, lr
  ]
  [ "$cond" = "EQ" :LOR: "$cond"="CS" :LOR: "$cond"="HS" :LOR: "$cond"="MI" :LOR: "$cond"="GE" :LOR: "$cond"="LE"
        MSR$cond CPSR_f, #N_bit + Z_bit + C_bit + V_bit
        MOV$cond pc, lr
  ]
  [ "$cond" = "HI" :LOR: "$cond"="LT"
        MSR$cond CPSR_f, #C_bit + V_bit
        MOV$cond pc, lr
  ]
  [ "$cond" = "GT"
        MSR$cond CPSR_f, #N_bit + V_bit
        MOV$cond pc, lr
  ]
  [ "$cond" = "VC"
        BVS     %FT01                   ; Skip on opposite condition
        MSR     CPSR_f, #V_bit
        MOV     pc, lr
01
  ]
 ]
        MEND

; ****************************************************
; ***  SCPSR - Set and clear bits in PSR from the  ***
; ***  masks $set, $clr, using register $regtmp    ***
; ****************************************************
        MACRO
$label  SCPSR   $set, $clr, $regtmp, $cond, $oldpsr
        LCLS    srcreg
CPU32_set PSRto32 $set
CPU32_clr PSRto32 $clr
      [ :LNOT: No32bitCode :LAND: ((CPU32_set :OR: CPU32_clr) :AND: 3) <> 0
        ; If 32bit modes are supported and we're changing mode, make sure we clear bits 2 and 3 of the mode so we can safely switch from ABT/UND/SYS to SVC/IRQ/etc.
CPU32_clr SETA CPU32_clr :OR: 12
      ]
        [ "$oldpsr"=""
srcreg  SETS    "$regtmp"
        |
srcreg  SETS    "$oldpsr"
        ]
        [ (($set) :AND: ($clr)) <> 0
        ! 1, "Attempt to simultaneously set and clear a bit in SCPSR"
        ]
 [ NoARMv3
$label
      [ "$cond"<>"" :LAND: "$cond"<>"AL"
        LCLS    rcc
rcc     SETS    :REVERSE_CC:"$cond"
        B$rcc   %FT02                   ; Go round when $cond-itional
      ]
        MOV     $srcreg, pc             ; Snapshot 26-bit PSR
        MRS     $srcreg, CPSR           ; Snapshot 32-bit PSR, or NOP pre ARMv3
        TEQ     r0, r0                  ; Set Z
        MSR     CPSR_f, #0              ; Clear Z if ARMv3 or later
        BNE     %FT01
      [ (($set) :OR: ($clr)) = ARM_CC_Mask
        TEQP    pc, #$set               ; All change, so skip the clear operation
      |
        ORR     $regtmp, $srcreg, #($set) :OR: ($clr)
        TEQP    $regtmp, #$clr
      ]
        B       %FT02
01
      [ (CPU32_set :AND: &F0000000) <> 0 :LAND: (CPU32_set :AND: &F0) <> 0
        ORR     $regtmp, $srcreg, #CPU32_set :AND: &F0000000
        ORR     $regtmp, $regtmp, #CPU32_set :AND: &0FFFFFFF
srcreg  SETS "$regtmp"
      |
        [ CPU32_set <> 0
        ORR     $regtmp, $srcreg, #CPU32_set
srcreg  SETS "$regtmp"
        ]
      ]
      [ (CPU32_clr :AND: &F0000000) <> 0 :LAND: (CPU32_clr :AND: &F0) <> 0
        BIC     $regtmp, $srcreg, #CPU32_clr :AND: &F0000000
        BIC     $regtmp, $regtmp, #CPU32_clr :AND: &0FFFFFFF
srcreg  SETS "$regtmp"
      |
        [ CPU32_clr <> 0
        BIC     $regtmp, $srcreg, #CPU32_clr
srcreg  SETS "$regtmp"
        ]
      ]
        somemsr AL, CPSR, $srcreg, CPU32_set:OR:CPU32_clr:OR:&F0000000
02
 |
$label  MRS$cond $srcreg, CPSR
 [ (CPU32_set :AND: &F0000000) <> 0 :LAND: (CPU32_set :AND: &F0) <> 0
        ORR$cond $regtmp, $srcreg, #CPU32_set :AND: &F0000000
        ORR$cond $regtmp, $regtmp, #CPU32_set :AND: &0FFFFFFF
srcreg  SETS "$regtmp"
 |
 [ CPU32_set <> 0
        ORR$cond $regtmp, $srcreg, #CPU32_set
srcreg  SETS "$regtmp"
 ]
 ]
 [ (CPU32_clr :AND: &F0000000) <> 0 :LAND: (CPU32_clr :AND: &F0) <> 0
        BIC$cond $regtmp, $srcreg, #CPU32_clr :AND: &F0000000
        BIC$cond $regtmp, $regtmp, #CPU32_clr :AND: &0FFFFFFF
srcreg  SETS "$regtmp"
 |
 [ CPU32_clr <> 0
        BIC$cond $regtmp, $srcreg, #CPU32_clr
srcreg  SETS "$regtmp"
 ]
 ]
        somemsr  $cond, CPSR,$srcreg, CPU32_set:OR:CPU32_clr, unsafe
 ]
        MEND

; ****************************************************
; ***  SavePSR - Save the PSR in a register, to be ***
; ***  restored later using RestPSR                ***
; ****************************************************
        MACRO
$label  SavePSR $reg, $cond
      [ NoARMv3
$label  MOV$cond $reg, pc               ; Benign on ARMv3+
        MRS$cond $reg, CPSR             ; NOP prior to ARMv3
      |
$label  MRS$cond $reg, CPSR
      ]
        MEND

; ****************************************************
; ***  RestPSR - Restore the PSR from a register   ***
; ***  set up by SavePSR                           ***
; ***  $fields may be set to "f" if the PSR fields ***
; ***  c,x,s do not need restoring, which will     ***
; ***  save a few cycles on newer ARMs (but the    ***
; ***  No32bitCode version of the macro will set   ***
; ***  the c field anyway). Values other than "f", ***
; ***  "cf", "fc" and unset are deprecated for     ***
; ***  compatibility with No32bitCode.             ***
; ****************************************************
        MACRO
$label  RestPSR $reg, $cond, $fields
        LCLS    field
  [ "$fields"="" :LOR: "$fields"="cf" :LOR: "$fields"="fc"
field   SETS    "cf"
  |
  [ "$fields"="f"
field   SETS    "f"
  |
        !       0, "Unpredictable behaviour due to deprecated RestPSR fields parameter"
field   SETS    "$fields"
  ]
  ]
    [ NoARMv3
$label
      [ "$cond"<>"" :LAND: "$cond"<>"AL"
        LCLS    rcc
rcc     SETS    :REVERSE_CC:"$cond"
        B$rcc   %FT02                   ; Go round when $cond-itional
      ]
        ; The save sequences for CLRPSR, SCPSR, SETPSR, TOGPSR, TOGPSRR
        ; and WritePSRc are arranged such that the saved value prefers the one from MRS.
        ; 26 bit mode pre ARMv3 => saved value is the pc
        ; 26 bit mode ARMv3/ARMv4 => saved value is from the CPSR
        ; 32 bit mode => saved value is from the CPSR
        ; So we want to know if MSR/MRS is supported rather than if running in 32 bit mode
        TEQ     r0, r0                  ; Set Z
        MSR     CPSR_f, #0              ; Clear Z (if MSR supported)
        BNE     %FT01
        TEQP    pc, $reg                ; Let the following MSR be the NOP
01
        MSR     CPSR_cf, $reg
02
    |
$label  mymsr   $cond, CPSR_$field, $reg, , unsafe
    ]
        MEND

; **********************************************
; ***  SEC - Set carry flag - will set nzCv  ***
; **********************************************
        MACRO
$label  SEC     $cond
    [ NoARMv3
$label
      [ "$cond"<>"" :LAND: "$cond"<>"AL"
        LCLS    rcc
rcc     SETS    :REVERSE_CC:"$cond"
        B$rcc   %FT01                   ; Go round when $cond-itional
      ]
        CMP     pc, #0                  ; SEC in 26 bit modes
        MSR     CPSR_f, #C_bit          ; ARMv3+ does SEC, NOP on ARMv2
01
    |
$label  MSR$cond CPSR_f, #C_bit
    ]
        MEND

; ************************************************
; ***  SETPSR - Set bits in PSR from the mask  ***
; ***  in $bits, using register $regtmp        ***
; ************************************************
        MACRO
$label  SETPSR  $bits, $regtmp, $cond, $oldpsr
$label  SCPSR   $bits, 0, $regtmp, $cond, $oldpsr
        MEND

; **************************************************
; ***  SETV - Set overflow flag - will set NzcV  ***
; **************************************************
        MACRO
$label  SETV    $cond
    [ NoARMv3
$label
      [ "$cond"<>"" :LAND: "$cond"<>"AL"
        LCLS    rcc
rcc     SETS    :REVERSE_CC:"$cond"
        B$rcc   %FT01                   ; Go round when $cond-itional
      ]
        CMP     pc, #&80000000          ; SETV in 26 bit modes
        MSR     CPSR_f, #N_bit+V_bit    ; ARMv3+ does SETV, NOP on ARMv2
01
    |
$label  MSR$cond CPSR_f, #N_bit+V_bit
    ]
        MEND

; *********************************************************
; ***  TOGPSR - Toggle bits in PSR from the            ***
; ***  immediate mask in $bits, using register $regtmp  ***
; *********************************************************
        MACRO
$label  TOGPSR  $bits, $regtmp, $cond, $oldpsr
        LCLS    srcreg
        [ "$oldpsr"=""
srcreg  SETS    "$regtmp"
        |
srcreg  SETS    "$oldpsr"
        ]
CPU32_bits PSRto32 $bits                ; Map to 32 bit PSR
    [ NoARMv3
$label
      [ "$cond"<>"" :LAND: "$cond"<>"AL"
        LCLS    rcc
rcc     SETS    :REVERSE_CC:"$cond"
        B$rcc   %FT02                   ; Go round when $cond-itional
      ]
        MOV     $srcreg, pc             ; Snapshot 26-bit PSR
        MRS     $srcreg, CPSR           ; Snapshot 32-bit PSR, or NOP pre ARMv3
        TEQ     r0, r0                  ; Set Z
        MSR     CPSR_f, #0              ; Clear Z if ARMv3 or later
        BNE     %FT01
        TEQP    $srcreg, #$bits
        B       %FT02
01
        EOR     $regtmp, $srcreg, #CPU32_bits
        somemsr AL, CPSR, $regtmp, CPU32_bits
02
    |
$label  MRS$cond $srcreg, CPSR
        EOR$cond $regtmp, $srcreg, #CPU32_bits
        somemsr $cond, CPSR, $regtmp, CPU32_bits, unsafe
    ]
        MEND

; ************************************************
; ***  TOGPSRR - Toggle bits in PSR from the   ***
; ***  mask in $regtog, using register $regtmp ***
; ************************************************
        MACRO
$label  TOGPSRR $regtog, $regtmp, $cond, $oldpsr, $fields
        LCLS    srcreg
        LCLS    field
        [ "$fields"=""
field   SETS    "cxsf"
        |
field   SETS    "$fields"
        ]
        [ "$oldpsr"=""
srcreg  SETS    "$regtmp"
        |
srcreg  SETS    "$oldpsr"
        ]
    [ NoARMv3
$label
      [ "$cond"<>"" :LAND: "$cond"<>"AL"
        LCLS    rcc
rcc     SETS    :REVERSE_CC:"$cond"
        B$rcc   %FT02                   ; Go round when $cond-itional
      ]
        MOV     $srcreg, pc             ; Snapshot 26-bit PSR
        MRS     $srcreg, CPSR           ; Snapshot 32-bit PSR, or NOP pre ARMv3
        TEQ     r0, r0                  ; Set Z
        MSR     CPSR_f, #0              ; Clear Z if ARMv3 or later
        BNE     %FT01
        TEQP    $regtog, $srcreg
        B       %FT02
01
        EOR     $regtmp, $srcreg, $regtog
        MSR     CPSR_$field, $regtmp
02
    |
$label  MRS$cond $srcreg, CPSR
        EOR$cond $regtmp, $srcreg, $regtog
        mymsr    $cond, CPSR_$field, $regtmp, , unsafe
    ]
        MEND

; ***************************************************
; ***  WritePSRc - Set the PSR control bits to    ***
; ***  an absolute value.                         ***
; ***  Sets I,F,M[0:3], corrupts NZVC.            ***
; ***  Preserves 32-bitness. Ignored in USR mode. ***
; ***  PSR is specified as 26bit form, so only    ***
; ***  USR/IRQ/FIQ/SVC can be used as dest mode,  ***
; ***  but source can be any non-USR mode         ***
; ***  (M[2:3] will be cleared)                   ***
; ***  Use instead of TEQP PC,#$value             ***
; ***************************************************
        MACRO
$label  WritePSRc $value, $regtmp, $cond, $oldpsr
        [ ($value :AND::NOT: (I_bit+F_bit+SVC_mode)) <> 0
        ! 1, "Illegal flags for WritePSRc"
        ]
   [ No26bitCode
        ; We only care about 32bit (non-thumb) processor modes
        ; Write the PSR directly to avoid unnecessary bloat from SCPSR
$label
CPU32_bits PSRto32 $value
      [ "$oldpsr" <> ""
        MRS$cond $oldpsr, CPSR
      ]
     [ NoARMv6 :LOR: ((CPU32_bits :AND: (I32_bit+F32_bit)) <> 0) :LOR: (("$cond" <> "") :LAND: ("$cond" <> "AL"))
        MSR$cond CPSR_c, #CPU32_bits :OR: USR32_mode
      [ "$cond" <> "" :LAND: "$cond" <> "AL" :LAND: StrongARM_MSR_bug
        NOP
      ]
     |
        ; CPS is faster than CPSR_c, but is limited in that if I+F are being set they must take the same value
        CPSIE if, #(CPU32_bits :AND: M32_bits) :OR: USR32_mode
     ]
   |
$label  SCPSR   $value, (I_bit+F_bit+SVC_mode):EOR:($value), $regtmp, $cond, $oldpsr
   ]
        MEND

 ] ; :LNOT: No32bitCode

; ****************************************************
; *** mrs/msr - Lowercase funny names for aasm.    ***
; *** Due for retirement, aasm is redundant now.   ***
;*****************************************************
        MACRO
$label  mrs     $cond, $rd, $psrs
$label  MRS$cond $rd, $psrs
        MEND

        MACRO
$label  msr     $cond, $psrl, $op2a, $op2b
$label  mymsr   $cond, $psrl, $op2a, $op2b
        MEND

; ***************************************************
; *** somemsr - Set some fields of the PSR from   ***
; *** $op, according to $mask. The mask should    ***
; *** indicate which bits have been modified.     ***
; *** This saves us writing the control field,    ***
; *** when it hasn't been modified, for example,  ***
; *** saving 2 cycles on some processors.         ***
; ***************************************************
        MACRO
$label  somemsr $cond, $psr, $op, $mask, $sabug
        LCLS    s
s       SETS    "$psr._"
 [ (($mask) :AND: &FF) <> 0
s       SETS    s:CC:"c"
 ]
 [ (($mask) :AND: &FF00) <> 0
s       SETS    s:CC:"x"
 ]
 [ (($mask) :AND: &FF0000) <> 0
s       SETS    s:CC:"s"
 ]
 [ (($mask) :AND: &FF000000) <> 0
s       SETS    s:CC:"f"
 ]
$label  mymsr   $cond, $s, $op, , $sabug
        MEND

; ****************************************************
; *** mymrs - Perform an MRS operation.            ***
; *** Due for retirement, objasm supports MRS now. ***
;*****************************************************
        MACRO
$label  mymrs   $cond, $rd, $psrs
$label  MRS$cond $rd, $psrs
        ASSERT  $rd <> 15
        MEND

; ****************************************************
; *** mymsr - Perform an MSR operation.            ***
; *** If $sabug is set to "safe", it's assumed the ***
; *** code around this operation is sufficiently   ***
; *** protected against the StrongARM conditional  ***
; *** MSR CPSR_c bug.                              ***
; *** If $sabug is set to "unsafe", a NOP will     ***
; *** automatically be inserted when generating an ***
; *** MSR that could trigger the bug (and we're    ***
; *** targeting a StrongARM machine type).         ***
; *** If $sabug is left unset, a warning and a NOP ***
; *** will be produced whenever a dangerous MSR is ***
; *** requested (if we're targeting StrongARM)     ***
; ****************************************************
        MACRO
$label  mymsr   $cond, $psrl, $op2a, $op2b, $sabug
$label
        LCLS    psr
psr     SETS    :LOWERCASE:"$psrl"
        LCLL    ctl
ctl     SETL    psr:RIGHT:4 = "_all" :LOR: psr:RIGHT:3 = "psr" :LOR: psr:RIGHT:4:LEFT:1 = "c" :LOR: psr:RIGHT:3:LEFT:1 = "c" :LOR: psr:RIGHT:2:LEFT:1 = "c" :LOR: psr:RIGHT:1 = "c"

 [ "$op2b" = ""
        MSR$cond $psr, $op2a
 |
        MSR$cond $psr, $op2a, $op2b
 ]

     [ StrongARM_MSR_bug :LAND: "$sabug" <> "safe" :LAND: "$cond" <> "AL" :LAND: "$cond" <> "" :LAND: psr:LEFT:4 = "cpsr" :LAND: ctl
      [ "$sabug" <> "unsafe"
        ! 0, "mymsr inserting NOP for StrongARM MSR CPSR_c bug", 1
      ]
        NOP
     ]
        MEND

; This next group of macros (SetMode, SExx, CLxx, and combinations) are
; optimised for 32bit architectures:
;
; * 32bit-only processor modes (e.g. SYS) are fully supported
; * Except for the T bit (assumed to be 0), only the indicated PSR fields will be touched (e.g. NZCV always preserved)
; * They assemble down to one instruction (CPS or MRS) wherever possible
;   * Due to the limits of CPS this means they're all unconditional
;   * For the one-instruction MRS form you'll have to provide 'hints' for the current mode/IF bits. But even though
;     you're specifying the mode + interrupt flags, these macros are still better than WritePSRc (for ARMv6+)
; * When building 26/32bit neutral or 26bit-only versions:
;   * The saved PSR will be the CPSR if MRS supported, else it's a saved PC (same rules as SavePSR, WritePSRc, etc.)
;   * The SetMode macros will preserve the 32bit-ness of the host, unless asked to switch into a 32bit-only mode (e.g. SYS)
;
; Note the side-effects of SEI/SEF/CLI/CLF. The macros assume that if IRQ state
; is being altered, FIQs are enabled, and if FIQ state is being altered, IRQs
; are disabled. This is based on the premise that it's unsafe/unreliable to
; have FIQs disabled while IRQs are enabled (RISC OS generally assumes FIQs are
; enabled, so if you have FIQs disabled but IRQs enabled it would be easy for
; an IRQ handler to come along and do something which enables FIQs).

; ****************************************************
; *** SetMode - sets processor mode to constant    ***
; *** value newmode using register regtmp as a     ***
; *** temporary.                                   ***
; *** If $irqs is provided it's expected to be the ***
; *** current I32+F32 flags                        ***
; *** $regtmp only required if 26bit support       ***
; *** required, or no $irqs provided.              ***
; ****************************************************
        MACRO
        SetMode $newmode, $regtmp, $oldpsr, $irqs
    [ :LNOT: NoARMv6
      [ "$oldpsr"<>""
        MRS     $oldpsr, CPSR
      ]
        CPS     #$newmode
    ELIF No26bitCode :LOR: ($newmode > SVC32_mode)
      [ "$oldpsr"<>""
        MRS     $oldpsr, CPSR
      ]
      [ "$irqs" <> ""
        MSR     CPSR_c, #$newmode + $irqs
      ELIF "$oldpsr"=""
        MRS     $regtmp, CPSR
        BIC     $regtmp, $regtmp, #M32_bits
        ORR     $regtmp, $regtmp, #$newmode
        MSR     CPSR_c, $regtmp
      |
        BIC     $regtmp, $oldpsr, #M32_bits
        ORR     $regtmp, $regtmp, #$newmode
        MSR     CPSR_c, $regtmp
      ]
    |
        ; Use SCPSR for 26/32bit-friendly PSR manipulation
        SCPSR   ($newmode :AND: 3), ($newmode :AND: 3) :EOR: 3, $regtmp, , $oldpsr
    ]
        MEND

; *************************************************
; *** SEI  - Disable IRQs, may enable FIQs      ***
; *** SEF  - Disable FIQs, may disable IRQs     ***
; *** SEIF - Disable IRQs+FIQs                  ***
; *** If $mode is provided (preferred) then it  ***
; *** must be the current 32bit processor mode. ***
; *** $regtmp only required if 26bit support    ***
; *** required, or no $mode provided.           ***
; *************************************************
        MACRO
        SEI     $mode, $regtmp, $oldpsr
        internalSEI $mode, $regtmp, $oldpsr
        MEND
        MACRO
        SEF     $mode, $regtmp, $oldpsr
        internalSEF $mode, $regtmp, $oldpsr
        MEND
        MACRO
        SEIF    $mode, $regtmp, $oldpsr
        internalSEIF $mode, $regtmp, $oldpsr
        MEND

        MACRO
        internalSE$op $mode, $regtmp, $oldpsr
CPU32_bits IFto32 $op
    [ :LNOT: NoARMv6
      [ "$oldpsr" <> ""
        MRS     $oldpsr, CPSR
      ]
        CPSID   $op
    ELIF ("$mode" <> "") :LAND: (No26bitCode :LOR: (($mode + 0) > SVC32_mode))
      [ "$oldpsr" <> ""
        MRS     $oldpsr, CPSR
      ]
        MSR     CPSR_c, #$mode+(CPU32_bits :OR: I32_bit)
    |
        ; Use SETPSR for 26/32bit-friendly PSR manipulation
        SETPSR  CPU32_bits :SHL: IF32_26Shift, $regtmp, , $oldpsr
    ]
        MEND

; *************************************************
; *** CLI  - Enable IRQs, may enable FIQs       ***
; *** CLF  - Enable FIQs, may disable IRQs      ***
; *** CLIF - Enable IRQs+FIQs                   ***
; *** If $mode is provided (preferred) then it  ***
; *** must be the current 32bit processor mode. ***
; *** $regtmp only required if 26bit support    ***
; *** required, or no $mode provided.           ***
; *************************************************
        MACRO
        CLI     $mode, $regtmp, $oldpsr
        internalCLI $mode, $regtmp, $oldpsr
        MEND
        MACRO
        CLF     $mode, $regtmp, $oldpsr
        internalCLF $mode, $regtmp, $oldpsr
        MEND
        MACRO
        CLIF    $mode, $regtmp, $oldpsr
        internalCLIF $mode, $regtmp, $oldpsr
        MEND

        MACRO
        internalCL$op $mode, $regtmp, $oldpsr
CPU32_bits IFto32 $op
    [ :LNOT: NoARMv6
      [ "$oldpsr" <> ""
        MRS     $oldpsr, CPSR
      ]
        CPSIE   $op
    ELIF ("$mode" <> "") :LAND: (No26bitCode :LOR: (($mode + 0) > SVC32_mode))
      [ "$oldpsr" <> ""
        MRS     $oldpsr, CPSR
      ]
        MSR     CPSR_c, #$mode+I32_bit+F32_bit-(CPU32_bits :OR: F32_bit)
    |
        ; Use CLRPSR for 26/32bit-friendly PSR manipulation
        CLRPSR  CPU32_bits :SHL: IF32_26Shift, $regtmp, , $oldpsr
    ]
        MEND

; ***************************************************************
; *** SetModeSEI  - Set mode + disable IRQs, may enable FIQs  ***
; *** SetModeSEF  - Set mode + disable FIQs, may disable IRQs ***
; *** SetModeSEIF - Set mode + disable IRQs+FIQs              ***
; *** $regtmp only required if 26bit support required         ***
; ***************************************************************
        MACRO
        SetModeSE$op   $mode, $regtmp, $oldpsr
CPU32_bits IFto32 $op
    [ :LNOT: NoARMv6
      [ "$oldpsr" <> ""
        MRS     $oldpsr, CPSR
      ]
        CPSID   $op, #$mode
    ELIF No26bitCode :LOR: ($mode > SVC32_mode)
      [ "$oldpsr" <> ""
        MRS     $oldpsr, CPSR
      ]
        MSR     CPSR_c, #$mode+(CPU32_bits :OR: I32_bit)
    |
        LCLA    clear_bits
        LCLA    set_bits
set_bits   SETA ($mode :AND: 3) + (CPU32_bits :SHL: IF32_26Shift)
clear_bits SETA 3 :AND: :NOT: set_bits
        SCPSR   &$set_bits, &$clear_bits, $regtmp, , $oldpsr
    ]
        MEND

; **************************************************************
; *** SetModeCLI  - Set mode + enable IRQs, may enable FIQs  ***
; *** SetModeCLF  - Set mode + enable FIQs, may disable IRQs ***
; *** SetModeCLIF - Set mode + enable IRQs+FIQs              ***
; *** $regtmp only required if 26bit support required        ***
; **************************************************************
        MACRO
        SetModeCL$op   $mode, $regtmp, $oldpsr
CPU32_bits IFto32 $op
    [ :LNOT: NoARMv6
      [ "$oldpsr" <> ""
        MRS     $oldpsr, CPSR
      ]
        CPSIE   $op, #$mode
    ELIF No26bitCode :LOR: ($mode > SVC32_mode)
      [ "$oldpsr" <> ""
        MRS     $oldpsr, CPSR
      ]
        MSR     CPSR_c, #$mode+I32_bit+F32_bit-(CPU32_bits :OR: F32_bit)
    |
        LCLA    clear_bits
        LCLA    set_bits
set_bits   SETA $mode :AND: 3
clear_bits SETA (3 :AND: :NOT: set_bits) + (CPU32_bits :SHL: IF32_26Shift)
        SCPSR   &$set_bits, &$clear_bits, $regtmp, , $oldpsr
    ]
        MEND

 ] ; :LNOT: :DEF: Included_Hdr_CPU_Generic32

        OPT     OldOpt
        END
