windows-nt/Source/XPSP1/NT/base/crts/fpw32/tran/i386/ldsplit.asm

310 lines
5.8 KiB
NASM
Raw Normal View History

2020-09-26 03:20:57 -05:00
page ,132
title ldsplit - split long double
;***
;ldsplit.asm - split long double into two doubles
;
; Copyright (c) 1992-2001, Microsoft Corporation. All rights reserved.
;
;Purpose:
; Helper for handling 10byte long double quantities if there is no
; compiler support.
;
;Revision History:
;
; 04/21/92 GDP written
;
;*******************************************************************************
.xlist
include cruntime.inc
include mrt386.inc
include elem87.inc
include os2supp.inc
.list
.data
labelB TagTable
; C2 C1 C0 C3 Meaning
db 2 * 4 ; 0 0 0 0 +Unnormal=> NAN
db 1 * 4 ; 0 0 0 1 +Zero => Zero
db 2 * 4 ; 0 0 1 0 +NAN => NAN
db 2 * 4 ; 0 0 1 1 Empty => NAN
db 2 * 4 ; 0 1 0 0 -Unnormal=> NAN
db 1 * 4 ; 0 1 0 1 -Zero => Zero
db 2 * 4 ; 0 1 1 0 -NAN => NAN
db 2 * 4 ; 0 1 1 1 Empty => NAN
db 0 * 4 ; 1 0 0 0 +Normal => Valid
db 4 * 4 ; 1 0 0 1 +Denormal=> Denormal
db 3 * 4 ; 1 0 1 0 +Infinity=> Infinity
db 2 * 4 ; 1 0 1 1 Empty => NAN
db 0 * 4 ; 1 1 0 0 -Normal => Valid
db 4 * 4 ; 1 1 0 1 -Denormal=> Zero
db 3 * 4 ; 1 1 1 0 -Infinity=> Infinity
db 2 * 4 ; 1 1 1 1 Empty => NAN
; factor = 2^64
staticQ factor, 043F0000000000000R
LDBIAS equ 3fffh
DBIAS equ 3ffh
MAX_BIASED_DEXP equ 7feh
CODESEG
table:
dd valid
dd zero
dd nan
dd inf
dd denorm
;***
;int _ldsplit(pld, pd1, pd2) - split long double
;
;Purpose:
; partition a long double quantity ld into two double quantities
; d1, d2 and an integer scaling factror s. The mantissa of d1 has
; the high order word of the mantissa of ld. Respectively, the
; mantissa of d2 has the low order word of the mantissa of ld.
; The following relation should be satisfied:
;
; ld == ((long double)d1 + (long double)d2) * 2^s
;
; s is 0, unless d1 or d2 cannot be expressed as normalized
; doubles; in that case s != 0, and .5 <= d1 < 1
;
;
;Entry:
; pld pointer to the long double argument
; pd1 pointer to d1
; pd2 pointer to d2
;
;Exit:
; *pd1, *pd2 are updated
; return value is equal to s
;
;
;Exceptions:
; This function should raise no IEEE exceptions.
; special cases:
; ld is QNAN or SNAN: d1 = QNAN, d2 = 0, s = 0
; ls is INF: d1 = INF, d2 = 0, s = 0
;
;
;******************************************************************************/
_ldsplit proc uses ebx edx edi, pld:dword, pd1:dword, pd2:dword
local ld:tbyte
local exp_adj:dword
local retvalue:dword
local denorm_adj:dword
mov [retvalue], 0 ; default return value
mov [denorm_adj], 0
mov ebx, [pld]
fld tbyte ptr [ebx]
fxam
fstsw ax
fstp [ld] ; store to local area
shl ah, 1
sar ah, 1
rol ah, 1
and ah, 0fh
mov al, ah
mov ebx, dataoffset TagTable ; Prepare for XLAT
xlat
movzx eax, al
mov ebx, OFFSET table
add ebx, eax
mov edx, pd1 ; edx points to the high order double
mov edi, pd2 ; edi points to the low order double
jmp [ebx]
lab valid
; have a valid normalized non-special long double
mov eax, dword ptr [ld]
or eax, eax
jz d2zero
; compute mantissa an exponent for d2
mov [exp_adj], 31 ; adjustment to be subtracted from exp of *pd2
;
; compute mantissa of d2
; shift left low order word of ld, until a '1' is hit
;
cmp eax, 0ffffh
ja shl16done
sal eax, 16
add [exp_adj], 16
lab shl16done
cmp eax, 0ffffffh
ja shl8done
sal eax, 8
add [exp_adj], 8
lab shl8done
lab shiftloop
inc [exp_adj]
sal eax, 1
jnc shiftloop
; now eax contains the mantissa for d2
; exp_adj is the difference of the
; exponents of d1 and d2
; exp_adj should be in the range
; 32 <= exp_adj <= 63
; By convention, if exp_adj is 0 then
; d2 is zero
lab setd2man
mov dword ptr [edi+4], 0
shld dword ptr [edi+4], eax, 20
shl eax, 20
mov [edi], eax
;
; set mantissa of d1
;
lab setd1man
mov eax, dword ptr [ld+4]
sal eax, 1 ; get rid of explicit bit
mov dword ptr [edx+4], 0
shld dword ptr [edx+4], eax, 20
shl eax, 20
mov [edx], eax
; check if exponent is in range
mov ax, word ptr [ld+8]
and ax, 07fffh ; clear sign bit
movzx eax, ax
sub eax, LDBIAS - DBIAS
cmp eax, MAX_BIASED_DEXP
ja expoutofrange
cmp eax, [exp_adj]
jb expoutofrange
;
; set exponent of d1
;
lab setexp1
mov ebx, eax ; save exp value
shl eax, 20
or dword ptr [edx+4], eax
cmp [exp_adj], 0
je exp2zero
sub ebx, [exp_adj]
je exp2zero
lab setexp2
shl ebx, 20
or dword ptr [edi+4], ebx
mov [retvalue], 0
lab setsign ; set correct signs and return
; at this point eax contains
; the return value
mov bx, word ptr [ld+8]
and bx, 1 SHL 15 ; get sign
or [edi+6], bx ; set sign bit
or [edx+6], bx ; set sign bit
mov eax, [retvalue]
add eax, [denorm_adj]
ret
lab d2zero
mov [exp_adj], 0
jmp setd2man
lab exp2zero
mov ebx, 0
jmp setexp2
lab expoutofrange
mov ebx, DBIAS
mov ecx, ebx
sub ecx, [exp_adj]
shl ebx, 20
or dword ptr [edx+4], ebx
shl ecx, 20
or dword ptr [edi+4], ecx
sub eax, DBIAS ; unbias exp
mov [retvalue], eax ; this is the return value
jmp short setsign
lab zero
mov dword ptr [edx], 0
mov dword ptr [edx+4], 0
mov dword ptr [edi], 0
mov dword ptr [edi+4], 0
jmp setsign
lab nan
mov dword ptr [edx], 0
mov dword ptr [edx+4], 07ff80000h
mov dword ptr [edi], 0
mov dword ptr [edi+4], 0
jmp setsign
lab inf
mov dword ptr [edx], 0
mov dword ptr [edx+4], 07ff00000h
mov dword ptr [edi], 0
mov dword ptr [edi+4], 0
jmp setsign
lab denorm
;
; We have a long double denormal
; so we know for sure that this is out of the double
; precision range, and the return value of _ldsplit
; should be non-zero.
; Multiply the denormal by 2^64, then adjust the
; return value by subtracting 64
;
; this assumes denormal exception masked
fld [ld]
fmul [factor]
fstp [ld]
mov [denorm_adj], 64
jmp valid
_ldsplit endp
end