INLINE void SET_CONDITION_CODES(m68000_base_device *m68k, floatx80 reg) { // UINT64 *regi; // regi = (UINT64 *)® REG_FPSR(m68k) &= ~(FPCC_N|FPCC_Z|FPCC_I|FPCC_NAN); // sign flag if (reg.high & 0x8000) { REG_FPSR(m68k) |= FPCC_N; } // zero flag if (((reg.high & 0x7fff) == 0) && ((reg.low<<1) == 0)) { REG_FPSR(m68k) |= FPCC_Z; } // infinity flag if (((reg.high & 0x7fff) == 0x7fff) && ((reg.low<<1) == 0)) { REG_FPSR(m68k) |= FPCC_I; } // NaN flag if (floatx80_is_nan(reg)) { REG_FPSR(m68k) |= FPCC_NAN; } }
static floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b) { int aIsNaN = floatx80_is_nan(a); int aIsSignalingNaN = floatx80_is_signaling_nan(a); int bIsNaN = floatx80_is_nan(b); int bIsSignalingNaN = floatx80_is_signaling_nan(b); a.low |= U64(0xC000000000000000); b.low |= U64(0xC000000000000000); if (aIsSignalingNaN | bIsSignalingNaN) float_raise(float_flag_invalid); if (aIsSignalingNaN) { if (bIsSignalingNaN) goto returnLargerSignificand; return bIsNaN ? b : a; } else if (aIsNaN) { if (bIsSignalingNaN | ! bIsNaN) return a; returnLargerSignificand: if (a.low < b.low) return b; if (b.low < a.low) return a; return (a.high < b.high) ? a : b; } else { return b; } }
BX_INSF_TYPE BX_CPP_AttrRegparmN(1) BX_CPU_C::FCOM_DOUBLE_REAL(bxInstruction_c *i) { BX_CPU_THIS_PTR prepareFPU(i); int pop_stack = i->nnn() & 1, rc; RMAddr(i) = BX_CPU_CALL_METHODR(i->ResolveModrm, (i)); float64 load_reg = read_virtual_qword(i->seg(), RMAddr(i)); BX_CPU_THIS_PTR FPU_update_last_instruction(i); clear_C1(); if (IS_TAG_EMPTY(0)) { FPU_exception(FPU_EX_Stack_Underflow); setcc(FPU_SW_C0|FPU_SW_C2|FPU_SW_C3); if(BX_CPU_THIS_PTR the_i387.is_IA_masked()) { if (pop_stack) BX_CPU_THIS_PTR the_i387.FPU_pop(); } BX_NEXT_INSTR(i); } float_status_t status = FPU_pre_exception_handling(BX_CPU_THIS_PTR the_i387.get_control_word()); floatx80 a = BX_READ_FPU_REG(0); if (floatx80_is_nan(a) || floatx80_is_unsupported(a) || float64_is_nan(load_reg)) { rc = float_relation_unordered; float_raise(status, float_flag_invalid); } else { rc = floatx80_compare(a, float64_to_floatx80(load_reg, status), status); } setcc(status_word_flags_fpu_compare(rc)); if (! FPU_exception(status.float_exception_flags)) { if (pop_stack) BX_CPU_THIS_PTR the_i387.FPU_pop(); } BX_NEXT_INSTR(i); }
/* This instruction sets the flags N, Z, C, V in the FPSR. */ static unsigned int PerformComparison(const unsigned int opcode) { FPA11 *fpa11 = GET_FPA11(); unsigned int Fn = getFn(opcode), Fm = getFm(opcode); int e_flag = opcode & 0x400000; /* 1 if CxFE */ int n_flag = opcode & 0x200000; /* 1 if CNxx */ unsigned int flags = 0; #ifdef CONFIG_FPE_NWFPE_XP floatx80 rFn, rFm; /* Check for unordered condition and convert all operands to 80-bit format. ?? Might be some mileage in avoiding this conversion if possible. Eg, if both operands are 32-bit, detect this and do a 32-bit comparison (cheaper than an 80-bit one). */ switch (fpa11->fType[Fn]) { case typeSingle: //printk("single.\n"); if (float32_is_nan(fpa11->fpreg[Fn].fSingle)) goto unordered; rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle); break; case typeDouble: //printk("double.\n"); if (float64_is_nan(fpa11->fpreg[Fn].fDouble)) goto unordered; rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); break; case typeExtended: //printk("extended.\n"); if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended)) goto unordered; rFn = fpa11->fpreg[Fn].fExtended; break; default: return 0; } if (CONSTANT_FM(opcode)) { //printk("Fm is a constant: #%d.\n",Fm); rFm = getExtendedConstant(Fm); if (floatx80_is_nan(rFm)) goto unordered; } else { //printk("Fm = r%d which contains a ",Fm); switch (fpa11->fType[Fm]) { case typeSingle: //printk("single.\n"); if (float32_is_nan(fpa11->fpreg[Fm].fSingle)) goto unordered; rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle); break; case typeDouble: //printk("double.\n"); if (float64_is_nan(fpa11->fpreg[Fm].fDouble)) goto unordered; rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble); break; case typeExtended: //printk("extended.\n"); if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended)) goto unordered; rFm = fpa11->fpreg[Fm].fExtended; break; default: return 0; } } if (n_flag) rFm.high ^= 0x8000; /* test for less than condition */ if (floatx80_lt(rFn, rFm)) flags |= CC_NEGATIVE; /* test for equal condition */ if (floatx80_eq(rFn, rFm)) flags |= CC_ZERO; /* test for greater than or equal condition */ if (floatx80_lt(rFm, rFn)) flags |= CC_CARRY; #else if (CONSTANT_FM(opcode)) { /* Fm is a constant. Do the comparison in whatever precision Fn happens to be stored in. */ if (fpa11->fType[Fn] == typeSingle) { float32 rFm = getSingleConstant(Fm); float32 rFn = fpa11->fpreg[Fn].fSingle; if (float32_is_nan(rFn)) goto unordered; if (n_flag) rFm ^= 0x80000000; /* test for less than condition */ if (float32_lt_nocheck(rFn, rFm)) flags |= CC_NEGATIVE; /* test for equal condition */ if (float32_eq_nocheck(rFn, rFm)) flags |= CC_ZERO; /* test for greater than or equal condition */ if (float32_lt_nocheck(rFm, rFn)) flags |= CC_CARRY; } else { float64 rFm = getDoubleConstant(Fm); float64 rFn = fpa11->fpreg[Fn].fDouble; if (float64_is_nan(rFn)) goto unordered; if (n_flag) rFm ^= 0x8000000000000000ULL; /* test for less than condition */ if (float64_lt_nocheck(rFn, rFm)) flags |= CC_NEGATIVE; /* test for equal condition */ if (float64_eq_nocheck(rFn, rFm)) flags |= CC_ZERO; /* test for greater than or equal condition */ if (float64_lt_nocheck(rFm, rFn)) flags |= CC_CARRY; } } else { /* Both operands are in registers. */ if (fpa11->fType[Fn] == typeSingle && fpa11->fType[Fm] == typeSingle) { float32 rFm = fpa11->fpreg[Fm].fSingle; float32 rFn = fpa11->fpreg[Fn].fSingle; if (float32_is_nan(rFn) || float32_is_nan(rFm)) goto unordered; if (n_flag) rFm ^= 0x80000000; /* test for less than condition */ if (float32_lt_nocheck(rFn, rFm)) flags |= CC_NEGATIVE; /* test for equal condition */ if (float32_eq_nocheck(rFn, rFm)) flags |= CC_ZERO; /* test for greater than or equal condition */ if (float32_lt_nocheck(rFm, rFn)) flags |= CC_CARRY; } else { /* Promote 32-bit operand to 64 bits. */ float64 rFm, rFn; rFm = (fpa11->fType[Fm] == typeSingle) ? float32_to_float64(fpa11->fpreg[Fm].fSingle) : fpa11->fpreg[Fm].fDouble; rFn = (fpa11->fType[Fn] == typeSingle) ? float32_to_float64(fpa11->fpreg[Fn].fSingle) : fpa11->fpreg[Fn].fDouble; if (float64_is_nan(rFn) || float64_is_nan(rFm)) goto unordered; if (n_flag) rFm ^= 0x8000000000000000ULL; /* test for less than condition */ if (float64_lt_nocheck(rFn, rFm)) flags |= CC_NEGATIVE; /* test for equal condition */ if (float64_eq_nocheck(rFn, rFm)) flags |= CC_ZERO; /* test for greater than or equal condition */ if (float64_lt_nocheck(rFm, rFn)) flags |= CC_CARRY; } } #endif writeConditionCodes(flags); return 1; unordered: /* ?? The FPA data sheet is pretty vague about this, in particular about whether the non-E comparisons can ever raise exceptions. This implementation is based on a combination of what it says in the data sheet, observation of how the Acorn emulator actually behaves (and how programs expect it to) and guesswork. */ flags |= CC_OVERFLOW; flags &= ~(CC_ZERO | CC_NEGATIVE); if (BIT_AC & readFPSR()) flags |= CC_CARRY; if (e_flag) float_raise(float_flag_invalid); writeConditionCodes(flags); return 1; }