;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; Microsoft Research Singularity ;;; ;;; Copyright (c) Microsoft Corporation. All rights reserved. ;;; ;;; This file contains ARM-specific assembly code. ;;; ; veneer_d.s ; ; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. ; ; RCS Revision: 1 ; Checkin Date: 2007/06/29 02:59:16 ; Revising Author ;=========================================================================== ;Veneers onto the arith.asm functions. ; ;This block should be assembled multiple times, once for each function. ;The possible functions are: ; ; addsub_s double precision add and subtract ; mul_s double precision multiply ; div_s double precision divide ; fmod_s implementation of math.h's fmod() [REM] ; sqrt_s implementation of math.h's sqrt() [SQRT] GET fpe.asm GET kxarm.inc ;=========================================================================== ; When veneering these functions we need to be able to convert from double ; to extended on entry and back again on exit. This macro provides the ; conversion function. ; *WARNING* If no ulabel is set then the next instruction is skipped in ; the case of a number that needs normalizing. This is INTENTIONAL, since ; this macro leaves the Z flag set in the case of an uncommon case, but ; might also leave it set in the case of a denorm, so the following, ; conditional, instruction is skipped. It might be better if ulabel weren't ; there at all, just to make it explicit. ; The __fp_norm_opx functions should also do the skipping, rather than ; hacking lr in the fast path. This should be fixed, but for the moment ; I'd rather not be so disgustingly vile. MACRO $label DoubleToInternal $op,$zlabel,$ulabel ASSERT ($op = 1) :LOR: ($op = 2) $label MOVS tmp,dOP$op.h,LSL #1 ;C:=sign; Z:=exp & frac.top zero TEQEQ dOP$op.l,#0 ;C unchanged; Z:=value is a zero [ "$zlabel" <> "" BEQ $zlabel ;Possible early abort ] MOV Rtmp,tmp,LSL #DExp_len-1 ;Frac.top in bits 30:11 of mhi MOV dOP$op.h,tmp,LSR #DExp_pos ;Exponent in bits 11:1 MOV OP$op.mlo,dOP$op.l,LSL #DExp_len; OP2mhi and 31:11 of OP2mlo ORR OP$op.mhi,Rtmp,dOP$op.l,LSR #DFhi_len+1 ;Fraction in bits 30:0 of ADDNE dOP$op.h,dOP$op.h,#(EIExp_bias - DExp_bias):SHL:1 MOV OP$op.sue,dOP$op.h,RRX ;Recombine sign and exponent ORRNE OP$op.mhi,OP$op.mhi,#EIUnits_bit ; Gets here with the *double precision* exponent in the top 11 bits ; of tmp. (Exponent<<1+DExp_pos.) We use a sign extended shift to ; spot the "maximum exponent case" - leaves us with -1 in tmp. MOVS tmp,tmp,ASR #1+DExp_pos ADDEQ lr,pc,#8 ;Skip two instructions past normalise call BEQ __fp_norm_op$op CMP tmp,#&ffffffff [ "$ulabel" <> "" BEQ $ulabel ] MEND MACRO InternalToDouble BL __fp_e2d TST a4,#Error_bit VReturn EQ ORR a4,a4,#Double_bit B __fp_veneer_error MEND MACRO Double $name IMPORT __fp_e2d IMPORT __fp_norm_op1 IMPORT __fp_norm_op2 MEND ;=========================================================================== ; Veneer functions [ :DEF: add_s ; Local stack size and offset defines LOC_SIZE EQU 0x24 ; Size of local storage on stack ORG_OP2h EQU 0x20 ; Original Operand 2 high half ORG_OP2l EQU 0x1C ; Original Operand 2 high low ORG_OP1h EQU 0x18 ; Original Operand 1 high half ORG_OP1l EQU 0x14 ; Original Operand 1 high low OPCODE EQU 0x10 ; Opcode (_FpAddD or _FpSubD) ExDRESh EQU 0x0C ; Exception record default result high ExDRESl EQU 0x08 ; Exception record default result low ExOp2h EQU 0x04 ; Exception record operand 2 high half ExOp2l EQU 0x00 ; Exception record operand 2 low half ExNewResh EQU 0x14 ; Exception new result high ExNewResl EQU 0x10 ; Exception new result low AREA |.text|, CODE, READONLY Double add ; This is the veneer onto __addd, _drsb and __subd. Export __addd IMPORT __fp_addsub_common IMPORT __fp_addsub_uncommon IMPORT FPE_Raise [ :LNOT: :DEF: thumb Export __subd ; __subd and __addd prologues must be the same. NESTED_ENTRY __subd ;VEnter_16 ; Includes extra ARM entry point STMFD sp!, veneer_s ; Save off non-volatiles SUB sp, sp, #LOC_SIZE ; Local storage PROLOG_END MOV r5, #_FpSubD ; Double precision subtract STR r3, [sp, #ORG_OP2h] ; Save off original args in case of exception STR r2, [sp, #ORG_OP2l] STR r1, [sp, #ORG_OP1h] STR r0, [sp, #ORG_OP1l] ;For sub we just invert the sign of operand 2 and branch to add. EOR dOP2h, dOP2h, #Sign_bit B coreadd ENTRY_END __subd ] ; __addd and __subd prologues must be the same. NESTED_ENTRY __addd ;VEnter_16 ; Includes extra ARM entry point STMFD sp!, veneer_s ; Save off non-volatiles SUB sp, sp, #LOC_SIZE ; Local storage PROLOG_END MOV r5, #_FpAddD ; Double precision addition STR r3, [sp, #ORG_OP2h] ; Save off original args in case of exception STR r2, [sp, #ORG_OP2l] STR r1, [sp, #ORG_OP1h] STR r0, [sp, #ORG_OP1l] coreadd STR r5, [sp, #OPCODE] ; Save off operation code in case of exception ; Catch the NaNs and INFs here. MOV r4, r1, LSL #1 MOV r4, r4, LSR #21 ADD r4, r4, #1 CMP r4, #2048 BEQ dadd_arg1_nan_inf ; Arg1 is a NaN/INF ; Arg1 is finite MOV r4, r3, LSL #1 MOV r4, r4, LSR #21 ADD r4, r4, #1 CMP r4, #2048 BEQ dadd_arg2_nan_inf ; Arg2 is a NaN/INF DoubleToInternal 2,,dadd_uncommon1 DoubleToInternal 1,,dadd_uncommon BL __fp_addsub_common dadd_return BL __fp_e2d dadd_check_cause TST r3, #FPECause_mask ADDEQ sp, sp, #LOC_SIZE ; VReturn EQ LDMEQFD sp!,veneer_s IF Interworking :LOR: Thumbing BXEQ lr ELSE MOVEQ pc, lr ENDIF LDR r12, [sp, #OPCODE] ; Get saved operation code STR r0, [sp, #ExDRESl] STR r1, [sp, #ExDRESh] LDR r0, [sp, #ORG_OP2l] LDR r1, [sp, #ORG_OP2h] STR r0, [sp, #ExOp2l] STR r1, [sp, #ExOp2h] LDR r2, [sp, #ORG_OP1l] ORR r1, r3, r12 LDR r3, [sp, #ORG_OP1h] ADD r0, sp, #ExNewResl CALL FPE_Raise IF Thumbing :LAND: :LNOT: Interworking CODE16 bx pc ; switch back to ARM mode nop CODE32 ENDIF LDR r0, [sp, #ExNewResl] LDR r1, [sp, #ExNewResh] ADD sp, sp, #LOC_SIZE LDMFD sp!, veneer_s IF Interworking :LOR: Thumbing BX lr ELSE MOV pc, lr ENDIF dadd_uncommon1 ORR OP2sue,OP2sue,#Uncommon_bit DoubleToInternal 1 ; Skips next instruction if denorm dadd_uncommon ORREQ OP1sue,OP1sue,#Uncommon_bit ADR lr,dadd_return MOV Rins,#Double_mask B __fp_addsub_uncommon ; Arg1 is an INF or a NaN. ; If Arg1 is an SNaN, signal invalid and return a QNaN. ; Else if Arg1 is a QNaN, check Arg2 for an SNaN. ; If Arg2 is an SNaN, signal invalid. ; Return the Arg1 QNaN. ; Else it is an INF ; If Arg2 is an SNaN, signal invalid and return a QNaN version of Arg2. ; Else if Arg2 is a QNaN, return it. ; Else if Arg2 is an INF of the same sign, return it. ; Else if Arg2 is an INF of the opposite sign, set invalid and return a QNaN. ; Else return the INF. dadd_arg1_nan_inf ORRS r4, r0, r1, LSL #12 ; Mantissa1 == 0? BEQ dadd_arg1_inf ; Mantissa1 == 0 so is an INF TST r1, #dSignalBit ; Arg1 == SNaN? ORREQ r5, r5, #IVO_bit ; If Arg1 == SNaN, signal invalid BEQ dadd_return_qnan ; and return a QNaN version of it MOV r4, r3, LSL #1 ; Extract exponent2 MOV r4, r4, LSR #21 ; .. ADD r4, r4, #1 ; Exponent2 == 2047? CMP r4, #2048 ; .. BNE dadd_return_qnan ; If !=, cannot be an SNaN ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? BEQ dadd_return_qnan ; If == 0, cannot be an SNaN TST r3, #dSignalBit ; Arg2 is a NaN. Is it an SNaN? ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid B dadd_return_qnan ; Return Arg1 QNaN dadd_arg1_inf MOVS r4, r3, LSL #1 ; Extract exponent2 MOV r4, r4, LSR #21 ; .. ADD r4, r4, #1 ; Exponent2 == 2047? CMP r4, #2048 ; .. MOVNE r3, r5 ; Copy exception information BNE dadd_check_cause ; and return the INF ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? BEQ dadd_check_inf ; If == 0, have an INF MOV r1, r3 ; Arg2 is a NaN, so we need to copy the MOV r0, r2 ; mantissa bits to return in the QNaN. TST r3, #dSignalBit ; Is Arg2 an SNaN? ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid B dadd_return_qnan ; Return Arg2 QNaN dadd_check_inf EORS r4, r1, r3 ; Check signs MOVPL r3, r5 ; Copy exception information BPL dadd_check_cause ; Return the INF ORR r5, r5, #IVO_bit B dadd_return_qnan ; Return a QNaN ; Arg2 is a NaN or an INF. Arg1 is a finite non-zero number. ; If Arg2 is an INF, return it. ; Else if Arg2 is a QNaN, return it. ; Else if Arg2 is an SNaN, signal invalid and return a QNaN version of it. dadd_arg2_nan_inf MOV r1, r3 ; Arg2 is a NaN or INF. We need to copy MOV r0, r2 ; the bits to the Arg1 registers. ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? BEQ dadd_check_cause ; .. TST r3, #dSignalBit ; Is Arg2 an SNaN? ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid ; Fall through to return QNaN ; Returns a QNaN. R1 and R0 must contain the mantissa portion ; of the QNaN. SNaNs are converted to QNaNs here. dadd_return_qnan ORR r1, r1, #0x7F000000 ; Set exponent = 0x7FF ORR r1, r1, #0x00F80000 ; ... and set mantissa[MSb] = 1 MOV r3, r5 ; Move exception information B dadd_check_cause ENTRY_END __addd ] ;--------------------------------------------------------------------------- [ {FALSE} :LAND: :DEF: sub_s :LAND: :DEF: thumb AREA |.text|, CODE, READONLY Double sub Export __subd IMPORT __addd __subd VEnter_16 EOR dOP2h, dOP2h, #Sign_bit ; Just do a tail call to addd. In the THUMB world, code density is ; king. (The addition skips the LDM on the __addd entry point, and ; is dangerous.) B __addd+4 [ {FALSE} DoubleToInternal 2,,dsub_uncommon1 DoubleToInternal 1,,dsub_uncommon BL __fp_addsub_common dsub_return InternalToDouble dsub_uncommon1 ORR OP2sue,OP2sue,#Uncommon_bit DoubleToInternal 1 ; Skips next instruction if denorm dsub_uncommon ORREQ OP1sue,OP1sue,#Uncommon_bit ADR lr,dsub_return MOV Rins,#Double_mask B __fp_addsub_uncommon ] ] ;--------------------------------------------------------------------------- [ :DEF: rsb_s :LAND: :DEF: thumb CodeArea |FPL$$drsb| Double rsb Export _drsb IMPORT __addd _drsb VEnter_16 EOR dOP1h, dOP1h, #Sign_bit ; Same as above - branch to add code. B __addd+4 [ {FALSE} DoubleToInternal 2,,drsb_uncommon1 DoubleToInternal 1,,drsb_uncommon BL __fp_addsub_common drsb_return InternalToDouble drsb_uncommon1 ORR OP2sue,OP2sue,#Uncommon_bit DoubleToInternal 1 ; Skips next instruction if denorm drsb_uncommon ORREQ OP1sue,OP1sue,#Uncommon_bit ADR lr,drsb_return MOV Rins,#Double_mask B __fp_addsub_uncommon ] ] ;--------------------------------------------------------------------------- [ :DEF: mul_s ; Local stack size and offset defines LOC_SIZE EQU 0x20 ; Size of local storage on stack OrgOP2h EQU 0x1C ; Original Operand 2 high half OrgOP2l EQU 0x18 ; Original Operand 2 high low OrgOP1h EQU 0x14 ; Original Operand 1 high half OrgOP1l EQU 0x10 ; Original Operand 1 high low ExDRESh EQU 0x0C ; Exception record default result high ExDRESl EQU 0x08 ; Exception record default result low ExOp2h EQU 0x04 ; Exception record operand 2 high half ExOp2l EQU 0x00 ; Exception record operand 2 low half ExNewResh EQU 0x14 ; Exception new result high ExNewResl EQU 0x10 ; Exception new result low AREA |.text|, CODE, READONLY Double mul Export __muld IMPORT __fp_mult_common IMPORT __fp_mult_uncommon IMPORT FPE_Raise NESTED_ENTRY __muld ; VEnter_16 STMFD sp!, veneer_s ; Save off non-volatiles SUB sp, sp, #LOC_SIZE ; Local storage PROLOG_END STR r3, [sp, #OrgOP2h] ; Save off original args in case of exception STR r2, [sp, #OrgOP2l] STR r1, [sp, #OrgOP1h] STR r0, [sp, #OrgOP1l] ; Catch the NaNs, INFs, and Zeros here. MOV r5, #0 ; Exception information initialized to none. ORRS r4, r0, r1, LSL #1 BEQ dmul_arg1_zero ; Arg1 is zero MOV r4, r1, LSL #1 MOV r4, r4, LSR #21 ADD r4, r4, #1 CMP r4, #2048 BEQ dmul_arg1_nan_inf ; Arg1 is a NaN/INF ; Arg1 is non-zero and finite ORRS r4, r2, r3, LSL #1 BEQ dmul_return_zero ; Arg2 is zero so just return a zero MOV r4, r3, LSL #1 MOV r4, r4, LSR #21 ADD r4, r4, #1 CMP r4, #2048 BEQ dmul_arg2_nan_inf ; Arg2 is a NaN/INF DoubleToInternal 2,,dmul_uncommon1 DoubleToInternal 1,,dmul_uncommon BL __fp_mult_common dmul_return BL __fp_e2d dmul_check_cause TST r3, #FPECause_mask ADDEQ sp, sp, #LOC_SIZE ; VReturn EQ LDMEQFD r13!,veneer_s IF Interworking :LOR: Thumbing BXEQ lr ELSE MOVEQ pc, lr ENDIF STR r0, [sp, #ExDRESl] STR r1, [sp, #ExDRESh] LDR r0, [sp, #OrgOP2l] LDR r1, [sp, #OrgOP2h] STR r0, [sp, #ExOp2l] STR r1, [sp, #ExOp2h] LDR r2, [sp, #OrgOP1l] ORR r1, r3, #_FpMulD LDR r3, [sp, #OrgOP1h] ADD r0, sp, #ExNewResl CALL FPE_Raise IF Thumbing :LAND: :LNOT: Interworking CODE16 bx pc ; switch back to ARM mode nop CODE32 ENDIF LDR r0, [sp, #ExNewResl] LDR r1, [sp, #ExNewResh] ADD sp, sp, #LOC_SIZE LDMFD sp!, veneer_s IF Interworking :LOR: Thumbing BX lr ELSE MOV pc, lr ENDIF dmul_uncommon1 ORR OP2sue,OP2sue,#Uncommon_bit DoubleToInternal 1 ; Skips next instruction if denorm dmul_uncommon ORREQ OP1sue,OP1sue,#Uncommon_bit ADR lr,dmul_return MOV Rins,#Double_mask B __fp_mult_uncommon ; Arg1 is a zero. If Arg2 isn't a NaN or an INF, we return a zero. ; If Arg2 is an INF, we have an invalid operation and return a QNaN. ; If Arg2 is a QNaN, we return the QNaN. ; If Arg2 is an SNaN, we return a QNaN and signal invalid operation. dmul_arg1_zero MOV r4, r3, LSL #1 ; Extract exponent2 MOV r4, r4, LSR #21 ; .. ADD r4, r4, #1 ; Exponent2 == 2047? CMP r4, #2048 ; .. BNE dmul_return_zero ; If != 2047, return 0 ORRS r4, r2, r3, LSL #12 ; Else if mantissa2==0 ORREQ r5, r5, #IVO_bit ; If ==, invalid op BEQ dmul_return_qnan ; return QNaN MOV r1, r3 ; Else have a NaN so copy mantissas to MOV r0, r2 ; return QNaN and ... TST r1, #dSignalBit ; check for an SNaN ORREQ r5, r5, #IVO_bit ; If clear, have SNaN so invalid operation B dmul_return_qnan ; Arg1 is an INF or a NaN. ; If it is an SNaN, signal invalid and return a QNaN. ; else if it is a QNaN, check Arg2 for an SNaN. ; If Arg2 is an SNaN, signal invalid. ; Return the Arg1 QNaN. ; Else it is an INF ; If Arg2 is an SNaN, signal invalid and return a QNaN version of Arg2. ; Else if Arg2 is a QNaN, return it. ; Else if Arg2 is an INF, return an INF. ; Else if Arg2 is a zero, signal invalid and return a QNaN. dmul_arg1_nan_inf ORRS r4, r0, r1, LSL #12 ; Mantissa1 == 0? BEQ dmul_arg1_inf ; Mantissa1 == 0 so is an INF TST r1, #dSignalBit ; Arg1 == SNaN? ORREQ r5, r5, #IVO_bit ; If Arg1 == SNaN, signal invalid BEQ dmul_return_qnan ; and return a QNaN version of it MOV r4, r3, LSL #1 ; Extract exponent2 MOV r4, r4, LSR #21 ; .. ADD r4, r4, #1 ; Exponent2 == 2047? CMP r4, #2048 ; .. BNE dmul_return_qnan ; If !=, cannot be an SNaN ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? BEQ dmul_return_qnan ; If == 0, cannot be an SNaN TST r3, #dSignalBit ; Arg2 is a NaN. Is it an SNaN? ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid B dmul_return_qnan ; Return Arg1 QNaN dmul_arg1_inf ORRS r4, r2, r3, LSL #1 ; Arg2 == 0? ORREQ r5, r5, #IVO_bit ; If == 0, signal invalid, return BEQ dmul_return_qnan ; return a QNaN MOVS r4, r3, LSL #1 ; Extract exponent2 MOV r4, r4, LSR #21 ; .. ADD r4, r4, #1 ; Exponent2 == 2047? CMP r4, #2048 ; .. BNE dmul_return_inf ; If !=, cannot be a NaN or INF ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? BEQ dmul_return_inf ; If == 0, cannot be a NaN MOV r1, r3 ; Arg2 is a NaN, so we need to copy the MOV r0, r2 ; mantissa bits to return in the QNaN. TST r3, #dSignalBit ; Is Arg2 an SNaN? ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid B dmul_return_qnan ; Return Arg2 QNaN ; Arg2 is a NaN or an INF. Arg1 is a finite non-zero number. ; If Arg2 is an INF, return it. ; Else if Arg2 is a QNaN, return it. ; Else if Arg2 is an SNaN, signal invalid and return a QNaN version of it. dmul_arg2_nan_inf ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? BEQ dmul_return_inf ; If == 0, Arg2 is an INF, so return it. MOV r1, r3 ; Arg2 is a NaN, so we need to copy the MOV r0, r2 ; mantissa bits to return in the QNaN. TST r3, #dSignalBit ; Is Arg2 an SNaN? ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid B dmul_return_qnan ; Return Arg2 QNaN ; Returns a QNaN. R1 and R0 must contain the mantissa portion ; of the QNaN. SNaNs are converted to QNaNs here. dmul_return_qnan ORR r1, r1, #0x7F000000 ; Set exponent = 0x7FF ORR r1, r1, #0x00F80000 ; ... and set mantissa[MSb] = 1 MOV r3, r5 ; Move exception information B dmul_check_cause ; Returns a properly signed INF. r1 and r3 must contain the ; sign bits in the MSb. dmul_return_inf EORS r4, r1, r3 ; Check signs of Arg1 and Arg2 MOV r0, #0 ; Clear mantissa2 ... MOV r1, #0x7F000000 ; ... and set exponent = 0x7FF ORR r1, r1, #0x00F00000 ; .. ORRMI r1, r1, #0x80000000 ; Set sign bit if negative MOV r3, r5 ; Move exception information B dmul_check_cause ; Returns a properly signed zero. r1 and r3 must contain the ; sign bits in the MSb. dmul_return_zero EORS r4, r1, r3 ; Check signs of Arg1 and Arg2 MOV r0, #0 ; Clear sign, exponent, and mantissa MOV r1, #0 ; .. ORRMI r1, r1, #0x80000000 ; Set sign bit if negative MOV r3, r5 ; Move exception information B dmul_check_cause ENTRY_END __muld ] ;--------------------------------------------------------------------------- [ :DEF: div_s ; Local stack size and offset defines LOC_SIZE EQU 0x20 ; Size of local storage on stack OrgOP2h EQU 0x1C ; Original Operand 2 high half OrgOP2l EQU 0x18 ; Original Operand 2 high low OrgOP1h EQU 0x14 ; Original Operand 1 high half OrgOP1l EQU 0x10 ; Original Operand 1 high low ExDRESh EQU 0x0C ; Exception record default result high ExDRESl EQU 0x08 ; Exception record default result low ExOp2h EQU 0x04 ; Exception record operand 2 high half ExOp2l EQU 0x00 ; Exception record operand 2 low half ExNewResh EQU 0x14 ; Exception new result high ExNewResl EQU 0x10 ; Exception new result low AREA |.text|, CODE, READONLY Double div Export __divd IMPORT FPE_Raise IMPORT __fp_div_common IMPORT __fp_div_uncommon IMPORT __fp_veneer_error ; RDCFix: Get rid of this. NESTED_ENTRY __divd ; VEnter_16 STMFD r13!, veneer_s ; Save off non-volatiles SUB sp, sp, #LOC_SIZE ; Local storage PROLOG_END STR r3, [sp, #OrgOP2h] ; Save off original args in case of exception STR r2, [sp, #OrgOP2l] STR r1, [sp, #OrgOP1h] STR r0, [sp, #OrgOP1l] ; Catch the NaNs, INFs, and Zeros here. MOV r5, #0 ; Exception information initialized to none. ORRS r4, r0, r1, LSL #1 BEQ ddiv_arg1_zero ; Arg1 is zero MOV r4, r1, LSL #1 MOV r4, r4, LSR #21 ADD r4, r4, #1 CMP r4, #2048 BEQ ddiv_arg1_nan_inf ; Arg1 is a NaN/INF ; Arg1 is non-zero and finite ORRS r4, r2, r3, LSL #1 BEQ ddiv_zero_divide ; Arg2 is zero so have zero divide MOV r4, r3, LSL #1 MOV r4, r4, LSR #21 ADD r4, r4, #1 CMP r4, #2048 BEQ ddiv_arg2_nan_inf ; Arg2 is a NaN/INF DoubleToInternal 2,ddiv_zero2,ddiv_uncommon1 DoubleToInternal 1,ddiv_zero1,ddiv_uncommon MOV Rins,#Double_mask BL __fp_div_common ddiv_return BL __fp_e2d ddiv_check_cause TST r3, #FPECause_mask ADDEQ sp, sp, #LOC_SIZE ; VReturn EQ LDMEQFD r13!,veneer_s IF Interworking :LOR: Thumbing BXEQ lr ELSE MOVEQ pc, lr ENDIF STR r0, [sp, #ExDRESl] STR r1, [sp, #ExDRESh] LDR r0, [sp, #OrgOP2l] LDR r1, [sp, #OrgOP2h] STR r0, [sp, #ExOp2l] STR r1, [sp, #ExOp2h] LDR r2, [sp, #OrgOP1l] ORR r1, r3, #_FpDivD LDR r3, [sp, #OrgOP1h] ADD r0, sp, #ExNewResl CALL FPE_Raise IF Thumbing :LAND: :LNOT: Interworking CODE16 bx pc ; switch back to ARM mode nop CODE32 ENDIF LDR r0, [sp, #ExNewResl] LDR r1, [sp, #ExNewResh] ADD sp, sp, #LOC_SIZE LDMFD r13!, veneer_s IF Interworking :LOR: Thumbing BX lr ELSE MOV pc, lr ENDIF ddiv_uncommon1 ORR OP2sue,OP2sue,#Uncommon_bit DoubleToInternal 1,ddiv_zero3 ddiv_uncommon ORREQ OP1sue,OP1sue,#Uncommon_bit ADR lr,ddiv_return MOV Rins,#Double_mask B __fp_div_uncommon ddiv_zero3 ; Op1 is a zero, Op2 is an uncommon non-zero. Op2 is in the converted form. ; Op2 is an infinity if all bits are zero (result is a signed zero). Otherwise ; a quiet NaN/exception. ORRS tmp,OP2mlo,OP2mhi,LSL #1 BEQ ddiv_zero1 MOVS OP2mhi,OP2mhi,LSL #1 BPL ddiv_ivo ; return any old quiet NaN ; RDCFix: Get rid of this stuff. ;ddiv_return_qnan ; MOV dOPh,#-1 ; VReturn ddiv_zero2 ; Op2 is a zero. If operand 1 is a zero or a SNaN, this is an invalid ; operation, otherwise it is a divide by zero. MOVS tmp, dOP1h, LSL #1 TEQEQ dOP1l, #0 ; Z <- zero BEQ ddiv_ivo MVNS tmp, tmp, ASR #32-DExp_len-1 ; Z <- QNaN VReturn EQ ; Return Op1 (QNaN) ; tmp==1 and mantissa==0 => Inf (Inf) ; tmp==1 and mantissa!=0 => SNaN (IVO) TEQ tmp, #1 BNE ddiv_dvz ORRS tmp, dOP1l, dOP1h, LSL #DExp_len+1 ; Z <- zero mantissa (Inf) ; MLS 2890 ; Infinty/Zero returns appropriately signed infinity. ; Given the representations of Infinty and Zero, we can get ; this sign in a single instruction. EOREQ dOP1h, dOP1h, dOP2h VReturn EQ ; Return Op1 (Inf) ddiv_ivo MOV a4, #IVO_bit:OR:Double_bit B __fp_veneer_error ddiv_dvz MOV a4, #DVZ_bit:OR:Double_bit ; MLS report 2899 ; division by -0.0 should return inversely signed infinity. EOR dOP1h, dOP1h, dOP2h B __fp_veneer_error ddiv_zero1 ; Op1 is a zero, Op2 is in the extended form, and can't be an "uncommon". EOR dOP1h, dOP1h, OP2sue AND dOP1h, dOP1h, #Sign_bit VReturn ; Arg1 is a zero. ; If Arg2 is a zero, set invalid operation and return a QNaN. ; Else if Arg2 is an SNaN, set invalid and return a QNaN version of the SNaN. ; Else if Arg2 is a QNaN, return it. ; Else return a zero. ddiv_arg1_zero ORRS r4, r2, r3, LSL #1 ; If Arg2 == 0 ORREQ r5, r5, #IVO_bit ; set invalid BEQ ddiv_return_qnan ; return a QNaN MOV r4, r3, LSL #1 ; Extract exponent2 MOV r4, r4, LSR #21 ; .. ADD r4, r4, #1 ; If exponent2 == 2047 CMP r4, #2048 ; .. BNE ddiv_return_zero ; Arg2 finite, return a zero ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? BEQ ddiv_return_zero ; Have an INF, return a zero TST r3, #dSignalBit ; SNaN? ORREQ r5, r5, #IVO_bit ; If SNaN, set invalid MOV r0, r2 ; Copy mantissa2 for QNaN return MOV r1, r3 ; .. B ddiv_return_qnan ; Return QNaN ; Arg1 is a NaN or an INF. ; If Arg1 is an INF ; If Arg2 is an SNaN, set invalid operation and return a QNaN version of ; the SNaN. ; If Arg2 is a QNaN, return it. ; If Arg2 is an INF, set invalid operation and return a QNaN. ; Else return an INF. ; Else if Arg1 is an SNaN, set invalid operation and return a QNaN version ; of the SNaN. ; Else if Arg1 is a QNaN. ; If Arg2 is an SNaN, set invalid and return the QNaN. ; Else return the QNaN. ddiv_arg1_nan_inf ORRS r4, r0, r1, LSL #12 ; Mantissa1 == 0? BEQ ddiv_arg1_inf ; If ==0, have an INF TST r1, #dSignalBit ; Check if Arg1 is an SNaN ORREQ r5, r5, #IVO_bit ; If is an SNaN, signal invalid BEQ ddiv_return_qnan ; and return a QNaN version of it MOV r4, r3, LSL #1 ; Extract exponent2 MOV r4, r4, LSR #21 ; .. ADD r4, r4, #1 ; If exponent2 == 2047 CMP r4, #2048 ; .. BNE ddiv_return_qnan ; If !=, Arg2 finite, so return the QNaN ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? BEQ ddiv_return_qnan ; If ==, Arg2 is INF, so return the QNaN TST r3, #dSignalBit ; Check for SNaN ORREQ r5, r5, #IVO_bit ; If == 0, set invalid operation B ddiv_return_qnan ddiv_arg1_inf MOV r4, r3, LSL #1 ; Extract exponent2 MOV r4, r4, LSR #21 ; .. ADD r4, r4, #1 ; If exponent2 == 2047 CMP r4, #2048 ; .. BNE ddiv_return_inf ; If !=, Arg2 is finite, so return an INF ORRS r4, r2, r3, LSL #12 ; Mantissa2 == 0? ORREQ r5, r5, #IVO_bit ; If ==, have an INF, so set invalid BEQ ddiv_return_qnan ; and return a QNaN MOV r0, r2 ; Copy mantissa2 for QNaN return MOV r1, r3 ; .. TST r3, #dSignalBit ; Is Arg2 an SNaN ORREQ r5, r5, #IVO_bit ; If it is, set invalid operation B ddiv_return_qnan ; Return QNaN ; Arg2 is a NaN or INF. Arg1 is finite, possibly zero. ; If Arg2 is an INF, return zero. ; Else if Arg2 is an SNaN, set invalid operation and return a QNaN version of the SNaN. ; Else if Arg2 is a QNaN, return it. ddiv_arg2_nan_inf ORRS r4, r2, r3, LSL #12 ; If Arg2 == INF BEQ ddiv_return_zero ; Then return zero TST r3, #dSignalBit ; Have a NaN, check for SNaN ORREQ r5, r5, #IVO_bit ; If == 0, set invalid MOV r1, r3 ; Copy mantissa2 for QNaN return MOV r0, r2 ; .. B ddiv_return_qnan ; Return QNaN ; Returns a QNaN. R1 and R0 must contain the mantissa portion ; of the QNaN. SNaNs are converted to QNaNs here. ddiv_return_qnan ORR r1, r1, #0x7F000000 ; Set exponent = 0x7FF ORR r1, r1, #0x00F80000 ; ... and set mantissa[MSb] = 1 MOV r3, r5 ; Move exception information B ddiv_check_cause ; Sets the divide-by-zero exception and falls through to return an INF. ddiv_zero_divide ORR r5, r5, #DVZ_bit ; Set zero divide ; Returns a properly signed INF. r1 and r3 must contain the ; sign bits in the MSb. ddiv_return_inf EORS r4, r1, r3 ; Check signs of Arg1 and Arg2 MOV r0, #0 ; Clear mantissa2 ... MOV r1, #0x7F000000 ; ... and set exponent = 0x7FF ORR r1, r1, #0x00F00000 ; .. ORRMI r1, r1, #0x80000000 ; Set sign bit if negative MOV r3, r5 ; Move exception information B ddiv_check_cause ; Returns a properly signed zero. r1 and r3 must contain the ; sign bits in the MSb. ddiv_return_zero EORS r4, r1, r3 ; Check signs of Arg1 and Arg2 MOV r0, #0 ; Clear sign, exponent, and mantissa MOV r1, #0 ; .. ORRMI r1, r1, #0x80000000 ; Set sign bit if negative MOV r3, r5 ; Move exception information B ddiv_check_cause ENTRY_END __divd ] ;--------------------------------------------------------------------------- [ :DEF: rdv_s CodeArea |FPL$$drdv| Double rdv IMPORT __fp_rdv_common IMPORT __fp_rdv_uncommon IMPORT __fp_veneer_error ;Export _drdv Export _drdiv ;_drdv _drdiv VEnter_16 DoubleToInternal 2,drdv_zero2,drdv_uncommon1 DoubleToInternal 1,drdv_dvz,drdv_uncommon MOV Rins,#Double_mask :OR: Reverse BL __fp_rdv_common drdv_return InternalToDouble drdv_uncommon1 ORR OP2sue,OP2sue,#Uncommon_bit DoubleToInternal 1,drdv_zero1 drdv_uncommon ORREQ OP1sue,OP1sue,#Uncommon_bit ADR lr,drdv_return MOV Rins,#Double_mask:OR:Reverse B __fp_rdv_uncommon drdv_zero1 ; Op2 is uncommon, but Op1 is a zero. Return Inf for Op2=Inf, IVO for ; Op2=SNaN or a QNaN for Op2=QNaN MOVS tmp, dOP2h, LSL #DExp_len+1 ; N <- QNaN TEQEQ dOP1l, #0 ; Z <- Inf MOVMIS tmp, #0 ; Z <- N BNE drdv_ivo MOV dOP1h, dOP2h ; Return a QNaN/Inf MOV dOP1l, dOP2l VReturn drdv_zero2 ; Op2 is a zero. If Op1 is a zero or SNaN, this is an invalid operation, ; otherwise it is an appropiately signed zero unless Op1=QNaN MOVS tmp, dOP1h, LSL #1 TEQEQ dOP1l, #0 ; Z <- Op1=0 BEQ drdv_ivo MVNS tmp, tmp, ASR #32-DExp_len-1 ; Z <- Op1=QNaN VReturn EQ ; Return QNaN ORRS dOP1l, dOP1l, dOP1h, LSL #DExp_len+1 ; Z <- zero mantissa BEQ drdv_return_zero TEQ tmp, #1 ; Z <- SNaN BEQ drdv_ivo drdv_return_zero EOR dOP1h, dOP1h, dOP2h AND dOP1h, dOP1h, #Sign_bit MOV dOP1l, #0 VReturn drdv_dvz MOV a4,#DVZ_bit:OR:Double_bit B __fp_veneer_error drdv_ivo MOV a4,#IVO_bit:OR:Double_bit B __fp_veneer_error ] ;--------------------------------------------------------------------------- [ :DEF: fmod_s CodeArea |FPL$$dfmod| Double fmod EXPORT fmod Import_32 __fp_edom fmod VEnter DoubleToInternal 2,fmod_divide_by_zero,fmod_uncommon1 DoubleToInternal 1,fmod_Op1Zero,fmod_uncommon BL Rem_Common fmod_return InternalToDouble fmod_Op1Zero ; Op1 is zero => result is Op1. Op1h/Op1l hasn't been changed. VReturn fmod_uncommon1 ORR OP2sue,OP2sue,#Uncommon_bit DoubleToInternal 1 fmod_uncommon ORREQ OP1sue,OP1sue,#Uncommon_bit ADR lr,fmod_return MOV Rins,#Double_mask B Rem_Uncommon fmod_divide_by_zero ; We return -HUGE_VAL and set errno=EDOM VPull MOV a1, #Sign_bit MOV a2, #1 ; true B_32 __fp_edom GET arith.asm ] ;--------------------------------------------------------------------------- [ :DEF: sqrt_s CodeArea |FPL$$dsqrt| Double sqrt EXPORT sqrt IMPORT __fp_sqrt_common IMPORT __fp_sqrt_uncommon sqrt VEnter DoubleToInternal 1,sqrt_Zero,sqrt_uncommon MOV Rins,#Double_mask BL __fp_sqrt_common sqrt_return BL __fp_e2d TST a4, #Error_bit VReturn EQ ; error - set errno to EDOM and return -HUGE_VAL MOV a1, #Sign_bit MOV a2, #1 ; something non-zero sqrt_edom Import_32 __fp_edom ; tail call VPull B_32 __fp_edom sqrt_Zero ; C contains the sign bit - if set, record a domain error, ; but return -0.0 (which is what's in a1/a2 already) [ :DEF: SqrtMinusZeroGivesEDOM VReturn CC B sqrt_edom ; save a few bytes in error case | ; Otherwise, just return the zero passed in VReturn ] sqrt_uncommon ORR OP1sue,OP1sue,#Uncommon_bit ADR lr,sqrt_return MOV Rins,#Double_mask B __fp_sqrt_uncommon ] ;=========================================================================== END