]>
Commit | Line | Data |
---|---|---|
075d047e YS |
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" | |
fafe0021 | 26 | #include "tcg/debug-assert.h" |
075d047e | 27 | |
8905770b MAL |
28 | static inline G_NORETURN |
29 | void raise_exception(CPURXState *env, int index, | |
30 | uintptr_t retaddr); | |
075d047e YS |
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 | } | |
77182df1 | 206 | do { |
075d047e YS |
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 | } | |
77182df1 | 213 | } while (env->regs[3] != 0); |
075d047e YS |
214 | env->psw_z = tmp0 - tmp1; |
215 | env->psw_c = (tmp0 >= tmp1); | |
216 | } | |
217 | ||
218 | static uint32_t (* const cpu_ldufn[])(CPUArchState *env, | |
022b9bce | 219 | abi_ptr ptr, |
075d047e YS |
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, | |
022b9bce | 225 | abi_ptr ptr, |
075d047e YS |
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, | |
022b9bce | 231 | abi_ptr ptr, |
075d047e YS |
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) { | |
c1dadb84 | 290 | return; |
075d047e | 291 | } |
77182df1 | 292 | do { |
075d047e YS |
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 | } | |
77182df1 | 299 | } while (env->regs[3] != 0); |
075d047e YS |
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) { | |
c1dadb84 | 309 | return; |
075d047e | 310 | } |
77182df1 | 311 | do { |
075d047e YS |
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 | } | |
77182df1 | 318 | } while (env->regs[3] != 0); |
075d047e YS |
319 | env->psw_z = env->regs[3]; |
320 | env->psw_c = (tmp <= env->regs[2]); | |
321 | } | |
322 | ||
97841438 | 323 | /* accumulator operations */ |
075d047e YS |
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 */ | |
8905770b MAL |
423 | static inline G_NORETURN |
424 | void raise_exception(CPURXState *env, int index, | |
425 | uintptr_t retaddr) | |
075d047e YS |
426 | { |
427 | CPUState *cs = env_cpu(env); | |
428 | ||
429 | cs->exception_index = index; | |
430 | cpu_loop_exit_restore(cs, retaddr); | |
431 | } | |
432 | ||
8905770b | 433 | G_NORETURN void helper_raise_privilege_violation(CPURXState *env) |
075d047e YS |
434 | { |
435 | raise_exception(env, 20, GETPC()); | |
436 | } | |
437 | ||
8905770b | 438 | G_NORETURN void helper_raise_access_fault(CPURXState *env) |
075d047e YS |
439 | { |
440 | raise_exception(env, 21, GETPC()); | |
441 | } | |
442 | ||
8905770b | 443 | G_NORETURN void helper_raise_illegal_instruction(CPURXState *env) |
075d047e YS |
444 | { |
445 | raise_exception(env, 23, GETPC()); | |
446 | } | |
447 | ||
8905770b | 448 | G_NORETURN void helper_wait(CPURXState *env) |
075d047e YS |
449 | { |
450 | CPUState *cs = env_cpu(env); | |
451 | ||
452 | cs->halted = 1; | |
453 | env->in_sleep = 1; | |
335cd065 | 454 | env->psw_i = 1; |
075d047e YS |
455 | raise_exception(env, EXCP_HLT, 0); |
456 | } | |
457 | ||
8905770b | 458 | G_NORETURN void helper_rxint(CPURXState *env, uint32_t vec) |
075d047e YS |
459 | { |
460 | raise_exception(env, 0x100 + vec, 0); | |
461 | } | |
462 | ||
8905770b | 463 | G_NORETURN void helper_rxbrk(CPURXState *env) |
075d047e YS |
464 | { |
465 | raise_exception(env, 0x100, 0); | |
466 | } |