/*---------------------------------------------------------------------------+ | reg_compare.c | | | | Compare two floating point registers | | | | Copyright (C) 1992,1993,1994 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail billm@vaxc.cc.monash.edu.au | | | | | +---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------+ | 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" int compare(FPU_REG const *b) { int diff; char st0_tag; FPU_REG *st0_ptr; st0_ptr = &st(0); st0_tag = st0_ptr->tag; if ( st0_tag | b->tag ) { if ( st0_tag == TW_Zero ) { if ( b->tag == TW_Zero ) return COMP_A_eq_B; if ( b->tag == TW_Valid ) { return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) #ifdef DENORM_OPERAND | ((b->exp <= EXP_UNDER) ? COMP_Denormal : 0) #endif DENORM_OPERAND ; } } else if ( b->tag == TW_Zero ) { if ( st0_tag == TW_Valid ) { return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) #ifdef DENORM_OPERAND | ((st0_ptr->exp <= EXP_UNDER ) ? COMP_Denormal : 0 ) #endif DENORM_OPERAND ; } } if ( st0_tag == TW_Infinity ) { if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) ) { return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) #ifdef DENORM_OPERAND | (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ? COMP_Denormal : 0 ) #endif DENORM_OPERAND ; } else if ( b->tag == TW_Infinity ) { /* The 80486 book says that infinities can be equal! */ return (st0_ptr->sign == b->sign) ? COMP_A_eq_B : ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); } /* Fall through to the NaN code */ } else if ( b->tag == TW_Infinity ) { if ( (st0_tag == TW_Valid) || (st0_tag == TW_Zero) ) { return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) #ifdef DENORM_OPERAND | (((st0_tag == TW_Valid) && (st0_ptr->exp <= EXP_UNDER)) ? COMP_Denormal : 0) #endif DENORM_OPERAND ; } /* 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) || (b->tag == TW_NaN) ) { if ( ((st0_tag == TW_NaN) && !(st0_ptr->sigh & 0x40000000)) || ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)) ) /* At least one arg is a signaling NaN */ return COMP_No_Comp | COMP_SNaN | COMP_NaN; else /* Neither is a signaling NaN */ return COMP_No_Comp | COMP_NaN; } EXCEPTION(EX_Invalid); } #ifdef PARANOID if (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid); if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid); #endif PARANOID if (st0_ptr->sign != b->sign) { return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) #ifdef DENORM_OPERAND | ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? COMP_Denormal : 0) #endif DENORM_OPERAND ; } diff = st0_ptr->exp - b->exp; 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_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) #ifdef DENORM_OPERAND | ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? COMP_Denormal : 0) #endif DENORM_OPERAND ; } if ( diff < 0 ) { return ((st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) #ifdef DENORM_OPERAND | ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? COMP_Denormal : 0) #endif DENORM_OPERAND ; } return COMP_A_eq_B #ifdef DENORM_OPERAND | ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? COMP_Denormal : 0) #endif DENORM_OPERAND ; } /* This function requires that st(0) is not empty */ int compare_st_data(FPU_REG const *loaded_data) { int f, c; c = compare(loaded_data); 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(); } return 0; } static int compare_st_st(int nr) { int f, c; if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) { setcc(SW_C3 | SW_C2 | SW_C0); /* Stack fault */ EXCEPTION(EX_StackUnder); return !(control_word & CW_Invalid); } c = compare(&st(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(); } return 0; } static int compare_u_st_st(int nr) { int f, c; if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) { setcc(SW_C3 | SW_C2 | SW_C0); /* Stack fault */ EXCEPTION(EX_StackUnder); return !(control_word & CW_Invalid); } c = compare(&st(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(); } return 0; } /*---------------------------------------------------------------------------*/ void fcom_st() { /* fcom st(i) */ compare_st_st(FPU_rm); } void fcompst() { /* fcomp st(i) */ if ( !compare_st_st(FPU_rm) ) pop(); } void fcompp() { /* fcompp */ if (FPU_rm != 1) { FPU_illegal(); return; } if ( !compare_st_st(1) ) poppop(); } void fucom_() { /* fucom st(i) */ compare_u_st_st(FPU_rm); } void fucomp() { /* fucomp st(i) */ if ( !compare_u_st_st(FPU_rm) ) pop(); } void fucompp() { /* fucompp */ if (FPU_rm == 1) { if ( !compare_u_st_st(1) ) poppop(); } else FPU_illegal(); }