[funini.com] -> [kei@sodan] -> Kernel Reading

root/arch/x86/math-emu/reg_compare.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. compare
  2. FPU_compare_st_data
  3. compare_st_st
  4. compare_u_st_st
  5. fcom_st
  6. fcompst
  7. fcompp
  8. fucom_
  9. fucomp
  10. fucompp

/*---------------------------------------------------------------------------+
 |  reg_compare.c                                                            |
 |                                                                           |
 | Compare two floating point registers                                      |
 |                                                                           |
 | Copyright (C) 1992,1993,1994,1997                                         |
 |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
 |                  E-mail   billm@suburbia.net                              |
 |                                                                           |
 |                                                                           |
 +---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------+
 | compare() is the core FPU_REG comparison function                         |
 +---------------------------------------------------------------------------*/

#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
#include "control_w.h"
#include "status_w.h"

static int compare(FPU_REG const *b, int tagb)
{
        int diff, exp0, expb;
        u_char st0_tag;
        FPU_REG *st0_ptr;
        FPU_REG x, y;
        u_char st0_sign, signb = getsign(b);

        st0_ptr = &st(0);
        st0_tag = FPU_gettag0();
        st0_sign = getsign(st0_ptr);

        if (tagb == TAG_Special)
                tagb = FPU_Special(b);
        if (st0_tag == TAG_Special)
                st0_tag = FPU_Special(st0_ptr);

        if (((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal))
            || ((tagb != TAG_Valid) && (tagb != TW_Denormal))) {
                if (st0_tag == TAG_Zero) {
                        if (tagb == TAG_Zero)
                                return COMP_A_eq_B;
                        if (tagb == TAG_Valid)
                                return ((signb ==
                                         SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
                        if (tagb == TW_Denormal)
                                return ((signb ==
                                         SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
                                    | COMP_Denormal;
                } else if (tagb == TAG_Zero) {
                        if (st0_tag == TAG_Valid)
                                return ((st0_sign ==
                                         SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
                        if (st0_tag == TW_Denormal)
                                return ((st0_sign ==
                                         SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
                                    | COMP_Denormal;
                }

                if (st0_tag == TW_Infinity) {
                        if ((tagb == TAG_Valid) || (tagb == TAG_Zero))
                                return ((st0_sign ==
                                         SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
                        else if (tagb == TW_Denormal)
                                return ((st0_sign ==
                                         SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
                                    | COMP_Denormal;
                        else if (tagb == TW_Infinity) {
                                /* The 80486 book says that infinities can be equal! */
                                return (st0_sign == signb) ? COMP_A_eq_B :
                                    ((st0_sign ==
                                      SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
                        }
                        /* Fall through to the NaN code */
                } else if (tagb == TW_Infinity) {
                        if ((st0_tag == TAG_Valid) || (st0_tag == TAG_Zero))
                                return ((signb ==
                                         SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
                        if (st0_tag == TW_Denormal)
                                return ((signb ==
                                         SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
                                    | COMP_Denormal;
                        /* Fall through to the NaN code */
                }

                /* The only possibility now should be that one of the arguments
                   is a NaN */
                if ((st0_tag == TW_NaN) || (tagb == TW_NaN)) {
                        int signalling = 0, unsupported = 0;
                        if (st0_tag == TW_NaN) {
                                signalling =
                                    (st0_ptr->sigh & 0xc0000000) == 0x80000000;
                                unsupported = !((exponent(st0_ptr) == EXP_OVER)
                                                && (st0_ptr->
                                                    sigh & 0x80000000));
                        }
                        if (tagb == TW_NaN) {
                                signalling |=
                                    (b->sigh & 0xc0000000) == 0x80000000;
                                unsupported |= !((exponent(b) == EXP_OVER)
                                                 && (b->sigh & 0x80000000));
                        }
                        if (signalling || unsupported)
                                return COMP_No_Comp | COMP_SNaN | COMP_NaN;
                        else
                                /* Neither is a signaling NaN */
                                return COMP_No_Comp | COMP_NaN;
                }

                EXCEPTION(EX_Invalid);
        }

        if (st0_sign != signb) {
                return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
                    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
                       COMP_Denormal : 0);
        }

        if ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) {
                FPU_to_exp16(st0_ptr, &x);
                FPU_to_exp16(b, &y);
                st0_ptr = &x;
                b = &y;
                exp0 = exponent16(st0_ptr);
                expb = exponent16(b);
        } else {
                exp0 = exponent(st0_ptr);
                expb = exponent(b);
        }

#ifdef PARANOID
        if (!(st0_ptr->sigh & 0x80000000))
                EXCEPTION(EX_Invalid);
        if (!(b->sigh & 0x80000000))
                EXCEPTION(EX_Invalid);
#endif /* PARANOID */

        diff = exp0 - expb;
        if (diff == 0) {
                diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are
                                                   identical */
                if (diff == 0) {
                        diff = st0_ptr->sigl > b->sigl;
                        if (diff == 0)
                                diff = -(st0_ptr->sigl < b->sigl);
                }
        }

        if (diff > 0) {
                return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
                    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
                       COMP_Denormal : 0);
        }
        if (diff < 0) {
                return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
                    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
                       COMP_Denormal : 0);
        }

        return COMP_A_eq_B
            | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
               COMP_Denormal : 0);

}

/* This function requires that st(0) is not empty */
int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag)
{
        int f = 0, c;

        c = compare(loaded_data, loaded_tag);

        if (c & COMP_NaN) {
                EXCEPTION(EX_Invalid);
                f = SW_C3 | SW_C2 | SW_C0;
        } else
                switch (c & 7) {
                case COMP_A_lt_B:
                        f = SW_C0;
                        break;
                case COMP_A_eq_B:
                        f = SW_C3;
                        break;
                case COMP_A_gt_B:
                        f = 0;
                        break;
                case COMP_No_Comp:
                        f = SW_C3 | SW_C2 | SW_C0;
                        break;
#ifdef PARANOID
                default:
                        EXCEPTION(EX_INTERNAL | 0x121);
                        f = SW_C3 | SW_C2 | SW_C0;
                        break;
#endif /* PARANOID */
                }
        setcc(f);
        if (c & COMP_Denormal) {
                return denormal_operand() < 0;
        }
        return 0;
}

static int compare_st_st(int nr)
{
        int f = 0, c;
        FPU_REG *st_ptr;

        if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
                setcc(SW_C3 | SW_C2 | SW_C0);
                /* Stack fault */
                EXCEPTION(EX_StackUnder);
                return !(control_word & CW_Invalid);
        }

        st_ptr = &st(nr);
        c = compare(st_ptr, FPU_gettagi(nr));
        if (c & COMP_NaN) {
                setcc(SW_C3 | SW_C2 | SW_C0);
                EXCEPTION(EX_Invalid);
                return !(control_word & CW_Invalid);
        } else
                switch (c & 7) {
                case COMP_A_lt_B:
                        f = SW_C0;
                        break;
                case COMP_A_eq_B:
                        f = SW_C3;
                        break;
                case COMP_A_gt_B:
                        f = 0;
                        break;
                case COMP_No_Comp:
                        f = SW_C3 | SW_C2 | SW_C0;
                        break;
#ifdef PARANOID
                default:
                        EXCEPTION(EX_INTERNAL | 0x122);
                        f = SW_C3 | SW_C2 | SW_C0;
                        break;
#endif /* PARANOID */
                }
        setcc(f);
        if (c & COMP_Denormal) {
                return denormal_operand() < 0;
        }
        return 0;
}

static int compare_u_st_st(int nr)
{
        int f = 0, c;
        FPU_REG *st_ptr;

        if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
                setcc(SW_C3 | SW_C2 | SW_C0);
                /* Stack fault */
                EXCEPTION(EX_StackUnder);
                return !(control_word & CW_Invalid);
        }

        st_ptr = &st(nr);
        c = compare(st_ptr, FPU_gettagi(nr));
        if (c & COMP_NaN) {
                setcc(SW_C3 | SW_C2 | SW_C0);
                if (c & COMP_SNaN) {    /* This is the only difference between
                                           un-ordered and ordinary comparisons */
                        EXCEPTION(EX_Invalid);
                        return !(control_word & CW_Invalid);
                }
                return 0;
        } else
                switch (c & 7) {
                case COMP_A_lt_B:
                        f = SW_C0;
                        break;
                case COMP_A_eq_B:
                        f = SW_C3;
                        break;
                case COMP_A_gt_B:
                        f = 0;
                        break;
                case COMP_No_Comp:
                        f = SW_C3 | SW_C2 | SW_C0;
                        break;
#ifdef PARANOID
                default:
                        EXCEPTION(EX_INTERNAL | 0x123);
                        f = SW_C3 | SW_C2 | SW_C0;
                        break;
#endif /* PARANOID */
                }
        setcc(f);
        if (c & COMP_Denormal) {
                return denormal_operand() < 0;
        }
        return 0;
}

/*---------------------------------------------------------------------------*/

void fcom_st(void)
{
        /* fcom st(i) */
        compare_st_st(FPU_rm);
}

void fcompst(void)
{
        /* fcomp st(i) */
        if (!compare_st_st(FPU_rm))
                FPU_pop();
}

void fcompp(void)
{
        /* fcompp */
        if (FPU_rm != 1) {
                FPU_illegal();
                return;
        }
        if (!compare_st_st(1))
                poppop();
}

void fucom_(void)
{
        /* fucom st(i) */
        compare_u_st_st(FPU_rm);

}

void fucomp(void)
{
        /* fucomp st(i) */
        if (!compare_u_st_st(FPU_rm))
                FPU_pop();
}

void fucompp(void)
{
        /* fucompp */
        if (FPU_rm == 1) {
                if (!compare_u_st_st(1))
                        poppop();
        } else
                FPU_illegal();
}

/* [<][>][^][v][top][bottom][index][help] */

[funini.com] -> [kei@sodan] -> Kernel Reading