123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- // SPDX-License-Identifier: GPL-2.0
- /*---------------------------------------------------------------------------+
- | 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, 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;
- default:
- #ifdef PARANOID
- EXCEPTION(EX_INTERNAL | 0x121);
- #endif /* PARANOID */
- f = SW_C3 | SW_C2 | SW_C0;
- break;
- }
- setcc(f);
- if (c & COMP_Denormal) {
- return denormal_operand() < 0;
- }
- return 0;
- }
- static int compare_st_st(int nr)
- {
- int f, 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;
- default:
- #ifdef PARANOID
- EXCEPTION(EX_INTERNAL | 0x122);
- #endif /* PARANOID */
- f = SW_C3 | SW_C2 | SW_C0;
- break;
- }
- setcc(f);
- if (c & COMP_Denormal) {
- return denormal_operand() < 0;
- }
- return 0;
- }
- static int compare_i_st_st(int nr)
- {
- int f, c;
- FPU_REG *st_ptr;
- if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
- FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
- /* Stack fault */
- EXCEPTION(EX_StackUnder);
- return !(control_word & CW_Invalid);
- }
- partial_status &= ~SW_C0;
- st_ptr = &st(nr);
- c = compare(st_ptr, FPU_gettagi(nr));
- if (c & COMP_NaN) {
- FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
- EXCEPTION(EX_Invalid);
- return !(control_word & CW_Invalid);
- }
- switch (c & 7) {
- case COMP_A_lt_B:
- f = X86_EFLAGS_CF;
- break;
- case COMP_A_eq_B:
- f = X86_EFLAGS_ZF;
- break;
- case COMP_A_gt_B:
- f = 0;
- break;
- case COMP_No_Comp:
- f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
- break;
- default:
- #ifdef PARANOID
- EXCEPTION(EX_INTERNAL | 0x122);
- #endif /* PARANOID */
- f = 0;
- break;
- }
- FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | 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;
- }
- static int compare_ui_st_st(int nr)
- {
- int f = 0, c;
- FPU_REG *st_ptr;
- if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
- FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
- /* Stack fault */
- EXCEPTION(EX_StackUnder);
- return !(control_word & CW_Invalid);
- }
- partial_status &= ~SW_C0;
- st_ptr = &st(nr);
- c = compare(st_ptr, FPU_gettagi(nr));
- if (c & COMP_NaN) {
- FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
- 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;
- }
- switch (c & 7) {
- case COMP_A_lt_B:
- f = X86_EFLAGS_CF;
- break;
- case COMP_A_eq_B:
- f = X86_EFLAGS_ZF;
- break;
- case COMP_A_gt_B:
- f = 0;
- break;
- case COMP_No_Comp:
- f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
- break;
- #ifdef PARANOID
- default:
- EXCEPTION(EX_INTERNAL | 0x123);
- f = 0;
- break;
- #endif /* PARANOID */
- }
- FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | 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();
- }
- /* P6+ compare-to-EFLAGS ops */
- void fcomi_(void)
- {
- /* fcomi st(i) */
- compare_i_st_st(FPU_rm);
- }
- void fcomip(void)
- {
- /* fcomip st(i) */
- if (!compare_i_st_st(FPU_rm))
- FPU_pop();
- }
- void fucomi_(void)
- {
- /* fucomi st(i) */
- compare_ui_st_st(FPU_rm);
- }
- void fucomip(void)
- {
- /* fucomip st(i) */
- if (!compare_ui_st_st(FPU_rm))
- FPU_pop();
- }
|