xref: /qemu/linux-user/arm/nwfpe/fpa11_cprt.c (revision d39594e9d96b4f0be27bdbc02a5b1816b72fe1d2)
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 
22*d39594e9SPeter Maydell #include "qemu/osdep.h"
2300406dffSbellard #include "fpa11.h"
246b4c305cSPaolo Bonzini #include "fpu/softfloat.h"
2500406dffSbellard #include "fpopcode.h"
2600406dffSbellard #include "fpa11.inl"
2700406dffSbellard //#include "fpmodule.h"
2800406dffSbellard //#include "fpmodule.inl"
2900406dffSbellard 
3000406dffSbellard unsigned int PerformFLT(const unsigned int opcode);
3100406dffSbellard unsigned int PerformFIX(const unsigned int opcode);
3200406dffSbellard 
3300406dffSbellard static unsigned int
3400406dffSbellard PerformComparison(const unsigned int opcode);
3500406dffSbellard 
EmulateCPRT(const unsigned int opcode)3600406dffSbellard unsigned int EmulateCPRT(const unsigned int opcode)
3700406dffSbellard {
3800406dffSbellard   unsigned int nRc = 1;
3900406dffSbellard 
4000406dffSbellard   //printk("EmulateCPRT(0x%08x)\n",opcode);
4100406dffSbellard 
4200406dffSbellard   if (opcode & 0x800000)
4300406dffSbellard   {
4400406dffSbellard      /* This is some variant of a comparison (PerformComparison will
4500406dffSbellard 	sort out which one).  Since most of the other CPRT
4600406dffSbellard 	instructions are oddball cases of some sort or other it makes
4700406dffSbellard 	sense to pull this out into a fast path.  */
4800406dffSbellard      return PerformComparison(opcode);
4900406dffSbellard   }
5000406dffSbellard 
5100406dffSbellard   /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
5200406dffSbellard   switch ((opcode & 0x700000) >> 20)
5300406dffSbellard   {
5400406dffSbellard     case  FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
5500406dffSbellard     case  FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
5600406dffSbellard 
5700406dffSbellard     case  WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
5800406dffSbellard     case  RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
5900406dffSbellard 
6000406dffSbellard #if 0    /* We currently have no use for the FPCR, so there's no point
6100406dffSbellard 	    in emulating it. */
6200406dffSbellard     case  WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode)));
6300406dffSbellard     case  RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break;
6400406dffSbellard #endif
6500406dffSbellard 
6600406dffSbellard     default: nRc = 0;
6700406dffSbellard   }
6800406dffSbellard 
6900406dffSbellard   return nRc;
7000406dffSbellard }
7100406dffSbellard 
PerformFLT(const unsigned int opcode)7200406dffSbellard unsigned int PerformFLT(const unsigned int opcode)
7300406dffSbellard {
7400406dffSbellard    FPA11 *fpa11 = GET_FPA11();
7500406dffSbellard 
7600406dffSbellard    unsigned int nRc = 1;
7700406dffSbellard    SetRoundingMode(opcode);
7800406dffSbellard 
7900406dffSbellard    switch (opcode & MASK_ROUNDING_PRECISION)
8000406dffSbellard    {
8100406dffSbellard       case ROUND_SINGLE:
8200406dffSbellard       {
8300406dffSbellard         fpa11->fType[getFn(opcode)] = typeSingle;
8400406dffSbellard         fpa11->fpreg[getFn(opcode)].fSingle =
8520495218Sbellard 	   int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status);
8600406dffSbellard       }
8700406dffSbellard       break;
8800406dffSbellard 
8900406dffSbellard       case ROUND_DOUBLE:
9000406dffSbellard       {
9100406dffSbellard         fpa11->fType[getFn(opcode)] = typeDouble;
9200406dffSbellard         fpa11->fpreg[getFn(opcode)].fDouble =
9320495218Sbellard             int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status);
9400406dffSbellard       }
9500406dffSbellard       break;
9600406dffSbellard 
9700406dffSbellard       case ROUND_EXTENDED:
9800406dffSbellard       {
9900406dffSbellard         fpa11->fType[getFn(opcode)] = typeExtended;
10000406dffSbellard         fpa11->fpreg[getFn(opcode)].fExtended =
10120495218Sbellard 	   int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status);
10200406dffSbellard       }
10300406dffSbellard       break;
10400406dffSbellard 
10500406dffSbellard       default: nRc = 0;
10600406dffSbellard   }
10700406dffSbellard 
10800406dffSbellard   return nRc;
10900406dffSbellard }
11000406dffSbellard 
PerformFIX(const unsigned int opcode)11100406dffSbellard unsigned int PerformFIX(const unsigned int opcode)
11200406dffSbellard {
11300406dffSbellard    FPA11 *fpa11 = GET_FPA11();
11400406dffSbellard    unsigned int nRc = 1;
11500406dffSbellard    unsigned int Fn = getFm(opcode);
11600406dffSbellard 
11700406dffSbellard    SetRoundingMode(opcode);
11800406dffSbellard 
11900406dffSbellard    switch (fpa11->fType[Fn])
12000406dffSbellard    {
12100406dffSbellard       case typeSingle:
12200406dffSbellard       {
12300406dffSbellard          writeRegister(getRd(opcode),
12420495218Sbellard 	               float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status));
12500406dffSbellard       }
12600406dffSbellard       break;
12700406dffSbellard 
12800406dffSbellard       case typeDouble:
12900406dffSbellard       {
13026a76461Sbellard          //printf("F%d is 0x%" PRIx64 "\n",Fn,fpa11->fpreg[Fn].fDouble);
13100406dffSbellard          writeRegister(getRd(opcode),
13220495218Sbellard 	               float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status));
13300406dffSbellard       }
13400406dffSbellard       break;
13500406dffSbellard 
13600406dffSbellard       case typeExtended:
13700406dffSbellard       {
13800406dffSbellard          writeRegister(getRd(opcode),
13920495218Sbellard 	               floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status));
14000406dffSbellard       }
14100406dffSbellard       break;
14200406dffSbellard 
14300406dffSbellard       default: nRc = 0;
14400406dffSbellard   }
14500406dffSbellard 
14600406dffSbellard   return nRc;
14700406dffSbellard }
14800406dffSbellard 
14900406dffSbellard 
15086178a57SJuan Quintela static __inline unsigned int
PerformComparisonOperation(floatx80 Fn,floatx80 Fm)15100406dffSbellard PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
15200406dffSbellard {
15320495218Sbellard    FPA11 *fpa11 = GET_FPA11();
15400406dffSbellard    unsigned int flags = 0;
15500406dffSbellard 
15600406dffSbellard    /* test for less than condition */
15720495218Sbellard    if (floatx80_lt(Fn,Fm, &fpa11->fp_status))
15800406dffSbellard    {
15900406dffSbellard       flags |= CC_NEGATIVE;
16000406dffSbellard    }
16100406dffSbellard 
16200406dffSbellard    /* test for equal condition */
163211315fbSAurelien Jarno    if (floatx80_eq_quiet(Fn,Fm, &fpa11->fp_status))
16400406dffSbellard    {
16500406dffSbellard       flags |= CC_ZERO;
16600406dffSbellard    }
16700406dffSbellard 
16800406dffSbellard    /* test for greater than or equal condition */
16920495218Sbellard    if (floatx80_lt(Fm,Fn, &fpa11->fp_status))
17000406dffSbellard    {
17100406dffSbellard       flags |= CC_CARRY;
17200406dffSbellard    }
17300406dffSbellard 
17400406dffSbellard    writeConditionCodes(flags);
17500406dffSbellard    return 1;
17600406dffSbellard }
17700406dffSbellard 
17800406dffSbellard /* This instruction sets the flags N, Z, C, V in the FPSR. */
17900406dffSbellard 
PerformComparison(const unsigned int opcode)18000406dffSbellard static unsigned int PerformComparison(const unsigned int opcode)
18100406dffSbellard {
18200406dffSbellard    FPA11 *fpa11 = GET_FPA11();
18300406dffSbellard    unsigned int Fn, Fm;
18400406dffSbellard    floatx80 rFn, rFm;
18500406dffSbellard    int e_flag = opcode & 0x400000;	/* 1 if CxFE */
18600406dffSbellard    int n_flag = opcode & 0x200000;	/* 1 if CNxx */
18700406dffSbellard    unsigned int flags = 0;
18800406dffSbellard 
18900406dffSbellard    //printk("PerformComparison(0x%08x)\n",opcode);
19000406dffSbellard 
19100406dffSbellard    Fn = getFn(opcode);
19200406dffSbellard    Fm = getFm(opcode);
19300406dffSbellard 
19400406dffSbellard    /* Check for unordered condition and convert all operands to 80-bit
19500406dffSbellard       format.
19600406dffSbellard       ?? Might be some mileage in avoiding this conversion if possible.
19700406dffSbellard       Eg, if both operands are 32-bit, detect this and do a 32-bit
19800406dffSbellard       comparison (cheaper than an 80-bit one).  */
19900406dffSbellard    switch (fpa11->fType[Fn])
20000406dffSbellard    {
20100406dffSbellard       case typeSingle:
20200406dffSbellard         //printk("single.\n");
2033ebe80c2SPeter Maydell 	if (float32_is_any_nan(fpa11->fpreg[Fn].fSingle))
20400406dffSbellard 	   goto unordered;
20520495218Sbellard         rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
20600406dffSbellard       break;
20700406dffSbellard 
20800406dffSbellard       case typeDouble:
20900406dffSbellard         //printk("double.\n");
2103ebe80c2SPeter Maydell 	if (float64_is_any_nan(fpa11->fpreg[Fn].fDouble))
21100406dffSbellard 	   goto unordered;
21220495218Sbellard         rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
21300406dffSbellard       break;
21400406dffSbellard 
21500406dffSbellard       case typeExtended:
21600406dffSbellard         //printk("extended.\n");
2173ebe80c2SPeter Maydell 	if (floatx80_is_any_nan(fpa11->fpreg[Fn].fExtended))
21800406dffSbellard 	   goto unordered;
21900406dffSbellard         rFn = fpa11->fpreg[Fn].fExtended;
22000406dffSbellard       break;
22100406dffSbellard 
22200406dffSbellard       default: return 0;
22300406dffSbellard    }
22400406dffSbellard 
22500406dffSbellard    if (CONSTANT_FM(opcode))
22600406dffSbellard    {
22700406dffSbellard      //printk("Fm is a constant: #%d.\n",Fm);
22800406dffSbellard      rFm = getExtendedConstant(Fm);
2293ebe80c2SPeter Maydell      if (floatx80_is_any_nan(rFm))
23000406dffSbellard         goto unordered;
23100406dffSbellard    }
23200406dffSbellard    else
23300406dffSbellard    {
23400406dffSbellard      //printk("Fm = r%d which contains a ",Fm);
23500406dffSbellard       switch (fpa11->fType[Fm])
23600406dffSbellard       {
23700406dffSbellard          case typeSingle:
23800406dffSbellard            //printk("single.\n");
2393ebe80c2SPeter Maydell 	   if (float32_is_any_nan(fpa11->fpreg[Fm].fSingle))
24000406dffSbellard 	      goto unordered;
24120495218Sbellard            rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status);
24200406dffSbellard          break;
24300406dffSbellard 
24400406dffSbellard          case typeDouble:
24500406dffSbellard            //printk("double.\n");
2463ebe80c2SPeter Maydell 	   if (float64_is_any_nan(fpa11->fpreg[Fm].fDouble))
24700406dffSbellard 	      goto unordered;
24820495218Sbellard            rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status);
24900406dffSbellard          break;
25000406dffSbellard 
25100406dffSbellard          case typeExtended:
25200406dffSbellard            //printk("extended.\n");
2533ebe80c2SPeter Maydell 	   if (floatx80_is_any_nan(fpa11->fpreg[Fm].fExtended))
25400406dffSbellard 	      goto unordered;
25500406dffSbellard            rFm = fpa11->fpreg[Fm].fExtended;
25600406dffSbellard          break;
25700406dffSbellard 
25800406dffSbellard          default: return 0;
25900406dffSbellard       }
26000406dffSbellard    }
26100406dffSbellard 
26200406dffSbellard    if (n_flag)
26300406dffSbellard    {
26400406dffSbellard       rFm.high ^= 0x8000;
26500406dffSbellard    }
26600406dffSbellard 
26700406dffSbellard    return PerformComparisonOperation(rFn,rFm);
26800406dffSbellard 
26900406dffSbellard  unordered:
27000406dffSbellard    /* ?? The FPA data sheet is pretty vague about this, in particular
27100406dffSbellard       about whether the non-E comparisons can ever raise exceptions.
27200406dffSbellard       This implementation is based on a combination of what it says in
27300406dffSbellard       the data sheet, observation of how the Acorn emulator actually
27400406dffSbellard       behaves (and how programs expect it to) and guesswork.  */
27500406dffSbellard    flags |= CC_OVERFLOW;
27600406dffSbellard    flags &= ~(CC_ZERO | CC_NEGATIVE);
27700406dffSbellard 
27800406dffSbellard    if (BIT_AC & readFPSR()) flags |= CC_CARRY;
27900406dffSbellard 
28020495218Sbellard    if (e_flag) float_raise(float_flag_invalid, &fpa11->fp_status);
28100406dffSbellard 
28200406dffSbellard    writeConditionCodes(flags);
28300406dffSbellard    return 1;
28400406dffSbellard }
285