xref: /openbmc/linux/arch/arm/nwfpe/fpa11_cpdo.c (revision 74ba9207)
174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds     NetWinder Floating Point Emulator
41da177e4SLinus Torvalds     (c) Rebel.COM, 1998,1999
51da177e4SLinus Torvalds     (c) Philip Blundell, 2001
61da177e4SLinus Torvalds 
71da177e4SLinus Torvalds     Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #include "fpa11.h"
121da177e4SLinus Torvalds #include "fpopcode.h"
131da177e4SLinus Torvalds 
14f148af25SRichard Purdie unsigned int SingleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd);
15f148af25SRichard Purdie unsigned int DoubleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd);
16f148af25SRichard Purdie unsigned int ExtendedCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd);
171da177e4SLinus Torvalds 
EmulateCPDO(const unsigned int opcode)181da177e4SLinus Torvalds unsigned int EmulateCPDO(const unsigned int opcode)
191da177e4SLinus Torvalds {
201da177e4SLinus Torvalds 	FPA11 *fpa11 = GET_FPA11();
211da177e4SLinus Torvalds 	FPREG *rFd;
221da177e4SLinus Torvalds 	unsigned int nType, nDest, nRc;
23f148af25SRichard Purdie 	struct roundingData roundData;
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds 	/* Get the destination size.  If not valid let Linux perform
261da177e4SLinus Torvalds 	   an invalid instruction trap. */
271da177e4SLinus Torvalds 	nDest = getDestinationSize(opcode);
281da177e4SLinus Torvalds 	if (typeNone == nDest)
291da177e4SLinus Torvalds 		return 0;
301da177e4SLinus Torvalds 
31f148af25SRichard Purdie 	roundData.mode = SetRoundingMode(opcode);
32f148af25SRichard Purdie 	roundData.precision = SetRoundingPrecision(opcode);
33f148af25SRichard Purdie 	roundData.exception = 0;
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds 	/* Compare the size of the operands in Fn and Fm.
361da177e4SLinus Torvalds 	   Choose the largest size and perform operations in that size,
371da177e4SLinus Torvalds 	   in order to make use of all the precision of the operands.
381da177e4SLinus Torvalds 	   If Fm is a constant, we just grab a constant of a size
391da177e4SLinus Torvalds 	   matching the size of the operand in Fn. */
401da177e4SLinus Torvalds 	if (MONADIC_INSTRUCTION(opcode))
411da177e4SLinus Torvalds 		nType = nDest;
421da177e4SLinus Torvalds 	else
431da177e4SLinus Torvalds 		nType = fpa11->fType[getFn(opcode)];
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds 	if (!CONSTANT_FM(opcode)) {
461da177e4SLinus Torvalds 		register unsigned int Fm = getFm(opcode);
471da177e4SLinus Torvalds 		if (nType < fpa11->fType[Fm]) {
481da177e4SLinus Torvalds 			nType = fpa11->fType[Fm];
491da177e4SLinus Torvalds 		}
501da177e4SLinus Torvalds 	}
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds 	rFd = &fpa11->fpreg[getFd(opcode)];
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 	switch (nType) {
551da177e4SLinus Torvalds 	case typeSingle:
56f148af25SRichard Purdie 		nRc = SingleCPDO(&roundData, opcode, rFd);
571da177e4SLinus Torvalds 		break;
581da177e4SLinus Torvalds 	case typeDouble:
59f148af25SRichard Purdie 		nRc = DoubleCPDO(&roundData, opcode, rFd);
601da177e4SLinus Torvalds 		break;
611da177e4SLinus Torvalds #ifdef CONFIG_FPE_NWFPE_XP
621da177e4SLinus Torvalds 	case typeExtended:
63f148af25SRichard Purdie 		nRc = ExtendedCPDO(&roundData, opcode, rFd);
641da177e4SLinus Torvalds 		break;
651da177e4SLinus Torvalds #endif
661da177e4SLinus Torvalds 	default:
671da177e4SLinus Torvalds 		nRc = 0;
681da177e4SLinus Torvalds 	}
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 	/* The CPDO functions used to always set the destination type
711da177e4SLinus Torvalds 	   to be the same as their working size. */
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 	if (nRc != 0) {
741da177e4SLinus Torvalds 		/* If the operation succeeded, check to see if the result in the
751da177e4SLinus Torvalds 		   destination register is the correct size.  If not force it
761da177e4SLinus Torvalds 		   to be. */
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 		fpa11->fType[getFd(opcode)] = nDest;
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds #ifdef CONFIG_FPE_NWFPE_XP
811da177e4SLinus Torvalds 		if (nDest != nType) {
821da177e4SLinus Torvalds 			switch (nDest) {
831da177e4SLinus Torvalds 			case typeSingle:
841da177e4SLinus Torvalds 				{
851da177e4SLinus Torvalds 					if (typeDouble == nType)
86f148af25SRichard Purdie 						rFd->fSingle = float64_to_float32(&roundData, rFd->fDouble);
871da177e4SLinus Torvalds 					else
88f148af25SRichard Purdie 						rFd->fSingle = floatx80_to_float32(&roundData, rFd->fExtended);
891da177e4SLinus Torvalds 				}
901da177e4SLinus Torvalds 				break;
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds 			case typeDouble:
931da177e4SLinus Torvalds 				{
941da177e4SLinus Torvalds 					if (typeSingle == nType)
951da177e4SLinus Torvalds 						rFd->fDouble = float32_to_float64(rFd->fSingle);
961da177e4SLinus Torvalds 					else
97f148af25SRichard Purdie 						rFd->fDouble = floatx80_to_float64(&roundData, rFd->fExtended);
981da177e4SLinus Torvalds 				}
991da177e4SLinus Torvalds 				break;
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 			case typeExtended:
1021da177e4SLinus Torvalds 				{
1031da177e4SLinus Torvalds 					if (typeSingle == nType)
1041da177e4SLinus Torvalds 						rFd->fExtended = float32_to_floatx80(rFd->fSingle);
1051da177e4SLinus Torvalds 					else
1061da177e4SLinus Torvalds 						rFd->fExtended = float64_to_floatx80(rFd->fDouble);
1071da177e4SLinus Torvalds 				}
1081da177e4SLinus Torvalds 				break;
1091da177e4SLinus Torvalds 			}
1101da177e4SLinus Torvalds 		}
1111da177e4SLinus Torvalds #else
1121da177e4SLinus Torvalds 		if (nDest != nType) {
1131da177e4SLinus Torvalds 			if (nDest == typeSingle)
114f148af25SRichard Purdie 				rFd->fSingle = float64_to_float32(&roundData, rFd->fDouble);
1151da177e4SLinus Torvalds 			else
1161da177e4SLinus Torvalds 				rFd->fDouble = float32_to_float64(rFd->fSingle);
1171da177e4SLinus Torvalds 		}
1181da177e4SLinus Torvalds #endif
1191da177e4SLinus Torvalds 	}
1201da177e4SLinus Torvalds 
121f148af25SRichard Purdie 	if (roundData.exception)
122f148af25SRichard Purdie 		float_raise(roundData.exception);
123f148af25SRichard Purdie 
1241da177e4SLinus Torvalds 	return nRc;
1251da177e4SLinus Torvalds }
126