1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 NetWinder Floating Point Emulator
4 (c) Rebel.COM, 1998,1999
5 (c) Philip Blundell, 1999, 2001
6
7 Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
8
9*/
10
11#include "fpa11.h"
12#include "fpopcode.h"
13#include "fpa11.inl"
14#include "fpmodule.h"
15#include "fpmodule.inl"
16#include "softfloat.h"
17
18unsigned int PerformFLT(const unsigned int opcode);
19unsigned int PerformFIX(const unsigned int opcode);
20
21static unsigned int PerformComparison(const unsigned int opcode);
22
23unsigned int EmulateCPRT(const unsigned int opcode)
24{
25
26 if (opcode & 0x800000) {
27 /* This is some variant of a comparison (PerformComparison
28 will sort out which one). Since most of the other CPRT
29 instructions are oddball cases of some sort or other it
30 makes sense to pull this out into a fast path. */
31 return PerformComparison(opcode);
32 }
33
34 /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
35 switch ((opcode & 0x700000) >> 20) {
36 case FLT_CODE >> 20:
37 return PerformFLT(opcode);
38 break;
39 case FIX_CODE >> 20:
40 return PerformFIX(opcode);
41 break;
42
43 case WFS_CODE >> 20:
44 writeFPSR(reg: readRegister(getRd(opcode)));
45 break;
46 case RFS_CODE >> 20:
47 writeRegister(getRd(opcode), val: readFPSR());
48 break;
49
50 default:
51 return 0;
52 }
53
54 return 1;
55}
56
57unsigned int PerformFLT(const unsigned int opcode)
58{
59 FPA11 *fpa11 = GET_FPA11();
60 struct roundingData roundData;
61
62 roundData.mode = SetRoundingMode(opcode);
63 roundData.precision = SetRoundingPrecision(opcode);
64 roundData.exception = 0;
65
66 switch (opcode & MASK_ROUNDING_PRECISION) {
67 case ROUND_SINGLE:
68 {
69 fpa11->fType[getFn(opcode)] = typeSingle;
70 fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
71 }
72 break;
73
74 case ROUND_DOUBLE:
75 {
76 fpa11->fType[getFn(opcode)] = typeDouble;
77 fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
78 }
79 break;
80
81#ifdef CONFIG_FPE_NWFPE_XP
82 case ROUND_EXTENDED:
83 {
84 fpa11->fType[getFn(opcode)] = typeExtended;
85 fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
86 }
87 break;
88#endif
89
90 default:
91 return 0;
92 }
93
94 if (roundData.exception)
95 float_raise(roundData.exception);
96
97 return 1;
98}
99
100unsigned int PerformFIX(const unsigned int opcode)
101{
102 FPA11 *fpa11 = GET_FPA11();
103 unsigned int Fn = getFm(opcode);
104 struct roundingData roundData;
105
106 roundData.mode = SetRoundingMode(opcode);
107 roundData.precision = SetRoundingPrecision(opcode);
108 roundData.exception = 0;
109
110 switch (fpa11->fType[Fn]) {
111 case typeSingle:
112 {
113 writeRegister(getRd(opcode), val: float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
114 }
115 break;
116
117 case typeDouble:
118 {
119 writeRegister(getRd(opcode), val: float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
120 }
121 break;
122
123#ifdef CONFIG_FPE_NWFPE_XP
124 case typeExtended:
125 {
126 writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
127 }
128 break;
129#endif
130
131 default:
132 return 0;
133 }
134
135 if (roundData.exception)
136 float_raise(roundData.exception);
137
138 return 1;
139}
140
141/* This instruction sets the flags N, Z, C, V in the FPSR. */
142static unsigned int PerformComparison(const unsigned int opcode)
143{
144 FPA11 *fpa11 = GET_FPA11();
145 unsigned int Fn = getFn(opcode), Fm = getFm(opcode);
146 int e_flag = opcode & 0x400000; /* 1 if CxFE */
147 int n_flag = opcode & 0x200000; /* 1 if CNxx */
148 unsigned int flags = 0;
149
150#ifdef CONFIG_FPE_NWFPE_XP
151 floatx80 rFn, rFm;
152
153 /* Check for unordered condition and convert all operands to 80-bit
154 format.
155 ?? Might be some mileage in avoiding this conversion if possible.
156 Eg, if both operands are 32-bit, detect this and do a 32-bit
157 comparison (cheaper than an 80-bit one). */
158 switch (fpa11->fType[Fn]) {
159 case typeSingle:
160 //printk("single.\n");
161 if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
162 goto unordered;
163 rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
164 break;
165
166 case typeDouble:
167 //printk("double.\n");
168 if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
169 goto unordered;
170 rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
171 break;
172
173 case typeExtended:
174 //printk("extended.\n");
175 if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
176 goto unordered;
177 rFn = fpa11->fpreg[Fn].fExtended;
178 break;
179
180 default:
181 return 0;
182 }
183
184 if (CONSTANT_FM(opcode)) {
185 //printk("Fm is a constant: #%d.\n",Fm);
186 rFm = getExtendedConstant(Fm);
187 if (floatx80_is_nan(rFm))
188 goto unordered;
189 } else {
190 //printk("Fm = r%d which contains a ",Fm);
191 switch (fpa11->fType[Fm]) {
192 case typeSingle:
193 //printk("single.\n");
194 if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
195 goto unordered;
196 rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
197 break;
198
199 case typeDouble:
200 //printk("double.\n");
201 if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
202 goto unordered;
203 rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
204 break;
205
206 case typeExtended:
207 //printk("extended.\n");
208 if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
209 goto unordered;
210 rFm = fpa11->fpreg[Fm].fExtended;
211 break;
212
213 default:
214 return 0;
215 }
216 }
217
218 if (n_flag)
219 rFm.high ^= 0x8000;
220
221 /* test for less than condition */
222 if (floatx80_lt(rFn, rFm))
223 flags |= CC_NEGATIVE;
224
225 /* test for equal condition */
226 if (floatx80_eq(rFn, rFm))
227 flags |= CC_ZERO;
228
229 /* test for greater than or equal condition */
230 if (floatx80_lt(rFm, rFn))
231 flags |= CC_CARRY;
232
233#else
234 if (CONSTANT_FM(opcode)) {
235 /* Fm is a constant. Do the comparison in whatever precision
236 Fn happens to be stored in. */
237 if (fpa11->fType[Fn] == typeSingle) {
238 float32 rFm = getSingleConstant(nIndex: Fm);
239 float32 rFn = fpa11->fpreg[Fn].fSingle;
240
241 if (float32_is_nan(a: rFn))
242 goto unordered;
243
244 if (n_flag)
245 rFm ^= 0x80000000;
246
247 /* test for less than condition */
248 if (float32_lt_nocheck(a: rFn, b: rFm))
249 flags |= CC_NEGATIVE;
250
251 /* test for equal condition */
252 if (float32_eq_nocheck(a: rFn, b: rFm))
253 flags |= CC_ZERO;
254
255 /* test for greater than or equal condition */
256 if (float32_lt_nocheck(a: rFm, b: rFn))
257 flags |= CC_CARRY;
258 } else {
259 float64 rFm = getDoubleConstant(nIndex: Fm);
260 float64 rFn = fpa11->fpreg[Fn].fDouble;
261
262 if (float64_is_nan(a: rFn))
263 goto unordered;
264
265 if (n_flag)
266 rFm ^= 0x8000000000000000ULL;
267
268 /* test for less than condition */
269 if (float64_lt_nocheck(a: rFn, b: rFm))
270 flags |= CC_NEGATIVE;
271
272 /* test for equal condition */
273 if (float64_eq_nocheck(a: rFn, b: rFm))
274 flags |= CC_ZERO;
275
276 /* test for greater than or equal condition */
277 if (float64_lt_nocheck(a: rFm, b: rFn))
278 flags |= CC_CARRY;
279 }
280 } else {
281 /* Both operands are in registers. */
282 if (fpa11->fType[Fn] == typeSingle
283 && fpa11->fType[Fm] == typeSingle) {
284 float32 rFm = fpa11->fpreg[Fm].fSingle;
285 float32 rFn = fpa11->fpreg[Fn].fSingle;
286
287 if (float32_is_nan(a: rFn)
288 || float32_is_nan(a: rFm))
289 goto unordered;
290
291 if (n_flag)
292 rFm ^= 0x80000000;
293
294 /* test for less than condition */
295 if (float32_lt_nocheck(a: rFn, b: rFm))
296 flags |= CC_NEGATIVE;
297
298 /* test for equal condition */
299 if (float32_eq_nocheck(a: rFn, b: rFm))
300 flags |= CC_ZERO;
301
302 /* test for greater than or equal condition */
303 if (float32_lt_nocheck(a: rFm, b: rFn))
304 flags |= CC_CARRY;
305 } else {
306 /* Promote 32-bit operand to 64 bits. */
307 float64 rFm, rFn;
308
309 rFm = (fpa11->fType[Fm] == typeSingle) ?
310 float32_to_float64(fpa11->fpreg[Fm].fSingle)
311 : fpa11->fpreg[Fm].fDouble;
312
313 rFn = (fpa11->fType[Fn] == typeSingle) ?
314 float32_to_float64(fpa11->fpreg[Fn].fSingle)
315 : fpa11->fpreg[Fn].fDouble;
316
317 if (float64_is_nan(a: rFn)
318 || float64_is_nan(a: rFm))
319 goto unordered;
320
321 if (n_flag)
322 rFm ^= 0x8000000000000000ULL;
323
324 /* test for less than condition */
325 if (float64_lt_nocheck(a: rFn, b: rFm))
326 flags |= CC_NEGATIVE;
327
328 /* test for equal condition */
329 if (float64_eq_nocheck(a: rFn, b: rFm))
330 flags |= CC_ZERO;
331
332 /* test for greater than or equal condition */
333 if (float64_lt_nocheck(a: rFm, b: rFn))
334 flags |= CC_CARRY;
335 }
336 }
337
338#endif
339
340 writeConditionCodes(val: flags);
341
342 return 1;
343
344 unordered:
345 /* ?? The FPA data sheet is pretty vague about this, in particular
346 about whether the non-E comparisons can ever raise exceptions.
347 This implementation is based on a combination of what it says in
348 the data sheet, observation of how the Acorn emulator actually
349 behaves (and how programs expect it to) and guesswork. */
350 flags |= CC_OVERFLOW;
351 flags &= ~(CC_ZERO | CC_NEGATIVE);
352
353 if (BIT_AC & readFPSR())
354 flags |= CC_CARRY;
355
356 if (e_flag)
357 float_raise(float_flag_invalid);
358
359 writeConditionCodes(val: flags);
360 return 1;
361}
362

source code of linux/arch/arm/nwfpe/fpa11_cprt.c