;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; Microsoft Research Singularity ;;; ;;; Copyright (c) Microsoft Corporation. All rights reserved. ;;; ;;; This file contains ARM-specific assembly code. ;;; ; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved. ; ; RCS Revision: 1 ; Checkin Date: 2007/06/29 02:59:16 ; Revising Author GET fpe.asm GET kxarm.inc CodeArea |.text| IMPORT FPE_Raise SNaNInf EQU NaNInfExp_Single - EIExp_bias + SExp_bias DNaNInf EQU NaNInfExp_Double - EIExp_bias + DExp_bias DExp_max EQU 2047 SFrac_len EQU 23 exp RN 3 sign RN 2 tmp RN 12 ;============================================================================== ;Format conversions [ :DEF: d2f_s ; Local storage size and offsets LOC_SIZE EQU 0x18 OrgOp1h EQU 0x14 OrgOp1l EQU 0x10 ExDResl EQU 0x08 NewResl EQU 0x10 ; Ensure the defines are what they should be. Note that there was ; a problem when we swapped the double halves as the code relied on ; the low half of the double not being the register where floats are ; returned. I have put these asserts here so the registers don't ; change out from under me. We don't really need these conditions ; to hold true, just the code needs to be checked after register ; defines are changed. ASSERT exp = r3 ASSERT sign = r2 ASSERT tmp = r12 ASSERT dOPh = r1 ASSERT dOPl = r0 ASSERT fOP = r0 Export __dtos NESTED_ENTRY __dtos EnterWithLR_16 STMFD sp!, {r4, lr} ; Save off non-volatile, lr SUB sp, sp, #LOC_SIZE ; Allocate stack space PROLOG_END STR r1, [sp, #OrgOp1h] ; Save off arg in case of exception MOV r4, #_FpDToS ; Set double->float convert ORRS tmp, dOPl, dOPh, LSL #1 ; Special check for zero STR r0, [sp, #OrgOp1l] ; Save off arg in case of exception BEQ __dtos_return_zero ; If zero, return it AND sign, dOPh, #Sign_bit MOV tmp, #(DExp_bias - SExp_bias) << (DExp_pos+1) RSB dOPh, tmp, dOPh, LSL #1 MOVS exp, dOPh, LSR #DExp_pos+1 BEQ _d2f_ExpUnderflow CMP exp, #254 BHS _d2f_uncommon _d2f_round MOVS tmp, dOPl, LSL #3 ; Check for inexact ORRNE r4, r4, #INX_bit ; Raise inexact if inexact ORRS tmp, sign, dOPl, LSR #29 MOV r3, dOPl ; * Need to save dOPl somewhere else ADC fOP, tmp, dOPh, LSL #2 ; dOPh already shifted 1 bit BCC __dtos_return MOVS r3, r3, LSL #4 ; * Was: MOVS dOPl, dOPl, LSL #4 BNE __dtos_return BIC fOP, fOP, #1 B __dtos_return _d2f_uncommon ; exp out of range - check for special cases CMP exp, #(-(DExp_bias - SExp_bias)) :AND: 0x7FF BHS _d2f_ExpUnderflow ; if exponent 254 - test for overflow during rounding CMP exp, #254 MOVEQ tmp, dOPh, LSL #DExp_len ORREQ tmp, tmp, dOPl, LSR #DFhi_len CMNEQ tmp, #1 << 8 ; check if dOP rounds to overflow BLO _d2f_round ; no - continue (8 clk overhead) _d2f_ExpOverflow ; overflow, inf/NaN ADD exp, exp, #1 TEQ exp, #DExp_max - DExp_bias + SExp_bias + 1 MOVNE fOP, sign BNE __dtos_return_overflow ; overflow _d2f_Inf_or_NaN ; found inf or NaN ORRS tmp, dOPl, dOPh, LSL #DExp_len ; infinity if EQ MOVEQ tmp, #0xFF000000 ORR fOP, sign, tmp, LSR #1 ; return signed infinity BEQ __dtos_return ; sign in fOP MOVS tmp, dOPh, LSL #DExp_len ORRPL r4, r4, #IVO_bit ; Set invalid if SNaN LDR r2, fNaN ; Return quiet NaN MOV tmp, dOPh, LSL #DExp_len-9 ; insert high mantissa bits ORR tmp, tmp, dOPl, LSR #29 ; insert low mantissa bits ORR r0, r2, tmp ; Set exp, high mant bit B __dtos_return fNaN DCD &7FC00000 __dtos_return_zero MOV fOP, dOPh ; dOPh has the correctly signed B __dtos_return ; float zero, so return it _d2f_ExpUnderflow CMP exp, #0 SUBNE exp, exp, #0x800 ; was underflow - return denorm or zero (exp is -X .. 0) RSB exp, exp, #SExp_len+1 ; right shift for dOPh RSBS tmp, exp, #33 ; left shift for dOPh rounding bits MOVLS fOP, sign ; LO: shift larger than 33 -> return signed zero ORRLS r4, r4, #UNF_bit :OR: INX_bit BLS __dtos_return MOV dOPh, dOPh, LSL #DExp_len-1 ; dOPh shift left 1 bit ORR dOPh, dOPh, dOPl, LSR #DFhi_len+1 ORR dOPh, dOPh, #1 << 31 MOVS tmp, dOPh, LSL tmp ; CS -> round ORRNE r4, r4, #UNF_bit :OR: INX_bit MOV tmp, dOPl ; save dOPl since we may need it ADC fOP, sign, dOPh, LSR exp BCC __dtos_return MOVEQS tmp, tmp, LSL #32 - (DFhi_len+1) ; * Was: MOVEQS dOPl, dOPl, LSL #32 BICEQ fOP, fOP, #1 B __dtos_return __dtos_return_overflow ORR r4, r4, #OVF_bit :OR: INX_bit ; Set exception information AND r0, r0, #0x80000000 ; Keep sign bit MOV r1, #0xFF000000 ; Set exponent to max ORR r0, r0, r1, LSR #1 ; .. __dtos_return TST r4, #FPECause_mask ; Check for exceptions MOV r1, r4 ; Move exception info. ADDEQ sp, sp, #LOC_SIZE ; If none, pop input arg IF Interworking :LOR: Thumbing LDMEQFD sp!, {r4, lr} ; restore r4 and return BXEQ lr ELSE LDMEQFD sp!, {r4, pc} ; restore r4 and return ENDIF STR r0, [sp, #ExDResl] ; Store default result LDR r2, [sp, #OrgOp1l] ; Load original arg LDR r3, [sp, #OrgOp1h] ; .. ADD r0, sp, #NewResl ; Pointer to new result CALL FPE_Raise ; Deal with exception info. IF Thumbing :LAND: :LNOT: Interworking CODE16 bx pc ; switch back to ARM mode nop CODE32 ENDIF LDR r0, [sp, #NewResl] ; Load new result ADD sp, sp, #LOC_SIZE ; Pop exception record, arg IF Interworking :LOR: Thumbing LDMFD sp!, {r4, lr} ; Restore r4 and return BX lr ELSE LDMFD sp!, {r4, pc} ; Restore r4 and return ENDIF ENTRY_END __dtos ] ;------------------------------------------------------------------------------ [ :DEF: f2d_s LOC_SIZE EQU 0x18 OrgOp1l EQU 0x14 ExDResh EQU 0x0c ExDResl EQU 0x08 NewResh EQU 0x14 NewResl EQU 0x10 Export __stod IMPORT FPE_Raise ; Ensure the defines are what they should be. Note that there was ; a problem when we swapped the double halves as the code relied on ; the low half of the double not being the register where floats are ; passed. I have put these asserts here so the registers don't ; change out from under me. We don't really need these conditions ; to hold true, just the code needs to be checked after register ; defines are changed. ASSERT exp = r3 ASSERT sign = r2 ASSERT tmp = r12 ASSERT dOPh = r1 ASSERT dOPl = r0 ASSERT fOP = r0 NESTED_ENTRY __stod EnterWithLR_16 STMFD sp!, {lr} ; Save return address SUB sp, sp, #LOC_SIZE ; Allocate local storage PROLOG_END STR r0, [sp, #OrgOp1l] ; Store original arg in case of exception ADD tmp, fOP, #1 << SExp_pos ; filter out inf/NaN/denorm/zero TST tmp, #254 << SFrac_len BEQ _f2d_uncommon MOV tmp, fOP MOV dOPl, tmp, LSL #32 - 3 MOVS dOPh, tmp, ASR #3 ADD dOPh, dOPh, #(DExp_bias - SExp_bias) << DExp_pos ADDPL sp, sp, #LOC_SIZE IF Interworking :LOR: Thumbing LDMPLFD sp!, {lr} BXPL lr ELSE LDMPLFD sp!, {pc} ENDIF SUB dOPh, dOPh, #0x700 << DExp_pos ADD sp, sp, #LOC_SIZE IF Interworking :LOR: Thumbing LDMFD sp!, {lr} BX lr ELSE LDMFD sp!, {pc} ENDIF _f2d_uncommon TST tmp, #1 << SExp_pos ; inf/NaN -> EQ, zero/denorm ->NE MOV tmp, fOP BEQ _f2d_Inf_or_NaN _f2d_denorm MOVS dOPl, tmp, LSL #1 ; zero -> EQ MOVEQ dOPh, tmp ADDEQ sp, sp, #LOC_SIZE ; dOPl zero, dOPh sign bit IF Interworking :LOR: Thumbing LDMEQFD sp!, {lr} BXEQ lr ELSE LDMEQFD sp!, {pc} ENDIF ; We have a denormal that must be normalized. The exponent and sign ; are initialized, then the input argument's mantissa is shifted left ; until the hidden one is left justified. The exponent is adjusted to ; account for the shifts. Then the hidden one is removed and the ; final result assembled. AND exp, tmp, #Sign_bit ; Extract sign ADD r1, exp, #(DExp_bias-SExp_bias) << DExp_pos ; Initialize exponent MOV r0, tmp, LSL #9 ; Extract mantissa and left justify MOVS r2, r0, LSR #16 ; Any high 16 bits set? SUBEQ r1, r1, #16 << DExp_pos ; If not, adjust exponent MOVEQ r0, r0, LSL #16 ; shift mantissa TST r0, #0xFF000000 ; Any high 8 bits set? SUBEQ r1, r1, #8 << DExp_pos ; If not, adjust exponent MOVEQ r0, r0, LSL #8 ; shift mantissa TST r0, #0xF0000000 ; Any high 4 bits set? SUBEQ r1, r1, #4 << DExp_pos ; If not, adjust exponent MOVEQ r0, r0, LSL #4 ; shift mantissa TST r0, #0xC0000000 ; Any high 2 bits set? SUBEQ r1, r1, #2 << DExp_pos ; If not, adjust exponent MOVEQS r0, r0, LSL #2 ; shift mantissa MOVPL r0, r0, LSL #1 ; If high bit clear, adjust exponent SUBPL r1, r1, #1 << DExp_pos ; shift mantissa MOV r0, r0, LSL #1 ; Account for hidden 1 ORR r1, r1, r0, LSR #12 ; Form sign, exp, high mantissa MOV r0, r0, LSL #20 ; Form low mantissa ADD sp, sp, #LOC_SIZE ; Restore stack and IF Interworking :LOR: Thumbing LDMFD sp!, {lr} ; return BX lr ELSE LDMFD sp!, {pc} ; return ENDIF _f2d_Inf_or_NaN ; fOP is NaN/infinity. MOVS dOPl, tmp, LSL #SExp_len+1 ; EQ -> inf ORREQ dOPh, tmp, #0x007 << DExp_pos ; tranform float inf to double inf ADDEQ sp, sp, #LOC_SIZE ; Restore stack and IF Interworking :LOR: Thumbing LDMEQFD sp!, {lr} ; return BXEQ lr ELSE LDMEQFD sp!, {pc} ; return ENDIF ; MI if quiet NaN - sign in fOP BPL __stod_snan ; Have a QNaN so copy the mantissa bits and return the QNaN MOV dOPh, tmp, ASR #3 ; Load mantissa high, sign MOV dOPl, tmp, LSL #29 ; Load mantissa low ORR dOPh, dOPh, #0x70000000 ; Force exp to max ADD sp, sp, #LOC_SIZE ; Restore stack and IF Interworking :LOR: Thumbing LDMFD sp!, {lr} ; return BX lr ELSE LDMFD sp!, {pc} ; return ENDIF __stod_snan ; Got an SNaN so raise exception LDR r2, [sp, #OrgOp1l] ; Load original operand MOV r1, r12, ASR #3 ; Load mantissa high, sign MOV r0, r12, LSL #29 ; Load mantissa low ORR r1, r1, #0x70000000 ; Force exp to max ORR r1, r1, #0x00080000 ; Make QNaN STR r1, [sp, #ExDResh] ; Store default result STR r0, [sp, #ExDResl] ; .. ADD r0, sp, #NewResl ; Pointer to return result MOV r1, #_FpSToD ; Load opcode and exception info. ORR r1, r1, #IVO_bit ; .. CALL FPE_Raise ; Deal with exception info. IF Thumbing :LAND: :LNOT: Interworking CODE16 bx pc ; switch back to ARM mode nop CODE32 ENDIF LDR r1, [sp, #NewResh] ; Load new return value LDR r0, [sp, #NewResl] ; .. ADD sp, sp, #LOC_SIZE ; Pop space off stack IF Interworking :LOR: Thumbing LDMFD sp!, {lr} ; Return BX lr ELSE LDMFD sp!, {pc} ; Return ENDIF ENTRY_END __stod ] ;============================================================================== END