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" | |
43 | #include "sim-fpu.h" | |
44 | ||
45 | /* Within cp1.c we refer to sim_cpu directly. */ | |
46 | #define CPU cpu | |
18d8a52d | 47 | #define SD CPU_STATE(cpu) |
487f79b7 CD |
48 | |
49 | /*-- FPU support routines ---------------------------------------------------*/ | |
50 | ||
51 | /* Numbers are held in normalized form. The SINGLE and DOUBLE binary | |
bad673a9 CD |
52 | formats conform to ANSI/IEEE Std 754-1985. |
53 | ||
54 | SINGLE precision floating: | |
55 | seeeeeeeefffffffffffffffffffffff | |
56 | s = 1bit = sign | |
57 | e = 8bits = exponent | |
58 | f = 23bits = fraction | |
59 | ||
60 | SINGLE precision fixed: | |
61 | siiiiiiiiiiiiiiiiiiiiiiiiiiiiiii | |
62 | s = 1bit = sign | |
63 | i = 31bits = integer | |
64 | ||
65 | DOUBLE precision floating: | |
66 | seeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffff | |
67 | s = 1bit = sign | |
68 | e = 11bits = exponent | |
69 | f = 52bits = fraction | |
70 | ||
71 | DOUBLE precision fixed: | |
72 | siiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii | |
73 | s = 1bit = sign | |
74 | i = 63bits = integer | |
487f79b7 CD |
75 | */ |
76 | ||
37d146fa | 77 | /* Explicit QNaN values used when value required: */ |
487f79b7 CD |
78 | #define FPQNaN_SINGLE (0x7FBFFFFF) |
79 | #define FPQNaN_WORD (0x7FFFFFFF) | |
bad673a9 CD |
80 | #define FPQNaN_DOUBLE (UNSIGNED64 (0x7FF7FFFFFFFFFFFF)) |
81 | #define FPQNaN_LONG (UNSIGNED64 (0x7FFFFFFFFFFFFFFF)) | |
487f79b7 | 82 | |
07892c0b CD |
83 | static const char *fpu_format_name (FP_formats fmt); |
84 | #ifdef DEBUG | |
85 | static const char *fpu_rounding_mode_name (int rm); | |
86 | #endif | |
487f79b7 CD |
87 | |
88 | uword64 | |
18d8a52d | 89 | value_fpr (sim_cpu *cpu, |
487f79b7 CD |
90 | address_word cia, |
91 | int fpr, | |
92 | FP_formats fmt) | |
93 | { | |
94 | uword64 value = 0; | |
95 | int err = 0; | |
96 | ||
37d146fa | 97 | /* Treat unused register values, as fixed-point 64bit values: */ |
487f79b7 | 98 | if ((fmt == fmt_uninterpreted) || (fmt == fmt_unknown)) |
37d146fa | 99 | { |
487f79b7 | 100 | #if 1 |
37d146fa CD |
101 | /* If request to read data as "uninterpreted", then use the current |
102 | encoding: */ | |
103 | fmt = FPR_STATE[fpr]; | |
487f79b7 | 104 | #else |
37d146fa | 105 | fmt = fmt_long; |
487f79b7 | 106 | #endif |
37d146fa | 107 | } |
487f79b7 | 108 | |
37d146fa CD |
109 | /* For values not yet accessed, set to the desired format: */ |
110 | if (FPR_STATE[fpr] == fmt_uninterpreted) | |
111 | { | |
112 | FPR_STATE[fpr] = fmt; | |
487f79b7 | 113 | #ifdef DEBUG |
37d146fa CD |
114 | printf ("DBG: Register %d was fmt_uninterpreted. Now %s\n", fpr, |
115 | fpu_format_name (fmt)); | |
487f79b7 | 116 | #endif /* DEBUG */ |
487f79b7 | 117 | } |
37d146fa CD |
118 | if (fmt != FPR_STATE[fpr]) |
119 | { | |
18d8a52d | 120 | sim_io_eprintf (SD, "FPR %d (format %s) being accessed with format %s - setting to unknown (PC = 0x%s)\n", |
37d146fa CD |
121 | fpr, fpu_format_name (FPR_STATE[fpr]), |
122 | fpu_format_name (fmt), pr_addr (cia)); | |
123 | FPR_STATE[fpr] = fmt_unknown; | |
124 | } | |
487f79b7 | 125 | |
37d146fa CD |
126 | if (FPR_STATE[fpr] == fmt_unknown) |
127 | { | |
128 | /* Set QNaN value: */ | |
129 | switch (fmt) | |
130 | { | |
131 | case fmt_single: | |
132 | value = FPQNaN_SINGLE; | |
133 | break; | |
134 | ||
135 | case fmt_double: | |
136 | value = FPQNaN_DOUBLE; | |
137 | break; | |
138 | ||
139 | case fmt_word: | |
140 | value = FPQNaN_WORD; | |
141 | break; | |
142 | ||
143 | case fmt_long: | |
144 | value = FPQNaN_LONG; | |
145 | break; | |
146 | ||
147 | default: | |
148 | err = -1; | |
149 | break; | |
150 | } | |
151 | } | |
152 | else if (SizeFGR () == 64) | |
153 | { | |
154 | switch (fmt) | |
155 | { | |
156 | case fmt_single: | |
157 | case fmt_word: | |
158 | value = (FGR[fpr] & 0xFFFFFFFF); | |
159 | break; | |
160 | ||
161 | case fmt_uninterpreted: | |
162 | case fmt_double: | |
163 | case fmt_long: | |
164 | value = FGR[fpr]; | |
165 | break; | |
166 | ||
167 | default: | |
168 | err = -1; | |
169 | break; | |
170 | } | |
171 | } | |
172 | else | |
173 | { | |
174 | switch (fmt) | |
175 | { | |
176 | case fmt_single: | |
177 | case fmt_word: | |
178 | value = (FGR[fpr] & 0xFFFFFFFF); | |
179 | break; | |
180 | ||
181 | case fmt_uninterpreted: | |
182 | case fmt_double: | |
183 | case fmt_long: | |
184 | if ((fpr & 1) == 0) | |
185 | { | |
186 | /* even registers only */ | |
487f79b7 | 187 | #ifdef DEBUG |
37d146fa CD |
188 | printf ("DBG: ValueFPR: FGR[%d] = %s, FGR[%d] = %s\n", |
189 | fpr + 1, pr_uword64 ((uword64) FGR[fpr+1]), | |
190 | fpr, pr_uword64 ((uword64) FGR[fpr])); | |
487f79b7 | 191 | #endif |
37d146fa CD |
192 | value = ((((uword64) FGR[fpr+1]) << 32) |
193 | | (FGR[fpr] & 0xFFFFFFFF)); | |
194 | } | |
195 | else | |
196 | { | |
197 | SignalException (ReservedInstruction, 0); | |
198 | } | |
199 | break; | |
200 | ||
e80fc152 | 201 | default: |
37d146fa CD |
202 | err = -1; |
203 | break; | |
204 | } | |
487f79b7 | 205 | } |
487f79b7 CD |
206 | |
207 | if (err) | |
37d146fa | 208 | SignalExceptionSimulatorFault ("Unrecognised FP format in ValueFPR ()"); |
487f79b7 CD |
209 | |
210 | #ifdef DEBUG | |
37d146fa CD |
211 | printf ("DBG: ValueFPR: fpr = %d, fmt = %s, value = 0x%s : PC = 0x%s : SizeFGR () = %d\n", |
212 | fpr, fpu_format_name (fmt), pr_uword64 (value), pr_addr (cia), | |
213 | SizeFGR ()); | |
487f79b7 CD |
214 | #endif /* DEBUG */ |
215 | ||
37d146fa | 216 | return (value); |
487f79b7 CD |
217 | } |
218 | ||
219 | void | |
18d8a52d | 220 | store_fpr (sim_cpu *cpu, |
487f79b7 CD |
221 | address_word cia, |
222 | int fpr, | |
223 | FP_formats fmt, | |
224 | uword64 value) | |
225 | { | |
226 | int err = 0; | |
227 | ||
228 | #ifdef DEBUG | |
37d146fa CD |
229 | printf ("DBG: StoreFPR: fpr = %d, fmt = %s, value = 0x%s : PC = 0x%s : SizeFGR () = %d, \n", |
230 | fpr, fpu_format_name (fmt), pr_uword64 (value), pr_addr (cia), | |
231 | SizeFGR ()); | |
487f79b7 CD |
232 | #endif /* DEBUG */ |
233 | ||
37d146fa CD |
234 | if (SizeFGR () == 64) |
235 | { | |
236 | switch (fmt) | |
237 | { | |
238 | case fmt_uninterpreted_32: | |
239 | fmt = fmt_uninterpreted; | |
e80fc152 CD |
240 | case fmt_single: |
241 | case fmt_word: | |
37d146fa CD |
242 | if (STATE_VERBOSE_P (SD)) |
243 | sim_io_eprintf (SD, | |
244 | "Warning: PC 0x%s: interp.c store_fpr DEADCODE\n", | |
245 | pr_addr (cia)); | |
246 | FGR[fpr] = (((uword64) 0xDEADC0DE << 32) | (value & 0xFFFFFFFF)); | |
247 | FPR_STATE[fpr] = fmt; | |
248 | break; | |
249 | ||
250 | case fmt_uninterpreted_64: | |
251 | fmt = fmt_uninterpreted; | |
252 | case fmt_uninterpreted: | |
e80fc152 CD |
253 | case fmt_double: |
254 | case fmt_long: | |
37d146fa CD |
255 | FGR[fpr] = value; |
256 | FPR_STATE[fpr] = fmt; | |
257 | break; | |
258 | ||
e80fc152 | 259 | default: |
37d146fa CD |
260 | FPR_STATE[fpr] = fmt_unknown; |
261 | err = -1; | |
262 | break; | |
263 | } | |
487f79b7 | 264 | } |
37d146fa CD |
265 | else |
266 | { | |
267 | switch (fmt) | |
268 | { | |
269 | case fmt_uninterpreted_32: | |
270 | fmt = fmt_uninterpreted; | |
e80fc152 CD |
271 | case fmt_single: |
272 | case fmt_word: | |
487f79b7 | 273 | FGR[fpr] = (value & 0xFFFFFFFF); |
487f79b7 | 274 | FPR_STATE[fpr] = fmt; |
37d146fa CD |
275 | break; |
276 | ||
277 | case fmt_uninterpreted_64: | |
278 | fmt = fmt_uninterpreted; | |
279 | case fmt_uninterpreted: | |
e80fc152 CD |
280 | case fmt_double: |
281 | case fmt_long: | |
37d146fa CD |
282 | if ((fpr & 1) == 0) |
283 | { | |
284 | /* even register number only */ | |
285 | FGR[fpr+1] = (value >> 32); | |
286 | FGR[fpr] = (value & 0xFFFFFFFF); | |
287 | FPR_STATE[fpr + 1] = fmt; | |
288 | FPR_STATE[fpr] = fmt; | |
289 | } | |
290 | else | |
291 | { | |
292 | FPR_STATE[fpr] = fmt_unknown; | |
293 | FPR_STATE[fpr + 1] = fmt_unknown; | |
294 | SignalException (ReservedInstruction, 0); | |
295 | } | |
296 | break; | |
297 | ||
e80fc152 | 298 | default: |
487f79b7 | 299 | FPR_STATE[fpr] = fmt_unknown; |
37d146fa CD |
300 | err = -1; |
301 | break; | |
487f79b7 | 302 | } |
487f79b7 | 303 | } |
487f79b7 CD |
304 | |
305 | if (err) | |
37d146fa | 306 | SignalExceptionSimulatorFault ("Unrecognised FP format in StoreFPR ()"); |
487f79b7 CD |
307 | |
308 | #ifdef DEBUG | |
37d146fa CD |
309 | printf ("DBG: StoreFPR: fpr[%d] = 0x%s (format %s)\n", |
310 | fpr, pr_uword64 (FGR[fpr]), fpu_format_name (fmt)); | |
487f79b7 CD |
311 | #endif /* DEBUG */ |
312 | ||
313 | return; | |
314 | } | |
315 | ||
316 | int | |
37d146fa | 317 | NaN (op, fmt) |
487f79b7 | 318 | uword64 op; |
37d146fa | 319 | FP_formats fmt; |
487f79b7 CD |
320 | { |
321 | int boolean = 0; | |
37d146fa | 322 | switch (fmt) |
487f79b7 | 323 | { |
37d146fa CD |
324 | case fmt_single: |
325 | case fmt_word: | |
326 | { | |
327 | sim_fpu wop; | |
328 | sim_fpu_32to (&wop, op); | |
329 | boolean = sim_fpu_is_nan (&wop); | |
330 | break; | |
331 | } | |
332 | case fmt_double: | |
333 | case fmt_long: | |
334 | { | |
335 | sim_fpu wop; | |
336 | sim_fpu_64to (&wop, op); | |
337 | boolean = sim_fpu_is_nan (&wop); | |
338 | break; | |
339 | } | |
340 | default: | |
341 | fprintf (stderr, "Bad switch\n"); | |
342 | abort (); | |
487f79b7 | 343 | } |
487f79b7 CD |
344 | |
345 | #ifdef DEBUG | |
37d146fa CD |
346 | printf ("DBG: NaN: returning %d for 0x%s (format = %s)\n", |
347 | boolean, pr_addr (op), fpu_format_name (fmt)); | |
487f79b7 CD |
348 | #endif /* DEBUG */ |
349 | ||
37d146fa | 350 | return (boolean); |
487f79b7 CD |
351 | } |
352 | ||
487f79b7 | 353 | int |
37d146fa | 354 | Less (op1, op2, fmt) |
487f79b7 CD |
355 | uword64 op1; |
356 | uword64 op2; | |
37d146fa | 357 | FP_formats fmt; |
487f79b7 CD |
358 | { |
359 | int boolean = 0; | |
360 | ||
361 | /* Argument checking already performed by the FPCOMPARE code */ | |
362 | ||
363 | #ifdef DEBUG | |
37d146fa CD |
364 | printf ("DBG: Less: %s: op1 = 0x%s : op2 = 0x%s\n", |
365 | fpu_format_name (fmt), pr_addr (op1), pr_addr (op2)); | |
487f79b7 CD |
366 | #endif /* DEBUG */ |
367 | ||
37d146fa CD |
368 | /* The format type should already have been checked: */ |
369 | switch (fmt) | |
487f79b7 | 370 | { |
37d146fa CD |
371 | case fmt_single: |
372 | { | |
373 | sim_fpu wop1; | |
374 | sim_fpu wop2; | |
375 | sim_fpu_32to (&wop1, op1); | |
376 | sim_fpu_32to (&wop2, op2); | |
377 | boolean = sim_fpu_is_lt (&wop1, &wop2); | |
378 | break; | |
379 | } | |
380 | case fmt_double: | |
381 | { | |
382 | sim_fpu wop1; | |
383 | sim_fpu wop2; | |
384 | sim_fpu_64to (&wop1, op1); | |
385 | sim_fpu_64to (&wop2, op2); | |
386 | boolean = sim_fpu_is_lt (&wop1, &wop2); | |
387 | break; | |
388 | } | |
389 | default: | |
390 | fprintf (stderr, "Bad switch\n"); | |
391 | abort (); | |
487f79b7 | 392 | } |
487f79b7 CD |
393 | |
394 | #ifdef DEBUG | |
37d146fa CD |
395 | printf ("DBG: Less: returning %d (format = %s)\n", |
396 | boolean, fpu_format_name (fmt)); | |
487f79b7 CD |
397 | #endif /* DEBUG */ |
398 | ||
37d146fa | 399 | return (boolean); |
487f79b7 CD |
400 | } |
401 | ||
402 | int | |
37d146fa | 403 | Equal (op1, op2, fmt) |
487f79b7 CD |
404 | uword64 op1; |
405 | uword64 op2; | |
37d146fa | 406 | FP_formats fmt; |
487f79b7 CD |
407 | { |
408 | int boolean = 0; | |
409 | ||
410 | /* Argument checking already performed by the FPCOMPARE code */ | |
411 | ||
412 | #ifdef DEBUG | |
37d146fa CD |
413 | printf ("DBG: Equal: %s: op1 = 0x%s : op2 = 0x%s\n", |
414 | fpu_format_name (fmt), pr_addr (op1), pr_addr (op2)); | |
487f79b7 CD |
415 | #endif /* DEBUG */ |
416 | ||
37d146fa CD |
417 | /* The format type should already have been checked: */ |
418 | switch (fmt) | |
487f79b7 | 419 | { |
37d146fa CD |
420 | case fmt_single: |
421 | { | |
422 | sim_fpu wop1; | |
423 | sim_fpu wop2; | |
424 | sim_fpu_32to (&wop1, op1); | |
425 | sim_fpu_32to (&wop2, op2); | |
426 | boolean = sim_fpu_is_eq (&wop1, &wop2); | |
427 | break; | |
428 | } | |
429 | case fmt_double: | |
430 | { | |
431 | sim_fpu wop1; | |
432 | sim_fpu wop2; | |
433 | sim_fpu_64to (&wop1, op1); | |
434 | sim_fpu_64to (&wop2, op2); | |
435 | boolean = sim_fpu_is_eq (&wop1, &wop2); | |
436 | break; | |
437 | } | |
438 | default: | |
439 | fprintf (stderr, "Bad switch\n"); | |
440 | abort (); | |
487f79b7 | 441 | } |
487f79b7 CD |
442 | |
443 | #ifdef DEBUG | |
37d146fa CD |
444 | printf ("DBG: Equal: returning %d (format = %s)\n", |
445 | boolean, fpu_format_name (fmt)); | |
487f79b7 CD |
446 | #endif /* DEBUG */ |
447 | ||
37d146fa | 448 | return (boolean); |
487f79b7 CD |
449 | } |
450 | ||
487f79b7 | 451 | |
ba46ddd0 | 452 | /* Basic arithmetic operations. */ |
487f79b7 | 453 | |
ba46ddd0 CD |
454 | static unsigned64 |
455 | fp_unary(sim_cpu *cpu, | |
456 | address_word cia, | |
457 | int (*sim_fpu_op)(sim_fpu *, const sim_fpu *), | |
458 | unsigned64 op, | |
459 | FP_formats fmt) | |
487f79b7 | 460 | { |
ba46ddd0 CD |
461 | sim_fpu wop; |
462 | sim_fpu ans; | |
463 | unsigned64 result = 0; | |
487f79b7 | 464 | |
ba46ddd0 | 465 | /* The format type has already been checked: */ |
37d146fa | 466 | switch (fmt) |
487f79b7 | 467 | { |
37d146fa CD |
468 | case fmt_single: |
469 | { | |
37d146fa | 470 | unsigned32 res; |
ba46ddd0 CD |
471 | sim_fpu_32to (&wop, op); |
472 | (*sim_fpu_op) (&ans, &wop); | |
37d146fa CD |
473 | sim_fpu_to32 (&res, &ans); |
474 | result = res; | |
475 | break; | |
476 | } | |
477 | case fmt_double: | |
478 | { | |
37d146fa | 479 | unsigned64 res; |
ba46ddd0 CD |
480 | sim_fpu_64to (&wop, op); |
481 | (*sim_fpu_op) (&ans, &wop); | |
37d146fa CD |
482 | sim_fpu_to64 (&res, &ans); |
483 | result = res; | |
484 | break; | |
485 | } | |
486 | default: | |
ba46ddd0 | 487 | sim_io_eprintf (SD, "Bad switch\n"); |
37d146fa | 488 | abort (); |
487f79b7 | 489 | } |
487f79b7 | 490 | |
ba46ddd0 | 491 | return result; |
487f79b7 CD |
492 | } |
493 | ||
ba46ddd0 CD |
494 | static unsigned64 |
495 | fp_binary(sim_cpu *cpu, | |
496 | address_word cia, | |
497 | int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *), | |
498 | unsigned64 op1, | |
499 | unsigned64 op2, | |
500 | FP_formats fmt) | |
487f79b7 | 501 | { |
ba46ddd0 CD |
502 | sim_fpu wop1; |
503 | sim_fpu wop2; | |
504 | sim_fpu ans; | |
505 | unsigned64 result = 0; | |
487f79b7 | 506 | |
ba46ddd0 | 507 | /* The format type has already been checked: */ |
37d146fa | 508 | switch (fmt) |
487f79b7 | 509 | { |
37d146fa CD |
510 | case fmt_single: |
511 | { | |
37d146fa CD |
512 | unsigned32 res; |
513 | sim_fpu_32to (&wop1, op1); | |
514 | sim_fpu_32to (&wop2, op2); | |
ba46ddd0 | 515 | (*sim_fpu_op) (&ans, &wop1, &wop2); |
37d146fa CD |
516 | sim_fpu_to32 (&res, &ans); |
517 | result = res; | |
518 | break; | |
519 | } | |
520 | case fmt_double: | |
521 | { | |
37d146fa CD |
522 | unsigned64 res; |
523 | sim_fpu_64to (&wop1, op1); | |
524 | sim_fpu_64to (&wop2, op2); | |
ba46ddd0 | 525 | (*sim_fpu_op) (&ans, &wop1, &wop2); |
37d146fa CD |
526 | sim_fpu_to64 (&res, &ans); |
527 | result = res; | |
528 | break; | |
529 | } | |
530 | default: | |
ba46ddd0 | 531 | sim_io_eprintf (SD, "Bad switch\n"); |
37d146fa | 532 | abort (); |
487f79b7 | 533 | } |
487f79b7 | 534 | |
ba46ddd0 | 535 | return result; |
487f79b7 CD |
536 | } |
537 | ||
487f79b7 | 538 | |
ba46ddd0 CD |
539 | unsigned64 |
540 | fp_abs(sim_cpu *cpu, | |
541 | address_word cia, | |
542 | unsigned64 op, | |
543 | FP_formats fmt) | |
544 | { | |
545 | return fp_unary(cpu, cia, &sim_fpu_abs, op, fmt); | |
487f79b7 CD |
546 | } |
547 | ||
ba46ddd0 CD |
548 | unsigned64 |
549 | fp_neg(sim_cpu *cpu, | |
550 | address_word cia, | |
551 | unsigned64 op, | |
552 | FP_formats fmt) | |
487f79b7 | 553 | { |
ba46ddd0 | 554 | return fp_unary(cpu, cia, &sim_fpu_neg, op, fmt); |
487f79b7 CD |
555 | } |
556 | ||
ba46ddd0 CD |
557 | unsigned64 |
558 | fp_add(sim_cpu *cpu, | |
559 | address_word cia, | |
560 | unsigned64 op1, | |
561 | unsigned64 op2, | |
562 | FP_formats fmt) | |
487f79b7 | 563 | { |
ba46ddd0 CD |
564 | return fp_binary(cpu, cia, &sim_fpu_add, op1, op2, fmt); |
565 | } | |
487f79b7 | 566 | |
ba46ddd0 CD |
567 | unsigned64 |
568 | fp_sub(sim_cpu *cpu, | |
569 | address_word cia, | |
570 | unsigned64 op1, | |
571 | unsigned64 op2, | |
572 | FP_formats fmt) | |
573 | { | |
574 | return fp_binary(cpu, cia, &sim_fpu_sub, op1, op2, fmt); | |
575 | } | |
487f79b7 | 576 | |
ba46ddd0 CD |
577 | unsigned64 |
578 | fp_mul(sim_cpu *cpu, | |
579 | address_word cia, | |
580 | unsigned64 op1, | |
581 | unsigned64 op2, | |
582 | FP_formats fmt) | |
583 | { | |
584 | return fp_binary(cpu, cia, &sim_fpu_mul, op1, op2, fmt); | |
585 | } | |
487f79b7 | 586 | |
ba46ddd0 CD |
587 | unsigned64 |
588 | fp_div(sim_cpu *cpu, | |
589 | address_word cia, | |
590 | unsigned64 op1, | |
591 | unsigned64 op2, | |
592 | FP_formats fmt) | |
593 | { | |
594 | return fp_binary(cpu, cia, &sim_fpu_div, op1, op2, fmt); | |
595 | } | |
487f79b7 | 596 | |
ba46ddd0 CD |
597 | unsigned64 |
598 | fp_recip(sim_cpu *cpu, | |
599 | address_word cia, | |
600 | unsigned64 op, | |
601 | FP_formats fmt) | |
602 | { | |
603 | return fp_unary(cpu, cia, &sim_fpu_inv, op, fmt); | |
604 | } | |
487f79b7 | 605 | |
ba46ddd0 CD |
606 | unsigned64 |
607 | fp_sqrt(sim_cpu *cpu, | |
608 | address_word cia, | |
609 | unsigned64 op, | |
610 | FP_formats fmt) | |
611 | { | |
612 | return fp_unary(cpu, cia, &sim_fpu_sqrt, op, fmt); | |
487f79b7 CD |
613 | } |
614 | ||
ba46ddd0 | 615 | |
487f79b7 | 616 | uword64 |
18d8a52d | 617 | convert (sim_cpu *cpu, |
487f79b7 CD |
618 | address_word cia, |
619 | int rm, | |
620 | uword64 op, | |
621 | FP_formats from, | |
622 | FP_formats to) | |
623 | { | |
624 | sim_fpu wop; | |
625 | sim_fpu_round round; | |
626 | unsigned32 result32; | |
627 | unsigned64 result64; | |
628 | ||
629 | #ifdef DEBUG | |
630 | #if 0 /* FIXME: doesn't compile */ | |
37d146fa CD |
631 | printf ("DBG: Convert: mode %s : op 0x%s : from %s : to %s : (PC = 0x%s)\n", |
632 | fpu_rounding_mode_name (rm), pr_addr (op), fpu_format_name (from), | |
633 | fpu_format_name (to), pr_addr (IPC)); | |
487f79b7 CD |
634 | #endif |
635 | #endif /* DEBUG */ | |
636 | ||
637 | switch (rm) | |
638 | { | |
639 | case FP_RM_NEAREST: | |
640 | /* Round result to nearest representable value. When two | |
641 | representable values are equally near, round to the value | |
37d146fa | 642 | that has a least significant bit of zero (i.e. is even). */ |
487f79b7 CD |
643 | round = sim_fpu_round_near; |
644 | break; | |
645 | case FP_RM_TOZERO: | |
646 | /* Round result to the value closest to, and not greater in | |
37d146fa | 647 | magnitude than, the result. */ |
487f79b7 CD |
648 | round = sim_fpu_round_zero; |
649 | break; | |
650 | case FP_RM_TOPINF: | |
651 | /* Round result to the value closest to, and not less than, | |
37d146fa | 652 | the result. */ |
487f79b7 CD |
653 | round = sim_fpu_round_up; |
654 | break; | |
37d146fa | 655 | |
487f79b7 CD |
656 | case FP_RM_TOMINF: |
657 | /* Round result to the value closest to, and not greater than, | |
37d146fa | 658 | the result. */ |
487f79b7 CD |
659 | round = sim_fpu_round_down; |
660 | break; | |
661 | default: | |
662 | round = 0; | |
663 | fprintf (stderr, "Bad switch\n"); | |
664 | abort (); | |
665 | } | |
37d146fa | 666 | |
487f79b7 CD |
667 | /* Convert the input to sim_fpu internal format */ |
668 | switch (from) | |
669 | { | |
670 | case fmt_double: | |
671 | sim_fpu_64to (&wop, op); | |
672 | break; | |
673 | case fmt_single: | |
674 | sim_fpu_32to (&wop, op); | |
675 | break; | |
676 | case fmt_word: | |
677 | sim_fpu_i32to (&wop, op, round); | |
678 | break; | |
679 | case fmt_long: | |
680 | sim_fpu_i64to (&wop, op, round); | |
681 | break; | |
682 | default: | |
683 | fprintf (stderr, "Bad switch\n"); | |
684 | abort (); | |
685 | } | |
686 | ||
687 | /* Convert sim_fpu format into the output */ | |
688 | /* The value WOP is converted to the destination format, rounding | |
689 | using mode RM. When the destination is a fixed-point format, then | |
690 | a source value of Infinity, NaN or one which would round to an | |
691 | integer outside the fixed point range then an IEEE Invalid | |
37d146fa | 692 | Operation condition is raised. */ |
487f79b7 CD |
693 | switch (to) |
694 | { | |
695 | case fmt_single: | |
696 | sim_fpu_round_32 (&wop, round, 0); | |
697 | sim_fpu_to32 (&result32, &wop); | |
698 | result64 = result32; | |
699 | break; | |
700 | case fmt_double: | |
701 | sim_fpu_round_64 (&wop, round, 0); | |
702 | sim_fpu_to64 (&result64, &wop); | |
703 | break; | |
704 | case fmt_word: | |
705 | sim_fpu_to32i (&result32, &wop, round); | |
706 | result64 = result32; | |
707 | break; | |
708 | case fmt_long: | |
709 | sim_fpu_to64i (&result64, &wop, round); | |
710 | break; | |
711 | default: | |
712 | result64 = 0; | |
713 | fprintf (stderr, "Bad switch\n"); | |
714 | abort (); | |
715 | } | |
37d146fa | 716 | |
487f79b7 | 717 | #ifdef DEBUG |
37d146fa CD |
718 | printf ("DBG: Convert: returning 0x%s (to format = %s)\n", |
719 | pr_addr (result64), fpu_format_name (to)); | |
487f79b7 CD |
720 | #endif /* DEBUG */ |
721 | ||
37d146fa | 722 | return (result64); |
487f79b7 CD |
723 | } |
724 | ||
07892c0b CD |
725 | static const char * |
726 | fpu_format_name (FP_formats fmt) | |
727 | { | |
728 | switch (fmt) | |
729 | { | |
730 | case fmt_single: | |
731 | return "single"; | |
732 | case fmt_double: | |
733 | return "double"; | |
734 | case fmt_word: | |
735 | return "word"; | |
736 | case fmt_long: | |
737 | return "long"; | |
738 | case fmt_unknown: | |
739 | return "<unknown>"; | |
740 | case fmt_uninterpreted: | |
741 | return "<uninterpreted>"; | |
742 | case fmt_uninterpreted_32: | |
743 | return "<uninterpreted_32>"; | |
744 | case fmt_uninterpreted_64: | |
745 | return "<uninterpreted_64>"; | |
746 | default: | |
747 | return "<format error>"; | |
748 | } | |
749 | } | |
487f79b7 | 750 | |
07892c0b CD |
751 | #ifdef DEBUG |
752 | static const char * | |
753 | fpu_rounding_mode_name (int rm) | |
754 | { | |
755 | switch (rm) | |
756 | { | |
757 | case FP_RM_NEAREST: | |
758 | return "Round"; | |
759 | case FP_RM_TOZERO: | |
760 | return "Trunc"; | |
761 | case FP_RM_TOPINF: | |
762 | return "Ceil"; | |
763 | case FP_RM_TOMINF: | |
764 | return "Floor"; | |
765 | default: | |
766 | return "<rounding mode error>"; | |
767 | } | |
768 | } | |
769 | #endif /* DEBUG */ |