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_set_fpcr(CPUM68KState
*env
, uint32_t val
)
140 env
->fpcr
= val
& 0xffff;
142 if (m68k_feature(env
, M68K_FEATURE_CF_FPU
)) {
143 cf_restore_precision_mode(env
);
145 m68k_restore_precision_mode(env
);
147 restore_rounding_mode(env
);
150 void HELPER(fitrunc
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
152 int rounding_mode
= get_float_rounding_mode(&env
->fp_status
);
153 set_float_rounding_mode(float_round_to_zero
, &env
->fp_status
);
154 res
->d
= floatx80_round_to_int(val
->d
, &env
->fp_status
);
155 set_float_rounding_mode(rounding_mode
, &env
->fp_status
);
158 void HELPER(set_fpcr
)(CPUM68KState
*env
, uint32_t val
)
160 cpu_m68k_set_fpcr(env
, val
);
163 #define PREC_BEGIN(prec) \
166 old = get_floatx80_rounding_precision(&env->fp_status); \
167 set_floatx80_rounding_precision(prec, &env->fp_status) \
170 set_floatx80_rounding_precision(old, &env->fp_status); \
173 void HELPER(fsround
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
176 res
->d
= floatx80_round(val
->d
, &env
->fp_status
);
180 void HELPER(fdround
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
183 res
->d
= floatx80_round(val
->d
, &env
->fp_status
);
187 void HELPER(fsqrt
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
189 res
->d
= floatx80_sqrt(val
->d
, &env
->fp_status
);
192 void HELPER(fssqrt
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
195 res
->d
= floatx80_sqrt(val
->d
, &env
->fp_status
);
199 void HELPER(fdsqrt
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
202 res
->d
= floatx80_sqrt(val
->d
, &env
->fp_status
);
206 void HELPER(fabs
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
208 res
->d
= floatx80_round(floatx80_abs(val
->d
), &env
->fp_status
);
211 void HELPER(fsabs
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
214 res
->d
= floatx80_round(floatx80_abs(val
->d
), &env
->fp_status
);
218 void HELPER(fdabs
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
221 res
->d
= floatx80_round(floatx80_abs(val
->d
), &env
->fp_status
);
225 void HELPER(fneg
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
227 res
->d
= floatx80_round(floatx80_chs(val
->d
), &env
->fp_status
);
230 void HELPER(fsneg
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
233 res
->d
= floatx80_round(floatx80_chs(val
->d
), &env
->fp_status
);
237 void HELPER(fdneg
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
240 res
->d
= floatx80_round(floatx80_chs(val
->d
), &env
->fp_status
);
244 void HELPER(fadd
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
246 res
->d
= floatx80_add(val0
->d
, val1
->d
, &env
->fp_status
);
249 void HELPER(fsadd
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
252 res
->d
= floatx80_add(val0
->d
, val1
->d
, &env
->fp_status
);
256 void HELPER(fdadd
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
259 res
->d
= floatx80_add(val0
->d
, val1
->d
, &env
->fp_status
);
263 void HELPER(fsub
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
265 res
->d
= floatx80_sub(val1
->d
, val0
->d
, &env
->fp_status
);
268 void HELPER(fssub
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
271 res
->d
= floatx80_sub(val1
->d
, val0
->d
, &env
->fp_status
);
275 void HELPER(fdsub
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
278 res
->d
= floatx80_sub(val1
->d
, val0
->d
, &env
->fp_status
);
282 void HELPER(fmul
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
284 res
->d
= floatx80_mul(val0
->d
, val1
->d
, &env
->fp_status
);
287 void HELPER(fsmul
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
290 res
->d
= floatx80_mul(val0
->d
, val1
->d
, &env
->fp_status
);
294 void HELPER(fdmul
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
297 res
->d
= floatx80_mul(val0
->d
, val1
->d
, &env
->fp_status
);
301 void HELPER(fsglmul
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
303 int rounding_mode
= get_float_rounding_mode(&env
->fp_status
);
307 set_float_rounding_mode(float_round_to_zero
, &env
->fp_status
);
308 a
= floatx80_round(val0
->d
, &env
->fp_status
);
309 b
= floatx80_round(val1
->d
, &env
->fp_status
);
310 set_float_rounding_mode(rounding_mode
, &env
->fp_status
);
311 res
->d
= floatx80_mul(a
, b
, &env
->fp_status
);
315 void HELPER(fdiv
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
317 res
->d
= floatx80_div(val1
->d
, val0
->d
, &env
->fp_status
);
320 void HELPER(fsdiv
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
323 res
->d
= floatx80_div(val1
->d
, val0
->d
, &env
->fp_status
);
327 void HELPER(fddiv
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
330 res
->d
= floatx80_div(val1
->d
, val0
->d
, &env
->fp_status
);
334 void HELPER(fsgldiv
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
336 int rounding_mode
= get_float_rounding_mode(&env
->fp_status
);
340 set_float_rounding_mode(float_round_to_zero
, &env
->fp_status
);
341 a
= floatx80_round(val1
->d
, &env
->fp_status
);
342 b
= floatx80_round(val0
->d
, &env
->fp_status
);
343 set_float_rounding_mode(rounding_mode
, &env
->fp_status
);
344 res
->d
= floatx80_div(a
, b
, &env
->fp_status
);
348 static int float_comp_to_cc(int float_compare
)
350 switch (float_compare
) {
351 case float_relation_equal
:
353 case float_relation_less
:
355 case float_relation_unordered
:
357 case float_relation_greater
:
360 g_assert_not_reached();
364 void HELPER(fcmp
)(CPUM68KState
*env
, FPReg
*val0
, FPReg
*val1
)
368 float_compare
= floatx80_compare(val1
->d
, val0
->d
, &env
->fp_status
);
369 env
->fpsr
= (env
->fpsr
& ~FPSR_CC_MASK
) | float_comp_to_cc(float_compare
);
372 void HELPER(ftst
)(CPUM68KState
*env
, FPReg
*val
)
376 if (floatx80_is_neg(val
->d
)) {
380 if (floatx80_is_any_nan(val
->d
)) {
382 } else if (floatx80_is_infinity(val
->d
)) {
384 } else if (floatx80_is_zero(val
->d
)) {
387 env
->fpsr
= (env
->fpsr
& ~FPSR_CC_MASK
) | cc
;
390 void HELPER(fconst
)(CPUM68KState
*env
, FPReg
*val
, uint32_t offset
)
392 val
->d
= fpu_rom
[offset
];
395 typedef int (*float_access
)(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
398 static uint32_t fmovem_predec(CPUM68KState
*env
, uint32_t addr
, uint32_t mask
,
399 float_access access_fn
)
401 uintptr_t ra
= GETPC();
404 for (i
= 7; i
>= 0; i
--, mask
<<= 1) {
406 size
= access_fn(env
, addr
, &env
->fregs
[i
], ra
);
407 if ((mask
& 0xff) != 0x80) {
416 static uint32_t fmovem_postinc(CPUM68KState
*env
, uint32_t addr
, uint32_t mask
,
417 float_access access_fn
)
419 uintptr_t ra
= GETPC();
422 for (i
= 0; i
< 8; i
++, mask
<<= 1) {
424 size
= access_fn(env
, addr
, &env
->fregs
[i
], ra
);
432 static int cpu_ld_floatx80_ra(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
438 high
= cpu_ldl_data_ra(env
, addr
, ra
);
439 low
= cpu_ldq_data_ra(env
, addr
+ 4, ra
);
441 fp
->l
.upper
= high
>> 16;
447 static int cpu_st_floatx80_ra(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
450 cpu_stl_data_ra(env
, addr
, fp
->l
.upper
<< 16, ra
);
451 cpu_stq_data_ra(env
, addr
+ 4, fp
->l
.lower
, ra
);
456 static int cpu_ld_float64_ra(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
461 val
= cpu_ldq_data_ra(env
, addr
, ra
);
462 fp
->d
= float64_to_floatx80(*(float64
*)&val
, &env
->fp_status
);
467 static int cpu_st_float64_ra(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
472 val
= floatx80_to_float64(fp
->d
, &env
->fp_status
);
473 cpu_stq_data_ra(env
, addr
, *(uint64_t *)&val
, ra
);
478 uint32_t HELPER(fmovemx_st_predec
)(CPUM68KState
*env
, uint32_t addr
,
481 return fmovem_predec(env
, addr
, mask
, cpu_st_floatx80_ra
);
484 uint32_t HELPER(fmovemx_st_postinc
)(CPUM68KState
*env
, uint32_t addr
,
487 return fmovem_postinc(env
, addr
, mask
, cpu_st_floatx80_ra
);
490 uint32_t HELPER(fmovemx_ld_postinc
)(CPUM68KState
*env
, uint32_t addr
,
493 return fmovem_postinc(env
, addr
, mask
, cpu_ld_floatx80_ra
);
496 uint32_t HELPER(fmovemd_st_predec
)(CPUM68KState
*env
, uint32_t addr
,
499 return fmovem_predec(env
, addr
, mask
, cpu_st_float64_ra
);
502 uint32_t HELPER(fmovemd_st_postinc
)(CPUM68KState
*env
, uint32_t addr
,
505 return fmovem_postinc(env
, addr
, mask
, cpu_st_float64_ra
);
508 uint32_t HELPER(fmovemd_ld_postinc
)(CPUM68KState
*env
, uint32_t addr
,
511 return fmovem_postinc(env
, addr
, mask
, cpu_ld_float64_ra
);
514 static void make_quotient(CPUM68KState
*env
, floatx80 val
)
519 if (floatx80_is_any_nan(val
)) {
523 quotient
= floatx80_to_int32(val
, &env
->fp_status
);
526 quotient
= -quotient
;
529 quotient
= (sign
<< 7) | (quotient
& 0x7f);
530 env
->fpsr
= (env
->fpsr
& ~FPSR_QT_MASK
) | (quotient
<< FPSR_QT_SHIFT
);
533 void HELPER(fmod
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
535 res
->d
= floatx80_mod(val1
->d
, val0
->d
, &env
->fp_status
);
537 make_quotient(env
, res
->d
);
540 void HELPER(frem
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
542 res
->d
= floatx80_rem(val1
->d
, val0
->d
, &env
->fp_status
);
544 make_quotient(env
, res
->d
);
547 void HELPER(fgetexp
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
549 res
->d
= floatx80_getexp(val
->d
, &env
->fp_status
);
552 void HELPER(fgetman
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
554 res
->d
= floatx80_getman(val
->d
, &env
->fp_status
);
557 void HELPER(fscale
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
559 res
->d
= floatx80_scale(val1
->d
, val0
->d
, &env
->fp_status
);
562 void HELPER(flognp1
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
564 res
->d
= floatx80_lognp1(val
->d
, &env
->fp_status
);
567 void HELPER(flogn
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
569 res
->d
= floatx80_logn(val
->d
, &env
->fp_status
);
572 void HELPER(flog10
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
574 res
->d
= floatx80_log10(val
->d
, &env
->fp_status
);
577 void HELPER(flog2
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
579 res
->d
= floatx80_log2(val
->d
, &env
->fp_status
);
582 void HELPER(fetox
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
584 res
->d
= floatx80_etox(val
->d
, &env
->fp_status
);
587 void HELPER(ftwotox
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
589 res
->d
= floatx80_twotox(val
->d
, &env
->fp_status
);
592 void HELPER(ftentox
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
594 res
->d
= floatx80_tentox(val
->d
, &env
->fp_status
);
597 void HELPER(ftan
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
599 res
->d
= floatx80_tan(val
->d
, &env
->fp_status
);
602 void HELPER(fsin
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
604 res
->d
= floatx80_sin(val
->d
, &env
->fp_status
);
607 void HELPER(fcos
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
609 res
->d
= floatx80_cos(val
->d
, &env
->fp_status
);
612 void HELPER(fsincos
)(CPUM68KState
*env
, FPReg
*res0
, FPReg
*res1
, FPReg
*val
)
616 * If res0 and res1 specify the same floating-point data register,
617 * the sine result is stored in the register, and the cosine
618 * result is discarded.
620 res1
->d
= floatx80_cos(a
, &env
->fp_status
);
621 res0
->d
= floatx80_sin(a
, &env
->fp_status
);
624 void HELPER(fatan
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
626 res
->d
= floatx80_atan(val
->d
, &env
->fp_status
);
629 void HELPER(fasin
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
631 res
->d
= floatx80_asin(val
->d
, &env
->fp_status
);
634 void HELPER(facos
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
636 res
->d
= floatx80_acos(val
->d
, &env
->fp_status
);
639 void HELPER(fatanh
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
641 res
->d
= floatx80_atanh(val
->d
, &env
->fp_status
);
644 void HELPER(ftanh
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
646 res
->d
= floatx80_tanh(val
->d
, &env
->fp_status
);
649 void HELPER(fsinh
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
651 res
->d
= floatx80_sinh(val
->d
, &env
->fp_status
);
654 void HELPER(fcosh
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
656 res
->d
= floatx80_cosh(val
->d
, &env
->fp_status
);