fpa11_cprt.c 7.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
/*
    NetWinder Floating Point Emulator
    (c) Rebel.COM, 1998,1999
    (c) Philip Blundell, 1999

    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 02139, USA.
*/

#include "fpa11.h"
#include "softfloat.h"
#include "fpopcode.h"
#include "fpa11.inl"
//#include "fpmodule.h"
//#include "fpmodule.inl"

unsigned int PerformFLT(const unsigned int opcode);
unsigned int PerformFIX(const unsigned int opcode);

static unsigned int
PerformComparison(const unsigned int opcode);

unsigned int EmulateCPRT(const unsigned int opcode)
{
  unsigned int nRc = 1;

  //printk("EmulateCPRT(0x%08x)\n",opcode);

  if (opcode & 0x800000)
  {
     /* This is some variant of a comparison (PerformComparison will
	sort out which one).  Since most of the other CPRT
	instructions are oddball cases of some sort or other it makes
	sense to pull this out into a fast path.  */
     return PerformComparison(opcode);
  }

  /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
  switch ((opcode & 0x700000) >> 20)
  {
    case  FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
    case  FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
56

57 58 59 60 61 62 63 64 65 66 67
    case  WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
    case  RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;

#if 0    /* We currently have no use for the FPCR, so there's no point
	    in emulating it. */
    case  WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode)));
    case  RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break;
#endif

    default: nRc = 0;
  }
68

69 70 71 72 73 74
  return nRc;
}

unsigned int PerformFLT(const unsigned int opcode)
{
   FPA11 *fpa11 = GET_FPA11();
75

76 77 78 79 80 81 82 83 84
   unsigned int nRc = 1;
   SetRoundingMode(opcode);

   switch (opcode & MASK_ROUNDING_PRECISION)
   {
      case ROUND_SINGLE:
      {
        fpa11->fType[getFn(opcode)] = typeSingle;
        fpa11->fpreg[getFn(opcode)].fSingle =
B
bellard 已提交
85
	   int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status);
86 87 88 89 90 91 92
      }
      break;

      case ROUND_DOUBLE:
      {
        fpa11->fType[getFn(opcode)] = typeDouble;
        fpa11->fpreg[getFn(opcode)].fDouble =
B
bellard 已提交
93
            int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status);
94 95
      }
      break;
96

97 98 99 100
      case ROUND_EXTENDED:
      {
        fpa11->fType[getFn(opcode)] = typeExtended;
        fpa11->fpreg[getFn(opcode)].fExtended =
B
bellard 已提交
101
	   int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status);
102 103
      }
      break;
104

105 106
      default: nRc = 0;
  }
107

108 109 110 111 112 113 114 115
  return nRc;
}

unsigned int PerformFIX(const unsigned int opcode)
{
   FPA11 *fpa11 = GET_FPA11();
   unsigned int nRc = 1;
   unsigned int Fn = getFm(opcode);
116

117 118 119 120 121 122 123
   SetRoundingMode(opcode);

   switch (fpa11->fType[Fn])
   {
      case typeSingle:
      {
         writeRegister(getRd(opcode),
B
bellard 已提交
124
	               float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status));
125 126 127 128 129
      }
      break;

      case typeDouble:
      {
B
bellard 已提交
130
         //printf("F%d is 0x%" PRIx64 "\n",Fn,fpa11->fpreg[Fn].fDouble);
131
         writeRegister(getRd(opcode),
B
bellard 已提交
132
	               float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status));
133 134
      }
      break;
135

136 137 138
      case typeExtended:
      {
         writeRegister(getRd(opcode),
B
bellard 已提交
139
	               floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status));
140 141
      }
      break;
142

143 144
      default: nRc = 0;
  }
145

146 147 148
  return nRc;
}

149

