438 lines
13 KiB
NASM
438 lines
13 KiB
NASM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;;
|
|
;;; Microsoft Research Singularity
|
|
;;;
|
|
;;; Copyright (c) Microsoft Corporation. All rights reserved.
|
|
;;;
|
|
;;; This file contains ARM-specific assembly code.
|
|
;;;
|
|
|
|
; basic_f.s
|
|
;
|
|
; Copyright (C) Advanced RISC Machines Limited, 1994. All rights reserved.
|
|
;
|
|
; RCS Revision: 1
|
|
; Checkin Date: 2007/06/29 02:59:16
|
|
; Revising Author
|
|
|
|
; Basic floating point functions
|
|
;
|
|
; Fixed == and != compares to be IEEE-754 compliant when input QNaNs.
|
|
; No exceptions are raised when only QNaNs are the only NaNs input to
|
|
; == and !=. Moved NaN detection and exception raising here.
|
|
; Removed unnecessary macros for compares that return results in flags.
|
|
; Added WindowsCE SEH mechanism support.
|
|
; Renamed routines.
|
|
;
|
|
|
|
LOC_SIZE EQU 0x18
|
|
OrgOp1l EQU 0x14
|
|
OrgOp2l EQU 0x10
|
|
ExDResl EQU 0x08
|
|
ExOp2l EQU 0x00
|
|
NewResl EQU 0x10
|
|
|
|
|
|
GET fpe.asm
|
|
GET kxarm.inc
|
|
|
|
|
|
;This code is very similar to the "double" versions. The documentation isn't
|
|
;extensively repeated. Refer to basic_d.s for further documentation.
|
|
|
|
|
|
;==============================================================================
|
|
; Compare.
|
|
;
|
|
; Timing:
|
|
; Flags: 7 (pos), 11 (false NaN), 9 (neg), 13 (false NaN) SA1.1 cycles
|
|
; Others: 9 / 13 / 11 / 15
|
|
;==============================================================================
|
|
|
|
|
|
MACRO
|
|
CmpReturn $cc
|
|
MOV a1, #0
|
|
MOV$cc a1, #1
|
|
ADD sp, sp, #LOC_SIZE
|
|
IF Interworking :LOR: Thumbing
|
|
LDMFD sp!, {lr}
|
|
BX lr
|
|
ELSE
|
|
LDMFD sp!, {pc}
|
|
ENDIF
|
|
MEND
|
|
|
|
|
|
|
|
MACRO
|
|
$lab FloatCompare $cc, $NaN_lab
|
|
|
|
ASSERT "$cc"="LO":LOR:"$cc"="LS":LOR:"$cc"="HS":LOR:"$cc"="HI":LOR:"$cc"="EQ":LOR:"$cc"="NE"
|
|
|
|
NESTED_ENTRY $lab
|
|
EnterWithLR_16
|
|
STMFD sp!, {lr} ; Save return address
|
|
SUB sp, sp, #LOC_SIZE ; Allocate local storage
|
|
PROLOG_END
|
|
|
|
ORRS tmp, fOP1, fOP2 ; separate into opnd1/2 both positive, or one negative
|
|
BMI $lab._negative
|
|
CMN tmp, #1 << 23 ; check whether operands might be infinite/NaN
|
|
BMI $lab._NaN_check_pos
|
|
CMP fOP1, fOP2
|
|
CmpReturn $cc
|
|
|
|
$lab._NaN_check_pos ; opnd1/2 might be inf/NaNs - now do the real check
|
|
CMN fOP1, #1 << 23 ; these get about 9% false hits - overhead 4 cycles
|
|
CMNPL fOP2, #1 << 23
|
|
BMI $lab._Inf_or_NaN_pos
|
|
$lab._cmp_pos
|
|
CMP fOP1, fOP2
|
|
CmpReturn $cc
|
|
|
|
$lab._Inf_or_NaN_pos ; at least one operand infinite or NaN - filter out infinities
|
|
MOV tmp, #1 << 24
|
|
CMN tmp, fOP1, LSL #1
|
|
CMNLS tmp, fOP2, LSL #1
|
|
BLS $lab._cmp_pos ; no NaN - continue compare
|
|
B $NaN_lab
|
|
|
|
$lab._negative ; at least one negative operand
|
|
CMN tmp, #1 << 23
|
|
BPL $lab._NaN_check_neg
|
|
MOVS tmp, tmp, LSL #1 ; check -0 == 0 (CS & EQ -> HS and LS)
|
|
CMPNE fOP2, fOP1
|
|
CmpReturn $cc
|
|
|
|
$lab._NaN_check_neg ; opnd1/2 might be inf/NaNs - now do the real check
|
|
MOV tmp, #1 << 24 ; these get about 9% false hits - overhead 4 cycles
|
|
CMN tmp, fOP1, LSL #1
|
|
CMNLS tmp, fOP2, LSL #1
|
|
BHI $NaN_lab
|
|
CMP fOP2, fOP1 ; -0 == 0 check not needed...
|
|
CmpReturn $cc
|
|
|
|
MEND
|
|
|
|
|
|
|
|
;==============================================================================
|
|
;Invalid Operation checking (NaNs on compares)
|
|
;;
|
|
|
|
|
|
IMPORT FPE_Raise
|
|
|
|
;;
|
|
;; NANs on compares <, >, <=, and >=
|
|
;;
|
|
;; SNANs and QNANs both raise the invalid operation exception, so we don't
|
|
;; care which kind of NAN we get. This is because if we get an SNAN or SNANs,
|
|
;; we raise the invalid operation exception. If we get a QNAN or QNANs, we
|
|
;; have an unordered compare and must also raise the invalid operation
|
|
;; exception.
|
|
;;
|
|
;; Register usage on entry:
|
|
;; r0 - Arg1
|
|
;; r1 - Arg2
|
|
;; r14 - available for scratch
|
|
;; All others have normal usage semantics.
|
|
;;
|
|
MACRO
|
|
$l FCmpNaN $Filter_lab
|
|
$l STR r1, [sp, #ExOp2l] ;; Push Arg2
|
|
MOV r3, #_FpCompareUnordered ;; Load default result
|
|
STR r3, [sp, #ExDResl] ;; Push default result
|
|
MOV r2, r0 ;; Arg1
|
|
MOV r1, #IVO_bit ;; ExInfo: InvalidOp,
|
|
ORR r1, r1, #_FpCmpS ;; float compare
|
|
ADD r0, sp, #NewResl ;; Pointer to result
|
|
|
|
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 return value
|
|
ADD sp, sp, #LOC_SIZE ;; Retore stack
|
|
|
|
;;
|
|
;; Register usage:
|
|
;;
|
|
;; r0 - Result from exception handler
|
|
;;
|
|
;; We must now examine the result from the exception handler and change it
|
|
;; to TRUE or FALSE, depending on the operation. After changing the result,
|
|
;; we return to the caller of the FP double compare routine.
|
|
;;
|
|
B $Filter_lab
|
|
MEND
|
|
|
|
|
|
|
|
|
|
|
|
;;
|
|
;; NANs on compares == and !=
|
|
;;
|
|
;; SNANs and QNANs are treated differently for == and !=. If we get an SNAN
|
|
;; or SNANs, we must raise the invalid operation exception. If we only have
|
|
;; a QNAN or QNANs, then we simply return false and true for == and !=,
|
|
;; respectively. Unordered comparisions for == and != do not raise the
|
|
;; invalid operation exception.
|
|
;;
|
|
;; Register usage on entry:
|
|
;; r0 - Arg1
|
|
;; r1 - Arg2
|
|
;; r14 - available for scratch
|
|
;; All others have normal usage semantics.
|
|
;;
|
|
|
|
MACRO
|
|
$l FCmpSNaN $Filter_lab
|
|
|
|
$l MOV r14, r0, LSL #1 ;; Extract exponent from Arg1
|
|
MOV r14, r14, LSR #24 ;; ...
|
|
CMP r14, #0xFF ;; Arg1.exponent == 0xFF?
|
|
BNE $l.checkArg2 ;; Arg1 not a NaN so check Arg2
|
|
MOVS r14, r0, LSL #9 ;; Arg1.Mantissa bits set?
|
|
BEQ $l.checkArg2 ;; Arg1 not a NaN so check Arg2
|
|
TST r0, #fSignalBit ;; Check if SNAN
|
|
BEQ $l.SNaN ;; If high mant. bit clear, SNaN
|
|
|
|
$l.checkArg2
|
|
MOV r14, r1, LSL #1 ;; Extract exponent from Arg2
|
|
MOV r14, r14, LSR #24 ;; ...
|
|
CMP r14, #0xFF ;; Arg2.exponent == 0xFF?
|
|
BNE $l.cmpUnordered ;; Arg2 not a NaN so Arg1 is a QNaN
|
|
MOVS r14, r1, LSL #9 ;; Arg2.Mantissa bits set?
|
|
BEQ $l.cmpUnordered ;; Arg2 not a NaN so Arg1 is a QNaN
|
|
TST r1, #fSignalBit ;; Check if SNAN
|
|
BEQ $l.SNaN ;; If high mant. bit clear, SNaN
|
|
|
|
$l.cmpUnordered
|
|
MOV r0, #_FpCompareUnordered ;; Have an unordered compare so
|
|
B $Filter_lab ;; don't raise an exception
|
|
|
|
$l.SNaN
|
|
STR r1, [sp, #ExOp2l] ;; Push Arg2
|
|
MOV r3, #_FpCompareUnordered ;; Load default result
|
|
STR r3, [sp, #ExDResl] ;; Push default result
|
|
MOV r2, r0 ;; Arg1
|
|
MOV r1, #IVO_bit ;; ExInfo: InvalidOp,
|
|
ORR r1, r1, #_FpCmpS ;; float compare
|
|
ADD r0, sp, #NewResl ;; Pointer to result
|
|
|
|
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 return value
|
|
|
|
|
|
;;
|
|
;; Register usage:
|
|
;;
|
|
;; r0 - Result from exception handler
|
|
;;
|
|
;; We must now examine the result from the exception handler and change it
|
|
;; to TRUE or FALSE, depending on the operation. After changing the result,
|
|
;; we return to the caller of the FP double compare routine.
|
|
;;
|
|
B $Filter_lab
|
|
MEND
|
|
|
|
|
|
|
|
|
|
|
|
;==============================================================================
|
|
; Equality
|
|
|
|
[ :DEF: eq_s
|
|
|
|
Export __eqs
|
|
|
|
AREA |.text|, CODE, READONLY
|
|
|
|
__eqs FloatCompare EQ, __eqs_NaN
|
|
|
|
__eqs_NaN FCmpSNaN __eqs_Filter
|
|
|
|
__eqs_Filter
|
|
CMP r0, #_FpCompareEqual ;; Check if compared ==
|
|
MOVEQ r0, #1 ;; If did, return true
|
|
MOVNE r0, #0 ;; else return false
|
|
ADD sp, sp, #0x18 ;; Pop extra arg passing space
|
|
IF Interworking :LOR: Thumbing
|
|
LDMIA sp!, {lr} ;; Return
|
|
BX lr
|
|
ELSE
|
|
LDMIA sp!, {pc} ;; Return
|
|
ENDIF
|
|
|
|
ENTRY_END __eqs
|
|
|
|
]
|
|
|
|
;==============================================================================
|
|
;Inequality
|
|
|
|
[ :DEF: neq_s
|
|
|
|
Export __nes
|
|
|
|
AREA |.text|, CODE, READONLY
|
|
|
|
__nes FloatCompare NE, __nes_NaN
|
|
|
|
__nes_NaN FCmpSNaN __nes_Filter
|
|
|
|
__nes_Filter
|
|
CMP r0, #_FpCompareEqual ;; Check if compared ==
|
|
MOVEQ r0, #0 ;; If did, return false
|
|
MOVNE r0, #1 ;; else return true
|
|
ADD sp, sp, #0x18 ;; Pop extra arg passing space
|
|
IF Interworking :LOR: Thumbing
|
|
LDMIA sp!, {lr} ;; Return
|
|
BX lr
|
|
ELSE
|
|
LDMIA sp!, {pc} ;; Return
|
|
ENDIF
|
|
|
|
ENTRY_END __nes
|
|
|
|
]
|
|
|
|
|
|
;==============================================================================
|
|
;Less Than
|
|
|
|
[ :DEF: ls_s
|
|
|
|
Export __lts
|
|
|
|
AREA |.text|, CODE, READONLY
|
|
|
|
__lts FloatCompare LO, __lts_NaN
|
|
|
|
__lts_NaN FCmpNaN __lts_Filter
|
|
|
|
__lts_Filter
|
|
CMP r0, #_FpCompareLess ;; Check if compared <
|
|
MOVEQ r0, #1 ;; If did, return true
|
|
MOVNE r0, #0 ;; else return false
|
|
IF Interworking :LOR: Thumbing
|
|
LDMIA sp!, {lr} ;; Return
|
|
BX lr
|
|
ELSE
|
|
LDMIA sp!, {pc} ;; Return
|
|
ENDIF
|
|
|
|
ENTRY_END __lts
|
|
]
|
|
|
|
;==============================================================================
|
|
;Less Than or Equal
|
|
|
|
[ :DEF: leq_s
|
|
|
|
Export __les
|
|
|
|
AREA |.text|, CODE, READONLY
|
|
|
|
__les FloatCompare LS, __les_NaN
|
|
|
|
__les_NaN FCmpNaN __les_Filter
|
|
|
|
__les_Filter
|
|
CMP r0, #_FpCompareLess ;; Check if compared <
|
|
MOVEQ r0, #1 ;; If did,
|
|
BEQ __les_Filter_end ;; return true
|
|
CMP r0, #_FpCompareEqual ;; Check if compared ==
|
|
MOVEQ r0, #1 ;; If did, return true
|
|
MOVNE r0, #0 ;; else return false
|
|
__les_Filter_end
|
|
IF Interworking :LOR: Thumbing
|
|
LDMIA sp!, {lr} ;; Return
|
|
BX lr
|
|
ELSE
|
|
LDMIA sp!, {pc} ;; Return
|
|
ENDIF
|
|
|
|
ENTRY_END __les
|
|
|
|
]
|
|
|
|
;==============================================================================
|
|
;Greater Than
|
|
|
|
[ :DEF: gr_s
|
|
|
|
Export __gts
|
|
|
|
AREA |.text|, CODE, READONLY
|
|
|
|
__gts FloatCompare HI, __gts_NaN
|
|
|
|
__gts_NaN FCmpNaN __gts_Filter
|
|
|
|
__gts_Filter
|
|
CMP r0, #_FpCompareGreater ;; Check if compared >
|
|
MOVEQ r0, #1 ;; If did, return true
|
|
MOVNE r0, #0 ;; else return false
|
|
IF Interworking :LOR: Thumbing
|
|
LDMIA sp!, {lr} ;; Return
|
|
BX lr
|
|
ELSE
|
|
LDMIA sp!, {pc} ;; Return
|
|
ENDIF
|
|
|
|
ENTRY_END __gts
|
|
]
|
|
|
|
;==============================================================================
|
|
;Greater Than or Equal
|
|
|
|
[ :DEF: geq_s
|
|
|
|
Export __ges
|
|
|
|
AREA |.text|, CODE, READONLY
|
|
|
|
__ges FloatCompare HS, __ges_NaN
|
|
|
|
__ges_NaN FCmpNaN __ges_Filter
|
|
|
|
__ges_Filter
|
|
CMP r0, #_FpCompareGreater ;; Check if compared >
|
|
MOVEQ r0, #1 ;; If did,
|
|
BEQ __ges_Filter_end ;; return true
|
|
CMP r0, #_FpCompareEqual ;; Check if compared ==
|
|
MOVEQ r0, #1 ;; If did, return true
|
|
MOVNE r0, #0 ;; else return false
|
|
__ges_Filter_end
|
|
IF Interworking :LOR: Thumbing
|
|
LDMIA sp!, {lr} ;; Return
|
|
BX lr
|
|
ELSE
|
|
LDMIA sp!, {pc} ;; Return
|
|
ENDIF
|
|
|
|
ENTRY_END __ges
|
|
]
|
|
|
|
;==============================================================================
|
|
|
|
END
|