xref: /qemu/target/sh4/op_helper.c (revision 6402cbbb)
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 "qemu/osdep.h"
20 #include "cpu.h"
21 #include "exec/helper-proto.h"
22 #include "exec/exec-all.h"
23 #include "exec/cpu_ldst.h"
24 
25 #ifndef CONFIG_USER_ONLY
26 
27 void superh_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
28                                     MMUAccessType access_type,
29                                     int mmu_idx, uintptr_t retaddr)
30 {
31     switch (access_type) {
32     case MMU_INST_FETCH:
33     case MMU_DATA_LOAD:
34         cs->exception_index = 0x0e0;
35         break;
36     case MMU_DATA_STORE:
37         cs->exception_index = 0x100;
38         break;
39     }
40     cpu_loop_exit_restore(cs, retaddr);
41 }
42 
43 void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,
44               int mmu_idx, uintptr_t retaddr)
45 {
46     int ret;
47 
48     ret = superh_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx);
49     if (ret) {
50         /* now we have a real cpu fault */
51         cpu_loop_exit_restore(cs, retaddr);
52     }
53 }
54 
55 #endif
56 
57 void helper_ldtlb(CPUSH4State *env)
58 {
59 #ifdef CONFIG_USER_ONLY
60     SuperHCPU *cpu = sh_env_get_cpu(env);
61 
62     /* XXXXX */
63     cpu_abort(CPU(cpu), "Unhandled ldtlb");
64 #else
65     cpu_load_tlb(env);
66 #endif
67 }
68 
69 static inline void QEMU_NORETURN raise_exception(CPUSH4State *env, int index,
70                                                  uintptr_t retaddr)
71 {
72     CPUState *cs = CPU(sh_env_get_cpu(env));
73 
74     cs->exception_index = index;
75     cpu_loop_exit_restore(cs, retaddr);
76 }
77 
78 void helper_raise_illegal_instruction(CPUSH4State *env)
79 {
80     raise_exception(env, 0x180, 0);
81 }
82 
83 void helper_raise_slot_illegal_instruction(CPUSH4State *env)
84 {
85     raise_exception(env, 0x1a0, 0);
86 }
87 
88 void helper_raise_fpu_disable(CPUSH4State *env)
89 {
90     raise_exception(env, 0x800, 0);
91 }
92 
93 void helper_raise_slot_fpu_disable(CPUSH4State *env)
94 {
95     raise_exception(env, 0x820, 0);
96 }
97 
98 void helper_debug(CPUSH4State *env)
99 {
100     raise_exception(env, EXCP_DEBUG, 0);
101 }
102 
103 void helper_sleep(CPUSH4State *env)
104 {
105     CPUState *cs = CPU(sh_env_get_cpu(env));
106 
107     cs->halted = 1;
108     env->in_sleep = 1;
109     raise_exception(env, EXCP_HLT, 0);
110 }
111 
112 void helper_trapa(CPUSH4State *env, uint32_t tra)
113 {
114     env->tra = tra << 2;
115     raise_exception(env, 0x160, 0);
116 }
117 
118 void helper_exclusive(CPUSH4State *env)
119 {
120     /* We do not want cpu_restore_state to run.  */
121     cpu_loop_exit_atomic(ENV_GET_CPU(env), 0);
122 }
123 
124 void helper_movcal(CPUSH4State *env, uint32_t address, uint32_t value)
125 {
126     if (cpu_sh4_is_cached (env, address))
127     {
128         memory_content *r = g_new(memory_content, 1);
129 
130 	r->address = address;
131 	r->value = value;
132 	r->next = NULL;
133 
134 	*(env->movcal_backup_tail) = r;
135 	env->movcal_backup_tail = &(r->next);
136     }
137 }
138 
139 void helper_discard_movcal_backup(CPUSH4State *env)
140 {
141     memory_content *current = env->movcal_backup;
142 
143     while(current)
144     {
145 	memory_content *next = current->next;
146         g_free(current);
147 	env->movcal_backup = current = next;
148 	if (current == NULL)
149 	    env->movcal_backup_tail = &(env->movcal_backup);
150     }
151 }
152 
153 void helper_ocbi(CPUSH4State *env, uint32_t address)
154 {
155     memory_content **current = &(env->movcal_backup);
156     while (*current)
157     {
158 	uint32_t a = (*current)->address;
159 	if ((a & ~0x1F) == (address & ~0x1F))
160 	{
161 	    memory_content *next = (*current)->next;
162             cpu_stl_data(env, a, (*current)->value);
163 
164 	    if (next == NULL)
165 	    {
166 		env->movcal_backup_tail = current;
167 	    }
168 
169             g_free(*current);
170 	    *current = next;
171 	    break;
172 	}
173     }
174 }
175 
176 void helper_macl(CPUSH4State *env, uint32_t arg0, uint32_t arg1)
177 {
178     int64_t res;
179 
180     res = ((uint64_t) env->mach << 32) | env->macl;
181     res += (int64_t) (int32_t) arg0 *(int64_t) (int32_t) arg1;
182     env->mach = (res >> 32) & 0xffffffff;
183     env->macl = res & 0xffffffff;
184     if (env->sr & (1u << SR_S)) {
185 	if (res < 0)
186 	    env->mach |= 0xffff0000;
187 	else
188 	    env->mach &= 0x00007fff;
189     }
190 }
191 
192 void helper_macw(CPUSH4State *env, uint32_t arg0, uint32_t arg1)
193 {
194     int64_t res;
195 
196     res = ((uint64_t) env->mach << 32) | env->macl;
197     res += (int64_t) (int16_t) arg0 *(int64_t) (int16_t) arg1;
198     env->mach = (res >> 32) & 0xffffffff;
199     env->macl = res & 0xffffffff;
200     if (env->sr & (1u << SR_S)) {
201 	if (res < -0x80000000) {
202 	    env->mach = 1;
203 	    env->macl = 0x80000000;
204 	} else if (res > 0x000000007fffffff) {
205 	    env->mach = 1;
206 	    env->macl = 0x7fffffff;
207 	}
208     }
209 }
210 
211 void helper_ld_fpscr(CPUSH4State *env, uint32_t val)
212 {
213     env->fpscr = val & FPSCR_MASK;
214     if ((val & FPSCR_RM_MASK) == FPSCR_RM_ZERO) {
215 	set_float_rounding_mode(float_round_to_zero, &env->fp_status);
216     } else {
217 	set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
218     }
219     set_flush_to_zero((val & FPSCR_DN) != 0, &env->fp_status);
220 }
221 
222 static void update_fpscr(CPUSH4State *env, uintptr_t retaddr)
223 {
224     int xcpt, cause, enable;
225 
226     xcpt = get_float_exception_flags(&env->fp_status);
227 
228     /* Clear the cause entries */
229     env->fpscr &= ~FPSCR_CAUSE_MASK;
230 
231     if (unlikely(xcpt)) {
232         if (xcpt & float_flag_invalid) {
233             env->fpscr |= FPSCR_CAUSE_V;
234         }
235         if (xcpt & float_flag_divbyzero) {
236             env->fpscr |= FPSCR_CAUSE_Z;
237         }
238         if (xcpt & float_flag_overflow) {
239             env->fpscr |= FPSCR_CAUSE_O;
240         }
241         if (xcpt & float_flag_underflow) {
242             env->fpscr |= FPSCR_CAUSE_U;
243         }
244         if (xcpt & float_flag_inexact) {
245             env->fpscr |= FPSCR_CAUSE_I;
246         }
247 
248         /* Accumulate in flag entries */
249         env->fpscr |= (env->fpscr & FPSCR_CAUSE_MASK)
250                       >> (FPSCR_CAUSE_SHIFT - FPSCR_FLAG_SHIFT);
251 
252         /* Generate an exception if enabled */
253         cause = (env->fpscr & FPSCR_CAUSE_MASK) >> FPSCR_CAUSE_SHIFT;
254         enable = (env->fpscr & FPSCR_ENABLE_MASK) >> FPSCR_ENABLE_SHIFT;
255         if (cause & enable) {
256             raise_exception(env, 0x120, retaddr);
257         }
258     }
259 }
260 
261 float32 helper_fadd_FT(CPUSH4State *env, float32 t0, float32 t1)
262 {
263     set_float_exception_flags(0, &env->fp_status);
264     t0 = float32_add(t0, t1, &env->fp_status);
265     update_fpscr(env, GETPC());
266     return t0;
267 }
268 
269 float64 helper_fadd_DT(CPUSH4State *env, float64 t0, float64 t1)
270 {
271     set_float_exception_flags(0, &env->fp_status);
272     t0 = float64_add(t0, t1, &env->fp_status);
273     update_fpscr(env, GETPC());
274     return t0;
275 }
276 
277 uint32_t helper_fcmp_eq_FT(CPUSH4State *env, float32 t0, float32 t1)
278 {
279     int relation;
280 
281     set_float_exception_flags(0, &env->fp_status);
282     relation = float32_compare(t0, t1, &env->fp_status);
283     update_fpscr(env, GETPC());
284     return relation == float_relation_equal;
285 }
286 
287 uint32_t helper_fcmp_eq_DT(CPUSH4State *env, float64 t0, float64 t1)
288 {
289     int relation;
290 
291     set_float_exception_flags(0, &env->fp_status);
292     relation = float64_compare(t0, t1, &env->fp_status);
293     update_fpscr(env, GETPC());
294     return relation == float_relation_equal;
295 }
296 
297 uint32_t helper_fcmp_gt_FT(CPUSH4State *env, float32 t0, float32 t1)
298 {
299     int relation;
300 
301     set_float_exception_flags(0, &env->fp_status);
302     relation = float32_compare(t0, t1, &env->fp_status);
303     update_fpscr(env, GETPC());
304     return relation == float_relation_greater;
305 }
306 
307 uint32_t helper_fcmp_gt_DT(CPUSH4State *env, float64 t0, float64 t1)
308 {
309     int relation;
310 
311     set_float_exception_flags(0, &env->fp_status);
312     relation = float64_compare(t0, t1, &env->fp_status);
313     update_fpscr(env, GETPC());
314     return relation == float_relation_greater;
315 }
316 
317 float64 helper_fcnvsd_FT_DT(CPUSH4State *env, float32 t0)
318 {
319     float64 ret;
320     set_float_exception_flags(0, &env->fp_status);
321     ret = float32_to_float64(t0, &env->fp_status);
322     update_fpscr(env, GETPC());
323     return ret;
324 }
325 
326 float32 helper_fcnvds_DT_FT(CPUSH4State *env, float64 t0)
327 {
328     float32 ret;
329     set_float_exception_flags(0, &env->fp_status);
330     ret = float64_to_float32(t0, &env->fp_status);
331     update_fpscr(env, GETPC());
332     return ret;
333 }
334 
335 float32 helper_fdiv_FT(CPUSH4State *env, float32 t0, float32 t1)
336 {
337     set_float_exception_flags(0, &env->fp_status);
338     t0 = float32_div(t0, t1, &env->fp_status);
339     update_fpscr(env, GETPC());
340     return t0;
341 }
342 
343 float64 helper_fdiv_DT(CPUSH4State *env, float64 t0, float64 t1)
344 {
345     set_float_exception_flags(0, &env->fp_status);
346     t0 = float64_div(t0, t1, &env->fp_status);
347     update_fpscr(env, GETPC());
348     return t0;
349 }
350 
351 float32 helper_float_FT(CPUSH4State *env, uint32_t t0)
352 {
353     float32 ret;
354     set_float_exception_flags(0, &env->fp_status);
355     ret = int32_to_float32(t0, &env->fp_status);
356     update_fpscr(env, GETPC());
357     return ret;
358 }
359 
360 float64 helper_float_DT(CPUSH4State *env, uint32_t t0)
361 {
362     float64 ret;
363     set_float_exception_flags(0, &env->fp_status);
364     ret = int32_to_float64(t0, &env->fp_status);
365     update_fpscr(env, GETPC());
366     return ret;
367 }
368 
369 float32 helper_fmac_FT(CPUSH4State *env, float32 t0, float32 t1, float32 t2)
370 {
371     set_float_exception_flags(0, &env->fp_status);
372     t0 = float32_muladd(t0, t1, t2, 0, &env->fp_status);
373     update_fpscr(env, GETPC());
374     return t0;
375 }
376 
377 float32 helper_fmul_FT(CPUSH4State *env, float32 t0, float32 t1)
378 {
379     set_float_exception_flags(0, &env->fp_status);
380     t0 = float32_mul(t0, t1, &env->fp_status);
381     update_fpscr(env, GETPC());
382     return t0;
383 }
384 
385 float64 helper_fmul_DT(CPUSH4State *env, float64 t0, float64 t1)
386 {
387     set_float_exception_flags(0, &env->fp_status);
388     t0 = float64_mul(t0, t1, &env->fp_status);
389     update_fpscr(env, GETPC());
390     return t0;
391 }
392 
393 float32 helper_fsqrt_FT(CPUSH4State *env, float32 t0)
394 {
395     set_float_exception_flags(0, &env->fp_status);
396     t0 = float32_sqrt(t0, &env->fp_status);
397     update_fpscr(env, GETPC());
398     return t0;
399 }
400 
401 float64 helper_fsqrt_DT(CPUSH4State *env, float64 t0)
402 {
403     set_float_exception_flags(0, &env->fp_status);
404     t0 = float64_sqrt(t0, &env->fp_status);
405     update_fpscr(env, GETPC());
406     return t0;
407 }
408 
409 float32 helper_fsrra_FT(CPUSH4State *env, float32 t0)
410 {
411     set_float_exception_flags(0, &env->fp_status);
412     /* "Approximate" 1/sqrt(x) via actual computation.  */
413     t0 = float32_sqrt(t0, &env->fp_status);
414     t0 = float32_div(float32_one, t0, &env->fp_status);
415     /* Since this is supposed to be an approximation, an imprecision
416        exception is required.  One supposes this also follows the usual
417        IEEE rule that other exceptions take precidence.  */
418     if (get_float_exception_flags(&env->fp_status) == 0) {
419         set_float_exception_flags(float_flag_inexact, &env->fp_status);
420     }
421     update_fpscr(env, GETPC());
422     return t0;
423 }
424 
425 float32 helper_fsub_FT(CPUSH4State *env, float32 t0, float32 t1)
426 {
427     set_float_exception_flags(0, &env->fp_status);
428     t0 = float32_sub(t0, t1, &env->fp_status);
429     update_fpscr(env, GETPC());
430     return t0;
431 }
432 
433 float64 helper_fsub_DT(CPUSH4State *env, float64 t0, float64 t1)
434 {
435     set_float_exception_flags(0, &env->fp_status);
436     t0 = float64_sub(t0, t1, &env->fp_status);
437     update_fpscr(env, GETPC());
438     return t0;
439 }
440 
441 uint32_t helper_ftrc_FT(CPUSH4State *env, float32 t0)
442 {
443     uint32_t ret;
444     set_float_exception_flags(0, &env->fp_status);
445     ret = float32_to_int32_round_to_zero(t0, &env->fp_status);
446     update_fpscr(env, GETPC());
447     return ret;
448 }
449 
450 uint32_t helper_ftrc_DT(CPUSH4State *env, float64 t0)
451 {
452     uint32_t ret;
453     set_float_exception_flags(0, &env->fp_status);
454     ret = float64_to_int32_round_to_zero(t0, &env->fp_status);
455     update_fpscr(env, GETPC());
456     return ret;
457 }
458 
459 void helper_fipr(CPUSH4State *env, uint32_t m, uint32_t n)
460 {
461     int bank, i;
462     float32 r, p;
463 
464     bank = (env->sr & FPSCR_FR) ? 16 : 0;
465     r = float32_zero;
466     set_float_exception_flags(0, &env->fp_status);
467 
468     for (i = 0 ; i < 4 ; i++) {
469         p = float32_mul(env->fregs[bank + m + i],
470                         env->fregs[bank + n + i],
471                         &env->fp_status);
472         r = float32_add(r, p, &env->fp_status);
473     }
474     update_fpscr(env, GETPC());
475 
476     env->fregs[bank + n + 3] = r;
477 }
478 
479 void helper_ftrv(CPUSH4State *env, uint32_t n)
480 {
481     int bank_matrix, bank_vector;
482     int i, j;
483     float32 r[4];
484     float32 p;
485 
486     bank_matrix = (env->sr & FPSCR_FR) ? 0 : 16;
487     bank_vector = (env->sr & FPSCR_FR) ? 16 : 0;
488     set_float_exception_flags(0, &env->fp_status);
489     for (i = 0 ; i < 4 ; i++) {
490         r[i] = float32_zero;
491         for (j = 0 ; j < 4 ; j++) {
492             p = float32_mul(env->fregs[bank_matrix + 4 * j + i],
493                             env->fregs[bank_vector + j],
494                             &env->fp_status);
495             r[i] = float32_add(r[i], p, &env->fp_status);
496         }
497     }
498     update_fpscr(env, GETPC());
499 
500     for (i = 0 ; i < 4 ; i++) {
501         env->fregs[bank_vector + i] = r[i];
502     }
503 }
504