|  | /*---------------------------------------------------------------------------+ | 
|  | |  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(); | 
|  | } |