root / target-ppc / op_helper.c @ 70ead434
History | View | Annotate | Download (12.9 kB)
1 |
/*
|
---|---|
2 |
* PowerPC emulation helpers for qemu.
|
3 |
*
|
4 |
* Copyright (c) 2003-2005 Jocelyn Mayer
|
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, write to the Free Software
|
18 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
19 |
*/
|
20 |
#include "exec.h" |
21 |
|
22 |
#define MEMSUFFIX _raw
|
23 |
#include "op_helper_mem.h" |
24 |
#if !defined(CONFIG_USER_ONLY)
|
25 |
#define MEMSUFFIX _user
|
26 |
#include "op_helper_mem.h" |
27 |
#define MEMSUFFIX _kernel
|
28 |
#include "op_helper_mem.h" |
29 |
#endif
|
30 |
|
31 |
//#define DEBUG_OP
|
32 |
//#define DEBUG_EXCEPTIONS
|
33 |
//#define FLUSH_ALL_TLBS
|
34 |
|
35 |
#define Ts0 (long)((target_long)T0) |
36 |
#define Ts1 (long)((target_long)T1) |
37 |
#define Ts2 (long)((target_long)T2) |
38 |
|
39 |
/*****************************************************************************/
|
40 |
/* Exceptions processing helpers */
|
41 |
void cpu_loop_exit(void) |
42 |
{ |
43 |
longjmp(env->jmp_env, 1);
|
44 |
} |
45 |
|
46 |
void do_raise_exception_err (uint32_t exception, int error_code) |
47 |
{ |
48 |
#if 0
|
49 |
printf("Raise exception %3x code : %d\n", exception, error_code);
|
50 |
#endif
|
51 |
switch (exception) {
|
52 |
case EXCP_PROGRAM:
|
53 |
if (error_code == EXCP_FP && msr_fe0 == 0 && msr_fe1 == 0) |
54 |
return;
|
55 |
break;
|
56 |
default:
|
57 |
break;
|
58 |
} |
59 |
env->exception_index = exception; |
60 |
env->error_code = error_code; |
61 |
cpu_loop_exit(); |
62 |
} |
63 |
|
64 |
void do_raise_exception (uint32_t exception)
|
65 |
{ |
66 |
do_raise_exception_err(exception, 0);
|
67 |
} |
68 |
|
69 |
/*****************************************************************************/
|
70 |
/* Fixed point operations helpers */
|
71 |
void do_addo (void) |
72 |
{ |
73 |
T2 = T0; |
74 |
T0 += T1; |
75 |
if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) { |
76 |
xer_ov = 0;
|
77 |
} else {
|
78 |
xer_so = 1;
|
79 |
xer_ov = 1;
|
80 |
} |
81 |
} |
82 |
|
83 |
void do_addco (void) |
84 |
{ |
85 |
T2 = T0; |
86 |
T0 += T1; |
87 |
if (likely(T0 >= T2)) {
|
88 |
xer_ca = 0;
|
89 |
} else {
|
90 |
xer_ca = 1;
|
91 |
} |
92 |
if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) { |
93 |
xer_ov = 0;
|
94 |
} else {
|
95 |
xer_so = 1;
|
96 |
xer_ov = 1;
|
97 |
} |
98 |
} |
99 |
|
100 |
void do_adde (void) |
101 |
{ |
102 |
T2 = T0; |
103 |
T0 += T1 + xer_ca; |
104 |
if (likely(!(T0 < T2 || (xer_ca == 1 && T0 == T2)))) { |
105 |
xer_ca = 0;
|
106 |
} else {
|
107 |
xer_ca = 1;
|
108 |
} |
109 |
} |
110 |
|
111 |
void do_addeo (void) |
112 |
{ |
113 |
T2 = T0; |
114 |
T0 += T1 + xer_ca; |
115 |
if (likely(!(T0 < T2 || (xer_ca == 1 && T0 == T2)))) { |
116 |
xer_ca = 0;
|
117 |
} else {
|
118 |
xer_ca = 1;
|
119 |
} |
120 |
if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) { |
121 |
xer_ov = 0;
|
122 |
} else {
|
123 |
xer_so = 1;
|
124 |
xer_ov = 1;
|
125 |
} |
126 |
} |
127 |
|
128 |
void do_addmeo (void) |
129 |
{ |
130 |
T1 = T0; |
131 |
T0 += xer_ca + (-1);
|
132 |
if (likely(!(T1 & (T1 ^ T0) & (1 << 31)))) { |
133 |
xer_ov = 0;
|
134 |
} else {
|
135 |
xer_so = 1;
|
136 |
xer_ov = 1;
|
137 |
} |
138 |
if (likely(T1 != 0)) |
139 |
xer_ca = 1;
|
140 |
} |
141 |
|
142 |
void do_addzeo (void) |
143 |
{ |
144 |
T1 = T0; |
145 |
T0 += xer_ca; |
146 |
if (likely(!((T1 ^ (-1)) & (T1 ^ T0) & (1 << 31)))) { |
147 |
xer_ov = 0;
|
148 |
} else {
|
149 |
xer_so = 1;
|
150 |
xer_ov = 1;
|
151 |
} |
152 |
if (likely(T0 >= T1)) {
|
153 |
xer_ca = 0;
|
154 |
} else {
|
155 |
xer_ca = 1;
|
156 |
} |
157 |
} |
158 |
|
159 |
void do_divwo (void) |
160 |
{ |
161 |
if (likely(!((Ts0 == INT32_MIN && Ts1 == -1) || Ts1 == 0))) { |
162 |
xer_ov = 0;
|
163 |
T0 = (Ts0 / Ts1); |
164 |
} else {
|
165 |
xer_so = 1;
|
166 |
xer_ov = 1;
|
167 |
T0 = (-1) * ((uint32_t)T0 >> 31); |
168 |
} |
169 |
} |
170 |
|
171 |
void do_divwuo (void) |
172 |
{ |
173 |
if (likely((uint32_t)T1 != 0)) { |
174 |
xer_ov = 0;
|
175 |
T0 = (uint32_t)T0 / (uint32_t)T1; |
176 |
} else {
|
177 |
xer_so = 1;
|
178 |
xer_ov = 1;
|
179 |
T0 = 0;
|
180 |
} |
181 |
} |
182 |
|
183 |
void do_mullwo (void) |
184 |
{ |
185 |
int64_t res = (int64_t)Ts0 * (int64_t)Ts1; |
186 |
|
187 |
if (likely((int32_t)res == res)) {
|
188 |
xer_ov = 0;
|
189 |
} else {
|
190 |
xer_ov = 1;
|
191 |
xer_so = 1;
|
192 |
} |
193 |
T0 = (int32_t)res; |
194 |
} |
195 |
|
196 |
void do_nego (void) |
197 |
{ |
198 |
if (likely(T0 != INT32_MIN)) {
|
199 |
xer_ov = 0;
|
200 |
T0 = -Ts0; |
201 |
} else {
|
202 |
xer_ov = 1;
|
203 |
xer_so = 1;
|
204 |
} |
205 |
} |
206 |
|
207 |
void do_subfo (void) |
208 |
{ |
209 |
T2 = T0; |
210 |
T0 = T1 - T0; |
211 |
if (likely(!(((~T2) ^ T1 ^ (-1)) & ((~T2) ^ T0) & (1 << 31)))) { |
212 |
xer_ov = 0;
|
213 |
} else {
|
214 |
xer_so = 1;
|
215 |
xer_ov = 1;
|
216 |
} |
217 |
RETURN(); |
218 |
} |
219 |
|
220 |
void do_subfco (void) |
221 |
{ |
222 |
T2 = T0; |
223 |
T0 = T1 - T0; |
224 |
if (likely(T0 > T1)) {
|
225 |
xer_ca = 0;
|
226 |
} else {
|
227 |
xer_ca = 1;
|
228 |
} |
229 |
if (likely(!(((~T2) ^ T1 ^ (-1)) & ((~T2) ^ T0) & (1 << 31)))) { |
230 |
xer_ov = 0;
|
231 |
} else {
|
232 |
xer_so = 1;
|
233 |
xer_ov = 1;
|
234 |
} |
235 |
} |
236 |
|
237 |
void do_subfe (void) |
238 |
{ |
239 |
T0 = T1 + ~T0 + xer_ca; |
240 |
if (likely(T0 >= T1 && (xer_ca == 0 || T0 != T1))) { |
241 |
xer_ca = 0;
|
242 |
} else {
|
243 |
xer_ca = 1;
|
244 |
} |
245 |
} |
246 |
|
247 |
void do_subfeo (void) |
248 |
{ |
249 |
T2 = T0; |
250 |
T0 = T1 + ~T0 + xer_ca; |
251 |
if (likely(!((~T2 ^ T1 ^ (-1)) & (~T2 ^ T0) & (1 << 31)))) { |
252 |
xer_ov = 0;
|
253 |
} else {
|
254 |
xer_so = 1;
|
255 |
xer_ov = 1;
|
256 |
} |
257 |
if (likely(T0 >= T1 && (xer_ca == 0 || T0 != T1))) { |
258 |
xer_ca = 0;
|
259 |
} else {
|
260 |
xer_ca = 1;
|
261 |
} |
262 |
} |
263 |
|
264 |
void do_subfmeo (void) |
265 |
{ |
266 |
T1 = T0; |
267 |
T0 = ~T0 + xer_ca - 1;
|
268 |
if (likely(!(~T1 & (~T1 ^ T0) & (1 << 31)))) { |
269 |
xer_ov = 0;
|
270 |
} else {
|
271 |
xer_so = 1;
|
272 |
xer_ov = 1;
|
273 |
} |
274 |
if (likely(T1 != -1)) |
275 |
xer_ca = 1;
|
276 |
} |
277 |
|
278 |
void do_subfzeo (void) |
279 |
{ |
280 |
T1 = T0; |
281 |
T0 = ~T0 + xer_ca; |
282 |
if (likely(!((~T1 ^ (-1)) & ((~T1) ^ T0) & (1 << 31)))) { |
283 |
xer_ov = 0;
|
284 |
} else {
|
285 |
xer_ov = 1;
|
286 |
xer_so = 1;
|
287 |
} |
288 |
if (likely(T0 >= ~T1)) {
|
289 |
xer_ca = 0;
|
290 |
} else {
|
291 |
xer_ca = 1;
|
292 |
} |
293 |
} |
294 |
|
295 |
/* shift right arithmetic helper */
|
296 |
void do_sraw (void) |
297 |
{ |
298 |
int32_t ret; |
299 |
|
300 |
if (likely(!(T1 & 0x20UL))) { |
301 |
if (likely(T1 != 0)) { |
302 |
ret = (int32_t)T0 >> (T1 & 0x1fUL);
|
303 |
if (likely(ret >= 0 || ((int32_t)T0 & ((1 << T1) - 1)) == 0)) { |
304 |
xer_ca = 0;
|
305 |
} else {
|
306 |
xer_ca = 1;
|
307 |
} |
308 |
} else {
|
309 |
ret = T0; |
310 |
xer_ca = 0;
|
311 |
} |
312 |
} else {
|
313 |
ret = (-1) * ((uint32_t)T0 >> 31); |
314 |
if (likely(ret >= 0 || ((uint32_t)T0 & ~0x80000000UL) == 0)) { |
315 |
xer_ca = 0;
|
316 |
} else {
|
317 |
xer_ca = 1;
|
318 |
} |
319 |
} |
320 |
T0 = ret; |
321 |
} |
322 |
|
323 |
/*****************************************************************************/
|
324 |
/* Floating point operations helpers */
|
325 |
void do_fctiw (void) |
326 |
{ |
327 |
union {
|
328 |
double d;
|
329 |
uint64_t i; |
330 |
} p; |
331 |
|
332 |
/* XXX: higher bits are not supposed to be significant.
|
333 |
* to make tests easier, return the same as a real PowerPC 750 (aka G3)
|
334 |
*/
|
335 |
p.i = float64_to_int32(FT0, &env->fp_status); |
336 |
p.i |= 0xFFF80000ULL << 32; |
337 |
FT0 = p.d; |
338 |
} |
339 |
|
340 |
void do_fctiwz (void) |
341 |
{ |
342 |
union {
|
343 |
double d;
|
344 |
uint64_t i; |
345 |
} p; |
346 |
|
347 |
/* XXX: higher bits are not supposed to be significant.
|
348 |
* to make tests easier, return the same as a real PowerPC 750 (aka G3)
|
349 |
*/
|
350 |
p.i = float64_to_int32_round_to_zero(FT0, &env->fp_status); |
351 |
p.i |= 0xFFF80000ULL << 32; |
352 |
FT0 = p.d; |
353 |
} |
354 |
|
355 |
void do_fnmadd (void) |
356 |
{ |
357 |
FT0 = float64_mul(FT0, FT1, &env->fp_status); |
358 |
FT0 = float64_add(FT0, FT2, &env->fp_status); |
359 |
if (likely(!isnan(FT0)))
|
360 |
FT0 = float64_chs(FT0); |
361 |
} |
362 |
|
363 |
void do_fnmsub (void) |
364 |
{ |
365 |
FT0 = float64_mul(FT0, FT1, &env->fp_status); |
366 |
FT0 = float64_sub(FT0, FT2, &env->fp_status); |
367 |
if (likely(!isnan(FT0)))
|
368 |
FT0 = float64_chs(FT0); |
369 |
} |
370 |
|
371 |
void do_fsqrt (void) |
372 |
{ |
373 |
FT0 = float64_sqrt(FT0, &env->fp_status); |
374 |
} |
375 |
|
376 |
void do_fres (void) |
377 |
{ |
378 |
union {
|
379 |
double d;
|
380 |
uint64_t i; |
381 |
} p; |
382 |
|
383 |
if (likely(isnormal(FT0))) {
|
384 |
FT0 = (float)(1.0 / FT0); |
385 |
} else {
|
386 |
p.d = FT0; |
387 |
if (p.i == 0x8000000000000000ULL) { |
388 |
p.i = 0xFFF0000000000000ULL;
|
389 |
} else if (p.i == 0x0000000000000000ULL) { |
390 |
p.i = 0x7FF0000000000000ULL;
|
391 |
} else if (isnan(FT0)) { |
392 |
p.i = 0x7FF8000000000000ULL;
|
393 |
} else if (FT0 < 0.0) { |
394 |
p.i = 0x8000000000000000ULL;
|
395 |
} else {
|
396 |
p.i = 0x0000000000000000ULL;
|
397 |
} |
398 |
FT0 = p.d; |
399 |
} |
400 |
} |
401 |
|
402 |
void do_frsqrte (void) |
403 |
{ |
404 |
union {
|
405 |
double d;
|
406 |
uint64_t i; |
407 |
} p; |
408 |
|
409 |
if (likely(isnormal(FT0) && FT0 > 0.0)) { |
410 |
FT0 = float64_sqrt(FT0, &env->fp_status); |
411 |
FT0 = float32_div(1.0, FT0, &env->fp_status); |
412 |
} else {
|
413 |
p.d = FT0; |
414 |
if (p.i == 0x8000000000000000ULL) { |
415 |
p.i = 0xFFF0000000000000ULL;
|
416 |
} else if (p.i == 0x0000000000000000ULL) { |
417 |
p.i = 0x7FF0000000000000ULL;
|
418 |
} else if (isnan(FT0)) { |
419 |
if (!(p.i & 0x0008000000000000ULL)) |
420 |
p.i |= 0x000FFFFFFFFFFFFFULL;
|
421 |
} else if (FT0 < 0) { |
422 |
p.i = 0x7FF8000000000000ULL;
|
423 |
} else {
|
424 |
p.i = 0x0000000000000000ULL;
|
425 |
} |
426 |
FT0 = p.d; |
427 |
} |
428 |
} |
429 |
|
430 |
void do_fsel (void) |
431 |
{ |
432 |
if (FT0 >= 0) |
433 |
FT0 = FT1; |
434 |
else
|
435 |
FT0 = FT2; |
436 |
} |
437 |
|
438 |
void do_fcmpu (void) |
439 |
{ |
440 |
if (likely(!isnan(FT0) && !isnan(FT1))) {
|
441 |
if (float64_lt(FT0, FT1, &env->fp_status)) {
|
442 |
T0 = 0x08UL;
|
443 |
} else if (!float64_le(FT0, FT1, &env->fp_status)) { |
444 |
T0 = 0x04UL;
|
445 |
} else {
|
446 |
T0 = 0x02UL;
|
447 |
} |
448 |
} else {
|
449 |
T0 = 0x01UL;
|
450 |
env->fpscr[4] |= 0x1; |
451 |
env->fpscr[6] |= 0x1; |
452 |
} |
453 |
env->fpscr[3] = T0;
|
454 |
} |
455 |
|
456 |
void do_fcmpo (void) |
457 |
{ |
458 |
env->fpscr[4] &= ~0x1; |
459 |
if (likely(!isnan(FT0) && !isnan(FT1))) {
|
460 |
if (float64_lt(FT0, FT1, &env->fp_status)) {
|
461 |
T0 = 0x08UL;
|
462 |
} else if (!float64_le(FT0, FT1, &env->fp_status)) { |
463 |
T0 = 0x04UL;
|
464 |
} else {
|
465 |
T0 = 0x02UL;
|
466 |
} |
467 |
} else {
|
468 |
T0 = 0x01UL;
|
469 |
env->fpscr[4] |= 0x1; |
470 |
/* I don't know how to test "quiet" nan... */
|
471 |
if (0 /* || ! quiet_nan(...) */) { |
472 |
env->fpscr[6] |= 0x1; |
473 |
if (!(env->fpscr[1] & 0x8)) |
474 |
env->fpscr[4] |= 0x8; |
475 |
} else {
|
476 |
env->fpscr[4] |= 0x8; |
477 |
} |
478 |
} |
479 |
env->fpscr[3] = T0;
|
480 |
} |
481 |
|
482 |
void do_rfi (void) |
483 |
{ |
484 |
env->nip = env->spr[SPR_SRR0] & ~0x00000003;
|
485 |
T0 = env->spr[SPR_SRR1] & ~0xFFFF0000UL;
|
486 |
do_store_msr(env, T0); |
487 |
#if defined (DEBUG_OP)
|
488 |
dump_rfi(); |
489 |
#endif
|
490 |
env->interrupt_request |= CPU_INTERRUPT_EXITTB; |
491 |
} |
492 |
|
493 |
void do_tw (uint32_t cmp, int flags) |
494 |
{ |
495 |
if (!likely(!((Ts0 < (int32_t)cmp && (flags & 0x10)) || |
496 |
(Ts0 > (int32_t)cmp && (flags & 0x08)) ||
|
497 |
(Ts0 == (int32_t)cmp && (flags & 0x04)) ||
|
498 |
(T0 < cmp && (flags & 0x02)) ||
|
499 |
(T0 > cmp && (flags & 0x01)))))
|
500 |
do_raise_exception_err(EXCP_PROGRAM, EXCP_TRAP); |
501 |
} |
502 |
|
503 |
/* Instruction cache invalidation helper */
|
504 |
void do_icbi (void) |
505 |
{ |
506 |
uint32_t tmp; |
507 |
/* Invalidate one cache line :
|
508 |
* PowerPC specification says this is to be treated like a load
|
509 |
* (not a fetch) by the MMU. To be sure it will be so,
|
510 |
* do the load "by hand".
|
511 |
*/
|
512 |
#if defined(TARGET_PPC64)
|
513 |
if (!msr_sf)
|
514 |
T0 &= 0xFFFFFFFFULL;
|
515 |
#endif
|
516 |
tmp = ldl_kernel(T0); |
517 |
T0 &= ~(ICACHE_LINE_SIZE - 1);
|
518 |
tb_invalidate_page_range(T0, T0 + ICACHE_LINE_SIZE); |
519 |
} |
520 |
|
521 |
/*****************************************************************************/
|
522 |
/* MMU related helpers */
|
523 |
/* TLB invalidation helpers */
|
524 |
void do_tlbia (void) |
525 |
{ |
526 |
tlb_flush(env, 1);
|
527 |
} |
528 |
|
529 |
void do_tlbie (void) |
530 |
{ |
531 |
#if !defined(FLUSH_ALL_TLBS)
|
532 |
tlb_flush_page(env, T0); |
533 |
#else
|
534 |
do_tlbia(); |
535 |
#endif
|
536 |
} |
537 |
|
538 |
/*****************************************************************************/
|
539 |
/* Softmmu support */
|
540 |
#if !defined (CONFIG_USER_ONLY)
|
541 |
|
542 |
#define MMUSUFFIX _mmu
|
543 |
#define GETPC() (__builtin_return_address(0)) |
544 |
|
545 |
#define SHIFT 0 |
546 |
#include "softmmu_template.h" |
547 |
|
548 |
#define SHIFT 1 |
549 |
#include "softmmu_template.h" |
550 |
|
551 |
#define SHIFT 2 |
552 |
#include "softmmu_template.h" |
553 |
|
554 |
#define SHIFT 3 |
555 |
#include "softmmu_template.h" |
556 |
|
557 |
/* try to fill the TLB and return an exception if error. If retaddr is
|
558 |
NULL, it means that the function was called in C code (i.e. not
|
559 |
from generated code or from helper.c) */
|
560 |
/* XXX: fix it to restore all registers */
|
561 |
void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr) |
562 |
{ |
563 |
TranslationBlock *tb; |
564 |
CPUState *saved_env; |
565 |
target_phys_addr_t pc; |
566 |
int ret;
|
567 |
|
568 |
/* XXX: hack to restore env in all cases, even if not called from
|
569 |
generated code */
|
570 |
saved_env = env; |
571 |
env = cpu_single_env; |
572 |
ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, is_user, 1);
|
573 |
if (!likely(ret == 0)) { |
574 |
if (likely(retaddr)) {
|
575 |
/* now we have a real cpu fault */
|
576 |
pc = (target_phys_addr_t)retaddr; |
577 |
tb = tb_find_pc(pc); |
578 |
if (likely(tb)) {
|
579 |
/* the PC is inside the translated code. It means that we have
|
580 |
a virtual CPU fault */
|
581 |
cpu_restore_state(tb, env, pc, NULL);
|
582 |
} |
583 |
} |
584 |
do_raise_exception_err(env->exception_index, env->error_code); |
585 |
} |
586 |
env = saved_env; |
587 |
} |
588 |
#endif /* !CONFIG_USER_ONLY */ |
589 |
|