2005-04-17 02:20:36 +04:00
/*
NetWinder Floating Point Emulator
( c ) Rebel . com , 1998 - 1999
( c ) Philip Blundell , 1998 , 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 "softfloat.h"
# include "fpopcode.h"
# include "fpmodule.h"
# include "fpmodule.inl"
# include <asm/uaccess.h>
static inline void loadSingle ( const unsigned int Fn , const unsigned int __user * pMem )
{
FPA11 * fpa11 = GET_FPA11 ( ) ;
fpa11 - > fType [ Fn ] = typeSingle ;
get_user ( fpa11 - > fpreg [ Fn ] . fSingle , pMem ) ;
}
static inline void loadDouble ( const unsigned int Fn , const unsigned int __user * pMem )
{
FPA11 * fpa11 = GET_FPA11 ( ) ;
unsigned int * p ;
p = ( unsigned int * ) & fpa11 - > fpreg [ Fn ] . fDouble ;
fpa11 - > fType [ Fn ] = typeDouble ;
# ifdef __ARMEB__
get_user ( p [ 0 ] , & pMem [ 0 ] ) ; /* sign & exponent */
get_user ( p [ 1 ] , & pMem [ 1 ] ) ;
# else
get_user ( p [ 0 ] , & pMem [ 1 ] ) ;
get_user ( p [ 1 ] , & pMem [ 0 ] ) ; /* sign & exponent */
# endif
}
# ifdef CONFIG_FPE_NWFPE_XP
static inline void loadExtended ( const unsigned int Fn , const unsigned int __user * pMem )
{
FPA11 * fpa11 = GET_FPA11 ( ) ;
unsigned int * p ;
p = ( unsigned int * ) & fpa11 - > fpreg [ Fn ] . fExtended ;
fpa11 - > fType [ Fn ] = typeExtended ;
get_user ( p [ 0 ] , & pMem [ 0 ] ) ; /* sign & exponent */
2005-11-08 00:12:08 +03:00
# ifdef __ARMEB__
get_user ( p [ 1 ] , & pMem [ 1 ] ) ; /* ms bits */
get_user ( p [ 2 ] , & pMem [ 2 ] ) ; /* ls bits */
# else
2005-04-17 02:20:36 +04:00
get_user ( p [ 1 ] , & pMem [ 2 ] ) ; /* ls bits */
get_user ( p [ 2 ] , & pMem [ 1 ] ) ; /* ms bits */
2005-11-08 00:12:08 +03:00
# endif
2005-04-17 02:20:36 +04:00
}
# endif
static inline void loadMultiple ( const unsigned int Fn , const unsigned int __user * pMem )
{
FPA11 * fpa11 = GET_FPA11 ( ) ;
register unsigned int * p ;
unsigned long x ;
p = ( unsigned int * ) & ( fpa11 - > fpreg [ Fn ] ) ;
get_user ( x , & pMem [ 0 ] ) ;
fpa11 - > fType [ Fn ] = ( x > > 14 ) & 0x00000003 ;
switch ( fpa11 - > fType [ Fn ] ) {
case typeSingle :
case typeDouble :
{
get_user ( p [ 0 ] , & pMem [ 2 ] ) ; /* Single */
get_user ( p [ 1 ] , & pMem [ 1 ] ) ; /* double msw */
p [ 2 ] = 0 ; /* empty */
}
break ;
# ifdef CONFIG_FPE_NWFPE_XP
case typeExtended :
{
get_user ( p [ 1 ] , & pMem [ 2 ] ) ;
get_user ( p [ 2 ] , & pMem [ 1 ] ) ; /* msw */
p [ 0 ] = ( x & 0x80003fff ) ;
}
break ;
# endif
}
}
2005-08-03 22:49:17 +04:00
static inline void storeSingle ( struct roundingData * roundData , const unsigned int Fn , unsigned int __user * pMem )
2005-04-17 02:20:36 +04:00
{
FPA11 * fpa11 = GET_FPA11 ( ) ;
union {
float32 f ;
unsigned int i [ 1 ] ;
} val ;
switch ( fpa11 - > fType [ Fn ] ) {
case typeDouble :
2005-08-03 22:49:17 +04:00
val . f = float64_to_float32 ( 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
val . f = floatx80_to_float32 ( roundData , fpa11 - > fpreg [ Fn ] . fExtended ) ;
2005-04-17 02:20:36 +04:00
break ;
# endif
default :
val . f = fpa11 - > fpreg [ Fn ] . fSingle ;
}
put_user ( val . i [ 0 ] , pMem ) ;
}
2005-08-03 22:49:17 +04:00
static inline void storeDouble ( struct roundingData * roundData , const unsigned int Fn , unsigned int __user * pMem )
2005-04-17 02:20:36 +04:00
{
FPA11 * fpa11 = GET_FPA11 ( ) ;
union {
float64 f ;
unsigned int i [ 2 ] ;
} val ;
switch ( fpa11 - > fType [ Fn ] ) {
case typeSingle :
val . f = float32_to_float64 ( fpa11 - > fpreg [ Fn ] . fSingle ) ;
break ;
# ifdef CONFIG_FPE_NWFPE_XP
case typeExtended :
2005-08-03 22:49:17 +04:00
val . f = floatx80_to_float64 ( roundData , fpa11 - > fpreg [ Fn ] . fExtended ) ;
2005-04-17 02:20:36 +04:00
break ;
# endif
default :
val . f = fpa11 - > fpreg [ Fn ] . fDouble ;
}
# ifdef __ARMEB__
put_user ( val . i [ 0 ] , & pMem [ 0 ] ) ; /* msw */
put_user ( val . i [ 1 ] , & pMem [ 1 ] ) ; /* lsw */
# else
put_user ( val . i [ 1 ] , & pMem [ 0 ] ) ; /* msw */
put_user ( val . i [ 0 ] , & pMem [ 1 ] ) ; /* lsw */
# endif
}
# ifdef CONFIG_FPE_NWFPE_XP
static inline void storeExtended ( const unsigned int Fn , unsigned int __user * pMem )
{
FPA11 * fpa11 = GET_FPA11 ( ) ;
union {
floatx80 f ;
unsigned int i [ 3 ] ;
} val ;
switch ( fpa11 - > fType [ Fn ] ) {
case typeSingle :
val . f = float32_to_floatx80 ( fpa11 - > fpreg [ Fn ] . fSingle ) ;
break ;
case typeDouble :
val . f = float64_to_floatx80 ( fpa11 - > fpreg [ Fn ] . fDouble ) ;
break ;
default :
val . f = fpa11 - > fpreg [ Fn ] . fExtended ;
}
put_user ( val . i [ 0 ] , & pMem [ 0 ] ) ; /* sign & exp */
2005-11-08 00:12:08 +03:00
# ifdef __ARMEB__
put_user ( val . i [ 1 ] , & pMem [ 1 ] ) ; /* msw */
put_user ( val . i [ 2 ] , & pMem [ 2 ] ) ;
# else
2005-04-17 02:20:36 +04:00
put_user ( val . i [ 1 ] , & pMem [ 2 ] ) ;
put_user ( val . i [ 2 ] , & pMem [ 1 ] ) ; /* msw */
2005-11-08 00:12:08 +03:00
# endif
2005-04-17 02:20:36 +04:00
}
# endif
static inline void storeMultiple ( const unsigned int Fn , unsigned int __user * pMem )
{
FPA11 * fpa11 = GET_FPA11 ( ) ;
register unsigned int nType , * p ;
p = ( unsigned int * ) & ( fpa11 - > fpreg [ Fn ] ) ;
nType = fpa11 - > fType [ Fn ] ;
switch ( nType ) {
case typeSingle :
case typeDouble :
{
put_user ( p [ 0 ] , & pMem [ 2 ] ) ; /* single */
put_user ( p [ 1 ] , & pMem [ 1 ] ) ; /* double msw */
put_user ( nType < < 14 , & pMem [ 0 ] ) ;
}
break ;
# ifdef CONFIG_FPE_NWFPE_XP
case typeExtended :
{
put_user ( p [ 2 ] , & pMem [ 1 ] ) ; /* msw */
put_user ( p [ 1 ] , & pMem [ 2 ] ) ;
put_user ( ( p [ 0 ] & 0x80003fff ) | ( nType < < 14 ) , & pMem [ 0 ] ) ;
}
break ;
# endif
}
}
unsigned int PerformLDF ( const unsigned int opcode )
{
unsigned int __user * pBase , * pAddress , * pFinal ;
unsigned int nRc = 1 , write_back = WRITE_BACK ( opcode ) ;
pBase = ( unsigned int __user * ) readRegister ( getRn ( opcode ) ) ;
if ( REG_PC = = getRn ( opcode ) ) {
pBase + = 2 ;
write_back = 0 ;
}
pFinal = pBase ;
if ( BIT_UP_SET ( opcode ) )
pFinal + = getOffset ( opcode ) ;
else
pFinal - = getOffset ( opcode ) ;
if ( PREINDEXED ( opcode ) )
pAddress = pFinal ;
else
pAddress = pBase ;
switch ( opcode & MASK_TRANSFER_LENGTH ) {
case TRANSFER_SINGLE :
loadSingle ( getFd ( opcode ) , pAddress ) ;
break ;
case TRANSFER_DOUBLE :
loadDouble ( getFd ( opcode ) , pAddress ) ;
break ;
# ifdef CONFIG_FPE_NWFPE_XP
case TRANSFER_EXTENDED :
loadExtended ( getFd ( opcode ) , pAddress ) ;
break ;
# endif
default :
nRc = 0 ;
}
if ( write_back )
writeRegister ( getRn ( opcode ) , ( unsigned long ) pFinal ) ;
return nRc ;
}
unsigned int PerformSTF ( const unsigned int opcode )
{
unsigned int __user * pBase , * pAddress , * pFinal ;
unsigned int nRc = 1 , write_back = WRITE_BACK ( 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
pBase = ( unsigned int __user * ) readRegister ( getRn ( opcode ) ) ;
if ( REG_PC = = getRn ( opcode ) ) {
pBase + = 2 ;
write_back = 0 ;
}
pFinal = pBase ;
if ( BIT_UP_SET ( opcode ) )
pFinal + = getOffset ( opcode ) ;
else
pFinal - = getOffset ( opcode ) ;
if ( PREINDEXED ( opcode ) )
pAddress = pFinal ;
else
pAddress = pBase ;
switch ( opcode & MASK_TRANSFER_LENGTH ) {
case TRANSFER_SINGLE :
2005-08-03 22:49:17 +04:00
storeSingle ( & roundData , getFd ( opcode ) , pAddress ) ;
2005-04-17 02:20:36 +04:00
break ;
case TRANSFER_DOUBLE :
2005-08-03 22:49:17 +04:00
storeDouble ( & roundData , getFd ( opcode ) , pAddress ) ;
2005-04-17 02:20:36 +04:00
break ;
# ifdef CONFIG_FPE_NWFPE_XP
case TRANSFER_EXTENDED :
storeExtended ( getFd ( opcode ) , pAddress ) ;
break ;
# endif
default :
nRc = 0 ;
}
2005-08-03 22:49:17 +04:00
if ( roundData . exception )
float_raise ( roundData . exception ) ;
2005-04-17 02:20:36 +04:00
if ( write_back )
writeRegister ( getRn ( opcode ) , ( unsigned long ) pFinal ) ;
return nRc ;
}
unsigned int PerformLFM ( const unsigned int opcode )
{
unsigned int __user * pBase , * pAddress , * pFinal ;
unsigned int i , Fd , write_back = WRITE_BACK ( opcode ) ;
pBase = ( unsigned int __user * ) readRegister ( getRn ( opcode ) ) ;
if ( REG_PC = = getRn ( opcode ) ) {
pBase + = 2 ;
write_back = 0 ;
}
pFinal = pBase ;
if ( BIT_UP_SET ( opcode ) )
pFinal + = getOffset ( opcode ) ;
else
pFinal - = getOffset ( opcode ) ;
if ( PREINDEXED ( opcode ) )
pAddress = pFinal ;
else
pAddress = pBase ;
Fd = getFd ( opcode ) ;
for ( i = getRegisterCount ( opcode ) ; i > 0 ; i - - ) {
loadMultiple ( Fd , pAddress ) ;
pAddress + = 3 ;
Fd + + ;
if ( Fd = = 8 )
Fd = 0 ;
}
if ( write_back )
writeRegister ( getRn ( opcode ) , ( unsigned long ) pFinal ) ;
return 1 ;
}
unsigned int PerformSFM ( const unsigned int opcode )
{
unsigned int __user * pBase , * pAddress , * pFinal ;
unsigned int i , Fd , write_back = WRITE_BACK ( opcode ) ;
pBase = ( unsigned int __user * ) readRegister ( getRn ( opcode ) ) ;
if ( REG_PC = = getRn ( opcode ) ) {
pBase + = 2 ;
write_back = 0 ;
}
pFinal = pBase ;
if ( BIT_UP_SET ( opcode ) )
pFinal + = getOffset ( opcode ) ;
else
pFinal - = getOffset ( opcode ) ;
if ( PREINDEXED ( opcode ) )
pAddress = pFinal ;
else
pAddress = pBase ;
Fd = getFd ( opcode ) ;
for ( i = getRegisterCount ( opcode ) ; i > 0 ; i - - ) {
storeMultiple ( Fd , pAddress ) ;
pAddress + = 3 ;
Fd + + ;
if ( Fd = = 8 )
Fd = 0 ;
}
if ( write_back )
writeRegister ( getRn ( opcode ) , ( unsigned long ) pFinal ) ;
return 1 ;
}
unsigned int EmulateCPDT ( const unsigned int opcode )
{
unsigned int nRc = 0 ;
if ( LDF_OP ( opcode ) ) {
nRc = PerformLDF ( opcode ) ;
} else if ( LFM_OP ( opcode ) ) {
nRc = PerformLFM ( opcode ) ;
} else if ( STF_OP ( opcode ) ) {
nRc = PerformSTF ( opcode ) ;
} else if ( SFM_OP ( opcode ) ) {
nRc = PerformSFM ( opcode ) ;
} else {
nRc = 0 ;
}
return nRc ;
}