subttl  emfadd.asm - Addition and Subtraction
	page
;*******************************************************************************
;	 Copyright (c) Microsoft Corporation 1991
;	 All Rights Reserved
;
;emfadd.asm - long double add and subtract
;	by Tim Paterson
;
;Purpose:
;	Long double add/subtract.
;Outputs:
;	Jumps to [RoundMode] to round and store result.
;
;Revision History:
;
; []	09/05/91  TP	Initial 32-bit version.
;
;*******************************************************************************

;*******************************************************************************
; Dispatch for Add/Sub/Subr
;
; Signs are passed in dx:
;       xor source sign with dl
;       xor dest sign with dh
;
;One operand has been loaded into ecx:ebx:esi ("source"), the other is
;pointed to by edi ("dest").  
;
;Tag of source is shifted.  Tag values are as follows:
.erre   TAG_SNGL        eq      0       ;SINGLE: low 32 bits are zero
.erre   TAG_VALID       eq      1
.erre   TAG_ZERO        eq      2
.erre   TAG_SPCL        eq      3       ;NAN, Infinity, Denormal, Empty
;Any special case routines not found in this file are in emarith.asm
tFaddDisp	label	dword		;Source (reg)	Dest (*[di])
	dd	AddDouble		;single		single
	dd	AddDouble		;single		double
	dd	AddSourceSign		;single		zero
	dd	AddSpclDest		;single		special
	dd	AddDouble		;double		single
	dd	AddDouble		;double		double
	dd	AddSourceSign		;double		zero
	dd	AddSpclDest		;double		special
	dd	AddDestSign		;zero		single
	dd	AddDestSign		;zero		double
	dd	AddZeroZero		;zero		zero
	dd	AddSpclDest		;zero		special
	dd	AddSpclSource		;special	single
	dd	AddSpclSource		;special	double
	dd	AddSpclSource		;special	zero
	dd	TwoOpBothSpcl		;special	special
	dd	AddTwoInf		;Two infinities

EM_ENTRY eFISUB16
eFISUB16:
        call    Load16Int
        mov     dx,bSign                ;Change sign of source
        jmp     AddSetResult

EM_ENTRY eFISUBR16
eFISUBR16:
        call    Load16Int
        mov     dx,bSign shl 8          ;Change sign of dest
        jmp     AddSetResult

EM_ENTRY eFIADD16
eFIADD16:
        call    Load16Int
        xor     edx,edx                 ;Both signs positive
        jmp     AddSetResult

EM_ENTRY eFISUB32
eFISUB32:
        call    Load32Int
        mov     dx,bSign                ;Change sign of source
        jmp     AddSetResult

EM_ENTRY eFISUBR32
eFISUBR32:
        call    Load32Int
        mov     dx,bSign shl 8          ;Change sign of dest
        jmp     AddSetResult

EM_ENTRY eFIADD32
eFIADD32:
        call    Load32Int
        xor     edx,edx                 ;Both signs positive
        jmp     AddSetResult

EM_ENTRY eFSUB32
eFSUB32:
        call    Load32Real
        mov     dx,bSign                ;Change sign of source
        jmp     AddSetResult

EM_ENTRY eFSUBR32
eFSUBR32:
        call    Load32Real
        mov     dx,bSign shl 8          ;Change sign of dest
        jmp     AddSetResult

EM_ENTRY eFADD32
eFADD32:
        call    Load32Real
        xor     edx,edx                 ;Both signs positive
        jmp     AddSetResult

EM_ENTRY eFSUB64
eFSUB64:
        call    Load64Real
        mov     dx,bSign                ;Change sign of source
        jmp     AddSetResult

EM_ENTRY eFSUBR64
eFSUBR64:
        call    Load64Real
        mov     dx,bSign shl 8          ;Change sign of dest
        jmp     AddSetResult

EM_ENTRY eFADD64
eFADD64:
        call    Load64Real
        xor     edx,edx                 ;Both signs positive
        jmp     AddSetResult


PolyAddDouble:
;This entry point is used by polynomial evaluator.
;It checks the operand in registers for zero, and doesn't require
;signs to be set up in dx.
;
;op1 mantissa in ebx:esi, exponent in high ecx, sign in ch bit 7, tag in cl
;edi = pointer to op2 in ds
	xor	edx,edx			;Addition
	cmp	cl,bTAG_ZERO		;Adding to zero?
        jnz     AddDouble
;Number in registers is zero, so just return value from memory.
        mov     ecx,EMSEG:[edi].ExpSgn
        mov     ebx,EMSEG:[edi].lManHi
        mov     esi,EMSEG:[edi].lManLo
        ret

