|  | /* | 
|  | NetWinder Floating Point Emulator | 
|  | (c) Rebel.COM, 1998,1999 | 
|  | (c) Philip Blundell, 1999 | 
|  |  | 
|  | 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, see <http://www.gnu.org/licenses/>. | 
|  | */ | 
|  |  | 
|  | #include "fpa11.h" | 
|  | #include "fpu/softfloat.h" | 
|  | #include "fpopcode.h" | 
|  | #include "fpa11.inl" | 
|  | //#include "fpmodule.h" | 
|  | //#include "fpmodule.inl" | 
|  |  | 
|  | 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) | 
|  | { | 
|  | unsigned int nRc = 1; | 
|  |  | 
|  | //printk("EmulateCPRT(0x%08x)\n",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: nRc = PerformFLT(opcode); break; | 
|  | case  FIX_CODE >> 20: nRc = PerformFIX(opcode); break; | 
|  |  | 
|  | case  WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break; | 
|  | case  RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break; | 
|  |  | 
|  | #if 0    /* We currently have no use for the FPCR, so there's no point | 
|  | in emulating it. */ | 
|  | case  WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode))); | 
|  | case  RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break; | 
|  | #endif | 
|  |  | 
|  | default: nRc = 0; | 
|  | } | 
|  |  | 
|  | return nRc; | 
|  | } | 
|  |  | 
|  | unsigned int PerformFLT(const unsigned int opcode) | 
|  | { | 
|  | FPA11 *fpa11 = GET_FPA11(); | 
|  |  | 
|  | unsigned int nRc = 1; | 
|  | SetRoundingMode(opcode); | 
|  |  | 
|  | switch (opcode & MASK_ROUNDING_PRECISION) | 
|  | { | 
|  | case ROUND_SINGLE: | 
|  | { | 
|  | fpa11->fType[getFn(opcode)] = typeSingle; | 
|  | fpa11->fpreg[getFn(opcode)].fSingle = | 
|  | int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case ROUND_DOUBLE: | 
|  | { | 
|  | fpa11->fType[getFn(opcode)] = typeDouble; | 
|  | fpa11->fpreg[getFn(opcode)].fDouble = | 
|  | int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case ROUND_EXTENDED: | 
|  | { | 
|  | fpa11->fType[getFn(opcode)] = typeExtended; | 
|  | fpa11->fpreg[getFn(opcode)].fExtended = | 
|  | int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status); | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: nRc = 0; | 
|  | } | 
|  |  | 
|  | return nRc; | 
|  | } | 
|  |  | 
|  | unsigned int PerformFIX(const unsigned int opcode) | 
|  | { | 
|  | FPA11 *fpa11 = GET_FPA11(); | 
|  | unsigned int nRc = 1; | 
|  | unsigned int Fn = getFm(opcode); | 
|  |  | 
|  | SetRoundingMode(opcode); | 
|  |  | 
|  | switch (fpa11->fType[Fn]) | 
|  | { | 
|  | case typeSingle: | 
|  | { | 
|  | writeRegister(getRd(opcode), | 
|  | float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status)); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case typeDouble: | 
|  | { | 
|  | //printf("F%d is 0x%" PRIx64 "\n",Fn,fpa11->fpreg[Fn].fDouble); | 
|  | writeRegister(getRd(opcode), | 
|  | float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status)); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case typeExtended: | 
|  | { | 
|  | writeRegister(getRd(opcode), | 
|  | floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status)); | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: nRc = 0; | 
|  | } | 
|  |  | 
|  | return nRc; | 
|  | } | 
|  |  | 
|  |  | 
|  | static __inline unsigned int | 
|  | PerformComparisonOperation(floatx80 Fn, floatx80 Fm) | 
|  | { | 
|  | FPA11 *fpa11 = GET_FPA11(); | 
|  | unsigned int flags = 0; | 
|  |  | 
|  | /* test for less than condition */ | 
|  | if (floatx80_lt(Fn,Fm, &fpa11->fp_status)) | 
|  | { | 
|  | flags |= CC_NEGATIVE; | 
|  | } | 
|  |  | 
|  | /* test for equal condition */ | 
|  | if (floatx80_eq_quiet(Fn,Fm, &fpa11->fp_status)) | 
|  | { | 
|  | flags |= CC_ZERO; | 
|  | } | 
|  |  | 
|  | /* test for greater than or equal condition */ | 
|  | if (floatx80_lt(Fm,Fn, &fpa11->fp_status)) | 
|  | { | 
|  | flags |= CC_CARRY; | 
|  | } | 
|  |  | 
|  | writeConditionCodes(flags); | 
|  | 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, Fm; | 
|  | floatx80 rFn, rFm; | 
|  | int e_flag = opcode & 0x400000;	/* 1 if CxFE */ | 
|  | int n_flag = opcode & 0x200000;	/* 1 if CNxx */ | 
|  | unsigned int flags = 0; | 
|  |  | 
|  | //printk("PerformComparison(0x%08x)\n",opcode); | 
|  |  | 
|  | Fn = getFn(opcode); | 
|  | Fm = getFm(opcode); | 
|  |  | 
|  | /* 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_any_nan(fpa11->fpreg[Fn].fSingle)) | 
|  | goto unordered; | 
|  | rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status); | 
|  | break; | 
|  |  | 
|  | case typeDouble: | 
|  | //printk("double.\n"); | 
|  | if (float64_is_any_nan(fpa11->fpreg[Fn].fDouble)) | 
|  | goto unordered; | 
|  | rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status); | 
|  | break; | 
|  |  | 
|  | case typeExtended: | 
|  | //printk("extended.\n"); | 
|  | if (floatx80_is_any_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_any_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_any_nan(fpa11->fpreg[Fm].fSingle)) | 
|  | goto unordered; | 
|  | rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status); | 
|  | break; | 
|  |  | 
|  | case typeDouble: | 
|  | //printk("double.\n"); | 
|  | if (float64_is_any_nan(fpa11->fpreg[Fm].fDouble)) | 
|  | goto unordered; | 
|  | rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status); | 
|  | break; | 
|  |  | 
|  | case typeExtended: | 
|  | //printk("extended.\n"); | 
|  | if (floatx80_is_any_nan(fpa11->fpreg[Fm].fExtended)) | 
|  | goto unordered; | 
|  | rFm = fpa11->fpreg[Fm].fExtended; | 
|  | break; | 
|  |  | 
|  | default: return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (n_flag) | 
|  | { | 
|  | rFm.high ^= 0x8000; | 
|  | } | 
|  |  | 
|  | return PerformComparisonOperation(rFn,rFm); | 
|  |  | 
|  | 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, &fpa11->fp_status); | 
|  |  | 
|  | writeConditionCodes(flags); | 
|  | return 1; | 
|  | } |