2005-04-17 02:20:36 +04:00
/*
NetWinder Floating Point Emulator
( c ) Rebel . COM , 1998 , 1999
( c ) Philip Blundell , 2001
Direct questions , comments to Scott Bambrough < scottb @ netwinder . org >
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "fpa11.h"
# include "fpopcode.h"
2005-08-03 22:49:17 +04:00
unsigned int SingleCPDO ( struct roundingData * roundData , const unsigned int opcode , FPREG * rFd ) ;
unsigned int DoubleCPDO ( struct roundingData * roundData , const unsigned int opcode , FPREG * rFd ) ;
unsigned int ExtendedCPDO ( struct roundingData * roundData , const unsigned int opcode , FPREG * rFd ) ;
2005-04-17 02:20:36 +04:00
unsigned int EmulateCPDO ( const unsigned int opcode )
{
FPA11 * fpa11 = GET_FPA11 ( ) ;
FPREG * rFd ;
unsigned int nType , nDest , nRc ;
2005-08-03 22:49:17 +04:00
struct roundingData roundData ;
2005-04-17 02:20:36 +04:00
/* Get the destination size. If not valid let Linux perform
an invalid instruction trap . */
nDest = getDestinationSize ( opcode ) ;
if ( typeNone = = nDest )
return 0 ;
2005-08-03 22:49:17 +04:00
roundData . mode = SetRoundingMode ( opcode ) ;
roundData . precision = SetRoundingPrecision ( opcode ) ;
roundData . exception = 0 ;
2005-04-17 02:20:36 +04:00
/* Compare the size of the operands in Fn and Fm.
Choose the largest size and perform operations in that size ,
in order to make use of all the precision of the operands .
If Fm is a constant , we just grab a constant of a size
matching the size of the operand in Fn . */
if ( MONADIC_INSTRUCTION ( opcode ) )
nType = nDest ;
else
nType = fpa11 - > fType [ getFn ( opcode ) ] ;
if ( ! CONSTANT_FM ( opcode ) ) {
register unsigned int Fm = getFm ( opcode ) ;
if ( nType < fpa11 - > fType [ Fm ] ) {
nType = fpa11 - > fType [ Fm ] ;
}
}
rFd = & fpa11 - > fpreg [ getFd ( opcode ) ] ;
switch ( nType ) {
case typeSingle :
2005-08-03 22:49:17 +04:00
nRc = SingleCPDO ( & roundData , opcode , rFd ) ;
2005-04-17 02:20:36 +04:00
break ;
case typeDouble :
2005-08-03 22:49:17 +04:00
nRc = DoubleCPDO ( & roundData , opcode , rFd ) ;
2005-04-17 02:20:36 +04:00
break ;
# ifdef CONFIG_FPE_NWFPE_XP
case typeExtended :
2005-08-03 22:49:17 +04:00
nRc = ExtendedCPDO ( & roundData , opcode , rFd ) ;
2005-04-17 02:20:36 +04:00
break ;
# endif
default :
nRc = 0 ;
}
/* The CPDO functions used to always set the destination type
to be the same as their working size . */
if ( nRc ! = 0 ) {
/* If the operation succeeded, check to see if the result in the
destination register is the correct size . If not force it
to be . */
fpa11 - > fType [ getFd ( opcode ) ] = nDest ;
# ifdef CONFIG_FPE_NWFPE_XP
if ( nDest ! = nType ) {
switch ( nDest ) {
case typeSingle :
{
if ( typeDouble = = nType )
2005-08-03 22:49:17 +04:00
rFd - > fSingle = float64_to_float32 ( & roundData , rFd - > fDouble ) ;
2005-04-17 02:20:36 +04:00
else
2005-08-03 22:49:17 +04:00
rFd - > fSingle = floatx80_to_float32 ( & roundData , rFd - > fExtended ) ;
2005-04-17 02:20:36 +04:00
}
break ;
case typeDouble :
{
if ( typeSingle = = nType )
rFd - > fDouble = float32_to_float64 ( rFd - > fSingle ) ;
else
2005-08-03 22:49:17 +04:00
rFd - > fDouble = floatx80_to_float64 ( & roundData , rFd - > fExtended ) ;
2005-04-17 02:20:36 +04:00
}
break ;
case typeExtended :
{
if ( typeSingle = = nType )
rFd - > fExtended = float32_to_floatx80 ( rFd - > fSingle ) ;
else
rFd - > fExtended = float64_to_floatx80 ( rFd - > fDouble ) ;
}
break ;
}
}
# else
if ( nDest ! = nType ) {
if ( nDest = = typeSingle )
2005-08-03 22:49:17 +04:00
rFd - > fSingle = float64_to_float32 ( & roundData , rFd - > fDouble ) ;
2005-04-17 02:20:36 +04:00
else
rFd - > fDouble = float32_to_float64 ( rFd - > fSingle ) ;
}
# endif
}
2005-08-03 22:49:17 +04:00
if ( roundData . exception )
float_raise ( roundData . exception ) ;
2005-04-17 02:20:36 +04:00
return nRc ;
}