| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 | /*    NetWinder Floating Point Emulator    (c) Rebel.COM, 1998,1999    (c) Philip Blundell, 1999, 2001    Direct questions, comments to Scott Bambrough <scottb@netwinder.org>    This program is free software; you can redistribute it and/or modify    it under the terms of the GNU General Public License as published by    the Free Software Foundation; either version 2 of the License, or    (at your option) any later version.    This program is distributed in the hope that it will be useful,    but WITHOUT ANY WARRANTY; without even the implied warranty of    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    GNU General Public License for more details.    You should have received a copy of the GNU General Public License    along with this program; if not, write to the Free Software    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#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;}
 |