xref: /openbmc/qemu/target/rx/op_helper.c (revision f5fe7c17ac4e309e47e78f0f9761aebc8d2f2c81)
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  
_set_psw(CPURXState * env,uint32_t psw,uint32_t rte)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  
helper_set_psw(CPURXState * env,uint32_t psw)49  void helper_set_psw(CPURXState *env, uint32_t psw)
50  {
51      _set_psw(env, psw, 0);
52  }
53  
helper_set_psw_rte(CPURXState * env,uint32_t psw)54  void helper_set_psw_rte(CPURXState *env, uint32_t psw)
55  {
56      _set_psw(env, psw, 1);
57  }
58  
helper_pack_psw(CPURXState * env)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 */
update_fpsw(CPURXState * env,float32 ret,uintptr_t retaddr)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  
helper_set_fpsw(CPURXState * env,uint32_t val)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  
FLOATOP(fadd,float32_add)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  
helper_ftoi(CPURXState * env,float32 t0)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  
helper_round(CPURXState * env,float32 t0)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  
helper_itof(CPURXState * env,uint32_t t0)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 */
helper_scmpu(CPURXState * env)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  
helper_sstr(CPURXState * env,uint32_t sz)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  
smov(uint32_t mode,CPURXState * env)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  
helper_smovu(CPURXState * env)269  void helper_smovu(CPURXState *env)
270  {
271      smov(OP_SMOVU, env);
272  }
273  
helper_smovf(CPURXState * env)274  void helper_smovf(CPURXState *env)
275  {
276      smov(OP_SMOVF, env);
277  }
278  
helper_smovb(CPURXState * env)279  void helper_smovb(CPURXState *env)
280  {
281      smov(OP_SMOVB, env);
282  }
283  
284  
helper_suntil(CPURXState * env,uint32_t sz)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  
helper_swhile(CPURXState * env,uint32_t sz)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 */
helper_rmpa(CPURXState * env,uint32_t sz)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  
helper_racw(CPURXState * env,uint32_t imm)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  
helper_satr(CPURXState * env)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 */
helper_div(CPURXState * env,uint32_t num,uint32_t den)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  
helper_divu(CPURXState * env,uint32_t num,uint32_t den)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
raise_exception(CPURXState * env,int index,uintptr_t retaddr)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  
helper_raise_privilege_violation(CPURXState * env)433  G_NORETURN void helper_raise_privilege_violation(CPURXState *env)
434  {
435      raise_exception(env, 20, GETPC());
436  }
437  
helper_raise_access_fault(CPURXState * env)438  G_NORETURN void helper_raise_access_fault(CPURXState *env)
439  {
440      raise_exception(env, 21, GETPC());
441  }
442  
helper_raise_illegal_instruction(CPURXState * env)443  G_NORETURN void helper_raise_illegal_instruction(CPURXState *env)
444  {
445      raise_exception(env, 23, GETPC());
446  }
447  
helper_wait(CPURXState * env)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  
helper_rxint(CPURXState * env,uint32_t vec)458  G_NORETURN void helper_rxint(CPURXState *env, uint32_t vec)
459  {
460      raise_exception(env, 0x100 + vec, 0);
461  }
462  
helper_rxbrk(CPURXState * env)463  G_NORETURN void helper_rxbrk(CPURXState *env)
464  {
465      raise_exception(env, 0x100, 0);
466  }
467