2021-06-01 22:35:17 +03:00
|
|
|
/*
|
|
|
|
* Power ISA decode for Fixed-Point Facility instructions
|
|
|
|
*
|
|
|
|
* Copyright (c) 2021 Instituto de Pesquisas Eldorado (eldorado.org.br)
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2021-06-01 22:35:18 +03:00
|
|
|
|
2021-06-01 22:35:20 +03:00
|
|
|
/*
|
|
|
|
* Fixed-Point Load/Store Instructions
|
|
|
|
*/
|
|
|
|
|
|
|
|
static bool do_ldst(DisasContext *ctx, int rt, int ra, TCGv displ, bool update,
|
|
|
|
bool store, MemOp mop)
|
|
|
|
{
|
|
|
|
TCGv ea;
|
|
|
|
|
|
|
|
if (update && (ra == 0 || (!store && ra == rt))) {
|
|
|
|
gen_invalid(ctx);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
gen_set_access_type(ctx, ACCESS_INT);
|
|
|
|
|
2021-10-29 23:23:51 +03:00
|
|
|
ea = do_ea_calc(ctx, ra, displ);
|
2021-06-01 22:35:20 +03:00
|
|
|
mop ^= ctx->default_tcg_memop_mask;
|
|
|
|
if (store) {
|
|
|
|
tcg_gen_qemu_st_tl(cpu_gpr[rt], ea, ctx->mem_idx, mop);
|
|
|
|
} else {
|
|
|
|
tcg_gen_qemu_ld_tl(cpu_gpr[rt], ea, ctx->mem_idx, mop);
|
|
|
|
}
|
|
|
|
if (update) {
|
|
|
|
tcg_gen_mov_tl(cpu_gpr[ra], ea);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool do_ldst_D(DisasContext *ctx, arg_D *a, bool update, bool store,
|
|
|
|
MemOp mop)
|
|
|
|
{
|
|
|
|
return do_ldst(ctx, a->rt, a->ra, tcg_constant_tl(a->si), update, store, mop);
|
|
|
|
}
|
|
|
|
|
2021-06-01 22:35:21 +03:00
|
|
|
static bool do_ldst_PLS_D(DisasContext *ctx, arg_PLS_D *a, bool update,
|
|
|
|
bool store, MemOp mop)
|
|
|
|
{
|
|
|
|
arg_D d;
|
|
|
|
if (!resolve_PLS_D(ctx, &d, a)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return do_ldst_D(ctx, &d, update, store, mop);
|
|
|
|
}
|
|
|
|
|
2021-06-01 22:35:20 +03:00
|
|
|
static bool do_ldst_X(DisasContext *ctx, arg_X *a, bool update,
|
|
|
|
bool store, MemOp mop)
|
|
|
|
{
|
|
|
|
return do_ldst(ctx, a->rt, a->ra, cpu_gpr[a->rb], update, store, mop);
|
|
|
|
}
|
|
|
|
|
2021-10-29 23:23:55 +03:00
|
|
|
static bool do_ldst_quad(DisasContext *ctx, arg_D *a, bool store, bool prefixed)
|
|
|
|
{
|
|
|
|
#if defined(TARGET_PPC64)
|
|
|
|
TCGv ea;
|
2023-08-21 18:30:51 +03:00
|
|
|
TCGv_i64 lo, hi;
|
2023-05-20 01:22:12 +03:00
|
|
|
TCGv_i128 t16;
|
2021-10-29 23:23:55 +03:00
|
|
|
|
|
|
|
REQUIRE_INSNS_FLAGS(ctx, 64BX);
|
|
|
|
|
|
|
|
if (!prefixed && !(ctx->insns_flags2 & PPC2_LSQ_ISA207)) {
|
2022-07-01 16:34:58 +03:00
|
|
|
/* lq and stq were privileged prior to V. 2.07 */
|
|
|
|
REQUIRE_SV(ctx);
|
2021-10-29 23:23:55 +03:00
|
|
|
|
|
|
|
if (ctx->le_mode) {
|
|
|
|
gen_align_no_le(ctx);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!store && unlikely(a->ra == a->rt)) {
|
|
|
|
gen_invalid(ctx);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
gen_set_access_type(ctx, ACCESS_INT);
|
|
|
|
ea = do_ea_calc(ctx, a->ra, tcg_constant_tl(a->si));
|
|
|
|
|
2023-08-21 18:30:51 +03:00
|
|
|
if (ctx->le_mode && prefixed) {
|
|
|
|
lo = cpu_gpr[a->rt];
|
|
|
|
hi = cpu_gpr[a->rt + 1];
|
2021-10-29 23:23:55 +03:00
|
|
|
} else {
|
2023-08-21 18:30:51 +03:00
|
|
|
lo = cpu_gpr[a->rt + 1];
|
|
|
|
hi = cpu_gpr[a->rt];
|
2021-10-29 23:23:55 +03:00
|
|
|
}
|
2023-05-20 01:22:12 +03:00
|
|
|
t16 = tcg_temp_new_i128();
|
2021-10-29 23:23:55 +03:00
|
|
|
|
2023-05-20 01:22:12 +03:00
|
|
|
if (store) {
|
2023-08-21 18:30:51 +03:00
|
|
|
tcg_gen_concat_i64_i128(t16, lo, hi);
|
2023-05-20 01:22:12 +03:00
|
|
|
tcg_gen_qemu_st_i128(t16, ea, ctx->mem_idx, DEF_MEMOP(MO_128));
|
2021-10-29 23:23:55 +03:00
|
|
|
} else {
|
2023-05-20 01:22:12 +03:00
|
|
|
tcg_gen_qemu_ld_i128(t16, ea, ctx->mem_idx, DEF_MEMOP(MO_128));
|
2023-08-21 18:30:51 +03:00
|
|
|
tcg_gen_extr_i128_i64(lo, hi, t16);
|
2021-10-29 23:23:55 +03:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
qemu_build_not_reached();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-10-29 23:23:56 +03:00
|
|
|
static bool do_ldst_quad_PLS_D(DisasContext *ctx, arg_PLS_D *a, bool store)
|
|
|
|
{
|
|
|
|
arg_D d;
|
|
|
|
if (!resolve_PLS_D(ctx, &d, a)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return do_ldst_quad(ctx, &d, store, true);
|
|
|
|
}
|
|
|
|
|
2021-06-01 22:35:20 +03:00
|
|
|
/* Load Byte and Zero */
|
|
|
|
TRANS(LBZ, do_ldst_D, false, false, MO_UB)
|
|
|
|
TRANS(LBZX, do_ldst_X, false, false, MO_UB)
|
|
|
|
TRANS(LBZU, do_ldst_D, true, false, MO_UB)
|
|
|
|
TRANS(LBZUX, do_ldst_X, true, false, MO_UB)
|
2021-06-01 22:35:21 +03:00
|
|
|
TRANS(PLBZ, do_ldst_PLS_D, false, false, MO_UB)
|
2021-06-01 22:35:20 +03:00
|
|
|
|
|
|
|
/* Load Halfword and Zero */
|
|
|
|
TRANS(LHZ, do_ldst_D, false, false, MO_UW)
|
|
|
|
TRANS(LHZX, do_ldst_X, false, false, MO_UW)
|
|
|
|
TRANS(LHZU, do_ldst_D, true, false, MO_UW)
|
|
|
|
TRANS(LHZUX, do_ldst_X, true, false, MO_UW)
|
2021-06-01 22:35:21 +03:00
|
|
|
TRANS(PLHZ, do_ldst_PLS_D, false, false, MO_UW)
|
2021-06-01 22:35:20 +03:00
|
|
|
|
|
|
|
/* Load Halfword Algebraic */
|
|
|
|
TRANS(LHA, do_ldst_D, false, false, MO_SW)
|
|
|
|
TRANS(LHAX, do_ldst_X, false, false, MO_SW)
|
|
|
|
TRANS(LHAU, do_ldst_D, true, false, MO_SW)
|
|
|
|
TRANS(LHAXU, do_ldst_X, true, false, MO_SW)
|
2021-06-01 22:35:21 +03:00
|
|
|
TRANS(PLHA, do_ldst_PLS_D, false, false, MO_SW)
|
2021-06-01 22:35:20 +03:00
|
|
|
|
|
|
|
/* Load Word and Zero */
|
|
|
|
TRANS(LWZ, do_ldst_D, false, false, MO_UL)
|
|
|
|
TRANS(LWZX, do_ldst_X, false, false, MO_UL)
|
|
|
|
TRANS(LWZU, do_ldst_D, true, false, MO_UL)
|
|
|
|
TRANS(LWZUX, do_ldst_X, true, false, MO_UL)
|
2021-06-01 22:35:21 +03:00
|
|
|
TRANS(PLWZ, do_ldst_PLS_D, false, false, MO_UL)
|
2021-06-01 22:35:20 +03:00
|
|
|
|
|
|
|
/* Load Word Algebraic */
|
|
|
|
TRANS64(LWA, do_ldst_D, false, false, MO_SL)
|
|
|
|
TRANS64(LWAX, do_ldst_X, false, false, MO_SL)
|
|
|
|
TRANS64(LWAUX, do_ldst_X, true, false, MO_SL)
|
2021-06-01 22:35:21 +03:00
|
|
|
TRANS64(PLWA, do_ldst_PLS_D, false, false, MO_SL)
|
2021-06-01 22:35:20 +03:00
|
|
|
|
|
|
|
/* Load Doubleword */
|
2022-01-07 00:00:51 +03:00
|
|
|
TRANS64(LD, do_ldst_D, false, false, MO_UQ)
|
|
|
|
TRANS64(LDX, do_ldst_X, false, false, MO_UQ)
|
|
|
|
TRANS64(LDU, do_ldst_D, true, false, MO_UQ)
|
|
|
|
TRANS64(LDUX, do_ldst_X, true, false, MO_UQ)
|
|
|
|
TRANS64(PLD, do_ldst_PLS_D, false, false, MO_UQ)
|
2021-06-01 22:35:20 +03:00
|
|
|
|
2021-10-29 23:23:55 +03:00
|
|
|
/* Load Quadword */
|
|
|
|
TRANS64(LQ, do_ldst_quad, false, false);
|
2021-10-29 23:23:56 +03:00
|
|
|
TRANS64(PLQ, do_ldst_quad_PLS_D, false);
|
2021-10-29 23:23:55 +03:00
|
|
|
|
2021-06-01 22:35:22 +03:00
|
|
|
/* Store Byte */
|
|
|
|
TRANS(STB, do_ldst_D, false, true, MO_UB)
|
|
|
|
TRANS(STBX, do_ldst_X, false, true, MO_UB)
|
|
|
|
TRANS(STBU, do_ldst_D, true, true, MO_UB)
|
|
|
|
TRANS(STBUX, do_ldst_X, true, true, MO_UB)
|
2021-06-01 22:35:23 +03:00
|
|
|
TRANS(PSTB, do_ldst_PLS_D, false, true, MO_UB)
|
2021-06-01 22:35:22 +03:00
|
|
|
|
|
|
|
/* Store Halfword */
|
|
|
|
TRANS(STH, do_ldst_D, false, true, MO_UW)
|
|
|
|
TRANS(STHX, do_ldst_X, false, true, MO_UW)
|
|
|
|
TRANS(STHU, do_ldst_D, true, true, MO_UW)
|
|
|
|
TRANS(STHUX, do_ldst_X, true, true, MO_UW)
|
2021-06-01 22:35:23 +03:00
|
|
|
TRANS(PSTH, do_ldst_PLS_D, false, true, MO_UW)
|
2021-06-01 22:35:22 +03:00
|
|
|
|
|
|
|
/* Store Word */
|
|
|
|
TRANS(STW, do_ldst_D, false, true, MO_UL)
|
|
|
|
TRANS(STWX, do_ldst_X, false, true, MO_UL)
|
|
|
|
TRANS(STWU, do_ldst_D, true, true, MO_UL)
|
|
|
|
TRANS(STWUX, do_ldst_X, true, true, MO_UL)
|
2021-06-01 22:35:23 +03:00
|
|
|
TRANS(PSTW, do_ldst_PLS_D, false, true, MO_UL)
|
2021-06-01 22:35:22 +03:00
|
|
|
|
|
|
|
/* Store Doubleword */
|
2022-01-07 00:00:51 +03:00
|
|
|
TRANS64(STD, do_ldst_D, false, true, MO_UQ)
|
|
|
|
TRANS64(STDX, do_ldst_X, false, true, MO_UQ)
|
|
|
|
TRANS64(STDU, do_ldst_D, true, true, MO_UQ)
|
|
|
|
TRANS64(STDUX, do_ldst_X, true, true, MO_UQ)
|
|
|
|
TRANS64(PSTD, do_ldst_PLS_D, false, true, MO_UQ)
|
2021-06-01 22:35:22 +03:00
|
|
|
|
2021-10-29 23:23:55 +03:00
|
|
|
/* Store Quadword */
|
|
|
|
TRANS64(STQ, do_ldst_quad, true, false);
|
2021-10-29 23:23:56 +03:00
|
|
|
TRANS64(PSTQ, do_ldst_quad_PLS_D, true);
|
2021-10-29 23:23:55 +03:00
|
|
|
|
2021-06-01 22:35:28 +03:00
|
|
|
/*
|
|
|
|
* Fixed-Point Compare Instructions
|
|
|
|
*/
|
|
|
|
|
|
|
|
static bool do_cmp_X(DisasContext *ctx, arg_X_bfl *a, bool s)
|
|
|
|
{
|
2021-07-20 16:55:07 +03:00
|
|
|
if ((ctx->insns_flags & PPC_64B) == 0) {
|
|
|
|
/*
|
|
|
|
* For 32-bit implementations, The Programming Environments Manual says
|
|
|
|
* that "the L field must be cleared, otherwise the instruction form is
|
|
|
|
* invalid." It seems, however, that most 32-bit CPUs ignore invalid
|
|
|
|
* forms (e.g., section "Instruction Formats" of the 405 and 440
|
|
|
|
* manuals, "Integer Compare Instructions" of the 601 manual), with the
|
|
|
|
* notable exception of the e500 and e500mc, where L=1 was reported to
|
|
|
|
* cause an exception.
|
|
|
|
*/
|
|
|
|
if (a->l) {
|
|
|
|
if ((ctx->insns_flags2 & PPC2_BOOKE206)) {
|
|
|
|
/*
|
|
|
|
* For 32-bit Book E v2.06 implementations (i.e. e500/e500mc),
|
|
|
|
* generate an illegal instruction exception.
|
|
|
|
*/
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
|
"Invalid form of CMP%s at 0x" TARGET_FMT_lx ", L = 1\n",
|
|
|
|
s ? "" : "L", ctx->cia);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
gen_op_cmp32(cpu_gpr[a->ra], cpu_gpr[a->rb], s, a->bf);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For 64-bit implementations, deal with bit L accordingly. */
|
2021-06-01 22:35:28 +03:00
|
|
|
if (a->l) {
|
|
|
|
gen_op_cmp(cpu_gpr[a->ra], cpu_gpr[a->rb], s, a->bf);
|
|
|
|
} else {
|
|
|
|
gen_op_cmp32(cpu_gpr[a->ra], cpu_gpr[a->rb], s, a->bf);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool do_cmp_D(DisasContext *ctx, arg_D_bf *a, bool s)
|
|
|
|
{
|
2021-07-20 16:55:07 +03:00
|
|
|
if ((ctx->insns_flags & PPC_64B) == 0) {
|
|
|
|
/*
|
|
|
|
* For 32-bit implementations, The Programming Environments Manual says
|
|
|
|
* that "the L field must be cleared, otherwise the instruction form is
|
|
|
|
* invalid." It seems, however, that most 32-bit CPUs ignore invalid
|
|
|
|
* forms (e.g., section "Instruction Formats" of the 405 and 440
|
|
|
|
* manuals, "Integer Compare Instructions" of the 601 manual), with the
|
|
|
|
* notable exception of the e500 and e500mc, where L=1 was reported to
|
|
|
|
* cause an exception.
|
|
|
|
*/
|
|
|
|
if (a->l) {
|
|
|
|
if ((ctx->insns_flags2 & PPC2_BOOKE206)) {
|
|
|
|
/*
|
|
|
|
* For 32-bit Book E v2.06 implementations (i.e. e500/e500mc),
|
|
|
|
* generate an illegal instruction exception.
|
|
|
|
*/
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
|
|
"Invalid form of CMP%s at 0x" TARGET_FMT_lx ", L = 1\n",
|
|
|
|
s ? "I" : "LI", ctx->cia);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
gen_op_cmp32(cpu_gpr[a->ra], tcg_constant_tl(a->imm), s, a->bf);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For 64-bit implementations, deal with bit L accordingly. */
|
2021-06-01 22:35:28 +03:00
|
|
|
if (a->l) {
|
|
|
|
gen_op_cmp(cpu_gpr[a->ra], tcg_constant_tl(a->imm), s, a->bf);
|
|
|
|
} else {
|
|
|
|
gen_op_cmp32(cpu_gpr[a->ra], tcg_constant_tl(a->imm), s, a->bf);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRANS(CMP, do_cmp_X, true);
|
|
|
|
TRANS(CMPL, do_cmp_X, false);
|
|
|
|
TRANS(CMPI, do_cmp_D, true);
|
|
|
|
TRANS(CMPLI, do_cmp_D, false);
|
|
|
|
|
2021-06-01 22:35:20 +03:00
|
|
|
/*
|
|
|
|
* Fixed-Point Arithmetic Instructions
|
|
|
|
*/
|
|
|
|
|
2021-06-01 22:35:18 +03:00
|
|
|
static bool trans_ADDI(DisasContext *ctx, arg_D *a)
|
|
|
|
{
|
|
|
|
if (a->ra) {
|
|
|
|
tcg_gen_addi_tl(cpu_gpr[a->rt], cpu_gpr[a->ra], a->si);
|
|
|
|
} else {
|
|
|
|
tcg_gen_movi_tl(cpu_gpr[a->rt], a->si);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool trans_PADDI(DisasContext *ctx, arg_PLS_D *a)
|
|
|
|
{
|
|
|
|
arg_D d;
|
|
|
|
if (!resolve_PLS_D(ctx, &d, a)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return trans_ADDI(ctx, &d);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool trans_ADDIS(DisasContext *ctx, arg_D *a)
|
|
|
|
{
|
|
|
|
a->si <<= 16;
|
|
|
|
return trans_ADDI(ctx, a);
|
|
|
|
}
|
2021-06-01 22:35:19 +03:00
|
|
|
|
2021-06-01 22:35:27 +03:00
|
|
|
static bool trans_ADDPCIS(DisasContext *ctx, arg_DX *a)
|
|
|
|
{
|
|
|
|
REQUIRE_INSNS_FLAGS2(ctx, ISA300);
|
|
|
|
tcg_gen_movi_tl(cpu_gpr[a->rt], ctx->base.pc_next + (a->d << 16));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-02-14 12:40:27 +03:00
|
|
|
static bool trans_ADDEX(DisasContext *ctx, arg_X *a)
|
|
|
|
{
|
|
|
|
REQUIRE_INSNS_FLAGS2(ctx, ISA300);
|
|
|
|
gen_op_arith_add(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb],
|
|
|
|
cpu_ov, cpu_ov32, true, true, false, false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool do_add_D(DisasContext *ctx, arg_D *a, bool add_ca, bool compute_ca,
|
|
|
|
bool compute_ov, bool compute_rc0)
|
|
|
|
{
|
|
|
|
gen_op_arith_add(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra],
|
|
|
|
tcg_constant_tl(a->si), cpu_ca, cpu_ca32,
|
|
|
|
add_ca, compute_ca, compute_ov, compute_rc0);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool do_add_XO(DisasContext *ctx, arg_XO *a, bool add_ca,
|
|
|
|
bool compute_ca)
|
|
|
|
{
|
|
|
|
gen_op_arith_add(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb],
|
|
|
|
cpu_ca, cpu_ca32, add_ca, compute_ca, a->oe, a->rc);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool do_add_const_XO(DisasContext *ctx, arg_XO_ta *a, TCGv const_val,
|
|
|
|
bool add_ca, bool compute_ca)
|
|
|
|
{
|
|
|
|
gen_op_arith_add(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], const_val,
|
|
|
|
cpu_ca, cpu_ca32, add_ca, compute_ca, a->oe, a->rc);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRANS(ADD, do_add_XO, false, false);
|
|
|
|
TRANS(ADDC, do_add_XO, false, true);
|
|
|
|
TRANS(ADDE, do_add_XO, true, true);
|
|
|
|
TRANS(ADDME, do_add_const_XO, tcg_constant_tl(-1LL), true, true);
|
|
|
|
TRANS(ADDZE, do_add_const_XO, tcg_constant_tl(0), true, true);
|
|
|
|
TRANS(ADDIC, do_add_D, false, true, false, false);
|
|
|
|
TRANS(ADDIC_, do_add_D, false, true, false, true);
|
|
|
|
|
|
|
|
static bool trans_SUBFIC(DisasContext *ctx, arg_D *a)
|
|
|
|
{
|
|
|
|
gen_op_arith_subf(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra],
|
|
|
|
tcg_constant_tl(a->si), false, true, false, false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool do_subf_XO(DisasContext *ctx, arg_XO *a, bool add_ca,
|
|
|
|
bool compute_ca)
|
|
|
|
{
|
|
|
|
gen_op_arith_subf(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb],
|
|
|
|
add_ca, compute_ca, a->oe, a->rc);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool do_subf_const_XO(DisasContext *ctx, arg_XO_ta *a, TCGv const_val,
|
|
|
|
bool add_ca, bool compute_ca)
|
|
|
|
{
|
|
|
|
gen_op_arith_subf(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], const_val,
|
|
|
|
add_ca, compute_ca, a->oe, a->rc);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRANS(SUBF, do_subf_XO, false, false)
|
|
|
|
TRANS(SUBFC, do_subf_XO, false, true)
|
|
|
|
TRANS(SUBFE, do_subf_XO, true, true)
|
|
|
|
TRANS(SUBFME, do_subf_const_XO, tcg_constant_tl(-1LL), true, true)
|
|
|
|
TRANS(SUBFZE, do_subf_const_XO, tcg_constant_tl(0), true, true)
|
|
|
|
|
2021-06-01 22:35:19 +03:00
|
|
|
static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a)
|
|
|
|
{
|
|
|
|
gen_invalid(ctx);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool trans_PNOP(DisasContext *ctx, arg_PNOP *a)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2021-06-01 22:35:24 +03:00
|
|
|
|
|
|
|
static bool do_set_bool_cond(DisasContext *ctx, arg_X_bi *a, bool neg, bool rev)
|
|
|
|
{
|
|
|
|
REQUIRE_INSNS_FLAGS2(ctx, ISA310);
|
|
|
|
uint32_t mask = 0x08 >> (a->bi & 0x03);
|
|
|
|
TCGCond cond = rev ? TCG_COND_EQ : TCG_COND_NE;
|
|
|
|
TCGv temp = tcg_temp_new();
|
2023-08-05 03:22:26 +03:00
|
|
|
TCGv zero = tcg_constant_tl(0);
|
2021-06-01 22:35:24 +03:00
|
|
|
|
|
|
|
tcg_gen_extu_i32_tl(temp, cpu_crf[a->bi >> 2]);
|
|
|
|
tcg_gen_andi_tl(temp, temp, mask);
|
|
|
|
if (neg) {
|
2023-08-05 03:22:26 +03:00
|
|
|
tcg_gen_negsetcond_tl(cond, cpu_gpr[a->rt], temp, zero);
|
|
|
|
} else {
|
|
|
|
tcg_gen_setcond_tl(cond, cpu_gpr[a->rt], temp, zero);
|
2021-06-01 22:35:24 +03:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRANS(SETBC, do_set_bool_cond, false, false)
|
|
|
|
TRANS(SETBCR, do_set_bool_cond, false, true)
|
|
|
|
TRANS(SETNBC, do_set_bool_cond, true, false)
|
|
|
|
TRANS(SETNBCR, do_set_bool_cond, true, true)
|
2021-06-01 22:35:25 +03:00
|
|
|
|
|
|
|
static bool trans_CFUGED(DisasContext *ctx, arg_X *a)
|
|
|
|
{
|
|
|
|
REQUIRE_64BIT(ctx);
|
|
|
|
REQUIRE_INSNS_FLAGS2(ctx, ISA310);
|
|
|
|
#if defined(TARGET_PPC64)
|
2021-11-04 15:36:55 +03:00
|
|
|
gen_helper_CFUGED(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]);
|
2021-06-01 22:35:25 +03:00
|
|
|
#else
|
|
|
|
qemu_build_not_reached();
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
2021-10-29 23:23:57 +03:00
|
|
|
|
2021-11-04 15:36:56 +03:00
|
|
|
static void do_cntzdm(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 mask, int64_t trail)
|
2021-10-29 23:23:57 +03:00
|
|
|
{
|
2021-11-04 15:37:19 +03:00
|
|
|
TCGv_i64 t0, t1;
|
2021-10-29 23:23:57 +03:00
|
|
|
|
2021-11-04 15:37:19 +03:00
|
|
|
t0 = tcg_temp_new_i64();
|
|
|
|
t1 = tcg_temp_new_i64();
|
2021-10-29 23:23:57 +03:00
|
|
|
|
2021-11-04 15:37:19 +03:00
|
|
|
tcg_gen_and_i64(t0, src, mask);
|
2021-10-29 23:23:58 +03:00
|
|
|
if (trail) {
|
2021-11-04 15:37:19 +03:00
|
|
|
tcg_gen_ctzi_i64(t0, t0, -1);
|
2021-10-29 23:23:58 +03:00
|
|
|
} else {
|
2021-11-04 15:37:19 +03:00
|
|
|
tcg_gen_clzi_i64(t0, t0, -1);
|
2021-10-29 23:23:58 +03:00
|
|
|
}
|
2021-10-29 23:23:57 +03:00
|
|
|
|
2021-11-04 15:37:19 +03:00
|
|
|
tcg_gen_setcondi_i64(TCG_COND_NE, t1, t0, -1);
|
|
|
|
tcg_gen_andi_i64(t0, t0, 63);
|
|
|
|
tcg_gen_xori_i64(t0, t0, 63);
|
2021-10-29 23:23:58 +03:00
|
|
|
if (trail) {
|
2021-11-04 15:37:19 +03:00
|
|
|
tcg_gen_shl_i64(t0, mask, t0);
|
|
|
|
tcg_gen_shl_i64(t0, t0, t1);
|
2021-10-29 23:23:58 +03:00
|
|
|
} else {
|
2021-11-04 15:37:19 +03:00
|
|
|
tcg_gen_shr_i64(t0, mask, t0);
|
|
|
|
tcg_gen_shr_i64(t0, t0, t1);
|
2021-10-29 23:23:58 +03:00
|
|
|
}
|
2021-10-29 23:23:57 +03:00
|
|
|
|
2021-11-04 15:37:19 +03:00
|
|
|
tcg_gen_ctpop_i64(dst, t0);
|
2021-10-29 23:23:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool trans_CNTLZDM(DisasContext *ctx, arg_X *a)
|
|
|
|
{
|
|
|
|
REQUIRE_64BIT(ctx);
|
|
|
|
REQUIRE_INSNS_FLAGS2(ctx, ISA310);
|
|
|
|
#if defined(TARGET_PPC64)
|
2021-10-29 23:23:58 +03:00
|
|
|
do_cntzdm(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb], false);
|
|
|
|
#else
|
|
|
|
qemu_build_not_reached();
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool trans_CNTTZDM(DisasContext *ctx, arg_X *a)
|
|
|
|
{
|
|
|
|
REQUIRE_64BIT(ctx);
|
|
|
|
REQUIRE_INSNS_FLAGS2(ctx, ISA310);
|
|
|
|
#if defined(TARGET_PPC64)
|
|
|
|
do_cntzdm(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb], true);
|
2021-10-29 23:23:57 +03:00
|
|
|
#else
|
|
|
|
qemu_build_not_reached();
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
2021-10-29 23:23:59 +03:00
|
|
|
|
|
|
|
static bool trans_PDEPD(DisasContext *ctx, arg_X *a)
|
|
|
|
{
|
|
|
|
REQUIRE_64BIT(ctx);
|
|
|
|
REQUIRE_INSNS_FLAGS2(ctx, ISA310);
|
|
|
|
#if defined(TARGET_PPC64)
|
|
|
|
gen_helper_PDEPD(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]);
|
|
|
|
#else
|
|
|
|
qemu_build_not_reached();
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
2021-10-29 23:24:00 +03:00
|
|
|
|
|
|
|
static bool trans_PEXTD(DisasContext *ctx, arg_X *a)
|
|
|
|
{
|
|
|
|
REQUIRE_64BIT(ctx);
|
|
|
|
REQUIRE_INSNS_FLAGS2(ctx, ISA310);
|
|
|
|
#if defined(TARGET_PPC64)
|
|
|
|
gen_helper_PEXTD(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]);
|
|
|
|
#else
|
|
|
|
qemu_build_not_reached();
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
2022-06-29 19:29:02 +03:00
|
|
|
|
|
|
|
static bool trans_ADDG6S(DisasContext *ctx, arg_X *a)
|
|
|
|
{
|
2023-02-26 21:45:26 +03:00
|
|
|
const target_ulong carry_bits = (target_ulong)-1 / 0xf;
|
|
|
|
TCGv in1, in2, carryl, carryh, tmp;
|
|
|
|
TCGv zero = tcg_constant_tl(0);
|
2022-06-29 19:29:02 +03:00
|
|
|
|
|
|
|
REQUIRE_INSNS_FLAGS2(ctx, BCDA_ISA206);
|
|
|
|
|
2023-02-26 21:45:26 +03:00
|
|
|
in1 = cpu_gpr[a->ra];
|
|
|
|
in2 = cpu_gpr[a->rb];
|
|
|
|
tmp = tcg_temp_new();
|
|
|
|
carryl = tcg_temp_new();
|
|
|
|
carryh = tcg_temp_new();
|
|
|
|
|
|
|
|
/* Addition with carry. */
|
|
|
|
tcg_gen_add2_tl(carryl, carryh, in1, zero, in2, zero);
|
|
|
|
/* Addition without carry. */
|
|
|
|
tcg_gen_xor_tl(tmp, in1, in2);
|
|
|
|
/* Difference between the two is carry in to each bit. */
|
|
|
|
tcg_gen_xor_tl(carryl, carryl, tmp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The carry-out that we're looking for is the carry-in to
|
|
|
|
* the next nibble. Shift the double-word down one nibble,
|
|
|
|
* which puts all of the bits back into one word.
|
|
|
|
*/
|
|
|
|
tcg_gen_extract2_tl(carryl, carryl, carryh, 4);
|
|
|
|
|
|
|
|
/* Invert, isolate the carry bits, and produce 6's. */
|
|
|
|
tcg_gen_andc_tl(carryl, tcg_constant_tl(carry_bits), carryl);
|
|
|
|
tcg_gen_muli_tl(cpu_gpr[a->rt], carryl, 6);
|
2022-06-29 19:29:02 +03:00
|
|
|
return true;
|
|
|
|
}
|
2022-06-29 19:29:03 +03:00
|
|
|
|
2022-06-29 19:29:04 +03:00
|
|
|
static bool trans_CDTBCD(DisasContext *ctx, arg_X_sa *a)
|
|
|
|
{
|
|
|
|
REQUIRE_INSNS_FLAGS2(ctx, BCDA_ISA206);
|
|
|
|
gen_helper_CDTBCD(cpu_gpr[a->ra], cpu_gpr[a->rs]);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-06-29 19:29:03 +03:00
|
|
|
static bool trans_CBCDTD(DisasContext *ctx, arg_X_sa *a)
|
|
|
|
{
|
|
|
|
REQUIRE_INSNS_FLAGS2(ctx, BCDA_ISA206);
|
|
|
|
gen_helper_CBCDTD(cpu_gpr[a->ra], cpu_gpr[a->rs]);
|
|
|
|
return true;
|
|
|
|
}
|
2022-07-15 23:54:38 +03:00
|
|
|
|
|
|
|
static bool do_hash(DisasContext *ctx, arg_X *a, bool priv,
|
|
|
|
void (*helper)(TCGv_ptr, TCGv, TCGv, TCGv))
|
|
|
|
{
|
|
|
|
TCGv ea;
|
|
|
|
|
|
|
|
if (!(ctx->insns_flags2 & PPC2_ISA310)) {
|
|
|
|
/* if version is before v3.1, this operation is a nop */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (priv) {
|
|
|
|
/* if instruction is privileged but the context is in user space */
|
|
|
|
REQUIRE_SV(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unlikely(a->ra == 0)) {
|
|
|
|
/* if RA=0, the instruction form is invalid */
|
|
|
|
gen_invalid(ctx);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ea = do_ea_calc(ctx, a->ra, tcg_constant_tl(a->rt));
|
2023-09-14 02:37:36 +03:00
|
|
|
helper(tcg_env, ea, cpu_gpr[a->ra], cpu_gpr[a->rb]);
|
2022-07-15 23:54:38 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRANS(HASHST, do_hash, false, gen_helper_HASHST)
|
|
|
|
TRANS(HASHCHK, do_hash, false, gen_helper_HASHCHK)
|
2022-07-15 23:54:39 +03:00
|
|
|
TRANS(HASHSTP, do_hash, true, gen_helper_HASHSTP)
|
|
|
|
TRANS(HASHCHKP, do_hash, true, gen_helper_HASHCHKP)
|