2005-04-17 02:20:36 +04:00
/*---------------------------------------------------------------------------+
| reg_divide . c |
| |
| Divide one FPU_REG by another and put the result in a destination FPU_REG . |
| |
| Copyright ( C ) 1996 |
| W . Metzenthen , 22 Parker St , Ormond , Vic 3163 , Australia |
| E - mail billm @ jacobi . maths . monash . edu . au |
| |
| Return value is the tag of the answer , or - ed with FPU_Exception if |
| one was raised , or - 1 on internal error . |
| |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*---------------------------------------------------------------------------+
| The destination may be any FPU_REG , including one of the source FPU_REGs . |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# include "exception.h"
# include "reg_constant.h"
# include "fpu_emu.h"
# include "fpu_system.h"
/*
Divide one register by another and put the result into a third register .
*/
int FPU_div ( int flags , int rm , int control_w )
{
2008-01-30 15:30:11 +03:00
FPU_REG x , y ;
FPU_REG const * a , * b , * st0_ptr , * st_ptr ;
FPU_REG * dest ;
u_char taga , tagb , signa , signb , sign , saved_sign ;
int tag , deststnr ;
if ( flags & DEST_RM )
deststnr = rm ;
else
deststnr = 0 ;
if ( flags & REV ) {
b = & st ( 0 ) ;
st0_ptr = b ;
tagb = FPU_gettag0 ( ) ;
if ( flags & LOADED ) {
a = ( FPU_REG * ) rm ;
taga = flags & 0x0f ;
} else {
a = & st ( rm ) ;
st_ptr = a ;
taga = FPU_gettagi ( rm ) ;
}
} else {
a = & st ( 0 ) ;
st0_ptr = a ;
taga = FPU_gettag0 ( ) ;
if ( flags & LOADED ) {
b = ( FPU_REG * ) rm ;
tagb = flags & 0x0f ;
} else {
b = & st ( rm ) ;
st_ptr = b ;
tagb = FPU_gettagi ( rm ) ;
}
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:30:11 +03:00
signa = getsign ( a ) ;
signb = getsign ( b ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:30:11 +03:00
sign = signa ^ signb ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:30:11 +03:00
dest = & st ( deststnr ) ;
saved_sign = getsign ( dest ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:30:11 +03:00
if ( ! ( taga | tagb ) ) {
/* Both regs Valid, this should be the most common case. */
reg_copy ( a , & x ) ;
reg_copy ( b , & y ) ;
setpositive ( & x ) ;
setpositive ( & y ) ;
tag = FPU_u_div ( & x , & y , dest , control_w , sign ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:30:11 +03:00
if ( tag < 0 )
return tag ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:30:11 +03:00
FPU_settagi ( deststnr , tag ) ;
return tag ;
}
2005-04-17 02:20:36 +04:00
2008-01-30 15:30:11 +03:00
if ( taga = = TAG_Special )
taga = FPU_Special ( a ) ;
if ( tagb = = TAG_Special )
tagb = FPU_Special ( b ) ;
2005-04-17 02:20:36 +04:00
2008-01-30 15:30:11 +03:00
if ( ( ( taga = = TAG_Valid ) & & ( tagb = = TW_Denormal ) )
2005-04-17 02:20:36 +04:00
| | ( ( taga = = TW_Denormal ) & & ( tagb = = TAG_Valid ) )
2008-01-30 15:30:11 +03:00
| | ( ( taga = = TW_Denormal ) & & ( tagb = = TW_Denormal ) ) ) {
if ( denormal_operand ( ) < 0 )
return FPU_Exception ;
FPU_to_exp16 ( a , & x ) ;
FPU_to_exp16 ( b , & y ) ;
tag = FPU_u_div ( & x , & y , dest , control_w , sign ) ;
if ( tag < 0 )
return tag ;
FPU_settagi ( deststnr , tag ) ;
return tag ;
} else if ( ( taga < = TW_Denormal ) & & ( tagb < = TW_Denormal ) ) {
if ( tagb ! = TAG_Zero ) {
/* Want to find Zero/Valid */
if ( tagb = = TW_Denormal ) {
if ( denormal_operand ( ) < 0 )
return FPU_Exception ;
}
/* The result is zero. */
FPU_copy_to_regi ( & CONST_Z , TAG_Zero , deststnr ) ;
setsign ( dest , sign ) ;
return TAG_Zero ;
}
/* We have an exception condition, either 0/0 or Valid/Zero. */
if ( taga = = TAG_Zero ) {
/* 0/0 */
return arith_invalid ( deststnr ) ;
}
/* Valid/Zero */
return FPU_divide_by_zero ( deststnr , sign ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-30 15:30:11 +03:00
/* Must have infinities, NaNs, etc */
else if ( ( taga = = TW_NaN ) | | ( tagb = = TW_NaN ) ) {
if ( flags & LOADED )
return real_2op_NaN ( ( FPU_REG * ) rm , flags & 0x0f , 0 ,
st0_ptr ) ;
if ( flags & DEST_RM ) {
int tag ;
tag = FPU_gettag0 ( ) ;
if ( tag = = TAG_Special )
tag = FPU_Special ( st0_ptr ) ;
return real_2op_NaN ( st0_ptr , tag , rm ,
( flags & REV ) ? st0_ptr : & st ( rm ) ) ;
} else {
int tag ;
tag = FPU_gettagi ( rm ) ;
if ( tag = = TAG_Special )
tag = FPU_Special ( & st ( rm ) ) ;
return real_2op_NaN ( & st ( rm ) , tag , 0 ,
( flags & REV ) ? st0_ptr : & st ( rm ) ) ;
}
} else if ( taga = = TW_Infinity ) {
if ( tagb = = TW_Infinity ) {
/* infinity/infinity */
return arith_invalid ( deststnr ) ;
} else {
/* tagb must be Valid or Zero */
if ( ( tagb = = TW_Denormal ) & & ( denormal_operand ( ) < 0 ) )
return FPU_Exception ;
/* Infinity divided by Zero or Valid does
not raise and exception , but returns Infinity */
FPU_copy_to_regi ( a , TAG_Special , deststnr ) ;
setsign ( dest , sign ) ;
return taga ;
}
} else if ( tagb = = TW_Infinity ) {
if ( ( taga = = TW_Denormal ) & & ( denormal_operand ( ) < 0 ) )
return FPU_Exception ;
/* The result is zero. */
FPU_copy_to_regi ( & CONST_Z , TAG_Zero , deststnr ) ;
setsign ( dest , sign ) ;
return TAG_Zero ;
2005-04-17 02:20:36 +04:00
}
# ifdef PARANOID
2008-01-30 15:30:11 +03:00
else {
EXCEPTION ( EX_INTERNAL | 0x102 ) ;
return FPU_Exception ;
}
# endif /* PARANOID */
2005-04-17 02:20:36 +04:00
return 0 ;
}