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