1/*
2    NetWinder Floating Point Emulator
3    (c) Rebel.COM, 1998,1999
4    (c) Philip Blundell, 1999
5
6    Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21*/
22
23#include "fpa11.h"
24#include "milieu.h"
25#include "softfloat.h"
26#include "fpopcode.h"
27#include "fpa11.inl"
28#include "fpmodule.h"
29#include "fpmodule.inl"
30
31extern flag floatx80_is_nan(floatx80);
32extern flag float64_is_nan( float64);
33extern flag float32_is_nan( float32);
34
35void SetRoundingMode(const unsigned int opcode);
36
37unsigned int PerformFLT(const unsigned int opcode);
38unsigned int PerformFIX(const unsigned int opcode);
39
40static unsigned int
41PerformComparison(const unsigned int opcode);
42
43unsigned int EmulateCPRT(const unsigned int opcode)
44{
45  unsigned int nRc = 1;
46
47  //printk("EmulateCPRT(0x%08x)\n",opcode);
48
49  if (opcode & 0x800000)
50  {
51     /* This is some variant of a comparison (PerformComparison will
52	sort out which one).  Since most of the other CPRT
53	instructions are oddball cases of some sort or other it makes
54	sense to pull this out into a fast path.  */
55     return PerformComparison(opcode);
56  }
57
58  /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
59  switch ((opcode & 0x700000) >> 20)
60  {
61    case  FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
62    case  FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
63
64    case  WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
65    case  RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
66
67#if 0    /* We currently have no use for the FPCR, so there's no point
68	    in emulating it. */
69    case  WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode)));
70    case  RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break;
71#endif
72
73    default: nRc = 0;
74  }
75
76  return nRc;
77}
78
79unsigned int PerformFLT(const unsigned int opcode)
80{
81   FPA11 *fpa11 = GET_FPA11();
82
83   unsigned int nRc = 1;
84   SetRoundingMode(opcode);
85
86   switch (opcode & MASK_ROUNDING_PRECISION)
87   {
88      case ROUND_SINGLE:
89      {
90        fpa11->fType[getFn(opcode)] = typeSingle;
91        fpa11->fpreg[getFn(opcode)].fSingle =
92	   int32_to_float32(readRegister(getRd(opcode)));
93      }
94      break;
95
96      case ROUND_DOUBLE:
97      {
98        fpa11->fType[getFn(opcode)] = typeDouble;
99        fpa11->fpreg[getFn(opcode)].fDouble =
100            int32_to_float64(readRegister(getRd(opcode)));
101      }
102      break;
103
104      case ROUND_EXTENDED:
105      {
106        fpa11->fType[getFn(opcode)] = typeExtended;
107        fpa11->fpreg[getFn(opcode)].fExtended =
108	   int32_to_floatx80(readRegister(getRd(opcode)));
109      }
110      break;
111
112      default: nRc = 0;
113  }
114
115  return nRc;
116}
117
118unsigned int PerformFIX(const unsigned int opcode)
119{
120   FPA11 *fpa11 = GET_FPA11();
121   unsigned int nRc = 1;
122   unsigned int Fn = getFm(opcode);
123
124   SetRoundingMode(opcode);
125
126   switch (fpa11->fType[Fn])
127   {
128      case typeSingle:
129      {
130         writeRegister(getRd(opcode),
131	               float32_to_int32(fpa11->fpreg[Fn].fSingle));
132      }
133      break;
134
135      case typeDouble:
136      {
137         writeRegister(getRd(opcode),
138	               float64_to_int32(fpa11->fpreg[Fn].fDouble));
139      }
140      break;
141
142      case typeExtended:
143      {
144         writeRegister(getRd(opcode),
145	               floatx80_to_int32(fpa11->fpreg[Fn].fExtended));
146      }
147      break;
148
149      default: nRc = 0;
150  }
151
152  return nRc;
153}
154
155
156static unsigned int __inline__
157PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
158{
159   unsigned int flags = 0;
160
161   /* test for less than condition */
162   if (floatx80_lt(Fn,Fm))
163   {
164      flags |= CC_NEGATIVE;
165   }
166
167   /* test for equal condition */
168   if (floatx80_eq(Fn,Fm))
169   {
170      flags |= CC_ZERO;
171   }
172
173   /* test for greater than or equal condition */
174   if (floatx80_lt(Fm,Fn))
175   {
176      flags |= CC_CARRY;
177   }
178
179   writeConditionCodes(flags);
180   return 1;
181}
182
183/* This instruction sets the flags N, Z, C, V in the FPSR. */
184
185static unsigned int PerformComparison(const unsigned int opcode)
186{
187   FPA11 *fpa11 = GET_FPA11();
188   unsigned int Fn, Fm;
189   floatx80 rFn, rFm;
190   int e_flag = opcode & 0x400000;	/* 1 if CxFE */
191   int n_flag = opcode & 0x200000;	/* 1 if CNxx */
192   unsigned int flags = 0;
193
194   //printk("PerformComparison(0x%08x)\n",opcode);
195
196   Fn = getFn(opcode);
197   Fm = getFm(opcode);
198
199   /* Check for unordered condition and convert all operands to 80-bit
200      format.
201      ?? Might be some mileage in avoiding this conversion if possible.
202      Eg, if both operands are 32-bit, detect this and do a 32-bit
203      comparison (cheaper than an 80-bit one).  */
204   switch (fpa11->fType[Fn])
205   {
206      case typeSingle:
207        //printk("single.\n");
208	if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
209	   goto unordered;
210        rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
211      break;
212
213      case typeDouble:
214        //printk("double.\n");
215	if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
216	   goto unordered;
217        rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
218      break;
219
220      case typeExtended:
221        //printk("extended.\n");
222	if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
223	   goto unordered;
224        rFn = fpa11->fpreg[Fn].fExtended;
225      break;
226
227      default: return 0;
228   }
229
230   if (CONSTANT_FM(opcode))
231   {
232     //printk("Fm is a constant: #%d.\n",Fm);
233     rFm = getExtendedConstant(Fm);
234     if (floatx80_is_nan(rFm))
235        goto unordered;
236   }
237   else
238   {
239     //printk("Fm = r%d which contains a ",Fm);
240      switch (fpa11->fType[Fm])
241      {
242         case typeSingle:
243           //printk("single.\n");
244	   if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
245	      goto unordered;
246           rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
247         break;
248
249         case typeDouble:
250           //printk("double.\n");
251	   if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
252	      goto unordered;
253           rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
254         break;
255
256         case typeExtended:
257           //printk("extended.\n");
258	   if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
259	      goto unordered;
260           rFm = fpa11->fpreg[Fm].fExtended;
261         break;
262
263         default: return 0;
264      }
265   }
266
267   if (n_flag)
268   {
269      rFm.high ^= 0x8000;
270   }
271
272   return PerformComparisonOperation(rFn,rFm);
273
274 unordered:
275   /* ?? The FPA data sheet is pretty vague about this, in particular
276      about whether the non-E comparisons can ever raise exceptions.
277      This implementation is based on a combination of what it says in
278      the data sheet, observation of how the Acorn emulator actually
279      behaves (and how programs expect it to) and guesswork.  */
280   flags |= CC_OVERFLOW;
281   flags &= ~(CC_ZERO | CC_NEGATIVE);
282
283   if (BIT_AC & readFPSR()) flags |= CC_CARRY;
284
285   if (e_flag) float_raise(float_flag_invalid);
286
287   writeConditionCodes(flags);
288   return 1;
289}
290