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