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