EM_ENTRY eFSUBPreg
eFSUBPreg:
        push    offset PopWhenDone

EM_ENTRY eFSUBreg
eFSUBreg:
        xchg    esi,edi

EM_ENTRY eFSUBtop
eFSUBtop:
        mov     dx,bSign                ;Change sign of source
        jmp     AddHaveSgn

EM_ENTRY eFSUBRPreg
eFSUBRPreg:
        push    offset PopWhenDone

EM_ENTRY eFSUBRreg
eFSUBRreg:
        xchg    esi,edi

EM_ENTRY eFSUBRtop
eFSUBRtop:
        mov     dx,bSign shl 8          ;Change sign of dest
        jmp     AddHaveSgn


InsignifAdd:
	mov	eax,1			;Set sticky bit
	shl	ch,1			;Get sign, CY set IFF subtracting mant.
	jnc	ReturnOp1
	sub	esi,eax			;Subtract 1 from mantissa
	sbb	ebx,0
	neg	eax
ReturnOp1:
;ebx:esi:eax = normalized unrounded mantissa
;high half of ecx = exponent
;high bit of ch = sign
	jmp	EMSEG:[RoundMode]

EM_ENTRY eFADDPreg
eFADDPreg:
        push    offset PopWhenDone

EM_ENTRY eFADDreg
eFADDreg:
        xchg    esi,edi

EM_ENTRY eFADDtop
eFADDtop:
        xor     edx,edx                 ;Both signs positive
AddHaveSgn:
        mov     ecx,EMSEG:[esi].ExpSgn
        mov     ebx,EMSEG:[esi].lManHi
        mov     esi,EMSEG:[esi].lManLo
AddSetResult:
        mov     ebp,offset tFaddDisp
        mov     EMSEG:[Result],edi            ;Save result pointer
        mov     al,cl
        mov     ah,EMSEG:[edi].bTag
        test    ax,ZEROorSPCL * 100H + ZEROorSPCL
        jnz     TwoOpDispatch

;.erre   AddDouble eq $                  ;Fall into AddDouble

;*********
AddDouble:
;*********
;
;op1 mantissa in ebx:esi, exponent in high ecx, sign in ch bit 7
;dl = sign change for op1
;dh = sign change for op2
;edi = pointer to op2

	xor	ch,dl			;Flip sign if subtracting
	mov	eax,EMSEG:[edi].ExpSgn
	xor	ah,dh			;Flip sign if subtracting
	mov	edx,EMSEG:[edi].lManHi
	mov	edi,EMSEG:[edi].lManLo

AddDoubleReg:
;op1 mantissa in ebx:esi, exponent in high ecx, sign in ch bit 7
;op2 mantissa in edx:edi, exponent in high eax, sign in ah bit 7

	cmp	eax,ecx			;Compare exponents
.erre	TexpBias eq 0			;Not biased, use signed jump
	jle	short HavLg		;op1 is larger, we have the right order
	xchg	esi,edi
	xchg	ebx,edx
	xchg	eax,ecx
HavLg:
;Larger in ebx:esi.  Note that if the exponents were equal, things like
;the sign bit or tag may have determined which is "larger".  It doesn't
;matter which is which if the exponents are equal, however.
	and	ah,80H			;Keep sign bit
	sar	ch,1			;Extend sign into bit 6 of byte
	xor	ch,ah			;See if signs are the same
	xor	ax,ax			;Clear out sign and tag
	neg	eax			;ax still 0
	add	eax,ecx			;Get exponent difference
	shr	eax,16			;Bring exp. difference down to low end
	jz	short Aligned
	cmp	eax,64+1		;Is difference in range?
;CONSIDER: tell me again why 1/4 LSB could have effect.  It seems like
;CONSIDER: 1/2 LSB is the limit.
	ja	short InsignifAdd	;  (Even 1/4 LSB could have effect)
	mov	cl,al			;Shift count to cl
;High half ecx = exponent
;ch bit 7 = sign difference
;ch bit 6 = sign
;cl = shift count
	xor	eax,eax			;Prepare to take bits shifted out
	cmp	cl,32			;More than a whole word?
	jb	short ShortShift
	xchg	eax,edx			;Save bits shifted out in eax
	xchg	edi,eax
	sub	cl,32
	cmp	cl,8			;Safe to shift this much
	jb	short ShortSticky
;Collapse all (sticky) bits of eax into LSB of edi
	neg	eax			;Sets CY if eax was not zero
	sbb	eax,eax			;-1 if CY was set, zero otherwise
	neg	eax			;Sticky bit in LSB only
	or	di,ax			;Move sticky bit up
	cmp	cl,32			;Less than another Dword?
	jb	short ShortShift
	mov	eax,edi
	xor	edi,edi			;edx = edi = 0
