singrdk/base/Kernel/Native/arm/Crt/r_i64tod.asm

223 lines
8.9 KiB
NASM

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Microsoft Research Singularity
;;;
;;; Copyright (c) Microsoft Corporation. All rights reserved.
;;;
;;; This file contains ARM-specific assembly code.
;;;
; __i64tod: __int64 to double conversion routine.
; __u64tod: unsigned __int64 to double conversion routine.
;
; Input: r1 - Most significant word of 64-bit integer to be converted
; r0 - Least significant word of 64-bit integer to be converted
;
; Output: r1 - Most significant word of converted number to floating point
; double precision format.
; r0 - Least significant word of converted number to floating point
; double precision format.
; Local storage size and offsets
LOC_SIZE EQU 0x18
OrgOp1h EQU 0x14
OrgOp1l EQU 0x10
ExDResh EQU 0x0C
ExDResl EQU 0x08
NewResh EQU 0x14
NewResl EQU 0x10
GET fpe.asm
GET kxarm.inc
Export __u64tod
Export __i64tod
IMPORT FPE_Raise
AREA |.text|, CODE, READONLY
; Prolog must match __i64tod
NESTED_ENTRY __u64tod
EnterWithLR_16
STMFD sp!, {lr} ; Save return address
SUB sp, sp, #LOC_SIZE ; Allocate local storage
PROLOG_END
STR r0, [sp, #OrgOp1l] ; Save original arg in case of exception
ORRS r2, r0, r1 ; Check for zero
STR r1, [sp, #OrgOp1h] ; Save original arg in case of exception
ADDEQ sp, sp, #LOC_SIZE ; If zero, restore stack and
IF Interworking :LOR: Thumbing
LDMEQFD sp!, {lr} ; return zero
BXEQ lr
ELSE
LDMEQFD sp!, {pc} ; return zero
ENDIF
MOV r14, #_FpU64ToD ; Initialize opcode, no exceptions
MOV r12, #0 ; Initialize sign to 0 (positive)
B LongSingleNormalize
ENTRY_END __u64tod
; Prolog must match __u64tod
NESTED_ENTRY __i64tod
EnterWithLR_16
STMFD sp!, {lr} ; Save return address
SUB sp, sp, #LOC_SIZE ; Allocate local storage
PROLOG_END
STR r0, [sp, #OrgOp1l] ; Save original arg in case of exception
ORRS r2, r0, r1 ; Check for zero
STR r1, [sp, #OrgOp1h] ; Save original arg in case of exception
ADDEQ sp, sp, #LOC_SIZE ; If zero, restore stack and
IF Interworking :LOR: Thumbing
LDMEQFD sp!, {lr} ; return zero
BXEQ lr
ELSE
LDMEQFD sp!, {pc} ; return zero
ENDIF
MOV r14, #_FpI64ToD ; Initialize opcode, no exceptions
ANDS r12, r1, #Sign_bit ; Extract sign
BEQ LongSingleNormalize ; Positive value so do convert
RSBS r0, r0, #0 ; Else we have a negative number so
RSC r1, r1, #0 ; take the 2's complement inverse
; Sign is in r12.
; Mantissa abs(input 64-bit integer) is stored in r1:r0.
;
; Sign
; +-+-------------------------------+
; r12: |S|0000000000000000000000000000000|
; +-+-------------------------------+
;
; Unnormalized mantissa
; +--------------------------------+
; r1: | Most Significant Word |
; +--------------------------------+
;
; +--------------------------------+
; r0: | Least Significant Word |
; +--------------------------------+
;
; Only the high mantissa is shifted. After it is shifted into the correct
; position, the low mantissa is shifted into place. The exception is the
; first shift by 32 which moves the low mantissa into the high.
;
LongSingleNormalize
MOV r2, #0 ; Exponent adjustment/mantissa shift
CMP r1, #0 ; Any high 32 bits set?
ADDEQ r2, r2, #32 ; If not, adjust exponent
MOVEQ r1, r0 ; and shift the WHOLE mantissa
MOVEQ r0, #0 ; ..
MOVS r3, r1, LSR #16 ; Any high 16 bits set?
ADDEQ r2, r2, #16 ; If not, adjust exponent
MOVEQ r1, r1, LSL #16 ; and shift high mantissa
TST r1, #0xFF000000 ; Any high 8 bits set?
ADDEQ r2, r2, #8 ; If not, adjust exponent
MOVEQ r1, r1, LSL #8 ; and shift high mantissa
TST r1, #0xF0000000 ; Any high 4 bits set?
ADDEQ r2, r2, #4 ; If not, adjust exponent
MOVEQ r1, r1, LSL #4 ; and shift high mantissa
TST r1, #0xC0000000 ; Any high 2 bits set?
ADDEQ r2, r2, #2 ; If not, adjust exponent
MOVEQS r1, r1, LSL #2 ; and shift high mantissa
ADDPL r2, r2, #1 ; If high bit clear, adjust exponent
MOVPL r1, r1, LSL #1 ; and shift high mantissa
MOV r3, #DExp_bias+1 ; Load r3 = DExp_bias+63
ADD r3, r3, #62 ; ..
RSB r3, r2, r3 ; Calculate exponent
ORR r12, r12, r3, LSL #DExp_pos
; Combine sign and exponent
CMP r2, #32 ; If all low shifted into high ...
MOV r1, r1, LSL #1 ; Hide the hidden one
ADD r2, r2, #1 ; ..
BGE insert_mantissa ; ... just insert the mantissa
; R2 contains the amount the mantissa has been shifted left, including
; the shift for the hidden one. It is in the range 1..32. R1 contains
; the high portion of the mantissa without any of R0 shifted into it.
; R0 is unshifted. Note that the case where the entire low word was
; shifted into the high word has already been taken care of and never
; reaches this code.
RSB r3, r2, #32 ; Determine amount of low mantissa
; shifted into high mantissa
ORR r1, r1, r0, LSR r3 ; Get part of low word shifted in high
MOV r0, r0, LSL r2 ; Shift low word into position
insert_mantissa
ORR r3, r12, r1, LSR #12 ; Insert high word into result high
MOV r2, r1, LSL #20 ; Insert high word into result low
ORR r2, r2, r0, LSR #12 ; Insert low word into result low
MOVS r0, r0, LSL #20 ; Check for inexact
ORRNE r14, r14, #INX_bit ; Set inexact if bits are lost
MOVS r0, r0, LSL #1 ; Round
; CS -> guard bit
; MI -> round bit
; NE -> sticky bit | round bit
BCC __i64tod_return ; If guard bit clear, leave result
; Guard bit is set
MOVS r0, r0, LSR #1 ; Must clear carry bit
; NE -> sticky bit | round bit
TSTEQ r2, #0x1 ; Check lost bit
ADDNES r2, r2, #1 ; If G & (L | R | S) round up
ADC r3, r3, #0 ; ..
__i64tod_return
MOV r1, r3 ; Move result into return regs
MOV r0, r2 ; ..
TST r14, #FPECause_mask ; Any exceptions?
ADDEQ sp, sp, #LOC_SIZE ; pop off original arg, and
IF Interworking :LOR: Thumbing
LDMEQFD sp!, {lr} ; return
BXEQ lr
ELSE
LDMEQFD sp!, {pc} ; return
ENDIF
; Have an exception
LDR r2, [sp, #OrgOp1l] ; Load original operand
LDR r3, [sp, #OrgOp1h] ; ..
STR r0, [sp, #ExDResl] ; Store default result
STR r1, [sp, #ExDResh] ; ..
ADD r0, sp, #NewResl ; Pointer to new result
MOV r1, r14 ; Exception information
CALL FPE_Raise ; Deal with exception information
IF Thumbing :LAND: :LNOT: Interworking
CODE16
bx pc ; switch back to ARM mode
nop
CODE32
ENDIF
LDR r0, [sp, #NewResl] ; Load new result
LDR r1, [sp, #NewResh] ; ..
ADD sp, sp, #LOC_SIZE ; Pop off exception info and orig arg
IF Interworking :LOR: Thumbing
LDMFD sp!, {lr} ; Return
BX lr
ELSE
LDMFD sp!, {pc} ; Return
ENDIF
ENTRY_END __i64tod
END