]>
Commit | Line | Data |
---|---|---|
487f79b7 | 1 | /*> cp1.c <*/ |
d3eb724f CD |
2 | /* MIPS Simulator FPU (CoProcessor 1) support. |
3 | Copyright (C) 2002 Free Software Foundation, Inc. | |
4 | Originally created by Cygnus Solutions, modified substially | |
5 | by Broadcom Corporation (SiByte). | |
6 | ||
7 | This file is part of GDB, the GNU debugger. | |
8 | ||
9 | This program is free software; you can redistribute it and/or modify | |
10 | it under the terms of the GNU General Public License as published by | |
11 | the Free Software Foundation; either version 2, or (at your option) | |
12 | any later version. | |
13 | ||
14 | This program is distributed in the hope that it will be useful, | |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | GNU General Public License for more details. | |
18 | ||
19 | You should have received a copy of the GNU General Public License along | |
20 | with this program; if not, write to the Free Software Foundation, Inc., | |
21 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |
22 | ||
23 | /* XXX: The following notice should be removed as soon as is practical: */ | |
487f79b7 CD |
24 | /* Floating Point Support for gdb MIPS simulators |
25 | ||
26 | This file is part of the MIPS sim | |
27 | ||
28 | THIS SOFTWARE IS NOT COPYRIGHTED | |
d3eb724f | 29 | (by Cygnus.) |
487f79b7 CD |
30 | |
31 | Cygnus offers the following for use in the public domain. Cygnus | |
32 | makes no warranty with regard to the software or it's performance | |
33 | and the user accepts the software "AS IS" with all faults. | |
34 | ||
35 | CYGNUS DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO | |
36 | THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
37 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
38 | ||
39 | (Originally, this code was in interp.c) | |
40 | */ | |
41 | ||
42 | #include "sim-main.h" | |
487f79b7 CD |
43 | |
44 | /* Within cp1.c we refer to sim_cpu directly. */ | |
45 | #define CPU cpu | |
18d8a52d | 46 | #define SD CPU_STATE(cpu) |
487f79b7 CD |
47 | |
48 | /*-- FPU support routines ---------------------------------------------------*/ | |
49 | ||
50 | /* Numbers are held in normalized form. The SINGLE and DOUBLE binary | |
bad673a9 CD |
51 | formats conform to ANSI/IEEE Std 754-1985. |
52 | ||
53 | SINGLE precision floating: | |
54 | seeeeeeeefffffffffffffffffffffff | |
55 | s = 1bit = sign | |
56 | e = 8bits = exponent | |
57 | f = 23bits = fraction | |
58 | ||
59 | SINGLE precision fixed: | |
60 | siiiiiiiiiiiiiiiiiiiiiiiiiiiiiii | |
61 | s = 1bit = sign | |
62 | i = 31bits = integer | |
63 | ||
64 | DOUBLE precision floating: | |
65 | seeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffff | |
66 | s = 1bit = sign | |
67 | e = 11bits = exponent | |
68 | f = 52bits = fraction | |
69 | ||
70 | DOUBLE precision fixed: | |
71 | siiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii | |
72 | s = 1bit = sign | |
73 | i = 63bits = integer | |
487f79b7 CD |
74 | */ |
75 | ||
37d146fa | 76 | /* Explicit QNaN values used when value required: */ |
487f79b7 CD |
77 | #define FPQNaN_SINGLE (0x7FBFFFFF) |
78 | #define FPQNaN_WORD (0x7FFFFFFF) | |
bad673a9 CD |
79 | #define FPQNaN_DOUBLE (UNSIGNED64 (0x7FF7FFFFFFFFFFFF)) |
80 | #define FPQNaN_LONG (UNSIGNED64 (0x7FFFFFFFFFFFFFFF)) | |
487f79b7 | 81 | |
07892c0b CD |
82 | static const char *fpu_format_name (FP_formats fmt); |
83 | #ifdef DEBUG | |
84 | static const char *fpu_rounding_mode_name (int rm); | |
85 | #endif | |
487f79b7 CD |
86 | |
87 | uword64 | |
18d8a52d | 88 | value_fpr (sim_cpu *cpu, |
487f79b7 CD |
89 | address_word cia, |
90 | int fpr, | |
91 | FP_formats fmt) | |
92 | { | |
93 | uword64 value = 0; | |
94 | int err = 0; | |
95 | ||
37d146fa | 96 | /* Treat unused register values, as fixed-point 64bit values: */ |
487f79b7 | 97 | if ((fmt == fmt_uninterpreted) || (fmt == fmt_unknown)) |
37d146fa | 98 | { |
487f79b7 | 99 | #if 1 |
37d146fa CD |
100 | /* If request to read data as "uninterpreted", then use the current |
101 | encoding: */ | |
102 | fmt = FPR_STATE[fpr]; | |
487f79b7 | 103 | #else |
37d146fa | 104 | fmt = fmt_long; |
487f79b7 | 105 | #endif |
37d146fa | 106 | } |
487f79b7 | 107 | |
37d146fa CD |
108 | /* For values not yet accessed, set to the desired format: */ |
109 | if (FPR_STATE[fpr] == fmt_uninterpreted) | |
110 | { | |
111 | FPR_STATE[fpr] = fmt; | |
487f79b7 | 112 | #ifdef DEBUG |
37d146fa CD |
113 | printf ("DBG: Register %d was fmt_uninterpreted. Now %s\n", fpr, |
114 | fpu_format_name (fmt)); | |
487f79b7 | 115 | #endif /* DEBUG */ |
487f79b7 | 116 | } |
37d146fa CD |
117 | if (fmt != FPR_STATE[fpr]) |
118 | { | |
18d8a52d | 119 | sim_io_eprintf (SD, "FPR %d (format %s) being accessed with format %s - setting to unknown (PC = 0x%s)\n", |
37d146fa CD |
120 | fpr, fpu_format_name (FPR_STATE[fpr]), |
121 | fpu_format_name (fmt), pr_addr (cia)); | |
122 | FPR_STATE[fpr] = fmt_unknown; | |
123 | } | |
487f79b7 | 124 | |
37d146fa CD |
125 | if (FPR_STATE[fpr] == fmt_unknown) |
126 | { | |
127 | /* Set QNaN value: */ | |
128 | switch (fmt) | |
129 | { | |
196496ed CD |
130 | case fmt_single: value = FPQNaN_SINGLE; break; |
131 | case fmt_double: value = FPQNaN_DOUBLE; break; | |
132 | case fmt_word: value = FPQNaN_WORD; break; | |
133 | case fmt_long: value = FPQNaN_LONG; break; | |
134 | default: err = -1; break; | |
37d146fa CD |
135 | } |
136 | } | |
137 | else if (SizeFGR () == 64) | |
138 | { | |
139 | switch (fmt) | |
140 | { | |
141 | case fmt_single: | |
142 | case fmt_word: | |
143 | value = (FGR[fpr] & 0xFFFFFFFF); | |
144 | break; | |
145 | ||
146 | case fmt_uninterpreted: | |
147 | case fmt_double: | |
148 | case fmt_long: | |
149 | value = FGR[fpr]; | |
150 | break; | |
151 | ||
152 | default: | |
153 | err = -1; | |
154 | break; | |
155 | } | |
156 | } | |
157 | else | |
158 | { | |
159 | switch (fmt) | |
160 | { | |
161 | case fmt_single: | |
162 | case fmt_word: | |
163 | value = (FGR[fpr] & 0xFFFFFFFF); | |
164 | break; | |
165 | ||
166 | case fmt_uninterpreted: | |
167 | case fmt_double: | |
168 | case fmt_long: | |
169 | if ((fpr & 1) == 0) | |
170 | { | |
196496ed | 171 | /* Even registers numbers only. */ |
487f79b7 | 172 | #ifdef DEBUG |
37d146fa CD |
173 | printf ("DBG: ValueFPR: FGR[%d] = %s, FGR[%d] = %s\n", |
174 | fpr + 1, pr_uword64 ((uword64) FGR[fpr+1]), | |
175 | fpr, pr_uword64 ((uword64) FGR[fpr])); | |
487f79b7 | 176 | #endif |
37d146fa CD |
177 | value = ((((uword64) FGR[fpr+1]) << 32) |
178 | | (FGR[fpr] & 0xFFFFFFFF)); | |
179 | } | |
180 | else | |
181 | { | |
182 | SignalException (ReservedInstruction, 0); | |
183 | } | |
184 | break; | |
185 | ||
e80fc152 | 186 | default: |
37d146fa CD |
187 | err = -1; |
188 | break; | |
189 | } | |
487f79b7 | 190 | } |
487f79b7 CD |
191 | |
192 | if (err) | |
37d146fa | 193 | SignalExceptionSimulatorFault ("Unrecognised FP format in ValueFPR ()"); |
487f79b7 CD |
194 | |
195 | #ifdef DEBUG | |
37d146fa CD |
196 | printf ("DBG: ValueFPR: fpr = %d, fmt = %s, value = 0x%s : PC = 0x%s : SizeFGR () = %d\n", |
197 | fpr, fpu_format_name (fmt), pr_uword64 (value), pr_addr (cia), | |
198 | SizeFGR ()); | |
487f79b7 CD |
199 | #endif /* DEBUG */ |
200 | ||
37d146fa | 201 | return (value); |
487f79b7 CD |
202 | } |
203 | ||
204 | void | |
18d8a52d | 205 | store_fpr (sim_cpu *cpu, |
487f79b7 CD |
206 | address_word cia, |
207 | int fpr, | |
208 | FP_formats fmt, | |
209 | uword64 value) | |
210 | { | |
211 | int err = 0; | |
212 | ||
213 | #ifdef DEBUG | |
37d146fa CD |
214 | printf ("DBG: StoreFPR: fpr = %d, fmt = %s, value = 0x%s : PC = 0x%s : SizeFGR () = %d, \n", |
215 | fpr, fpu_format_name (fmt), pr_uword64 (value), pr_addr (cia), | |
216 | SizeFGR ()); | |
487f79b7 CD |
217 | #endif /* DEBUG */ |
218 | ||
37d146fa CD |
219 | if (SizeFGR () == 64) |
220 | { | |
221 | switch (fmt) | |
222 | { | |
223 | case fmt_uninterpreted_32: | |
224 | fmt = fmt_uninterpreted; | |
e80fc152 CD |
225 | case fmt_single: |
226 | case fmt_word: | |
37d146fa CD |
227 | if (STATE_VERBOSE_P (SD)) |
228 | sim_io_eprintf (SD, | |
229 | "Warning: PC 0x%s: interp.c store_fpr DEADCODE\n", | |
230 | pr_addr (cia)); | |
231 | FGR[fpr] = (((uword64) 0xDEADC0DE << 32) | (value & 0xFFFFFFFF)); | |
232 | FPR_STATE[fpr] = fmt; | |
233 | break; | |
234 | ||
235 | case fmt_uninterpreted_64: | |
236 | fmt = fmt_uninterpreted; | |
237 | case fmt_uninterpreted: | |
e80fc152 CD |
238 | case fmt_double: |
239 | case fmt_long: | |
37d146fa CD |
240 | FGR[fpr] = value; |
241 | FPR_STATE[fpr] = fmt; | |
242 | break; | |
243 | ||
e80fc152 | 244 | default: |
37d146fa CD |
245 | FPR_STATE[fpr] = fmt_unknown; |
246 | err = -1; | |
247 | break; | |
248 | } | |
487f79b7 | 249 | } |
37d146fa CD |
250 | else |
251 | { | |
252 | switch (fmt) | |
253 | { | |
254 | case fmt_uninterpreted_32: | |
255 | fmt = fmt_uninterpreted; | |
e80fc152 CD |
256 | case fmt_single: |
257 | case fmt_word: | |
487f79b7 | 258 | FGR[fpr] = (value & 0xFFFFFFFF); |
487f79b7 | 259 | FPR_STATE[fpr] = fmt; |
37d146fa CD |
260 | break; |
261 | ||
262 | case fmt_uninterpreted_64: | |
263 | fmt = fmt_uninterpreted; | |
264 | case fmt_uninterpreted: | |
e80fc152 CD |
265 | case fmt_double: |
266 | case fmt_long: | |
37d146fa CD |
267 | if ((fpr & 1) == 0) |
268 | { | |
196496ed | 269 | /* Even register numbers only. */ |
37d146fa CD |
270 | FGR[fpr+1] = (value >> 32); |
271 | FGR[fpr] = (value & 0xFFFFFFFF); | |
272 | FPR_STATE[fpr + 1] = fmt; | |
273 | FPR_STATE[fpr] = fmt; | |
274 | } | |
275 | else | |
276 | { | |
277 | FPR_STATE[fpr] = fmt_unknown; | |
278 | FPR_STATE[fpr + 1] = fmt_unknown; | |
279 | SignalException (ReservedInstruction, 0); | |
280 | } | |
281 | break; | |
282 | ||
e80fc152 | 283 | default: |
487f79b7 | 284 | FPR_STATE[fpr] = fmt_unknown; |
37d146fa CD |
285 | err = -1; |
286 | break; | |
487f79b7 | 287 | } |
487f79b7 | 288 | } |
487f79b7 CD |
289 | |
290 | if (err) | |
37d146fa | 291 | SignalExceptionSimulatorFault ("Unrecognised FP format in StoreFPR ()"); |
487f79b7 CD |
292 | |
293 | #ifdef DEBUG | |
37d146fa CD |
294 | printf ("DBG: StoreFPR: fpr[%d] = 0x%s (format %s)\n", |
295 | fpr, pr_uword64 (FGR[fpr]), fpu_format_name (fmt)); | |
487f79b7 CD |
296 | #endif /* DEBUG */ |
297 | ||
298 | return; | |
299 | } | |
300 | ||
cfe9ea23 CD |
301 | |
302 | /* CP1 control/status registers */ | |
303 | ||
304 | void | |
305 | test_fcsr (sim_cpu *cpu, | |
306 | address_word cia) | |
487f79b7 | 307 | { |
cfe9ea23 CD |
308 | unsigned int cause; |
309 | ||
310 | cause = (FCSR & fcsr_CAUSE_mask) >> fcsr_CAUSE_shift; | |
311 | if ((cause & ((FCSR & fcsr_ENABLES_mask) >> fcsr_ENABLES_shift)) != 0 | |
312 | || (cause & (1 << UO))) | |
487f79b7 | 313 | { |
cfe9ea23 | 314 | SignalExceptionFPE(); |
487f79b7 | 315 | } |
cfe9ea23 | 316 | } |
487f79b7 | 317 | |
cfe9ea23 CD |
318 | unsigned_word |
319 | value_fcr(sim_cpu *cpu, | |
320 | address_word cia, | |
321 | int fcr) | |
322 | { | |
323 | unsigned32 value = 0; | |
324 | ||
325 | switch (fcr) | |
326 | { | |
327 | case 0: /* FP Implementation and Revision Register */ | |
328 | value = FCR0; | |
329 | break; | |
330 | case 25: /* FP Condition Codes Register */ | |
331 | value = (FCR31 & fcsr_FCC_mask) >> fcsr_FCC_shift; | |
332 | value = (value & 0x1) | (value >> 1); /* close FCC gap */ | |
333 | break; | |
334 | case 26: /* FP Exceptions Register */ | |
335 | value = FCR31 & (fcsr_CAUSE_mask | fcsr_FLAGS_mask); | |
336 | break; | |
337 | case 28: /* FP Enables Register */ | |
338 | value = FCR31 & (fcsr_ENABLES_mask | fcsr_RM_mask); | |
339 | if (FCR31 & fcsr_FS) | |
340 | value |= 0x4; /* nonstandard FS bit */ | |
341 | break; | |
342 | case 31: /* FP Control/Status Register */ | |
343 | value = FCR31 & ~fcsr_ZERO_mask; | |
344 | break; | |
345 | } | |
346 | ||
347 | return (EXTEND32 (value)); | |
348 | } | |
349 | ||
350 | void | |
351 | store_fcr(sim_cpu *cpu, | |
352 | address_word cia, | |
353 | int fcr, | |
354 | unsigned_word value) | |
355 | { | |
356 | unsigned32 v; | |
487f79b7 | 357 | |
cfe9ea23 CD |
358 | v = VL4_8(value); |
359 | switch (fcr) | |
360 | { | |
361 | case 25: /* FP Condition Codes Register */ | |
362 | v = (v << 1) | (v & 0x1); /* adjust for FCC gap */ | |
363 | FCR31 &= ~fcsr_FCC_mask; | |
364 | FCR31 |= ((v << fcsr_FCC_shift) & fcsr_FCC_mask); | |
365 | break; | |
366 | case 26: /* FP Exceptions Register */ | |
367 | FCR31 &= ~(fcsr_CAUSE_mask | fcsr_FLAGS_mask); | |
368 | FCR31 |= (v & (fcsr_CAUSE_mask | fcsr_FLAGS_mask)); | |
369 | test_fcsr(cpu, cia); | |
370 | break; | |
371 | case 28: /* FP Enables Register */ | |
372 | if (v & 0x4) /* nonstandard FS bit */ | |
373 | v |= fcsr_FS; | |
374 | else | |
375 | v &= ~fcsr_FS; | |
376 | FCR31 &= (fcsr_FCC_mask | fcsr_CAUSE_mask | fcsr_FLAGS_mask); | |
377 | FCR31 |= (v & (fcsr_FS | fcsr_ENABLES_mask | fcsr_RM_mask)); | |
378 | test_fcsr(cpu, cia); | |
379 | break; | |
380 | case 31: /* FP Control/Status Register */ | |
381 | FCR31 = v & ~fcsr_ZERO_mask; | |
382 | test_fcsr(cpu, cia); | |
383 | break; | |
384 | } | |
487f79b7 CD |
385 | } |
386 | ||
cfe9ea23 CD |
387 | void |
388 | update_fcsr (sim_cpu *cpu, | |
389 | address_word cia, | |
390 | sim_fpu_status status) | |
487f79b7 | 391 | { |
cfe9ea23 | 392 | FCSR &= ~fcsr_CAUSE_mask; |
487f79b7 | 393 | |
cfe9ea23 CD |
394 | if (status != 0) |
395 | { | |
396 | unsigned int cause = 0; | |
397 | ||
398 | /* map between sim_fpu codes and MIPS FCSR */ | |
399 | if (status & (sim_fpu_status_invalid_snan | |
400 | | sim_fpu_status_invalid_isi | |
401 | | sim_fpu_status_invalid_idi | |
402 | | sim_fpu_status_invalid_zdz | |
403 | | sim_fpu_status_invalid_imz | |
404 | | sim_fpu_status_invalid_cmp | |
405 | | sim_fpu_status_invalid_sqrt | |
406 | | sim_fpu_status_invalid_cvi)) | |
407 | cause |= (1 << IO); | |
408 | if (status & sim_fpu_status_invalid_div0) | |
409 | cause |= (1 << DZ); | |
410 | if (status & sim_fpu_status_overflow) | |
411 | cause |= (1 << OF); | |
412 | if (status & sim_fpu_status_underflow) | |
413 | cause |= (1 << UF); | |
414 | if (status & sim_fpu_status_inexact) | |
415 | cause |= (1 << IR); | |
416 | #if 0 /* Not yet. */ | |
417 | /* Implicit clearing of other bits by unimplemented done by callers. */ | |
418 | if (status & sim_fpu_status_unimplemented) | |
419 | cause |= (1 << UO); | |
420 | #endif | |
487f79b7 | 421 | |
cfe9ea23 CD |
422 | FCSR |= (cause << fcsr_CAUSE_shift); |
423 | test_fcsr (cpu, cia); | |
424 | FCSR |= ((cause & ~(1 << UO)) << fcsr_FLAGS_shift); | |
425 | } | |
426 | return; | |
427 | } | |
428 | ||
577d8c4b CD |
429 | static sim_fpu_round |
430 | rounding_mode(int rm) | |
431 | { | |
432 | sim_fpu_round round; | |
433 | ||
434 | switch (rm) | |
435 | { | |
436 | case FP_RM_NEAREST: | |
437 | /* Round result to nearest representable value. When two | |
438 | representable values are equally near, round to the value | |
439 | that has a least significant bit of zero (i.e. is even). */ | |
440 | round = sim_fpu_round_near; | |
441 | break; | |
442 | case FP_RM_TOZERO: | |
443 | /* Round result to the value closest to, and not greater in | |
444 | magnitude than, the result. */ | |
445 | round = sim_fpu_round_zero; | |
446 | break; | |
447 | case FP_RM_TOPINF: | |
448 | /* Round result to the value closest to, and not less than, | |
449 | the result. */ | |
450 | round = sim_fpu_round_up; | |
451 | break; | |
452 | case FP_RM_TOMINF: | |
453 | /* Round result to the value closest to, and not greater than, | |
454 | the result. */ | |
455 | round = sim_fpu_round_down; | |
456 | break; | |
457 | default: | |
458 | round = 0; | |
459 | fprintf (stderr, "Bad switch\n"); | |
460 | abort (); | |
461 | } | |
462 | return round; | |
463 | } | |
464 | ||
cfe9ea23 CD |
465 | |
466 | /* Comparison operations. */ | |
467 | ||
468 | static sim_fpu_status | |
469 | fp_test(unsigned64 op1, | |
470 | unsigned64 op2, | |
471 | FP_formats fmt, | |
472 | int abs, | |
473 | int cond, | |
474 | int *condition) | |
475 | { | |
476 | sim_fpu wop1; | |
477 | sim_fpu wop2; | |
478 | sim_fpu_status status = 0; | |
479 | int less, equal, unordered; | |
487f79b7 | 480 | |
cfe9ea23 | 481 | /* The format type has already been checked: */ |
37d146fa | 482 | switch (fmt) |
487f79b7 | 483 | { |
37d146fa CD |
484 | case fmt_single: |
485 | { | |
37d146fa CD |
486 | sim_fpu_32to (&wop1, op1); |
487 | sim_fpu_32to (&wop2, op2); | |
37d146fa CD |
488 | break; |
489 | } | |
490 | case fmt_double: | |
491 | { | |
37d146fa CD |
492 | sim_fpu_64to (&wop1, op1); |
493 | sim_fpu_64to (&wop2, op2); | |
37d146fa CD |
494 | break; |
495 | } | |
496 | default: | |
497 | fprintf (stderr, "Bad switch\n"); | |
498 | abort (); | |
487f79b7 | 499 | } |
487f79b7 | 500 | |
cfe9ea23 CD |
501 | if (sim_fpu_is_nan (&wop1) || sim_fpu_is_nan (&wop2)) |
502 | { | |
503 | if ((cond & (1 << 3)) || | |
504 | sim_fpu_is_snan (&wop1) || sim_fpu_is_snan (&wop2)) | |
505 | status = sim_fpu_status_invalid_snan; | |
506 | less = 0; | |
507 | equal = 0; | |
508 | unordered = 1; | |
509 | } | |
510 | else | |
511 | { | |
512 | if (abs) | |
513 | { | |
514 | status |= sim_fpu_abs (&wop1, &wop1); | |
515 | status |= sim_fpu_abs (&wop2, &wop2); | |
516 | } | |
517 | equal = sim_fpu_is_eq (&wop1, &wop2); | |
518 | less = !equal && sim_fpu_is_lt (&wop1, &wop2); | |
519 | unordered = 0; | |
520 | } | |
521 | *condition = (((cond & (1 << 2)) && less) | |
522 | || ((cond & (1 << 1)) && equal) | |
523 | || ((cond & (1 << 0)) && unordered)); | |
524 | return status; | |
487f79b7 CD |
525 | } |
526 | ||
cfe9ea23 CD |
527 | void |
528 | fp_cmp(sim_cpu *cpu, | |
529 | address_word cia, | |
530 | unsigned64 op1, | |
531 | unsigned64 op2, | |
532 | FP_formats fmt, | |
533 | int abs, | |
534 | int cond, | |
535 | int cc) | |
487f79b7 | 536 | { |
cfe9ea23 | 537 | sim_fpu_status status = 0; |
487f79b7 | 538 | |
cfe9ea23 | 539 | /* The format type should already have been checked: */ |
37d146fa | 540 | switch (fmt) |
487f79b7 | 541 | { |
37d146fa | 542 | case fmt_single: |
37d146fa CD |
543 | case fmt_double: |
544 | { | |
cfe9ea23 CD |
545 | int result; |
546 | status = fp_test(op1, op2, fmt, abs, cond, &result); | |
547 | update_fcsr (cpu, cia, status); | |
548 | SETFCC (cc, result); | |
37d146fa CD |
549 | break; |
550 | } | |
551 | default: | |
cfe9ea23 | 552 | sim_io_eprintf (SD, "Bad switch\n"); |
37d146fa | 553 | abort (); |
487f79b7 | 554 | } |
487f79b7 CD |
555 | } |
556 | ||
487f79b7 | 557 | |
ba46ddd0 | 558 | /* Basic arithmetic operations. */ |
487f79b7 | 559 | |
ba46ddd0 CD |
560 | static unsigned64 |
561 | fp_unary(sim_cpu *cpu, | |
562 | address_word cia, | |
563 | int (*sim_fpu_op)(sim_fpu *, const sim_fpu *), | |
564 | unsigned64 op, | |
565 | FP_formats fmt) | |
487f79b7 | 566 | { |
ba46ddd0 CD |
567 | sim_fpu wop; |
568 | sim_fpu ans; | |
569 | unsigned64 result = 0; | |
487f79b7 | 570 | |
ba46ddd0 | 571 | /* The format type has already been checked: */ |
37d146fa | 572 | switch (fmt) |
487f79b7 | 573 | { |
37d146fa CD |
574 | case fmt_single: |
575 | { | |
37d146fa | 576 | unsigned32 res; |
ba46ddd0 CD |
577 | sim_fpu_32to (&wop, op); |
578 | (*sim_fpu_op) (&ans, &wop); | |
37d146fa CD |
579 | sim_fpu_to32 (&res, &ans); |
580 | result = res; | |
581 | break; | |
582 | } | |
583 | case fmt_double: | |
584 | { | |
37d146fa | 585 | unsigned64 res; |
ba46ddd0 CD |
586 | sim_fpu_64to (&wop, op); |
587 | (*sim_fpu_op) (&ans, &wop); | |
37d146fa CD |
588 | sim_fpu_to64 (&res, &ans); |
589 | result = res; | |
590 | break; | |
591 | } | |
592 | default: | |
ba46ddd0 | 593 | sim_io_eprintf (SD, "Bad switch\n"); |
37d146fa | 594 | abort (); |
487f79b7 | 595 | } |
487f79b7 | 596 | |
ba46ddd0 | 597 | return result; |
487f79b7 CD |
598 | } |
599 | ||
ba46ddd0 CD |
600 | static unsigned64 |
601 | fp_binary(sim_cpu *cpu, | |
602 | address_word cia, | |
603 | int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *), | |
604 | unsigned64 op1, | |
605 | unsigned64 op2, | |
606 | FP_formats fmt) | |
487f79b7 | 607 | { |
ba46ddd0 CD |
608 | sim_fpu wop1; |
609 | sim_fpu wop2; | |
610 | sim_fpu ans; | |
611 | unsigned64 result = 0; | |
487f79b7 | 612 | |
ba46ddd0 | 613 | /* The format type has already been checked: */ |
37d146fa | 614 | switch (fmt) |
487f79b7 | 615 | { |
37d146fa CD |
616 | case fmt_single: |
617 | { | |
37d146fa CD |
618 | unsigned32 res; |
619 | sim_fpu_32to (&wop1, op1); | |
620 | sim_fpu_32to (&wop2, op2); | |
ba46ddd0 | 621 | (*sim_fpu_op) (&ans, &wop1, &wop2); |
37d146fa CD |
622 | sim_fpu_to32 (&res, &ans); |
623 | result = res; | |
624 | break; | |
625 | } | |
626 | case fmt_double: | |
627 | { | |
37d146fa CD |
628 | unsigned64 res; |
629 | sim_fpu_64to (&wop1, op1); | |
630 | sim_fpu_64to (&wop2, op2); | |
ba46ddd0 | 631 | (*sim_fpu_op) (&ans, &wop1, &wop2); |
37d146fa CD |
632 | sim_fpu_to64 (&res, &ans); |
633 | result = res; | |
634 | break; | |
635 | } | |
636 | default: | |
ba46ddd0 | 637 | sim_io_eprintf (SD, "Bad switch\n"); |
37d146fa | 638 | abort (); |
487f79b7 | 639 | } |
487f79b7 | 640 | |
ba46ddd0 | 641 | return result; |
487f79b7 CD |
642 | } |
643 | ||
487f79b7 | 644 | |
ba46ddd0 CD |
645 | unsigned64 |
646 | fp_abs(sim_cpu *cpu, | |
647 | address_word cia, | |
648 | unsigned64 op, | |
649 | FP_formats fmt) | |
650 | { | |
651 | return fp_unary(cpu, cia, &sim_fpu_abs, op, fmt); | |
487f79b7 CD |
652 | } |
653 | ||
ba46ddd0 CD |
654 | unsigned64 |
655 | fp_neg(sim_cpu *cpu, | |
656 | address_word cia, | |
657 | unsigned64 op, | |
658 | FP_formats fmt) | |
487f79b7 | 659 | { |
ba46ddd0 | 660 | return fp_unary(cpu, cia, &sim_fpu_neg, op, fmt); |
487f79b7 CD |
661 | } |
662 | ||
ba46ddd0 CD |
663 | unsigned64 |
664 | fp_add(sim_cpu *cpu, | |
665 | address_word cia, | |
666 | unsigned64 op1, | |
667 | unsigned64 op2, | |
668 | FP_formats fmt) | |
487f79b7 | 669 | { |
ba46ddd0 CD |
670 | return fp_binary(cpu, cia, &sim_fpu_add, op1, op2, fmt); |
671 | } | |
487f79b7 | 672 | |
ba46ddd0 CD |
673 | unsigned64 |
674 | fp_sub(sim_cpu *cpu, | |
675 | address_word cia, | |
676 | unsigned64 op1, | |
677 | unsigned64 op2, | |
678 | FP_formats fmt) | |
679 | { | |
680 | return fp_binary(cpu, cia, &sim_fpu_sub, op1, op2, fmt); | |
681 | } | |
487f79b7 | 682 | |
ba46ddd0 CD |
683 | unsigned64 |
684 | fp_mul(sim_cpu *cpu, | |
685 | address_word cia, | |
686 | unsigned64 op1, | |
687 | unsigned64 op2, | |
688 | FP_formats fmt) | |
689 | { | |
690 | return fp_binary(cpu, cia, &sim_fpu_mul, op1, op2, fmt); | |
691 | } | |
487f79b7 | 692 | |
ba46ddd0 CD |
693 | unsigned64 |
694 | fp_div(sim_cpu *cpu, | |
695 | address_word cia, | |
696 | unsigned64 op1, | |
697 | unsigned64 op2, | |
698 | FP_formats fmt) | |
699 | { | |
700 | return fp_binary(cpu, cia, &sim_fpu_div, op1, op2, fmt); | |
701 | } | |
487f79b7 | 702 | |
ba46ddd0 CD |
703 | unsigned64 |
704 | fp_recip(sim_cpu *cpu, | |
705 | address_word cia, | |
706 | unsigned64 op, | |
707 | FP_formats fmt) | |
708 | { | |
709 | return fp_unary(cpu, cia, &sim_fpu_inv, op, fmt); | |
710 | } | |
487f79b7 | 711 | |
ba46ddd0 CD |
712 | unsigned64 |
713 | fp_sqrt(sim_cpu *cpu, | |
714 | address_word cia, | |
715 | unsigned64 op, | |
716 | FP_formats fmt) | |
717 | { | |
718 | return fp_unary(cpu, cia, &sim_fpu_sqrt, op, fmt); | |
487f79b7 CD |
719 | } |
720 | ||
ba46ddd0 | 721 | |
cfe9ea23 CD |
722 | /* Conversion operations. */ |
723 | ||
487f79b7 | 724 | uword64 |
18d8a52d | 725 | convert (sim_cpu *cpu, |
487f79b7 CD |
726 | address_word cia, |
727 | int rm, | |
728 | uword64 op, | |
729 | FP_formats from, | |
730 | FP_formats to) | |
731 | { | |
732 | sim_fpu wop; | |
577d8c4b | 733 | sim_fpu_round round = rounding_mode (rm); |
487f79b7 CD |
734 | unsigned32 result32; |
735 | unsigned64 result64; | |
736 | ||
487f79b7 CD |
737 | /* Convert the input to sim_fpu internal format */ |
738 | switch (from) | |
739 | { | |
740 | case fmt_double: | |
741 | sim_fpu_64to (&wop, op); | |
742 | break; | |
743 | case fmt_single: | |
744 | sim_fpu_32to (&wop, op); | |
745 | break; | |
746 | case fmt_word: | |
747 | sim_fpu_i32to (&wop, op, round); | |
748 | break; | |
749 | case fmt_long: | |
750 | sim_fpu_i64to (&wop, op, round); | |
751 | break; | |
752 | default: | |
753 | fprintf (stderr, "Bad switch\n"); | |
754 | abort (); | |
755 | } | |
756 | ||
757 | /* Convert sim_fpu format into the output */ | |
758 | /* The value WOP is converted to the destination format, rounding | |
759 | using mode RM. When the destination is a fixed-point format, then | |
760 | a source value of Infinity, NaN or one which would round to an | |
761 | integer outside the fixed point range then an IEEE Invalid | |
37d146fa | 762 | Operation condition is raised. */ |
487f79b7 CD |
763 | switch (to) |
764 | { | |
765 | case fmt_single: | |
766 | sim_fpu_round_32 (&wop, round, 0); | |
767 | sim_fpu_to32 (&result32, &wop); | |
768 | result64 = result32; | |
769 | break; | |
770 | case fmt_double: | |
771 | sim_fpu_round_64 (&wop, round, 0); | |
772 | sim_fpu_to64 (&result64, &wop); | |
773 | break; | |
774 | case fmt_word: | |
775 | sim_fpu_to32i (&result32, &wop, round); | |
776 | result64 = result32; | |
777 | break; | |
778 | case fmt_long: | |
779 | sim_fpu_to64i (&result64, &wop, round); | |
780 | break; | |
781 | default: | |
782 | result64 = 0; | |
783 | fprintf (stderr, "Bad switch\n"); | |
784 | abort (); | |
785 | } | |
37d146fa | 786 | |
487f79b7 | 787 | #ifdef DEBUG |
37d146fa CD |
788 | printf ("DBG: Convert: returning 0x%s (to format = %s)\n", |
789 | pr_addr (result64), fpu_format_name (to)); | |
487f79b7 CD |
790 | #endif /* DEBUG */ |
791 | ||
37d146fa | 792 | return (result64); |
487f79b7 CD |
793 | } |
794 | ||
07892c0b CD |
795 | static const char * |
796 | fpu_format_name (FP_formats fmt) | |
797 | { | |
798 | switch (fmt) | |
799 | { | |
800 | case fmt_single: | |
801 | return "single"; | |
802 | case fmt_double: | |
803 | return "double"; | |
804 | case fmt_word: | |
805 | return "word"; | |
806 | case fmt_long: | |
807 | return "long"; | |
808 | case fmt_unknown: | |
809 | return "<unknown>"; | |
810 | case fmt_uninterpreted: | |
811 | return "<uninterpreted>"; | |
812 | case fmt_uninterpreted_32: | |
813 | return "<uninterpreted_32>"; | |
814 | case fmt_uninterpreted_64: | |
815 | return "<uninterpreted_64>"; | |
816 | default: | |
817 | return "<format error>"; | |
818 | } | |
819 | } | |
487f79b7 | 820 | |
07892c0b CD |
821 | #ifdef DEBUG |
822 | static const char * | |
823 | fpu_rounding_mode_name (int rm) | |
824 | { | |
825 | switch (rm) | |
826 | { | |
827 | case FP_RM_NEAREST: | |
828 | return "Round"; | |
829 | case FP_RM_TOZERO: | |
830 | return "Trunc"; | |
831 | case FP_RM_TOPINF: | |
832 | return "Ceil"; | |
833 | case FP_RM_TOMINF: | |
834 | return "Floor"; | |
835 | default: | |
836 | return "<rounding mode error>"; | |
837 | } | |
838 | } | |
839 | #endif /* DEBUG */ |