xref: /qemu/linux-user/arm/nwfpe/fpa11_cprt.c (revision 6b4c305c)
13ebdd119Saurel32 /*
23ebdd119Saurel32     NetWinder Floating Point Emulator
33ebdd119Saurel32     (c) Rebel.COM, 1998,1999
43ebdd119Saurel32     (c) Philip Blundell, 1999
53ebdd119Saurel32 
63ebdd119Saurel32     Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
73ebdd119Saurel32 
83ebdd119Saurel32     This program is free software; you can redistribute it and/or modify
93ebdd119Saurel32     it under the terms of the GNU General Public License as published by
103ebdd119Saurel32     the Free Software Foundation; either version 2 of the License, or
113ebdd119Saurel32     (at your option) any later version.
123ebdd119Saurel32 
133ebdd119Saurel32     This program is distributed in the hope that it will be useful,
143ebdd119Saurel32     but WITHOUT ANY WARRANTY; without even the implied warranty of
153ebdd119Saurel32     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
163ebdd119Saurel32     GNU General Public License for more details.
173ebdd119Saurel32 
183ebdd119Saurel32     You should have received a copy of the GNU General Public License
1970539e18SBlue Swirl     along with this program; if not, see <http://www.gnu.org/licenses/>.
203ebdd119Saurel32 */
213ebdd119Saurel32 
223ebdd119Saurel32 #include "fpa11.h"
23*6b4c305cSPaolo Bonzini #include "fpu/softfloat.h"
243ebdd119Saurel32 #include "fpopcode.h"
253ebdd119Saurel32 #include "fpa11.inl"
263ebdd119Saurel32 //#include "fpmodule.h"
273ebdd119Saurel32 //#include "fpmodule.inl"
283ebdd119Saurel32 
293ebdd119Saurel32 unsigned int PerformFLT(const unsigned int opcode);
303ebdd119Saurel32 unsigned int PerformFIX(const unsigned int opcode);
313ebdd119Saurel32 
323ebdd119Saurel32 static unsigned int
333ebdd119Saurel32 PerformComparison(const unsigned int opcode);
343ebdd119Saurel32 
353ebdd119Saurel32 unsigned int EmulateCPRT(const unsigned int opcode)
363ebdd119Saurel32 {
373ebdd119Saurel32   unsigned int nRc = 1;
383ebdd119Saurel32 
393ebdd119Saurel32   //printk("EmulateCPRT(0x%08x)\n",opcode);
403ebdd119Saurel32 
413ebdd119Saurel32   if (opcode & 0x800000)
423ebdd119Saurel32   {
433ebdd119Saurel32      /* This is some variant of a comparison (PerformComparison will
443ebdd119Saurel32 	sort out which one).  Since most of the other CPRT
453ebdd119Saurel32 	instructions are oddball cases of some sort or other it makes
463ebdd119Saurel32 	sense to pull this out into a fast path.  */
473ebdd119Saurel32      return PerformComparison(opcode);
483ebdd119Saurel32   }
493ebdd119Saurel32 
503ebdd119Saurel32   /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
513ebdd119Saurel32   switch ((opcode & 0x700000) >> 20)
523ebdd119Saurel32   {
533ebdd119Saurel32     case  FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
543ebdd119Saurel32     case  FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
553ebdd119Saurel32 
563ebdd119Saurel32     case  WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
573ebdd119Saurel32     case  RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
583ebdd119Saurel32 
593ebdd119Saurel32 #if 0    /* We currently have no use for the FPCR, so there's no point
603ebdd119Saurel32 	    in emulating it. */
613ebdd119Saurel32     case  WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode)));
623ebdd119Saurel32     case  RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break;
633ebdd119Saurel32 #endif
643ebdd119Saurel32 
653ebdd119Saurel32     default: nRc = 0;
663ebdd119Saurel32   }
673ebdd119Saurel32 
683ebdd119Saurel32   return nRc;
693ebdd119Saurel32 }
703ebdd119Saurel32 
713ebdd119Saurel32 unsigned int PerformFLT(const unsigned int opcode)
723ebdd119Saurel32 {
733ebdd119Saurel32    FPA11 *fpa11 = GET_FPA11();
743ebdd119Saurel32 
753ebdd119Saurel32    unsigned int nRc = 1;
763ebdd119Saurel32    SetRoundingMode(opcode);
773ebdd119Saurel32 
783ebdd119Saurel32    switch (opcode & MASK_ROUNDING_PRECISION)
793ebdd119Saurel32    {
803ebdd119Saurel32       case ROUND_SINGLE:
813ebdd119Saurel32       {
823ebdd119Saurel32         fpa11->fType[getFn(opcode)] = typeSingle;
833ebdd119Saurel32         fpa11->fpreg[getFn(opcode)].fSingle =
843ebdd119Saurel32 	   int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status);
853ebdd119Saurel32       }
863ebdd119Saurel32       break;
873ebdd119Saurel32 
883ebdd119Saurel32       case ROUND_DOUBLE:
893ebdd119Saurel32       {
903ebdd119Saurel32         fpa11->fType[getFn(opcode)] = typeDouble;
913ebdd119Saurel32         fpa11->fpreg[getFn(opcode)].fDouble =
923ebdd119Saurel32             int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status);
933ebdd119Saurel32       }
943ebdd119Saurel32       break;
953ebdd119Saurel32 
963ebdd119Saurel32       case ROUND_EXTENDED:
973ebdd119Saurel32       {
983ebdd119Saurel32         fpa11->fType[getFn(opcode)] = typeExtended;
993ebdd119Saurel32         fpa11->fpreg[getFn(opcode)].fExtended =
1003ebdd119Saurel32 	   int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status);
1013ebdd119Saurel32       }
1023ebdd119Saurel32       break;
1033ebdd119Saurel32 
1043ebdd119Saurel32       default: nRc = 0;
1053ebdd119Saurel32   }
1063ebdd119Saurel32 
1073ebdd119Saurel32   return nRc;
1083ebdd119Saurel32 }
1093ebdd119Saurel32 
1103ebdd119Saurel32 unsigned int PerformFIX(const unsigned int opcode)
1113ebdd119Saurel32 {
1123ebdd119Saurel32    FPA11 *fpa11 = GET_FPA11();
1133ebdd119Saurel32    unsigned int nRc = 1;
1143ebdd119Saurel32    unsigned int Fn = getFm(opcode);
1153ebdd119Saurel32 
1163ebdd119Saurel32    SetRoundingMode(opcode);
1173ebdd119Saurel32 
1183ebdd119Saurel32    switch (fpa11->fType[Fn])
1193ebdd119Saurel32    {
1203ebdd119Saurel32       case typeSingle:
1213ebdd119Saurel32       {
1223ebdd119Saurel32          writeRegister(getRd(opcode),
1233ebdd119Saurel32 	               float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status));
1243ebdd119Saurel32       }
1253ebdd119Saurel32       break;
1263ebdd119Saurel32 
1273ebdd119Saurel32       case typeDouble:
1283ebdd119Saurel32       {
1293ebdd119Saurel32          //printf("F%d is 0x%" PRIx64 "\n",Fn,fpa11->fpreg[Fn].fDouble);
1303ebdd119Saurel32          writeRegister(getRd(opcode),
1313ebdd119Saurel32 	               float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status));
1323ebdd119Saurel32       }
1333ebdd119Saurel32       break;
1343ebdd119Saurel32 
1353ebdd119Saurel32       case typeExtended:
1363ebdd119Saurel32       {
1373ebdd119Saurel32          writeRegister(getRd(opcode),
1383ebdd119Saurel32 	               floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status));
1393ebdd119Saurel32       }
1403ebdd119Saurel32       break;
1413ebdd119Saurel32 
1423ebdd119Saurel32       default: nRc = 0;
1433ebdd119Saurel32   }
1443ebdd119Saurel32 
1453ebdd119Saurel32   return nRc;
1463ebdd119Saurel32 }
1473ebdd119Saurel32 
1483ebdd119Saurel32 
14986178a57SJuan Quintela static __inline unsigned int
1503ebdd119Saurel32 PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
1513ebdd119Saurel32 {
1523ebdd119Saurel32    FPA11 *fpa11 = GET_FPA11();
1533ebdd119Saurel32    unsigned int flags = 0;
1543ebdd119Saurel32 
1553ebdd119Saurel32    /* test for less than condition */
1563ebdd119Saurel32    if (floatx80_lt(Fn,Fm, &fpa11->fp_status))
1573ebdd119Saurel32    {
1583ebdd119Saurel32       flags |= CC_NEGATIVE;
1593ebdd119Saurel32    }
1603ebdd119Saurel32 
1613ebdd119Saurel32    /* test for equal condition */
162211315fbSAurelien Jarno    if (floatx80_eq_quiet(Fn,Fm, &fpa11->fp_status))
1633ebdd119Saurel32    {
1643ebdd119Saurel32       flags |= CC_ZERO;
1653ebdd119Saurel32    }
1663ebdd119Saurel32 
1673ebdd119Saurel32    /* test for greater than or equal condition */
1683ebdd119Saurel32    if (floatx80_lt(Fm,Fn, &fpa11->fp_status))
1693ebdd119Saurel32    {
1703ebdd119Saurel32       flags |= CC_CARRY;
1713ebdd119Saurel32    }
1723ebdd119Saurel32 
1733ebdd119Saurel32    writeConditionCodes(flags);
1743ebdd119Saurel32    return 1;
1753ebdd119Saurel32 }
1763ebdd119Saurel32 
1773ebdd119Saurel32 /* This instruction sets the flags N, Z, C, V in the FPSR. */
1783ebdd119Saurel32 
1793ebdd119Saurel32 static unsigned int PerformComparison(const unsigned int opcode)
1803ebdd119Saurel32 {
1813ebdd119Saurel32    FPA11 *fpa11 = GET_FPA11();
1823ebdd119Saurel32    unsigned int Fn, Fm;
1833ebdd119Saurel32    floatx80 rFn, rFm;
1843ebdd119Saurel32    int e_flag = opcode & 0x400000;	/* 1 if CxFE */
1853ebdd119Saurel32    int n_flag = opcode & 0x200000;	/* 1 if CNxx */
1863ebdd119Saurel32    unsigned int flags = 0;
1873ebdd119Saurel32 
1883ebdd119Saurel32    //printk("PerformComparison(0x%08x)\n",opcode);
1893ebdd119Saurel32 
1903ebdd119Saurel32    Fn = getFn(opcode);
1913ebdd119Saurel32    Fm = getFm(opcode);
1923ebdd119Saurel32 
1933ebdd119Saurel32    /* Check for unordered condition and convert all operands to 80-bit
1943ebdd119Saurel32       format.
1953ebdd119Saurel32       ?? Might be some mileage in avoiding this conversion if possible.
1963ebdd119Saurel32       Eg, if both operands are 32-bit, detect this and do a 32-bit
1973ebdd119Saurel32       comparison (cheaper than an 80-bit one).  */
1983ebdd119Saurel32    switch (fpa11->fType[Fn])
1993ebdd119Saurel32    {
2003ebdd119Saurel32       case typeSingle:
2013ebdd119Saurel32         //printk("single.\n");
2023ebe80c2SPeter Maydell 	if (float32_is_any_nan(fpa11->fpreg[Fn].fSingle))
2033ebdd119Saurel32 	   goto unordered;
2043ebdd119Saurel32         rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
2053ebdd119Saurel32       break;
2063ebdd119Saurel32 
2073ebdd119Saurel32       case typeDouble:
2083ebdd119Saurel32         //printk("double.\n");
2093ebe80c2SPeter Maydell 	if (float64_is_any_nan(fpa11->fpreg[Fn].fDouble))
2103ebdd119Saurel32 	   goto unordered;
2113ebdd119Saurel32         rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
2123ebdd119Saurel32       break;
2133ebdd119Saurel32 
2143ebdd119Saurel32       case typeExtended:
2153ebdd119Saurel32         //printk("extended.\n");
2163ebe80c2SPeter Maydell 	if (floatx80_is_any_nan(fpa11->fpreg[Fn].fExtended))
2173ebdd119Saurel32 	   goto unordered;
2183ebdd119Saurel32         rFn = fpa11->fpreg[Fn].fExtended;
2193ebdd119Saurel32       break;
2203ebdd119Saurel32 
2213ebdd119Saurel32       default: return 0;
2223ebdd119Saurel32    }
2233ebdd119Saurel32 
2243ebdd119Saurel32    if (CONSTANT_FM(opcode))
2253ebdd119Saurel32    {
2263ebdd119Saurel32      //printk("Fm is a constant: #%d.\n",Fm);
2273ebdd119Saurel32      rFm = getExtendedConstant(Fm);
2283ebe80c2SPeter Maydell      if (floatx80_is_any_nan(rFm))
2293ebdd119Saurel32         goto unordered;
2303ebdd119Saurel32    }
2313ebdd119Saurel32    else
2323ebdd119Saurel32    {
2333ebdd119Saurel32      //printk("Fm = r%d which contains a ",Fm);
2343ebdd119Saurel32       switch (fpa11->fType[Fm])
2353ebdd119Saurel32       {
2363ebdd119Saurel32          case typeSingle:
2373ebdd119Saurel32            //printk("single.\n");
2383ebe80c2SPeter Maydell 	   if (float32_is_any_nan(fpa11->fpreg[Fm].fSingle))
2393ebdd119Saurel32 	      goto unordered;
2403ebdd119Saurel32            rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status);
2413ebdd119Saurel32          break;
2423ebdd119Saurel32 
2433ebdd119Saurel32          case typeDouble:
2443ebdd119Saurel32            //printk("double.\n");
2453ebe80c2SPeter Maydell 	   if (float64_is_any_nan(fpa11->fpreg[Fm].fDouble))
2463ebdd119Saurel32 	      goto unordered;
2473ebdd119Saurel32            rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status);
2483ebdd119Saurel32          break;
2493ebdd119Saurel32 
2503ebdd119Saurel32          case typeExtended:
2513ebdd119Saurel32            //printk("extended.\n");
2523ebe80c2SPeter Maydell 	   if (floatx80_is_any_nan(fpa11->fpreg[Fm].fExtended))
2533ebdd119Saurel32 	      goto unordered;
2543ebdd119Saurel32            rFm = fpa11->fpreg[Fm].fExtended;
2553ebdd119Saurel32          break;
2563ebdd119Saurel32 
2573ebdd119Saurel32          default: return 0;
2583ebdd119Saurel32       }
2593ebdd119Saurel32    }
2603ebdd119Saurel32 
2613ebdd119Saurel32    if (n_flag)
2623ebdd119Saurel32    {
2633ebdd119Saurel32       rFm.high ^= 0x8000;
2643ebdd119Saurel32    }
2653ebdd119Saurel32 
2663ebdd119Saurel32    return PerformComparisonOperation(rFn,rFm);
2673ebdd119Saurel32 
2683ebdd119Saurel32  unordered:
2693ebdd119Saurel32    /* ?? The FPA data sheet is pretty vague about this, in particular
2703ebdd119Saurel32       about whether the non-E comparisons can ever raise exceptions.
2713ebdd119Saurel32       This implementation is based on a combination of what it says in
2723ebdd119Saurel32       the data sheet, observation of how the Acorn emulator actually
2733ebdd119Saurel32       behaves (and how programs expect it to) and guesswork.  */
2743ebdd119Saurel32    flags |= CC_OVERFLOW;
2753ebdd119Saurel32    flags &= ~(CC_ZERO | CC_NEGATIVE);
2763ebdd119Saurel32 
2773ebdd119Saurel32    if (BIT_AC & readFPSR()) flags |= CC_CARRY;
2783ebdd119Saurel32 
2793ebdd119Saurel32    if (e_flag) float_raise(float_flag_invalid, &fpa11->fp_status);
2803ebdd119Saurel32 
2813ebdd119Saurel32    writeConditionCodes(flags);
2823ebdd119Saurel32    return 1;
2833ebdd119Saurel32 }
284