369 lines
14 KiB
NASM
369 lines
14 KiB
NASM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;
|
|
;;; 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
|