xref: /qemu/linux-user/arm/nwfpe/fpa11_cprt.c (revision 6b4c305cbd549e9d12a6b0192fdb8d6519a9664c)
100406dffSbellard /*
200406dffSbellard     NetWinder Floating Point Emulator
300406dffSbellard     (c) Rebel.COM, 1998,1999
400406dffSbellard     (c) Philip Blundell, 1999
500406dffSbellard 
600406dffSbellard     Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
700406dffSbellard 
800406dffSbellard     This program is free software; you can redistribute it and/or modify
900406dffSbellard     it under the terms of the GNU General Public License as published by
1000406dffSbellard     the Free Software Foundation; either version 2 of the License, or
1100406dffSbellard     (at your option) any later version.
1200406dffSbellard 
1300406dffSbellard     This program is distributed in the hope that it will be useful,
1400406dffSbellard     but WITHOUT ANY WARRANTY; without even the implied warranty of
1500406dffSbellard     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1600406dffSbellard     GNU General Public License for more details.
1700406dffSbellard 
1800406dffSbellard     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/>.
2000406dffSbellard */
2100406dffSbellard 
2200406dffSbellard #include "fpa11.h"
23*6b4c305cSPaolo Bonzini #include "fpu/softfloat.h"
2400406dffSbellard #include "fpopcode.h"
2500406dffSbellard #include "fpa11.inl"
2600406dffSbellard //#include "fpmodule.h"
2700406dffSbellard //#include "fpmodule.inl"
2800406dffSbellard 
2900406dffSbellard unsigned int PerformFLT(const unsigned int opcode);
3000406dffSbellard unsigned int PerformFIX(const unsigned int opcode);
3100406dffSbellard 
3200406dffSbellard static unsigned int
3300406dffSbellard PerformComparison(const unsigned int opcode);
3400406dffSbellard 
3500406dffSbellard unsigned int EmulateCPRT(const unsigned int opcode)
3600406dffSbellard {
3700406dffSbellard   unsigned int nRc = 1;
3800406dffSbellard 
3900406dffSbellard   //printk("EmulateCPRT(0x%08x)\n",opcode);
4000406dffSbellard 
4100406dffSbellard   if (opcode & 0x800000)
4200406dffSbellard   {
4300406dffSbellard      /* This is some variant of a comparison (PerformComparison will
4400406dffSbellard 	sort out which one).  Since most of the other CPRT
4500406dffSbellard 	instructions are oddball cases of some sort or other it makes
4600406dffSbellard 	sense to pull this out into a fast path.  */
4700406dffSbellard      return PerformComparison(opcode);
4800406dffSbellard   }
4900406dffSbellard 
5000406dffSbellard   /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
5100406dffSbellard   switch ((opcode & 0x700000) >> 20)
5200406dffSbellard   {
5300406dffSbellard     case  FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
5400406dffSbellard     case  FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
5500406dffSbellard 
5600406dffSbellard     case  WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
5700406dffSbellard     case  RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
5800406dffSbellard 
5900406dffSbellard #if 0    /* We currently have no use for the FPCR, so there's no point
6000406dffSbellard 	    in emulating it. */
6100406dffSbellard     case  WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode)));
6200406dffSbellard     case  RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break;
6300406dffSbellard #endif
6400406dffSbellard 
6500406dffSbellard     default: nRc = 0;
6600406dffSbellard   }
6700406dffSbellard 
6800406dffSbellard   return nRc;
6900406dffSbellard }
7000406dffSbellard 
7100406dffSbellard unsigned int PerformFLT(const unsigned int opcode)
7200406dffSbellard {
7300406dffSbellard    FPA11 *fpa11 = GET_FPA11();
7400406dffSbellard 
7500406dffSbellard    unsigned int nRc = 1;
7600406dffSbellard    SetRoundingMode(opcode);
7700406dffSbellard 
7800406dffSbellard    switch (opcode & MASK_ROUNDING_PRECISION)
7900406dffSbellard    {
8000406dffSbellard       case ROUND_SINGLE:
8100406dffSbellard       {
8200406dffSbellard         fpa11->fType[getFn(opcode)] = typeSingle;
8300406dffSbellard         fpa11->fpreg[getFn(opcode)].fSingle =
8420495218Sbellard 	   int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status);
8500406dffSbellard       }
8600406dffSbellard       break;
8700406dffSbellard 
8800406dffSbellard       case ROUND_DOUBLE:
8900406dffSbellard       {
9000406dffSbellard         fpa11->fType[getFn(opcode)] = typeDouble;
9100406dffSbellard         fpa11->fpreg[getFn(opcode)].fDouble =
9220495218Sbellard             int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status);
9300406dffSbellard       }
9400406dffSbellard       break;
9500406dffSbellard 
9600406dffSbellard       case ROUND_EXTENDED:
9700406dffSbellard       {
9800406dffSbellard         fpa11->fType[getFn(opcode)] = typeExtended;
9900406dffSbellard         fpa11->fpreg[getFn(opcode)].fExtended =
10020495218Sbellard 	   int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status);
10100406dffSbellard       }
10200406dffSbellard       break;
10300406dffSbellard 
10400406dffSbellard       default: nRc = 0;
10500406dffSbellard   }
10600406dffSbellard 
10700406dffSbellard   return nRc;
10800406dffSbellard }
10900406dffSbellard 
11000406dffSbellard unsigned int PerformFIX(const unsigned int opcode)
11100406dffSbellard {
11200406dffSbellard    FPA11 *fpa11 = GET_FPA11();
11300406dffSbellard    unsigned int nRc = 1;
11400406dffSbellard    unsigned int Fn = getFm(opcode);
11500406dffSbellard 
11600406dffSbellard    SetRoundingMode(opcode);
11700406dffSbellard 
11800406dffSbellard    switch (fpa11->fType[Fn])
11900406dffSbellard    {
12000406dffSbellard       case typeSingle:
12100406dffSbellard       {
12200406dffSbellard          writeRegister(getRd(opcode),
12320495218Sbellard 	               float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status));
12400406dffSbellard       }
12500406dffSbellard       break;
12600406dffSbellard 
12700406dffSbellard       case typeDouble:
12800406dffSbellard       {
12926a76461Sbellard          //printf("F%d is 0x%" PRIx64 "\n",Fn,fpa11->fpreg[Fn].fDouble);
13000406dffSbellard          writeRegister(getRd(opcode),
13120495218Sbellard 	               float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status));
13200406dffSbellard       }
13300406dffSbellard       break;
13400406dffSbellard 
13500406dffSbellard       case typeExtended:
13600406dffSbellard       {
13700406dffSbellard          writeRegister(getRd(opcode),
13820495218Sbellard 	               floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status));
13900406dffSbellard       }
14000406dffSbellard       break;
14100406dffSbellard 
14200406dffSbellard       default: nRc = 0;
14300406dffSbellard   }
14400406dffSbellard 
14500406dffSbellard   return nRc;
14600406dffSbellard }
14700406dffSbellard 
14800406dffSbellard 
14986178a57SJuan Quintela static __inline unsigned int
15000406dffSbellard PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
15100406dffSbellard {
15220495218Sbellard    FPA11 *fpa11 = GET_FPA11();
15300406dffSbellard    unsigned int flags = 0;
15400406dffSbellard 
15500406dffSbellard    /* test for less than condition */
15620495218Sbellard    if (floatx80_lt(Fn,Fm, &fpa11->fp_status))
15700406dffSbellard    {
15800406dffSbellard       flags |= CC_NEGATIVE;
15900406dffSbellard    }
16000406dffSbellard 
16100406dffSbellard    /* test for equal condition */
162211315fbSAurelien Jarno    if (floatx80_eq_quiet(Fn,Fm, &fpa11->fp_status))
16300406dffSbellard    {
16400406dffSbellard       flags |= CC_ZERO;
16500406dffSbellard    }
16600406dffSbellard 
16700406dffSbellard    /* test for greater than or equal condition */
16820495218Sbellard    if (floatx80_lt(Fm,Fn, &fpa11->fp_status))
16900406dffSbellard    {
17000406dffSbellard       flags |= CC_CARRY;
17100406dffSbellard    }
17200406dffSbellard 
17300406dffSbellard    writeConditionCodes(flags);
17400406dffSbellard    return 1;
17500406dffSbellard }
17600406dffSbellard 
17700406dffSbellard /* This instruction sets the flags N, Z, C, V in the FPSR. */
17800406dffSbellard 
17900406dffSbellard static unsigned int PerformComparison(const unsigned int opcode)
18000406dffSbellard {
18100406dffSbellard    FPA11 *fpa11 = GET_FPA11();
18200406dffSbellard    unsigned int Fn, Fm;
18300406dffSbellard    floatx80 rFn, rFm;
18400406dffSbellard    int e_flag = opcode & 0x400000;	/* 1 if CxFE */
18500406dffSbellard    int n_flag = opcode & 0x200000;	/* 1 if CNxx */
18600406dffSbellard    unsigned int flags = 0;
18700406dffSbellard 
18800406dffSbellard    //printk("PerformComparison(0x%08x)\n",opcode);
18900406dffSbellard 
19000406dffSbellard    Fn = getFn(opcode);
19100406dffSbellard    Fm = getFm(opcode);
19200406dffSbellard 
19300406dffSbellard    /* Check for unordered condition and convert all operands to 80-bit
19400406dffSbellard       format.
19500406dffSbellard       ?? Might be some mileage in avoiding this conversion if possible.
19600406dffSbellard       Eg, if both operands are 32-bit, detect this and do a 32-bit
19700406dffSbellard       comparison (cheaper than an 80-bit one).  */
19800406dffSbellard    switch (fpa11->fType[Fn])
19900406dffSbellard    {
20000406dffSbellard       case typeSingle:
20100406dffSbellard         //printk("single.\n");
2023ebe80c2SPeter Maydell 	if (float32_is_any_nan(fpa11->fpreg[Fn].fSingle))
20300406dffSbellard 	   goto unordered;
20420495218Sbellard         rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
20500406dffSbellard       break;
20600406dffSbellard 
20700406dffSbellard       case typeDouble:
20800406dffSbellard         //printk("double.\n");
2093ebe80c2SPeter Maydell 	if (float64_is_any_nan(fpa11->fpreg[Fn].fDouble))
21000406dffSbellard 	   goto unordered;
21120495218Sbellard         rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
21200406dffSbellard       break;
21300406dffSbellard 
21400406dffSbellard       case typeExtended:
21500406dffSbellard         //printk("extended.\n");
2163ebe80c2SPeter Maydell 	if (floatx80_is_any_nan(fpa11->fpreg[Fn].fExtended))
21700406dffSbellard 	   goto unordered;
21800406dffSbellard         rFn = fpa11->fpreg[Fn].fExtended;
21900406dffSbellard       break;
22000406dffSbellard 
22100406dffSbellard       default: return 0;
22200406dffSbellard    }
22300406dffSbellard 
22400406dffSbellard    if (CONSTANT_FM(opcode))
22500406dffSbellard    {
22600406dffSbellard      //printk("Fm is a constant: #%d.\n",Fm);
22700406dffSbellard      rFm = getExtendedConstant(Fm);
2283ebe80c2SPeter Maydell      if (floatx80_is_any_nan(rFm))
22900406dffSbellard         goto unordered;
23000406dffSbellard    }
23100406dffSbellard    else
23200406dffSbellard    {
23300406dffSbellard      //printk("Fm = r%d which contains a ",Fm);
23400406dffSbellard       switch (fpa11->fType[Fm])
23500406dffSbellard       {
23600406dffSbellard          case typeSingle:
23700406dffSbellard            //printk("single.\n");
2383ebe80c2SPeter Maydell 	   if (float32_is_any_nan(fpa11->fpreg[Fm].fSingle))
23900406dffSbellard 	      goto unordered;
24020495218Sbellard            rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status);
24100406dffSbellard          break;
24200406dffSbellard 
24300406dffSbellard          case typeDouble:
24400406dffSbellard            //printk("double.\n");
2453ebe80c2SPeter Maydell 	   if (float64_is_any_nan(fpa11->fpreg[Fm].fDouble))
24600406dffSbellard 	      goto unordered;
24720495218Sbellard            rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status);
24800406dffSbellard          break;
24900406dffSbellard 
25000406dffSbellard          case typeExtended:
25100406dffSbellard            //printk("extended.\n");
2523ebe80c2SPeter Maydell 	   if (floatx80_is_any_nan(fpa11->fpreg[Fm].fExtended))
25300406dffSbellard 	      goto unordered;
25400406dffSbellard            rFm = fpa11->fpreg[Fm].fExtended;
25500406dffSbellard          break;
25600406dffSbellard 
25700406dffSbellard          default: return 0;
25800406dffSbellard       }
25900406dffSbellard    }
26000406dffSbellard 
26100406dffSbellard    if (n_flag)
26200406dffSbellard    {
26300406dffSbellard       rFm.high ^= 0x8000;
26400406dffSbellard    }
26500406dffSbellard 
26600406dffSbellard    return PerformComparisonOperation(rFn,rFm);
26700406dffSbellard 
26800406dffSbellard  unordered:
26900406dffSbellard    /* ?? The FPA data sheet is pretty vague about this, in particular
27000406dffSbellard       about whether the non-E comparisons can ever raise exceptions.
27100406dffSbellard       This implementation is based on a combination of what it says in
27200406dffSbellard       the data sheet, observation of how the Acorn emulator actually
27300406dffSbellard       behaves (and how programs expect it to) and guesswork.  */
27400406dffSbellard    flags |= CC_OVERFLOW;
27500406dffSbellard    flags &= ~(CC_ZERO | CC_NEGATIVE);
27600406dffSbellard 
27700406dffSbellard    if (BIT_AC & readFPSR()) flags |= CC_CARRY;
27800406dffSbellard 
27920495218Sbellard    if (e_flag) float_raise(float_flag_invalid, &fpa11->fp_status);
28000406dffSbellard 
28100406dffSbellard    writeConditionCodes(flags);
28200406dffSbellard    return 1;
28300406dffSbellard }
284