static void fyl2x()

in math-emu/fpu_trig.c [1026:1195]


static void fyl2x(FPU_REG *st0_ptr, u_char st0_tag)
{
	FPU_REG *st1_ptr = &st(1), exponent;
	u_char st1_tag = FPU_gettagi(1);
	u_char sign;
	int e, tag;

	clear_C1();

	if ((st0_tag == TAG_Valid) && (st1_tag == TAG_Valid)) {
	      both_valid:
		/* Both regs are Valid or Denormal */
		if (signpositive(st0_ptr)) {
			if (st0_tag == TW_Denormal)
				FPU_to_exp16(st0_ptr, st0_ptr);
			else
				/* Convert st(0) for internal use. */
				setexponent16(st0_ptr, exponent(st0_ptr));

			if ((st0_ptr->sigh == 0x80000000)
			    && (st0_ptr->sigl == 0)) {
				/* Special case. The result can be precise. */
				u_char esign;
				e = exponent16(st0_ptr);
				if (e >= 0) {
					exponent.sigh = e;
					esign = SIGN_POS;
				} else {
					exponent.sigh = -e;
					esign = SIGN_NEG;
				}
				exponent.sigl = 0;
				setexponent16(&exponent, 31);
				tag = FPU_normalize_nuo(&exponent);
				stdexp(&exponent);
				setsign(&exponent, esign);
				tag =
				    FPU_mul(&exponent, tag, 1, FULL_PRECISION);
				if (tag >= 0)
					FPU_settagi(1, tag);
			} else {
				/* The usual case */
				sign = getsign(st1_ptr);
				if (st1_tag == TW_Denormal)
					FPU_to_exp16(st1_ptr, st1_ptr);
				else
					/* Convert st(1) for internal use. */
					setexponent16(st1_ptr,
						      exponent(st1_ptr));
				poly_l2(st0_ptr, st1_ptr, sign);
			}
		} else {
			/* negative */
			if (arith_invalid(1) < 0)
				return;
		}

		FPU_pop();

		return;
	}

	if (st0_tag == TAG_Special)
		st0_tag = FPU_Special(st0_ptr);
	if (st1_tag == TAG_Special)
		st1_tag = FPU_Special(st1_ptr);

	if ((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty)) {
		FPU_stack_underflow_pop(1);
		return;
	} else if ((st0_tag <= TW_Denormal) && (st1_tag <= TW_Denormal)) {
		if (st0_tag == TAG_Zero) {
			if (st1_tag == TAG_Zero) {
				/* Both args zero is invalid */
				if (arith_invalid(1) < 0)
					return;
			} else {
				u_char sign;
				sign = getsign(st1_ptr) ^ SIGN_NEG;
				if (FPU_divide_by_zero(1, sign) < 0)
					return;

				setsign(st1_ptr, sign);
			}
		} else if (st1_tag == TAG_Zero) {
			/* st(1) contains zero, st(0) valid <> 0 */
			/* Zero is the valid answer */
			sign = getsign(st1_ptr);

			if (signnegative(st0_ptr)) {
				/* log(negative) */
				if (arith_invalid(1) < 0)
					return;
			} else if ((st0_tag == TW_Denormal)
				   && (denormal_operand() < 0))
				return;
			else {
				if (exponent(st0_ptr) < 0)
					sign ^= SIGN_NEG;

				FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
				setsign(st1_ptr, sign);
			}
		} else {
			/* One or both operands are denormals. */
			if (denormal_operand() < 0)
				return;
			goto both_valid;
		}
	} else if ((st0_tag == TW_NaN) || (st1_tag == TW_NaN)) {
		if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
			return;
	}
	/* One or both arg must be an infinity */
	else if (st0_tag == TW_Infinity) {
		if ((signnegative(st0_ptr)) || (st1_tag == TAG_Zero)) {
			/* log(-infinity) or 0*log(infinity) */
			if (arith_invalid(1) < 0)
				return;
		} else {
			u_char sign = getsign(st1_ptr);

			if ((st1_tag == TW_Denormal)
			    && (denormal_operand() < 0))
				return;

			FPU_copy_to_reg1(&CONST_INF, TAG_Special);
			setsign(st1_ptr, sign);
		}
	}
	/* st(1) must be infinity here */
	else if (((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal))
		 && (signpositive(st0_ptr))) {
		if (exponent(st0_ptr) >= 0) {
			if ((exponent(st0_ptr) == 0) &&
			    (st0_ptr->sigh == 0x80000000) &&
			    (st0_ptr->sigl == 0)) {
				/* st(0) holds 1.0 */
				/* infinity*log(1) */
				if (arith_invalid(1) < 0)
					return;
			}
			/* else st(0) is positive and > 1.0 */
		} else {
			/* st(0) is positive and < 1.0 */

			if ((st0_tag == TW_Denormal)
			    && (denormal_operand() < 0))
				return;

			changesign(st1_ptr);
		}
	} else {
		/* st(0) must be zero or negative */
		if (st0_tag == TAG_Zero) {
			/* This should be invalid, but a real 80486 is happy with it. */

#ifndef PECULIAR_486
			sign = getsign(st1_ptr);
			if (FPU_divide_by_zero(1, sign) < 0)
				return;
#endif /* PECULIAR_486 */

			changesign(st1_ptr);
		} else if (arith_invalid(1) < 0)	/* log(negative) */
			return;
	}

	FPU_pop();
}