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