;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; Microsoft Research Singularity ;;; ;;; Copyright (c) Microsoft Corporation. All rights reserved. ;;; ;;; This file contains ARM-specific assembly code. ;;; ; Assembler source for FPA support code and emulator ; ================================================== ; Some useful assembler macros. Also used by "fplib". ; ; Copyright (C) Advanced RISC Machines Limited, 1992-7. All rights reserved. ; ; RCS Revision: 1 ; Checkin Date: 2007/06/29 02:59:16 ; Revising Author ;=========================================================================== ; Register names used when isolating the base register of a PC-relative or ; register-relative expression in the macros below. The technique is to set ; a temporary arithmetic variable Base to :BASE:(expression), then refer to ; R$Base. R00000000 RN R0 R00000001 RN R1 R00000002 RN R2 R00000003 RN R3 R00000004 RN R4 R00000005 RN R5 R00000006 RN R6 R00000007 RN R7 R00000008 RN R8 R00000009 RN R9 R0000000A RN R10 R0000000B RN R11 R0000000C RN R12 R0000000D RN R13 R0000000E RN R14 R0000000F RN R15 ;=========================================================================== ; Two general purpose arithmetic variables. GBLA Tempa GBLA Tempa2 ;=========================================================================== ; The following macro is useful for shifting bit fields around when their ; positions are symbolic constants - which makes it unclear to the author ; whether LSR or LSL is needed. MACRO $label BiShift $opc,$Rd,$Rn,$Rm,$rshift,$lshift [ "$lshift":LEFT:5 <> "LSL #" ! 4,"Left shift must start exactly 'LSL #'" | [ "$rshift":LEFT:5 <> "LSR #" ! 4,"Right shift must start exactly 'LSR #'" | LCLS left LCLS right left SETS "$lshift":RIGHT:(:LEN:"$lshift" - 5) right SETS "$rshift":RIGHT:(:LEN:"$rshift" - 5) [ "$Rn" = "" ASSERT (("$opc":LEFT:3) <> "LDR") :LAND: (("$opc":LEFT:3) <> "STR") [ ($right) > ($left) $label $opc $Rd,$Rm,LSR #(($right) - ($left)) | $label $opc $Rd,$Rm,LSL #(($left) - ($right)) ] | [ ($right) > ($left) [ (("$opc":LEFT:3) = "LDR") :LOR: (("$opc":LEFT:3) = "STR") $label $opc $Rd,[$Rn,$Rm,LSR #(($right) - ($left))] | $label $opc $Rd,$Rn,$Rm,LSR #(($right) - ($left)) ] | [ (("$opc":LEFT:3) = "LDR") :LOR: (("$opc":LEFT:3) = "STR") $label $opc $Rd,[$Rn,$Rm,LSL #(($left) - ($right))] | $label $opc $Rd,$Rn,$Rm,LSL #(($left) - ($right)) ] ] ] ] ] MEND ;=========================================================================== ; The following macro isolates the exponent field from the standard sign/ ; uncommon bit/exponent word, putting it at the top of the destination ; register. MACRO $label Exp2Top $dest,$src,$cond,$s [ EIExp_pos = 0 $label MOV$cond$s $dest,$src,LSL #32-EIExp_len | $label MOV$cond $dest,$src,LSR #EIExp_pos MOV$cond$s $dest,$dest,LSL #32-EIExp_len ] MEND ;=========================================================================== ; The following macros isolate the exponent fields from two standard sign/ ; uncommon bit/exponent words, putting the first one at the top of a ; destination register. ExpDiff puts the difference at the top of another ; register and sets the condition codes on it, while ExpComp simply sets the ; condition codes on the difference. MACRO $label ExpComp $dest,$src1,$src2,$tmp ASSERT $dest <> $src1 ASSERT $dest <> $src2 ASSERT $dest <> $tmp ASSERT $tmp <> $src1 ASSERT $tmp <> $src2 [ EIExp_pos = 0 $label MOV $dest,$src1,LSL #32-EIExp_len CMP $dest,$src2,LSL #32-EIExp_len | $label MOV $dest,$src1,LSR #EIExp_pos MOV $dest,$dest,LSL #32-EIExp_len MOV $tmp,$src2,LSR #EIExp_pos CMP $dest,$tmp,LSL #32-EIExp_len ] MEND MACRO $label ExpDiff $diff,$dest,$src1,$src2 ASSERT $diff <> $dest ASSERT $diff <> $src1 ASSERT $diff <> $src2 ASSERT $dest <> $src1 ASSERT $dest <> $src2 [ EIExp_pos = 0 $label MOV $dest,$src1,LSL #32-EIExp_len SUBS $diff,$dest,$src2,LSL #32-EIExp_len | $label MOV $dest,$src1,LSR #EIExp_pos MOV $dest,$dest,LSL #32-EIExp_len MOV $diff,$src2,LSR #EIExp_pos SUBS $diff,$dest,$diff,LSL #32-EIExp_len ] MEND ;=========================================================================== ; The following macro performs the standard test for infinities or NaNs on ; an internal floating point number. It only works on legitimate internal ; precision numbers - i.e. it produces undefined results if the bit pattern ; in the internal precision number is an undefined one. The parameters are: ; $res: On exit, the top bit of this register is set if the number is a ; NaN or infinity, clear if it isn't; ; $sue: Register holding sign, uncommon bit and exponent of number to be ; tested; preserved on exit; ; $mhi: Register containing high word of mantissa of number to be tested; ; preserved on exit; ; $mlo: Register containing low word of mantissa of number to be tested; ; preserved on exit; ; ; In addition, the N flag is set on exit if the number is a NaN or infinity, ; clear if it isn't. ; ; The criterion used for a number to be a NaN or infinity is: ; ; Uncommon bit = 1; and ; Exponent top bit = 1; and ; Exponent = MAX or units bit = 1. ; ; Whether the operand is in fact a NaN or an infinity is then determined by ; seeing whether the fraction is non-zero or zero. MACRO $label TNaNInf $res,$sue,$mhi ASSERT $res <> $sue ASSERT $res <> $mhi $label MOV $res,$sue,LSL #32-(EIExp_pos+EIExp_len) ;Top bit of exponent CMN $res,#1:SHL:(32-(EIExp_pos+EIExp_len)) ;Is exp. = MAX? If ANDCC $res,$res,$mhi ; not, use units bit ANDS $res,$res,$sue,LSL #31-Uncommon_pos ;Use uncommon anyway MEND ;=========================================================================== ; The following macro contains the standard code for denormalising a ; mantissa by a specified amount, producing guard, round and sticky bits in ; the process. The parameters are: ; $mhi: Register containing mantissa high word; updated on exit; ; $mlo: Register containing mantissa low word; updated on exit; ; $grs: Register that will contain the guard bit (in bit 31), the round ; bit (in bit 30) and the sticky bit (in whether bits 29:0 are zero ; or non-zero) on exit; ; $sh: Register containing the shift amount; corrupt on exit; ; $t1,$t2: Registers used as temporaries; corrupt on exit. ; $grs may be null to indicate that the guard, round and sticky information ; isn't wanted. $mlo can be null to indicate that we only need to ; denormalise a single word: in this case, $grs must be null. ; Note that the $grs register may alternatively be interpreted as ; containing a round bit in bit 31 and a sticky bit in bits 30:0, in cases ; when there is no need for a guard bit. Also note that $sh may be the same ; register as either of $grs and $t2; otherwise, the registers must be ; distinct from each other. ; Finally, note that branch instructions are used around a 4 instruction ; sequence and a 5 instruction sequence. This is because statistics show ; that larger shift amounts are less common than smaller ones in general: ; thus these instruction sequences are obeyed less than 50% of the time, ; which makes the code with branches slightly faster. MACRO $label Denorm $mhi,$mlo,$grs,$sh,$t1,$t2 ASSERT $mhi <> $sh ASSERT $mhi <> $t1 ASSERT $mhi <> $t2 ASSERT $sh <> $t1 ASSERT $t1 <> $t2 $label MOV $t1,$sh,LSR #5 ;Number of words to shift by BIC $t2,$sh,$t1,LSL #5 ;Number of odd bits to shift by [ "$mlo" = "" ASSERT "$grs" = "" CMP $t1,#1 ;At least one word? MOVLO $mhi,$mhi,LSR $t2 ;Shift by odd bits if not MOVHS $mhi,#0 ;And clear out completely if so | ASSERT $mlo <> $mhi ASSERT $mlo <> $sh ASSERT $mlo <> $t1 ASSERT $mlo <> $t2 [ "$grs" = "" CMP $t1,#1 ;HI for 2+ words, EQ for 1, LO for 0 RSBLS $t1,$t2,#32 ;Shift by the number of odd bits MOVLS $mlo,$mlo,LSR $t2 ORRLS $mlo,$mlo,$mhi,LSL $t1 MOVLS $mhi,$mhi,LSR $t2 MOVEQ $mlo,$mhi ;Now do full words MOVHI $mlo,#0 MOVHS $mhi,#0 | ASSERT $grs <> $mhi ASSERT $grs <> $mlo ASSERT $grs <> $t1 ASSERT $grs <> $t2 CMP $t1,#2 ;CS/NE for 3+ words, CS/EQ for 2, TEQCC $t1,#0 ; CC/NE for 1 and CC/EQ for 0. RSB $t1,$t2,#32 ;Shift by the number of odd bits MOV $grs,$mlo,LSL $t1 MOV $mlo,$mlo,LSR $t2 ORR $mlo,$mlo,$mhi,LSL $t1 MOV $mhi,$mhi,LSR $t2 BEQ %f90 ;Branch if no 32-bit shift ORRNE $grs,$grs,$grs,LSL #2 ;Shift by 32 bits, accumulating ORRNE $grs,$mlo,$grs,LSR #2 ; sticky bit MOVNE $mlo,$mhi MOVNE $mhi,#0 90 BCC %f99 ;Branch if no 64-bit shift ORRCS $grs,$grs,$mlo ;Shift by 64 bits, accumulating ORRCS $grs,$grs,$grs,LSL #2 ; sticky bit ORRCS $grs,$mhi,$grs,LSR #2 MOVCS $mlo,#0 MOVCS $mhi,#0 99 ] ] MEND ;=========================================================================== ; Macro to separate a 32-bit value in a register into its two 16-bit halves. MACRO $label Split16 $resh,$resl,$src ASSERT $resh <> $src $label MOV $resh,$src,LSR #16 BIC $resl,$src,$resh,LSL #16 MEND ;=========================================================================== ; Macro to do a (16,16)x32 -> 64 multiplication. Done by breaking it up into ; four 16x16 multiplications and recombining the pieces. (N.B. The trick ; described in Knuth section 4.3.3 for reducing the four multiplications to ; three plus some additions and sign manipulations is not profitable at this ; size: it only becomes profitable when trying to synthesise a 64x64 ; multiplication out of 32x32 multiplications.) ; Also allows the flags to be set on the high word of the result and an ; optional addend to be added into the high word of the result: however, ; combining these does *not* result in the C flag being set correctly for ; the carry-out from the notional addition of the addend and the high word. ; Only the Z and N flags have meaningful values. ; The operands are: ; $resh,$resl: Registers that will receive the 64-bit product; ; $op1h,$op1l: Registers containing the high and low 16 bits of the first ; 32-bit operand; ; $op2: Register containing the second 32-bit operand; ; $add: If present, register containing the addend; ; $s: "S" to set the condition codes; ; $t1,$t2,$t3: Three temporary registers required during the calculation. ; The restrictions on which registers may be the same are complicated and ; are detailed in the ASSERT statements below. MACRO $label Mul64 $resh,$resl,$op1h,$op1l,$op2,$add,$s,$t1,$t2,$t3 ASSERT $resh <> $resl ASSERT $resl <> $op1h ASSERT $resl <> $t1 ASSERT $resl <> $t2 ASSERT $resl <> $t3 ASSERT $op1h <> $op1l ASSERT $op1h <> $op2 ASSERT $op1h <> $t1 ASSERT $op1h <> $t2 ASSERT $op1h <> $t3 ASSERT $op1l <> $op2 ASSERT $op1l <> $t1 ASSERT $op1l <> $t2 ASSERT $op1l <> $t3 ASSERT $op2 <> $t1 ASSERT $t1 <> $t2 ASSERT $t1 <> $t3 ASSERT $t2 <> $t3 $label Split16 $t1,$t2,$op2 ;t1 := op2h, t2 := op2l [ "$add" <> "" ASSERT $add <> $op1h ASSERT $add <> $op1l ASSERT $add <> $op2 ASSERT $add <> $t1 ASSERT $add <> $t2 MLA $t3,$op1h,$t1,$add ;t3 := op1h * op2h + add | MUL $t3,$op1h,$t1 ;t3 := op1h * op2h ] MUL $t1,$op1l,$t1 ;t1 := op1l * op2h MUL $resl,$t2,$op1l ;resl := op1l * op2l ADDS $resl,$resl,$t1,LSL #16 ;Add op1l * op2h into (t3,resl) ADC $t3,$t3,$t1,LSR #16 MUL $t2,$op1h,$t2 ;t2 := op1h * op2l ADDS $resl,$resl,$t2,LSL #16 ;Add op1h * op2l into (t3,resl) ADC$s $resh,$t3,$t2,LSR #16 ; to produce (resh,resl) MEND ;=========================================================================== ; Macro to transfer the destination register of an instruction to a set of ; registers. Operands are: ; $type: "FPASC" or "FPE"; ; $dest: The destination register list; ; $instr: The instruction whose destination is to be transferred; ; $t: A temporary. MACRO $label GetDst $type,$dest,$instr,$t LCLA Base LCLA Offset ASSERT ("$type" = "FPASC") :LOR: ("$type" = "FPE") [ ("$type" = "FPASC") $label TST $instr,#4:SHL:Ds_pos ;Check whether F0-3 or F4-7 SFMEQFD F0,4,[Rsp]! ;Dump set of 4 registers - SFMNEFD F4,4,[Rsp]! ; faster than trying to get ; the correct register only AND $t,$instr,#3:SHL:Ds_pos ;Get position in dump ADD $t,$t,$t,LSL #1 ;Convert to number of words BiShift ADD,$t,Rsp,$t,LSR #Ds_pos,LSL #2 ;Make address of register LDMIA $t,$dest ; value, then get value ADD Rsp,Rsp,#48 ;Discard dumped registers ASSERT Ds_mask = ((4+3):SHL:Ds_pos) | $label AND $t,$instr,#Ds_mask [ :LNOT:FPE4WordsPerReg ADD $t,$t,$t,LSL #1 ASSERT Ds_pos <= 27 ] Base SETA :BASE:FPE_Regs [ Base = 15 Offset SETA FPE_Regs-({PC}+8) | Offset SETA :INDEX:FPE_Regs ] [ FPE4WordsPerReg BiShift ADD,$t,R$Base,$t,LSR #Ds_pos,LSL #4 | BiShift ADD,$t,R$Base,$t,LSR #Ds_pos,LSL #2 ] [ Offset <> 0 ADD $t,$t,#Offset ] LDMIA $t,$dest ] MEND ;=========================================================================== ; Macro to transfer the destination register of a non-FLT instruction from a ; set of registers. Operands are: ; $type: "FPASC" or "FPE"; ; $source: The source register list; ; $instr: The instruction whose destination is to be transferred; ; $t: A temporary. ; $l: If present, this produces a "long" form of the macro MACRO $label PutDst $type,$source,$instr,$t,$l ASSERT $t <> $instr LCLA Base LCLA Offset ASSERT ("$type" = "FPASC") :LOR: ("$type" = "FPE") [ ("$type" = "FPASC") ALIGN $label STMFD Rsp!,$source AND $t,$instr,#Ds_mask 10 BiShift ADD,$t,PC,$t,LSR #Ds_pos,LSL #3 [ "$l"="" MOV LR,PC ADD PC,$t,#($type._PutDstRoutines - (%b10+8)) | ADD $t,$t,#($type._PutDstRoutines - (%b10+8)):AND:&FF MOV LR,PC ADD PC,$t,#($type._PutDstRoutines - (%b10+8)):AND::NOT:&FF ] | ; "$type" = "FPE" $label AND $t,$instr,#Ds_mask [ :LNOT:FPE4WordsPerReg ADD $t,$t,$t,LSL #1 ASSERT Ds_pos <= 27 ] Base SETA :BASE:FPE_Regs [ Base = 15 Offset SETA FPE_Regs-({PC}+8) | Offset SETA :INDEX:FPE_Regs ] [ FPE4WordsPerReg BiShift ADD,$t,R$Base,$t,LSR #Ds_pos,LSL #4 | BiShift ADD,$t,R$Base,$t,LSR #Ds_pos,LSL #2 ] [ Offset <> 0 ADD $t,$t,#Offset ] STMIA $t,$source ] MEND ;=========================================================================== ; Macro to transfer the destination register of a FLT instruction from a set ; of registers. Operands are: ; $type: "FPASC" or "FPE"; ; $source: The source register list; ; $instr: The instruction whose destination is to be transferred; ; $t: A temporary. MACRO $label PutFDst $type,$source,$instr,$t ASSERT $t <> $instr LCLA Base LCLA Offset ASSERT ("$type" = "FPASC") :LOR: ("$type" = "FPE") [ ("$type" = "FPASC") ALIGN $label STMFD Rsp!,$source AND $t,$instr,#S1_mask 10 BiShift ADD,$t,PC,$t,LSR #S1_pos,LSL #3 MOV LR,PC ADD PC,$t,#($type._PutDstRoutines - (%b10+8)) | ; "$type" = "FPE" $label AND $t,$instr,#S1_mask [ :LNOT:FPE4WordsPerReg ADD $t,$t,$t,LSL #1 ASSERT S1_pos <= 27 ] Base SETA :BASE:FPE_Regs [ Base = 15 Offset SETA FPE_Regs-({PC}+8) | Offset SETA :INDEX:FPE_Regs ] [ FPE4WordsPerReg BiShift ADD,$t,R$Base,$t,LSR #S1_pos,LSL #4 | BiShift ADD,$t,R$Base,$t,LSR #S1_pos,LSL #2 ] [ Offset <> 0 ADD $t,$t,#Offset ] STMIA $t,$source ] MEND ;=========================================================================== ; Macro to get the first source register of an instruction into three ; registers. Operands are: ; $type: "FPASC" or "FPE"; ; $dest: The destination register list; ; $instr: The instruction whose first source is to be transferred; ; $t: A temporary. MACRO $label GetS1 $type,$dest,$instr,$t LCLA Base LCLA Offset ASSERT ("$type" = "FPASC") :LOR: ("$type" = "FPE") [ ("$type" = "FPASC") $label TST $instr,#4:SHL:S1_pos ;Check whether F0-3 or F4-7 SFMEQFD F0,4,[Rsp]! ;Dump set of 4 registers - SFMNEFD F4,4,[Rsp]! ; faster than trying to get ; the correct register only AND $t,$instr,#3:SHL:S1_pos ;Get position in dump ADD $t,$t,$t,LSL #1 ;Convert to number of words BiShift ADD,$t,Rsp,$t,LSR #S1_pos,LSL #2 ;Make address of register LDMIA $t,$dest ; value, then get value ADD Rsp,Rsp,#48 ;Discard dumped registers ASSERT S1_mask = ((4+3):SHL:S1_pos) | $label AND $t,$instr,#S1_mask [ :LNOT:FPE4WordsPerReg ADD $t,$t,$t,LSL #1 ASSERT S1_pos <= 27 ] Base SETA :BASE:FPE_Regs [ Base = 15 Offset SETA FPE_Regs-({PC}+8) | Offset SETA :INDEX:FPE_Regs ] [ FPE4WordsPerReg BiShift ADD,$t,R$Base,$t,LSR #S1_pos,LSL #4 | BiShift ADD,$t,R$Base,$t,LSR #S1_pos,LSL #2 ] [ Offset <> 0 ADD $t,$t,#Offset ] LDMIA $t,$dest ] MEND ;=========================================================================== ; Macro to get the second source register or constant of an instruction into ; three registers. Operands are: ; $type: "FPASC" or "FPE"; ; $dest: The destination register list; ; $instr: The instruction whose second source is to be transferred; ; $t,$t2: Temporaries. MACRO $label GetS2 $type,$dest,$instr,$t,$t2 LCLA Base LCLA Offset ASSERT ("$type" = "FPASC") :LOR: ("$type" = "FPE") ASSERT S2_Ibit = 1:SHL:(S2_pos+3) ASSERT S2_pos = 0 ASSERT $t <> $t2 $label MOVS $t2,$instr,LSL #29 ;C:=S2_Ibit, N:=F4-7, not F0-3 ;$t2 := left-al. reg/const no. [ ("$type" = "FPASC") Base SETA :BASE:$type.ConstTable [ Base = 15 Offset SETA $type.ConstTable-({PC}+8) | Offset SETA :INDEX:$type.ConstTable ] ADDCS $t,R$Base,$t2,LSR #25 ;Address the constant if it [ Offset <> 0 ADDCS $t,$t,#Offset ; is one ] BCS %f10 SFMPLFD F0,4,[Rsp]! ;Dump set of 4 registers - SFMMIFD F4,4,[Rsp]! ; faster than trying to get ; the correct register only BIC $t2,$t2,#TopBit ;Get position within set ADD $t,Rsp,$t2,LSR #27 ;Make address of register ADD $t,$t,$t2,LSR #26 ; value 10 LDMIA $t,$dest ;Get reg. value or constant ADDCC Rsp,Rsp,#48 ;If reg, discard dumped regs | Base SETA :BASE:FPEConstTable [ Base = 15 Offset SETA FPEConstTable-({PC}+8) | Offset SETA :INDEX:FPEConstTable ] ADDCS $t,R$Base,$t2,LSR #25 [ Offset <> 0 ADDCS $t,$t,#Offset ] Base SETA :BASE:FPE_Regs [ Base = 15 Offset SETA FPE_Regs-({PC}+8) | Offset SETA :INDEX:FPE_Regs ] [ FPE4WordsPerReg ADDCC $t,R$Base,$t2,LSR #25 | ADDCC $t,R$Base,$t2,LSR #27 ADDCC $t,$t,$t2,LSR #26 ] [ Offset <> 0 ADDCC $t,$t,#Offset ] LDMIA $t,$dest ] MEND ;=========================================================================== ; Macro to get both source operands of an instruction into two groups of ; three registers. Operands are: ; $type: "FPASC" or "FPE"; ; $dest1: The destination register list for the first operand; ; $dest2: The destination register list for the second operand; ; $instr: The instruction whose second source is to be transferred; ; $t, $t2: Temporaries. MACRO $label GetS12 $type,$dest1,$dest2,$instr,$t,$t2 LCLA Base LCLA Offset ASSERT ("$type" = "FPASC") :LOR: ("$type" = "FPE") [ ("$type" = "FPASC") SFMFD F4,4,[Rsp]! ;Dump all registers SFMFD F0,4,[Rsp]! AND $t,$instr,#S1_mask ;Get S1 position in dump ADD $t,$t,$t,LSL #1 ;Convert to number of words BiShift ADD,$t,Rsp,$t,LSR #S1_pos,LSL #2 ;Make address of register LDMIA $t,$dest1 ; value, then get value ASSERT S2_Ibit = 1:SHL:(S2_pos+3) ASSERT S2_pos = 0 MOVS $t2,$instr,LSL #29 ;C:=S2_Ibit, N:=F4-7, not F0-3 ;$t2 := left-al. reg/const no. Base SETA :BASE:$type.ConstTable [ Base = 15 Offset SETA $type.ConstTable-({PC}+8) | Offset SETA :INDEX:$type.ConstTable ] ADDCS $t,R$Base,$t2,LSR #25 ;Address the constant if it [ Offset <> 0 ADDCS $t,$t,#Offset ; is one ] ADDCC $t,Rsp,$t2,LSR #27 ;Otherwise address the register ADDCC $t,$t,$t2,LSR #26 ; value LDMIA $t,$dest2 ADD Rsp,Rsp,#96 ;Discard the register dump | $label GetS1 $type,$dest1,$instr,$t GetS2 $type,$dest2,$instr,$t,$t2 ] MEND ;=========================================================================== ; The standard macro to return to the caller. Note that care has to be taken ; here never to leave Rsp pointing above any useful stack contents, in case ; of a badly-timed interrupt. MACRO $label Return [ {CONFIG} = 26 $label MOV Rsp,Rfp ;Discard now-spurious stack contents ] [ {CONFIG} = 32 $label LDMDB Rfp,{Rtmp,Rtmp2} ;Recover the SPSR and CPSR MSR CPSR_all,Rtmp2 ; (restoring the CPSR re-disables MSR SPSR_all,Rtmp ; interrupts, so the SPSR isn't ever ; valid when interrupts are enabled) MOV Rsp,Rfp ;Discard now-spurious stack contents ] LDMIA Rfp,{R0-R14}^ ;Coding rules: cannot use write-back NOP ; and must protect next instruction ADD Rsp,Rsp,#15*4 ;Do the write-back LDMFD Rsp!,{PC}^ ;Restore R13_svr/R13_und, PC and PSR MEND ;=========================================================================== ; Macro to get the processor mode for the caller, with the 26/32 bit ; distinction removed. The flags are set on the result value, so Z indicates ; whether we're in user mode. MACRO $label GetMode $res [ {CONFIG}=32 $label LDR $res,[Rfp,#-8] ;Recover original SPSR value ANDS $res,$res,#Mode_mask-Mode_32not26 ASSERT (Mode_USR26:AND::NOT:Mode_32not26) = 0 ASSERT (Mode_USR32:AND::NOT:Mode_32not26) = 0 ] [ {CONFIG}=26 $label LDR $res,[Rfp,#15*4] ;Recover original LR value ANDS $res,$res,#Mode_mask ASSERT Mode_USR26 = 0 ] MEND ;=========================================================================== ; Macro to insert the right amount of padding between a table-driven branch ; instruction (e.g. ADD PC,PC,reg,LSL #2) and the in-line branch table that ; follows it. Made into a macro for documentation purposes, and also just in ; case the user-visible pipeline depth has to change at some point in the ; future. MACRO BranchTablePad DCD 0 ;Padding before branch table MEND ;=========================================================================== ; Macro to re-enable interrupts if "EnableInterrupts" is {TRUE}. Only ; argument is a temporary register. MACRO $label InterruptEnable $t [ EnableInterrupts [ {CONFIG} = 32 $label MRS $t,CPSR_all BIC $t,$t,#I_bit MSR CPSR_all,$t ] [ {CONFIG} = 26 $label MOV $t,PC BIC $t,$t,#I_bit TEQP PC,$t ] NOP ] MEND ;=========================================================================== ; Macro to re-disable interrupts if "EnableInterrupts" is {TRUE}. Only ; argument is a temporary register. MACRO $label InterruptDisable $t [ EnableInterrupts [ {CONFIG} = 32 $label MRS $t,CPSR_all ORR $t,$t,#I_bit MSR CPSR_all,$t ] [ {CONFIG} = 26 $label MOV $t,PC ORR $t,$t,#I_bit TEQP PC,$t ] NOP ] MEND ;=========================================================================== ; Macro to do the standard "enter recursive floating point code" processing. ; The operands are: ; $freeregs: Number of floating point registers to free up, in the range 1 ; to 8; ; $extra: Number of bytes of extra space to be left on the stack above ; the register dump; ; $addr: Register to contain address of extra space - also used to hold ; temporary values during macro, so must be present even if no ; extra space is wanted; ; $nofpsr: If this operand is non-null, it inhibits the FPSR changes ; described below. ; Rsp and Rfpsr are also operands. ; This code is written quite carefully, to avoid the use of floating point ; instructions the FPE won't like (e.g. those which use mode-dependent ; registers). It also disables interrupts (if they were ever enabled) and ; leaves a record of what floating point registers are on the stack, to make ; certain core_abort works. Finally, it clears out the exception enable bits ; and cumulative flags from the real FPSR, since exceptions in recursive ; code must not go out to the user trap handlers. Note that Rfpsr holds the ; real FPSR value and will be written back to the real FPSR before control ; is returned to the user, either via a trap handler or by normal return. MACRO $label EnterRecursive $freeregs,$extra,$addr,$nofpsr ASSERT ($freeregs) <= 8 ASSERT ($freeregs) >= 1 $label SUB Rsp,Rsp,#($freeregs)*12+4+($extra) MOV $addr,Rfpsr,LSL #8 ORR $addr,$addr,#(1:SHL:($freeregs))-1 STR $addr,[Rsp] InterruptDisable $addr [ "$nofpsr" = "" BIC $addr,Rfpsr,#IOE_bit+DZE_bit+OFE_bit+UFE_bit+IXE_bit BIC $addr,$addr,#IOC_bit+DZC_bit+OFC_bit+UFC_bit+IXC_bit WFS $addr ] ADD $addr,Rsp,#($freeregs)*12+4 [ ($freeregs) <= 4 SFM F0,($freeregs),[$addr,#-12*($freeregs)] | SFM F0,4,[$addr,#-12*($freeregs)] SFM F4,($freeregs)-4,[$addr,#-12*($freeregs)+48] ] MEND ;=========================================================================== ; Macro to do the standard "exit recursive floating point code" processing. ; The operands are: ; $freeregs: Number of floating point registers to recover from the stack, ; in the range 1 to 8; ; $extra: Number of bytes of extra space to recover from the stack; ; $t: A temporary register; ; $fpres: Floating point register that contains the floating point result ; - null if no such result; ; $result: Register list to take 3 word floating point result - null if ; no such result. ; Rsp is also an operand. MACRO $label ExitRecursive $freeregs,$extra,$t,$fpres,$result ASSERT ($freeregs) <= 8 ASSERT ($freeregs) >= 1 ADD $t,Rsp,#($freeregs)*12+4 [ "$fpres" <> "" ASSERT ($extra) >= 12 SFM $fpres,1,[$t] LDMIA $t,$result ] [ ($freeregs) <= 4 LFM F0,($freeregs),[$t,#-12*($freeregs)] | LFM F0,4,[$t,#-12*($freeregs)] LFM F4,($freeregs)-4,[$t,#-12*($freeregs)+48] ] ADD Rsp,Rsp,#($freeregs)*12+4+($extra) InterruptEnable $t MEND ;=========================================================================== ; Macro to determine whether an exception was precise or imprecise. Implicit ; operands are Rfpsr (for the SO bit and to determine whether it is a ; hardware or software context) and Rins (to determine what type of ; instruction this is). Explicit operands are: ; $dst: Set to zero if precise, non-zero if imprecise; ; $t: A temporary register; ; $tbl_fpa: The address of a bit table used to determine whether FPA ; instructions are capable of producing imprecise exceptions. ; ; The full set of conditions for the operation to be imprecise are: ; ; * The FPA hardware is being used; ; * The SO bit is clear in the FPSR; ; * The instruction is a CPDO or CPRT; ; * The instruction is capable of delivering an imprecise exception (e.g. ; not a purely software-implemented instruction, a FIX or a compare); ; ; Testing this last condition is complicated: it is done via the bit table ; mentioned above. ; ; The optimisation that the whole test is unnecessary for FPE-only code is ; not performed here: it is instead done in the main assembly source and ; this macro is never used. MACRO $label TestImp $dst,$t,$tbl_fpa ASSERT $dst <> Rins ASSERT $dst <> Rfpsr ASSERT $t <> Rins ASSERT $t <> Rfpsr ASSERT $t <> $dst $label ; Address the table ADR $t,$tbl_fpa ; Then look up correct table entry AND $dst,Rins,#RTnotDO_bit BiShift LDR,$dst,$t,$dst,LSR #RTnotDO_pos,LSL #2 TST Rins,#Op2_mask MOVNE $dst,$dst,LSR #16 AND $t,Rins,#Op1_mask MOV $t,$t,LSR #Op1_pos ; Now incorporate other tests MOV $dst,$dst,LSR $t ;Bit0 is result so far AND $dst,$dst,Rfpsr,LSR #31 ;Isolate bit0 and AND ; with (hardware in use) BIC $dst,$dst,Rfpsr,LSR #SO_pos ;AND with (SO bit clear) AND $dst,$dst,Rins,LSR #RTDOnotDT_pos ;AND with (CPRT or CPDO) MEND ;=========================================================================== ; Macros used for byte arrays. GBLA ByteArrayCount ByteArrayCount SETA 0 MACRO $label BytesStart ALIGN $label ByteArray_$ByteArrayCount MEND MACRO $label BytesEnd ALIGN $label ByteArrayEnd_$ByteArrayCount ByteArrayCount SETA ByteArrayCount+1 MEND ;=========================================================================== END