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