ppc: Improve emulation of THRM registers
[qemu/kevin.git] / target-alpha / fpu_helper.c
blob9645978aaaae0f6b6cb2179147f86608e9cbe788
1 /*
2 * Helpers for floating point instructions.
4 * Copyright (c) 2007 Jocelyn Mayer
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 #include "qemu/osdep.h"
21 #include "cpu.h"
22 #include "exec/exec-all.h"
23 #include "exec/helper-proto.h"
24 #include "fpu/softfloat.h"
26 #define FP_STATUS (env->fp_status)
29 void helper_setroundmode(CPUAlphaState *env, uint32_t val)
31 set_float_rounding_mode(val, &FP_STATUS);
34 void helper_setflushzero(CPUAlphaState *env, uint32_t val)
36 set_flush_to_zero(val, &FP_STATUS);
39 #define CONVERT_BIT(X, SRC, DST) \
40 (SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC))
42 static uint32_t soft_to_fpcr_exc(CPUAlphaState *env)
44 uint8_t exc = get_float_exception_flags(&FP_STATUS);
45 uint32_t ret = 0;
47 if (unlikely(exc)) {
48 set_float_exception_flags(0, &FP_STATUS);
49 ret |= CONVERT_BIT(exc, float_flag_invalid, FPCR_INV);
50 ret |= CONVERT_BIT(exc, float_flag_divbyzero, FPCR_DZE);
51 ret |= CONVERT_BIT(exc, float_flag_overflow, FPCR_OVF);
52 ret |= CONVERT_BIT(exc, float_flag_underflow, FPCR_UNF);
53 ret |= CONVERT_BIT(exc, float_flag_inexact, FPCR_INE);
56 return ret;
59 static void fp_exc_raise1(CPUAlphaState *env, uintptr_t retaddr,
60 uint32_t exc, uint32_t regno, uint32_t hw_exc)
62 hw_exc |= CONVERT_BIT(exc, FPCR_INV, EXC_M_INV);
63 hw_exc |= CONVERT_BIT(exc, FPCR_DZE, EXC_M_DZE);
64 hw_exc |= CONVERT_BIT(exc, FPCR_OVF, EXC_M_FOV);
65 hw_exc |= CONVERT_BIT(exc, FPCR_UNF, EXC_M_UNF);
66 hw_exc |= CONVERT_BIT(exc, FPCR_INE, EXC_M_INE);
67 hw_exc |= CONVERT_BIT(exc, FPCR_IOV, EXC_M_IOV);
69 arith_excp(env, retaddr, hw_exc, 1ull << regno);
72 /* Raise exceptions for ieee fp insns without software completion.
73 In that case there are no exceptions that don't trap; the mask
74 doesn't apply. */
75 void helper_fp_exc_raise(CPUAlphaState *env, uint32_t ignore, uint32_t regno)
77 uint32_t exc = env->error_code;
78 if (exc) {
79 env->fpcr |= exc;
80 exc &= ~ignore;
81 if (exc) {
82 fp_exc_raise1(env, GETPC(), exc, regno, 0);
87 /* Raise exceptions for ieee fp insns with software completion. */
88 void helper_fp_exc_raise_s(CPUAlphaState *env, uint32_t ignore, uint32_t regno)
90 uint32_t exc = env->error_code & ~ignore;
91 if (exc) {
92 env->fpcr |= exc;
93 exc &= ~ignore;
94 if (exc) {
95 exc &= env->fpcr_exc_enable;
96 fp_exc_raise1(env, GETPC(), exc, regno, EXC_M_SWC);
101 /* Input handing without software completion. Trap for all
102 non-finite numbers. */
103 void helper_ieee_input(CPUAlphaState *env, uint64_t val)
105 uint32_t exp = (uint32_t)(val >> 52) & 0x7ff;
106 uint64_t frac = val & 0xfffffffffffffull;
108 if (exp == 0) {
109 /* Denormals without /S raise an exception. */
110 if (frac != 0) {
111 arith_excp(env, GETPC(), EXC_M_INV, 0);
113 } else if (exp == 0x7ff) {
114 /* Infinity or NaN. */
115 env->fpcr |= FPCR_INV;
116 arith_excp(env, GETPC(), EXC_M_INV, 0);
120 /* Similar, but does not trap for infinities. Used for comparisons. */
121 void helper_ieee_input_cmp(CPUAlphaState *env, uint64_t val)
123 uint32_t exp = (uint32_t)(val >> 52) & 0x7ff;
124 uint64_t frac = val & 0xfffffffffffffull;
126 if (exp == 0) {
127 /* Denormals without /S raise an exception. */
128 if (frac != 0) {
129 arith_excp(env, GETPC(), EXC_M_INV, 0);
131 } else if (exp == 0x7ff && frac) {
132 /* NaN. */
133 env->fpcr |= FPCR_INV;
134 arith_excp(env, GETPC(), EXC_M_INV, 0);
138 /* Input handing with software completion. Trap for denorms, unless DNZ
139 is set. If we try to support DNOD (which none of the produced hardware
140 did, AFAICS), we'll need to suppress the trap when FPCR.DNOD is set;
141 then the code downstream of that will need to cope with denorms sans
142 flush_input_to_zero. Most of it should work sanely, but there's
143 nothing to compare with. */
144 void helper_ieee_input_s(CPUAlphaState *env, uint64_t val)
146 if (unlikely(2 * val - 1 < 0x1fffffffffffffull)
147 && !env->fp_status.flush_inputs_to_zero) {
148 arith_excp(env, GETPC(), EXC_M_INV | EXC_M_SWC, 0);
152 /* S floating (single) */
154 /* Taken from linux/arch/alpha/kernel/traps.c, s_mem_to_reg. */
155 static inline uint64_t float32_to_s_int(uint32_t fi)
157 uint32_t frac = fi & 0x7fffff;
158 uint32_t sign = fi >> 31;
159 uint32_t exp_msb = (fi >> 30) & 1;
160 uint32_t exp_low = (fi >> 23) & 0x7f;
161 uint32_t exp;
163 exp = (exp_msb << 10) | exp_low;
164 if (exp_msb) {
165 if (exp_low == 0x7f) {
166 exp = 0x7ff;
168 } else {
169 if (exp_low != 0x00) {
170 exp |= 0x380;
174 return (((uint64_t)sign << 63)
175 | ((uint64_t)exp << 52)
176 | ((uint64_t)frac << 29));
179 static inline uint64_t float32_to_s(float32 fa)
181 CPU_FloatU a;
182 a.f = fa;
183 return float32_to_s_int(a.l);
186 static inline uint32_t s_to_float32_int(uint64_t a)
188 return ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff);
191 static inline float32 s_to_float32(uint64_t a)
193 CPU_FloatU r;
194 r.l = s_to_float32_int(a);
195 return r.f;
198 uint32_t helper_s_to_memory(uint64_t a)
200 return s_to_float32_int(a);
203 uint64_t helper_memory_to_s(uint32_t a)
205 return float32_to_s_int(a);
208 uint64_t helper_adds(CPUAlphaState *env, uint64_t a, uint64_t b)
210 float32 fa, fb, fr;
212 fa = s_to_float32(a);
213 fb = s_to_float32(b);
214 fr = float32_add(fa, fb, &FP_STATUS);
215 env->error_code = soft_to_fpcr_exc(env);
217 return float32_to_s(fr);
220 uint64_t helper_subs(CPUAlphaState *env, uint64_t a, uint64_t b)
222 float32 fa, fb, fr;
224 fa = s_to_float32(a);
225 fb = s_to_float32(b);
226 fr = float32_sub(fa, fb, &FP_STATUS);
227 env->error_code = soft_to_fpcr_exc(env);
229 return float32_to_s(fr);
232 uint64_t helper_muls(CPUAlphaState *env, uint64_t a, uint64_t b)
234 float32 fa, fb, fr;
236 fa = s_to_float32(a);
237 fb = s_to_float32(b);
238 fr = float32_mul(fa, fb, &FP_STATUS);
239 env->error_code = soft_to_fpcr_exc(env);
241 return float32_to_s(fr);
244 uint64_t helper_divs(CPUAlphaState *env, uint64_t a, uint64_t b)
246 float32 fa, fb, fr;
248 fa = s_to_float32(a);
249 fb = s_to_float32(b);
250 fr = float32_div(fa, fb, &FP_STATUS);
251 env->error_code = soft_to_fpcr_exc(env);
253 return float32_to_s(fr);
256 uint64_t helper_sqrts(CPUAlphaState *env, uint64_t a)
258 float32 fa, fr;
260 fa = s_to_float32(a);
261 fr = float32_sqrt(fa, &FP_STATUS);
262 env->error_code = soft_to_fpcr_exc(env);
264 return float32_to_s(fr);
268 /* T floating (double) */
269 static inline float64 t_to_float64(uint64_t a)
271 /* Memory format is the same as float64 */
272 CPU_DoubleU r;
273 r.ll = a;
274 return r.d;
277 static inline uint64_t float64_to_t(float64 fa)
279 /* Memory format is the same as float64 */
280 CPU_DoubleU r;
281 r.d = fa;
282 return r.ll;
285 uint64_t helper_addt(CPUAlphaState *env, uint64_t a, uint64_t b)
287 float64 fa, fb, fr;
289 fa = t_to_float64(a);
290 fb = t_to_float64(b);
291 fr = float64_add(fa, fb, &FP_STATUS);
292 env->error_code = soft_to_fpcr_exc(env);
294 return float64_to_t(fr);
297 uint64_t helper_subt(CPUAlphaState *env, uint64_t a, uint64_t b)
299 float64 fa, fb, fr;
301 fa = t_to_float64(a);
302 fb = t_to_float64(b);
303 fr = float64_sub(fa, fb, &FP_STATUS);
304 env->error_code = soft_to_fpcr_exc(env);
306 return float64_to_t(fr);
309 uint64_t helper_mult(CPUAlphaState *env, uint64_t a, uint64_t b)
311 float64 fa, fb, fr;
313 fa = t_to_float64(a);
314 fb = t_to_float64(b);
315 fr = float64_mul(fa, fb, &FP_STATUS);
316 env->error_code = soft_to_fpcr_exc(env);
318 return float64_to_t(fr);
321 uint64_t helper_divt(CPUAlphaState *env, uint64_t a, uint64_t b)
323 float64 fa, fb, fr;
325 fa = t_to_float64(a);
326 fb = t_to_float64(b);
327 fr = float64_div(fa, fb, &FP_STATUS);
328 env->error_code = soft_to_fpcr_exc(env);
330 return float64_to_t(fr);
333 uint64_t helper_sqrtt(CPUAlphaState *env, uint64_t a)
335 float64 fa, fr;
337 fa = t_to_float64(a);
338 fr = float64_sqrt(fa, &FP_STATUS);
339 env->error_code = soft_to_fpcr_exc(env);
341 return float64_to_t(fr);
344 /* Comparisons */
345 uint64_t helper_cmptun(CPUAlphaState *env, uint64_t a, uint64_t b)
347 float64 fa, fb;
348 uint64_t ret = 0;
350 fa = t_to_float64(a);
351 fb = t_to_float64(b);
353 if (float64_unordered_quiet(fa, fb, &FP_STATUS)) {
354 ret = 0x4000000000000000ULL;
356 env->error_code = soft_to_fpcr_exc(env);
358 return ret;
361 uint64_t helper_cmpteq(CPUAlphaState *env, uint64_t a, uint64_t b)
363 float64 fa, fb;
364 uint64_t ret = 0;
366 fa = t_to_float64(a);
367 fb = t_to_float64(b);
369 if (float64_eq_quiet(fa, fb, &FP_STATUS)) {
370 ret = 0x4000000000000000ULL;
372 env->error_code = soft_to_fpcr_exc(env);
374 return ret;
377 uint64_t helper_cmptle(CPUAlphaState *env, uint64_t a, uint64_t b)
379 float64 fa, fb;
380 uint64_t ret = 0;
382 fa = t_to_float64(a);
383 fb = t_to_float64(b);
385 if (float64_le(fa, fb, &FP_STATUS)) {
386 ret = 0x4000000000000000ULL;
388 env->error_code = soft_to_fpcr_exc(env);
390 return ret;
393 uint64_t helper_cmptlt(CPUAlphaState *env, uint64_t a, uint64_t b)
395 float64 fa, fb;
396 uint64_t ret = 0;
398 fa = t_to_float64(a);
399 fb = t_to_float64(b);
401 if (float64_lt(fa, fb, &FP_STATUS)) {
402 ret = 0x4000000000000000ULL;
404 env->error_code = soft_to_fpcr_exc(env);
406 return ret;
409 /* Floating point format conversion */
410 uint64_t helper_cvtts(CPUAlphaState *env, uint64_t a)
412 float64 fa;
413 float32 fr;
415 fa = t_to_float64(a);
416 fr = float64_to_float32(fa, &FP_STATUS);
417 env->error_code = soft_to_fpcr_exc(env);
419 return float32_to_s(fr);
422 uint64_t helper_cvtst(CPUAlphaState *env, uint64_t a)
424 float32 fa;
425 float64 fr;
427 fa = s_to_float32(a);
428 fr = float32_to_float64(fa, &FP_STATUS);
429 env->error_code = soft_to_fpcr_exc(env);
431 return float64_to_t(fr);
434 uint64_t helper_cvtqs(CPUAlphaState *env, uint64_t a)
436 float32 fr = int64_to_float32(a, &FP_STATUS);
437 env->error_code = soft_to_fpcr_exc(env);
439 return float32_to_s(fr);
442 /* Implement float64 to uint64_t conversion without saturation -- we must
443 supply the truncated result. This behaviour is used by the compiler
444 to get unsigned conversion for free with the same instruction. */
446 static uint64_t do_cvttq(CPUAlphaState *env, uint64_t a, int roundmode)
448 uint64_t frac, ret = 0;
449 uint32_t exp, sign, exc = 0;
450 int shift;
452 sign = (a >> 63);
453 exp = (uint32_t)(a >> 52) & 0x7ff;
454 frac = a & 0xfffffffffffffull;
456 if (exp == 0) {
457 if (unlikely(frac != 0) && !env->fp_status.flush_inputs_to_zero) {
458 goto do_underflow;
460 } else if (exp == 0x7ff) {
461 exc = FPCR_INV;
462 } else {
463 /* Restore implicit bit. */
464 frac |= 0x10000000000000ull;
466 shift = exp - 1023 - 52;
467 if (shift >= 0) {
468 /* In this case the number is so large that we must shift
469 the fraction left. There is no rounding to do. */
470 if (shift < 64) {
471 ret = frac << shift;
473 /* Check for overflow. Note the special case of -0x1p63. */
474 if (shift >= 11 && a != 0xC3E0000000000000ull) {
475 exc = FPCR_IOV | FPCR_INE;
477 } else {
478 uint64_t round;
480 /* In this case the number is smaller than the fraction as
481 represented by the 52 bit number. Here we must think
482 about rounding the result. Handle this by shifting the
483 fractional part of the number into the high bits of ROUND.
484 This will let us efficiently handle round-to-nearest. */
485 shift = -shift;
486 if (shift < 63) {
487 ret = frac >> shift;
488 round = frac << (64 - shift);
489 } else {
490 /* The exponent is so small we shift out everything.
491 Leave a sticky bit for proper rounding below. */
492 do_underflow:
493 round = 1;
496 if (round) {
497 exc = FPCR_INE;
498 switch (roundmode) {
499 case float_round_nearest_even:
500 if (round == (1ull << 63)) {
501 /* Fraction is exactly 0.5; round to even. */
502 ret += (ret & 1);
503 } else if (round > (1ull << 63)) {
504 ret += 1;
506 break;
507 case float_round_to_zero:
508 break;
509 case float_round_up:
510 ret += 1 - sign;
511 break;
512 case float_round_down:
513 ret += sign;
514 break;
518 if (sign) {
519 ret = -ret;
522 env->error_code = exc;
524 return ret;
527 uint64_t helper_cvttq(CPUAlphaState *env, uint64_t a)
529 return do_cvttq(env, a, FP_STATUS.float_rounding_mode);
532 uint64_t helper_cvttq_c(CPUAlphaState *env, uint64_t a)
534 return do_cvttq(env, a, float_round_to_zero);
537 uint64_t helper_cvtqt(CPUAlphaState *env, uint64_t a)
539 float64 fr = int64_to_float64(a, &FP_STATUS);
540 env->error_code = soft_to_fpcr_exc(env);
541 return float64_to_t(fr);
544 uint64_t helper_cvtql(CPUAlphaState *env, uint64_t val)
546 uint32_t exc = 0;
547 if (val != (int32_t)val) {
548 exc = FPCR_IOV | FPCR_INE;
550 env->error_code = exc;
552 return ((val & 0xc0000000) << 32) | ((val & 0x3fffffff) << 29);