patch-2.3.7 linux/arch/arm/nwfpe/fpa11_cprt.c

Next file: linux/arch/arm/nwfpe/fpmodule.c
Previous file: linux/arch/arm/nwfpe/fpa11_cpdt.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpa11_cprt.c linux/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;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)