Contributors: 6
Author Tokens Token Proportion Commits Commit Proportion
Linus Torvalds (pre-git) 951 66.64% 13 68.42%
Ralph Siemsen 369 25.86% 2 10.53%
Richard Purdie 79 5.54% 1 5.26%
Linus Torvalds 23 1.61% 1 5.26%
Russell King 3 0.21% 1 5.26%
Thomas Gleixner 2 0.14% 1 5.26%
Total 1427 19

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
// SPDX-License-Identifier: GPL-2.0-or-later
/*
    NetWinder Floating Point Emulator
    (c) Rebel.COM, 1998,1999
    (c) Philip Blundell, 1999, 2001

    Direct questions, comments to Scott Bambrough <scottb@netwinder.org>

*/

#include "fpa11.h"
#include "fpopcode.h"
#include "fpa11.inl"
#include "fpmodule.h"
#include "fpmodule.inl"
#include "softfloat.h"

unsigned int PerformFLT(const unsigned int opcode);
unsigned int PerformFIX(const unsigned int opcode);

static unsigned int PerformComparison(const unsigned int opcode);

unsigned int EmulateCPRT(const unsigned int opcode)
{

	if (opcode & 0x800000) {
		/* This is some variant of a comparison (PerformComparison
		   will sort out which one).  Since most of the other CPRT
		   instructions are oddball cases of some sort or other it
		   makes sense to pull this out into a fast path.  */
		return PerformComparison(opcode);
	}

	/* Hint to GCC that we'd like a jump table rather than a load of CMPs */
	switch ((opcode & 0x700000) >> 20) {
	case FLT_CODE >> 20:
		return PerformFLT(opcode);
		break;
	case FIX_CODE >> 20:
		return PerformFIX(opcode);
		break;

	case WFS_CODE >> 20:
		writeFPSR(readRegister(getRd(opcode)));
		break;
	case RFS_CODE >> 20:
		writeRegister(getRd(opcode), readFPSR());
		break;

	default:
		return 0;
	}

	return 1;
}

unsigned int PerformFLT(const unsigned int opcode)
{
	FPA11 *fpa11 = GET_FPA11();
	struct roundingData roundData;

	roundData.mode = SetRoundingMode(opcode);
	roundData.precision = SetRoundingPrecision(opcode);
	roundData.exception = 0;

	switch (opcode & MASK_ROUNDING_PRECISION) {
	case ROUND_SINGLE:
		{
			fpa11->fType[getFn(opcode)] = typeSingle;
			fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
		}
		break;

	case ROUND_DOUBLE:
		{
			fpa11->fType[getFn(opcode)] = typeDouble;
			fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
		}
		break;

#ifdef CONFIG_FPE_NWFPE_XP
	case ROUND_EXTENDED:
		{
			fpa11->fType[getFn(opcode)] = typeExtended;
			fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
		}
		break;
#endif

	default:
		return 0;
	}

	if (roundData.exception)
		float_raise(roundData.exception);

	return 1;
}

unsigned int PerformFIX(const unsigned int opcode)
{
	FPA11 *fpa11 = GET_FPA11();
	unsigned int Fn = getFm(opcode);
	struct roundingData roundData;

	roundData.mode = SetRoundingMode(opcode);
	roundData.precision = SetRoundingPrecision(opcode);
	roundData.exception = 0;

	switch (fpa11->fType[Fn]) {
	case typeSingle:
		{
			writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
		}
		break;

	case typeDouble:
		{
			writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
		}
		break;

#ifdef CONFIG_FPE_NWFPE_XP
	case typeExtended:
		{
			writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
		}
		break;
#endif

	default:
		return 0;
	}

	if (roundData.exception)
		float_raise(roundData.exception);

	return 1;
}

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