ShortSticky:
;Shift will not be more than 8 bits
	or	ah,al			;Move up sticky bits
ShortShift:
	shrd	eax,edi,cl		;Save bits shifted out in eax
	shrd	edi,edx,cl
	shr	edx,cl
Aligned:
	shl	ch,1			;Were signs the same?
	jc	short SubMant		;No--go subtract mantissas
;Add mantissas
	add	esi,edi
	adc	ebx,edx
	jnc	short AddExit
;Addition of mantissas overflowed. Bump exponent and shift right
	shrd	eax,esi,1
	shrd	esi,ebx,1		;Faster than RCR
	sar	ebx,1
	or	ebx,1 shl 31		;Set MSB
	add	ecx,1 shl 16
AddExit:
;ebx:esi:eax = normalized unrounded mantissa
;high half of ecx = exponent
;high bit of ch = sign
	jmp	EMSEG:[RoundMode]

NegMant:
;To get here, exponents must have been equal and op2 was bigger than op1.
;Note that this means nothing ever got shifted into eax.
	not	ch			;Change sign of result
	not	ebx
	neg	esi
	sbb	ebx,-1
	js	short AddExit		;Already normalized?
	test	ebx,40000000H		;Only one bit out of normal?
	jz	short NormalizeAdd
	jmp	short NormOneBit

SubMant:
;Subtract mantissas
	neg	eax			;Pretend minuend is zero extended
	sbb	esi,edi
	sbb	ebx,edx
	jc	short NegMant
	js	short AddExit		;Already normalized?
NormChk:
	test	ebx,40000000H		;Only one bit out of normal?
	jz	short NormalizeAdd
;One bit normalization
NormOneBit:
	sub	ecx,1 shl 16		;Adjust exponent
ShiftOneBit:				;Entry point from emfmul.asm
	shld	ebx,esi,1
	shld	esi,eax,1
	shl	eax,1
	jmp	EMSEG:[RoundMode]

;***********
AddZeroZero:				;Entry point for adding two zeros
;***********
	mov	ah,EMSEG:[edi].bSgn	;Get sign of op
	xor	ch,dl			;Possibly subtracting source
	xor	ah,dh			;Possibly subtracting dest
	xor	ch,ah			;Do signs match?
	js	FindZeroSign		;No - use rounding mode to set sign
	mov	EMSEG:[edi].bSgn,ah	;Correct the sign if subtracting
	ret				;Result at [edi] is now correct

ZeroChk:
;Upper 64 bits were all zero, but there could be 1 bit in the MSB
;of eax.
	or	eax,eax
	jnz	short OneBitLeft
	mov	ebx,eax
	mov	esi,eax			;Zero mantissa
FindZeroSign:
;Round to -0 if "round down" mode, round to +0 otherwise
	xor	ecx,ecx			;Zero exponent, positive sign
	mov	dl,EMSEG:[CWcntl]	;Get control word
	and	dl,RoundControl
        cmp	dl,RCdown		;Rounding down?
	jnz	ZeroJmp
	mov	ch,80H			;Set sign bit
ZeroJmp:
	mov	cl,bTAG_ZERO
	jmp	EMSEG:[ZeroVector]

OneBitLeft:
	xchg	ebx,eax			;Bit now normalized
	sub	ecx,64 shl 16		;Adjust exponent
	jmp	EMSEG:[RoundMode]

NormalizeAdd:
;Inputs:
;	ebx:esi:eax = 65-bit number
;	ecx high half = exponent
;
;Since we are more than 1 bit out of normalization, exponents must have
;differed by 0 or 1.  Thus rounding will not be necessary for 64 bits.
	bsr	edx,ebx			;Scan for MSB
	jnz	short ShortNorm
	bsr	edx,esi
	jz	short ZeroChk
	sub	ecx,32 shl 16		;Adjust exponent
	mov	ebx,esi			;Push it up 32 bits
	mov	esi,eax
ShortNorm:
;Bit number in edx ranges from 0 to 31
	mov	cl,dl
	not	cl			;Convert bit number to shift count
	shld	ebx,esi,cl
	shld	esi,eax,cl
	shl	edx,16			;Move exp. adjustment to high end
	lea	ecx,[ecx+edx-(31 shl 16)] ;Adjust exponent
	xor	eax,eax			;No extra bits
	jmp	EMSEG:[RoundMode]

AddDestSign:
	xor	EMSEG:[edi].bSgn,dh
	ret

AddSourceSign:
	xor	ch,dl
	jmp	SaveResult