29 |
#include "op_helper_mem.h" |
#include "op_helper_mem.h" |
30 |
#endif |
#endif |
31 |
|
|
32 |
|
//#define DEBUG_OP |
33 |
|
//#define DEBUG_EXCEPTIONS |
34 |
|
//#define FLUSH_ALL_TLBS |
35 |
|
|
36 |
|
#define Ts0 (long)((target_long)T0) |
37 |
|
#define Ts1 (long)((target_long)T1) |
38 |
|
#define Ts2 (long)((target_long)T2) |
39 |
|
|
40 |
/*****************************************************************************/ |
/*****************************************************************************/ |
41 |
/* Exceptions processing helpers */ |
/* Exceptions processing helpers */ |
42 |
void cpu_loop_exit(void) |
void cpu_loop_exit(void) |
68 |
} |
} |
69 |
|
|
70 |
/*****************************************************************************/ |
/*****************************************************************************/ |
71 |
/* Helpers for "fat" micro operations */ |
/* Fixed point operations helpers */ |
72 |
|
void do_addo (void) |
73 |
|
{ |
74 |
|
T2 = T0; |
75 |
|
T0 += T1; |
76 |
|
if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) { |
77 |
|
xer_ov = 0; |
78 |
|
} else { |
79 |
|
xer_so = 1; |
80 |
|
xer_ov = 1; |
81 |
|
} |
82 |
|
} |
83 |
|
|
84 |
|
void do_addco (void) |
85 |
|
{ |
86 |
|
T2 = T0; |
87 |
|
T0 += T1; |
88 |
|
if (likely(T0 >= T2)) { |
89 |
|
xer_ca = 0; |
90 |
|
} else { |
91 |
|
xer_ca = 1; |
92 |
|
} |
93 |
|
if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) { |
94 |
|
xer_ov = 0; |
95 |
|
} else { |
96 |
|
xer_so = 1; |
97 |
|
xer_ov = 1; |
98 |
|
} |
99 |
|
} |
100 |
|
|
101 |
|
void do_adde (void) |
102 |
|
{ |
103 |
|
T2 = T0; |
104 |
|
T0 += T1 + xer_ca; |
105 |
|
if (likely(!(T0 < T2 || (xer_ca == 1 && T0 == T2)))) { |
106 |
|
xer_ca = 0; |
107 |
|
} else { |
108 |
|
xer_ca = 1; |
109 |
|
} |
110 |
|
} |
111 |
|
|
112 |
|
void do_addeo (void) |
113 |
|
{ |
114 |
|
T2 = T0; |
115 |
|
T0 += T1 + xer_ca; |
116 |
|
if (likely(!(T0 < T2 || (xer_ca == 1 && T0 == T2)))) { |
117 |
|
xer_ca = 0; |
118 |
|
} else { |
119 |
|
xer_ca = 1; |
120 |
|
} |
121 |
|
if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) { |
122 |
|
xer_ov = 0; |
123 |
|
} else { |
124 |
|
xer_so = 1; |
125 |
|
xer_ov = 1; |
126 |
|
} |
127 |
|
} |
128 |
|
|
129 |
|
void do_addmeo (void) |
130 |
|
{ |
131 |
|
T1 = T0; |
132 |
|
T0 += xer_ca + (-1); |
133 |
|
if (likely(!(T1 & (T1 ^ T0) & (1 << 31)))) { |
134 |
|
xer_ov = 0; |
135 |
|
} else { |
136 |
|
xer_so = 1; |
137 |
|
xer_ov = 1; |
138 |
|
} |
139 |
|
if (likely(T1 != 0)) |
140 |
|
xer_ca = 1; |
141 |
|
} |
142 |
|
|
143 |
|
void do_addzeo (void) |
144 |
|
{ |
145 |
|
T1 = T0; |
146 |
|
T0 += xer_ca; |
147 |
|
if (likely(!((T1 ^ (-1)) & (T1 ^ T0) & (1 << 31)))) { |
148 |
|
xer_ov = 0; |
149 |
|
} else { |
150 |
|
xer_so = 1; |
151 |
|
xer_ov = 1; |
152 |
|
} |
153 |
|
if (likely(T0 >= T1)) { |
154 |
|
xer_ca = 0; |
155 |
|
} else { |
156 |
|
xer_ca = 1; |
157 |
|
} |
158 |
|
} |
159 |
|
|
160 |
|
void do_divwo (void) |
161 |
|
{ |
162 |
|
if (likely(!((Ts0 == INT32_MIN && Ts1 == -1) || Ts1 == 0))) { |
163 |
|
xer_ov = 0; |
164 |
|
T0 = (Ts0 / Ts1); |
165 |
|
} else { |
166 |
|
xer_so = 1; |
167 |
|
xer_ov = 1; |
168 |
|
T0 = (-1) * ((uint32_t)T0 >> 31); |
169 |
|
} |
170 |
|
} |
171 |
|
|
172 |
|
void do_divwuo (void) |
173 |
|
{ |
174 |
|
if (likely((uint32_t)T1 != 0)) { |
175 |
|
xer_ov = 0; |
176 |
|
T0 = (uint32_t)T0 / (uint32_t)T1; |
177 |
|
} else { |
178 |
|
xer_so = 1; |
179 |
|
xer_ov = 1; |
180 |
|
T0 = 0; |
181 |
|
} |
182 |
|
} |
183 |
|
|
184 |
|
void do_mullwo (void) |
185 |
|
{ |
186 |
|
int64_t res = (int64_t)Ts0 * (int64_t)Ts1; |
187 |
|
|
188 |
|
if (likely((int32_t)res == res)) { |
189 |
|
xer_ov = 0; |
190 |
|
} else { |
191 |
|
xer_ov = 1; |
192 |
|
xer_so = 1; |
193 |
|
} |
194 |
|
T0 = (int32_t)res; |
195 |
|
} |
196 |
|
|
197 |
|
void do_nego (void) |
198 |
|
{ |
199 |
|
if (likely(T0 != INT32_MIN)) { |
200 |
|
xer_ov = 0; |
201 |
|
T0 = -Ts0; |
202 |
|
} else { |
203 |
|
xer_ov = 1; |
204 |
|
xer_so = 1; |
205 |
|
} |
206 |
|
} |
207 |
|
|
208 |
|
void do_subfo (void) |
209 |
|
{ |
210 |
|
T2 = T0; |
211 |
|
T0 = T1 - T0; |
212 |
|
if (likely(!(((~T2) ^ T1 ^ (-1)) & ((~T2) ^ T0) & (1 << 31)))) { |
213 |
|
xer_ov = 0; |
214 |
|
} else { |
215 |
|
xer_so = 1; |
216 |
|
xer_ov = 1; |
217 |
|
} |
218 |
|
RETURN(); |
219 |
|
} |
220 |
|
|
221 |
|
void do_subfco (void) |
222 |
|
{ |
223 |
|
T2 = T0; |
224 |
|
T0 = T1 - T0; |
225 |
|
if (likely(T0 > T1)) { |
226 |
|
xer_ca = 0; |
227 |
|
} else { |
228 |
|
xer_ca = 1; |
229 |
|
} |
230 |
|
if (likely(!(((~T2) ^ T1 ^ (-1)) & ((~T2) ^ T0) & (1 << 31)))) { |
231 |
|
xer_ov = 0; |
232 |
|
} else { |
233 |
|
xer_so = 1; |
234 |
|
xer_ov = 1; |
235 |
|
} |
236 |
|
} |
237 |
|
|
238 |
|
void do_subfe (void) |
239 |
|
{ |
240 |
|
T0 = T1 + ~T0 + xer_ca; |
241 |
|
if (likely(T0 >= T1 && (xer_ca == 0 || T0 != T1))) { |
242 |
|
xer_ca = 0; |
243 |
|
} else { |
244 |
|
xer_ca = 1; |
245 |
|
} |
246 |
|
} |
247 |
|
|
248 |
|
void do_subfeo (void) |
249 |
|
{ |
250 |
|
T2 = T0; |
251 |
|
T0 = T1 + ~T0 + xer_ca; |
252 |
|
if (likely(!((~T2 ^ T1 ^ (-1)) & (~T2 ^ T0) & (1 << 31)))) { |
253 |
|
xer_ov = 0; |
254 |
|
} else { |
255 |
|
xer_so = 1; |
256 |
|
xer_ov = 1; |
257 |
|
} |
258 |
|
if (likely(T0 >= T1 && (xer_ca == 0 || T0 != T1))) { |
259 |
|
xer_ca = 0; |
260 |
|
} else { |
261 |
|
xer_ca = 1; |
262 |
|
} |
263 |
|
} |
264 |
|
|
265 |
|
void do_subfmeo (void) |
266 |
|
{ |
267 |
|
T1 = T0; |
268 |
|
T0 = ~T0 + xer_ca - 1; |
269 |
|
if (likely(!(~T1 & (~T1 ^ T0) & (1 << 31)))) { |
270 |
|
xer_ov = 0; |
271 |
|
} else { |
272 |
|
xer_so = 1; |
273 |
|
xer_ov = 1; |
274 |
|
} |
275 |
|
if (likely(T1 != -1)) |
276 |
|
xer_ca = 1; |
277 |
|
} |
278 |
|
|
279 |
|
void do_subfzeo (void) |
280 |
|
{ |
281 |
|
T1 = T0; |
282 |
|
T0 = ~T0 + xer_ca; |
283 |
|
if (likely(!((~T1 ^ (-1)) & ((~T1) ^ T0) & (1 << 31)))) { |
284 |
|
xer_ov = 0; |
285 |
|
} else { |
286 |
|
xer_ov = 1; |
287 |
|
xer_so = 1; |
288 |
|
} |
289 |
|
if (likely(T0 >= ~T1)) { |
290 |
|
xer_ca = 0; |
291 |
|
} else { |
292 |
|
xer_ca = 1; |
293 |
|
} |
294 |
|
} |
295 |
|
|
296 |
/* shift right arithmetic helper */ |
/* shift right arithmetic helper */ |
297 |
void do_sraw (void) |
void do_sraw (void) |
298 |
{ |
{ |
299 |
int32_t ret; |
int32_t ret; |
300 |
|
|
301 |
|
if (likely(!(T1 & 0x20UL))) { |
302 |
|
if (likely(T1 != 0)) { |
303 |
|
ret = (int32_t)T0 >> (T1 & 0x1fUL); |
304 |
|
if (likely(ret >= 0 || ((int32_t)T0 & ((1 << T1) - 1)) == 0)) { |
305 |
xer_ca = 0; |
xer_ca = 0; |
306 |
if (T1 & 0x20) { |
} else { |
|
ret = (-1) * (T0 >> 31); |
|
|
if (ret < 0 && (T0 & ~0x80000000) != 0) |
|
307 |
xer_ca = 1; |
xer_ca = 1; |
308 |
#if 1 // TRY |
} |
309 |
} else if (T1 == 0) { |
} else { |
310 |
ret = T0; |
ret = T0; |
311 |
#endif |
xer_ca = 0; |
312 |
|
} |
313 |
|
} else { |
314 |
|
ret = (-1) * ((uint32_t)T0 >> 31); |
315 |
|
if (likely(ret >= 0 || ((uint32_t)T0 & ~0x80000000UL) == 0)) { |
316 |
|
xer_ca = 0; |
317 |
} else { |
} else { |
|
ret = (int32_t)T0 >> (T1 & 0x1f); |
|
|
if (ret < 0 && ((int32_t)T0 & ((1 << T1) - 1)) != 0) |
|
318 |
xer_ca = 1; |
xer_ca = 1; |
319 |
} |
} |
320 |
|
} |
321 |
T0 = ret; |
T0 = ret; |
322 |
} |
} |
323 |
|
|
324 |
|
/*****************************************************************************/ |
325 |
/* Floating point operations helpers */ |
/* Floating point operations helpers */ |
326 |
void do_fctiw (void) |
void do_fctiw (void) |
327 |
{ |
{ |
355 |
|
|
356 |
void do_fnmadd (void) |
void do_fnmadd (void) |
357 |
{ |
{ |
358 |
FT0 = (FT0 * FT1) + FT2; |
FT0 = float64_mul(FT0, FT1, &env->fp_status); |
359 |
if (!isnan(FT0)) |
FT0 = float64_add(FT0, FT2, &env->fp_status); |
360 |
FT0 = -FT0; |
if (likely(!isnan(FT0))) |
361 |
|
FT0 = float64_chs(FT0); |
362 |
} |
} |
363 |
|
|
364 |
void do_fnmsub (void) |
void do_fnmsub (void) |
365 |
{ |
{ |
366 |
FT0 = (FT0 * FT1) - FT2; |
FT0 = float64_mul(FT0, FT1, &env->fp_status); |
367 |
if (!isnan(FT0)) |
FT0 = float64_sub(FT0, FT2, &env->fp_status); |
368 |
FT0 = -FT0; |
if (likely(!isnan(FT0))) |
369 |
} |
FT0 = float64_chs(FT0); |
|
|
|
|
void do_fdiv (void) |
|
|
{ |
|
|
if (FT0 == -0.0 && FT1 == -0.0) |
|
|
FT0 = 0.0 / 0.0; |
|
|
else |
|
|
FT0 /= FT1; |
|
370 |
} |
} |
371 |
|
|
372 |
void do_fsqrt (void) |
void do_fsqrt (void) |
373 |
{ |
{ |
374 |
FT0 = sqrt(FT0); |
FT0 = float64_sqrt(FT0, &env->fp_status); |
375 |
} |
} |
376 |
|
|
377 |
void do_fres (void) |
void do_fres (void) |
381 |
uint64_t i; |
uint64_t i; |
382 |
} p; |
} p; |
383 |
|
|
384 |
if (isnormal(FT0)) { |
if (likely(isnormal(FT0))) { |
385 |
FT0 = (float)(1.0 / FT0); |
FT0 = (float)(1.0 / FT0); |
386 |
} else { |
} else { |
387 |
p.d = FT0; |
p.d = FT0; |
407 |
uint64_t i; |
uint64_t i; |
408 |
} p; |
} p; |
409 |
|
|
410 |
if (isnormal(FT0) && FT0 > 0.0) { |
if (likely(isnormal(FT0) && FT0 > 0.0)) { |
411 |
FT0 = (float)(1.0 / sqrt(FT0)); |
FT0 = float64_sqrt(FT0, &env->fp_status); |
412 |
|
FT0 = float32_div(1.0, FT0, &env->fp_status); |
413 |
} else { |
} else { |
414 |
p.d = FT0; |
p.d = FT0; |
415 |
if (p.i == 0x8000000000000000ULL) { |
if (p.i == 0x8000000000000000ULL) { |
438 |
|
|
439 |
void do_fcmpu (void) |
void do_fcmpu (void) |
440 |
{ |
{ |
441 |
if (isnan(FT0) || isnan(FT1)) { |
if (likely(!isnan(FT0) && !isnan(FT1))) { |
442 |
T0 = 0x01; |
if (float64_lt(FT0, FT1, &env->fp_status)) { |
443 |
|
T0 = 0x08UL; |
444 |
|
} else if (!float64_le(FT0, FT1, &env->fp_status)) { |
445 |
|
T0 = 0x04UL; |
446 |
|
} else { |
447 |
|
T0 = 0x02UL; |
448 |
|
} |
449 |
|
} else { |
450 |
|
T0 = 0x01UL; |
451 |
env->fpscr[4] |= 0x1; |
env->fpscr[4] |= 0x1; |
452 |
env->fpscr[6] |= 0x1; |
env->fpscr[6] |= 0x1; |
|
} else if (FT0 < FT1) { |
|
|
T0 = 0x08; |
|
|
} else if (FT0 > FT1) { |
|
|
T0 = 0x04; |
|
|
} else { |
|
|
T0 = 0x02; |
|
453 |
} |
} |
454 |
env->fpscr[3] = T0; |
env->fpscr[3] = T0; |
455 |
} |
} |
457 |
void do_fcmpo (void) |
void do_fcmpo (void) |
458 |
{ |
{ |
459 |
env->fpscr[4] &= ~0x1; |
env->fpscr[4] &= ~0x1; |
460 |
if (isnan(FT0) || isnan(FT1)) { |
if (likely(!isnan(FT0) && !isnan(FT1))) { |
461 |
T0 = 0x01; |
if (float64_lt(FT0, FT1, &env->fp_status)) { |
462 |
|
T0 = 0x08UL; |
463 |
|
} else if (!float64_le(FT0, FT1, &env->fp_status)) { |
464 |
|
T0 = 0x04UL; |
465 |
|
} else { |
466 |
|
T0 = 0x02UL; |
467 |
|
} |
468 |
|
} else { |
469 |
|
T0 = 0x01UL; |
470 |
env->fpscr[4] |= 0x1; |
env->fpscr[4] |= 0x1; |
471 |
/* I don't know how to test "quiet" nan... */ |
/* I don't know how to test "quiet" nan... */ |
472 |
if (0 /* || ! quiet_nan(...) */) { |
if (0 /* || ! quiet_nan(...) */) { |
476 |
} else { |
} else { |
477 |
env->fpscr[4] |= 0x8; |
env->fpscr[4] |= 0x8; |
478 |
} |
} |
|
} else if (FT0 < FT1) { |
|
|
T0 = 0x08; |
|
|
} else if (FT0 > FT1) { |
|
|
T0 = 0x04; |
|
|
} else { |
|
|
T0 = 0x02; |
|
479 |
} |
} |
480 |
env->fpscr[3] = T0; |
env->fpscr[3] = T0; |
481 |
} |
} |
482 |
|
|
483 |
void do_fabs (void) |
void do_rfi (void) |
484 |
{ |
{ |
485 |
union { |
env->nip = env->spr[SPR_SRR0] & ~0x00000003; |
486 |
double d; |
T0 = env->spr[SPR_SRR1] & ~0xFFFF0000UL; |
487 |
uint64_t i; |
do_store_msr(env, T0); |
488 |
} p; |
#if defined (DEBUG_OP) |
489 |
|
dump_rfi(); |
490 |
p.d = FT0; |
#endif |
491 |
p.i &= ~0x8000000000000000ULL; |
env->interrupt_request |= CPU_INTERRUPT_EXITTB; |
|
FT0 = p.d; |
|
492 |
} |
} |
493 |
|
|
494 |
void do_fnabs (void) |
void do_tw (uint32_t cmp, int flags) |
495 |
{ |
{ |
496 |
union { |
if (!likely(!((Ts0 < (int32_t)cmp && (flags & 0x10)) || |
497 |
double d; |
(Ts0 > (int32_t)cmp && (flags & 0x08)) || |
498 |
uint64_t i; |
(Ts0 == (int32_t)cmp && (flags & 0x04)) || |
499 |
} p; |
(T0 < cmp && (flags & 0x02)) || |
500 |
|
(T0 > cmp && (flags & 0x01))))) |
501 |
p.d = FT0; |
do_raise_exception_err(EXCP_PROGRAM, EXCP_TRAP); |
|
p.i |= 0x8000000000000000ULL; |
|
|
FT0 = p.d; |
|
502 |
} |
} |
503 |
|
|
504 |
/* Instruction cache invalidation helper */ |
/* Instruction cache invalidation helper */ |
|
#define ICACHE_LINE_SIZE 32 |
|
|
|
|
|
void do_check_reservation (void) |
|
|
{ |
|
|
if ((env->reserve & ~0x03) == T0) |
|
|
env->reserve = -1; |
|
|
} |
|
|
|
|
505 |
void do_icbi (void) |
void do_icbi (void) |
506 |
{ |
{ |
507 |
/* Invalidate one cache line */ |
uint32_t tmp; |
508 |
|
/* Invalidate one cache line : |
509 |
|
* PowerPC specification says this is to be treated like a load |
510 |
|
* (not a fetch) by the MMU. To be sure it will be so, |
511 |
|
* do the load "by hand". |
512 |
|
*/ |
513 |
|
#if defined(TARGET_PPC64) |
514 |
|
if (!msr_sf) |
515 |
|
T0 &= 0xFFFFFFFFULL; |
516 |
|
#endif |
517 |
|
tmp = ldl_kernel(T0); |
518 |
T0 &= ~(ICACHE_LINE_SIZE - 1); |
T0 &= ~(ICACHE_LINE_SIZE - 1); |
519 |
tb_invalidate_page_range(T0, T0 + ICACHE_LINE_SIZE); |
tb_invalidate_page_range(T0, T0 + ICACHE_LINE_SIZE); |
520 |
} |
} |
521 |
|
|
522 |
|
/*****************************************************************************/ |
523 |
|
/* MMU related helpers */ |
524 |
/* TLB invalidation helpers */ |
/* TLB invalidation helpers */ |
525 |
void do_tlbia (void) |
void do_tlbia (void) |
526 |
{ |
{ |
529 |
|
|
530 |
void do_tlbie (void) |
void do_tlbie (void) |
531 |
{ |
{ |
532 |
|
#if !defined(FLUSH_ALL_TLBS) |
533 |
tlb_flush_page(env, T0); |
tlb_flush_page(env, T0); |
534 |
|
#else |
535 |
|
do_tlbia(); |
536 |
|
#endif |
537 |
|
} |
538 |
|
|
539 |
|
/*****************************************************************************/ |
540 |
|
/* Softmmu support */ |
541 |
|
#if !defined (CONFIG_USER_ONLY) |
542 |
|
|
543 |
|
#define MMUSUFFIX _mmu |
544 |
|
#define GETPC() (__builtin_return_address(0)) |
545 |
|
|
546 |
|
#define SHIFT 0 |
547 |
|
#include "softmmu_template.h" |
548 |
|
|
549 |
|
#define SHIFT 1 |
550 |
|
#include "softmmu_template.h" |
551 |
|
|
552 |
|
#define SHIFT 2 |
553 |
|
#include "softmmu_template.h" |
554 |
|
|
555 |
|
#define SHIFT 3 |
556 |
|
#include "softmmu_template.h" |
557 |
|
|
558 |
|
/* try to fill the TLB and return an exception if error. If retaddr is |
559 |
|
NULL, it means that the function was called in C code (i.e. not |
560 |
|
from generated code or from helper.c) */ |
561 |
|
/* XXX: fix it to restore all registers */ |
562 |
|
void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr) |
563 |
|
{ |
564 |
|
TranslationBlock *tb; |
565 |
|
CPUState *saved_env; |
566 |
|
target_phys_addr_t pc; |
567 |
|
int ret; |
568 |
|
|
569 |
|
/* XXX: hack to restore env in all cases, even if not called from |
570 |
|
generated code */ |
571 |
|
saved_env = env; |
572 |
|
env = cpu_single_env; |
573 |
|
ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, is_user, 1); |
574 |
|
if (!likely(ret == 0)) { |
575 |
|
if (likely(retaddr)) { |
576 |
|
/* now we have a real cpu fault */ |
577 |
|
pc = (target_phys_addr_t)retaddr; |
578 |
|
tb = tb_find_pc(pc); |
579 |
|
if (likely(tb)) { |
580 |
|
/* the PC is inside the translated code. It means that we have |
581 |
|
a virtual CPU fault */ |
582 |
|
cpu_restore_state(tb, env, pc, NULL); |
583 |
|
} |
584 |
|
} |
585 |
|
do_raise_exception_err(env->exception_index, env->error_code); |
586 |
|
} |
587 |
|
env = saved_env; |
588 |
} |
} |
589 |
|
#endif /* !CONFIG_USER_ONLY */ |
590 |
|
|