xref: /qemu/target/rx/op_helper.c (revision b49f4755)
1 /*
2  *  RX helper functions
3  *
4  *  Copyright (c) 2019 Yoshinori Sato
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2 or later, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "qemu/osdep.h"
20 #include "qemu/bitops.h"
21 #include "cpu.h"
22 #include "exec/exec-all.h"
23 #include "exec/helper-proto.h"
24 #include "exec/cpu_ldst.h"
25 #include "fpu/softfloat.h"
26 #include "tcg/debug-assert.h"
27 
28 static inline G_NORETURN
29 void raise_exception(CPURXState *env, int index,
30                      uintptr_t retaddr);
31 
32 static void _set_psw(CPURXState *env, uint32_t psw, uint32_t rte)
33 {
34     uint32_t prev_u;
35     prev_u = env->psw_u;
36     rx_cpu_unpack_psw(env, psw, rte);
37     if (prev_u != env->psw_u) {
38         /* switch r0  */
39         if (env->psw_u) {
40             env->isp = env->regs[0];
41             env->regs[0] = env->usp;
42         } else {
43             env->usp = env->regs[0];
44             env->regs[0] = env->isp;
45         }
46     }
47 }
48 
49 void helper_set_psw(CPURXState *env, uint32_t psw)
50 {
51     _set_psw(env, psw, 0);
52 }
53 
54 void helper_set_psw_rte(CPURXState *env, uint32_t psw)
55 {
56     _set_psw(env, psw, 1);
57 }
58 
59 uint32_t helper_pack_psw(CPURXState *env)
60 {
61     return rx_cpu_pack_psw(env);
62 }
63 
64 #define SET_FPSW(b)                                             \
65     do {                                                        \
66         env->fpsw = FIELD_DP32(env->fpsw, FPSW, C ## b, 1);     \
67         if (!FIELD_EX32(env->fpsw, FPSW, E ## b)) {             \
68             env->fpsw = FIELD_DP32(env->fpsw, FPSW, F ## b, 1); \
69         }                                                       \
70     } while (0)
71 
72 /* fp operations */
73 static void update_fpsw(CPURXState *env, float32 ret, uintptr_t retaddr)
74 {
75     int xcpt, cause, enable;
76 
77     env->psw_z = ret & ~(1 << 31); /* mask sign bit */
78     env->psw_s = ret;
79 
80     xcpt = get_float_exception_flags(&env->fp_status);
81 
82     /* Clear the cause entries */
83     env->fpsw = FIELD_DP32(env->fpsw, FPSW, CAUSE, 0);
84 
85     /* set FPSW */
86     if (unlikely(xcpt)) {
87         if (xcpt & float_flag_invalid) {
88             SET_FPSW(V);
89         }
90         if (xcpt & float_flag_divbyzero) {
91             SET_FPSW(Z);
92         }
93         if (xcpt & float_flag_overflow) {
94             SET_FPSW(O);
95         }
96         if (xcpt & float_flag_underflow) {
97             SET_FPSW(U);
98         }
99         if (xcpt & float_flag_inexact) {
100             SET_FPSW(X);
101         }
102         if ((xcpt & (float_flag_input_denormal
103                      | float_flag_output_denormal))
104             && !FIELD_EX32(env->fpsw, FPSW, DN)) {
105             env->fpsw = FIELD_DP32(env->fpsw, FPSW, CE, 1);
106         }
107 
108         /* update FPSW_FLAG_S */
109         if (FIELD_EX32(env->fpsw, FPSW, FLAGS) != 0) {
110             env->fpsw = FIELD_DP32(env->fpsw, FPSW, FS, 1);
111         }
112 
113         /* Generate an exception if enabled */
114         cause = FIELD_EX32(env->fpsw, FPSW, CAUSE);
115         enable = FIELD_EX32(env->fpsw, FPSW, ENABLE);
116         enable |= 1 << 5; /* CE always enabled */
117         if (cause & enable) {
118             raise_exception(env, 21, retaddr);
119         }
120     }
121 }
122 
123 void helper_set_fpsw(CPURXState *env, uint32_t val)
124 {
125     static const int roundmode[] = {
126         float_round_nearest_even,
127         float_round_to_zero,
128         float_round_up,
129         float_round_down,
130     };
131     uint32_t fpsw = env->fpsw;
132     fpsw |= 0x7fffff03;
133     val &= ~0x80000000;
134     fpsw &= val;
135     FIELD_DP32(fpsw, FPSW, FS, FIELD_EX32(fpsw, FPSW, FLAGS) != 0);
136     env->fpsw = fpsw;
137     set_float_rounding_mode(roundmode[FIELD_EX32(env->fpsw, FPSW, RM)],
138                             &env->fp_status);
139 }
140 
141 #define FLOATOP(op, func)                                           \
142     float32 helper_##op(CPURXState *env, float32 t0, float32 t1)    \
143     {                                                               \
144         float32 ret;                                                \
145         ret = func(t0, t1, &env->fp_status);                        \
146         update_fpsw(env, *(uint32_t *)&ret, GETPC());               \
147         return ret;                                                 \
148     }
149 
150 FLOATOP(fadd, float32_add)
151 FLOATOP(fsub, float32_sub)
152 FLOATOP(fmul, float32_mul)
153 FLOATOP(fdiv, float32_div)
154 
155 void helper_fcmp(CPURXState *env, float32 t0, float32 t1)
156 {
157     int st;
158     st = float32_compare(t0, t1, &env->fp_status);
159     update_fpsw(env, 0, GETPC());
160     env->psw_z = 1;
161     env->psw_s = env->psw_o = 0;
162     switch (st) {
163     case float_relation_equal:
164         env->psw_z = 0;
165         break;
166     case float_relation_less:
167         env->psw_s = -1;
168         break;
169     case float_relation_unordered:
170         env->psw_o = -1;
171         break;
172     }
173 }
174 
175 uint32_t helper_ftoi(CPURXState *env, float32 t0)
176 {
177     uint32_t ret;
178     ret = float32_to_int32_round_to_zero(t0, &env->fp_status);
179     update_fpsw(env, ret, GETPC());
180     return ret;
181 }
182 
183 uint32_t helper_round(CPURXState *env, float32 t0)
184 {
185     uint32_t ret;
186     ret = float32_to_int32(t0, &env->fp_status);
187     update_fpsw(env, ret, GETPC());
188     return ret;
189 }
190 
191 float32 helper_itof(CPURXState *env, uint32_t t0)
192 {
193     float32 ret;
194     ret = int32_to_float32(t0, &env->fp_status);
195     update_fpsw(env, ret, GETPC());
196     return ret;
197 }
198 
199 /* string operations */
200 void helper_scmpu(CPURXState *env)
201 {
202     uint8_t tmp0, tmp1;
203     if (env->regs[3] == 0) {
204         return;
205     }
206     do {
207         tmp0 = cpu_ldub_data_ra(env, env->regs[1]++, GETPC());
208         tmp1 = cpu_ldub_data_ra(env, env->regs[2]++, GETPC());
209         env->regs[3]--;
210         if (tmp0 != tmp1 || tmp0 == '\0') {
211             break;
212         }
213     } while (env->regs[3] != 0);
214     env->psw_z = tmp0 - tmp1;
215     env->psw_c = (tmp0 >= tmp1);
216 }
217 
218 static uint32_t (* const cpu_ldufn[])(CPUArchState *env,
219                                      abi_ptr ptr,
220                                      uintptr_t retaddr) = {
221     cpu_ldub_data_ra, cpu_lduw_data_ra, cpu_ldl_data_ra,
222 };
223 
224 static uint32_t (* const cpu_ldfn[])(CPUArchState *env,
225                                      abi_ptr ptr,
226                                      uintptr_t retaddr) = {
227     cpu_ldub_data_ra, cpu_lduw_data_ra, cpu_ldl_data_ra,
228 };
229 
230 static void (* const cpu_stfn[])(CPUArchState *env,
231                                  abi_ptr ptr,
232                                  uint32_t val,
233                                  uintptr_t retaddr) = {
234     cpu_stb_data_ra, cpu_stw_data_ra, cpu_stl_data_ra,
235 };
236 
237 void helper_sstr(CPURXState *env, uint32_t sz)
238 {
239     tcg_debug_assert(sz < 3);
240     while (env->regs[3] != 0) {
241         cpu_stfn[sz](env, env->regs[1], env->regs[2], GETPC());
242         env->regs[1] += 1 << sz;
243         env->regs[3]--;
244     }
245 }
246 
247 #define OP_SMOVU 1
248 #define OP_SMOVF 0
249 #define OP_SMOVB 2
250 
251 static void smov(uint32_t mode, CPURXState *env)
252 {
253     uint8_t tmp;
254     int dir;
255 
256     dir = (mode & OP_SMOVB) ? -1 : 1;
257     while (env->regs[3] != 0) {
258         tmp = cpu_ldub_data_ra(env, env->regs[2], GETPC());
259         cpu_stb_data_ra(env, env->regs[1], tmp, GETPC());
260         env->regs[1] += dir;
261         env->regs[2] += dir;
262         env->regs[3]--;
263         if ((mode & OP_SMOVU) && tmp == 0) {
264             break;
265         }
266     }
267 }
268 
269 void helper_smovu(CPURXState *env)
270 {
271     smov(OP_SMOVU, env);
272 }
273 
274 void helper_smovf(CPURXState *env)
275 {
276     smov(OP_SMOVF, env);
277 }
278 
279 void helper_smovb(CPURXState *env)
280 {
281     smov(OP_SMOVB, env);
282 }
283 
284 
285 void helper_suntil(CPURXState *env, uint32_t sz)
286 {
287     uint32_t tmp;
288     tcg_debug_assert(sz < 3);
289     if (env->regs[3] == 0) {
290         return;
291     }
292     do {
293         tmp = cpu_ldufn[sz](env, env->regs[1], GETPC());
294         env->regs[1] += 1 << sz;
295         env->regs[3]--;
296         if (tmp == env->regs[2]) {
297             break;
298         }
299     } while (env->regs[3] != 0);
300     env->psw_z = tmp - env->regs[2];
301     env->psw_c = (tmp <= env->regs[2]);
302 }
303 
304 void helper_swhile(CPURXState *env, uint32_t sz)
305 {
306     uint32_t tmp;
307     tcg_debug_assert(sz < 3);
308     if (env->regs[3] == 0) {
309         return;
310     }
311     do {
312         tmp = cpu_ldufn[sz](env, env->regs[1], GETPC());
313         env->regs[1] += 1 << sz;
314         env->regs[3]--;
315         if (tmp != env->regs[2]) {
316             break;
317         }
318     } while (env->regs[3] != 0);
319     env->psw_z = env->regs[3];
320     env->psw_c = (tmp <= env->regs[2]);
321 }
322 
323 /* accumulator operations */
324 void helper_rmpa(CPURXState *env, uint32_t sz)
325 {
326     uint64_t result_l, prev;
327     int32_t result_h;
328     int64_t tmp0, tmp1;
329 
330     if (env->regs[3] == 0) {
331         return;
332     }
333     result_l = env->regs[5];
334     result_l <<= 32;
335     result_l |= env->regs[4];
336     result_h = env->regs[6];
337     env->psw_o = 0;
338 
339     while (env->regs[3] != 0) {
340         tmp0 = cpu_ldfn[sz](env, env->regs[1], GETPC());
341         tmp1 = cpu_ldfn[sz](env, env->regs[2], GETPC());
342         tmp0 *= tmp1;
343         prev = result_l;
344         result_l += tmp0;
345         /* carry / bollow */
346         if (tmp0 < 0) {
347             if (prev > result_l) {
348                 result_h--;
349             }
350         } else {
351             if (prev < result_l) {
352                 result_h++;
353             }
354         }
355 
356         env->regs[1] += 1 << sz;
357         env->regs[2] += 1 << sz;
358     }
359     env->psw_s = result_h;
360     env->psw_o = (result_h != 0 && result_h != -1) << 31;
361     env->regs[6] = result_h;
362     env->regs[5] = result_l >> 32;
363     env->regs[4] = result_l & 0xffffffff;
364 }
365 
366 void helper_racw(CPURXState *env, uint32_t imm)
367 {
368     int64_t acc;
369     acc = env->acc;
370     acc <<= (imm + 1);
371     acc += 0x0000000080000000LL;
372     if (acc > 0x00007fff00000000LL) {
373         acc = 0x00007fff00000000LL;
374     } else if (acc < -0x800000000000LL) {
375         acc = -0x800000000000LL;
376     } else {
377         acc &= 0xffffffff00000000LL;
378     }
379     env->acc = acc;
380 }
381 
382 void helper_satr(CPURXState *env)
383 {
384     if (env->psw_o >> 31) {
385         if ((int)env->psw_s < 0) {
386             env->regs[6] = 0x00000000;
387             env->regs[5] = 0x7fffffff;
388             env->regs[4] = 0xffffffff;
389         } else {
390             env->regs[6] = 0xffffffff;
391             env->regs[5] = 0x80000000;
392             env->regs[4] = 0x00000000;
393         }
394     }
395 }
396 
397 /* div */
398 uint32_t helper_div(CPURXState *env, uint32_t num, uint32_t den)
399 {
400     uint32_t ret = num;
401     if (!((num == INT_MIN && den == -1) || den == 0)) {
402         ret = (int32_t)num / (int32_t)den;
403         env->psw_o = 0;
404     } else {
405         env->psw_o = -1;
406     }
407     return ret;
408 }
409 
410 uint32_t helper_divu(CPURXState *env, uint32_t num, uint32_t den)
411 {
412     uint32_t ret = num;
413     if (den != 0) {
414         ret = num / den;
415         env->psw_o = 0;
416     } else {
417         env->psw_o = -1;
418     }
419     return ret;
420 }
421 
422 /* exception */
423 static inline G_NORETURN
424 void raise_exception(CPURXState *env, int index,
425                      uintptr_t retaddr)
426 {
427     CPUState *cs = env_cpu(env);
428 
429     cs->exception_index = index;
430     cpu_loop_exit_restore(cs, retaddr);
431 }
432 
433 G_NORETURN void helper_raise_privilege_violation(CPURXState *env)
434 {
435     raise_exception(env, 20, GETPC());
436 }
437 
438 G_NORETURN void helper_raise_access_fault(CPURXState *env)
439 {
440     raise_exception(env, 21, GETPC());
441 }
442 
443 G_NORETURN void helper_raise_illegal_instruction(CPURXState *env)
444 {
445     raise_exception(env, 23, GETPC());
446 }
447 
448 G_NORETURN void helper_wait(CPURXState *env)
449 {
450     CPUState *cs = env_cpu(env);
451 
452     cs->halted = 1;
453     env->in_sleep = 1;
454     env->psw_i = 1;
455     raise_exception(env, EXCP_HLT, 0);
456 }
457 
458 G_NORETURN void helper_rxint(CPURXState *env, uint32_t vec)
459 {
460     raise_exception(env, 0x100 + vec, 0);
461 }
462 
463 G_NORETURN void helper_rxbrk(CPURXState *env)
464 {
465     raise_exception(env, 0x100, 0);
466 }
467