1 | /* |
2 | * Helpers for HPPA instructions. |
3 | * |
4 | * Copyright (c) 2016 Richard Henderson <rth@twiddle.net> |
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 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 | |
20 | #include "qemu/osdep.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 "qemu/timer.h" |
26 | #include "sysemu/runstate.h" |
27 | #include "fpu/softfloat.h" |
28 | #include "trace.h" |
29 | |
30 | void QEMU_NORETURN HELPER(excp)(CPUHPPAState *env, int excp) |
31 | { |
32 | CPUState *cs = env_cpu(env); |
33 | |
34 | cs->exception_index = excp; |
35 | cpu_loop_exit(cs); |
36 | } |
37 | |
38 | void QEMU_NORETURN hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra) |
39 | { |
40 | CPUState *cs = env_cpu(env); |
41 | |
42 | cs->exception_index = excp; |
43 | cpu_loop_exit_restore(cs, ra); |
44 | } |
45 | |
46 | void HELPER(tsv)(CPUHPPAState *env, target_ureg cond) |
47 | { |
48 | if (unlikely((target_sreg)cond < 0)) { |
49 | hppa_dynamic_excp(env, EXCP_OVERFLOW, GETPC()); |
50 | } |
51 | } |
52 | |
53 | void HELPER(tcond)(CPUHPPAState *env, target_ureg cond) |
54 | { |
55 | if (unlikely(cond)) { |
56 | hppa_dynamic_excp(env, EXCP_COND, GETPC()); |
57 | } |
58 | } |
59 | |
60 | static void atomic_store_3(CPUHPPAState *env, target_ulong addr, uint32_t val, |
61 | uint32_t mask, uintptr_t ra) |
62 | { |
63 | #ifdef CONFIG_USER_ONLY |
64 | uint32_t old, new, cmp; |
65 | |
66 | uint32_t *haddr = g2h(addr - 1); |
67 | old = *haddr; |
68 | while (1) { |
69 | new = (old & ~mask) | (val & mask); |
70 | cmp = atomic_cmpxchg(haddr, old, new); |
71 | if (cmp == old) { |
72 | return; |
73 | } |
74 | old = cmp; |
75 | } |
76 | #else |
77 | /* FIXME -- we can do better. */ |
78 | cpu_loop_exit_atomic(env_cpu(env), ra); |
79 | #endif |
80 | } |
81 | |
82 | static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val, |
83 | bool parallel, uintptr_t ra) |
84 | { |
85 | switch (addr & 3) { |
86 | case 3: |
87 | cpu_stb_data_ra(env, addr, val, ra); |
88 | break; |
89 | case 2: |
90 | cpu_stw_data_ra(env, addr, val, ra); |
91 | break; |
92 | case 1: |
93 | /* The 3 byte store must appear atomic. */ |
94 | if (parallel) { |
95 | atomic_store_3(env, addr, val, 0x00ffffffu, ra); |
96 | } else { |
97 | cpu_stb_data_ra(env, addr, val >> 16, ra); |
98 | cpu_stw_data_ra(env, addr + 1, val, ra); |
99 | } |
100 | break; |
101 | default: |
102 | cpu_stl_data_ra(env, addr, val, ra); |
103 | break; |
104 | } |
105 | } |
106 | |
107 | void HELPER(stby_b)(CPUHPPAState *env, target_ulong addr, target_ureg val) |
108 | { |
109 | do_stby_b(env, addr, val, false, GETPC()); |
110 | } |
111 | |
112 | void HELPER(stby_b_parallel)(CPUHPPAState *env, target_ulong addr, |
113 | target_ureg val) |
114 | { |
115 | do_stby_b(env, addr, val, true, GETPC()); |
116 | } |
117 | |
118 | static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ureg val, |
119 | bool parallel, uintptr_t ra) |
120 | { |
121 | switch (addr & 3) { |
122 | case 3: |
123 | /* The 3 byte store must appear atomic. */ |
124 | if (parallel) { |
125 | atomic_store_3(env, addr - 3, val, 0xffffff00u, ra); |
126 | } else { |
127 | cpu_stw_data_ra(env, addr - 3, val >> 16, ra); |
128 | cpu_stb_data_ra(env, addr - 1, val >> 8, ra); |
129 | } |
130 | break; |
131 | case 2: |
132 | cpu_stw_data_ra(env, addr - 2, val >> 16, ra); |
133 | break; |
134 | case 1: |
135 | cpu_stb_data_ra(env, addr - 1, val >> 24, ra); |
136 | break; |
137 | default: |
138 | /* Nothing is stored, but protection is checked and the |
139 | cacheline is marked dirty. */ |
140 | probe_write(env, addr, 0, cpu_mmu_index(env, 0), ra); |
141 | break; |
142 | } |
143 | } |
144 | |
145 | void HELPER(stby_e)(CPUHPPAState *env, target_ulong addr, target_ureg val) |
146 | { |
147 | do_stby_e(env, addr, val, false, GETPC()); |
148 | } |
149 | |
150 | void HELPER(stby_e_parallel)(CPUHPPAState *env, target_ulong addr, |
151 | target_ureg val) |
152 | { |
153 | do_stby_e(env, addr, val, true, GETPC()); |
154 | } |
155 | |
156 | target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr, |
157 | uint32_t level, uint32_t want) |
158 | { |
159 | #ifdef CONFIG_USER_ONLY |
160 | return page_check_range(addr, 1, want); |
161 | #else |
162 | int prot, excp; |
163 | hwaddr phys; |
164 | |
165 | trace_hppa_tlb_probe(addr, level, want); |
166 | /* Fail if the requested privilege level is higher than current. */ |
167 | if (level < (env->iaoq_f & 3)) { |
168 | return 0; |
169 | } |
170 | |
171 | excp = hppa_get_physical_address(env, addr, level, 0, &phys, &prot); |
172 | if (excp >= 0) { |
173 | if (env->psw & PSW_Q) { |
174 | /* ??? Needs tweaking for hppa64. */ |
175 | env->cr[CR_IOR] = addr; |
176 | env->cr[CR_ISR] = addr >> 32; |
177 | } |
178 | if (excp == EXCP_DTLB_MISS) { |
179 | excp = EXCP_NA_DTLB_MISS; |
180 | } |
181 | hppa_dynamic_excp(env, excp, GETPC()); |
182 | } |
183 | return (want & prot) != 0; |
184 | #endif |
185 | } |
186 | |
187 | void HELPER(loaded_fr0)(CPUHPPAState *env) |
188 | { |
189 | uint32_t shadow = env->fr[0] >> 32; |
190 | int rm, d; |
191 | |
192 | env->fr0_shadow = shadow; |
193 | |
194 | switch (extract32(shadow, 9, 2)) { |
195 | default: |
196 | rm = float_round_nearest_even; |
197 | break; |
198 | case 1: |
199 | rm = float_round_to_zero; |
200 | break; |
201 | case 2: |
202 | rm = float_round_up; |
203 | break; |
204 | case 3: |
205 | rm = float_round_down; |
206 | break; |
207 | } |
208 | set_float_rounding_mode(rm, &env->fp_status); |
209 | |
210 | d = extract32(shadow, 5, 1); |
211 | set_flush_to_zero(d, &env->fp_status); |
212 | set_flush_inputs_to_zero(d, &env->fp_status); |
213 | } |
214 | |
215 | void cpu_hppa_loaded_fr0(CPUHPPAState *env) |
216 | { |
217 | helper_loaded_fr0(env); |
218 | } |
219 | |
220 | #define CONVERT_BIT(X, SRC, DST) \ |
221 | ((SRC) > (DST) \ |
222 | ? (X) / ((SRC) / (DST)) & (DST) \ |
223 | : ((X) & (SRC)) * ((DST) / (SRC))) |
224 | |
225 | static void update_fr0_op(CPUHPPAState *env, uintptr_t ra) |
226 | { |
227 | uint32_t soft_exp = get_float_exception_flags(&env->fp_status); |
228 | uint32_t hard_exp = 0; |
229 | uint32_t shadow = env->fr0_shadow; |
230 | |
231 | if (likely(soft_exp == 0)) { |
232 | env->fr[0] = (uint64_t)shadow << 32; |
233 | return; |
234 | } |
235 | set_float_exception_flags(0, &env->fp_status); |
236 | |
237 | hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, 1u << 0); |
238 | hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, 1u << 1); |
239 | hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, 1u << 2); |
240 | hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, 1u << 3); |
241 | hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, 1u << 4); |
242 | shadow |= hard_exp << (32 - 5); |
243 | env->fr0_shadow = shadow; |
244 | env->fr[0] = (uint64_t)shadow << 32; |
245 | |
246 | if (hard_exp & shadow) { |
247 | hppa_dynamic_excp(env, EXCP_ASSIST, ra); |
248 | } |
249 | } |
250 | |
251 | float32 HELPER(fsqrt_s)(CPUHPPAState *env, float32 arg) |
252 | { |
253 | float32 ret = float32_sqrt(arg, &env->fp_status); |
254 | update_fr0_op(env, GETPC()); |
255 | return ret; |
256 | } |
257 | |
258 | float32 HELPER(frnd_s)(CPUHPPAState *env, float32 arg) |
259 | { |
260 | float32 ret = float32_round_to_int(arg, &env->fp_status); |
261 | update_fr0_op(env, GETPC()); |
262 | return ret; |
263 | } |
264 | |
265 | float32 HELPER(fadd_s)(CPUHPPAState *env, float32 a, float32 b) |
266 | { |
267 | float32 ret = float32_add(a, b, &env->fp_status); |
268 | update_fr0_op(env, GETPC()); |
269 | return ret; |
270 | } |
271 | |
272 | float32 HELPER(fsub_s)(CPUHPPAState *env, float32 a, float32 b) |
273 | { |
274 | float32 ret = float32_sub(a, b, &env->fp_status); |
275 | update_fr0_op(env, GETPC()); |
276 | return ret; |
277 | } |
278 | |
279 | float32 HELPER(fmpy_s)(CPUHPPAState *env, float32 a, float32 b) |
280 | { |
281 | float32 ret = float32_mul(a, b, &env->fp_status); |
282 | update_fr0_op(env, GETPC()); |
283 | return ret; |
284 | } |
285 | |
286 | float32 HELPER(fdiv_s)(CPUHPPAState *env, float32 a, float32 b) |
287 | { |
288 | float32 ret = float32_div(a, b, &env->fp_status); |
289 | update_fr0_op(env, GETPC()); |
290 | return ret; |
291 | } |
292 | |
293 | float64 HELPER(fsqrt_d)(CPUHPPAState *env, float64 arg) |
294 | { |
295 | float64 ret = float64_sqrt(arg, &env->fp_status); |
296 | update_fr0_op(env, GETPC()); |
297 | return ret; |
298 | } |
299 | |
300 | float64 HELPER(frnd_d)(CPUHPPAState *env, float64 arg) |
301 | { |
302 | float64 ret = float64_round_to_int(arg, &env->fp_status); |
303 | update_fr0_op(env, GETPC()); |
304 | return ret; |
305 | } |
306 | |
307 | float64 HELPER(fadd_d)(CPUHPPAState *env, float64 a, float64 b) |
308 | { |
309 | float64 ret = float64_add(a, b, &env->fp_status); |
310 | update_fr0_op(env, GETPC()); |
311 | return ret; |
312 | } |
313 | |
314 | float64 HELPER(fsub_d)(CPUHPPAState *env, float64 a, float64 b) |
315 | { |
316 | float64 ret = float64_sub(a, b, &env->fp_status); |
317 | update_fr0_op(env, GETPC()); |
318 | return ret; |
319 | } |
320 | |
321 | float64 HELPER(fmpy_d)(CPUHPPAState *env, float64 a, float64 b) |
322 | { |
323 | float64 ret = float64_mul(a, b, &env->fp_status); |
324 | update_fr0_op(env, GETPC()); |
325 | return ret; |
326 | } |
327 | |
328 | float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b) |
329 | { |
330 | float64 ret = float64_div(a, b, &env->fp_status); |
331 | update_fr0_op(env, GETPC()); |
332 | return ret; |
333 | } |
334 | |
335 | float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg) |
336 | { |
337 | float64 ret = float32_to_float64(arg, &env->fp_status); |
338 | update_fr0_op(env, GETPC()); |
339 | return ret; |
340 | } |
341 | |
342 | float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg) |
343 | { |
344 | float32 ret = float64_to_float32(arg, &env->fp_status); |
345 | update_fr0_op(env, GETPC()); |
346 | return ret; |
347 | } |
348 | |
349 | float32 HELPER(fcnv_w_s)(CPUHPPAState *env, int32_t arg) |
350 | { |
351 | float32 ret = int32_to_float32(arg, &env->fp_status); |
352 | update_fr0_op(env, GETPC()); |
353 | return ret; |
354 | } |
355 | |
356 | float32 HELPER(fcnv_dw_s)(CPUHPPAState *env, int64_t arg) |
357 | { |
358 | float32 ret = int64_to_float32(arg, &env->fp_status); |
359 | update_fr0_op(env, GETPC()); |
360 | return ret; |
361 | } |
362 | |
363 | float64 HELPER(fcnv_w_d)(CPUHPPAState *env, int32_t arg) |
364 | { |
365 | float64 ret = int32_to_float64(arg, &env->fp_status); |
366 | update_fr0_op(env, GETPC()); |
367 | return ret; |
368 | } |
369 | |
370 | float64 HELPER(fcnv_dw_d)(CPUHPPAState *env, int64_t arg) |
371 | { |
372 | float64 ret = int64_to_float64(arg, &env->fp_status); |
373 | update_fr0_op(env, GETPC()); |
374 | return ret; |
375 | } |
376 | |
377 | int32_t HELPER(fcnv_s_w)(CPUHPPAState *env, float32 arg) |
378 | { |
379 | int32_t ret = float32_to_int32(arg, &env->fp_status); |
380 | update_fr0_op(env, GETPC()); |
381 | return ret; |
382 | } |
383 | |
384 | int32_t HELPER(fcnv_d_w)(CPUHPPAState *env, float64 arg) |
385 | { |
386 | int32_t ret = float64_to_int32(arg, &env->fp_status); |
387 | update_fr0_op(env, GETPC()); |
388 | return ret; |
389 | } |
390 | |
391 | int64_t HELPER(fcnv_s_dw)(CPUHPPAState *env, float32 arg) |
392 | { |
393 | int64_t ret = float32_to_int64(arg, &env->fp_status); |
394 | update_fr0_op(env, GETPC()); |
395 | return ret; |
396 | } |
397 | |
398 | int64_t HELPER(fcnv_d_dw)(CPUHPPAState *env, float64 arg) |
399 | { |
400 | int64_t ret = float64_to_int64(arg, &env->fp_status); |
401 | update_fr0_op(env, GETPC()); |
402 | return ret; |
403 | } |
404 | |
405 | int32_t HELPER(fcnv_t_s_w)(CPUHPPAState *env, float32 arg) |
406 | { |
407 | int32_t ret = float32_to_int32_round_to_zero(arg, &env->fp_status); |
408 | update_fr0_op(env, GETPC()); |
409 | return ret; |
410 | } |
411 | |
412 | int32_t HELPER(fcnv_t_d_w)(CPUHPPAState *env, float64 arg) |
413 | { |
414 | int32_t ret = float64_to_int32_round_to_zero(arg, &env->fp_status); |
415 | update_fr0_op(env, GETPC()); |
416 | return ret; |
417 | } |
418 | |
419 | int64_t HELPER(fcnv_t_s_dw)(CPUHPPAState *env, float32 arg) |
420 | { |
421 | int64_t ret = float32_to_int64_round_to_zero(arg, &env->fp_status); |
422 | update_fr0_op(env, GETPC()); |
423 | return ret; |
424 | } |
425 | |
426 | int64_t HELPER(fcnv_t_d_dw)(CPUHPPAState *env, float64 arg) |
427 | { |
428 | int64_t ret = float64_to_int64_round_to_zero(arg, &env->fp_status); |
429 | update_fr0_op(env, GETPC()); |
430 | return ret; |
431 | } |
432 | |
433 | float32 HELPER(fcnv_uw_s)(CPUHPPAState *env, uint32_t arg) |
434 | { |
435 | float32 ret = uint32_to_float32(arg, &env->fp_status); |
436 | update_fr0_op(env, GETPC()); |
437 | return ret; |
438 | } |
439 | |
440 | float32 HELPER(fcnv_udw_s)(CPUHPPAState *env, uint64_t arg) |
441 | { |
442 | float32 ret = uint64_to_float32(arg, &env->fp_status); |
443 | update_fr0_op(env, GETPC()); |
444 | return ret; |
445 | } |
446 | |
447 | float64 HELPER(fcnv_uw_d)(CPUHPPAState *env, uint32_t arg) |
448 | { |
449 | float64 ret = uint32_to_float64(arg, &env->fp_status); |
450 | update_fr0_op(env, GETPC()); |
451 | return ret; |
452 | } |
453 | |
454 | float64 HELPER(fcnv_udw_d)(CPUHPPAState *env, uint64_t arg) |
455 | { |
456 | float64 ret = uint64_to_float64(arg, &env->fp_status); |
457 | update_fr0_op(env, GETPC()); |
458 | return ret; |
459 | } |
460 | |
461 | uint32_t HELPER(fcnv_s_uw)(CPUHPPAState *env, float32 arg) |
462 | { |
463 | uint32_t ret = float32_to_uint32(arg, &env->fp_status); |
464 | update_fr0_op(env, GETPC()); |
465 | return ret; |
466 | } |
467 | |
468 | uint32_t HELPER(fcnv_d_uw)(CPUHPPAState *env, float64 arg) |
469 | { |
470 | uint32_t ret = float64_to_uint32(arg, &env->fp_status); |
471 | update_fr0_op(env, GETPC()); |
472 | return ret; |
473 | } |
474 | |
475 | uint64_t HELPER(fcnv_s_udw)(CPUHPPAState *env, float32 arg) |
476 | { |
477 | uint64_t ret = float32_to_uint64(arg, &env->fp_status); |
478 | update_fr0_op(env, GETPC()); |
479 | return ret; |
480 | } |
481 | |
482 | uint64_t HELPER(fcnv_d_udw)(CPUHPPAState *env, float64 arg) |
483 | { |
484 | uint64_t ret = float64_to_uint64(arg, &env->fp_status); |
485 | update_fr0_op(env, GETPC()); |
486 | return ret; |
487 | } |
488 | |
489 | uint32_t HELPER(fcnv_t_s_uw)(CPUHPPAState *env, float32 arg) |
490 | { |
491 | uint32_t ret = float32_to_uint32_round_to_zero(arg, &env->fp_status); |
492 | update_fr0_op(env, GETPC()); |
493 | return ret; |
494 | } |
495 | |
496 | uint32_t HELPER(fcnv_t_d_uw)(CPUHPPAState *env, float64 arg) |
497 | { |
498 | uint32_t ret = float64_to_uint32_round_to_zero(arg, &env->fp_status); |
499 | update_fr0_op(env, GETPC()); |
500 | return ret; |
501 | } |
502 | |
503 | uint64_t HELPER(fcnv_t_s_udw)(CPUHPPAState *env, float32 arg) |
504 | { |
505 | uint64_t ret = float32_to_uint64_round_to_zero(arg, &env->fp_status); |
506 | update_fr0_op(env, GETPC()); |
507 | return ret; |
508 | } |
509 | |
510 | uint64_t HELPER(fcnv_t_d_udw)(CPUHPPAState *env, float64 arg) |
511 | { |
512 | uint64_t ret = float64_to_uint64_round_to_zero(arg, &env->fp_status); |
513 | update_fr0_op(env, GETPC()); |
514 | return ret; |
515 | } |
516 | |
517 | static void update_fr0_cmp(CPUHPPAState *env, uint32_t y, uint32_t c, int r) |
518 | { |
519 | uint32_t shadow = env->fr0_shadow; |
520 | |
521 | switch (r) { |
522 | case float_relation_greater: |
523 | c = extract32(c, 4, 1); |
524 | break; |
525 | case float_relation_less: |
526 | c = extract32(c, 3, 1); |
527 | break; |
528 | case float_relation_equal: |
529 | c = extract32(c, 2, 1); |
530 | break; |
531 | case float_relation_unordered: |
532 | c = extract32(c, 1, 1); |
533 | break; |
534 | default: |
535 | g_assert_not_reached(); |
536 | } |
537 | |
538 | if (y) { |
539 | /* targeted comparison */ |
540 | /* set fpsr[ca[y - 1]] to current compare */ |
541 | shadow = deposit32(shadow, 21 - (y - 1), 1, c); |
542 | } else { |
543 | /* queued comparison */ |
544 | /* shift cq right by one place */ |
545 | shadow = deposit32(shadow, 11, 10, extract32(shadow, 12, 10)); |
546 | /* move fpsr[c] to fpsr[cq[0]] */ |
547 | shadow = deposit32(shadow, 21, 1, extract32(shadow, 26, 1)); |
548 | /* set fpsr[c] to current compare */ |
549 | shadow = deposit32(shadow, 26, 1, c); |
550 | } |
551 | |
552 | env->fr0_shadow = shadow; |
553 | env->fr[0] = (uint64_t)shadow << 32; |
554 | } |
555 | |
556 | void HELPER(fcmp_s)(CPUHPPAState *env, float32 a, float32 b, |
557 | uint32_t y, uint32_t c) |
558 | { |
559 | int r; |
560 | if (c & 1) { |
561 | r = float32_compare(a, b, &env->fp_status); |
562 | } else { |
563 | r = float32_compare_quiet(a, b, &env->fp_status); |
564 | } |
565 | update_fr0_op(env, GETPC()); |
566 | update_fr0_cmp(env, y, c, r); |
567 | } |
568 | |
569 | void HELPER(fcmp_d)(CPUHPPAState *env, float64 a, float64 b, |
570 | uint32_t y, uint32_t c) |
571 | { |
572 | int r; |
573 | if (c & 1) { |
574 | r = float64_compare(a, b, &env->fp_status); |
575 | } else { |
576 | r = float64_compare_quiet(a, b, &env->fp_status); |
577 | } |
578 | update_fr0_op(env, GETPC()); |
579 | update_fr0_cmp(env, y, c, r); |
580 | } |
581 | |
582 | float32 HELPER(fmpyfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) |
583 | { |
584 | float32 ret = float32_muladd(a, b, c, 0, &env->fp_status); |
585 | update_fr0_op(env, GETPC()); |
586 | return ret; |
587 | } |
588 | |
589 | float32 HELPER(fmpynfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) |
590 | { |
591 | float32 ret = float32_muladd(a, b, c, float_muladd_negate_product, |
592 | &env->fp_status); |
593 | update_fr0_op(env, GETPC()); |
594 | return ret; |
595 | } |
596 | |
597 | float64 HELPER(fmpyfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) |
598 | { |
599 | float64 ret = float64_muladd(a, b, c, 0, &env->fp_status); |
600 | update_fr0_op(env, GETPC()); |
601 | return ret; |
602 | } |
603 | |
604 | float64 HELPER(fmpynfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) |
605 | { |
606 | float64 ret = float64_muladd(a, b, c, float_muladd_negate_product, |
607 | &env->fp_status); |
608 | update_fr0_op(env, GETPC()); |
609 | return ret; |
610 | } |
611 | |
612 | target_ureg HELPER(read_interval_timer)(void) |
613 | { |
614 | #ifdef CONFIG_USER_ONLY |
615 | /* In user-mode, QEMU_CLOCK_VIRTUAL doesn't exist. |
616 | Just pass through the host cpu clock ticks. */ |
617 | return cpu_get_host_ticks(); |
618 | #else |
619 | /* In system mode we have access to a decent high-resolution clock. |
620 | In order to make OS-level time accounting work with the cr16, |
621 | present it with a well-timed clock fixed at 250MHz. */ |
622 | return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) >> 2; |
623 | #endif |
624 | } |
625 | |
626 | #ifndef CONFIG_USER_ONLY |
627 | void HELPER(write_interval_timer)(CPUHPPAState *env, target_ureg val) |
628 | { |
629 | HPPACPU *cpu = env_archcpu(env); |
630 | uint64_t current = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
631 | uint64_t timeout; |
632 | |
633 | /* Even in 64-bit mode, the comparator is always 32-bit. But the |
634 | value we expose to the guest is 1/4 of the speed of the clock, |
635 | so moosh in 34 bits. */ |
636 | timeout = deposit64(current, 0, 34, (uint64_t)val << 2); |
637 | |
638 | /* If the mooshing puts the clock in the past, advance to next round. */ |
639 | if (timeout < current + 1000) { |
640 | timeout += 1ULL << 34; |
641 | } |
642 | |
643 | cpu->env.cr[CR_IT] = timeout; |
644 | timer_mod(cpu->alarm_timer, timeout); |
645 | } |
646 | |
647 | void HELPER(halt)(CPUHPPAState *env) |
648 | { |
649 | qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); |
650 | helper_excp(env, EXCP_HLT); |
651 | } |
652 | |
653 | void HELPER(reset)(CPUHPPAState *env) |
654 | { |
655 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
656 | helper_excp(env, EXCP_HLT); |
657 | } |
658 | |
659 | target_ureg HELPER(swap_system_mask)(CPUHPPAState *env, target_ureg nsm) |
660 | { |
661 | target_ulong psw = env->psw; |
662 | /* |
663 | * Setting the PSW Q bit to 1, if it was not already 1, is an |
664 | * undefined operation. |
665 | * |
666 | * However, HP-UX 10.20 does this with the SSM instruction. |
667 | * Tested this on HP9000/712 and HP9000/785/C3750 and both |
668 | * machines set the Q bit from 0 to 1 without an exception, |
669 | * so let this go without comment. |
670 | */ |
671 | env->psw = (psw & ~PSW_SM) | (nsm & PSW_SM); |
672 | return psw & PSW_SM; |
673 | } |
674 | |
675 | void HELPER(rfi)(CPUHPPAState *env) |
676 | { |
677 | env->iasq_f = (uint64_t)env->cr[CR_IIASQ] << 32; |
678 | env->iasq_b = (uint64_t)env->cr_back[0] << 32; |
679 | env->iaoq_f = env->cr[CR_IIAOQ]; |
680 | env->iaoq_b = env->cr_back[1]; |
681 | cpu_hppa_put_psw(env, env->cr[CR_IPSW]); |
682 | } |
683 | |
684 | void HELPER(rfi_r)(CPUHPPAState *env) |
685 | { |
686 | env->gr[1] = env->shadow[0]; |
687 | env->gr[8] = env->shadow[1]; |
688 | env->gr[9] = env->shadow[2]; |
689 | env->gr[16] = env->shadow[3]; |
690 | env->gr[17] = env->shadow[4]; |
691 | env->gr[24] = env->shadow[5]; |
692 | env->gr[25] = env->shadow[6]; |
693 | helper_rfi(env); |
694 | } |
695 | #endif |
696 | |