4 * Copyright (c) 2006-2007 CodeSourcery
5 * Written by Paul Brook
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 #include "qemu/osdep.h"
23 #include "exec/helper-proto.h"
24 #include "exec/exec-all.h"
25 #include "exec/cpu_ldst.h"
26 #include "softfloat.h"
29 * Undefined offsets may be different on various FPU.
30 * On 68040 they return 0.0 (floatx80_zero)
33 static const floatx80 fpu_rom
[128] = {
34 [0x00] = make_floatx80_init(0x4000, 0xc90fdaa22168c235ULL
), /* Pi */
35 [0x0b] = make_floatx80_init(0x3ffd, 0x9a209a84fbcff798ULL
), /* Log10(2) */
36 [0x0c] = make_floatx80_init(0x4000, 0xadf85458a2bb4a9aULL
), /* e */
37 [0x0d] = make_floatx80_init(0x3fff, 0xb8aa3b295c17f0bcULL
), /* Log2(e) */
38 [0x0e] = make_floatx80_init(0x3ffd, 0xde5bd8a937287195ULL
), /* Log10(e) */
39 [0x0f] = make_floatx80_init(0x0000, 0x0000000000000000ULL
), /* Zero */
40 [0x30] = make_floatx80_init(0x3ffe, 0xb17217f7d1cf79acULL
), /* ln(2) */
41 [0x31] = make_floatx80_init(0x4000, 0x935d8dddaaa8ac17ULL
), /* ln(10) */
42 [0x32] = make_floatx80_init(0x3fff, 0x8000000000000000ULL
), /* 10^0 */
43 [0x33] = make_floatx80_init(0x4002, 0xa000000000000000ULL
), /* 10^1 */
44 [0x34] = make_floatx80_init(0x4005, 0xc800000000000000ULL
), /* 10^2 */
45 [0x35] = make_floatx80_init(0x400c, 0x9c40000000000000ULL
), /* 10^4 */
46 [0x36] = make_floatx80_init(0x4019, 0xbebc200000000000ULL
), /* 10^8 */
47 [0x37] = make_floatx80_init(0x4034, 0x8e1bc9bf04000000ULL
), /* 10^16 */
48 [0x38] = make_floatx80_init(0x4069, 0x9dc5ada82b70b59eULL
), /* 10^32 */
49 [0x39] = make_floatx80_init(0x40d3, 0xc2781f49ffcfa6d5ULL
), /* 10^64 */
50 [0x3a] = make_floatx80_init(0x41a8, 0x93ba47c980e98ce0ULL
), /* 10^128 */
51 [0x3b] = make_floatx80_init(0x4351, 0xaa7eebfb9df9de8eULL
), /* 10^256 */
52 [0x3c] = make_floatx80_init(0x46a3, 0xe319a0aea60e91c7ULL
), /* 10^512 */
53 [0x3d] = make_floatx80_init(0x4d48, 0xc976758681750c17ULL
), /* 10^1024 */
54 [0x3e] = make_floatx80_init(0x5a92, 0x9e8b3b5dc53d5de5ULL
), /* 10^2048 */
55 [0x3f] = make_floatx80_init(0x7525, 0xc46052028a20979bULL
), /* 10^4096 */
58 int32_t HELPER(reds32
)(CPUM68KState
*env
, FPReg
*val
)
60 return floatx80_to_int32(val
->d
, &env
->fp_status
);
63 float32
HELPER(redf32
)(CPUM68KState
*env
, FPReg
*val
)
65 return floatx80_to_float32(val
->d
, &env
->fp_status
);
68 void HELPER(exts32
)(CPUM68KState
*env
, FPReg
*res
, int32_t val
)
70 res
->d
= int32_to_floatx80(val
, &env
->fp_status
);
73 void HELPER(extf32
)(CPUM68KState
*env
, FPReg
*res
, float32 val
)
75 res
->d
= float32_to_floatx80(val
, &env
->fp_status
);
78 void HELPER(extf64
)(CPUM68KState
*env
, FPReg
*res
, float64 val
)
80 res
->d
= float64_to_floatx80(val
, &env
->fp_status
);
83 float64
HELPER(redf64
)(CPUM68KState
*env
, FPReg
*val
)
85 return floatx80_to_float64(val
->d
, &env
->fp_status
);
88 void HELPER(firound
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
90 res
->d
= floatx80_round_to_int(val
->d
, &env
->fp_status
);
93 static void m68k_restore_precision_mode(CPUM68KState
*env
)
95 switch (env
->fpcr
& FPCR_PREC_MASK
) {
96 case FPCR_PREC_X
: /* extended */
97 set_floatx80_rounding_precision(80, &env
->fp_status
);
99 case FPCR_PREC_S
: /* single */
100 set_floatx80_rounding_precision(32, &env
->fp_status
);
102 case FPCR_PREC_D
: /* double */
103 set_floatx80_rounding_precision(64, &env
->fp_status
);
105 case FPCR_PREC_U
: /* undefined */
111 static void cf_restore_precision_mode(CPUM68KState
*env
)
113 if (env
->fpcr
& FPCR_PREC_S
) { /* single */
114 set_floatx80_rounding_precision(32, &env
->fp_status
);
115 } else { /* double */
116 set_floatx80_rounding_precision(64, &env
->fp_status
);
120 static void restore_rounding_mode(CPUM68KState
*env
)
122 switch (env
->fpcr
& FPCR_RND_MASK
) {
123 case FPCR_RND_N
: /* round to nearest */
124 set_float_rounding_mode(float_round_nearest_even
, &env
->fp_status
);
126 case FPCR_RND_Z
: /* round to zero */
127 set_float_rounding_mode(float_round_to_zero
, &env
->fp_status
);
129 case FPCR_RND_M
: /* round toward minus infinity */
130 set_float_rounding_mode(float_round_down
, &env
->fp_status
);
132 case FPCR_RND_P
: /* round toward positive infinity */
133 set_float_rounding_mode(float_round_up
, &env
->fp_status
);
138 void cpu_m68k_restore_fp_status(CPUM68KState
*env
)
140 if (m68k_feature(env
, M68K_FEATURE_CF_FPU
)) {
141 cf_restore_precision_mode(env
);
143 m68k_restore_precision_mode(env
);
145 restore_rounding_mode(env
);
148 void cpu_m68k_set_fpcr(CPUM68KState
*env
, uint32_t val
)
150 env
->fpcr
= val
& 0xffff;
151 cpu_m68k_restore_fp_status(env
);
154 void HELPER(fitrunc
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
156 FloatRoundMode rounding_mode
= get_float_rounding_mode(&env
->fp_status
);
157 set_float_rounding_mode(float_round_to_zero
, &env
->fp_status
);
158 res
->d
= floatx80_round_to_int(val
->d
, &env
->fp_status
);
159 set_float_rounding_mode(rounding_mode
, &env
->fp_status
);
162 void HELPER(set_fpcr
)(CPUM68KState
*env
, uint32_t val
)
164 cpu_m68k_set_fpcr(env
, val
);
167 #define PREC_BEGIN(prec) \
170 old = get_floatx80_rounding_precision(&env->fp_status); \
171 set_floatx80_rounding_precision(prec, &env->fp_status) \
174 set_floatx80_rounding_precision(old, &env->fp_status); \
177 void HELPER(fsround
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
180 res
->d
= floatx80_round(val
->d
, &env
->fp_status
);
184 void HELPER(fdround
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
187 res
->d
= floatx80_round(val
->d
, &env
->fp_status
);
191 void HELPER(fsqrt
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
193 res
->d
= floatx80_sqrt(val
->d
, &env
->fp_status
);
196 void HELPER(fssqrt
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
199 res
->d
= floatx80_sqrt(val
->d
, &env
->fp_status
);
203 void HELPER(fdsqrt
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
206 res
->d
= floatx80_sqrt(val
->d
, &env
->fp_status
);
210 void HELPER(fabs
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
212 res
->d
= floatx80_round(floatx80_abs(val
->d
), &env
->fp_status
);
215 void HELPER(fsabs
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
218 res
->d
= floatx80_round(floatx80_abs(val
->d
), &env
->fp_status
);
222 void HELPER(fdabs
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
225 res
->d
= floatx80_round(floatx80_abs(val
->d
), &env
->fp_status
);
229 void HELPER(fneg
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
231 res
->d
= floatx80_round(floatx80_chs(val
->d
), &env
->fp_status
);
234 void HELPER(fsneg
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
237 res
->d
= floatx80_round(floatx80_chs(val
->d
), &env
->fp_status
);
241 void HELPER(fdneg
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
244 res
->d
= floatx80_round(floatx80_chs(val
->d
), &env
->fp_status
);
248 void HELPER(fadd
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
250 res
->d
= floatx80_add(val0
->d
, val1
->d
, &env
->fp_status
);
253 void HELPER(fsadd
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
256 res
->d
= floatx80_add(val0
->d
, val1
->d
, &env
->fp_status
);
260 void HELPER(fdadd
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
263 res
->d
= floatx80_add(val0
->d
, val1
->d
, &env
->fp_status
);
267 void HELPER(fsub
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
269 res
->d
= floatx80_sub(val1
->d
, val0
->d
, &env
->fp_status
);
272 void HELPER(fssub
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
275 res
->d
= floatx80_sub(val1
->d
, val0
->d
, &env
->fp_status
);
279 void HELPER(fdsub
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
282 res
->d
= floatx80_sub(val1
->d
, val0
->d
, &env
->fp_status
);
286 void HELPER(fmul
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
288 res
->d
= floatx80_mul(val0
->d
, val1
->d
, &env
->fp_status
);
291 void HELPER(fsmul
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
294 res
->d
= floatx80_mul(val0
->d
, val1
->d
, &env
->fp_status
);
298 void HELPER(fdmul
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
301 res
->d
= floatx80_mul(val0
->d
, val1
->d
, &env
->fp_status
);
305 void HELPER(fsglmul
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
307 FloatRoundMode rounding_mode
= get_float_rounding_mode(&env
->fp_status
);
311 set_float_rounding_mode(float_round_to_zero
, &env
->fp_status
);
312 a
= floatx80_round(val0
->d
, &env
->fp_status
);
313 b
= floatx80_round(val1
->d
, &env
->fp_status
);
314 set_float_rounding_mode(rounding_mode
, &env
->fp_status
);
315 res
->d
= floatx80_mul(a
, b
, &env
->fp_status
);
319 void HELPER(fdiv
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
321 res
->d
= floatx80_div(val1
->d
, val0
->d
, &env
->fp_status
);
324 void HELPER(fsdiv
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
327 res
->d
= floatx80_div(val1
->d
, val0
->d
, &env
->fp_status
);
331 void HELPER(fddiv
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
334 res
->d
= floatx80_div(val1
->d
, val0
->d
, &env
->fp_status
);
338 void HELPER(fsgldiv
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
340 FloatRoundMode rounding_mode
= get_float_rounding_mode(&env
->fp_status
);
344 set_float_rounding_mode(float_round_to_zero
, &env
->fp_status
);
345 a
= floatx80_round(val1
->d
, &env
->fp_status
);
346 b
= floatx80_round(val0
->d
, &env
->fp_status
);
347 set_float_rounding_mode(rounding_mode
, &env
->fp_status
);
348 res
->d
= floatx80_div(a
, b
, &env
->fp_status
);
352 static int float_comp_to_cc(int float_compare
)
354 switch (float_compare
) {
355 case float_relation_equal
:
357 case float_relation_less
:
359 case float_relation_unordered
:
361 case float_relation_greater
:
364 g_assert_not_reached();
368 void HELPER(fcmp
)(CPUM68KState
*env
, FPReg
*val0
, FPReg
*val1
)
372 float_compare
= floatx80_compare(val1
->d
, val0
->d
, &env
->fp_status
);
373 env
->fpsr
= (env
->fpsr
& ~FPSR_CC_MASK
) | float_comp_to_cc(float_compare
);
376 void HELPER(ftst
)(CPUM68KState
*env
, FPReg
*val
)
380 if (floatx80_is_neg(val
->d
)) {
384 if (floatx80_is_any_nan(val
->d
)) {
386 } else if (floatx80_is_infinity(val
->d
)) {
388 } else if (floatx80_is_zero(val
->d
)) {
391 env
->fpsr
= (env
->fpsr
& ~FPSR_CC_MASK
) | cc
;
394 void HELPER(fconst
)(CPUM68KState
*env
, FPReg
*val
, uint32_t offset
)
396 val
->d
= fpu_rom
[offset
];
399 typedef int (*float_access
)(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
402 static uint32_t fmovem_predec(CPUM68KState
*env
, uint32_t addr
, uint32_t mask
,
403 float_access access_fn
)
405 uintptr_t ra
= GETPC();
408 for (i
= 7; i
>= 0; i
--, mask
<<= 1) {
410 size
= access_fn(env
, addr
, &env
->fregs
[i
], ra
);
411 if ((mask
& 0xff) != 0x80) {
420 static uint32_t fmovem_postinc(CPUM68KState
*env
, uint32_t addr
, uint32_t mask
,
421 float_access access_fn
)
423 uintptr_t ra
= GETPC();
426 for (i
= 0; i
< 8; i
++, mask
<<= 1) {
428 size
= access_fn(env
, addr
, &env
->fregs
[i
], ra
);
436 static int cpu_ld_floatx80_ra(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
442 high
= cpu_ldl_data_ra(env
, addr
, ra
);
443 low
= cpu_ldq_data_ra(env
, addr
+ 4, ra
);
445 fp
->l
.upper
= high
>> 16;
451 static int cpu_st_floatx80_ra(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
454 cpu_stl_data_ra(env
, addr
, fp
->l
.upper
<< 16, ra
);
455 cpu_stq_data_ra(env
, addr
+ 4, fp
->l
.lower
, ra
);
460 static int cpu_ld_float64_ra(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
465 val
= cpu_ldq_data_ra(env
, addr
, ra
);
466 fp
->d
= float64_to_floatx80(*(float64
*)&val
, &env
->fp_status
);
471 static int cpu_st_float64_ra(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
476 val
= floatx80_to_float64(fp
->d
, &env
->fp_status
);
477 cpu_stq_data_ra(env
, addr
, *(uint64_t *)&val
, ra
);
482 uint32_t HELPER(fmovemx_st_predec
)(CPUM68KState
*env
, uint32_t addr
,
485 return fmovem_predec(env
, addr
, mask
, cpu_st_floatx80_ra
);
488 uint32_t HELPER(fmovemx_st_postinc
)(CPUM68KState
*env
, uint32_t addr
,
491 return fmovem_postinc(env
, addr
, mask
, cpu_st_floatx80_ra
);
494 uint32_t HELPER(fmovemx_ld_postinc
)(CPUM68KState
*env
, uint32_t addr
,
497 return fmovem_postinc(env
, addr
, mask
, cpu_ld_floatx80_ra
);
500 uint32_t HELPER(fmovemd_st_predec
)(CPUM68KState
*env
, uint32_t addr
,
503 return fmovem_predec(env
, addr
, mask
, cpu_st_float64_ra
);
506 uint32_t HELPER(fmovemd_st_postinc
)(CPUM68KState
*env
, uint32_t addr
,
509 return fmovem_postinc(env
, addr
, mask
, cpu_st_float64_ra
);
512 uint32_t HELPER(fmovemd_ld_postinc
)(CPUM68KState
*env
, uint32_t addr
,
515 return fmovem_postinc(env
, addr
, mask
, cpu_ld_float64_ra
);
518 static void make_quotient(CPUM68KState
*env
, floatx80 val
)
523 if (floatx80_is_any_nan(val
)) {
527 quotient
= floatx80_to_int32(val
, &env
->fp_status
);
530 quotient
= -quotient
;
533 quotient
= (sign
<< 7) | (quotient
& 0x7f);
534 env
->fpsr
= (env
->fpsr
& ~FPSR_QT_MASK
) | (quotient
<< FPSR_QT_SHIFT
);
537 void HELPER(fmod
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
539 res
->d
= floatx80_mod(val1
->d
, val0
->d
, &env
->fp_status
);
541 make_quotient(env
, res
->d
);
544 void HELPER(frem
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
546 res
->d
= floatx80_rem(val1
->d
, val0
->d
, &env
->fp_status
);
548 make_quotient(env
, res
->d
);
551 void HELPER(fgetexp
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
553 res
->d
= floatx80_getexp(val
->d
, &env
->fp_status
);
556 void HELPER(fgetman
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
558 res
->d
= floatx80_getman(val
->d
, &env
->fp_status
);
561 void HELPER(fscale
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
563 res
->d
= floatx80_scale(val1
->d
, val0
->d
, &env
->fp_status
);
566 void HELPER(flognp1
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
568 res
->d
= floatx80_lognp1(val
->d
, &env
->fp_status
);
571 void HELPER(flogn
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
573 res
->d
= floatx80_logn(val
->d
, &env
->fp_status
);
576 void HELPER(flog10
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
578 res
->d
= floatx80_log10(val
->d
, &env
->fp_status
);
581 void HELPER(flog2
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
583 res
->d
= floatx80_log2(val
->d
, &env
->fp_status
);
586 void HELPER(fetox
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
588 res
->d
= floatx80_etox(val
->d
, &env
->fp_status
);
591 void HELPER(ftwotox
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
593 res
->d
= floatx80_twotox(val
->d
, &env
->fp_status
);
596 void HELPER(ftentox
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
598 res
->d
= floatx80_tentox(val
->d
, &env
->fp_status
);
601 void HELPER(ftan
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
603 res
->d
= floatx80_tan(val
->d
, &env
->fp_status
);
606 void HELPER(fsin
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
608 res
->d
= floatx80_sin(val
->d
, &env
->fp_status
);
611 void HELPER(fcos
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
613 res
->d
= floatx80_cos(val
->d
, &env
->fp_status
);
616 void HELPER(fsincos
)(CPUM68KState
*env
, FPReg
*res0
, FPReg
*res1
, FPReg
*val
)
620 * If res0 and res1 specify the same floating-point data register,
621 * the sine result is stored in the register, and the cosine
622 * result is discarded.
624 res1
->d
= floatx80_cos(a
, &env
->fp_status
);
625 res0
->d
= floatx80_sin(a
, &env
->fp_status
);
628 void HELPER(fatan
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
630 res
->d
= floatx80_atan(val
->d
, &env
->fp_status
);
633 void HELPER(fasin
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
635 res
->d
= floatx80_asin(val
->d
, &env
->fp_status
);
638 void HELPER(facos
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
640 res
->d
= floatx80_acos(val
->d
, &env
->fp_status
);
643 void HELPER(fatanh
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
645 res
->d
= floatx80_atanh(val
->d
, &env
->fp_status
);
648 void HELPER(fetoxm1
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
650 res
->d
= floatx80_etoxm1(val
->d
, &env
->fp_status
);
653 void HELPER(ftanh
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
655 res
->d
= floatx80_tanh(val
->d
, &env
->fp_status
);
658 void HELPER(fsinh
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
660 res
->d
= floatx80_sinh(val
->d
, &env
->fp_status
);
663 void HELPER(fcosh
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
665 res
->d
= floatx80_cosh(val
->d
, &env
->fp_status
);