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_i_st_st(int nr)
 253{
 254        int f, c;
 255        FPU_REG *st_ptr;
 256
 257        if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
 258                FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
 259                /* Stack fault */
 260                EXCEPTION(EX_StackUnder);
 261                return !(control_word & CW_Invalid);
 262        }
 263
 264        partial_status &= ~SW_C0;
 265        st_ptr = &st(nr);
 266        c = compare(st_ptr, FPU_gettagi(nr));
 267        if (c & COMP_NaN) {
 268                FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
 269                EXCEPTION(EX_Invalid);
 270                return !(control_word & CW_Invalid);
 271        }
 272
 273        switch (c & 7) {
 274        case COMP_A_lt_B:
 275                f = X86_EFLAGS_CF;
 276                break;
 277        case COMP_A_eq_B:
 278                f = X86_EFLAGS_ZF;
 279                break;
 280        case COMP_A_gt_B:
 281                f = 0;
 282                break;
 283        case COMP_No_Comp:
 284                f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
 285                break;
 286#ifdef PARANOID
 287        default:
 288                EXCEPTION(EX_INTERNAL | 0x122);
 289                f = 0;
 290                break;
 291#endif /* PARANOID */
 292        }
 293        FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
 294        if (c & COMP_Denormal) {
 295                return denormal_operand() < 0;
 296        }
 297        return 0;
 298}
 299
 300static int compare_u_st_st(int nr)
 301{
 302        int f = 0, c;
 303        FPU_REG *st_ptr;
 304
 305        if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
 306                setcc(SW_C3 | SW_C2 | SW_C0);
 307                /* Stack fault */
 308                EXCEPTION(EX_StackUnder);
 309                return !(control_word & CW_Invalid);
 310        }
 311
 312        st_ptr = &st(nr);
 313        c = compare(st_ptr, FPU_gettagi(nr));
 314        if (c & COMP_NaN) {
 315                setcc(SW_C3 | SW_C2 | SW_C0);
 316                if (c & COMP_SNaN) {    /* This is the only difference between
 317                                           un-ordered and ordinary comparisons */
 318                        EXCEPTION(EX_Invalid);
 319                        return !(control_word & CW_Invalid);
 320                }
 321                return 0;
 322        } else
 323                switch (c & 7) {
 324                case COMP_A_lt_B:
 325                        f = SW_C0;
 326                        break;
 327                case COMP_A_eq_B:
 328                        f = SW_C3;
 329                        break;
 330                case COMP_A_gt_B:
 331                        f = 0;
 332                        break;
 333                case COMP_No_Comp:
 334                        f = SW_C3 | SW_C2 | SW_C0;
 335                        break;
 336#ifdef PARANOID
 337                default:
 338                        EXCEPTION(EX_INTERNAL | 0x123);
 339                        f = SW_C3 | SW_C2 | SW_C0;
 340                        break;
 341#endif /* PARANOID */
 342                }
 343        setcc(f);
 344        if (c & COMP_Denormal) {
 345                return denormal_operand() < 0;
 346        }
 347        return 0;
 348}
 349
 350static int compare_ui_st_st(int nr)
 351{
 352        int f = 0, c;
 353        FPU_REG *st_ptr;
 354
 355        if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
 356                FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
 357                /* Stack fault */
 358                EXCEPTION(EX_StackUnder);
 359                return !(control_word & CW_Invalid);
 360        }
 361
 362        partial_status &= ~SW_C0;
 363        st_ptr = &st(nr);
 364        c = compare(st_ptr, FPU_gettagi(nr));
 365        if (c & COMP_NaN) {
 366                FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
 367                if (c & COMP_SNaN) {    /* This is the only difference between
 368                                           un-ordered and ordinary comparisons */
 369                        EXCEPTION(EX_Invalid);
 370                        return !(control_word & CW_Invalid);
 371                }
 372                return 0;
 373        }
 374
 375        switch (c & 7) {
 376        case COMP_A_lt_B:
 377                f = X86_EFLAGS_CF;
 378                break;
 379        case COMP_A_eq_B:
 380                f = X86_EFLAGS_ZF;
 381                break;
 382        case COMP_A_gt_B:
 383                f = 0;
 384                break;
 385        case COMP_No_Comp:
 386                f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
 387                break;
 388#ifdef PARANOID
 389        default:
 390                EXCEPTION(EX_INTERNAL | 0x123);
 391                f = 0;
 392                break;
 393#endif /* PARANOID */
 394        }
 395        FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
 396        if (c & COMP_Denormal) {
 397                return denormal_operand() < 0;
 398        }
 399        return 0;
 400}
 401
 402/*---------------------------------------------------------------------------*/
 403
 404void fcom_st(void)
 405{
 406        /* fcom st(i) */
 407        compare_st_st(FPU_rm);
 408}
 409
 410void fcompst(void)
 411{
 412        /* fcomp st(i) */
 413        if (!compare_st_st(FPU_rm))
 414                FPU_pop();
 415}
 416
 417void fcompp(void)
 418{
 419        /* fcompp */
 420        if (FPU_rm != 1) {
 421                FPU_illegal();
 422                return;
 423        }
 424        if (!compare_st_st(1))
 425                poppop();
 426}
 427
 428void fucom_(void)
 429{
 430        /* fucom st(i) */
 431        compare_u_st_st(FPU_rm);
 432
 433}
 434
 435void fucomp(void)
 436{
 437        /* fucomp st(i) */
 438        if (!compare_u_st_st(FPU_rm))
 439                FPU_pop();
 440}
 441
 442void fucompp(void)
 443{
 444        /* fucompp */
 445        if (FPU_rm == 1) {
 446                if (!compare_u_st_st(1))
 447                        poppop();
 448        } else
 449                FPU_illegal();
 450}
 451
 452/* P6+ compare-to-EFLAGS ops */
 453
 454void fcomi_(void)
 455{
 456        /* fcomi st(i) */
 457        compare_i_st_st(FPU_rm);
 458}
 459
 460void fcomip(void)
 461{
 462        /* fcomip st(i) */
 463        if (!compare_i_st_st(FPU_rm))
 464                FPU_pop();
 465}
 466
 467void fucomi_(void)
 468{
 469        /* fucomi st(i) */
 470        compare_ui_st_st(FPU_rm);
 471}
 472
 473void fucomip(void)
 474{
 475        /* fucomip st(i) */
 476        if (!compare_ui_st_st(FPU_rm))
 477                FPU_pop();
 478}
 479