summaryrefslogtreecommitdiffstats
path: root/arch/arm/nwfpe/fpa11_cprt.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-06-22 23:05:57 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-06-22 23:05:57 +0000
commit51d3b7814cdccef9188240fe0cbd8d97ff2c7470 (patch)
tree5cbb01d0323d4f63ade66bdf48ba4a91aaa6df16 /arch/arm/nwfpe/fpa11_cprt.c
parent52273a23c9a84336b93a35e4847fc88fac7eb0e4 (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.c313
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;
+}