1/*
2 * Power ISA decode for Fixed-Point Facility instructions
3 *
4 * Copyright (c) 2021 Instituto de Pesquisas Eldorado (eldorado.org.br)
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.1 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/*
21 * Incorporate CIA into the constant when R=1.
22 * Validate that when R=1, RA=0.
23 */
24static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a)
25{
26    d->rt = a->rt;
27    d->ra = a->ra;
28    d->si = a->si;
29    if (a->r) {
30        if (unlikely(a->ra != 0)) {
31            gen_invalid(ctx);
32            return false;
33        }
34        d->si += ctx->cia;
35    }
36    return true;
37}
38
39/*
40 * Fixed-Point Load/Store Instructions
41 */
42
43static bool do_ldst(DisasContext *ctx, int rt, int ra, TCGv displ, bool update,
44                    bool store, MemOp mop)
45{
46    TCGv ea;
47
48    if (update && (ra == 0 || (!store && ra == rt))) {
49        gen_invalid(ctx);
50        return true;
51    }
52    gen_set_access_type(ctx, ACCESS_INT);
53
54    ea = tcg_temp_new();
55    if (ra) {
56        tcg_gen_add_tl(ea, cpu_gpr[ra], displ);
57    } else {
58        tcg_gen_mov_tl(ea, displ);
59    }
60    if (NARROW_MODE(ctx)) {
61        tcg_gen_ext32u_tl(ea, ea);
62    }
63    mop ^= ctx->default_tcg_memop_mask;
64    if (store) {
65        tcg_gen_qemu_st_tl(cpu_gpr[rt], ea, ctx->mem_idx, mop);
66    } else {
67        tcg_gen_qemu_ld_tl(cpu_gpr[rt], ea, ctx->mem_idx, mop);
68    }
69    if (update) {
70        tcg_gen_mov_tl(cpu_gpr[ra], ea);
71    }
72    tcg_temp_free(ea);
73
74    return true;
75}
76
77static bool do_ldst_D(DisasContext *ctx, arg_D *a, bool update, bool store,
78                      MemOp mop)
79{
80    return do_ldst(ctx, a->rt, a->ra, tcg_constant_tl(a->si), update, store, mop);
81}
82
83static bool do_ldst_PLS_D(DisasContext *ctx, arg_PLS_D *a, bool update,
84                          bool store, MemOp mop)
85{
86    arg_D d;
87    if (!resolve_PLS_D(ctx, &d, a)) {
88        return true;
89    }
90    return do_ldst_D(ctx, &d, update, store, mop);
91}
92
93static bool do_ldst_X(DisasContext *ctx, arg_X *a, bool update,
94                      bool store, MemOp mop)
95{
96    return do_ldst(ctx, a->rt, a->ra, cpu_gpr[a->rb], update, store, mop);
97}
98
99/* Load Byte and Zero */
100TRANS(LBZ, do_ldst_D, false, false, MO_UB)
101TRANS(LBZX, do_ldst_X, false, false, MO_UB)
102TRANS(LBZU, do_ldst_D, true, false, MO_UB)
103TRANS(LBZUX, do_ldst_X, true, false, MO_UB)
104TRANS(PLBZ, do_ldst_PLS_D, false, false, MO_UB)
105
106/* Load Halfword and Zero */
107TRANS(LHZ, do_ldst_D, false, false, MO_UW)
108TRANS(LHZX, do_ldst_X, false, false, MO_UW)
109TRANS(LHZU, do_ldst_D, true, false, MO_UW)
110TRANS(LHZUX, do_ldst_X, true, false, MO_UW)
111TRANS(PLHZ, do_ldst_PLS_D, false, false, MO_UW)
112
113/* Load Halfword Algebraic */
114TRANS(LHA, do_ldst_D, false, false, MO_SW)
115TRANS(LHAX, do_ldst_X, false, false, MO_SW)
116TRANS(LHAU, do_ldst_D, true, false, MO_SW)
117TRANS(LHAXU, do_ldst_X, true, false, MO_SW)
118TRANS(PLHA, do_ldst_PLS_D, false, false, MO_SW)
119
120/* Load Word and Zero */
121TRANS(LWZ, do_ldst_D, false, false, MO_UL)
122TRANS(LWZX, do_ldst_X, false, false, MO_UL)
123TRANS(LWZU, do_ldst_D, true, false, MO_UL)
124TRANS(LWZUX, do_ldst_X, true, false, MO_UL)
125TRANS(PLWZ, do_ldst_PLS_D, false, false, MO_UL)
126
127/* Load Word Algebraic */
128TRANS64(LWA, do_ldst_D, false, false, MO_SL)
129TRANS64(LWAX, do_ldst_X, false, false, MO_SL)
130TRANS64(LWAUX, do_ldst_X, true, false, MO_SL)
131TRANS64(PLWA, do_ldst_PLS_D, false, false, MO_SL)
132
133/* Load Doubleword */
134TRANS64(LD, do_ldst_D, false, false, MO_Q)
135TRANS64(LDX, do_ldst_X, false, false, MO_Q)
136TRANS64(LDU, do_ldst_D, true, false, MO_Q)
137TRANS64(LDUX, do_ldst_X, true, false, MO_Q)
138TRANS64(PLD, do_ldst_PLS_D, false, false, MO_Q)
139
140/* Store Byte */
141TRANS(STB, do_ldst_D, false, true, MO_UB)
142TRANS(STBX, do_ldst_X, false, true, MO_UB)
143TRANS(STBU, do_ldst_D, true, true, MO_UB)
144TRANS(STBUX, do_ldst_X, true, true, MO_UB)
145TRANS(PSTB, do_ldst_PLS_D, false, true, MO_UB)
146
147/* Store Halfword */
148TRANS(STH, do_ldst_D, false, true, MO_UW)
149TRANS(STHX, do_ldst_X, false, true, MO_UW)
150TRANS(STHU, do_ldst_D, true, true, MO_UW)
151TRANS(STHUX, do_ldst_X, true, true, MO_UW)
152TRANS(PSTH, do_ldst_PLS_D, false, true, MO_UW)
153
154/* Store Word */
155TRANS(STW, do_ldst_D, false, true, MO_UL)
156TRANS(STWX, do_ldst_X, false, true, MO_UL)
157TRANS(STWU, do_ldst_D, true, true, MO_UL)
158TRANS(STWUX, do_ldst_X, true, true, MO_UL)
159TRANS(PSTW, do_ldst_PLS_D, false, true, MO_UL)
160
161/* Store Doubleword */
162TRANS64(STD, do_ldst_D, false, true, MO_Q)
163TRANS64(STDX, do_ldst_X, false, true, MO_Q)
164TRANS64(STDU, do_ldst_D, true, true, MO_Q)
165TRANS64(STDUX, do_ldst_X, true, true, MO_Q)
166TRANS64(PSTD, do_ldst_PLS_D, false, true, MO_Q)
167
168/*
169 * Fixed-Point Compare Instructions
170 */
171
172static bool do_cmp_X(DisasContext *ctx, arg_X_bfl *a, bool s)
173{
174    if ((ctx->insns_flags & PPC_64B) == 0) {
175        /*
176         * For 32-bit implementations, The Programming Environments Manual says
177         * that "the L field must be cleared, otherwise the instruction form is
178         * invalid." It seems, however, that most 32-bit CPUs ignore invalid
179         * forms (e.g., section "Instruction Formats" of the 405 and 440
180         * manuals, "Integer Compare Instructions" of the 601 manual), with the
181         * notable exception of the e500 and e500mc, where L=1 was reported to
182         * cause an exception.
183         */
184        if (a->l) {
185            if ((ctx->insns_flags2 & PPC2_BOOKE206)) {
186                /*
187                 * For 32-bit Book E v2.06 implementations (i.e. e500/e500mc),
188                 * generate an illegal instruction exception.
189                 */
190                return false;
191            } else {
192                qemu_log_mask(LOG_GUEST_ERROR,
193                        "Invalid form of CMP%s at 0x" TARGET_FMT_lx ", L = 1\n",
194                        s ? "" : "L", ctx->cia);
195            }
196        }
197        gen_op_cmp32(cpu_gpr[a->ra], cpu_gpr[a->rb], s, a->bf);
198        return true;
199    }
200
201    /* For 64-bit implementations, deal with bit L accordingly. */
202    if (a->l) {
203        gen_op_cmp(cpu_gpr[a->ra], cpu_gpr[a->rb], s, a->bf);
204    } else {
205        gen_op_cmp32(cpu_gpr[a->ra], cpu_gpr[a->rb], s, a->bf);
206    }
207    return true;
208}
209
210static bool do_cmp_D(DisasContext *ctx, arg_D_bf *a, bool s)
211{
212    if ((ctx->insns_flags & PPC_64B) == 0) {
213        /*
214         * For 32-bit implementations, The Programming Environments Manual says
215         * that "the L field must be cleared, otherwise the instruction form is
216         * invalid." It seems, however, that most 32-bit CPUs ignore invalid
217         * forms (e.g., section "Instruction Formats" of the 405 and 440
218         * manuals, "Integer Compare Instructions" of the 601 manual), with the
219         * notable exception of the e500 and e500mc, where L=1 was reported to
220         * cause an exception.
221         */
222        if (a->l) {
223            if ((ctx->insns_flags2 & PPC2_BOOKE206)) {
224                /*
225                 * For 32-bit Book E v2.06 implementations (i.e. e500/e500mc),
226                 * generate an illegal instruction exception.
227                 */
228                return false;
229            } else {
230                qemu_log_mask(LOG_GUEST_ERROR,
231                        "Invalid form of CMP%s at 0x" TARGET_FMT_lx ", L = 1\n",
232                        s ? "I" : "LI", ctx->cia);
233            }
234        }
235        gen_op_cmp32(cpu_gpr[a->ra], tcg_constant_tl(a->imm), s, a->bf);
236        return true;
237    }
238
239    /* For 64-bit implementations, deal with bit L accordingly. */
240    if (a->l) {
241        gen_op_cmp(cpu_gpr[a->ra], tcg_constant_tl(a->imm), s, a->bf);
242    } else {
243        gen_op_cmp32(cpu_gpr[a->ra], tcg_constant_tl(a->imm), s, a->bf);
244    }
245    return true;
246}
247
248TRANS(CMP, do_cmp_X, true);
249TRANS(CMPL, do_cmp_X, false);
250TRANS(CMPI, do_cmp_D, true);
251TRANS(CMPLI, do_cmp_D, false);
252
253/*
254 * Fixed-Point Arithmetic Instructions
255 */
256
257static bool trans_ADDI(DisasContext *ctx, arg_D *a)
258{
259    if (a->ra) {
260        tcg_gen_addi_tl(cpu_gpr[a->rt], cpu_gpr[a->ra], a->si);
261    } else {
262        tcg_gen_movi_tl(cpu_gpr[a->rt], a->si);
263    }
264    return true;
265}
266
267static bool trans_PADDI(DisasContext *ctx, arg_PLS_D *a)
268{
269    arg_D d;
270    if (!resolve_PLS_D(ctx, &d, a)) {
271        return true;
272    }
273    return trans_ADDI(ctx, &d);
274}
275
276static bool trans_ADDIS(DisasContext *ctx, arg_D *a)
277{
278    a->si <<= 16;
279    return trans_ADDI(ctx, a);
280}
281
282static bool trans_ADDPCIS(DisasContext *ctx, arg_DX *a)
283{
284    REQUIRE_INSNS_FLAGS2(ctx, ISA300);
285    tcg_gen_movi_tl(cpu_gpr[a->rt], ctx->base.pc_next + (a->d << 16));
286    return true;
287}
288
289static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a)
290{
291    gen_invalid(ctx);
292    return true;
293}
294
295static bool trans_PNOP(DisasContext *ctx, arg_PNOP *a)
296{
297    return true;
298}
299
300static bool do_set_bool_cond(DisasContext *ctx, arg_X_bi *a, bool neg, bool rev)
301{
302    REQUIRE_INSNS_FLAGS2(ctx, ISA310);
303    uint32_t mask = 0x08 >> (a->bi & 0x03);
304    TCGCond cond = rev ? TCG_COND_EQ : TCG_COND_NE;
305    TCGv temp = tcg_temp_new();
306
307    tcg_gen_extu_i32_tl(temp, cpu_crf[a->bi >> 2]);
308    tcg_gen_andi_tl(temp, temp, mask);
309    tcg_gen_setcondi_tl(cond, cpu_gpr[a->rt], temp, 0);
310    if (neg) {
311        tcg_gen_neg_tl(cpu_gpr[a->rt], cpu_gpr[a->rt]);
312    }
313    tcg_temp_free(temp);
314
315    return true;
316}
317
318TRANS(SETBC, do_set_bool_cond, false, false)
319TRANS(SETBCR, do_set_bool_cond, false, true)
320TRANS(SETNBC, do_set_bool_cond, true, false)
321TRANS(SETNBCR, do_set_bool_cond, true, true)
322
323static bool trans_CFUGED(DisasContext *ctx, arg_X *a)
324{
325    REQUIRE_64BIT(ctx);
326    REQUIRE_INSNS_FLAGS2(ctx, ISA310);
327#if defined(TARGET_PPC64)
328    gen_helper_cfuged(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]);
329#else
330    qemu_build_not_reached();
331#endif
332    return true;
333}
334