diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-22 23:05:57 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-22 23:05:57 +0000 |
commit | 51d3b7814cdccef9188240fe0cbd8d97ff2c7470 (patch) | |
tree | 5cbb01d0323d4f63ade66bdf48ba4a91aaa6df16 /arch/arm/nwfpe/fpa11_cprt.c | |
parent | 52273a23c9a84336b93a35e4847fc88fac7eb0e4 (diff) |
Merge with Linux 2.3.7.
WARNING: 2.3.7 is known to eat filesystems for breakfast and little
children for lunch, so if you try this on your machine make backups
first ...
Diffstat (limited to 'arch/arm/nwfpe/fpa11_cprt.c')
-rw-r--r-- | arch/arm/nwfpe/fpa11_cprt.c | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/arch/arm/nwfpe/fpa11_cprt.c b/arch/arm/nwfpe/fpa11_cprt.c new file mode 100644 index 000000000..bfe13ba1f --- /dev/null +++ b/arch/arm/nwfpe/fpa11_cprt.c @@ -0,0 +1,313 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + (c) Philip Blundell, 1999 + + Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com> + + 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 "config.h" +#include "milieu.h" +#include "softfloat.h" +#include "fpopcode.h" +#include "fpa11.h" +#include "fpa11.inl" +#include "fpmodule.h" +#include "fpmodule.inl" + +extern flag floatx80_is_nan(floatx80); +extern flag float64_is_nan( float64); +extern flag float32_is_nan( float32); + +void SetRoundingMode(const unsigned int opcode); + +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; + + //fp_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 + /* ?? Not at all sure about the mode checks here. Linux never + calls the emulator from a non-USR fault but we always run in SVC + mode. Is there even any point trying to emulate the way FPA11 + behaves in this respect? + + No - and I quote: 'The FPCR may only be present in some + implementations: it is there to control the hardware in an + implementation-specific manner, ... The user mode of the + ARM is not permitted to use this register, and the WFC and + RFC instructions will trap if tried from user mode.' + Therefore, we do not provide the RFC and WFC instructions. + (rmk, 3/05/1999) + */ + case WFC_CODE >> 20: + { + int mode = 0; + __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode)); + nRc = (0x13 == mode) ? 1 : 0; /* in SVC processor mode? */ + if (nRc) writeFPCR(readRegister(getRd(opcode))); + } + break; + + case RFC_CODE >> 20: + { + int mode = 0; + __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode)); + nRc = (0x13 == mode) ? 1 : 0; /* in SVC processor mode? */ + if (nRc) writeRegister(getRd(opcode),readFPCR()); break; + } + break; +#endif + + default: nRc = 0; + } + + return nRc; +} + +unsigned int PerformFLT(const unsigned int opcode) +{ + unsigned int nRc = 1; + SetRoundingMode(opcode); + SetRoundingPrecision(opcode); + + switch (opcode & MASK_ROUNDING_PRECISION) + { + case ROUND_SINGLE: + { + fpa11->fpreg[getFn(opcode)].fType = typeSingle; + fpa11->fpreg[getFn(opcode)].fValue.fSingle = + int32_to_float32(readRegister(getRd(opcode))); + } + break; + + case ROUND_DOUBLE: + { + fpa11->fpreg[getFn(opcode)].fType = typeDouble; + fpa11->fpreg[getFn(opcode)].fValue.fDouble = + int32_to_float64(readRegister(getRd(opcode))); + } + break; + + case ROUND_EXTENDED: + { + fpa11->fpreg[getFn(opcode)].fType = typeExtended; + fpa11->fpreg[getFn(opcode)].fValue.fExtended = + int32_to_floatx80(readRegister(getRd(opcode))); + } + break; + + default: nRc = 0; + } + + return nRc; +} + +unsigned int PerformFIX(const unsigned int opcode) +{ + unsigned int nRc = 1; + unsigned int Fn = getFm(opcode); + + SetRoundingMode(opcode); + + switch (fpa11->fpreg[Fn].fType) + { + case typeSingle: + { + writeRegister(getRd(opcode), + float32_to_int32(fpa11->fpreg[Fn].fValue.fSingle)); + } + break; + + case typeDouble: + { + writeRegister(getRd(opcode), + float64_to_int32(fpa11->fpreg[Fn].fValue.fDouble)); + } + break; + + case typeExtended: + { + writeRegister(getRd(opcode), + floatx80_to_int32(fpa11->fpreg[Fn].fValue.fExtended)); + } + break; + + default: nRc = 0; + } + + return nRc; +} + + +static unsigned int __inline__ +PerformComparisonOperation(floatx80 Fn, floatx80 Fm) +{ + unsigned int flags = 0; + + /* test for less than condition */ + if (floatx80_lt(Fn,Fm)) + { + flags |= CC_NEGATIVE; + } + + /* test for equal condition */ + if (floatx80_eq(Fn,Fm)) + { + flags |= CC_ZERO; + } + + /* test for greater than or equal condition */ + if (floatx80_lt(Fm,Fn)) + { + 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) +{ + 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; + + //fp_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->fpreg[Fn].fType) + { + case typeSingle: + //fp_printk("single.\n"); + if (float32_is_nan(fpa11->fpreg[Fn].fValue.fSingle)) + goto unordered; + rFn = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle); + break; + + case typeDouble: + //fp_printk("double.\n"); + if (float64_is_nan(fpa11->fpreg[Fn].fValue.fDouble)) + goto unordered; + rFn = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble); + break; + + case typeExtended: + //fp_printk("extended.\n"); + if (floatx80_is_nan(fpa11->fpreg[Fn].fValue.fExtended)) + goto unordered; + rFn = fpa11->fpreg[Fn].fValue.fExtended; + break; + + default: return 0; + } + + if (CONSTANT_FM(opcode)) + { + //fp_printk("Fm is a constant: #%d.\n",Fm); + rFm = getExtendedConstant(Fm); + if (floatx80_is_nan(rFm)) + goto unordered; + } + else + { + //fp_printk("Fm = r%d which contains a ",Fm); + switch (fpa11->fpreg[Fm].fType) + { + case typeSingle: + //fp_printk("single.\n"); + if (float32_is_nan(fpa11->fpreg[Fm].fValue.fSingle)) + goto unordered; + rFm = float32_to_floatx80(fpa11->fpreg[Fm].fValue.fSingle); + break; + + case typeDouble: + //fp_printk("double.\n"); + if (float64_is_nan(fpa11->fpreg[Fm].fValue.fDouble)) + goto unordered; + rFm = float64_to_floatx80(fpa11->fpreg[Fm].fValue.fDouble); + break; + + case typeExtended: + //fp_printk("extended.\n"); + if (floatx80_is_nan(fpa11->fpreg[Fm].fValue.fExtended)) + goto unordered; + rFm = fpa11->fpreg[Fm].fValue.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; + + if (BIT_AC & readFPSR()) flags |= CC_CARRY; + + if (e_flag) float_raise(float_flag_invalid); + + writeConditionCodes(flags); + return 1; +} |