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