]> git.proxmox.com Git - qemu.git/blob - target-sh4/op_helper.c
target-sh4: implement FPU exceptions
[qemu.git] / target-sh4 / op_helper.c
1 /*
2 * SH4 emulation
3 *
4 * Copyright (c) 2005 Samuel Tardieu
5 *
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.
10 *
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.
15 *
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/>.
18 */
19 #include <assert.h>
20 #include <stdlib.h>
21 #include "exec.h"
22 #include "helper.h"
23
24 static void cpu_restore_state_from_retaddr(void *retaddr)
25 {
26 TranslationBlock *tb;
27 unsigned long pc;
28
29 if (retaddr) {
30 pc = (unsigned long) retaddr;
31 tb = tb_find_pc(pc);
32 if (tb) {
33 /* the PC is inside the translated code. It means that we have
34 a virtual CPU fault */
35 cpu_restore_state(tb, env, pc, NULL);
36 }
37 }
38 }
39
40 #ifndef CONFIG_USER_ONLY
41
42 #define MMUSUFFIX _mmu
43
44 #define SHIFT 0
45 #include "softmmu_template.h"
46
47 #define SHIFT 1
48 #include "softmmu_template.h"
49
50 #define SHIFT 2
51 #include "softmmu_template.h"
52
53 #define SHIFT 3
54 #include "softmmu_template.h"
55
56 void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr)
57 {
58 CPUState *saved_env;
59 int ret;
60
61 /* XXX: hack to restore env in all cases, even if not called from
62 generated code */
63 saved_env = env;
64 env = cpu_single_env;
65 ret = cpu_sh4_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
66 if (ret) {
67 /* now we have a real cpu fault */
68 cpu_restore_state_from_retaddr(retaddr);
69 cpu_loop_exit();
70 }
71 env = saved_env;
72 }
73
74 #endif
75
76 void helper_ldtlb(void)
77 {
78 #ifdef CONFIG_USER_ONLY
79 /* XXXXX */
80 cpu_abort(env, "Unhandled ldtlb");
81 #else
82 cpu_load_tlb(env);
83 #endif
84 }
85
86 void helper_raise_illegal_instruction(void)
87 {
88 env->exception_index = 0x180;
89 cpu_loop_exit();
90 }
91
92 void helper_raise_slot_illegal_instruction(void)
93 {
94 env->exception_index = 0x1a0;
95 cpu_loop_exit();
96 }
97
98 void helper_raise_fpu_disable(void)
99 {
100 env->exception_index = 0x800;
101 cpu_loop_exit();
102 }
103
104 void helper_raise_slot_fpu_disable(void)
105 {
106 env->exception_index = 0x820;
107 cpu_loop_exit();
108 }
109
110 void helper_debug(void)
111 {
112 env->exception_index = EXCP_DEBUG;
113 cpu_loop_exit();
114 }
115
116 void helper_sleep(uint32_t next_pc)
117 {
118 env->halted = 1;
119 env->exception_index = EXCP_HLT;
120 env->pc = next_pc;
121 cpu_loop_exit();
122 }
123
124 void helper_trapa(uint32_t tra)
125 {
126 env->tra = tra << 2;
127 env->exception_index = 0x160;
128 cpu_loop_exit();
129 }
130
131 void helper_movcal(uint32_t address, uint32_t value)
132 {
133 if (cpu_sh4_is_cached (env, address))
134 {
135 memory_content *r = malloc (sizeof(memory_content));
136 r->address = address;
137 r->value = value;
138 r->next = NULL;
139
140 *(env->movcal_backup_tail) = r;
141 env->movcal_backup_tail = &(r->next);
142 }
143 }
144
145 void helper_discard_movcal_backup(void)
146 {
147 memory_content *current = env->movcal_backup;
148
149 while(current)
150 {
151 memory_content *next = current->next;
152 free (current);
153 env->movcal_backup = current = next;
154 if (current == NULL)
155 env->movcal_backup_tail = &(env->movcal_backup);
156 }
157 }
158
159 void helper_ocbi(uint32_t address)
160 {
161 memory_content **current = &(env->movcal_backup);
162 while (*current)
163 {
164 uint32_t a = (*current)->address;
165 if ((a & ~0x1F) == (address & ~0x1F))
166 {
167 memory_content *next = (*current)->next;
168 stl(a, (*current)->value);
169
170 if (next == NULL)
171 {
172 env->movcal_backup_tail = current;
173 }
174
175 free (*current);
176 *current = next;
177 break;
178 }
179 }
180 }
181
182 uint32_t helper_addc(uint32_t arg0, uint32_t arg1)
183 {
184 uint32_t tmp0, tmp1;
185
186 tmp1 = arg0 + arg1;
187 tmp0 = arg1;
188 arg1 = tmp1 + (env->sr & 1);
189 if (tmp0 > tmp1)
190 env->sr |= SR_T;
191 else
192 env->sr &= ~SR_T;
193 if (tmp1 > arg1)
194 env->sr |= SR_T;
195 return arg1;
196 }
197
198 uint32_t helper_addv(uint32_t arg0, uint32_t arg1)
199 {
200 uint32_t dest, src, ans;
201
202 if ((int32_t) arg1 >= 0)
203 dest = 0;
204 else
205 dest = 1;
206 if ((int32_t) arg0 >= 0)
207 src = 0;
208 else
209 src = 1;
210 src += dest;
211 arg1 += arg0;
212 if ((int32_t) arg1 >= 0)
213 ans = 0;
214 else
215 ans = 1;
216 ans += dest;
217 if (src == 0 || src == 2) {
218 if (ans == 1)
219 env->sr |= SR_T;
220 else
221 env->sr &= ~SR_T;
222 } else
223 env->sr &= ~SR_T;
224 return arg1;
225 }
226
227 #define T (env->sr & SR_T)
228 #define Q (env->sr & SR_Q ? 1 : 0)
229 #define M (env->sr & SR_M ? 1 : 0)
230 #define SETT env->sr |= SR_T
231 #define CLRT env->sr &= ~SR_T
232 #define SETQ env->sr |= SR_Q
233 #define CLRQ env->sr &= ~SR_Q
234 #define SETM env->sr |= SR_M
235 #define CLRM env->sr &= ~SR_M
236
237 uint32_t helper_div1(uint32_t arg0, uint32_t arg1)
238 {
239 uint32_t tmp0, tmp2;
240 uint8_t old_q, tmp1 = 0xff;
241
242 //printf("div1 arg0=0x%08x arg1=0x%08x M=%d Q=%d T=%d\n", arg0, arg1, M, Q, T);
243 old_q = Q;
244 if ((0x80000000 & arg1) != 0)
245 SETQ;
246 else
247 CLRQ;
248 tmp2 = arg0;
249 arg1 <<= 1;
250 arg1 |= T;
251 switch (old_q) {
252 case 0:
253 switch (M) {
254 case 0:
255 tmp0 = arg1;
256 arg1 -= tmp2;
257 tmp1 = arg1 > tmp0;
258 switch (Q) {
259 case 0:
260 if (tmp1)
261 SETQ;
262 else
263 CLRQ;
264 break;
265 case 1:
266 if (tmp1 == 0)
267 SETQ;
268 else
269 CLRQ;
270 break;
271 }
272 break;
273 case 1:
274 tmp0 = arg1;
275 arg1 += tmp2;
276 tmp1 = arg1 < tmp0;
277 switch (Q) {
278 case 0:
279 if (tmp1 == 0)
280 SETQ;
281 else
282 CLRQ;
283 break;
284 case 1:
285 if (tmp1)
286 SETQ;
287 else
288 CLRQ;
289 break;
290 }
291 break;
292 }
293 break;
294 case 1:
295 switch (M) {
296 case 0:
297 tmp0 = arg1;
298 arg1 += tmp2;
299 tmp1 = arg1 < tmp0;
300 switch (Q) {
301 case 0:
302 if (tmp1)
303 SETQ;
304 else
305 CLRQ;
306 break;
307 case 1:
308 if (tmp1 == 0)
309 SETQ;
310 else
311 CLRQ;
312 break;
313 }
314 break;
315 case 1:
316 tmp0 = arg1;
317 arg1 -= tmp2;
318 tmp1 = arg1 > tmp0;
319 switch (Q) {
320 case 0:
321 if (tmp1 == 0)
322 SETQ;
323 else
324 CLRQ;
325 break;
326 case 1:
327 if (tmp1)
328 SETQ;
329 else
330 CLRQ;
331 break;
332 }
333 break;
334 }
335 break;
336 }
337 if (Q == M)
338 SETT;
339 else
340 CLRT;
341 //printf("Output: arg1=0x%08x M=%d Q=%d T=%d\n", arg1, M, Q, T);
342 return arg1;
343 }
344
345 void helper_macl(uint32_t arg0, uint32_t arg1)
346 {
347 int64_t res;
348
349 res = ((uint64_t) env->mach << 32) | env->macl;
350 res += (int64_t) (int32_t) arg0 *(int64_t) (int32_t) arg1;
351 env->mach = (res >> 32) & 0xffffffff;
352 env->macl = res & 0xffffffff;
353 if (env->sr & SR_S) {
354 if (res < 0)
355 env->mach |= 0xffff0000;
356 else
357 env->mach &= 0x00007fff;
358 }
359 }
360
361 void helper_macw(uint32_t arg0, uint32_t arg1)
362 {
363 int64_t res;
364
365 res = ((uint64_t) env->mach << 32) | env->macl;
366 res += (int64_t) (int16_t) arg0 *(int64_t) (int16_t) arg1;
367 env->mach = (res >> 32) & 0xffffffff;
368 env->macl = res & 0xffffffff;
369 if (env->sr & SR_S) {
370 if (res < -0x80000000) {
371 env->mach = 1;
372 env->macl = 0x80000000;
373 } else if (res > 0x000000007fffffff) {
374 env->mach = 1;
375 env->macl = 0x7fffffff;
376 }
377 }
378 }
379
380 uint32_t helper_negc(uint32_t arg)
381 {
382 uint32_t temp;
383
384 temp = -arg;
385 arg = temp - (env->sr & SR_T);
386 if (0 < temp)
387 env->sr |= SR_T;
388 else
389 env->sr &= ~SR_T;
390 if (temp < arg)
391 env->sr |= SR_T;
392 return arg;
393 }
394
395 uint32_t helper_subc(uint32_t arg0, uint32_t arg1)
396 {
397 uint32_t tmp0, tmp1;
398
399 tmp1 = arg1 - arg0;
400 tmp0 = arg1;
401 arg1 = tmp1 - (env->sr & SR_T);
402 if (tmp0 < tmp1)
403 env->sr |= SR_T;
404 else
405 env->sr &= ~SR_T;
406 if (tmp1 < arg1)
407 env->sr |= SR_T;
408 return arg1;
409 }
410
411 uint32_t helper_subv(uint32_t arg0, uint32_t arg1)
412 {
413 int32_t dest, src, ans;
414
415 if ((int32_t) arg1 >= 0)
416 dest = 0;
417 else
418 dest = 1;
419 if ((int32_t) arg0 >= 0)
420 src = 0;
421 else
422 src = 1;
423 src += dest;
424 arg1 -= arg0;
425 if ((int32_t) arg1 >= 0)
426 ans = 0;
427 else
428 ans = 1;
429 ans += dest;
430 if (src == 1) {
431 if (ans == 1)
432 env->sr |= SR_T;
433 else
434 env->sr &= ~SR_T;
435 } else
436 env->sr &= ~SR_T;
437 return arg1;
438 }
439
440 static inline void set_t(void)
441 {
442 env->sr |= SR_T;
443 }
444
445 static inline void clr_t(void)
446 {
447 env->sr &= ~SR_T;
448 }
449
450 void helper_ld_fpscr(uint32_t val)
451 {
452 env->fpscr = val & FPSCR_MASK;
453 if ((val & FPSCR_RM_MASK) == FPSCR_RM_ZERO) {
454 set_float_rounding_mode(float_round_to_zero, &env->fp_status);
455 } else {
456 set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
457 }
458 set_flush_to_zero((val & FPSCR_DN) != 0, &env->fp_status);
459 }
460
461 static void update_fpscr(void *retaddr)
462 {
463 int xcpt, cause, enable;
464
465 xcpt = get_float_exception_flags(&env->fp_status);
466
467 /* Clear the flag entries */
468 env->fpscr &= ~FPSCR_FLAG_MASK;
469
470 if (unlikely(xcpt)) {
471 if (xcpt & float_flag_invalid) {
472 env->fpscr |= FPSCR_FLAG_V;
473 }
474 if (xcpt & float_flag_divbyzero) {
475 env->fpscr |= FPSCR_FLAG_Z;
476 }
477 if (xcpt & float_flag_overflow) {
478 env->fpscr |= FPSCR_FLAG_O;
479 }
480 if (xcpt & float_flag_underflow) {
481 env->fpscr |= FPSCR_FLAG_U;
482 }
483 if (xcpt & float_flag_inexact) {
484 env->fpscr |= FPSCR_FLAG_I;
485 }
486
487 /* Accumulate in cause entries */
488 env->fpscr |= (env->fpscr & FPSCR_FLAG_MASK)
489 << (FPSCR_CAUSE_SHIFT - FPSCR_FLAG_SHIFT);
490
491 /* Generate an exception if enabled */
492 cause = (env->fpscr & FPSCR_CAUSE_MASK) >> FPSCR_CAUSE_SHIFT;
493 enable = (env->fpscr & FPSCR_ENABLE_MASK) >> FPSCR_ENABLE_SHIFT;
494 if (cause & enable) {
495 cpu_restore_state_from_retaddr(retaddr);
496 env->exception_index = 0x120;
497 cpu_loop_exit();
498 }
499 }
500 }
501
502 uint32_t helper_fabs_FT(uint32_t t0)
503 {
504 CPU_FloatU f;
505 f.l = t0;
506 f.f = float32_abs(f.f);
507 return f.l;
508 }
509
510 uint64_t helper_fabs_DT(uint64_t t0)
511 {
512 CPU_DoubleU d;
513 d.ll = t0;
514 d.d = float64_abs(d.d);
515 return d.ll;
516 }
517
518 uint32_t helper_fadd_FT(uint32_t t0, uint32_t t1)
519 {
520 CPU_FloatU f0, f1;
521 f0.l = t0;
522 f1.l = t1;
523 set_float_exception_flags(0, &env->fp_status);
524 f0.f = float32_add(f0.f, f1.f, &env->fp_status);
525 update_fpscr(GETPC());
526 return f0.l;
527 }
528
529 uint64_t helper_fadd_DT(uint64_t t0, uint64_t t1)
530 {
531 CPU_DoubleU d0, d1;
532 d0.ll = t0;
533 d1.ll = t1;
534 set_float_exception_flags(0, &env->fp_status);
535 d0.d = float64_add(d0.d, d1.d, &env->fp_status);
536 update_fpscr(GETPC());
537 return d0.ll;
538 }
539
540 void helper_fcmp_eq_FT(uint32_t t0, uint32_t t1)
541 {
542 CPU_FloatU f0, f1;
543 int relation;
544 f0.l = t0;
545 f1.l = t1;
546
547 set_float_exception_flags(0, &env->fp_status);
548 relation = float32_compare(f0.f, f1.f, &env->fp_status);
549 if (unlikely(relation == float_relation_unordered)) {
550 update_fpscr(GETPC());
551 } else if (relation == float_relation_equal) {
552 set_t();
553 } else {
554 clr_t();
555 }
556 }
557
558 void helper_fcmp_eq_DT(uint64_t t0, uint64_t t1)
559 {
560 CPU_DoubleU d0, d1;
561 int relation;
562 d0.ll = t0;
563 d1.ll = t1;
564
565 set_float_exception_flags(0, &env->fp_status);
566 relation = float64_compare(d0.d, d1.d, &env->fp_status);
567 if (unlikely(relation == float_relation_unordered)) {
568 update_fpscr(GETPC());
569 } else if (relation == float_relation_equal) {
570 set_t();
571 } else {
572 clr_t();
573 }
574 }
575
576 void helper_fcmp_gt_FT(uint32_t t0, uint32_t t1)
577 {
578 CPU_FloatU f0, f1;
579 int relation;
580 f0.l = t0;
581 f1.l = t1;
582
583 set_float_exception_flags(0, &env->fp_status);
584 relation = float32_compare(f0.f, f1.f, &env->fp_status);
585 if (unlikely(relation == float_relation_unordered)) {
586 update_fpscr(GETPC());
587 } else if (relation == float_relation_greater) {
588 set_t();
589 } else {
590 clr_t();
591 }
592 }
593
594 void helper_fcmp_gt_DT(uint64_t t0, uint64_t t1)
595 {
596 CPU_DoubleU d0, d1;
597 int relation;
598 d0.ll = t0;
599 d1.ll = t1;
600
601 set_float_exception_flags(0, &env->fp_status);
602 relation = float64_compare(d0.d, d1.d, &env->fp_status);
603 if (unlikely(relation == float_relation_unordered)) {
604 update_fpscr(GETPC());
605 } else if (relation == float_relation_greater) {
606 set_t();
607 } else {
608 clr_t();
609 }
610 }
611
612 uint64_t helper_fcnvsd_FT_DT(uint32_t t0)
613 {
614 CPU_DoubleU d;
615 CPU_FloatU f;
616 f.l = t0;
617 set_float_exception_flags(0, &env->fp_status);
618 d.d = float32_to_float64(f.f, &env->fp_status);
619 update_fpscr(GETPC());
620 return d.ll;
621 }
622
623 uint32_t helper_fcnvds_DT_FT(uint64_t t0)
624 {
625 CPU_DoubleU d;
626 CPU_FloatU f;
627 d.ll = t0;
628 set_float_exception_flags(0, &env->fp_status);
629 f.f = float64_to_float32(d.d, &env->fp_status);
630 update_fpscr(GETPC());
631 return f.l;
632 }
633
634 uint32_t helper_fdiv_FT(uint32_t t0, uint32_t t1)
635 {
636 CPU_FloatU f0, f1;
637 f0.l = t0;
638 f1.l = t1;
639 set_float_exception_flags(0, &env->fp_status);
640 f0.f = float32_div(f0.f, f1.f, &env->fp_status);
641 update_fpscr(GETPC());
642 return f0.l;
643 }
644
645 uint64_t helper_fdiv_DT(uint64_t t0, uint64_t t1)
646 {
647 CPU_DoubleU d0, d1;
648 d0.ll = t0;
649 d1.ll = t1;
650 set_float_exception_flags(0, &env->fp_status);
651 d0.d = float64_div(d0.d, d1.d, &env->fp_status);
652 update_fpscr(GETPC());
653 return d0.ll;
654 }
655
656 uint32_t helper_float_FT(uint32_t t0)
657 {
658 CPU_FloatU f;
659
660 set_float_exception_flags(0, &env->fp_status);
661 f.f = int32_to_float32(t0, &env->fp_status);
662 update_fpscr(GETPC());
663
664 return f.l;
665 }
666
667 uint64_t helper_float_DT(uint32_t t0)
668 {
669 CPU_DoubleU d;
670 set_float_exception_flags(0, &env->fp_status);
671 d.d = int32_to_float64(t0, &env->fp_status);
672 update_fpscr(GETPC());
673 return d.ll;
674 }
675
676 uint32_t helper_fmac_FT(uint32_t t0, uint32_t t1, uint32_t t2)
677 {
678 CPU_FloatU f0, f1, f2;
679 f0.l = t0;
680 f1.l = t1;
681 f2.l = t2;
682 set_float_exception_flags(0, &env->fp_status);
683 f0.f = float32_mul(f0.f, f1.f, &env->fp_status);
684 f0.f = float32_add(f0.f, f2.f, &env->fp_status);
685 update_fpscr(GETPC());
686
687 return f0.l;
688 }
689
690 uint32_t helper_fmul_FT(uint32_t t0, uint32_t t1)
691 {
692 CPU_FloatU f0, f1;
693 f0.l = t0;
694 f1.l = t1;
695 set_float_exception_flags(0, &env->fp_status);
696 f0.f = float32_mul(f0.f, f1.f, &env->fp_status);
697 update_fpscr(GETPC());
698 return f0.l;
699 }
700
701 uint64_t helper_fmul_DT(uint64_t t0, uint64_t t1)
702 {
703 CPU_DoubleU d0, d1;
704 d0.ll = t0;
705 d1.ll = t1;
706 set_float_exception_flags(0, &env->fp_status);
707 d0.d = float64_mul(d0.d, d1.d, &env->fp_status);
708 update_fpscr(GETPC());
709
710 return d0.ll;
711 }
712
713 uint32_t helper_fneg_T(uint32_t t0)
714 {
715 CPU_FloatU f;
716 f.l = t0;
717 f.f = float32_chs(f.f);
718 return f.l;
719 }
720
721 uint32_t helper_fsqrt_FT(uint32_t t0)
722 {
723 CPU_FloatU f;
724 f.l = t0;
725 set_float_exception_flags(0, &env->fp_status);
726 f.f = float32_sqrt(f.f, &env->fp_status);
727 update_fpscr(GETPC());
728 return f.l;
729 }
730
731 uint64_t helper_fsqrt_DT(uint64_t t0)
732 {
733 CPU_DoubleU d;
734 d.ll = t0;
735 set_float_exception_flags(0, &env->fp_status);
736 d.d = float64_sqrt(d.d, &env->fp_status);
737 update_fpscr(GETPC());
738 return d.ll;
739 }
740
741 uint32_t helper_fsub_FT(uint32_t t0, uint32_t t1)
742 {
743 CPU_FloatU f0, f1;
744 f0.l = t0;
745 f1.l = t1;
746 set_float_exception_flags(0, &env->fp_status);
747 f0.f = float32_sub(f0.f, f1.f, &env->fp_status);
748 update_fpscr(GETPC());
749 return f0.l;
750 }
751
752 uint64_t helper_fsub_DT(uint64_t t0, uint64_t t1)
753 {
754 CPU_DoubleU d0, d1;
755
756 d0.ll = t0;
757 d1.ll = t1;
758 set_float_exception_flags(0, &env->fp_status);
759 d0.d = float64_sub(d0.d, d1.d, &env->fp_status);
760 update_fpscr(GETPC());
761 return d0.ll;
762 }
763
764 uint32_t helper_ftrc_FT(uint32_t t0)
765 {
766 CPU_FloatU f;
767 uint32_t ret;
768 f.l = t0;
769 set_float_exception_flags(0, &env->fp_status);
770 ret = float32_to_int32_round_to_zero(f.f, &env->fp_status);
771 update_fpscr(GETPC());
772 return ret;
773 }
774
775 uint32_t helper_ftrc_DT(uint64_t t0)
776 {
777 CPU_DoubleU d;
778 uint32_t ret;
779 d.ll = t0;
780 set_float_exception_flags(0, &env->fp_status);
781 ret = float64_to_int32_round_to_zero(d.d, &env->fp_status);
782 update_fpscr(GETPC());
783 return ret;
784 }