linux/arch/x86/math-emu/reg_compare.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*---------------------------------------------------------------------------+
   3 |  reg_compare.c                                                            |
   4 |                                                                           |
   5 | Compare two floating point registers                                      |
   6 |                                                                           |
   7 | Copyright (C) 1992,1993,1994,1997                                         |
   8 |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
   9 |                  E-mail   billm@suburbia.net                              |
  10 |                                                                           |
  11 |                                                                           |
  12 +---------------------------------------------------------------------------*/
  13
  14/*---------------------------------------------------------------------------+
  15 | compare() is the core FPU_REG comparison function                         |
  16 +---------------------------------------------------------------------------*/
  17
  18#include "fpu_system.h"
  19#include "exception.h"
  20#include "fpu_emu.h"
  21#include "control_w.h"
  22#include "status_w.h"
  23
  24static int compare(FPU_REG const *b, int tagb)
  25{
  26        int diff, exp0, expb;
  27        u_char st0_tag;
  28        FPU_REG *st0_ptr;
  29        FPU_REG x, y;
  30        u_char st0_sign, signb = getsign(b);
  31
  32        st0_ptr = &st(0);
  33        st0_tag = FPU_gettag0();
  34        st0_sign = getsign(st0_ptr);
  35
  36        if (tagb == TAG_Special)
  37                tagb = FPU_Special(b);
  38        if (st0_tag == TAG_Special)
  39                st0_tag = FPU_Special(st0_ptr);
  40
  41        if (((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal))
  42            || ((tagb != TAG_Valid) && (tagb != TW_Denormal))) {
  43                if (st0_tag == TAG_Zero) {
  44                        if (tagb == TAG_Zero)
  45                                return COMP_A_eq_B;
  46                        if (tagb == TAG_Valid)
  47                                return ((signb ==
  48                                         SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
  49                        if (tagb == TW_Denormal)
  50                                return ((signb ==
  51                                         SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
  52                                    | COMP_Denormal;
  53                } else if (tagb == TAG_Zero) {
  54                        if (st0_tag == TAG_Valid)
  55                                return ((st0_sign ==
  56                                         SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
  57                        if (st0_tag == TW_Denormal)
  58                                return ((st0_sign ==
  59                                         SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
  60                                    | COMP_Denormal;
  61                }
  62
  63                if (st0_tag == TW_Infinity) {
  64                        if ((tagb == TAG_Valid) || (tagb == TAG_Zero))
  65                                return ((st0_sign ==
  66                                         SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
  67                        else if (tagb == TW_Denormal)
  68                                return ((st0_sign ==
  69                                         SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
  70                                    | COMP_Denormal;
  71                        else if (tagb == TW_Infinity) {
  72                                /* The 80486 book says that infinities can be equal! */
  73                                return (st0_sign == signb) ? COMP_A_eq_B :
  74                                    ((st0_sign ==
  75                                      SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
  76                        }
  77                        /* Fall through to the NaN code */
  78                } else if (tagb == TW_Infinity) {
  79                        if ((st0_tag == TAG_Valid) || (st0_tag == TAG_Zero))
  80                                return ((signb ==
  81                                         SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
  82                        if (st0_tag == TW_Denormal)
  83                                return ((signb ==
  84                                         SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
  85                                    | COMP_Denormal;
  86                        /* Fall through to the NaN code */
  87                }
  88
  89                /* The only possibility now should be that one of the arguments
  90                   is a NaN */
  91                if ((st0_tag == TW_NaN) || (tagb == TW_NaN)) {
  92                        int signalling = 0, unsupported = 0;
  93                        if (st0_tag == TW_NaN) {
  94                                signalling =
  95                                    (st0_ptr->sigh & 0xc0000000) == 0x80000000;
  96                                unsupported = !((exponent(st0_ptr) == EXP_OVER)
  97                                                && (st0_ptr->
  98                                                    sigh & 0x80000000));
  99                        }
 100                        if (tagb == TW_NaN) {
 101                                signalling |=
 102                                    (b->sigh & 0xc0000000) == 0x80000000;
 103                                unsupported |= !((exponent(b) == EXP_OVER)
 104                                                 && (b->sigh & 0x80000000));
 105                        }
 106                        if (signalling || unsupported)
 107                                return COMP_No_Comp | COMP_SNaN | COMP_NaN;
 108                        else
 109                                /* Neither is a signaling NaN */
 110                                return COMP_No_Comp | COMP_NaN;
 111                }
 112
 113                EXCEPTION(EX_Invalid);
 114        }
 115
 116        if (st0_sign != signb) {
 117                return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
 118                    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
 119                       COMP_Denormal : 0);
 120        }
 121
 122        if ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) {
 123                FPU_to_exp16(st0_ptr, &x);
 124                FPU_to_exp16(b, &y);
 125                st0_ptr = &x;
 126                b = &y;
 127                exp0 = exponent16(st0_ptr);
 128                expb = exponent16(b);
 129        } else {
 130                exp0 = exponent(st0_ptr);
 131                expb = exponent(b);
 132        }
 133
 134#ifdef PARANOID
 135        if (!(st0_ptr->sigh & 0x80000000))
 136                EXCEPTION(EX_Invalid);
 137        if (!(b->sigh & 0x80000000))
 138                EXCEPTION(EX_Invalid);
 139#endif /* PARANOID */
 140
 141        diff = exp0 - expb;
 142        if (diff == 0) {
 143                diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are
 144                                                   identical */
 145                if (diff == 0) {
 146                        diff = st0_ptr->sigl > b->sigl;
 147                        if (diff == 0)
 148                                diff = -(st0_ptr->sigl < b->sigl);
 149                }
 150        }
 151
 152        if (diff > 0) {
 153                return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
 154                    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
 155                       COMP_Denormal : 0);
 156        }
 157        if (diff < 0) {
 158                return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
 159                    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
 160                       COMP_Denormal : 0);
 161        }
 162
 163        return COMP_A_eq_B
 164            | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
 165               COMP_Denormal : 0);
 166
 167}
 168
 169/* This function requires that st(0) is not empty */
 170int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag)
 171{
 172        int f, c;
 173
 174        c = compare(loaded_data, loaded_tag);
 175
 176        if (c & COMP_NaN) {
 177                EXCEPTION(EX_Invalid);
 178                f = SW_C3 | SW_C2 | SW_C0;
 179        } else
 180                switch (c & 7) {
 181                case COMP_A_lt_B:
 182                        f = SW_C0;
 183                        break;
 184                case COMP_A_eq_B:
 185                        f = SW_C3;
 186                        break;
 187                case COMP_A_gt_B:
 188                        f = 0;
 189                        break;
 190                case COMP_No_Comp:
 191                        f = SW_C3 | SW_C2 | SW_C0;
 192                        break;
 193                default:
 194#ifdef PARANOID
 195                        EXCEPTION(EX_INTERNAL | 0x121);
 196#endif /* PARANOID */
 197                        f = SW_C3 | SW_C2 | SW_C0;
 198                        break;
 199                }
 200        setcc(f);
 201        if (c & COMP_Denormal) {
 202                return denormal_operand() < 0;
 203        }
 204        return 0;
 205}
 206
 207static int compare_st_st(int nr)
 208{
 209        int f, c;
 210        FPU_REG *st_ptr;
 211
 212        if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
 213                setcc(SW_C3 | SW_C2 | SW_C0);
 214                /* Stack fault */
 215                EXCEPTION(EX_StackUnder);
 216                return !(control_word & CW_Invalid);
 217        }
 218
 219        st_ptr = &st(nr);
 220        c = compare(st_ptr, FPU_gettagi(nr));
 221        if (c & COMP_NaN) {
 222                setcc(SW_C3 | SW_C2 | SW_C0);
 223                EXCEPTION(EX_Invalid);
 224                return !(control_word & CW_Invalid);
 225        } else
 226                switch (c & 7) {
 227                case COMP_A_lt_B:
 228                        f = SW_C0;
 229                        break;
 230                case COMP_A_eq_B:
 231                        f = SW_C3;
 232                        break;
 233                case COMP_A_gt_B:
 234                        f = 0;
 235                        break;
 236                case COMP_No_Comp:
 237                        f = SW_C3 | SW_C2 | SW_C0;
 238                        break;
 239                default:
 240#ifdef PARANOID
 241                        EXCEPTION(EX_INTERNAL | 0x122);
 242#endif /* PARANOID */
 243                        f = SW_C3 | SW_C2 | SW_C0;
 244                        break;
 245                }
 246        setcc(f);
 247        if (c & COMP_Denormal) {
 248                return denormal_operand() < 0;
 249        }
 250        return 0;
 251}
 252
 253static int compare_i_st_st(int nr)
 254{
 255        int f, c;
 256        FPU_REG *st_ptr;
 257
 258        if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
 259                FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
 260                /* Stack fault */
 261                EXCEPTION(EX_StackUnder);
 262                return !(control_word & CW_Invalid);
 263        }
 264
 265        partial_status &= ~SW_C0;
 266        st_ptr = &st(nr);
 267        c = compare(st_ptr, FPU_gettagi(nr));
 268        if (c & COMP_NaN) {
 269                FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
 270                EXCEPTION(EX_Invalid);
 271                return !(control_word & CW_Invalid);
 272        }
 273
 274        switch (c & 7) {
 275        case COMP_A_lt_B:
 276                f = X86_EFLAGS_CF;
 277                break;
 278        case COMP_A_eq_B:
 279                f = X86_EFLAGS_ZF;
 280                break;
 281        case COMP_A_gt_B:
 282                f = 0;
 283                break;
 284        case COMP_No_Comp:
 285                f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
 286                break;
 287        default:
 288#ifdef PARANOID
 289                EXCEPTION(EX_INTERNAL | 0x122);
 290#endif /* PARANOID */
 291                f = 0;
 292                break;
 293        }
 294        FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
 295        if (c & COMP_Denormal) {
 296                return denormal_operand() < 0;
 297        }
 298        return 0;
 299}
 300
 301static int compare_u_st_st(int nr)
 302{
 303        int f = 0, c;
 304        FPU_REG *st_ptr;
 305
 306        if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
 307                setcc(SW_C3 | SW_C2 | SW_C0);
 308                /* Stack fault */
 309                EXCEPTION(EX_StackUnder);
 310                return !(control_word & CW_Invalid);
 311        }
 312
 313        st_ptr = &st(nr);
 314        c = compare(st_ptr, FPU_gettagi(nr));
 315        if (c & COMP_NaN) {
 316                setcc(SW_C3 | SW_C2 | SW_C0);
 317                if (c & COMP_SNaN) {    /* This is the only difference between
 318                                           un-ordered and ordinary comparisons */
 319                        EXCEPTION(EX_Invalid);
 320                        return !(control_word & CW_Invalid);
 321                }
 322                return 0;
 323        } else
 324                switch (c & 7) {
 325                case COMP_A_lt_B:
 326                        f = SW_C0;
 327                        break;
 328                case COMP_A_eq_B:
 329                        f = SW_C3;
 330                        break;
 331                case COMP_A_gt_B:
 332                        f = 0;
 333                        break;
 334                case COMP_No_Comp:
 335                        f = SW_C3 | SW_C2 | SW_C0;
 336                        break;
 337#ifdef PARANOID
 338                default:
 339                        EXCEPTION(EX_INTERNAL | 0x123);
 340                        f = SW_C3 | SW_C2 | SW_C0;
 341                        break;
 342#endif /* PARANOID */
 343                }
 344        setcc(f);
 345        if (c & COMP_Denormal) {
 346                return denormal_operand() < 0;
 347        }
 348        return 0;
 349}
 350
 351static int compare_ui_st_st(int nr)
 352{
 353        int f = 0, c;
 354        FPU_REG *st_ptr;
 355
 356        if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
 357                FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
 358                /* Stack fault */
 359                EXCEPTION(EX_StackUnder);
 360                return !(control_word & CW_Invalid);
 361        }
 362
 363        partial_status &= ~SW_C0;
 364        st_ptr = &st(nr);
 365        c = compare(st_ptr, FPU_gettagi(nr));
 366        if (c & COMP_NaN) {
 367                FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
 368                if (c & COMP_SNaN) {    /* This is the only difference between
 369                                           un-ordered and ordinary comparisons */
 370                        EXCEPTION(EX_Invalid);
 371                        return !(control_word & CW_Invalid);
 372                }
 373                return 0;
 374        }
 375
 376        switch (c & 7) {
 377        case COMP_A_lt_B:
 378                f = X86_EFLAGS_CF;
 379                break;
 380        case COMP_A_eq_B:
 381                f = X86_EFLAGS_ZF;
 382                break;
 383        case COMP_A_gt_B:
 384                f = 0;
 385                break;
 386        case COMP_No_Comp:
 387                f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
 388                break;
 389#ifdef PARANOID
 390        default:
 391                EXCEPTION(EX_INTERNAL | 0x123);
 392                f = 0;
 393                break;
 394#endif /* PARANOID */
 395        }
 396        FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
 397        if (c & COMP_Denormal) {
 398                return denormal_operand() < 0;
 399        }
 400        return 0;
 401}
 402
 403/*---------------------------------------------------------------------------*/
 404
 405void fcom_st(void)
 406{
 407        /* fcom st(i) */
 408        compare_st_st(FPU_rm);
 409}
 410
 411void fcompst(void)
 412{
 413        /* fcomp st(i) */
 414        if (!compare_st_st(FPU_rm))
 415                FPU_pop();
 416}
 417
 418void fcompp(void)
 419{
 420        /* fcompp */
 421        if (FPU_rm != 1) {
 422                FPU_illegal();
 423                return;
 424        }
 425        if (!compare_st_st(1))
 426                poppop();
 427}
 428
 429void fucom_(void)
 430{
 431        /* fucom st(i) */
 432        compare_u_st_st(FPU_rm);
 433
 434}
 435
 436void fucomp(void)
 437{
 438        /* fucomp st(i) */
 439        if (!compare_u_st_st(FPU_rm))
 440                FPU_pop();
 441}
 442
 443void fucompp(void)
 444{
 445        /* fucompp */
 446        if (FPU_rm == 1) {
 447                if (!compare_u_st_st(1))
 448                        poppop();
 449        } else
 450                FPU_illegal();
 451}
 452
 453/* P6+ compare-to-EFLAGS ops */
 454
 455void fcomi_(void)
 456{
 457        /* fcomi st(i) */
 458        compare_i_st_st(FPU_rm);
 459}
 460
 461void fcomip(void)
 462{
 463        /* fcomip st(i) */
 464        if (!compare_i_st_st(FPU_rm))
 465                FPU_pop();
 466}
 467
 468void fucomi_(void)
 469{
 470        /* fucomi st(i) */
 471        compare_ui_st_st(FPU_rm);
 472}
 473
 474void fucomip(void)
 475{
 476        /* fucomip st(i) */
 477        if (!compare_ui_st_st(FPU_rm))
 478                FPU_pop();
 479}
 480