linux/arch/arm/nwfpe/fpa11_cprt.c
<<
>>
Prefs
   1/*
   2    NetWinder Floating Point Emulator
   3    (c) Rebel.COM, 1998,1999
   4    (c) Philip Blundell, 1999, 2001
   5
   6    Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
   7
   8    This program is free software; you can redistribute it and/or modify
   9    it under the terms of the GNU General Public License as published by
  10    the Free Software Foundation; either version 2 of the License, or
  11    (at your option) any later version.
  12
  13    This program is distributed in the hope that it will be useful,
  14    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16    GNU General Public License for more details.
  17
  18    You should have received a copy of the GNU General Public License
  19    along with this program; if not, write to the Free Software
  20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21*/
  22
  23#include "fpa11.h"
  24#include "fpopcode.h"
  25#include "fpa11.inl"
  26#include "fpmodule.h"
  27#include "fpmodule.inl"
  28#include "softfloat.h"
  29
  30unsigned int PerformFLT(const unsigned int opcode);
  31unsigned int PerformFIX(const unsigned int opcode);
  32
  33static unsigned int PerformComparison(const unsigned int opcode);
  34
  35unsigned int EmulateCPRT(const unsigned int opcode)
  36{
  37
  38        if (opcode & 0x800000) {
  39                /* This is some variant of a comparison (PerformComparison
  40                   will sort out which one).  Since most of the other CPRT
  41                   instructions are oddball cases of some sort or other it
  42                   makes sense to pull this out into a fast path.  */
  43                return PerformComparison(opcode);
  44        }
  45
  46        /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
  47        switch ((opcode & 0x700000) >> 20) {
  48        case FLT_CODE >> 20:
  49                return PerformFLT(opcode);
  50                break;
  51        case FIX_CODE >> 20:
  52                return PerformFIX(opcode);
  53                break;
  54
  55        case WFS_CODE >> 20:
  56                writeFPSR(readRegister(getRd(opcode)));
  57                break;
  58        case RFS_CODE >> 20:
  59                writeRegister(getRd(opcode), readFPSR());
  60                break;
  61
  62        default:
  63                return 0;
  64        }
  65
  66        return 1;
  67}
  68
  69unsigned int PerformFLT(const unsigned int opcode)
  70{
  71        FPA11 *fpa11 = GET_FPA11();
  72        struct roundingData roundData;
  73
  74        roundData.mode = SetRoundingMode(opcode);
  75        roundData.precision = SetRoundingPrecision(opcode);
  76        roundData.exception = 0;
  77
  78        switch (opcode & MASK_ROUNDING_PRECISION) {
  79        case ROUND_SINGLE:
  80                {
  81                        fpa11->fType[getFn(opcode)] = typeSingle;
  82                        fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
  83                }
  84                break;
  85
  86        case ROUND_DOUBLE:
  87                {
  88                        fpa11->fType[getFn(opcode)] = typeDouble;
  89                        fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
  90                }
  91                break;
  92
  93#ifdef CONFIG_FPE_NWFPE_XP
  94        case ROUND_EXTENDED:
  95                {
  96                        fpa11->fType[getFn(opcode)] = typeExtended;
  97                        fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
  98                }
  99                break;
 100#endif
 101
 102        default:
 103                return 0;
 104        }
 105
 106        if (roundData.exception)
 107                float_raise(roundData.exception);
 108
 109        return 1;
 110}
 111
 112unsigned int PerformFIX(const unsigned int opcode)
 113{
 114        FPA11 *fpa11 = GET_FPA11();
 115        unsigned int Fn = getFm(opcode);
 116        struct roundingData roundData;
 117
 118        roundData.mode = SetRoundingMode(opcode);
 119        roundData.precision = SetRoundingPrecision(opcode);
 120        roundData.exception = 0;
 121
 122        switch (fpa11->fType[Fn]) {
 123        case typeSingle:
 124                {
 125                        writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
 126                }
 127                break;
 128
 129        case typeDouble:
 130                {
 131                        writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
 132                }
 133                break;
 134
 135#ifdef CONFIG_FPE_NWFPE_XP
 136        case typeExtended:
 137                {
 138                        writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
 139                }
 140                break;
 141#endif
 142
 143        default:
 144                return 0;
 145        }
 146
 147        if (roundData.exception)
 148                float_raise(roundData.exception);
 149
 150        return 1;
 151}
 152
 153/* This instruction sets the flags N, Z, C, V in the FPSR. */
 154static unsigned int PerformComparison(const unsigned int opcode)
 155{
 156        FPA11 *fpa11 = GET_FPA11();
 157        unsigned int Fn = getFn(opcode), Fm = getFm(opcode);
 158        int e_flag = opcode & 0x400000; /* 1 if CxFE */
 159        int n_flag = opcode & 0x200000; /* 1 if CNxx */
 160        unsigned int flags = 0;
 161
 162#ifdef CONFIG_FPE_NWFPE_XP
 163        floatx80 rFn, rFm;
 164
 165        /* Check for unordered condition and convert all operands to 80-bit
 166           format.
 167           ?? Might be some mileage in avoiding this conversion if possible.
 168           Eg, if both operands are 32-bit, detect this and do a 32-bit
 169           comparison (cheaper than an 80-bit one).  */
 170        switch (fpa11->fType[Fn]) {
 171        case typeSingle:
 172                //printk("single.\n");
 173                if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
 174                        goto unordered;
 175                rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
 176                break;
 177
 178        case typeDouble:
 179                //printk("double.\n");
 180                if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
 181                        goto unordered;
 182                rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
 183                break;
 184
 185        case typeExtended:
 186                //printk("extended.\n");
 187                if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
 188                        goto unordered;
 189                rFn = fpa11->fpreg[Fn].fExtended;
 190                break;
 191
 192        default:
 193                return 0;
 194        }
 195
 196        if (CONSTANT_FM(opcode)) {
 197                //printk("Fm is a constant: #%d.\n",Fm);
 198                rFm = getExtendedConstant(Fm);
 199                if (floatx80_is_nan(rFm))
 200                        goto unordered;
 201        } else {
 202                //printk("Fm = r%d which contains a ",Fm);
 203                switch (fpa11->fType[Fm]) {
 204                case typeSingle:
 205                        //printk("single.\n");
 206                        if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
 207                                goto unordered;
 208                        rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
 209                        break;
 210
 211                case typeDouble:
 212                        //printk("double.\n");
 213                        if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
 214                                goto unordered;
 215                        rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
 216                        break;
 217
 218                case typeExtended:
 219                        //printk("extended.\n");
 220                        if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
 221                                goto unordered;
 222                        rFm = fpa11->fpreg[Fm].fExtended;
 223                        break;
 224
 225                default:
 226                        return 0;
 227                }
 228        }
 229
 230        if (n_flag)
 231                rFm.high ^= 0x8000;
 232
 233        /* test for less than condition */
 234        if (floatx80_lt(rFn, rFm))
 235                flags |= CC_NEGATIVE;
 236
 237        /* test for equal condition */
 238        if (floatx80_eq(rFn, rFm))
 239                flags |= CC_ZERO;
 240
 241        /* test for greater than or equal condition */
 242        if (floatx80_lt(rFm, rFn))
 243                flags |= CC_CARRY;
 244
 245#else
 246        if (CONSTANT_FM(opcode)) {
 247                /* Fm is a constant.  Do the comparison in whatever precision
 248                   Fn happens to be stored in.  */
 249                if (fpa11->fType[Fn] == typeSingle) {
 250                        float32 rFm = getSingleConstant(Fm);
 251                        float32 rFn = fpa11->fpreg[Fn].fSingle;
 252
 253                        if (float32_is_nan(rFn))
 254                                goto unordered;
 255
 256                        if (n_flag)
 257                                rFm ^= 0x80000000;
 258
 259                        /* test for less than condition */
 260                        if (float32_lt_nocheck(rFn, rFm))
 261                                flags |= CC_NEGATIVE;
 262
 263                        /* test for equal condition */
 264                        if (float32_eq_nocheck(rFn, rFm))
 265                                flags |= CC_ZERO;
 266
 267                        /* test for greater than or equal condition */
 268                        if (float32_lt_nocheck(rFm, rFn))
 269                                flags |= CC_CARRY;
 270                } else {
 271                        float64 rFm = getDoubleConstant(Fm);
 272                        float64 rFn = fpa11->fpreg[Fn].fDouble;
 273
 274                        if (float64_is_nan(rFn))
 275                                goto unordered;
 276
 277                        if (n_flag)
 278                                rFm ^= 0x8000000000000000ULL;
 279
 280                        /* test for less than condition */
 281                        if (float64_lt_nocheck(rFn, rFm))
 282                                flags |= CC_NEGATIVE;
 283
 284                        /* test for equal condition */
 285                        if (float64_eq_nocheck(rFn, rFm))
 286                                flags |= CC_ZERO;
 287
 288                        /* test for greater than or equal condition */
 289                        if (float64_lt_nocheck(rFm, rFn))
 290                                flags |= CC_CARRY;
 291                }
 292        } else {
 293                /* Both operands are in registers.  */
 294                if (fpa11->fType[Fn] == typeSingle
 295                    && fpa11->fType[Fm] == typeSingle) {
 296                        float32 rFm = fpa11->fpreg[Fm].fSingle;
 297                        float32 rFn = fpa11->fpreg[Fn].fSingle;
 298
 299                        if (float32_is_nan(rFn)
 300                            || float32_is_nan(rFm))
 301                                goto unordered;
 302
 303                        if (n_flag)
 304                                rFm ^= 0x80000000;
 305
 306                        /* test for less than condition */
 307                        if (float32_lt_nocheck(rFn, rFm))
 308                                flags |= CC_NEGATIVE;
 309
 310                        /* test for equal condition */
 311                        if (float32_eq_nocheck(rFn, rFm))
 312                                flags |= CC_ZERO;
 313
 314                        /* test for greater than or equal condition */
 315                        if (float32_lt_nocheck(rFm, rFn))
 316                                flags |= CC_CARRY;
 317                } else {
 318                        /* Promote 32-bit operand to 64 bits.  */
 319                        float64 rFm, rFn;
 320
 321                        rFm = (fpa11->fType[Fm] == typeSingle) ?
 322                            float32_to_float64(fpa11->fpreg[Fm].fSingle)
 323                            : fpa11->fpreg[Fm].fDouble;
 324
 325                        rFn = (fpa11->fType[Fn] == typeSingle) ?
 326                            float32_to_float64(fpa11->fpreg[Fn].fSingle)
 327                            : fpa11->fpreg[Fn].fDouble;
 328
 329                        if (float64_is_nan(rFn)
 330                            || float64_is_nan(rFm))
 331                                goto unordered;
 332
 333                        if (n_flag)
 334                                rFm ^= 0x8000000000000000ULL;
 335
 336                        /* test for less than condition */
 337                        if (float64_lt_nocheck(rFn, rFm))
 338                                flags |= CC_NEGATIVE;
 339
 340                        /* test for equal condition */
 341                        if (float64_eq_nocheck(rFn, rFm))
 342                                flags |= CC_ZERO;
 343
 344                        /* test for greater than or equal condition */
 345                        if (float64_lt_nocheck(rFm, rFn))
 346                                flags |= CC_CARRY;
 347                }
 348        }
 349
 350#endif
 351
 352        writeConditionCodes(flags);
 353
 354        return 1;
 355
 356      unordered:
 357        /* ?? The FPA data sheet is pretty vague about this, in particular
 358           about whether the non-E comparisons can ever raise exceptions.
 359           This implementation is based on a combination of what it says in
 360           the data sheet, observation of how the Acorn emulator actually
 361           behaves (and how programs expect it to) and guesswork.  */
 362        flags |= CC_OVERFLOW;
 363        flags &= ~(CC_ZERO | CC_NEGATIVE);
 364
 365        if (BIT_AC & readFPSR())
 366                flags |= CC_CARRY;
 367
 368        if (e_flag)
 369                float_raise(float_flag_invalid);
 370
 371        writeConditionCodes(flags);
 372        return 1;
 373}
 374