713 lines
21 KiB
NASM
713 lines
21 KiB
NASM
|
subttl emround.asm - Rounding and Precision Control and FRNDINT
|
||
|
page
|
||
|
;*******************************************************************************
|
||
|
;emround.asm - Rounding and Precision Control
|
||
|
;
|
||
|
; Microsoft Confidential
|
||
|
;
|
||
|
; Copyright (c) Microsoft Corporation 1991
|
||
|
; All Rights Reserved
|
||
|
;
|
||
|
;Purpose:
|
||
|
; Rounding and precision control. The correct routine is jumped
|
||
|
; to through the [RoundMode] vector.
|
||
|
;
|
||
|
;Revision History:
|
||
|
;
|
||
|
; [] 09/05/91 TP Initial 32-bit version.
|
||
|
; 02/28/92 JWM Minor bug fix in NotNearLow
|
||
|
;
|
||
|
;*******************************************************************************
|
||
|
|
||
|
|
||
|
RndIntSpcl:
|
||
|
cmp cl,bTAG_INF
|
||
|
jz RndIntX ;Leave infinity unchanged
|
||
|
cmp cl,bTAG_DEN
|
||
|
jnz SpclDestNotDen ;Handle NAN & empty - in emarith.asm
|
||
|
;Handle denormal
|
||
|
mov EMSEG:[CURerr],Denormal
|
||
|
test EMSEG:[CWmask],Denormal ;Is it masked?
|
||
|
jnz NormRndInt ;If so, ignore denormalization
|
||
|
RndIntX:
|
||
|
ret
|
||
|
|
||
|
;********
|
||
|
EM_ENTRY eFRNDINT
|
||
|
eFRNDINT:
|
||
|
;********
|
||
|
;edi points to top of stack
|
||
|
mov ecx,EMSEG:[edi].ExpSgn
|
||
|
cmp cl,bTAG_ZERO
|
||
|
.erre bTAG_VALID lt bTAG_ZERO
|
||
|
.erre bTAG_SNGL lt bTAG_ZERO
|
||
|
jz RndIntX
|
||
|
ja RndIntSpcl
|
||
|
cmp ecx,63 shl 16 ;Is it already integer?
|
||
|
jge RndIntX
|
||
|
NormRndInt:
|
||
|
mov ebx,EMSEG:[edi].lManHi
|
||
|
mov esi,EMSEG:[edi].lManLo
|
||
|
mov EMSEG:[Result],edi ;Save result pointer
|
||
|
xor eax,eax ;Extend mantissa
|
||
|
push offset SaveResult
|
||
|
jmp RoundToBit
|
||
|
|
||
|
;*******************************************************************************
|
||
|
|
||
|
ResultOverflow:
|
||
|
;mantissa in ebx:esi, exponent in high ecx, sign in ch bit 7, tag in cl.
|
||
|
;We were all ready to save the rounded result, but the exponent turned out
|
||
|
;to be too large.
|
||
|
or EMSEG:[CURerr],Overflow
|
||
|
sub ecx,UnderBias shl 16 ;Unmasked response
|
||
|
test EMSEG:[CWmask],Overflow ;Is exception unmasked?
|
||
|
jz SaveResult ;If so, we're ready
|
||
|
;Produce masked overflow response
|
||
|
mov ebx,1 shl 31 ;Assume infinity
|
||
|
xor esi,esi
|
||
|
mov cl,bTAG_INF
|
||
|
mov al,EMSEG:[CWcntl] ;Get rounding control
|
||
|
mov ah,al
|
||
|
and ah,RCchop ;Rounding control only
|
||
|
;Return max value if RCup bit = 1 and -, or RCdown bit = 1 and +
|
||
|
;i.e., RCup & sign OR RCdown & not sign
|
||
|
.erre RCchop eq RCup + RCdown ;Always return max value
|
||
|
.erre RCnear eq 0 ;Never return max value
|
||
|
sar ch,7 ;Expand sign through whole byte
|
||
|
.erre (RCdown and bSign) eq 0 ;Don't want to change real sign
|
||
|
xor ch,RCdown ;Flip sign for RCdown bit
|
||
|
and ah,ch ;RCup & sign OR RCdown & not sign
|
||
|
jnz SaveMax
|
||
|
and ecx,0FFFFH
|
||
|
or ecx,TexpMax shl 16
|
||
|
jmp SaveResult ;Save Infinity
|
||
|
SaveMax:
|
||
|
;Get max value for current precision
|
||
|
mov ebx,0FFFFFF00H ;Max value for 24 bits
|
||
|
and ecx,bSign shl 8 ;Preserve only sign
|
||
|
or ecx,(IexpMax-IexpBias-1) shl 16 + bTAG_VALID ;Set up max value
|
||
|
and al,PrecisionControl
|
||
|
.erre PC24 eq 0
|
||
|
jz SaveResult ;Save 24-bit max value
|
||
|
dec esi ;esi == -1
|
||
|
mov ebx,esi
|
||
|
cmp al,PC53
|
||
|
jnz SaveResult ;Save 64-bit max value
|
||
|
mov esi,0FFFFF800H
|
||
|
jmp SaveResult ;Save 53-bit max value
|
||
|
|
||
|
;*******************************************************************************
|
||
|
;
|
||
|
;64-bit rounding routines
|
||
|
;
|
||
|
|
||
|
;***********
|
||
|
Round64down:
|
||
|
;***********
|
||
|
cmp ecx,(IexpMin-IexpBias+1) shl 16 ;Test for Underflow
|
||
|
jl RndDenorm64
|
||
|
or eax,eax ;Exact result?
|
||
|
jz SaveValidResult
|
||
|
or EMSEG:[CURerr],Precision ;Set flag on inexact result
|
||
|
;Chop if positive, increase mantissa if negative
|
||
|
test ch,bSign
|
||
|
jz SaveValidResult ;Positive, so chop
|
||
|
jmp RoundUp64 ;Round up if negative
|
||
|
|
||
|
RndDenorm64:
|
||
|
test EMSEG:[CWmask],Underflow ;Is exception unmasked?
|
||
|
jz RndSetUnder
|
||
|
Denormalize:
|
||
|
;We don't really store in denormalized format, but we need the number
|
||
|
;to be rounded as if we do. If the exponent were -IexpBias, we would
|
||
|
;lose 1 bit of precision; as it gets more negative, we lose more bits.
|
||
|
;We'll do this by adjusting the exponent so that the bits we want to
|
||
|
;keep look like integer bits, and performing round-to-integer.
|
||
|
add ecx,(IexpBias+62) shl 16 ;Adjust exponent so we're integer
|
||
|
call RoundToBit
|
||
|
;Set underflow exception if precision exception is set
|
||
|
mov al,EMSEG:[CURerr]
|
||
|
and al,Precision
|
||
|
ror al,Precision-Underflow ;Move Precision bit to Underflow pos.
|
||
|
or EMSEG:[CURerr],al ;Signal Underflow if inexact
|
||
|
cmp cl,bTAG_ZERO
|
||
|
jz SaveResult
|
||
|
sub ecx,(IexpBias+62) shl 16;Restore unbiased exponent
|
||
|
cmp ecx,TexpMin shl 16 ;Did we round out of denorm?
|
||
|
jae SaveResult
|
||
|
mov cl,bTAG_DEN
|
||
|
jmp SaveResult
|
||
|
|
||
|
RndSetUnder:
|
||
|
;Underflow exception not masked. Adjust exponent and try again.
|
||
|
or EMSEG:[CURerr],Underflow
|
||
|
add ecx,UnderBias shl 16
|
||
|
jmp EMSEG:[RoundMode] ;Try again with revised exponent
|
||
|
|
||
|
;***********
|
||
|
Round64near:
|
||
|
;***********
|
||
|
;mantissa in ebx:esi:eax, exponent in high ecx, sign in ch bit 7
|
||
|
cmp ecx,TexpMin shl 16 ;Test for Underflow
|
||
|
jl RndDenorm64
|
||
|
or eax,eax ;Exact result?
|
||
|
jz short SaveValidResult
|
||
|
or EMSEG:[CURerr],Precision ;Set flag on inexact result
|
||
|
|
||
|
;To perform "round even" when the round bit is set and the sticky bits
|
||
|
;are zero, we treat the LSB as if it were a sticky bit. Thus if the LSB
|
||
|
;is set, that will always force a round up (to even) if the round bit is
|
||
|
;set. If the LSB is zero, then the sticky bits remain zero and we always
|
||
|
;round down. This rounding rule is implemented by adding RoundBit-1
|
||
|
;(7F..FFH), setting CY if round up.
|
||
|
|
||
|
bt esi,0 ;Is mantissa even or odd? (set CY)
|
||
|
adc eax,(1 shl 31)-1 ;Sum LSB & sticky bits--CY if round up
|
||
|
jnc SaveValidResult
|
||
|
RoundUp64:
|
||
|
mov EMSEG:[SWcc],RoundUp
|
||
|
add esi,1
|
||
|
adc ebx,0
|
||
|
jc BumpExponent ;Overflowed, increment exponent
|
||
|
|
||
|
SaveValidResult: ;A jump to here requires 9 clocks
|
||
|
or esi,esi ;Any bits in low half?
|
||
|
.erre bTAG_VALID eq 1
|
||
|
.erre bTAG_SNGL eq 0
|
||
|
setnz cl ;if low half==0 then cl=0 else cl=1
|
||
|
cmp ecx,TexpMax shl 16 ;Test for overflow
|
||
|
jge ResultOverflow
|
||
|
|
||
|
SaveResult: ;A jump to here requires 10 clocks
|
||
|
;mantissa in ebx:esi, exponent in high ecx, sign in ch bit 7, tag in cl
|
||
|
mov edi,EMSEG:[Result]
|
||
|
SaveResultEdi:
|
||
|
mov EMSEG:[edi].lManLo,esi
|
||
|
mov EMSEG:[edi].lManHi,ebx
|
||
|
SaveExpSgn:
|
||
|
mov EMSEG:[edi].ExpSgn,ecx
|
||
|
ret
|
||
|
|
||
|
;***********
|
||
|
Round64up:
|
||
|
;***********
|
||
|
cmp ecx,TexpMin shl 16 ;Test for Underflow
|
||
|
jl RndDenorm64
|
||
|
or eax,eax ;Exact result?
|
||
|
jz short SaveValidResult
|
||
|
or EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
;Chop if negative, increase mantissa if positive
|
||
|
cmp ch,bSign ;No CY iff sign bit is set
|
||
|
jc RoundUp64 ;Round up if positive
|
||
|
jmp short SaveValidResult
|
||
|
|
||
|
;***********
|
||
|
Round64chop:
|
||
|
;***********
|
||
|
cmp ecx,TexpMin shl 16 ;Test for Underflow
|
||
|
jl RndDenorm64
|
||
|
or eax,eax ;Exact result?
|
||
|
jz short SaveValidResult
|
||
|
or EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
jmp short SaveValidResult
|
||
|
|
||
|
;*******************************************************************************
|
||
|
;
|
||
|
;53-bit rounding routines
|
||
|
;
|
||
|
|
||
|
;***********
|
||
|
Round53down:
|
||
|
;***********
|
||
|
cmp ecx,TexpMin shl 16 ;Test for Underflow
|
||
|
jl RndDenorm53
|
||
|
mov edx,esi ;Get low bits
|
||
|
and edx,(1 shl 11) - 1 ;Mask to last 11 bits
|
||
|
or edx,eax ;Throwing away any bits?
|
||
|
jz SaveValidResult
|
||
|
or EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
;Chop if positive, increase mantissa if negative
|
||
|
and esi,not ((1 shl 11)-1) ;Mask off low 11 bits
|
||
|
test ch,bSign
|
||
|
jz SaveValidResult ;Positive, go chop
|
||
|
jmp RoundUp53
|
||
|
|
||
|
RndDenorm53:
|
||
|
test EMSEG:[CWmask],Underflow;Is exception unmasked?
|
||
|
jz RndSetUnder
|
||
|
;We don't really store in denormalized format, but we need the number
|
||
|
;to be rounded as if we do. If the exponent were -IexpBias, we would
|
||
|
;lose 1 bit of precision; as it gets more negative, we lose more bits.
|
||
|
;We'll do this by adjusting the exponent so that the bits we want to
|
||
|
;keep look like integer bits, and performing round-to-integer.
|
||
|
add ecx,(IexpBias+51) shl 16 ;Adjust exponent so we're integer
|
||
|
call RoundToBit
|
||
|
;Set underflow exception if precision exception is set
|
||
|
mov al,EMSEG:[CURerr]
|
||
|
and al,Precision
|
||
|
ror al,Precision-Underflow ;Move Precision bit to Underflow pos.
|
||
|
or EMSEG:[CURerr],al ;Signal Underflow if inexact
|
||
|
cmp cl,bTAG_ZERO
|
||
|
jz SaveResult
|
||
|
sub ecx,(IexpBias+51) shl 16;Restore unbiased exponent
|
||
|
cmp ecx,(IexpMin-IexpBias+1) shl 16 ;Did we round out of denorm?
|
||
|
jae SaveResult
|
||
|
mov cl,bTAG_DEN
|
||
|
jmp SaveResult
|
||
|
|
||
|
;***********
|
||
|
Round53near:
|
||
|
;***********
|
||
|
;mantissa in ebx:esi:eax, exponent in high ecx, sign in ch bit 7
|
||
|
cmp ecx,TexpMin shl 16 ;Test for Underflow
|
||
|
jl RndDenorm53
|
||
|
mov edx,esi ;Get low bits
|
||
|
and edx,(1 shl 11) - 1 ;Mask to last 11 bits
|
||
|
or edx,eax ;Throwing away any bits?
|
||
|
jz SaveValidResult
|
||
|
or EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
|
||
|
;To perform "round even" when the round bit is set and the sticky bits
|
||
|
;are zero, we treat the LSB as if it were a sticky bit. Thus if the LSB
|
||
|
;is set, that will always force a round up (to even) if the round bit is
|
||
|
;set. If the LSB is zero, then the sticky bits remain zero and we always
|
||
|
;round down.
|
||
|
|
||
|
mov edx,esi
|
||
|
and esi,not ((1 shl 11)-1) ;Mask off low 11 bits
|
||
|
test edx,1 shl 10 ;Is round bit set?
|
||
|
jz SaveValidResult
|
||
|
and edx,(3 shl 10)-1 ;Keep only sticky bits and LSB
|
||
|
or eax,edx ;Combine with other sticky bits
|
||
|
jz SaveValidResult
|
||
|
RoundUp53:
|
||
|
mov EMSEG:[SWcc],RoundUp
|
||
|
add esi,1 shl 11 ;Round
|
||
|
adc ebx,0
|
||
|
jnc SaveValidResult
|
||
|
BumpExponent:
|
||
|
add ecx,1 shl 16 ;Mantissa overflowed, bump exponent
|
||
|
or ebx,1 shl 31 ;Set MSB
|
||
|
jmp SaveValidResult
|
||
|
|
||
|
;***********
|
||
|
Round53up:
|
||
|
;***********
|
||
|
cmp ecx,TexpMin shl 16 ;Test for Underflow
|
||
|
jl RndDenorm53
|
||
|
mov edx,esi ;Get low bits
|
||
|
and edx,(1 shl 11) - 1 ;Mask to last 11 bits
|
||
|
or edx,eax ;Throwing away any bits?
|
||
|
jz SaveValidResult
|
||
|
or EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
;Chop if negative, increase mantissa if positive
|
||
|
and esi,not ((1 shl 11)-1) ;Mask off low 11 bits
|
||
|
test ch,bSign
|
||
|
jz RoundUp53 ;Round up if positive
|
||
|
jmp SaveValidResult
|
||
|
|
||
|
;***********
|
||
|
Round53chop:
|
||
|
;***********
|
||
|
cmp ecx,TexpMin shl 16 ;Test for Underflow
|
||
|
jl RndDenorm53
|
||
|
mov edx,esi ;Get low bits
|
||
|
and edx,(1 shl 11) - 1 ;Mask to last 11 bits
|
||
|
or edx,eax ;Throwing away any bits?
|
||
|
jz SaveValidResult
|
||
|
or EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
and esi,not ((1 shl 11)-1) ;Mask off low 11 bits
|
||
|
jmp SaveValidResult
|
||
|
|
||
|
;*******************************************************************************
|
||
|
;
|
||
|
;24-bit rounding routines
|
||
|
;
|
||
|
|
||
|
;***********
|
||
|
Round24down:
|
||
|
;***********
|
||
|
cmp ecx,TexpMin shl 16 ;Test for Underflow
|
||
|
jl RndDenorm24
|
||
|
or eax,esi ;Low dword is just sticky bits
|
||
|
mov edx,ebx ;Get low bits
|
||
|
and edx,(1 shl 8) - 1 ;Mask to last 8 bits
|
||
|
or edx,eax ;Throwing away any bits?
|
||
|
jz SaveValidResult
|
||
|
or EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
;Chop if positive, increase mantissa if negative
|
||
|
xor esi,esi
|
||
|
and ebx,not ((1 shl 8)-1) ;Mask off low 8 bits
|
||
|
test ch,bSign
|
||
|
jz SaveValidResult ;Chop if positive
|
||
|
jmp RoundUp24
|
||
|
|
||
|
RndDenorm24:
|
||
|
test EMSEG:[CWmask],Underflow;Is exception unmasked?
|
||
|
jz RndSetUnder
|
||
|
;We don't really store in denormalized format, but we need the number
|
||
|
;to be rounded as if we do. If the exponent were -IexpBias, we would
|
||
|
;lose 1 bit of precision; as it gets more negative, we lose more bits.
|
||
|
;We'll do this by adjusting the exponent so that the bits we want to
|
||
|
;keep look like integer bits, and performing round-to-integer.
|
||
|
add ecx,(IexpBias+22) shl 16 ;Adjust exponent so we're integer
|
||
|
call RoundToBit
|
||
|
;Set underflow exception if precision exception is set
|
||
|
mov al,EMSEG:[CURerr]
|
||
|
and al,Precision
|
||
|
ror al,Precision-Underflow ;Move Precision bit to Underflow pos.
|
||
|
or EMSEG:[CURerr],al ;Signal Underflow if inexact
|
||
|
cmp cl,bTAG_ZERO
|
||
|
jz SaveResult
|
||
|
sub ecx,(IexpBias+22) shl 16;Restore unbiased exponent
|
||
|
cmp ecx,(IexpMin-IexpBias+1) shl 16 ;Did we round out of denorm?
|
||
|
jae SaveResult
|
||
|
mov cl,bTAG_DEN
|
||
|
jmp SaveResult
|
||
|
|
||
|
;***********
|
||
|
Round24near:
|
||
|
;***********
|
||
|
;mantissa in ebx:esi:eax, exponent in high ecx, sign in ch bit 7
|
||
|
cmp ecx,TexpMin shl 16 ;Test for Underflow
|
||
|
jl RndDenorm24
|
||
|
or eax,esi ;Low dword is just sticky bits
|
||
|
mov edx,ebx ;Get low bits
|
||
|
and edx,(1 shl 8) - 1 ;Mask to last 8 bits
|
||
|
or edx,eax ;Throwing away any bits?
|
||
|
jz SaveValidResult
|
||
|
or EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
xor esi,esi
|
||
|
|
||
|
;To perform "round even" when the round bit is set and the sticky bits
|
||
|
;are zero, we treat the LSB as if it were a sticky bit. Thus if the LSB
|
||
|
;is set, that will always force a round up (to even) if the round bit is
|
||
|
;set. If the LSB is zero, then the sticky bits remain zero and we always
|
||
|
;round down.
|
||
|
|
||
|
mov edx,ebx
|
||
|
and ebx,not ((1 shl 8)-1) ;Mask off low 8 bits
|
||
|
test dl,1 shl 7 ;Round bit set?
|
||
|
jz SaveValidResult
|
||
|
and edx,(3 shl 7)-1 ;Mask to LSB and sticky bits
|
||
|
or eax,edx ;Combine all sticky bits
|
||
|
jz SaveValidResult
|
||
|
RoundUp24:
|
||
|
mov EMSEG:[SWcc],RoundUp
|
||
|
add ebx,1 shl 8
|
||
|
jnc SaveValidResult
|
||
|
jmp BumpExponent ;Overflowed, increment exponent
|
||
|
|
||
|
;***********
|
||
|
Round24up:
|
||
|
;***********
|
||
|
cmp ecx,TexpMin shl 16 ;Test for Underflow
|
||
|
jl RndDenorm24
|
||
|
or eax,esi ;Low dword is just sticky bits
|
||
|
mov edx,ebx ;Get low bits
|
||
|
and edx,(1 shl 8) - 1 ;Mask to last 8 bits
|
||
|
or edx,eax ;Throwing away any bits?
|
||
|
jz SaveValidResult
|
||
|
or EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
;Chop if negative, increase mantissa if positive
|
||
|
xor esi,esi
|
||
|
and ebx,not ((1 shl 8)-1) ;Mask off low 8 bits
|
||
|
test ch,bSign
|
||
|
jz RoundUp24 ;Round up if positive
|
||
|
jmp SaveValidResult
|
||
|
|
||
|
;***********
|
||
|
Round24chop:
|
||
|
;***********
|
||
|
cmp ecx,TexpMin shl 16 ;Test for Underflow
|
||
|
jl RndDenorm24
|
||
|
or eax,esi ;Low dword is just sticky bits
|
||
|
mov edx,ebx ;Get low bits
|
||
|
and edx,(1 shl 8) - 1 ;Mask to last 8 bits
|
||
|
or edx,eax ;Throwing away any bits?
|
||
|
jz SaveValidResult
|
||
|
or EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
xor esi,esi
|
||
|
and ebx,not ((1 shl 8)-1) ;Mask off low 8 bits
|
||
|
jmp SaveValidResult
|
||
|
|
||
|
;*******************************************************************************
|
||
|
|
||
|
;*** RoundToInteger
|
||
|
;
|
||
|
;This routine is used by FISTP Int64 and BSTP. Unlike RoundToBit, this
|
||
|
;unnormalizes the number into a 64-bit integer.
|
||
|
;
|
||
|
;Inputs:
|
||
|
; edi = pointer to number to round in stack
|
||
|
;Outputs:
|
||
|
; CY set if invalid operation
|
||
|
; ebx:edi = rounded integer if CY clear
|
||
|
; ch = sign if CY clear
|
||
|
;Note:
|
||
|
; FIST/FISTP/BSTP exception rules are used: If the number is too big,
|
||
|
; Invalid Operation occurs. Denormals are ignored.
|
||
|
;
|
||
|
;esi preserved
|
||
|
|
||
|
RoundSpcl64Int:
|
||
|
cmp cl,bTAG_DEN
|
||
|
jz NormRound64Int ;Ignore denormal
|
||
|
cmp cl,bTAG_EMPTY
|
||
|
jnz RoundInvalid ;All other specials are invalid
|
||
|
mov EMSEG:[CURerr],StackFlag+Invalid
|
||
|
stc ;Flag exception to caller
|
||
|
ret
|
||
|
|
||
|
RoundInvalid:
|
||
|
;Overflow on integer store is invalid according to IEEE
|
||
|
mov EMSEG:[CURerr],Invalid
|
||
|
stc ;Flag exception to caller
|
||
|
ret
|
||
|
|
||
|
RoundToInteger:
|
||
|
mov ebx,EMSEG:[edi].lManHi
|
||
|
mov ecx,EMSEG:[edi].ExpSgn
|
||
|
mov edi,EMSEG:[edi].lManLo
|
||
|
;mantissa in ebx:edi, exponent in high ecx, sign in ch bit 7, tag in cl
|
||
|
mov al,ch ;Save sign bit
|
||
|
cmp cl,bTAG_ZERO
|
||
|
.erre bTAG_VALID lt bTAG_ZERO
|
||
|
.erre bTAG_SNGL lt bTAG_ZERO
|
||
|
jz RoundIntX ;Just return zero
|
||
|
ja RoundSpcl64Int
|
||
|
NormRound64Int:
|
||
|
xor edx,edx
|
||
|
sar ecx,16 ;Bring exponent down
|
||
|
cmp ecx,-1 ;Is it less than 1?
|
||
|
jle Under64Int
|
||
|
cmp ecx,63
|
||
|
jg RoundInvalid
|
||
|
sub ecx,63
|
||
|
neg ecx ;cl = amount to shift right
|
||
|
mov ch,al ;Get sign out of al
|
||
|
xor eax,eax
|
||
|
cmp cl,32 ;Too big for one shift?
|
||
|
jl ShortShft64
|
||
|
;32-bit shift right
|
||
|
xchg edx,edi
|
||
|
xchg ebx,edi ;ebx=0 now
|
||
|
shrd eax,edx,cl
|
||
|
;Max total shift is 63 bits, so we know that the LSB of eax is still zero.
|
||
|
;We can rotate this zero to the MSB so the sticky bits in eax can be combined
|
||
|
;with those in edx without affecting the rounding bit in the MSB of edx.
|
||
|
ror eax,1 ;MSB is now zero
|
||
|
ShortShft64:
|
||
|
;Shift count in cl is modulo-32
|
||
|
shrd edx,edi,cl
|
||
|
shrd edi,ebx,cl
|
||
|
shr ebx,cl
|
||
|
or edx,eax ;Collapse sticky bits into one dword
|
||
|
jz RoundIntX ;No sticky or round bits, so don't round
|
||
|
;Result will not be exact--check rounding mode
|
||
|
Round64Int:
|
||
|
mov EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
test EMSEG:[CWcntl],RoundControl ;Check rounding control bits
|
||
|
.erre RCnear eq 0
|
||
|
jnz NotNearest64Int ;Not just round-to-nearest
|
||
|
|
||
|
;To perform "round even" when the round bit is set and the sticky bits
|
||
|
;are zero, we treat the LSB as if it were a sticky bit. Thus if the LSB
|
||
|
;is set, that will always force a round up (to even) if the round bit is
|
||
|
;set. If the LSB is zero, then the sticky bits remain zero and we always
|
||
|
;round down.
|
||
|
|
||
|
bt edi,0 ;Look at LSB (for round even)
|
||
|
adc edx,(1 shl 31)-1 ;CY set if round up
|
||
|
jnc RoundIntX
|
||
|
mov EMSEG:[SWcc],RoundUp
|
||
|
add edi,1 ;Round
|
||
|
adc ebx,0
|
||
|
jc RoundInvalid
|
||
|
RoundIntX:
|
||
|
ret ;CY clear, no Invalid exception
|
||
|
|
||
|
Shift64Round:
|
||
|
or edi,edi
|
||
|
setnz dl ;Set sticky bit in edx
|
||
|
xor edi,edi ;Mantissa is all zero
|
||
|
jmp Round64Int
|
||
|
|
||
|
Under64Int:
|
||
|
;ZF set if exponent is -1
|
||
|
xchg ebx,edx ;64-bit right shift
|
||
|
mov ch,al ;Restore sign to ch
|
||
|
jz Shift64Round ;Exp. is -1, could need to round up
|
||
|
xor edi,edi ;Mantissa is all zero
|
||
|
mov EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
NotNearest64Int:
|
||
|
;We want to increase the magnitude if RCup and +, or RCdown and -
|
||
|
mov al,EMSEG:[CWcntl] ;Get rounding control
|
||
|
.erre (not RCup and RoundControl) eq RCdown
|
||
|
sar ch,7 ;Expand sign through whole byte
|
||
|
xor al,ch ;Flip round mode if -
|
||
|
and al,RoundControl
|
||
|
cmp al,RCup ;Rounding up?
|
||
|
jnz RoundIntOk ;No, chop it
|
||
|
mov EMSEG:[SWcc],RoundUp
|
||
|
add edi,1
|
||
|
adc ebx,0
|
||
|
jc RoundInvalid
|
||
|
RoundIntOk:
|
||
|
clc
|
||
|
ret
|
||
|
|
||
|
;*******************************************************************************
|
||
|
|
||
|
;*** RoundToBit
|
||
|
;
|
||
|
;This is a relatively low performance routine used by FRNDINT and to
|
||
|
;generate internal-format denormals. It can round to any bit position.
|
||
|
;
|
||
|
;Inputs:
|
||
|
; mantissa in ebx:esi:eax, exponent in high ecx, sign in ch bit 7
|
||
|
;Purpose:
|
||
|
; Round number to integer. Zero exponent means number is in the
|
||
|
; range [1,2), so only the MSB will survive (MSB-1 is round bit).
|
||
|
; Larger exponents keep more bits; 63 would mean no rounding.
|
||
|
;Outputs:
|
||
|
; mantissa in ebx:esi, exponent in high ecx, sign in ch bit 7, tag in cl
|
||
|
;
|
||
|
;Does NOT detect overflow.
|
||
|
|
||
|
NoSigBits:
|
||
|
;Exponent was negative: no integer part
|
||
|
and ecx,bSign shl 8 ;Zero exponent, preserve sign
|
||
|
mov cl,bTAG_ZERO
|
||
|
or EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
test EMSEG:[CWcntl],RoundControl ;Check rounding control bits
|
||
|
.erre RCnear eq 0
|
||
|
jnz NotNearNoSig ;Not just round-to-nearest
|
||
|
cmp edx,-1 ;Exponent of -1 ==> range [.5,1)
|
||
|
je HalfBitRound
|
||
|
RndIntToZero:
|
||
|
xor ebx,ebx
|
||
|
mov esi,ebx ;Just return zero
|
||
|
ret
|
||
|
|
||
|
NotNearNoSig:
|
||
|
;We want to increase the magnitude if RCup and +, or RCdown and -
|
||
|
mov al,EMSEG:[CWcntl] ;Get rounding control
|
||
|
sar ch,7 ;Expand sign through whole byte
|
||
|
xor al,ch ;Flip rounding bits if negative
|
||
|
and al,RoundControl
|
||
|
cmp al,RCup ;Rounding up?
|
||
|
jnz RndIntToZero ;No, chop it
|
||
|
RndIntToOne:
|
||
|
mov ebx,1 shl 31
|
||
|
xor esi,esi
|
||
|
mov cl,bTAG_SNGL
|
||
|
mov EMSEG:[SWcc],RoundUp
|
||
|
ret
|
||
|
|
||
|
HalfBitRound:
|
||
|
add ebx,ebx ;Shift off MSB (round bit)
|
||
|
or ebx,esi
|
||
|
or ebx,eax
|
||
|
jnz RndIntToOne
|
||
|
ret ;Return zero
|
||
|
|
||
|
;**********
|
||
|
RoundToBit:
|
||
|
;**********
|
||
|
mov edx,ecx ;Make copy of exponent
|
||
|
sar edx,16 ;Bring rounding exponent down
|
||
|
jl NoSigBits
|
||
|
mov cl,dl
|
||
|
cmp cl,32 ;Rounding in low word?
|
||
|
jae RoundLow
|
||
|
;When cl = 31, the RoundBit is in the low half while the LSB is in the
|
||
|
;high half. We must preserve the RoundBit when we move it to eax.
|
||
|
xchg eax,esi ;Low half becomes sticky bits
|
||
|
or ah,al ;Preserve lowest bits in ah
|
||
|
add esi,-1 ;Set CY if any original sticky bits
|
||
|
sbb al,al ;Put original sticky bits in al
|
||
|
mov esi,ebx
|
||
|
xor ebx,ebx ;Shift mantissa right 32 bits
|
||
|
RoundLow:
|
||
|
mov edx,(1 shl 31) - 1
|
||
|
shr edx,cl ;Make mask
|
||
|
;Note in the case of cl = 31, edx is now zero.
|
||
|
mov edi,esi
|
||
|
and edi,edx
|
||
|
or edi,eax ;Any bits being lost?
|
||
|
jz RndSetTag ;All done
|
||
|
inc edx ;Mask for LSB
|
||
|
or EMSEG:[CURerr],Precision;Set flag on inexact result
|
||
|
test EMSEG:[CWcntl],RoundControl ;Check rounding control bits
|
||
|
.erre RCnear eq 0
|
||
|
jnz NotNearLow ;Not just round-to-nearest
|
||
|
mov edi,edx ;Save LSB mask
|
||
|
shr edi,1 ;Mask for round bit
|
||
|
jc SplitRound ;Round bit in eax?
|
||
|
test esi,edi ;Round bit set?
|
||
|
jz MaskOffLow
|
||
|
dec edi ;Mask for sticky bits
|
||
|
or edi,edx ;Sticky bits + LSB
|
||
|
and edi,esi
|
||
|
or edi,eax ;Any sticky bits set?
|
||
|
jz MaskOffLow
|
||
|
RoundUpThenMask:
|
||
|
mov EMSEG:[SWcc],RoundUp
|
||
|
add esi,edx ;Round up
|
||
|
adc ebx,0
|
||
|
jc RoundBumpExp
|
||
|
MaskOffLow:
|
||
|
dec edx ;Mask for round & sticky bits
|
||
|
not edx
|
||
|
and esi,edx ;Zero out low bits
|
||
|
RndSetTag:
|
||
|
or ebx,ebx ;Is it normalized?
|
||
|
jns RoundedHighHalf
|
||
|
or esi,esi ;Any bits in low half?
|
||
|
.erre bTAG_VALID eq 1
|
||
|
.erre bTAG_SNGL eq 0
|
||
|
setnz cl ;if low half==0 then cl=0 else cl=1
|
||
|
ret
|
||
|
|
||
|
SplitRound:
|
||
|
;Rounding high half in esi on rounding bit in eax
|
||
|
bt esi,0 ;Look at LSB
|
||
|
adc eax,(1 shl 31) - 1 ;Set CY if round up
|
||
|
jc RoundUpThenMask
|
||
|
or ebx,ebx ;Will set ZF for jnz below
|
||
|
RoundedHighHalf:
|
||
|
;Rounding occured in high half, which had been moved low.
|
||
|
;Move it back up high.
|
||
|
;
|
||
|
;ZF set here on content of ebx. If not zero, rounding high half in esi
|
||
|
;rippled forward into zero in ebx.
|
||
|
mov cl,bTAG_SNGL
|
||
|
jnz RndIntNorm ;Present high half should be zero
|
||
|
xchg ebx,esi ;Shift left 32 bits
|
||
|
ret
|
||
|
|
||
|
RndIntNorm:
|
||
|
;Rounded up high half of mantissa, which rolled over to 0.
|
||
|
add ecx,1 shl 16 ;Increase exponent
|
||
|
mov ebx,1 shl 31 ;Restore MSB
|
||
|
ret ;Tag already set to SNGL
|
||
|
|
||
|
RoundBumpExp:
|
||
|
;Mantissa was FFFFF... and rolled over to 0 when we rounded
|
||
|
add ecx,1 shl 16 ;Increase exponent
|
||
|
mov ebx,1 shl 31 ;Restore MSB
|
||
|
jmp MaskOffLow
|
||
|
|
||
|
NotNearLow:
|
||
|
;We want to increase the magnitude if RCup and +, or RCdown and -
|
||
|
mov al,EMSEG:[CWcntl] ;Get rounding control
|
||
|
sar ch,7 ;Expand sign through whole byte
|
||
|
.erre (not RCup and RoundControl) eq RCdown
|
||
|
xor al,ch ;Flip rounding bits if negative
|
||
|
and al,RoundControl
|
||
|
cmp al,RCup ;Rounding up?
|
||
|
jz RoundUpThenMask ;yes
|
||
|
jmp MaskOffLow ;No, chop it
|