150
static __inline unsigned int
151 152
PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
{
B
bellard 已提交
153
   FPA11 *fpa11 = GET_FPA11();
154 155 156
   unsigned int flags = 0;

   /* test for less than condition */
B
bellard 已提交
157
   if (floatx80_lt(Fn,Fm, &fpa11->fp_status))
158 159 160
   {
      flags |= CC_NEGATIVE;
   }
161

162
   /* test for equal condition */
B
bellard 已提交
163
   if (floatx80_eq(Fn,Fm, &fpa11->fp_status))
164 165 166 167 168
   {
      flags |= CC_ZERO;
   }

   /* test for greater than or equal condition */
B
bellard 已提交
169
   if (floatx80_lt(Fm,Fn, &fpa11->fp_status))
170 171 172
   {
      flags |= CC_CARRY;
   }
173

174 175 176 177 178
   writeConditionCodes(flags);
   return 1;
}

/* This instruction sets the flags N, Z, C, V in the FPSR. */
179

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
static unsigned int PerformComparison(const unsigned int opcode)
{
   FPA11 *fpa11 = GET_FPA11();
   unsigned int Fn, Fm;
   floatx80 rFn, rFm;
   int e_flag = opcode & 0x400000;	/* 1 if CxFE */
   int n_flag = opcode & 0x200000;	/* 1 if CNxx */
   unsigned int flags = 0;

   //printk("PerformComparison(0x%08x)\n",opcode);

   Fn = getFn(opcode);
   Fm = getFm(opcode);

   /* Check for unordered condition and convert all operands to 80-bit
      format.
      ?? Might be some mileage in avoiding this conversion if possible.
      Eg, if both operands are 32-bit, detect this and do a 32-bit
      comparison (cheaper than an 80-bit one).  */
   switch (fpa11->fType[Fn])
   {
201
      case typeSingle:
202 203 204
        //printk("single.\n");
	if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
	   goto unordered;
B
bellard 已提交
205
        rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
206 207
      break;

208
      case typeDouble:
209 210 211
        //printk("double.\n");
	if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
	   goto unordered;
B
bellard 已提交
212
        rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
213
      break;
214

215
      case typeExtended:
216 217 218 219 220
        //printk("extended.\n");
	if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
	   goto unordered;
        rFn = fpa11->fpreg[Fn].fExtended;
      break;
221

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
      default: return 0;
   }

   if (CONSTANT_FM(opcode))
   {
     //printk("Fm is a constant: #%d.\n",Fm);
     rFm = getExtendedConstant(Fm);
     if (floatx80_is_nan(rFm))
        goto unordered;
   }
   else
   {
     //printk("Fm = r%d which contains a ",Fm);
      switch (fpa11->fType[Fm])
      {
237
         case typeSingle:
238 239 240
           //printk("single.\n");
	   if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
	      goto unordered;
B
bellard 已提交
241
           rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status);
242 243
         break;

244
         case typeDouble:
245 246 247
           //printk("double.\n");
	   if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
	      goto unordered;
B
bellard 已提交
248
           rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status);
249
         break;
250

251
         case typeExtended:
252 253 254 255 256
           //printk("extended.\n");
	   if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
	      goto unordered;
           rFm = fpa11->fpreg[Fm].fExtended;
         break;
257

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
         default: return 0;
      }
   }

   if (n_flag)
   {
      rFm.high ^= 0x8000;
   }

   return PerformComparisonOperation(rFn,rFm);

 unordered:
   /* ?? The FPA data sheet is pretty vague about this, in particular
      about whether the non-E comparisons can ever raise exceptions.
      This implementation is based on a combination of what it says in
      the data sheet, observation of how the Acorn emulator actually
      behaves (and how programs expect it to) and guesswork.  */
   flags |= CC_OVERFLOW;
   flags &= ~(CC_ZERO | CC_NEGATIVE);

   if (BIT_AC & readFPSR()) flags |= CC_CARRY;

B
bellard 已提交
280
   if (e_flag) float_raise(float_flag_invalid, &fpa11->fp_status);
281 282 283 284

   writeConditionCodes(flags);
   return 1;
}