2005-04-17 02:20:36 +04:00
/*
NetWinder Floating Point Emulator
( c ) Rebel . COM , 1998 , 1999
( c ) Philip Blundell , 1999 , 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"
# include "fpa11.inl"
# include "fpmodule.h"
# include "fpmodule.inl"
2005-10-12 22:58:10 +04:00
# include "softfloat.h"
2005-04-17 02:20:36 +04:00
unsigned int PerformFLT ( const unsigned int opcode ) ;
unsigned int PerformFIX ( const unsigned int opcode ) ;
static unsigned int PerformComparison ( const unsigned int opcode ) ;
unsigned int EmulateCPRT ( const unsigned int opcode )
{
if ( opcode & 0x800000 ) {
/* This is some variant of a comparison (PerformComparison
will sort out which one ) . Since most of the other CPRT
instructions are oddball cases of some sort or other it
makes sense to pull this out into a fast path . */
return PerformComparison ( opcode ) ;
}
/* Hint to GCC that we'd like a jump table rather than a load of CMPs */
switch ( ( opcode & 0x700000 ) > > 20 ) {
case FLT_CODE > > 20 :
return PerformFLT ( opcode ) ;
break ;
case FIX_CODE > > 20 :
return PerformFIX ( opcode ) ;
break ;
case WFS_CODE > > 20 :
writeFPSR ( readRegister ( getRd ( opcode ) ) ) ;
break ;
case RFS_CODE > > 20 :
writeRegister ( getRd ( opcode ) , readFPSR ( ) ) ;
break ;
default :
return 0 ;
}
return 1 ;
}
unsigned int PerformFLT ( const unsigned int opcode )
{
FPA11 * fpa11 = GET_FPA11 ( ) ;
2005-08-03 22:49:17 +04:00
struct roundingData roundData ;
roundData . mode = SetRoundingMode ( opcode ) ;
roundData . precision = SetRoundingPrecision ( opcode ) ;
roundData . exception = 0 ;
2005-04-17 02:20:36 +04:00
switch ( opcode & MASK_ROUNDING_PRECISION ) {
case ROUND_SINGLE :
{
fpa11 - > fType [ getFn ( opcode ) ] = typeSingle ;
2005-08-03 22:49:17 +04:00
fpa11 - > fpreg [ getFn ( opcode ) ] . fSingle = int32_to_float32 ( & roundData , readRegister ( getRd ( opcode ) ) ) ;
2005-04-17 02:20:36 +04:00
}
break ;
case ROUND_DOUBLE :
{
fpa11 - > fType [ getFn ( opcode ) ] = typeDouble ;
fpa11 - > fpreg [ getFn ( opcode ) ] . fDouble = int32_to_float64 ( readRegister ( getRd ( opcode ) ) ) ;
}
break ;
# ifdef CONFIG_FPE_NWFPE_XP
case ROUND_EXTENDED :
{
fpa11 - > fType [ getFn ( opcode ) ] = typeExtended ;
fpa11 - > fpreg [ getFn ( opcode ) ] . fExtended = int32_to_floatx80 ( readRegister ( getRd ( opcode ) ) ) ;
}
break ;
# endif
default :
return 0 ;
}
2005-08-03 22:49:17 +04:00
if ( roundData . exception )
float_raise ( roundData . exception ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
unsigned int PerformFIX ( const unsigned int opcode )
{
FPA11 * fpa11 = GET_FPA11 ( ) ;
unsigned int Fn = getFm ( opcode ) ;
2005-08-03 22:49:17 +04:00
struct roundingData roundData ;
2005-04-17 02:20:36 +04:00
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
switch ( fpa11 - > fType [ Fn ] ) {
case typeSingle :
{
2005-08-03 22:49:17 +04:00
writeRegister ( getRd ( opcode ) , float32_to_int32 ( & roundData , fpa11 - > fpreg [ Fn ] . fSingle ) ) ;
2005-04-17 02:20:36 +04:00
}
break ;
case typeDouble :
{
2005-08-03 22:49:17 +04:00
writeRegister ( getRd ( opcode ) , float64_to_int32 ( & roundData , fpa11 - > fpreg [ Fn ] . fDouble ) ) ;
2005-04-17 02:20:36 +04:00
}
break ;
# ifdef CONFIG_FPE_NWFPE_XP
case typeExtended :
{
2005-08-03 22:49:17 +04:00
writeRegister ( getRd ( opcode ) , floatx80_to_int32 ( & roundData , fpa11 - > fpreg [ Fn ] . fExtended ) ) ;
2005-04-17 02:20:36 +04:00
}
break ;
# endif
default :
return 0 ;
}
2005-08-03 22:49:17 +04:00
if ( roundData . exception )
float_raise ( roundData . exception ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
/* This instruction sets the flags N, Z, C, V in the FPSR. */
static unsigned int PerformComparison ( const unsigned int opcode )
{
FPA11 * fpa11 = GET_FPA11 ( ) ;
unsigned int Fn = getFn ( opcode ) , Fm = getFm ( opcode ) ;
int e_flag = opcode & 0x400000 ; /* 1 if CxFE */
int n_flag = opcode & 0x200000 ; /* 1 if CNxx */
unsigned int flags = 0 ;
# ifdef CONFIG_FPE_NWFPE_XP
floatx80 rFn , rFm ;
/* Check for unordered condition and convert all operands to 80-bit
format .
? ? Might be some mileage in avoiding this conversion if possible .
Eg , if both operands are 32 - bit , detect this and do a 32 - bit
comparison ( cheaper than an 80 - bit one ) . */
switch ( fpa11 - > fType [ Fn ] ) {
case typeSingle :
//printk("single.\n");
if ( float32_is_nan ( fpa11 - > fpreg [ Fn ] . fSingle ) )
goto unordered ;
rFn = float32_to_floatx80 ( fpa11 - > fpreg [ Fn ] . fSingle ) ;
break ;
case typeDouble :
//printk("double.\n");
if ( float64_is_nan ( fpa11 - > fpreg [ Fn ] . fDouble ) )
goto unordered ;
rFn = float64_to_floatx80 ( fpa11 - > fpreg [ Fn ] . fDouble ) ;
break ;
case typeExtended :
//printk("extended.\n");
if ( floatx80_is_nan ( fpa11 - > fpreg [ Fn ] . fExtended ) )
goto unordered ;
rFn = fpa11 - > fpreg [ Fn ] . fExtended ;
break ;
default :
return 0 ;
}
if ( CONSTANT_FM ( opcode ) ) {
//printk("Fm is a constant: #%d.\n",Fm);
rFm = getExtendedConstant ( Fm ) ;
if ( floatx80_is_nan ( rFm ) )
goto unordered ;
} else {
//printk("Fm = r%d which contains a ",Fm);
switch ( fpa11 - > fType [ Fm ] ) {
case typeSingle :
//printk("single.\n");
if ( float32_is_nan ( fpa11 - > fpreg [ Fm ] . fSingle ) )
goto unordered ;
rFm = float32_to_floatx80 ( fpa11 - > fpreg [ Fm ] . fSingle ) ;
break ;
case typeDouble :
//printk("double.\n");
if ( float64_is_nan ( fpa11 - > fpreg [ Fm ] . fDouble ) )
goto unordered ;
rFm = float64_to_floatx80 ( fpa11 - > fpreg [ Fm ] . fDouble ) ;
break ;
case typeExtended :
//printk("extended.\n");
if ( floatx80_is_nan ( fpa11 - > fpreg [ Fm ] . fExtended ) )
goto unordered ;
rFm = fpa11 - > fpreg [ Fm ] . fExtended ;
break ;
default :
return 0 ;
}
}
if ( n_flag )
rFm . high ^ = 0x8000 ;
/* test for less than condition */
if ( floatx80_lt ( rFn , rFm ) )
flags | = CC_NEGATIVE ;
/* test for equal condition */
if ( floatx80_eq ( rFn , rFm ) )
flags | = CC_ZERO ;
/* test for greater than or equal condition */
if ( floatx80_lt ( rFm , rFn ) )
flags | = CC_CARRY ;
# else
if ( CONSTANT_FM ( opcode ) ) {
/* Fm is a constant. Do the comparison in whatever precision
Fn happens to be stored in . */
if ( fpa11 - > fType [ Fn ] = = typeSingle ) {
float32 rFm = getSingleConstant ( Fm ) ;
float32 rFn = fpa11 - > fpreg [ Fn ] . fSingle ;
if ( float32_is_nan ( rFn ) )
goto unordered ;
if ( n_flag )
rFm ^ = 0x80000000 ;
/* test for less than condition */
if ( float32_lt_nocheck ( rFn , rFm ) )
flags | = CC_NEGATIVE ;
/* test for equal condition */
if ( float32_eq_nocheck ( rFn , rFm ) )
flags | = CC_ZERO ;
/* test for greater than or equal condition */
if ( float32_lt_nocheck ( rFm , rFn ) )
flags | = CC_CARRY ;
} else {
float64 rFm = getDoubleConstant ( Fm ) ;
float64 rFn = fpa11 - > fpreg [ Fn ] . fDouble ;
if ( float64_is_nan ( rFn ) )
goto unordered ;
if ( n_flag )
rFm ^ = 0x8000000000000000ULL ;
/* test for less than condition */
if ( float64_lt_nocheck ( rFn , rFm ) )
flags | = CC_NEGATIVE ;
/* test for equal condition */
if ( float64_eq_nocheck ( rFn , rFm ) )
flags | = CC_ZERO ;
/* test for greater than or equal condition */
if ( float64_lt_nocheck ( rFm , rFn ) )
flags | = CC_CARRY ;
}
} else {
/* Both operands are in registers. */
if ( fpa11 - > fType [ Fn ] = = typeSingle
& & fpa11 - > fType [ Fm ] = = typeSingle ) {
float32 rFm = fpa11 - > fpreg [ Fm ] . fSingle ;
float32 rFn = fpa11 - > fpreg [ Fn ] . fSingle ;
if ( float32_is_nan ( rFn )
| | float32_is_nan ( rFm ) )
goto unordered ;
if ( n_flag )
rFm ^ = 0x80000000 ;
/* test for less than condition */
if ( float32_lt_nocheck ( rFn , rFm ) )
flags | = CC_NEGATIVE ;
/* test for equal condition */
if ( float32_eq_nocheck ( rFn , rFm ) )
flags | = CC_ZERO ;
/* test for greater than or equal condition */
if ( float32_lt_nocheck ( rFm , rFn ) )
flags | = CC_CARRY ;
} else {
/* Promote 32-bit operand to 64 bits. */
float64 rFm , rFn ;
rFm = ( fpa11 - > fType [ Fm ] = = typeSingle ) ?
float32_to_float64 ( fpa11 - > fpreg [ Fm ] . fSingle )
: fpa11 - > fpreg [ Fm ] . fDouble ;
rFn = ( fpa11 - > fType [ Fn ] = = typeSingle ) ?
float32_to_float64 ( fpa11 - > fpreg [ Fn ] . fSingle )
: fpa11 - > fpreg [ Fn ] . fDouble ;
if ( float64_is_nan ( rFn )
| | float64_is_nan ( rFm ) )
goto unordered ;
if ( n_flag )
rFm ^ = 0x8000000000000000ULL ;
/* test for less than condition */
if ( float64_lt_nocheck ( rFn , rFm ) )
flags | = CC_NEGATIVE ;
/* test for equal condition */
if ( float64_eq_nocheck ( rFn , rFm ) )
flags | = CC_ZERO ;
/* test for greater than or equal condition */
if ( float64_lt_nocheck ( rFm , rFn ) )
flags | = CC_CARRY ;
}
}
# endif
writeConditionCodes ( flags ) ;
return 1 ;
unordered :
/* ?? The FPA data sheet is pretty vague about this, in particular
about whether the non - E comparisons can ever raise exceptions .
This implementation is based on a combination of what it says in
the data sheet , observation of how the Acorn emulator actually
behaves ( and how programs expect it to ) and guesswork . */
flags | = CC_OVERFLOW ;
flags & = ~ ( CC_ZERO | CC_NEGATIVE ) ;
if ( BIT_AC & readFPSR ( ) )
flags | = CC_CARRY ;
if ( e_flag )
float_raise ( float_flag_invalid ) ;
writeConditionCodes ( flags ) ;
return 1 ;
}