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"
28 /* Undefined offsets may be different on various FPU.
29 * On 68040 they return 0.0 (floatx80_zero)
32 static const floatx80 fpu_rom
[128] = {
33 [0x00] = make_floatx80_init(0x4000, 0xc90fdaa22168c235ULL
), /* Pi */
34 [0x0b] = make_floatx80_init(0x3ffd, 0x9a209a84fbcff798ULL
), /* Log10(2) */
35 [0x0c] = make_floatx80_init(0x4000, 0xadf85458a2bb4a9aULL
), /* e */
36 [0x0d] = make_floatx80_init(0x3fff, 0xb8aa3b295c17f0bcULL
), /* Log2(e) */
37 [0x0e] = make_floatx80_init(0x3ffd, 0xde5bd8a937287195ULL
), /* Log10(e) */
38 [0x0f] = make_floatx80_init(0x0000, 0x0000000000000000ULL
), /* Zero */
39 [0x30] = make_floatx80_init(0x3ffe, 0xb17217f7d1cf79acULL
), /* ln(2) */
40 [0x31] = make_floatx80_init(0x4000, 0x935d8dddaaa8ac17ULL
), /* ln(10) */
41 [0x32] = make_floatx80_init(0x3fff, 0x8000000000000000ULL
), /* 10^0 */
42 [0x33] = make_floatx80_init(0x4002, 0xa000000000000000ULL
), /* 10^1 */
43 [0x34] = make_floatx80_init(0x4005, 0xc800000000000000ULL
), /* 10^2 */
44 [0x35] = make_floatx80_init(0x400c, 0x9c40000000000000ULL
), /* 10^4 */
45 [0x36] = make_floatx80_init(0x4019, 0xbebc200000000000ULL
), /* 10^8 */
46 [0x37] = make_floatx80_init(0x4034, 0x8e1bc9bf04000000ULL
), /* 10^16 */
47 [0x38] = make_floatx80_init(0x4069, 0x9dc5ada82b70b59eULL
), /* 10^32 */
48 [0x39] = make_floatx80_init(0x40d3, 0xc2781f49ffcfa6d5ULL
), /* 10^64 */
49 [0x3a] = make_floatx80_init(0x41a8, 0x93ba47c980e98ce0ULL
), /* 10^128 */
50 [0x3b] = make_floatx80_init(0x4351, 0xaa7eebfb9df9de8eULL
), /* 10^256 */
51 [0x3c] = make_floatx80_init(0x46a3, 0xe319a0aea60e91c7ULL
), /* 10^512 */
52 [0x3d] = make_floatx80_init(0x4d48, 0xc976758681750c17ULL
), /* 10^1024 */
53 [0x3e] = make_floatx80_init(0x5a92, 0x9e8b3b5dc53d5de5ULL
), /* 10^2048 */
54 [0x3f] = make_floatx80_init(0x7525, 0xc46052028a20979bULL
), /* 10^4096 */
57 int32_t HELPER(reds32
)(CPUM68KState
*env
, FPReg
*val
)
59 return floatx80_to_int32(val
->d
, &env
->fp_status
);
62 float32
HELPER(redf32
)(CPUM68KState
*env
, FPReg
*val
)
64 return floatx80_to_float32(val
->d
, &env
->fp_status
);
67 void HELPER(exts32
)(CPUM68KState
*env
, FPReg
*res
, int32_t val
)
69 res
->d
= int32_to_floatx80(val
, &env
->fp_status
);
72 void HELPER(extf32
)(CPUM68KState
*env
, FPReg
*res
, float32 val
)
74 res
->d
= float32_to_floatx80(val
, &env
->fp_status
);
77 void HELPER(extf64
)(CPUM68KState
*env
, FPReg
*res
, float64 val
)
79 res
->d
= float64_to_floatx80(val
, &env
->fp_status
);
82 float64
HELPER(redf64
)(CPUM68KState
*env
, FPReg
*val
)
84 return floatx80_to_float64(val
->d
, &env
->fp_status
);
87 void HELPER(firound
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
89 res
->d
= floatx80_round_to_int(val
->d
, &env
->fp_status
);
92 static void m68k_restore_precision_mode(CPUM68KState
*env
)
94 switch (env
->fpcr
& FPCR_PREC_MASK
) {
95 case FPCR_PREC_X
: /* extended */
96 set_floatx80_rounding_precision(80, &env
->fp_status
);
98 case FPCR_PREC_S
: /* single */
99 set_floatx80_rounding_precision(32, &env
->fp_status
);
101 case FPCR_PREC_D
: /* double */
102 set_floatx80_rounding_precision(64, &env
->fp_status
);
104 case FPCR_PREC_U
: /* undefined */
110 static void cf_restore_precision_mode(CPUM68KState
*env
)
112 if (env
->fpcr
& FPCR_PREC_S
) { /* single */
113 set_floatx80_rounding_precision(32, &env
->fp_status
);
114 } else { /* double */
115 set_floatx80_rounding_precision(64, &env
->fp_status
);
119 static void restore_rounding_mode(CPUM68KState
*env
)
121 switch (env
->fpcr
& FPCR_RND_MASK
) {
122 case FPCR_RND_N
: /* round to nearest */
123 set_float_rounding_mode(float_round_nearest_even
, &env
->fp_status
);
125 case FPCR_RND_Z
: /* round to zero */
126 set_float_rounding_mode(float_round_to_zero
, &env
->fp_status
);
128 case FPCR_RND_M
: /* round toward minus infinity */
129 set_float_rounding_mode(float_round_down
, &env
->fp_status
);
131 case FPCR_RND_P
: /* round toward positive infinity */
132 set_float_rounding_mode(float_round_up
, &env
->fp_status
);
137 void cpu_m68k_set_fpcr(CPUM68KState
*env
, uint32_t val
)
139 env
->fpcr
= val
& 0xffff;
141 if (m68k_feature(env
, M68K_FEATURE_CF_FPU
)) {
142 cf_restore_precision_mode(env
);
144 m68k_restore_precision_mode(env
);
146 restore_rounding_mode(env
);
149 void HELPER(fitrunc
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
151 int rounding_mode
= get_float_rounding_mode(&env
->fp_status
);
152 set_float_rounding_mode(float_round_to_zero
, &env
->fp_status
);
153 res
->d
= floatx80_round_to_int(val
->d
, &env
->fp_status
);
154 set_float_rounding_mode(rounding_mode
, &env
->fp_status
);
157 void HELPER(set_fpcr
)(CPUM68KState
*env
, uint32_t val
)
159 cpu_m68k_set_fpcr(env
, val
);
162 #define PREC_BEGIN(prec) \
165 old = get_floatx80_rounding_precision(&env->fp_status); \
166 set_floatx80_rounding_precision(prec, &env->fp_status) \
169 set_floatx80_rounding_precision(old, &env->fp_status); \
172 void HELPER(fsround
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
175 res
->d
= floatx80_round(val
->d
, &env
->fp_status
);
179 void HELPER(fdround
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
182 res
->d
= floatx80_round(val
->d
, &env
->fp_status
);
186 void HELPER(fsqrt
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
188 res
->d
= floatx80_sqrt(val
->d
, &env
->fp_status
);
191 void HELPER(fssqrt
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
194 res
->d
= floatx80_sqrt(val
->d
, &env
->fp_status
);
198 void HELPER(fdsqrt
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
201 res
->d
= floatx80_sqrt(val
->d
, &env
->fp_status
);
205 void HELPER(fabs
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
207 res
->d
= floatx80_round(floatx80_abs(val
->d
), &env
->fp_status
);
210 void HELPER(fsabs
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
213 res
->d
= floatx80_round(floatx80_abs(val
->d
), &env
->fp_status
);
217 void HELPER(fdabs
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
220 res
->d
= floatx80_round(floatx80_abs(val
->d
), &env
->fp_status
);
224 void HELPER(fneg
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
226 res
->d
= floatx80_round(floatx80_chs(val
->d
), &env
->fp_status
);
229 void HELPER(fsneg
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
232 res
->d
= floatx80_round(floatx80_chs(val
->d
), &env
->fp_status
);
236 void HELPER(fdneg
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
239 res
->d
= floatx80_round(floatx80_chs(val
->d
), &env
->fp_status
);
243 void HELPER(fadd
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
245 res
->d
= floatx80_add(val0
->d
, val1
->d
, &env
->fp_status
);
248 void HELPER(fsadd
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
251 res
->d
= floatx80_add(val0
->d
, val1
->d
, &env
->fp_status
);
255 void HELPER(fdadd
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
258 res
->d
= floatx80_add(val0
->d
, val1
->d
, &env
->fp_status
);
262 void HELPER(fsub
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
264 res
->d
= floatx80_sub(val1
->d
, val0
->d
, &env
->fp_status
);
267 void HELPER(fssub
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
270 res
->d
= floatx80_sub(val1
->d
, val0
->d
, &env
->fp_status
);
274 void HELPER(fdsub
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
277 res
->d
= floatx80_sub(val1
->d
, val0
->d
, &env
->fp_status
);
281 void HELPER(fmul
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
283 res
->d
= floatx80_mul(val0
->d
, val1
->d
, &env
->fp_status
);
286 void HELPER(fsmul
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
289 res
->d
= floatx80_mul(val0
->d
, val1
->d
, &env
->fp_status
);
293 void HELPER(fdmul
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
296 res
->d
= floatx80_mul(val0
->d
, val1
->d
, &env
->fp_status
);
300 void HELPER(fsglmul
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
302 int rounding_mode
= get_float_rounding_mode(&env
->fp_status
);
306 set_float_rounding_mode(float_round_to_zero
, &env
->fp_status
);
307 a
= floatx80_round(val0
->d
, &env
->fp_status
);
308 b
= floatx80_round(val1
->d
, &env
->fp_status
);
309 set_float_rounding_mode(rounding_mode
, &env
->fp_status
);
310 res
->d
= floatx80_mul(a
, b
, &env
->fp_status
);
314 void HELPER(fdiv
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
316 res
->d
= floatx80_div(val1
->d
, val0
->d
, &env
->fp_status
);
319 void HELPER(fsdiv
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
322 res
->d
= floatx80_div(val1
->d
, val0
->d
, &env
->fp_status
);
326 void HELPER(fddiv
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
329 res
->d
= floatx80_div(val1
->d
, val0
->d
, &env
->fp_status
);
333 void HELPER(fsgldiv
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
335 int rounding_mode
= get_float_rounding_mode(&env
->fp_status
);
339 set_float_rounding_mode(float_round_to_zero
, &env
->fp_status
);
340 a
= floatx80_round(val1
->d
, &env
->fp_status
);
341 b
= floatx80_round(val0
->d
, &env
->fp_status
);
342 set_float_rounding_mode(rounding_mode
, &env
->fp_status
);
343 res
->d
= floatx80_div(a
, b
, &env
->fp_status
);
347 static int float_comp_to_cc(int float_compare
)
349 switch (float_compare
) {
350 case float_relation_equal
:
352 case float_relation_less
:
354 case float_relation_unordered
:
356 case float_relation_greater
:
359 g_assert_not_reached();
363 void HELPER(fcmp
)(CPUM68KState
*env
, FPReg
*val0
, FPReg
*val1
)
367 float_compare
= floatx80_compare(val1
->d
, val0
->d
, &env
->fp_status
);
368 env
->fpsr
= (env
->fpsr
& ~FPSR_CC_MASK
) | float_comp_to_cc(float_compare
);
371 void HELPER(ftst
)(CPUM68KState
*env
, FPReg
*val
)
375 if (floatx80_is_neg(val
->d
)) {
379 if (floatx80_is_any_nan(val
->d
)) {
381 } else if (floatx80_is_infinity(val
->d
)) {
383 } else if (floatx80_is_zero(val
->d
)) {
386 env
->fpsr
= (env
->fpsr
& ~FPSR_CC_MASK
) | cc
;
389 void HELPER(fconst
)(CPUM68KState
*env
, FPReg
*val
, uint32_t offset
)
391 val
->d
= fpu_rom
[offset
];
394 typedef int (*float_access
)(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
397 static uint32_t fmovem_predec(CPUM68KState
*env
, uint32_t addr
, uint32_t mask
,
400 uintptr_t ra
= GETPC();
403 for (i
= 7; i
>= 0; i
--, mask
<<= 1) {
405 size
= access(env
, addr
, &env
->fregs
[i
], ra
);
406 if ((mask
& 0xff) != 0x80) {
415 static uint32_t fmovem_postinc(CPUM68KState
*env
, uint32_t addr
, uint32_t mask
,
418 uintptr_t ra
= GETPC();
421 for (i
= 0; i
< 8; i
++, mask
<<= 1) {
423 size
= access(env
, addr
, &env
->fregs
[i
], ra
);
431 static int cpu_ld_floatx80_ra(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
437 high
= cpu_ldl_data_ra(env
, addr
, ra
);
438 low
= cpu_ldq_data_ra(env
, addr
+ 4, ra
);
440 fp
->l
.upper
= high
>> 16;
446 static int cpu_st_floatx80_ra(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
449 cpu_stl_data_ra(env
, addr
, fp
->l
.upper
<< 16, ra
);
450 cpu_stq_data_ra(env
, addr
+ 4, fp
->l
.lower
, ra
);
455 static int cpu_ld_float64_ra(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
460 val
= cpu_ldq_data_ra(env
, addr
, ra
);
461 fp
->d
= float64_to_floatx80(*(float64
*)&val
, &env
->fp_status
);
466 static int cpu_st_float64_ra(CPUM68KState
*env
, uint32_t addr
, FPReg
*fp
,
471 val
= floatx80_to_float64(fp
->d
, &env
->fp_status
);
472 cpu_stq_data_ra(env
, addr
, *(uint64_t *)&val
, ra
);
477 uint32_t HELPER(fmovemx_st_predec
)(CPUM68KState
*env
, uint32_t addr
,
480 return fmovem_predec(env
, addr
, mask
, cpu_st_floatx80_ra
);
483 uint32_t HELPER(fmovemx_st_postinc
)(CPUM68KState
*env
, uint32_t addr
,
486 return fmovem_postinc(env
, addr
, mask
, cpu_st_floatx80_ra
);
489 uint32_t HELPER(fmovemx_ld_postinc
)(CPUM68KState
*env
, uint32_t addr
,
492 return fmovem_postinc(env
, addr
, mask
, cpu_ld_floatx80_ra
);
495 uint32_t HELPER(fmovemd_st_predec
)(CPUM68KState
*env
, uint32_t addr
,
498 return fmovem_predec(env
, addr
, mask
, cpu_st_float64_ra
);
501 uint32_t HELPER(fmovemd_st_postinc
)(CPUM68KState
*env
, uint32_t addr
,
504 return fmovem_postinc(env
, addr
, mask
, cpu_st_float64_ra
);
507 uint32_t HELPER(fmovemd_ld_postinc
)(CPUM68KState
*env
, uint32_t addr
,
510 return fmovem_postinc(env
, addr
, mask
, cpu_ld_float64_ra
);
513 static void make_quotient(CPUM68KState
*env
, floatx80 val
)
518 if (floatx80_is_any_nan(val
)) {
522 quotient
= floatx80_to_int32(val
, &env
->fp_status
);
525 quotient
= -quotient
;
528 quotient
= (sign
<< 7) | (quotient
& 0x7f);
529 env
->fpsr
= (env
->fpsr
& ~FPSR_QT_MASK
) | (quotient
<< FPSR_QT_SHIFT
);
532 void HELPER(fmod
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
534 res
->d
= floatx80_mod(val1
->d
, val0
->d
, &env
->fp_status
);
536 make_quotient(env
, res
->d
);
539 void HELPER(frem
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
541 res
->d
= floatx80_rem(val1
->d
, val0
->d
, &env
->fp_status
);
543 make_quotient(env
, res
->d
);
546 void HELPER(fgetexp
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
548 res
->d
= floatx80_getexp(val
->d
, &env
->fp_status
);
551 void HELPER(fgetman
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
553 res
->d
= floatx80_getman(val
->d
, &env
->fp_status
);
556 void HELPER(fscale
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val0
, FPReg
*val1
)
558 res
->d
= floatx80_scale(val1
->d
, val0
->d
, &env
->fp_status
);
561 void HELPER(flognp1
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
563 res
->d
= floatx80_lognp1(val
->d
, &env
->fp_status
);
566 void HELPER(flogn
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
568 res
->d
= floatx80_logn(val
->d
, &env
->fp_status
);
571 void HELPER(flog10
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
573 res
->d
= floatx80_log10(val
->d
, &env
->fp_status
);
576 void HELPER(flog2
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
578 res
->d
= floatx80_log2(val
->d
, &env
->fp_status
);
581 void HELPER(fetox
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
583 res
->d
= floatx80_etox(val
->d
, &env
->fp_status
);
586 void HELPER(ftwotox
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
588 res
->d
= floatx80_twotox(val
->d
, &env
->fp_status
);
591 void HELPER(ftentox
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
593 res
->d
= floatx80_tentox(val
->d
, &env
->fp_status
);
596 void HELPER(ftan
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
598 res
->d
= floatx80_tan(val
->d
, &env
->fp_status
);
601 void HELPER(fsin
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
603 res
->d
= floatx80_sin(val
->d
, &env
->fp_status
);
606 void HELPER(fcos
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
608 res
->d
= floatx80_cos(val
->d
, &env
->fp_status
);
611 void HELPER(fsincos
)(CPUM68KState
*env
, FPReg
*res0
, FPReg
*res1
, FPReg
*val
)
614 /* If res0 and res1 specify the same floating-point data register,
615 * the sine result is stored in the register, and the cosine
616 * result is discarded.
618 res1
->d
= floatx80_cos(a
, &env
->fp_status
);
619 res0
->d
= floatx80_sin(a
, &env
->fp_status
);
622 void HELPER(fatan
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
624 res
->d
= floatx80_atan(val
->d
, &env
->fp_status
);
627 void HELPER(fasin
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
629 res
->d
= floatx80_asin(val
->d
, &env
->fp_status
);
632 void HELPER(facos
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
634 res
->d
= floatx80_acos(val
->d
, &env
->fp_status
);
637 void HELPER(fatanh
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
639 res
->d
= floatx80_atanh(val
->d
, &env
->fp_status
);
642 void HELPER(ftanh
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
644 res
->d
= floatx80_tanh(val
->d
, &env
->fp_status
);
647 void HELPER(fsinh
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
649 res
->d
= floatx80_sinh(val
->d
, &env
->fp_status
);
652 void HELPER(fcosh
)(CPUM68KState
*env
, FPReg
*res
, FPReg
*val
)
654 res
->d
= floatx80_cosh(val
->d
, &env
->fp_status
);