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