1/*
2 * translate-fp.c
3 *
4 * Standard FPU translation
5 */
6
7static inline void gen_reset_fpstatus(void)
8{
9    gen_helper_reset_fpstatus(tcg_env);
10}
11
12static inline void gen_compute_fprf_float64(TCGv_i64 arg)
13{
14    gen_helper_compute_fprf_float64(tcg_env, arg);
15    gen_helper_float_check_status(tcg_env);
16}
17
18#if defined(TARGET_PPC64)
19static void gen_set_cr1_from_fpscr(DisasContext *ctx)
20{
21    TCGv_i32 tmp = tcg_temp_new_i32();
22    tcg_gen_trunc_tl_i32(tmp, cpu_fpscr);
23    tcg_gen_shri_i32(cpu_crf[1], tmp, 28);
24}
25#else
26static void gen_set_cr1_from_fpscr(DisasContext *ctx)
27{
28    tcg_gen_shri_tl(cpu_crf[1], cpu_fpscr, 28);
29}
30#endif
31
32/***                       Floating-Point arithmetic                       ***/
33#define _GEN_FLOAT_ACB(name, op1, op2, set_fprf, type)                        \
34static void gen_f##name(DisasContext *ctx)                                    \
35{                                                                             \
36    TCGv_i64 t0;                                                              \
37    TCGv_i64 t1;                                                              \
38    TCGv_i64 t2;                                                              \
39    TCGv_i64 t3;                                                              \
40    if (unlikely(!ctx->fpu_enabled)) {                                        \
41        gen_exception(ctx, POWERPC_EXCP_FPU);                                 \
42        return;                                                               \
43    }                                                                         \
44    t0 = tcg_temp_new_i64();                                                  \
45    t1 = tcg_temp_new_i64();                                                  \
46    t2 = tcg_temp_new_i64();                                                  \
47    t3 = tcg_temp_new_i64();                                                  \
48    gen_reset_fpstatus();                                                     \
49    get_fpr(t0, rA(ctx->opcode));                                             \
50    get_fpr(t1, rC(ctx->opcode));                                             \
51    get_fpr(t2, rB(ctx->opcode));                                             \
52    gen_helper_f##name(t3, tcg_env, t0, t1, t2);                              \
53    set_fpr(rD(ctx->opcode), t3);                                             \
54    if (set_fprf) {                                                           \
55        gen_compute_fprf_float64(t3);                                         \
56    }                                                                         \
57    if (unlikely(Rc(ctx->opcode) != 0)) {                                     \
58        gen_set_cr1_from_fpscr(ctx);                                          \
59    }                                                                         \
60}
61
62#define GEN_FLOAT_ACB(name, op2, set_fprf, type)                              \
63_GEN_FLOAT_ACB(name, 0x3F, op2, set_fprf, type);                              \
64_GEN_FLOAT_ACB(name##s, 0x3B, op2, set_fprf, type);
65
66#define _GEN_FLOAT_AB(name, op1, op2, inval, set_fprf, type)                  \
67static void gen_f##name(DisasContext *ctx)                                    \
68{                                                                             \
69    TCGv_i64 t0;                                                              \
70    TCGv_i64 t1;                                                              \
71    TCGv_i64 t2;                                                              \
72    if (unlikely(!ctx->fpu_enabled)) {                                        \
73        gen_exception(ctx, POWERPC_EXCP_FPU);                                 \
74        return;                                                               \
75    }                                                                         \
76    t0 = tcg_temp_new_i64();                                                  \
77    t1 = tcg_temp_new_i64();                                                  \
78    t2 = tcg_temp_new_i64();                                                  \
79    gen_reset_fpstatus();                                                     \
80    get_fpr(t0, rA(ctx->opcode));                                             \
81    get_fpr(t1, rB(ctx->opcode));                                             \
82    gen_helper_f##name(t2, tcg_env, t0, t1);                                  \
83    set_fpr(rD(ctx->opcode), t2);                                             \
84    if (set_fprf) {                                                           \
85        gen_compute_fprf_float64(t2);                                         \
86    }                                                                         \
87    if (unlikely(Rc(ctx->opcode) != 0)) {                                     \
88        gen_set_cr1_from_fpscr(ctx);                                          \
89    }                                                                         \
90}
91#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type)                        \
92_GEN_FLOAT_AB(name, 0x3F, op2, inval, set_fprf, type);                        \
93_GEN_FLOAT_AB(name##s, 0x3B, op2, inval, set_fprf, type);
94
95#define _GEN_FLOAT_AC(name, op1, op2, inval, set_fprf, type)                  \
96static void gen_f##name(DisasContext *ctx)                                    \
97{                                                                             \
98    TCGv_i64 t0;                                                              \
99    TCGv_i64 t1;                                                              \
100    TCGv_i64 t2;                                                              \
101    if (unlikely(!ctx->fpu_enabled)) {                                        \
102        gen_exception(ctx, POWERPC_EXCP_FPU);                                 \
103        return;                                                               \
104    }                                                                         \
105    t0 = tcg_temp_new_i64();                                                  \
106    t1 = tcg_temp_new_i64();                                                  \
107    t2 = tcg_temp_new_i64();                                                  \
108    gen_reset_fpstatus();                                                     \
109    get_fpr(t0, rA(ctx->opcode));                                             \
110    get_fpr(t1, rC(ctx->opcode));                                             \
111    gen_helper_f##name(t2, tcg_env, t0, t1);                                  \
112    set_fpr(rD(ctx->opcode), t2);                                             \
113    if (set_fprf) {                                                           \
114        gen_compute_fprf_float64(t2);                                         \
115    }                                                                         \
116    if (unlikely(Rc(ctx->opcode) != 0)) {                                     \
117        gen_set_cr1_from_fpscr(ctx);                                          \
118    }                                                                         \
119}
120#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type)                        \
121_GEN_FLOAT_AC(name, 0x3F, op2, inval, set_fprf, type);                        \
122_GEN_FLOAT_AC(name##s, 0x3B, op2, inval, set_fprf, type);
123
124#define GEN_FLOAT_B(name, op2, op3, set_fprf, type)                           \
125static void gen_f##name(DisasContext *ctx)                                    \
126{                                                                             \
127    TCGv_i64 t0;                                                              \
128    TCGv_i64 t1;                                                              \
129    if (unlikely(!ctx->fpu_enabled)) {                                        \
130        gen_exception(ctx, POWERPC_EXCP_FPU);                                 \
131        return;                                                               \
132    }                                                                         \
133    t0 = tcg_temp_new_i64();                                                  \
134    t1 = tcg_temp_new_i64();                                                  \
135    gen_reset_fpstatus();                                                     \
136    get_fpr(t0, rB(ctx->opcode));                                             \
137    gen_helper_f##name(t1, tcg_env, t0);                                      \
138    set_fpr(rD(ctx->opcode), t1);                                             \
139    if (set_fprf) {                                                           \
140        gen_helper_compute_fprf_float64(tcg_env, t1);                         \
141    }                                                                         \
142    gen_helper_float_check_status(tcg_env);                                   \
143    if (unlikely(Rc(ctx->opcode) != 0)) {                                     \
144        gen_set_cr1_from_fpscr(ctx);                                          \
145    }                                                                         \
146}
147
148#define GEN_FLOAT_BS(name, op1, op2, set_fprf, type)                          \
149static void gen_f##name(DisasContext *ctx)                                    \
150{                                                                             \
151    TCGv_i64 t0;                                                              \
152    TCGv_i64 t1;                                                              \
153    if (unlikely(!ctx->fpu_enabled)) {                                        \
154        gen_exception(ctx, POWERPC_EXCP_FPU);                                 \
155        return;                                                               \
156    }                                                                         \
157    t0 = tcg_temp_new_i64();                                                  \
158    t1 = tcg_temp_new_i64();                                                  \
159    gen_reset_fpstatus();                                                     \
160    get_fpr(t0, rB(ctx->opcode));                                             \
161    gen_helper_f##name(t1, tcg_env, t0);                                      \
162    set_fpr(rD(ctx->opcode), t1);                                             \
163    if (set_fprf) {                                                           \
164        gen_compute_fprf_float64(t1);                                         \
165    }                                                                         \
166    if (unlikely(Rc(ctx->opcode) != 0)) {                                     \
167        gen_set_cr1_from_fpscr(ctx);                                          \
168    }                                                                         \
169}
170
171/* fadd - fadds */
172GEN_FLOAT_AB(add, 0x15, 0x000007C0, 1, PPC_FLOAT);
173/* fdiv - fdivs */
174GEN_FLOAT_AB(div, 0x12, 0x000007C0, 1, PPC_FLOAT);
175/* fmul - fmuls */
176GEN_FLOAT_AC(mul, 0x19, 0x0000F800, 1, PPC_FLOAT);
177
178/* fre */
179GEN_FLOAT_BS(re, 0x3F, 0x18, 1, PPC_FLOAT_EXT);
180
181/* fres */
182GEN_FLOAT_BS(res, 0x3B, 0x18, 1, PPC_FLOAT_FRES);
183
184/* frsqrte */
185GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE);
186
187/* frsqrtes */
188static void gen_frsqrtes(DisasContext *ctx)
189{
190    TCGv_i64 t0;
191    TCGv_i64 t1;
192    if (unlikely(!ctx->fpu_enabled)) {
193        gen_exception(ctx, POWERPC_EXCP_FPU);
194        return;
195    }
196    t0 = tcg_temp_new_i64();
197    t1 = tcg_temp_new_i64();
198    gen_reset_fpstatus();
199    get_fpr(t0, rB(ctx->opcode));
200    gen_helper_frsqrtes(t1, tcg_env, t0);
201    set_fpr(rD(ctx->opcode), t1);
202    gen_compute_fprf_float64(t1);
203    if (unlikely(Rc(ctx->opcode) != 0)) {
204        gen_set_cr1_from_fpscr(ctx);
205    }
206}
207
208static bool trans_FSEL(DisasContext *ctx, arg_A *a)
209{
210    TCGv_i64 t0, t1, t2;
211
212    REQUIRE_INSNS_FLAGS(ctx, FLOAT_FSEL);
213    REQUIRE_FPU(ctx);
214
215    t0 = tcg_temp_new_i64();
216    t1 = tcg_temp_new_i64();
217    t2 = tcg_temp_new_i64();
218
219    get_fpr(t0, a->fra);
220    get_fpr(t1, a->frb);
221    get_fpr(t2, a->frc);
222
223    gen_helper_FSEL(t0, t0, t1, t2);
224    set_fpr(a->frt, t0);
225    if (a->rc) {
226        gen_set_cr1_from_fpscr(ctx);
227    }
228    return true;
229}
230
231/* fsub - fsubs */
232GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT);
233/* Optional: */
234
235static bool do_helper_fsqrt(DisasContext *ctx, arg_A_tb *a,
236                            void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64))
237{
238    TCGv_i64 t0, t1;
239
240    REQUIRE_INSNS_FLAGS(ctx, FLOAT_FSQRT);
241    REQUIRE_FPU(ctx);
242
243    t0 = tcg_temp_new_i64();
244    t1 = tcg_temp_new_i64();
245
246    gen_reset_fpstatus();
247    get_fpr(t0, a->frb);
248    helper(t1, tcg_env, t0);
249    set_fpr(a->frt, t1);
250    gen_compute_fprf_float64(t1);
251    if (unlikely(a->rc != 0)) {
252        gen_set_cr1_from_fpscr(ctx);
253    }
254    return true;
255}
256
257TRANS(FSQRT, do_helper_fsqrt, gen_helper_FSQRT);
258TRANS(FSQRTS, do_helper_fsqrt, gen_helper_FSQRTS);
259
260/***                     Floating-Point multiply-and-add                   ***/
261/* fmadd - fmadds */
262GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT);
263/* fmsub - fmsubs */
264GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT);
265/* fnmadd - fnmadds */
266GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT);
267/* fnmsub - fnmsubs */
268GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT);
269
270/***                     Floating-Point round & convert                    ***/
271/* fctiw */
272GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT);
273/* fctiwu */
274GEN_FLOAT_B(ctiwu, 0x0E, 0x04, 0, PPC2_FP_CVT_ISA206);
275/* fctiwz */
276GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT);
277/* fctiwuz */
278GEN_FLOAT_B(ctiwuz, 0x0F, 0x04, 0, PPC2_FP_CVT_ISA206);
279/* frsp */
280GEN_FLOAT_B(rsp, 0x0C, 0x00, 1, PPC_FLOAT);
281/* fcfid */
282GEN_FLOAT_B(cfid, 0x0E, 0x1A, 1, PPC2_FP_CVT_S64);
283/* fcfids */
284GEN_FLOAT_B(cfids, 0x0E, 0x1A, 0, PPC2_FP_CVT_ISA206);
285/* fcfidu */
286GEN_FLOAT_B(cfidu, 0x0E, 0x1E, 0, PPC2_FP_CVT_ISA206);
287/* fcfidus */
288GEN_FLOAT_B(cfidus, 0x0E, 0x1E, 0, PPC2_FP_CVT_ISA206);
289/* fctid */
290GEN_FLOAT_B(ctid, 0x0E, 0x19, 0, PPC2_FP_CVT_S64);
291/* fctidu */
292GEN_FLOAT_B(ctidu, 0x0E, 0x1D, 0, PPC2_FP_CVT_ISA206);
293/* fctidz */
294GEN_FLOAT_B(ctidz, 0x0F, 0x19, 0, PPC2_FP_CVT_S64);
295/* fctidu */
296GEN_FLOAT_B(ctiduz, 0x0F, 0x1D, 0, PPC2_FP_CVT_ISA206);
297
298/* frin */
299GEN_FLOAT_B(rin, 0x08, 0x0C, 1, PPC_FLOAT_EXT);
300/* friz */
301GEN_FLOAT_B(riz, 0x08, 0x0D, 1, PPC_FLOAT_EXT);
302/* frip */
303GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT);
304/* frim */
305GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT);
306
307static void gen_ftdiv(DisasContext *ctx)
308{
309    TCGv_i64 t0;
310    TCGv_i64 t1;
311    if (unlikely(!ctx->fpu_enabled)) {
312        gen_exception(ctx, POWERPC_EXCP_FPU);
313        return;
314    }
315    t0 = tcg_temp_new_i64();
316    t1 = tcg_temp_new_i64();
317    get_fpr(t0, rA(ctx->opcode));
318    get_fpr(t1, rB(ctx->opcode));
319    gen_helper_ftdiv(cpu_crf[crfD(ctx->opcode)], t0, t1);
320}
321
322static void gen_ftsqrt(DisasContext *ctx)
323{
324    TCGv_i64 t0;
325    if (unlikely(!ctx->fpu_enabled)) {
326        gen_exception(ctx, POWERPC_EXCP_FPU);
327        return;
328    }
329    t0 = tcg_temp_new_i64();
330    get_fpr(t0, rB(ctx->opcode));
331    gen_helper_ftsqrt(cpu_crf[crfD(ctx->opcode)], t0);
332}
333
334
335
336/***                         Floating-Point compare                        ***/
337
338/* fcmpo */
339static void gen_fcmpo(DisasContext *ctx)
340{
341    TCGv_i32 crf;
342    TCGv_i64 t0;
343    TCGv_i64 t1;
344    if (unlikely(!ctx->fpu_enabled)) {
345        gen_exception(ctx, POWERPC_EXCP_FPU);
346        return;
347    }
348    t0 = tcg_temp_new_i64();
349    t1 = tcg_temp_new_i64();
350    gen_reset_fpstatus();
351    crf = tcg_constant_i32(crfD(ctx->opcode));
352    get_fpr(t0, rA(ctx->opcode));
353    get_fpr(t1, rB(ctx->opcode));
354    gen_helper_fcmpo(tcg_env, t0, t1, crf);
355    gen_helper_float_check_status(tcg_env);
356}
357
358/* fcmpu */
359static void gen_fcmpu(DisasContext *ctx)
360{
361    TCGv_i32 crf;
362    TCGv_i64 t0;
363    TCGv_i64 t1;
364    if (unlikely(!ctx->fpu_enabled)) {
365        gen_exception(ctx, POWERPC_EXCP_FPU);
366        return;
367    }
368    t0 = tcg_temp_new_i64();
369    t1 = tcg_temp_new_i64();
370    gen_reset_fpstatus();
371    crf = tcg_constant_i32(crfD(ctx->opcode));
372    get_fpr(t0, rA(ctx->opcode));
373    get_fpr(t1, rB(ctx->opcode));
374    gen_helper_fcmpu(tcg_env, t0, t1, crf);
375    gen_helper_float_check_status(tcg_env);
376}
377
378/***                         Floating-point move                           ***/
379/* fabs */
380/* XXX: beware that fabs never checks for NaNs nor update FPSCR */
381static void gen_fabs(DisasContext *ctx)
382{
383    TCGv_i64 t0;
384    TCGv_i64 t1;
385    if (unlikely(!ctx->fpu_enabled)) {
386        gen_exception(ctx, POWERPC_EXCP_FPU);
387        return;
388    }
389    t0 = tcg_temp_new_i64();
390    t1 = tcg_temp_new_i64();
391    get_fpr(t0, rB(ctx->opcode));
392    tcg_gen_andi_i64(t1, t0, ~(1ULL << 63));
393    set_fpr(rD(ctx->opcode), t1);
394    if (unlikely(Rc(ctx->opcode))) {
395        gen_set_cr1_from_fpscr(ctx);
396    }
397}
398
399/* fmr  - fmr. */
400/* XXX: beware that fmr never checks for NaNs nor update FPSCR */
401static void gen_fmr(DisasContext *ctx)
402{
403    TCGv_i64 t0;
404    if (unlikely(!ctx->fpu_enabled)) {
405        gen_exception(ctx, POWERPC_EXCP_FPU);
406        return;
407    }
408    t0 = tcg_temp_new_i64();
409    get_fpr(t0, rB(ctx->opcode));
410    set_fpr(rD(ctx->opcode), t0);
411    if (unlikely(Rc(ctx->opcode))) {
412        gen_set_cr1_from_fpscr(ctx);
413    }
414}
415
416/* fnabs */
417/* XXX: beware that fnabs never checks for NaNs nor update FPSCR */
418static void gen_fnabs(DisasContext *ctx)
419{
420    TCGv_i64 t0;
421    TCGv_i64 t1;
422    if (unlikely(!ctx->fpu_enabled)) {
423        gen_exception(ctx, POWERPC_EXCP_FPU);
424        return;
425    }
426    t0 = tcg_temp_new_i64();
427    t1 = tcg_temp_new_i64();
428    get_fpr(t0, rB(ctx->opcode));
429    tcg_gen_ori_i64(t1, t0, 1ULL << 63);
430    set_fpr(rD(ctx->opcode), t1);
431    if (unlikely(Rc(ctx->opcode))) {
432        gen_set_cr1_from_fpscr(ctx);
433    }
434}
435
436/* fneg */
437/* XXX: beware that fneg never checks for NaNs nor update FPSCR */
438static void gen_fneg(DisasContext *ctx)
439{
440    TCGv_i64 t0;
441    TCGv_i64 t1;
442    if (unlikely(!ctx->fpu_enabled)) {
443        gen_exception(ctx, POWERPC_EXCP_FPU);
444        return;
445    }
446    t0 = tcg_temp_new_i64();
447    t1 = tcg_temp_new_i64();
448    get_fpr(t0, rB(ctx->opcode));
449    tcg_gen_xori_i64(t1, t0, 1ULL << 63);
450    set_fpr(rD(ctx->opcode), t1);
451    if (unlikely(Rc(ctx->opcode))) {
452        gen_set_cr1_from_fpscr(ctx);
453    }
454}
455
456/* fcpsgn: PowerPC 2.05 specification */
457/* XXX: beware that fcpsgn never checks for NaNs nor update FPSCR */
458static void gen_fcpsgn(DisasContext *ctx)
459{
460    TCGv_i64 t0;
461    TCGv_i64 t1;
462    TCGv_i64 t2;
463    if (unlikely(!ctx->fpu_enabled)) {
464        gen_exception(ctx, POWERPC_EXCP_FPU);
465        return;
466    }
467    t0 = tcg_temp_new_i64();
468    t1 = tcg_temp_new_i64();
469    t2 = tcg_temp_new_i64();
470    get_fpr(t0, rA(ctx->opcode));
471    get_fpr(t1, rB(ctx->opcode));
472    tcg_gen_deposit_i64(t2, t0, t1, 0, 63);
473    set_fpr(rD(ctx->opcode), t2);
474    if (unlikely(Rc(ctx->opcode))) {
475        gen_set_cr1_from_fpscr(ctx);
476    }
477}
478
479static void gen_fmrgew(DisasContext *ctx)
480{
481    TCGv_i64 b0;
482    TCGv_i64 t0;
483    TCGv_i64 t1;
484    if (unlikely(!ctx->fpu_enabled)) {
485        gen_exception(ctx, POWERPC_EXCP_FPU);
486        return;
487    }
488    b0 = tcg_temp_new_i64();
489    t0 = tcg_temp_new_i64();
490    t1 = tcg_temp_new_i64();
491    get_fpr(t0, rB(ctx->opcode));
492    tcg_gen_shri_i64(b0, t0, 32);
493    get_fpr(t0, rA(ctx->opcode));
494    tcg_gen_deposit_i64(t1, t0, b0, 0, 32);
495    set_fpr(rD(ctx->opcode), t1);
496}
497
498static void gen_fmrgow(DisasContext *ctx)
499{
500    TCGv_i64 t0;
501    TCGv_i64 t1;
502    TCGv_i64 t2;
503    if (unlikely(!ctx->fpu_enabled)) {
504        gen_exception(ctx, POWERPC_EXCP_FPU);
505        return;
506    }
507    t0 = tcg_temp_new_i64();
508    t1 = tcg_temp_new_i64();
509    t2 = tcg_temp_new_i64();
510    get_fpr(t0, rB(ctx->opcode));
511    get_fpr(t1, rA(ctx->opcode));
512    tcg_gen_deposit_i64(t2, t0, t1, 32, 32);
513    set_fpr(rD(ctx->opcode), t2);
514}
515
516/***                  Floating-Point status & ctrl register                ***/
517
518/* mcrfs */
519static void gen_mcrfs(DisasContext *ctx)
520{
521    TCGv tmp = tcg_temp_new();
522    TCGv_i32 tmask;
523    TCGv_i64 tnew_fpscr = tcg_temp_new_i64();
524    int bfa;
525    int nibble;
526    int shift;
527
528    if (unlikely(!ctx->fpu_enabled)) {
529        gen_exception(ctx, POWERPC_EXCP_FPU);
530        return;
531    }
532    bfa = crfS(ctx->opcode);
533    nibble = 7 - bfa;
534    shift = 4 * nibble;
535    tcg_gen_shri_tl(tmp, cpu_fpscr, shift);
536    tcg_gen_trunc_tl_i32(cpu_crf[crfD(ctx->opcode)], tmp);
537    tcg_gen_andi_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)],
538                     0xf);
539    tcg_gen_extu_tl_i64(tnew_fpscr, cpu_fpscr);
540    /* Only the exception bits (including FX) should be cleared if read */
541    tcg_gen_andi_i64(tnew_fpscr, tnew_fpscr,
542                     ~((0xF << shift) & FP_EX_CLEAR_BITS));
543    /* FEX and VX need to be updated, so don't set fpscr directly */
544    tmask = tcg_constant_i32(1 << nibble);
545    gen_helper_store_fpscr(tcg_env, tnew_fpscr, tmask);
546}
547
548static TCGv_i64 place_from_fpscr(int rt, uint64_t mask)
549{
550    TCGv_i64 fpscr = tcg_temp_new_i64();
551    TCGv_i64 fpscr_masked = tcg_temp_new_i64();
552
553    tcg_gen_extu_tl_i64(fpscr, cpu_fpscr);
554    tcg_gen_andi_i64(fpscr_masked, fpscr, mask);
555    set_fpr(rt, fpscr_masked);
556
557    return fpscr;
558}
559
560static void store_fpscr_masked(TCGv_i64 fpscr, uint64_t clear_mask,
561                               TCGv_i64 set_mask, uint32_t store_mask)
562{
563    TCGv_i64 fpscr_masked = tcg_temp_new_i64();
564    TCGv_i32 st_mask = tcg_constant_i32(store_mask);
565
566    tcg_gen_andi_i64(fpscr_masked, fpscr, ~clear_mask);
567    tcg_gen_or_i64(fpscr_masked, fpscr_masked, set_mask);
568    gen_helper_store_fpscr(tcg_env, fpscr_masked, st_mask);
569}
570
571static bool trans_MFFS_ISA207(DisasContext *ctx, arg_X_t_rc *a)
572{
573    if (!(ctx->insns_flags2 & PPC2_ISA300)) {
574        /*
575         * Before Power ISA v3.0, MFFS bits 11~15 were reserved, any instruction
576         * with OPCD=63 and XO=583 should be decoded as MFFS.
577         */
578        return trans_MFFS(ctx, a);
579    }
580    /*
581     * For Power ISA v3.0+, return false and let the pattern group
582     * select the correct instruction.
583     */
584    return false;
585}
586
587static bool trans_MFFS(DisasContext *ctx, arg_X_t_rc *a)
588{
589    REQUIRE_FPU(ctx);
590
591    gen_reset_fpstatus();
592    place_from_fpscr(a->rt, UINT64_MAX);
593    if (a->rc) {
594        gen_set_cr1_from_fpscr(ctx);
595    }
596    return true;
597}
598
599static bool trans_MFFSCE(DisasContext *ctx, arg_X_t *a)
600{
601    TCGv_i64 fpscr;
602
603    REQUIRE_FPU(ctx);
604
605    gen_reset_fpstatus();
606    fpscr = place_from_fpscr(a->rt, UINT64_MAX);
607    store_fpscr_masked(fpscr, FP_ENABLES, tcg_constant_i64(0), 0x0003);
608    return true;
609}
610
611static bool trans_MFFSCRN(DisasContext *ctx, arg_X_tb *a)
612{
613    TCGv_i64 t1, fpscr;
614
615    REQUIRE_FPU(ctx);
616
617    t1 = tcg_temp_new_i64();
618    get_fpr(t1, a->rb);
619    tcg_gen_andi_i64(t1, t1, FP_RN);
620
621    gen_reset_fpstatus();
622    fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN);
623    store_fpscr_masked(fpscr, FP_RN, t1, 0x0001);
624    return true;
625}
626
627static bool trans_MFFSCDRN(DisasContext *ctx, arg_X_tb *a)
628{
629    TCGv_i64 t1, fpscr;
630
631    REQUIRE_FPU(ctx);
632
633    t1 = tcg_temp_new_i64();
634    get_fpr(t1, a->rb);
635    tcg_gen_andi_i64(t1, t1, FP_DRN);
636
637    gen_reset_fpstatus();
638    fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN);
639    store_fpscr_masked(fpscr, FP_DRN, t1, 0x0100);
640    return true;
641}
642
643static bool trans_MFFSCRNI(DisasContext *ctx, arg_X_imm2 *a)
644{
645    TCGv_i64 t1, fpscr;
646
647    REQUIRE_FPU(ctx);
648
649    t1 = tcg_temp_new_i64();
650    tcg_gen_movi_i64(t1, a->imm);
651
652    gen_reset_fpstatus();
653    fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN);
654    store_fpscr_masked(fpscr, FP_RN, t1, 0x0001);
655    return true;
656}
657
658static bool trans_MFFSCDRNI(DisasContext *ctx, arg_X_imm3 *a)
659{
660    TCGv_i64 t1, fpscr;
661
662    REQUIRE_FPU(ctx);
663
664    t1 = tcg_temp_new_i64();
665    tcg_gen_movi_i64(t1, (uint64_t)a->imm << FPSCR_DRN0);
666
667    gen_reset_fpstatus();
668    fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN);
669    store_fpscr_masked(fpscr, FP_DRN, t1, 0x0100);
670    return true;
671}
672
673static bool trans_MFFSL(DisasContext *ctx, arg_X_t *a)
674{
675    REQUIRE_FPU(ctx);
676
677    gen_reset_fpstatus();
678    place_from_fpscr(a->rt, FP_DRN | FP_STATUS | FP_ENABLES | FP_NI | FP_RN);
679    return true;
680}
681
682/* mtfsb0 */
683static void gen_mtfsb0(DisasContext *ctx)
684{
685    uint8_t crb;
686
687    if (unlikely(!ctx->fpu_enabled)) {
688        gen_exception(ctx, POWERPC_EXCP_FPU);
689        return;
690    }
691    crb = 31 - crbD(ctx->opcode);
692    gen_reset_fpstatus();
693    if (likely(crb != FPSCR_FEX && crb != FPSCR_VX)) {
694        gen_helper_fpscr_clrbit(tcg_env, tcg_constant_i32(crb));
695    }
696    if (unlikely(Rc(ctx->opcode) != 0)) {
697        tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr);
698        tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
699    }
700}
701
702/* mtfsb1 */
703static void gen_mtfsb1(DisasContext *ctx)
704{
705    uint8_t crb;
706
707    if (unlikely(!ctx->fpu_enabled)) {
708        gen_exception(ctx, POWERPC_EXCP_FPU);
709        return;
710    }
711    crb = 31 - crbD(ctx->opcode);
712    /* XXX: we pretend we can only do IEEE floating-point computations */
713    if (likely(crb != FPSCR_FEX && crb != FPSCR_VX && crb != FPSCR_NI)) {
714        gen_helper_fpscr_setbit(tcg_env, tcg_constant_i32(crb));
715    }
716    if (unlikely(Rc(ctx->opcode) != 0)) {
717        tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr);
718        tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
719    }
720    /* We can raise a deferred exception */
721    gen_helper_fpscr_check_status(tcg_env);
722}
723
724/* mtfsf */
725static void gen_mtfsf(DisasContext *ctx)
726{
727    TCGv_i32 t0;
728    TCGv_i64 t1;
729    int flm, l, w;
730
731    if (unlikely(!ctx->fpu_enabled)) {
732        gen_exception(ctx, POWERPC_EXCP_FPU);
733        return;
734    }
735    flm = FPFLM(ctx->opcode);
736    l = FPL(ctx->opcode);
737    w = FPW(ctx->opcode);
738    if (unlikely(w & !(ctx->insns_flags2 & PPC2_ISA205))) {
739        gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
740        return;
741    }
742    if (!l) {
743        t0 = tcg_constant_i32(flm << (w * 8));
744    } else if (ctx->insns_flags2 & PPC2_ISA205) {
745        t0 = tcg_constant_i32(0xffff);
746    } else {
747        t0 = tcg_constant_i32(0xff);
748    }
749    t1 = tcg_temp_new_i64();
750    get_fpr(t1, rB(ctx->opcode));
751    gen_helper_store_fpscr(tcg_env, t1, t0);
752    if (unlikely(Rc(ctx->opcode) != 0)) {
753        tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr);
754        tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
755    }
756    /* We can raise a deferred exception */
757    gen_helper_fpscr_check_status(tcg_env);
758}
759
760/* mtfsfi */
761static void gen_mtfsfi(DisasContext *ctx)
762{
763    int bf, sh, w;
764    TCGv_i64 t0;
765    TCGv_i32 t1;
766
767    if (unlikely(!ctx->fpu_enabled)) {
768        gen_exception(ctx, POWERPC_EXCP_FPU);
769        return;
770    }
771    w = FPW(ctx->opcode);
772    bf = FPBF(ctx->opcode);
773    if (unlikely(w & !(ctx->insns_flags2 & PPC2_ISA205))) {
774        gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
775        return;
776    }
777    sh = (8 * w) + 7 - bf;
778    t0 = tcg_constant_i64(((uint64_t)FPIMM(ctx->opcode)) << (4 * sh));
779    t1 = tcg_constant_i32(1 << sh);
780    gen_helper_store_fpscr(tcg_env, t0, t1);
781    if (unlikely(Rc(ctx->opcode) != 0)) {
782        tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr);
783        tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
784    }
785    /* We can raise a deferred exception */
786    gen_helper_fpscr_check_status(tcg_env);
787}
788
789static void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 dest, TCGv addr)
790{
791    TCGv_i32 tmp = tcg_temp_new_i32();
792    tcg_gen_qemu_ld_i32(tmp, addr, ctx->mem_idx, DEF_MEMOP(MO_UL));
793    gen_helper_todouble(dest, tmp);
794}
795
796/* lfdepx (external PID lfdx) */
797static void gen_lfdepx(DisasContext *ctx)
798{
799    TCGv EA;
800    TCGv_i64 t0;
801    CHK_SV(ctx);
802    if (unlikely(!ctx->fpu_enabled)) {
803        gen_exception(ctx, POWERPC_EXCP_FPU);
804        return;
805    }
806    gen_set_access_type(ctx, ACCESS_FLOAT);
807    EA = tcg_temp_new();
808    t0 = tcg_temp_new_i64();
809    gen_addr_reg_index(ctx, EA);
810    tcg_gen_qemu_ld_i64(t0, EA, PPC_TLB_EPID_LOAD, DEF_MEMOP(MO_UQ));
811    set_fpr(rD(ctx->opcode), t0);
812}
813
814/* lfdp */
815static void gen_lfdp(DisasContext *ctx)
816{
817    TCGv EA;
818    TCGv_i64 t0;
819    if (unlikely(!ctx->fpu_enabled)) {
820        gen_exception(ctx, POWERPC_EXCP_FPU);
821        return;
822    }
823    gen_set_access_type(ctx, ACCESS_FLOAT);
824    EA = tcg_temp_new();
825    gen_addr_imm_index(ctx, EA, 0);
826    t0 = tcg_temp_new_i64();
827    /*
828     * We only need to swap high and low halves. gen_qemu_ld64_i64
829     * does necessary 64-bit byteswap already.
830     */
831    if (unlikely(ctx->le_mode)) {
832        gen_qemu_ld64_i64(ctx, t0, EA);
833        set_fpr(rD(ctx->opcode) + 1, t0);
834        tcg_gen_addi_tl(EA, EA, 8);
835        gen_qemu_ld64_i64(ctx, t0, EA);
836        set_fpr(rD(ctx->opcode), t0);
837    } else {
838        gen_qemu_ld64_i64(ctx, t0, EA);
839        set_fpr(rD(ctx->opcode), t0);
840        tcg_gen_addi_tl(EA, EA, 8);
841        gen_qemu_ld64_i64(ctx, t0, EA);
842        set_fpr(rD(ctx->opcode) + 1, t0);
843    }
844}
845
846/* lfdpx */
847static void gen_lfdpx(DisasContext *ctx)
848{
849    TCGv EA;
850    TCGv_i64 t0;
851    if (unlikely(!ctx->fpu_enabled)) {
852        gen_exception(ctx, POWERPC_EXCP_FPU);
853        return;
854    }
855    gen_set_access_type(ctx, ACCESS_FLOAT);
856    EA = tcg_temp_new();
857    gen_addr_reg_index(ctx, EA);
858    t0 = tcg_temp_new_i64();
859    /*
860     * We only need to swap high and low halves. gen_qemu_ld64_i64
861     * does necessary 64-bit byteswap already.
862     */
863    if (unlikely(ctx->le_mode)) {
864        gen_qemu_ld64_i64(ctx, t0, EA);
865        set_fpr(rD(ctx->opcode) + 1, t0);
866        tcg_gen_addi_tl(EA, EA, 8);
867        gen_qemu_ld64_i64(ctx, t0, EA);
868        set_fpr(rD(ctx->opcode), t0);
869    } else {
870        gen_qemu_ld64_i64(ctx, t0, EA);
871        set_fpr(rD(ctx->opcode), t0);
872        tcg_gen_addi_tl(EA, EA, 8);
873        gen_qemu_ld64_i64(ctx, t0, EA);
874        set_fpr(rD(ctx->opcode) + 1, t0);
875    }
876}
877
878/* lfiwax */
879static void gen_lfiwax(DisasContext *ctx)
880{
881    TCGv EA;
882    TCGv t0;
883    TCGv_i64 t1;
884    if (unlikely(!ctx->fpu_enabled)) {
885        gen_exception(ctx, POWERPC_EXCP_FPU);
886        return;
887    }
888    gen_set_access_type(ctx, ACCESS_FLOAT);
889    EA = tcg_temp_new();
890    t0 = tcg_temp_new();
891    t1 = tcg_temp_new_i64();
892    gen_addr_reg_index(ctx, EA);
893    gen_qemu_ld32s(ctx, t0, EA);
894    tcg_gen_ext_tl_i64(t1, t0);
895    set_fpr(rD(ctx->opcode), t1);
896}
897
898/* lfiwzx */
899static void gen_lfiwzx(DisasContext *ctx)
900{
901    TCGv EA;
902    TCGv_i64 t0;
903    if (unlikely(!ctx->fpu_enabled)) {
904        gen_exception(ctx, POWERPC_EXCP_FPU);
905        return;
906    }
907    gen_set_access_type(ctx, ACCESS_FLOAT);
908    EA = tcg_temp_new();
909    t0 = tcg_temp_new_i64();
910    gen_addr_reg_index(ctx, EA);
911    gen_qemu_ld32u_i64(ctx, t0, EA);
912    set_fpr(rD(ctx->opcode), t0);
913}
914
915#define GEN_STXF(name, stop, opc2, opc3, type)                                \
916static void glue(gen_, name##x)(DisasContext *ctx)                            \
917{                                                                             \
918    TCGv EA;                                                                  \
919    TCGv_i64 t0;                                                              \
920    if (unlikely(!ctx->fpu_enabled)) {                                        \
921        gen_exception(ctx, POWERPC_EXCP_FPU);                                 \
922        return;                                                               \
923    }                                                                         \
924    gen_set_access_type(ctx, ACCESS_FLOAT);                                   \
925    EA = tcg_temp_new();                                                      \
926    t0 = tcg_temp_new_i64();                                                  \
927    gen_addr_reg_index(ctx, EA);                                              \
928    get_fpr(t0, rS(ctx->opcode));                                             \
929    gen_qemu_##stop(ctx, t0, EA);                                             \
930}
931
932static void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 src, TCGv addr)
933{
934    TCGv_i32 tmp = tcg_temp_new_i32();
935    gen_helper_tosingle(tmp, src);
936    tcg_gen_qemu_st_i32(tmp, addr, ctx->mem_idx, DEF_MEMOP(MO_UL));
937}
938
939/* stfdepx (external PID lfdx) */
940static void gen_stfdepx(DisasContext *ctx)
941{
942    TCGv EA;
943    TCGv_i64 t0;
944    CHK_SV(ctx);
945    if (unlikely(!ctx->fpu_enabled)) {
946        gen_exception(ctx, POWERPC_EXCP_FPU);
947        return;
948    }
949    gen_set_access_type(ctx, ACCESS_FLOAT);
950    EA = tcg_temp_new();
951    t0 = tcg_temp_new_i64();
952    gen_addr_reg_index(ctx, EA);
953    get_fpr(t0, rD(ctx->opcode));
954    tcg_gen_qemu_st_i64(t0, EA, PPC_TLB_EPID_STORE, DEF_MEMOP(MO_UQ));
955}
956
957/* stfdp */
958static void gen_stfdp(DisasContext *ctx)
959{
960    TCGv EA;
961    TCGv_i64 t0;
962    if (unlikely(!ctx->fpu_enabled)) {
963        gen_exception(ctx, POWERPC_EXCP_FPU);
964        return;
965    }
966    gen_set_access_type(ctx, ACCESS_FLOAT);
967    EA = tcg_temp_new();
968    t0 = tcg_temp_new_i64();
969    gen_addr_imm_index(ctx, EA, 0);
970    /*
971     * We only need to swap high and low halves. gen_qemu_st64_i64
972     * does necessary 64-bit byteswap already.
973     */
974    if (unlikely(ctx->le_mode)) {
975        get_fpr(t0, rD(ctx->opcode) + 1);
976        gen_qemu_st64_i64(ctx, t0, EA);
977        tcg_gen_addi_tl(EA, EA, 8);
978        get_fpr(t0, rD(ctx->opcode));
979        gen_qemu_st64_i64(ctx, t0, EA);
980    } else {
981        get_fpr(t0, rD(ctx->opcode));
982        gen_qemu_st64_i64(ctx, t0, EA);
983        tcg_gen_addi_tl(EA, EA, 8);
984        get_fpr(t0, rD(ctx->opcode) + 1);
985        gen_qemu_st64_i64(ctx, t0, EA);
986    }
987}
988
989/* stfdpx */
990static void gen_stfdpx(DisasContext *ctx)
991{
992    TCGv EA;
993    TCGv_i64 t0;
994    if (unlikely(!ctx->fpu_enabled)) {
995        gen_exception(ctx, POWERPC_EXCP_FPU);
996        return;
997    }
998    gen_set_access_type(ctx, ACCESS_FLOAT);
999    EA = tcg_temp_new();
1000    t0 = tcg_temp_new_i64();
1001    gen_addr_reg_index(ctx, EA);
1002    /*
1003     * We only need to swap high and low halves. gen_qemu_st64_i64
1004     * does necessary 64-bit byteswap already.
1005     */
1006    if (unlikely(ctx->le_mode)) {
1007        get_fpr(t0, rD(ctx->opcode) + 1);
1008        gen_qemu_st64_i64(ctx, t0, EA);
1009        tcg_gen_addi_tl(EA, EA, 8);
1010        get_fpr(t0, rD(ctx->opcode));
1011        gen_qemu_st64_i64(ctx, t0, EA);
1012    } else {
1013        get_fpr(t0, rD(ctx->opcode));
1014        gen_qemu_st64_i64(ctx, t0, EA);
1015        tcg_gen_addi_tl(EA, EA, 8);
1016        get_fpr(t0, rD(ctx->opcode) + 1);
1017        gen_qemu_st64_i64(ctx, t0, EA);
1018    }
1019}
1020
1021/* Optional: */
1022static inline void gen_qemu_st32fiw(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2)
1023{
1024    TCGv t0 = tcg_temp_new();
1025    tcg_gen_trunc_i64_tl(t0, arg1),
1026    gen_qemu_st32(ctx, t0, arg2);
1027}
1028/* stfiwx */
1029GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX);
1030
1031/*            Floating-point Load/Store Instructions                         */
1032static bool do_lsfpsd(DisasContext *ctx, int rt, int ra, TCGv displ,
1033                      bool update, bool store, bool single)
1034{
1035    TCGv ea;
1036    TCGv_i64 t0;
1037    REQUIRE_INSNS_FLAGS(ctx, FLOAT);
1038    REQUIRE_FPU(ctx);
1039    if (update && ra == 0) {
1040        gen_invalid(ctx);
1041        return true;
1042    }
1043    gen_set_access_type(ctx, ACCESS_FLOAT);
1044    t0 = tcg_temp_new_i64();
1045    ea = do_ea_calc(ctx, ra, displ);
1046    if (store) {
1047        get_fpr(t0, rt);
1048        if (single) {
1049            gen_qemu_st32fs(ctx, t0, ea);
1050        } else {
1051            gen_qemu_st64_i64(ctx, t0, ea);
1052        }
1053    } else {
1054        if (single) {
1055            gen_qemu_ld32fs(ctx, t0, ea);
1056        } else {
1057            gen_qemu_ld64_i64(ctx, t0, ea);
1058        }
1059        set_fpr(rt, t0);
1060    }
1061    if (update) {
1062        tcg_gen_mov_tl(cpu_gpr[ra], ea);
1063    }
1064    return true;
1065}
1066
1067static bool do_lsfp_D(DisasContext *ctx, arg_D *a, bool update, bool store,
1068                      bool single)
1069{
1070    return do_lsfpsd(ctx, a->rt, a->ra, tcg_constant_tl(a->si), update, store,
1071                     single);
1072}
1073
1074static bool do_lsfp_PLS_D(DisasContext *ctx, arg_PLS_D *a, bool update,
1075                          bool store, bool single)
1076{
1077    arg_D d;
1078    if (!resolve_PLS_D(ctx, &d, a)) {
1079        return true;
1080    }
1081    return do_lsfp_D(ctx, &d, update, store, single);
1082}
1083
1084static bool do_lsfp_X(DisasContext *ctx, arg_X *a, bool update,
1085                      bool store, bool single)
1086{
1087    return do_lsfpsd(ctx, a->rt, a->ra, cpu_gpr[a->rb], update, store, single);
1088}
1089
1090TRANS(LFS, do_lsfp_D, false, false, true)
1091TRANS(LFSU, do_lsfp_D, true, false, true)
1092TRANS(LFSX, do_lsfp_X, false, false, true)
1093TRANS(LFSUX, do_lsfp_X, true, false, true)
1094TRANS(PLFS, do_lsfp_PLS_D, false, false, true)
1095
1096TRANS(LFD, do_lsfp_D, false, false, false)
1097TRANS(LFDU, do_lsfp_D, true, false, false)
1098TRANS(LFDX, do_lsfp_X, false, false, false)
1099TRANS(LFDUX, do_lsfp_X, true, false, false)
1100TRANS(PLFD, do_lsfp_PLS_D, false, false, false)
1101
1102TRANS(STFS, do_lsfp_D, false, true, true)
1103TRANS(STFSU, do_lsfp_D, true, true, true)
1104TRANS(STFSX, do_lsfp_X, false, true, true)
1105TRANS(STFSUX, do_lsfp_X, true, true, true)
1106TRANS(PSTFS, do_lsfp_PLS_D, false, true, true)
1107
1108TRANS(STFD, do_lsfp_D, false, true, false)
1109TRANS(STFDU, do_lsfp_D, true, true, false)
1110TRANS(STFDX, do_lsfp_X, false, true, false)
1111TRANS(STFDUX, do_lsfp_X, true, true, false)
1112TRANS(PSTFD, do_lsfp_PLS_D, false, true, false)
1113
1114#undef _GEN_FLOAT_ACB
1115#undef GEN_FLOAT_ACB
1116#undef _GEN_FLOAT_AB
1117#undef GEN_FLOAT_AB
1118#undef _GEN_FLOAT_AC
1119#undef GEN_FLOAT_AC
1120#undef GEN_FLOAT_B
1121#undef GEN_FLOAT_BS
1122
1123#undef GEN_LDF
1124#undef GEN_LDUF
1125#undef GEN_LDUXF
1126#undef GEN_LDXF
1127#undef GEN_LDFS
1128
1129#undef GEN_STF
1130#undef GEN_STUF
1131#undef GEN_STUXF
1132#undef GEN_STXF
1133#undef GEN_STFS
1134