linux/arch/x86/math-emu/reg_compare.c
<<
>>
Prefs
   1/*---------------------------------------------------------------------------+
   2 |  reg_compare.c                                                            |
   3 |                                                                           |
   4 | Compare two floating point registers                                      |
   5 |                                                                           |
   6 | Copyright (C) 1992,1993,1994,1997                                         |
   7 |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
   8 |                  E-mail   billm@suburbia.net                              |
   9 |                                                                           |
  10 |                                                                           |
  11 +---------------------------------------------------------------------------*/
  12
  13/*---------------------------------------------------------------------------+
  14 | compare() is the core FPU_REG comparison function                         |
  15 +---------------------------------------------------------------------------*/
  16
  17#include "fpu_system.h"
  18#include "exception.h"
  19#include "fpu_emu.h"
  20#include "control_w.h"
  21#include "status_w.h"
  22
  23static int compare(FPU_REG const *b, int tagb)
  24{
  25        int diff, exp0, expb;
  26        u_char st0_tag;
  27        FPU_REG *st0_ptr;
  28        FPU_REG x, y;
  29        u_char st0_sign, signb = getsign(b);
  30
  31        st0_ptr = &st(0);
  32        st0_tag = FPU_gettag0();
  33        st0_sign = getsign(st0_ptr);
  34
  35        if (tagb == TAG_Special)
  36                tagb = FPU_Special(b);
  37        if (st0_tag == TAG_Special)
  38                st0_tag = FPU_Special(st0_ptr);
  39
  40        if (((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal))
  41            || ((tagb != TAG_Valid) && (tagb != TW_Denormal))) {
  42                if (st0_tag == TAG_Zero) {
  43                        if (tagb == TAG_Zero)
  44                                return COMP_A_eq_B;
  45                        if (tagb == TAG_Valid)
  46                                return ((signb ==
  47                                         SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
  48                        if (tagb == TW_Denormal)
  49                                return ((signb ==
  50                                         SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
  51                                    | COMP_Denormal;
  52                } else if (tagb == TAG_Zero) {
  53                        if (st0_tag == TAG_Valid)
  54                                return ((st0_sign ==
  55                                         SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
  56                        if (st0_tag == TW_Denormal)
  57                                return ((st0_sign ==
  58                                         SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
  59                                    | COMP_Denormal;
  60                }
  61
  62                if (st0_tag == TW_Infinity) {
  63                        if ((tagb == TAG_Valid) || (tagb == TAG_Zero))
  64                                return ((st0_sign ==
  65                                         SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
  66                        else if (tagb == TW_Denormal)
  67                                return ((st0_sign ==
  68                                         SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
  69                                    | COMP_Denormal;
  70                        else if (tagb == TW_Infinity) {
  71                                /* The 80486 book says that infinities can be equal! */
  72                                return (st0_sign == signb) ? COMP_A_eq_B :
  73                                    ((st0_sign ==
  74                                      SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
  75                        }
  76                        /* Fall through to the NaN code */
  77                } else if (tagb == TW_Infinity) {
  78                        if ((st0_tag == TAG_Valid) || (st0_tag == TAG_Zero))
  79                                return ((signb ==
  80                                         SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
  81                        if (st0_tag == TW_Denormal)
  82                                return ((signb ==
  83                                         SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
  84                                    | COMP_Denormal;
  85                        /* Fall through to the NaN code */
  86                }
  87
  88                /* The only possibility now should be that one of the arguments
  89                   is a NaN */
  90                if ((st0_tag == TW_NaN) || (tagb == TW_NaN)) {
  91                        int signalling = 0, unsupported = 0;
  92                        if (st0_tag == TW_NaN) {
  93                                signalling =
  94                                    (st0_ptr->sigh & 0xc0000000) == 0x80000000;
  95                                unsupported = !((exponent(st0_ptr) == EXP_OVER)
  96                                                && (st0_ptr->
  97                                                    sigh & 0x80000000));
  98                        }
  99                        if (tagb == TW_NaN) {
 100                                signalling |=
 101                                    (b->sigh & 0xc0000000) == 0x80000000;
 102                                unsupported |= !((exponent(b) == EXP_OVER)
 103                                                 && (b->sigh & 0x80000000));
 104                        }
 105                        if (signalling || unsupported)
 106                                return COMP_No_Comp | COMP_SNaN | COMP_NaN;
 107                        else
 108                                /* Neither is a signaling NaN */
 109                                return COMP_No_Comp | COMP_NaN;
 110                }
 111
 112                EXCEPTION(EX_Invalid);
 113        }
 114
 115        if (st0_sign != signb) {
 116                return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
 117                    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
 118                       COMP_Denormal : 0);
 119        }
 120
 121        if ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) {
 122                FPU_to_exp16(st0_ptr, &x);
 123                FPU_to_exp16(b, &y);
 124                st0_ptr = &x;
 125                b = &y;
 126                exp0 = exponent16(st0_ptr);
 127                expb = exponent16(b);
 128        } else {
 129                exp0 = exponent(st0_ptr);
 130                expb = exponent(b);
 131        }
 132
 133#ifdef PARANOID
 134        if (!(st0_ptr->sigh & 0x80000000))
 135                EXCEPTION(EX_Invalid);
 136        if (!(b->sigh & 0x80000000))
 137                EXCEPTION(EX_Invalid);
 138#endif /* PARANOID */
 139
 140        diff = exp0 - expb;
 141        if (diff == 0) {
 142                diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are
 143                                                   identical */
 144                if (diff == 0) {
 145                        diff = st0_ptr->sigl > b->sigl;
 146                        if (diff == 0)
 147                                diff = -(st0_ptr->sigl < b->sigl);
 148                }
 149        }
 150
 151        if (diff > 0) {
 152                return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
 153                    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
 154                       COMP_Denormal : 0);
 155        }
 156        if (diff < 0) {
 157                return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
 158                    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
 159                       COMP_Denormal : 0);
 160        }
 161
 162        return COMP_A_eq_B
 163            | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
 164               COMP_Denormal : 0);
 165
 166}
 167
 168/* This function requires that st(0) is not empty */
 169int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag)
 170{
 171        int f = 0, c;
 172
 173        c = compare(loaded_data, loaded_tag);
 174
 175        if (c & COMP_NaN) {
 176                EXCEPTION(EX_Invalid);
 177                f = SW_C3 | SW_C2 | SW_C0;
 178        } else
 179                switch (c & 7) {
 180                case COMP_A_lt_B:
 181                        f = SW_C0;
 182                        break;
 183                case COMP_A_eq_B:
 184                        f = SW_C3;
 185                        break;
 186                case COMP_A_gt_B:
 187                        f = 0;
 188                        break;
 189                case COMP_No_Comp:
 190                        f = SW_C3 | SW_C2 | SW_C0;
 191                        break;
 192#ifdef PARANOID
 193                default:
 194                        EXCEPTION(EX_INTERNAL | 0x121);
 195                        f = SW_C3 | SW_C2 | SW_C0;
 196                        break;
 197#endif /* PARANOID */
 198                }
 199        setcc(f);
 200        if (c & COMP_Denormal) {
 201                return denormal_operand() < 0;
 202        }
 203        return 0;
 204}
 205
 206static int compare_st_st(int nr)
 207{
 208        int f = 0, c;
 209        FPU_REG *st_ptr;
 210
 211        if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
 212                setcc(SW_C3 | SW_C2 | SW_C0);
 213                /* Stack fault */
 214                EXCEPTION(EX_StackUnder);
 215                return !(control_word & CW_Invalid);
 216        }
 217
 218        st_ptr = &st(nr);
 219        c = compare(st_ptr, FPU_gettagi(nr));
 220        if (c & COMP_NaN) {
 221                setcc(SW_C3 | SW_C2 | SW_C0);
 222                EXCEPTION(EX_Invalid);
 223                return !(control_word & CW_Invalid);
 224        } else
 225                switch (c & 7) {
 226                case COMP_A_lt_B:
 227                        f = SW_C0;
 228                        break;
 229                case COMP_A_eq_B:
 230                        f = SW_C3;
 231                        break;
 232                case COMP_A_gt_B:
 233                        f = 0;
 234                        break;
 235                case COMP_No_Comp:
 236                        f = SW_C3 | SW_C2 | SW_C0;
 237                        break;
 238#ifdef PARANOID
 239                default:
 240                        EXCEPTION(EX_INTERNAL | 0x122);
 241                        f = SW_C3 | SW_C2 | SW_C0;
 242                        break;
 243#endif /* PARANOID */
 244                }
 245        setcc(f);
 246        if (c & COMP_Denormal) {
 247                return denormal_operand() < 0;
 248        }
 249        return 0;
 250}
 251
 252static int compare_u_st_st(int nr)
 253{
 254        int f = 0, c;
 255        FPU_REG *st_ptr;
 256
 257        if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
 258                setcc(SW_C3 | SW_C2 | SW_C0);
 259                /* Stack fault */
 260                EXCEPTION(EX_StackUnder);
 261                return !(control_word & CW_Invalid);
 262        }
 263
 264        st_ptr = &st(nr);
 265        c = compare(st_ptr, FPU_gettagi(nr));
 266        if (c & COMP_NaN) {
 267                setcc(SW_C3 | SW_C2 | SW_C0);
 268                if (c & COMP_SNaN) {    /* This is the only difference between
 269                                           un-ordered and ordinary comparisons */
 270                        EXCEPTION(EX_Invalid);
 271                        return !(control_word & CW_Invalid);
 272                }
 273                return 0;
 274        } else
 275                switch (c & 7) {
 276                case COMP_A_lt_B:
 277                        f = SW_C0;
 278                        break;
 279                case COMP_A_eq_B:
 280                        f = SW_C3;
 281                        break;
 282                case COMP_A_gt_B:
 283                        f = 0;
 284                        break;
 285                case COMP_No_Comp:
 286                        f = SW_C3 | SW_C2 | SW_C0;
 287                        break;
 288#ifdef PARANOID
 289                default:
 290                        EXCEPTION(EX_INTERNAL | 0x123);
 291                        f = SW_C3 | SW_C2 | SW_C0;
 292                        break;
 293#endif /* PARANOID */
 294                }
 295        setcc(f);
 296        if (c & COMP_Denormal) {
 297                return denormal_operand() < 0;
 298        }
 299        return 0;
 300}
 301
 302/*---------------------------------------------------------------------------*/
 303
 304void fcom_st(void)
 305{
 306        /* fcom st(i) */
 307        compare_st_st(FPU_rm);
 308}
 309
 310void fcompst(void)
 311{
 312        /* fcomp st(i) */
 313        if (!compare_st_st(FPU_rm))
 314                FPU_pop();
 315}
 316
 317void fcompp(void)
 318{
 319        /* fcompp */
 320        if (FPU_rm != 1) {
 321                FPU_illegal();
 322                return;
 323        }
 324        if (!compare_st_st(1))
 325                poppop();
 326}
 327
 328void fucom_(void)
 329{
 330        /* fucom st(i) */
 331        compare_u_st_st(FPU_rm);
 332
 333}
 334
 335void fucomp(void)
 336{
 337        /* fucomp st(i) */
 338        if (!compare_u_st_st(FPU_rm))
 339                FPU_pop();
 340}
 341
 342void fucompp(void)
 343{
 344        /* fucompp */
 345        if (FPU_rm == 1) {
 346                if (!compare_u_st_st(1))
 347                        poppop();
 348        } else
 349                FPU_illegal();
 350}
 351