/*
 *  PowerPC Decimal Floating Point (DPF) emulation helpers for QEMU.
 *
 *  Copyright (c) 2014 IBM Corporation.
 *
 * 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/>.
 */

#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/helper-proto.h"

#define DECNUMDIGITS 34
#include "libdecnumber/decContext.h"
#include "libdecnumber/decNumber.h"
#include "libdecnumber/dpd/decimal32.h"
#include "libdecnumber/dpd/decimal64.h"
#include "libdecnumber/dpd/decimal128.h"


static void get_dfp64(ppc_vsr_t *dst, ppc_fprp_t *dfp)
{
    dst->VsrD(1) = dfp->VsrD(0);
}

static void get_dfp128(ppc_vsr_t *dst, ppc_fprp_t *dfp)
{
    dst->VsrD(0) = dfp[0].VsrD(0);
    dst->VsrD(1) = dfp[1].VsrD(0);
}

static void set_dfp64(ppc_fprp_t *dfp, ppc_vsr_t *src)
{
    dfp[0].VsrD(0) = src->VsrD(1);
    dfp[0].VsrD(1) = 0ULL;
}

static void set_dfp128(ppc_fprp_t *dfp, ppc_vsr_t *src)
{
    dfp[0].VsrD(0) = src->VsrD(0);
    dfp[1].VsrD(0) = src->VsrD(1);
    dfp[0].VsrD(1) = 0ULL;
    dfp[1].VsrD(1) = 0ULL;
}

static void set_dfp128_to_avr(ppc_avr_t *dst, ppc_vsr_t *src)
{
    *dst = *src;
}

struct PPC_DFP {
    CPUPPCState *env;
    ppc_vsr_t vt, va, vb;
    decNumber t, a, b;
    decContext context;
    uint8_t crbf;
};

static void dfp_prepare_rounding_mode(decContext *context, uint64_t fpscr)
{
    enum rounding rnd;

    switch ((fpscr & FP_DRN) >> FPSCR_DRN0) {
    case 0:
        rnd = DEC_ROUND_HALF_EVEN;
        break;
    case 1:
        rnd = DEC_ROUND_DOWN;
        break;
    case 2:
         rnd = DEC_ROUND_CEILING;
         break;
    case 3:
         rnd = DEC_ROUND_FLOOR;
         break;
    case 4:
         rnd = DEC_ROUND_HALF_UP;
         break;
    case 5:
         rnd = DEC_ROUND_HALF_DOWN;
         break;
    case 6:
         rnd = DEC_ROUND_UP;
         break;
    case 7:
         rnd = DEC_ROUND_05UP;
         break;
    default:
        g_assert_not_reached();
    }

    decContextSetRounding(context, rnd);
}

static void dfp_set_round_mode_from_immediate(uint8_t r, uint8_t rmc,
                                                  struct PPC_DFP *dfp)
{
    enum rounding rnd;
    if (r == 0) {
        switch (rmc & 3) {
        case 0:
            rnd = DEC_ROUND_HALF_EVEN;
            break;
        case 1:
            rnd = DEC_ROUND_DOWN;
            break;
        case 2:
            rnd = DEC_ROUND_HALF_UP;
            break;
        case 3: /* use FPSCR rounding mode */
            return;
        default:
            g_assert_not_reached();
        }
    } else { /* r == 1 */
        switch (rmc & 3) {
        case 0:
            rnd = DEC_ROUND_CEILING;
            break;
        case 1:
            rnd = DEC_ROUND_FLOOR;
            break;
        case 2:
            rnd = DEC_ROUND_UP;
            break;
        case 3:
            rnd = DEC_ROUND_HALF_DOWN;
            break;
        default:
            g_assert_not_reached();
        }
    }
    decContextSetRounding(&dfp->context, rnd);
}

static void dfp_prepare_decimal64(struct PPC_DFP *dfp, ppc_fprp_t *a,
                                  ppc_fprp_t *b, CPUPPCState *env)
{
    decContextDefault(&dfp->context, DEC_INIT_DECIMAL64);
    dfp_prepare_rounding_mode(&dfp->context, env->fpscr);
    dfp->env = env;

    if (a) {
        get_dfp64(&dfp->va, a);
        decimal64ToNumber((decimal64 *)&dfp->va.VsrD(1), &dfp->a);
    } else {
        dfp->va.VsrD(1) = 0;
        decNumberZero(&dfp->a);
    }

    if (b) {
        get_dfp64(&dfp->vb, b);
        decimal64ToNumber((decimal64 *)&dfp->vb.VsrD(1), &dfp->b);
    } else {
        dfp->vb.VsrD(1) = 0;
        decNumberZero(&dfp->b);
    }
}

static void dfp_prepare_decimal128(struct PPC_DFP *dfp, ppc_fprp_t *a,
                                   ppc_fprp_t *b, CPUPPCState *env)
{
    decContextDefault(&dfp->context, DEC_INIT_DECIMAL128);
    dfp_prepare_rounding_mode(&dfp->context, env->fpscr);
    dfp->env = env;

    if (a) {
        get_dfp128(&dfp->va, a);
        decimal128ToNumber((decimal128 *)&dfp->va, &dfp->a);
    } else {
        dfp->va.VsrD(0) = dfp->va.VsrD(1) = 0;
        decNumberZero(&dfp->a);
    }

    if (b) {
        get_dfp128(&dfp->vb, b);
        decimal128ToNumber((decimal128 *)&dfp->vb, &dfp->b);
    } else {
        dfp->vb.VsrD(0) = dfp->vb.VsrD(1) = 0;
        decNumberZero(&dfp->b);
    }
}

static void dfp_finalize_decimal64(struct PPC_DFP *dfp)
{
    decimal64FromNumber((decimal64 *)&dfp->vt.VsrD(1), &dfp->t, &dfp->context);
}

static void dfp_finalize_decimal128(struct PPC_DFP *dfp)
{
    decimal128FromNumber((decimal128 *)&dfp->vt, &dfp->t, &dfp->context);
}

static void dfp_set_FPSCR_flag(struct PPC_DFP *dfp, uint64_t flag,
                uint64_t enabled)
{
    dfp->env->fpscr |= (flag | FP_FX);
    if (dfp->env->fpscr & enabled) {
        dfp->env->fpscr |= FP_FEX;
    }
}

static void dfp_set_FPRF_from_FRT_with_context(struct PPC_DFP *dfp,
                decContext *context)
{
    uint64_t fprf = 0;

    /* construct FPRF */
    switch (decNumberClass(&dfp->t, context)) {
    case DEC_CLASS_SNAN:
        fprf = 0x01;
        break;
    case DEC_CLASS_QNAN:
        fprf = 0x11;
        break;
    case DEC_CLASS_NEG_INF:
        fprf = 0x09;
        break;
    case DEC_CLASS_NEG_NORMAL:
        fprf = 0x08;
        break;
    case DEC_CLASS_NEG_SUBNORMAL:
        fprf = 0x18;
        break;
    case DEC_CLASS_NEG_ZERO:
        fprf = 0x12;
        break;
    case DEC_CLASS_POS_ZERO:
        fprf = 0x02;
        break;
    case DEC_CLASS_POS_SUBNORMAL:
        fprf = 0x14;
        break;
    case DEC_CLASS_POS_NORMAL:
        fprf = 0x04;
        break;
    case DEC_CLASS_POS_INF:
        fprf = 0x05;
        break;
    default:
        assert(0); /* should never get here */
    }
    dfp->env->fpscr &= ~FP_FPRF;
    dfp->env->fpscr |= (fprf << FPSCR_FPRF);
}

static void dfp_set_FPRF_from_FRT(struct PPC_DFP *dfp)
{
    dfp_set_FPRF_from_FRT_with_context(dfp, &dfp->context);
}

static void dfp_set_FPRF_from_FRT_short(struct PPC_DFP *dfp)
{
    decContext shortContext;
    decContextDefault(&shortContext, DEC_INIT_DECIMAL32);
    dfp_set_FPRF_from_FRT_with_context(dfp, &shortContext);
}

static void dfp_set_FPRF_from_FRT_long(struct PPC_DFP *dfp)
{
    decContext longContext;
    decContextDefault(&longContext, DEC_INIT_DECIMAL64);
    dfp_set_FPRF_from_FRT_with_context(dfp, &longContext);
}

static void dfp_check_for_OX(struct PPC_DFP *dfp)
{
    if (dfp->context.status & DEC_Overflow) {
        dfp_set_FPSCR_flag(dfp, FP_OX, FP_OE);
    }
}

static void dfp_check_for_UX(struct PPC_DFP *dfp)
{
    if (dfp->context.status & DEC_Underflow) {
        dfp_set_FPSCR_flag(dfp, FP_UX, FP_UE);
    }
}

static void dfp_check_for_XX(struct PPC_DFP *dfp)
{
    if (dfp->context.status & DEC_Inexact) {
        dfp_set_FPSCR_flag(dfp, FP_XX | FP_FI, FP_XE);
    }
}

static void dfp_check_for_ZX(struct PPC_DFP *dfp)
{
    if (dfp->context.status & DEC_Division_by_zero) {
        dfp_set_FPSCR_flag(dfp, FP_ZX, FP_ZE);
    }
}

static void dfp_check_for_VXSNAN(struct PPC_DFP *dfp)
{
    if (dfp->context.status & DEC_Invalid_operation) {
        if (decNumberIsSNaN(&dfp->a) || decNumberIsSNaN(&dfp->b)) {
            dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXSNAN, FP_VE);
        }
    }
}

static void dfp_check_for_VXSNAN_and_convert_to_QNaN(struct PPC_DFP *dfp)
{
    if (decNumberIsSNaN(&dfp->t)) {
        dfp->t.bits &= ~DECSNAN;
        dfp->t.bits |= DECNAN;
        dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXSNAN, FP_VE);
    }
}

static void dfp_check_for_VXISI(struct PPC_DFP *dfp, int testForSameSign)
{
    if (dfp->context.status & DEC_Invalid_operation) {
        if (decNumberIsInfinite(&dfp->a) && decNumberIsInfinite(&dfp->b)) {
            int same = decNumberClass(&dfp->a, &dfp->context) ==
                       decNumberClass(&dfp->b, &dfp->context);
            if ((same && testForSameSign) || (!same && !testForSameSign)) {
                dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXISI, FP_VE);
            }
        }
    }
}

static void dfp_check_for_VXISI_add(struct PPC_DFP *dfp)
{
    dfp_check_for_VXISI(dfp, 0);
}

static void dfp_check_for_VXISI_subtract(struct PPC_DFP *dfp)
{
    dfp_check_for_VXISI(dfp, 1);
}

static void dfp_check_for_VXIMZ(struct PPC_DFP *dfp)
{
    if (dfp->context.status & DEC_Invalid_operation) {
        if ((decNumberIsInfinite(&dfp->a) && decNumberIsZero(&dfp->b)) ||
            (decNumberIsInfinite(&dfp->b) && decNumberIsZero(&dfp->a))) {
            dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXIMZ, FP_VE);
        }
    }
}

static void dfp_check_for_VXZDZ(struct PPC_DFP *dfp)
{
    if (dfp->context.status & DEC_Division_undefined) {
        dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXZDZ, FP_VE);
    }
}

static void dfp_check_for_VXIDI(struct PPC_DFP *dfp)
{
    if (dfp->context.status & DEC_Invalid_operation) {
        if (decNumberIsInfinite(&dfp->a) && decNumberIsInfinite(&dfp->b)) {
            dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXIDI, FP_VE);
        }
    }
}

static void dfp_check_for_VXVC(struct PPC_DFP *dfp)
{
    if (decNumberIsNaN(&dfp->a) || decNumberIsNaN(&dfp->b)) {
        dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXVC, FP_VE);
    }
}

static void dfp_check_for_VXCVI(struct PPC_DFP *dfp)
{
    if ((dfp->context.status & DEC_Invalid_operation) &&
        (!decNumberIsSNaN(&dfp->a)) &&
        (!decNumberIsSNaN(&dfp->b))) {
        dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXCVI, FP_VE);
    }
}

static void dfp_set_CRBF_from_T(struct PPC_DFP *dfp)
{
    if (decNumberIsNaN(&dfp->t)) {
        dfp->crbf = 1;
    } else if (decNumberIsZero(&dfp->t)) {
        dfp->crbf = 2;
    } else if (decNumberIsNegative(&dfp->t)) {
        dfp->crbf = 8;
    } else {
        dfp->crbf = 4;
    }
}

static void dfp_set_FPCC_from_CRBF(struct PPC_DFP *dfp)
{
    dfp->env->fpscr &= ~FP_FPCC;
    dfp->env->fpscr |= (dfp->crbf << FPSCR_FPCC);
}

static inline void dfp_makeQNaN(decNumber *dn)
{
    dn->bits &= ~DECSPECIAL;
    dn->bits |= DECNAN;
}

static inline int dfp_get_digit(decNumber *dn, int n)
{
    assert(DECDPUN == 3);
    int unit = n / DECDPUN;
    int dig = n % DECDPUN;
    switch (dig) {
    case 0:
        return dn->lsu[unit] % 10;
    case 1:
        return (dn->lsu[unit] / 10) % 10;
    case 2:
        return dn->lsu[unit] / 100;
    }
    g_assert_not_reached();
}

#define DFP_HELPER_TAB(op, dnop, postprocs, size)                              \
void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *a,               \
                 ppc_fprp_t *b)                                                \
{                                                                              \
    struct PPC_DFP dfp;                                                        \
    dfp_prepare_decimal##size(&dfp, a, b, env);                                \
    dnop(&dfp.t, &dfp.a, &dfp.b, &dfp.context);                                \
    dfp_finalize_decimal##size(&dfp);                                          \
    postprocs(&dfp);                                                           \
    set_dfp##size(t, &dfp.vt);                                                 \
}

static void ADD_PPs(struct PPC_DFP *dfp)
{
    dfp_set_FPRF_from_FRT(dfp);
    dfp_check_for_OX(dfp);
    dfp_check_for_UX(dfp);
    dfp_check_for_XX(dfp);
    dfp_check_for_VXSNAN(dfp);
    dfp_check_for_VXISI_add(dfp);
}

DFP_HELPER_TAB(DADD, decNumberAdd, ADD_PPs, 64)
DFP_HELPER_TAB(DADDQ, decNumberAdd, ADD_PPs, 128)

static void SUB_PPs(struct PPC_DFP *dfp)
{
    dfp_set_FPRF_from_FRT(dfp);
    dfp_check_for_OX(dfp);
    dfp_check_for_UX(dfp);
    dfp_check_for_XX(dfp);
    dfp_check_for_VXSNAN(dfp);
    dfp_check_for_VXISI_subtract(dfp);
}

DFP_HELPER_TAB(DSUB, decNumberSubtract, SUB_PPs, 64)
DFP_HELPER_TAB(DSUBQ, decNumberSubtract, SUB_PPs, 128)

static void MUL_PPs(struct PPC_DFP *dfp)
{
    dfp_set_FPRF_from_FRT(dfp);
    dfp_check_for_OX(dfp);
    dfp_check_for_UX(dfp);
    dfp_check_for_XX(dfp);
    dfp_check_for_VXSNAN(dfp);
    dfp_check_for_VXIMZ(dfp);
}

DFP_HELPER_TAB(DMUL, decNumberMultiply, MUL_PPs, 64)
DFP_HELPER_TAB(DMULQ, decNumberMultiply, MUL_PPs, 128)

static void DIV_PPs(struct PPC_DFP *dfp)
{
    dfp_set_FPRF_from_FRT(dfp);
    dfp_check_for_OX(dfp);
    dfp_check_for_UX(dfp);
    dfp_check_for_ZX(dfp);
    dfp_check_for_XX(dfp);
    dfp_check_for_VXSNAN(dfp);
    dfp_check_for_VXZDZ(dfp);
    dfp_check_for_VXIDI(dfp);
}

DFP_HELPER_TAB(DDIV, decNumberDivide, DIV_PPs, 64)
DFP_HELPER_TAB(DDIVQ, decNumberDivide, DIV_PPs, 128)

#define DFP_HELPER_BF_AB(op, dnop, postprocs, size)                            \
uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, ppc_fprp_t *b)           \
{                                                                              \
    struct PPC_DFP dfp;                                                        \
    dfp_prepare_decimal##size(&dfp, a, b, env);                                \
    dnop(&dfp.t, &dfp.a, &dfp.b, &dfp.context);                                \
    dfp_finalize_decimal##size(&dfp);                                          \
    postprocs(&dfp);                                                           \
    return dfp.crbf;                                                           \
}

static void CMPU_PPs(struct PPC_DFP *dfp)
{
    dfp_set_CRBF_from_T(dfp);
    dfp_set_FPCC_from_CRBF(dfp);
    dfp_check_for_VXSNAN(dfp);
}

DFP_HELPER_BF_AB(DCMPU, decNumberCompare, CMPU_PPs, 64)
DFP_HELPER_BF_AB(DCMPUQ, decNumberCompare, CMPU_PPs, 128)

static void CMPO_PPs(struct PPC_DFP *dfp)
{
    dfp_set_CRBF_from_T(dfp);
    dfp_set_FPCC_from_CRBF(dfp);
    dfp_check_for_VXSNAN(dfp);
    dfp_check_for_VXVC(dfp);
}

DFP_HELPER_BF_AB(DCMPO, decNumberCompare, CMPO_PPs, 64)
DFP_HELPER_BF_AB(DCMPOQ, decNumberCompare, CMPO_PPs, 128)

#define DFP_HELPER_TSTDC(op, size)                                       \
uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, uint32_t dcm)      \
{                                                                        \
    struct PPC_DFP dfp;                                                  \
    int match = 0;                                                       \
                                                                         \
    dfp_prepare_decimal##size(&dfp, a, 0, env);                          \
                                                                         \
    match |= (dcm & 0x20) && decNumberIsZero(&dfp.a);                    \
    match |= (dcm & 0x10) && decNumberIsSubnormal(&dfp.a, &dfp.context); \
    match |= (dcm & 0x08) && decNumberIsNormal(&dfp.a, &dfp.context);    \
    match |= (dcm & 0x04) && decNumberIsInfinite(&dfp.a);                \
    match |= (dcm & 0x02) && decNumberIsQNaN(&dfp.a);                    \
    match |= (dcm & 0x01) && decNumberIsSNaN(&dfp.a);                    \
                                                                         \
    if (decNumberIsNegative(&dfp.a)) {                                   \
        dfp.crbf = match ? 0xA : 0x8;                                    \
    } else {                                                             \
        dfp.crbf = match ? 0x2 : 0x0;                                    \
    }                                                                    \
                                                                         \
    dfp_set_FPCC_from_CRBF(&dfp);                                        \
    return dfp.crbf;                                                     \
}

DFP_HELPER_TSTDC(DTSTDC, 64)
DFP_HELPER_TSTDC(DTSTDCQ, 128)

#define DFP_HELPER_TSTDG(op, size)                                       \
uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, uint32_t dcm)      \
{                                                                        \
    struct PPC_DFP dfp;                                                  \
    int minexp, maxexp, nzero_digits, nzero_idx, is_negative, is_zero,   \
        is_extreme_exp, is_subnormal, is_normal, leftmost_is_nonzero,    \
        match;                                                           \
                                                                         \
    dfp_prepare_decimal##size(&dfp, a, 0, env);                          \
                                                                         \
    if ((size) == 64) {                                                  \
        minexp = -398;                                                   \
        maxexp = 369;                                                    \
        nzero_digits = 16;                                               \
        nzero_idx = 5;                                                   \
    } else if ((size) == 128) {                                          \
        minexp = -6176;                                                  \
        maxexp = 6111;                                                   \
        nzero_digits = 34;                                               \
        nzero_idx = 11;                                                  \
    }                                                                    \
                                                                         \
    is_negative = decNumberIsNegative(&dfp.a);                           \
    is_zero = decNumberIsZero(&dfp.a);                                   \
    is_extreme_exp = (dfp.a.exponent == maxexp) ||                       \
                     (dfp.a.exponent == minexp);                         \
    is_subnormal = decNumberIsSubnormal(&dfp.a, &dfp.context);           \
    is_normal = decNumberIsNormal(&dfp.a, &dfp.context);                 \
    leftmost_is_nonzero = (dfp.a.digits == nzero_digits) &&              \
                          (dfp.a.lsu[nzero_idx] != 0);                   \
    match = 0;                                                           \
                                                                         \
    match |= (dcm & 0x20) && is_zero && !is_extreme_exp;                 \
    match |= (dcm & 0x10) && is_zero && is_extreme_exp;                  \
    match |= (dcm & 0x08) &&                                             \
             (is_subnormal || (is_normal && is_extreme_exp));            \
    match |= (dcm & 0x04) && is_normal && !is_extreme_exp &&             \
             !leftmost_is_nonzero;                                       \
    match |= (dcm & 0x02) && is_normal && !is_extreme_exp &&             \
             leftmost_is_nonzero;                                        \
    match |= (dcm & 0x01) && decNumberIsSpecial(&dfp.a);                 \
                                                                         \
    if (is_negative) {                                                   \
        dfp.crbf = match ? 0xA : 0x8;                                    \
    } else {                                                             \
        dfp.crbf = match ? 0x2 : 0x0;                                    \
    }                                                                    \
                                                                         \
    dfp_set_FPCC_from_CRBF(&dfp);                                        \
    return dfp.crbf;                                                     \
}

DFP_HELPER_TSTDG(DTSTDG, 64)
DFP_HELPER_TSTDG(DTSTDGQ, 128)

#define DFP_HELPER_TSTEX(op, size)                                       \
uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, ppc_fprp_t *b)     \
{                                                                        \
    struct PPC_DFP dfp;                                                  \
    int expa, expb, a_is_special, b_is_special;                          \
                                                                         \
    dfp_prepare_decimal##size(&dfp, a, b, env);                          \
                                                                         \
    expa = dfp.a.exponent;                                               \
    expb = dfp.b.exponent;                                               \
    a_is_special = decNumberIsSpecial(&dfp.a);                           \
    b_is_special = decNumberIsSpecial(&dfp.b);                           \
                                                                         \
    if (a_is_special || b_is_special) {                                  \
        int atype = a_is_special ? (decNumberIsNaN(&dfp.a) ? 4 : 2) : 1; \
        int btype = b_is_special ? (decNumberIsNaN(&dfp.b) ? 4 : 2) : 1; \
        dfp.crbf = (atype ^ btype) ? 0x1 : 0x2;                          \
    } else if (expa < expb) {                                            \
        dfp.crbf = 0x8;                                                  \
    } else if (expa > expb) {                                            \
        dfp.crbf = 0x4;                                                  \
    } else {                                                             \
        dfp.crbf = 0x2;                                                  \
    }                                                                    \
                                                                         \
    dfp_set_FPCC_from_CRBF(&dfp);                                        \
    return dfp.crbf;                                                     \
}

DFP_HELPER_TSTEX(DTSTEX, 64)
DFP_HELPER_TSTEX(DTSTEXQ, 128)

#define DFP_HELPER_TSTSF(op, size)                                       \
uint32_t helper_##op(CPUPPCState *env, ppc_fprp_t *a, ppc_fprp_t *b)     \
{                                                                        \
    struct PPC_DFP dfp;                                                  \
    unsigned k;                                                          \
    ppc_vsr_t va;                                                        \
                                                                         \
    dfp_prepare_decimal##size(&dfp, 0, b, env);                          \
                                                                         \
    get_dfp64(&va, a);                                                   \
    k = va.VsrD(1) & 0x3F;                                               \
                                                                         \
    if (unlikely(decNumberIsSpecial(&dfp.b))) {                          \
        dfp.crbf = 1;                                                    \
    } else if (k == 0) {                                                 \
        dfp.crbf = 4;                                                    \
    } else if (unlikely(decNumberIsZero(&dfp.b))) {                      \
        /* Zero has no sig digits */                                     \
        dfp.crbf = 4;                                                    \
    } else {                                                             \
        unsigned nsd = dfp.b.digits;                                     \
        if (k < nsd) {                                                   \
            dfp.crbf = 8;                                                \
        } else if (k > nsd) {                                            \
            dfp.crbf = 4;                                                \
        } else {                                                         \
            dfp.crbf = 2;                                                \
        }                                                                \
    }                                                                    \
                                                                         \
    dfp_set_FPCC_from_CRBF(&dfp);                                        \
    return dfp.crbf;                                                     \
}

DFP_HELPER_TSTSF(DTSTSF, 64)
DFP_HELPER_TSTSF(DTSTSFQ, 128)

#define DFP_HELPER_TSTSFI(op, size)                                     \
uint32_t helper_##op(CPUPPCState *env, uint32_t a, ppc_fprp_t *b)       \
{                                                                       \
    struct PPC_DFP dfp;                                                 \
    unsigned uim;                                                       \
                                                                        \
    dfp_prepare_decimal##size(&dfp, 0, b, env);                         \
                                                                        \
    uim = a & 0x3F;                                                     \
                                                                        \
    if (unlikely(decNumberIsSpecial(&dfp.b))) {                         \
        dfp.crbf = 1;                                                   \
    } else if (uim == 0) {                                              \
        dfp.crbf = 4;                                                   \
    } else if (unlikely(decNumberIsZero(&dfp.b))) {                     \
        /* Zero has no sig digits */                                    \
        dfp.crbf = 4;                                                   \
    } else {                                                            \
        unsigned nsd = dfp.b.digits;                                    \
        if (uim < nsd) {                                                \
            dfp.crbf = 8;                                               \
        } else if (uim > nsd) {                                         \
            dfp.crbf = 4;                                               \
        } else {                                                        \
            dfp.crbf = 2;                                               \
        }                                                               \
    }                                                                   \
                                                                        \
    dfp_set_FPCC_from_CRBF(&dfp);                                       \
    return dfp.crbf;                                                    \
}

DFP_HELPER_TSTSFI(DTSTSFI, 64)
DFP_HELPER_TSTSFI(DTSTSFIQ, 128)

static void QUA_PPs(struct PPC_DFP *dfp)
{
    dfp_set_FPRF_from_FRT(dfp);
    dfp_check_for_XX(dfp);
    dfp_check_for_VXSNAN(dfp);
    dfp_check_for_VXCVI(dfp);
}

static void dfp_quantize(uint8_t rmc, struct PPC_DFP *dfp)
{
    dfp_set_round_mode_from_immediate(0, rmc, dfp);
    decNumberQuantize(&dfp->t, &dfp->b, &dfp->a, &dfp->context);
    if (decNumberIsSNaN(&dfp->a)) {
        dfp->t = dfp->a;
        dfp_makeQNaN(&dfp->t);
    } else if (decNumberIsSNaN(&dfp->b)) {
        dfp->t = dfp->b;
        dfp_makeQNaN(&dfp->t);
    } else if (decNumberIsQNaN(&dfp->a)) {
        dfp->t = dfp->a;
    } else if (decNumberIsQNaN(&dfp->b)) {
        dfp->t = dfp->b;
    }
}

#define DFP_HELPER_QUAI(op, size)                                       \
void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b,        \
                 uint32_t te, uint32_t rmc)                             \
{                                                                       \
    struct PPC_DFP dfp;                                                 \
                                                                        \
    dfp_prepare_decimal##size(&dfp, 0, b, env);                         \
                                                                        \
    decNumberFromUInt32(&dfp.a, 1);                                     \
    dfp.a.exponent = (int32_t)((int8_t)(te << 3) >> 3);                 \
                                                                        \
    dfp_quantize(rmc, &dfp);                                            \
    dfp_finalize_decimal##size(&dfp);                                   \
    QUA_PPs(&dfp);                                                      \
                                                                        \
    set_dfp##size(t, &dfp.vt);                                          \
}

DFP_HELPER_QUAI(DQUAI, 64)
DFP_HELPER_QUAI(DQUAIQ, 128)

#define DFP_HELPER_QUA(op, size)                                        \
void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *a,        \
                 ppc_fprp_t *b, uint32_t rmc)                           \
{                                                                       \
    struct PPC_DFP dfp;                                                 \
                                                                        \
    dfp_prepare_decimal##size(&dfp, a, b, env);                         \
                                                                        \
    dfp_quantize(rmc, &dfp);                                            \
    dfp_finalize_decimal##size(&dfp);                                   \
    QUA_PPs(&dfp);                                                      \
                                                                        \
    set_dfp##size(t, &dfp.vt);                                          \
}

DFP_HELPER_QUA(DQUA, 64)
DFP_HELPER_QUA(DQUAQ, 128)

static void _dfp_reround(uint8_t rmc, int32_t ref_sig, int32_t xmax,
                             struct PPC_DFP *dfp)
{
    int msd_orig, msd_rslt;

    if (unlikely((ref_sig == 0) || (dfp->b.digits <= ref_sig))) {
        dfp->t = dfp->b;
        if (decNumberIsSNaN(&dfp->b)) {
            dfp_makeQNaN(&dfp->t);
            dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXSNAN, FPSCR_VE);
        }
        return;
    }

    /* Reround is equivalent to quantizing b with 1**E(n) where */
    /* n = exp(b) + numDigits(b) - reference_significance.      */

    decNumberFromUInt32(&dfp->a, 1);
    dfp->a.exponent = dfp->b.exponent + dfp->b.digits - ref_sig;

    if (unlikely(dfp->a.exponent > xmax)) {
        dfp->t.digits = 0;
        dfp->t.bits &= ~DECNEG;
        dfp_makeQNaN(&dfp->t);
        dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXCVI, FPSCR_VE);
        return;
    }

    dfp_quantize(rmc, dfp);

    msd_orig = dfp_get_digit(&dfp->b, dfp->b.digits-1);
    msd_rslt = dfp_get_digit(&dfp->t, dfp->t.digits-1);

    /* If the quantization resulted in rounding up to the next magnitude, */
    /* then we need to shift the significand and adjust the exponent.     */

    if (unlikely((msd_orig == 9) && (msd_rslt == 1))) {

        decNumber negone;

        decNumberFromInt32(&negone, -1);
        decNumberShift(&dfp->t, &dfp->t, &negone, &dfp->context);
        dfp->t.exponent++;

        if (unlikely(dfp->t.exponent > xmax)) {
            dfp_makeQNaN(&dfp->t);
            dfp->t.digits = 0;
            dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXCVI, FP_VE);
            /* Inhibit XX in this case */
            decContextClearStatus(&dfp->context, DEC_Inexact);
        }
    }
}

#define DFP_HELPER_RRND(op, size)                                       \
void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *a,        \
                 ppc_fprp_t *b, uint32_t rmc)                           \
{                                                                       \
    struct PPC_DFP dfp;                                                 \
    ppc_vsr_t va;                                                       \
    int32_t ref_sig;                                                    \
    int32_t xmax = ((size) == 64) ? 369 : 6111;                         \
                                                                        \
    dfp_prepare_decimal##size(&dfp, 0, b, env);                         \
                                                                        \
    get_dfp64(&va, a);                                                  \
    ref_sig = va.VsrD(1) & 0x3f;                                        \
                                                                        \
    _dfp_reround(rmc, ref_sig, xmax, &dfp);                             \
    dfp_finalize_decimal##size(&dfp);                                   \
    QUA_PPs(&dfp);                                                      \
                                                                        \
    set_dfp##size(t, &dfp.vt);                                          \
}

DFP_HELPER_RRND(DRRND, 64)
DFP_HELPER_RRND(DRRNDQ, 128)

#define DFP_HELPER_RINT(op, postprocs, size)                                   \
void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b,               \
             uint32_t r, uint32_t rmc)                                         \
{                                                                              \
    struct PPC_DFP dfp;                                                        \
                                                                               \
    dfp_prepare_decimal##size(&dfp, 0, b, env);                                \
                                                                               \
    dfp_set_round_mode_from_immediate(r, rmc, &dfp);                           \
    decNumberToIntegralExact(&dfp.t, &dfp.b, &dfp.context);                    \
    dfp_finalize_decimal##size(&dfp);                                          \
    postprocs(&dfp);                                                           \
                                                                               \
    set_dfp##size(t, &dfp.vt);                                                 \
}

static void RINTX_PPs(struct PPC_DFP *dfp)
{
    dfp_set_FPRF_from_FRT(dfp);
    dfp_check_for_XX(dfp);
    dfp_check_for_VXSNAN(dfp);
}

DFP_HELPER_RINT(DRINTX, RINTX_PPs, 64)
DFP_HELPER_RINT(DRINTXQ, RINTX_PPs, 128)

static void RINTN_PPs(struct PPC_DFP *dfp)
{
    dfp_set_FPRF_from_FRT(dfp);
    dfp_check_for_VXSNAN(dfp);
}

DFP_HELPER_RINT(DRINTN, RINTN_PPs, 64)
DFP_HELPER_RINT(DRINTNQ, RINTN_PPs, 128)

void helper_DCTDP(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b)
{
    struct PPC_DFP dfp;
    ppc_vsr_t vb;
    uint32_t b_short;

    get_dfp64(&vb, b);
    b_short = (uint32_t)vb.VsrD(1);

    dfp_prepare_decimal64(&dfp, 0, 0, env);
    decimal32ToNumber((decimal32 *)&b_short, &dfp.t);
    dfp_finalize_decimal64(&dfp);
    set_dfp64(t, &dfp.vt);
    dfp_set_FPRF_from_FRT(&dfp);
}

void helper_DCTQPQ(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b)
{
    struct PPC_DFP dfp;
    ppc_vsr_t vb;
    dfp_prepare_decimal128(&dfp, 0, 0, env);
    get_dfp64(&vb, b);
    decimal64ToNumber((decimal64 *)&vb.VsrD(1), &dfp.t);

    dfp_check_for_VXSNAN_and_convert_to_QNaN(&dfp);
    dfp_set_FPRF_from_FRT(&dfp);

    dfp_finalize_decimal128(&dfp);
    set_dfp128(t, &dfp.vt);
}

void helper_DRSP(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b)
{
    struct PPC_DFP dfp;
    uint32_t t_short = 0;
    ppc_vsr_t vt;
    dfp_prepare_decimal64(&dfp, 0, b, env);
    decimal32FromNumber((decimal32 *)&t_short, &dfp.b, &dfp.context);
    decimal32ToNumber((decimal32 *)&t_short, &dfp.t);

    dfp_set_FPRF_from_FRT_short(&dfp);
    dfp_check_for_OX(&dfp);
    dfp_check_for_UX(&dfp);
    dfp_check_for_XX(&dfp);

    vt.VsrD(1) = (uint64_t)t_short;
    set_dfp64(t, &vt);
}

void helper_DRDPQ(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b)
{
    struct PPC_DFP dfp;
    dfp_prepare_decimal128(&dfp, 0, b, env);
    decimal64FromNumber((decimal64 *)&dfp.vt.VsrD(1), &dfp.b, &dfp.context);
    decimal64ToNumber((decimal64 *)&dfp.vt.VsrD(1), &dfp.t);

    dfp_check_for_VXSNAN_and_convert_to_QNaN(&dfp);
    dfp_set_FPRF_from_FRT_long(&dfp);
    dfp_check_for_OX(&dfp);
    dfp_check_for_UX(&dfp);
    dfp_check_for_XX(&dfp);

    dfp.vt.VsrD(0) = dfp.vt.VsrD(1) = 0;
    dfp_finalize_decimal64(&dfp);
    set_dfp128(t, &dfp.vt);
}

#define DFP_HELPER_CFFIX(op, size)                                             \
void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b)               \
{                                                                              \
    struct PPC_DFP dfp;                                                        \
    ppc_vsr_t vb;                                                              \
    dfp_prepare_decimal##size(&dfp, 0, b, env);                                \
    get_dfp64(&vb, b);                                                         \
    decNumberFromInt64(&dfp.t, (int64_t)vb.VsrD(1));                           \
    dfp_finalize_decimal##size(&dfp);                                          \
    CFFIX_PPs(&dfp);                                                           \
                                                                               \
    set_dfp##size(t, &dfp.vt);                                                 \
}

static void CFFIX_PPs(struct PPC_DFP *dfp)
{
    dfp_set_FPRF_from_FRT(dfp);
    dfp_check_for_XX(dfp);
}

DFP_HELPER_CFFIX(DCFFIX, 64)
DFP_HELPER_CFFIX(DCFFIXQ, 128)

void helper_DCFFIXQQ(CPUPPCState *env, ppc_fprp_t *t, ppc_avr_t *b)
{
    struct PPC_DFP dfp;

    dfp_prepare_decimal128(&dfp, NULL, NULL, env);
    decNumberFromInt128(&dfp.t, (uint64_t)b->VsrD(1), (int64_t)b->VsrD(0));
    dfp_finalize_decimal128(&dfp);
    CFFIX_PPs(&dfp);

    set_dfp128(t, &dfp.vt);
}

#define DFP_HELPER_CTFIX(op, size)                                            \
void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b)              \
{                                                                             \
    struct PPC_DFP dfp;                                                       \
    dfp_prepare_decimal##size(&dfp, 0, b, env);                               \
                                                                              \
    if (unlikely(decNumberIsSpecial(&dfp.b))) {                               \
        uint64_t invalid_flags = FP_VX | FP_VXCVI;                            \
        if (decNumberIsInfinite(&dfp.b)) {                                    \
            dfp.vt.VsrD(1) = decNumberIsNegative(&dfp.b) ? INT64_MIN :        \
                                                           INT64_MAX;         \
        } else { /* NaN */                                                    \
            dfp.vt.VsrD(1) = INT64_MIN;                                       \
            if (decNumberIsSNaN(&dfp.b)) {                                    \
                invalid_flags |= FP_VXSNAN;                                   \
            }                                                                 \
        }                                                                     \
        dfp_set_FPSCR_flag(&dfp, invalid_flags, FP_VE);                       \
    } else if (unlikely(decNumberIsZero(&dfp.b))) {                           \
        dfp.vt.VsrD(1) = 0;                                                   \
    } else {                                                                  \
        decNumberToIntegralExact(&dfp.b, &dfp.b, &dfp.context);               \
        dfp.vt.VsrD(1) = decNumberIntegralToInt64(&dfp.b, &dfp.context);      \
        if (decContextTestStatus(&dfp.context, DEC_Invalid_operation)) {      \
            dfp.vt.VsrD(1) = decNumberIsNegative(&dfp.b) ? INT64_MIN :        \
                                                           INT64_MAX;         \
            dfp_set_FPSCR_flag(&dfp, FP_VX | FP_VXCVI, FP_VE);                \
        } else {                                                              \
            dfp_check_for_XX(&dfp);                                           \
        }                                                                     \
    }                                                                         \
                                                                              \
    set_dfp64(t, &dfp.vt);                                                    \
}

DFP_HELPER_CTFIX(DCTFIX, 64)
DFP_HELPER_CTFIX(DCTFIXQ, 128)

void helper_DCTFIXQQ(CPUPPCState *env, ppc_avr_t *t, ppc_fprp_t *b)
{
    struct PPC_DFP dfp;
    dfp_prepare_decimal128(&dfp, 0, b, env);

    if (unlikely(decNumberIsSpecial(&dfp.b))) {
        uint64_t invalid_flags = FP_VX | FP_VXCVI;
        if (decNumberIsInfinite(&dfp.b)) {
            if (decNumberIsNegative(&dfp.b)) {
                dfp.vt.VsrD(0) = INT64_MIN;
                dfp.vt.VsrD(1) = 0;
            } else {
                dfp.vt.VsrD(0) = INT64_MAX;
                dfp.vt.VsrD(1) = UINT64_MAX;
            }
        } else { /* NaN */
            dfp.vt.VsrD(0) = INT64_MIN;
            dfp.vt.VsrD(1) = 0;
            if (decNumberIsSNaN(&dfp.b)) {
                invalid_flags |= FP_VXSNAN;
            }
        }
        dfp_set_FPSCR_flag(&dfp, invalid_flags, FP_VE);
    } else if (unlikely(decNumberIsZero(&dfp.b))) {
        dfp.vt.VsrD(0) = 0;
        dfp.vt.VsrD(1) = 0;
    } else {
        decNumberToIntegralExact(&dfp.b, &dfp.b, &dfp.context);
        decNumberIntegralToInt128(&dfp.b, &dfp.context,
                &dfp.vt.VsrD(1), &dfp.vt.VsrD(0));
        if (decContextTestStatus(&dfp.context, DEC_Invalid_operation)) {
            if (decNumberIsNegative(&dfp.b)) {
                dfp.vt.VsrD(0) = INT64_MIN;
                dfp.vt.VsrD(1) = 0;
            } else {
                dfp.vt.VsrD(0) = INT64_MAX;
                dfp.vt.VsrD(1) = UINT64_MAX;
            }
            dfp_set_FPSCR_flag(&dfp, FP_VX | FP_VXCVI, FP_VE);
        } else {
            dfp_check_for_XX(&dfp);
        }
    }

    set_dfp128_to_avr(t, &dfp.vt);
}

static inline void dfp_set_bcd_digit_64(ppc_vsr_t *t, uint8_t digit,
                                        unsigned n)
{
    t->VsrD(1) |= ((uint64_t)(digit & 0xF) << (n << 2));
}

static inline void dfp_set_bcd_digit_128(ppc_vsr_t *t, uint8_t digit,
                                         unsigned n)
{
    t->VsrD((n & 0x10) ? 0 : 1) |=
        ((uint64_t)(digit & 0xF) << ((n & 15) << 2));
}

static inline void dfp_set_sign_64(ppc_vsr_t *t, uint8_t sgn)
{
    t->VsrD(1) <<= 4;
    t->VsrD(1) |= (sgn & 0xF);
}

static inline void dfp_set_sign_128(ppc_vsr_t *t, uint8_t sgn)
{
    t->VsrD(0) <<= 4;
    t->VsrD(0) |= (t->VsrD(1) >> 60);
    t->VsrD(1) <<= 4;
    t->VsrD(1) |= (sgn & 0xF);
}

#define DFP_HELPER_DEDPD(op, size)                                        \
void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b,          \
                 uint32_t sp)                                             \
{                                                                         \
    struct PPC_DFP dfp;                                                   \
    uint8_t digits[34];                                                   \
    int i, N;                                                             \
                                                                          \
    dfp_prepare_decimal##size(&dfp, 0, b, env);                           \
                                                                          \
    decNumberGetBCD(&dfp.b, digits);                                      \
    dfp.vt.VsrD(0) = dfp.vt.VsrD(1) = 0;                                  \
    N = dfp.b.digits;                                                     \
                                                                          \
    for (i = 0; (i < N) && (i < (size)/4); i++) {                         \
        dfp_set_bcd_digit_##size(&dfp.vt, digits[N - i - 1], i);          \
    }                                                                     \
                                                                          \
    if (sp & 2) {                                                         \
        uint8_t sgn;                                                      \
                                                                          \
        if (decNumberIsNegative(&dfp.b)) {                                \
            sgn = 0xD;                                                    \
        } else {                                                          \
            sgn = ((sp & 1) ? 0xF : 0xC);                                 \
        }                                                                 \
        dfp_set_sign_##size(&dfp.vt, sgn);                                \
    }                                                                     \
                                                                          \
    set_dfp##size(t, &dfp.vt);                                            \
}

DFP_HELPER_DEDPD(DDEDPD, 64)
DFP_HELPER_DEDPD(DDEDPDQ, 128)

static inline uint8_t dfp_get_bcd_digit_64(ppc_vsr_t *t, unsigned n)
{
    return t->VsrD(1) >> ((n << 2) & 63) & 15;
}

static inline uint8_t dfp_get_bcd_digit_128(ppc_vsr_t *t, unsigned n)
{
    return t->VsrD((n & 0x10) ? 0 : 1) >> ((n << 2) & 63) & 15;
}

static inline void dfp_invalid_op_vxcvi_64(struct PPC_DFP *dfp)
{
    /* TODO: fpscr is incorrectly not being saved to env */
    dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXCVI, FPSCR_VE);
    if ((dfp->env->fpscr & FP_VE) == 0) {
        dfp->vt.VsrD(1) = 0x7c00000000000000; /* QNaN */
    }
}


static inline void dfp_invalid_op_vxcvi_128(struct PPC_DFP *dfp)
{
    /* TODO: fpscr is incorrectly not being saved to env */
    dfp_set_FPSCR_flag(dfp, FP_VX | FP_VXCVI, FPSCR_VE);
    if ((dfp->env->fpscr & FP_VE) == 0) {
        dfp->vt.VsrD(0) = 0x7c00000000000000; /* QNaN */
        dfp->vt.VsrD(1) = 0x0;
    }
}

#define DFP_HELPER_ENBCD(op, size)                                           \
void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b,             \
                 uint32_t s)                                                 \
{                                                                            \
    struct PPC_DFP dfp;                                                      \
    uint8_t digits[32];                                                      \
    int n = 0, offset = 0, sgn = 0, nonzero = 0;                             \
                                                                             \
    dfp_prepare_decimal##size(&dfp, 0, b, env);                              \
                                                                             \
    decNumberZero(&dfp.t);                                                   \
                                                                             \
    if (s) {                                                                 \
        uint8_t sgnNibble = dfp_get_bcd_digit_##size(&dfp.vb, offset++);     \
        switch (sgnNibble) {                                                 \
        case 0xD:                                                            \
        case 0xB:                                                            \
            sgn = 1;                                                         \
            break;                                                           \
        case 0xC:                                                            \
        case 0xF:                                                            \
        case 0xA:                                                            \
        case 0xE:                                                            \
            sgn = 0;                                                         \
            break;                                                           \
        default:                                                             \
            dfp_invalid_op_vxcvi_##size(&dfp);                               \
            set_dfp##size(t, &dfp.vt);                                       \
            return;                                                          \
        }                                                                    \
        }                                                                    \
                                                                             \
    while (offset < (size) / 4) {                                            \
        n++;                                                                 \
        digits[(size) / 4 - n] = dfp_get_bcd_digit_##size(&dfp.vb,           \
                                                          offset++);         \
        if (digits[(size) / 4 - n] > 10) {                                   \
            dfp_invalid_op_vxcvi_##size(&dfp);                               \
            set_dfp##size(t, &dfp.vt);                                       \
            return;                                                          \
        } else {                                                             \
            nonzero |= (digits[(size) / 4 - n] > 0);                         \
        }                                                                    \
    }                                                                        \
                                                                             \
    if (nonzero) {                                                           \
        decNumberSetBCD(&dfp.t, digits + ((size) / 4) - n, n);               \
    }                                                                        \
                                                                             \
    if (s && sgn)  {                                                         \
        dfp.t.bits |= DECNEG;                                                \
    }                                                                        \
    dfp_finalize_decimal##size(&dfp);                                        \
    dfp_set_FPRF_from_FRT(&dfp);                                             \
    set_dfp##size(t, &dfp.vt);                                               \
}

DFP_HELPER_ENBCD(DENBCD, 64)
DFP_HELPER_ENBCD(DENBCDQ, 128)

#define DFP_HELPER_XEX(op, size)                               \
void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) \
{                                                              \
    struct PPC_DFP dfp;                                        \
    ppc_vsr_t vt;                                              \
                                                               \
    dfp_prepare_decimal##size(&dfp, 0, b, env);                \
                                                               \
    if (unlikely(decNumberIsSpecial(&dfp.b))) {                \
        if (decNumberIsInfinite(&dfp.b)) {                     \
            vt.VsrD(1) = -1;                                   \
        } else if (decNumberIsSNaN(&dfp.b)) {                  \
            vt.VsrD(1) = -3;                                   \
        } else if (decNumberIsQNaN(&dfp.b)) {                  \
            vt.VsrD(1) = -2;                                   \
        } else {                                               \
            assert(0);                                         \
        }                                                      \
        set_dfp64(t, &vt);                                     \
    } else {                                                   \
        if ((size) == 64) {                                    \
            vt.VsrD(1) = dfp.b.exponent + 398;                 \
        } else if ((size) == 128) {                            \
            vt.VsrD(1) = dfp.b.exponent + 6176;                \
        } else {                                               \
            assert(0);                                         \
        }                                                      \
        set_dfp64(t, &vt);                                     \
    }                                                          \
}

DFP_HELPER_XEX(DXEX, 64)
DFP_HELPER_XEX(DXEXQ, 128)

static void dfp_set_raw_exp_64(ppc_vsr_t *t, uint64_t raw)
{
    t->VsrD(1) &= 0x8003ffffffffffffULL;
    t->VsrD(1) |= (raw << (63 - 13));
}

static void dfp_set_raw_exp_128(ppc_vsr_t *t, uint64_t raw)
{
    t->VsrD(0) &= 0x80003fffffffffffULL;
    t->VsrD(0) |= (raw << (63 - 17));
}

#define DFP_HELPER_IEX(op, size)                                          \
void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *a,          \
                 ppc_fprp_t *b)                                           \
{                                                                         \
    struct PPC_DFP dfp;                                                   \
    uint64_t raw_qnan, raw_snan, raw_inf, max_exp;                        \
    ppc_vsr_t va;                                                         \
    int bias;                                                             \
    int64_t exp;                                                          \
                                                                          \
    get_dfp64(&va, a);                                                    \
    exp = (int64_t)va.VsrD(1);                                            \
    dfp_prepare_decimal##size(&dfp, 0, b, env);                           \
                                                                          \
    if ((size) == 64) {                                                   \
        max_exp = 767;                                                    \
        raw_qnan = 0x1F00;                                                \
        raw_snan = 0x1F80;                                                \
        raw_inf = 0x1E00;                                                 \
        bias = 398;                                                       \
    } else if ((size) == 128) {                                           \
        max_exp = 12287;                                                  \
        raw_qnan = 0x1f000;                                               \
        raw_snan = 0x1f800;                                               \
        raw_inf = 0x1e000;                                                \
        bias = 6176;                                                      \
    } else {                                                              \
        assert(0);                                                        \
    }                                                                     \
                                                                          \
    if (unlikely((exp < 0) || (exp > max_exp))) {                         \
        dfp.vt.VsrD(0) = dfp.vb.VsrD(0);                                  \
        dfp.vt.VsrD(1) = dfp.vb.VsrD(1);                                  \
        if (exp == -1) {                                                  \
            dfp_set_raw_exp_##size(&dfp.vt, raw_inf);                     \
        } else if (exp == -3) {                                           \
            dfp_set_raw_exp_##size(&dfp.vt, raw_snan);                    \
        } else {                                                          \
            dfp_set_raw_exp_##size(&dfp.vt, raw_qnan);                    \
        }                                                                 \
    } else {                                                              \
        dfp.t = dfp.b;                                                    \
        if (unlikely(decNumberIsSpecial(&dfp.t))) {                       \
            dfp.t.bits &= ~DECSPECIAL;                                    \
        }                                                                 \
        dfp.t.exponent = exp - bias;                                      \
        dfp_finalize_decimal##size(&dfp);                                 \
    }                                                                     \
    set_dfp##size(t, &dfp.vt);                                            \
}

DFP_HELPER_IEX(DIEX, 64)
DFP_HELPER_IEX(DIEXQ, 128)

static void dfp_clear_lmd_from_g5msb(uint64_t *t)
{

    /* The most significant 5 bits of the PowerPC DFP format combine bits  */
    /* from the left-most decimal digit (LMD) and the biased exponent.     */
    /* This  routine clears the LMD bits while preserving the exponent     */
    /*  bits.  See "Figure 80: Encoding of bits 0:4 of the G field for     */
    /*  Finite Numbers" in the Power ISA for additional details.           */

    uint64_t g5msb = (*t >> 58) & 0x1F;

    if ((g5msb >> 3) < 3) { /* LMD in [0-7] ? */
       *t &= ~(7ULL << 58);
    } else {
       switch (g5msb & 7) {
       case 0:
       case 1:
           g5msb = 0;
           break;
       case 2:
       case 3:
           g5msb = 0x8;
           break;
       case 4:
       case 5:
           g5msb = 0x10;
           break;
       case 6:
           g5msb = 0x1E;
           break;
       case 7:
           g5msb = 0x1F;
           break;
       }

        *t &= ~(0x1fULL << 58);
        *t |= (g5msb << 58);
    }
}

#define DFP_HELPER_SHIFT(op, size, shift_left)                      \
void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *a,    \
                 uint32_t sh)                                       \
{                                                                   \
    struct PPC_DFP dfp;                                             \
    unsigned max_digits = ((size) == 64) ? 16 : 34;                 \
                                                                    \
    dfp_prepare_decimal##size(&dfp, a, 0, env);                     \
                                                                    \
    if (sh <= max_digits) {                                         \
                                                                    \
        decNumber shd;                                              \
        unsigned special = dfp.a.bits & DECSPECIAL;                 \
                                                                    \
        if (shift_left) {                                           \
            decNumberFromUInt32(&shd, sh);                          \
        } else {                                                    \
            decNumberFromInt32(&shd, -((int32_t)sh));               \
        }                                                           \
                                                                    \
        dfp.a.bits &= ~DECSPECIAL;                                  \
        decNumberShift(&dfp.t, &dfp.a, &shd, &dfp.context);         \
                                                                    \
        dfp.t.bits |= special;                                      \
        if (special && (dfp.t.digits >= max_digits)) {              \
            dfp.t.digits = max_digits - 1;                          \
        }                                                           \
                                                                    \
        dfp_finalize_decimal##size(&dfp);                           \
    } else {                                                        \
        if ((size) == 64) {                                         \
            dfp.vt.VsrD(1) = dfp.va.VsrD(1) &                       \
                             0xFFFC000000000000ULL;                 \
            dfp_clear_lmd_from_g5msb(&dfp.vt.VsrD(1));              \
        } else {                                                    \
            dfp.vt.VsrD(0) = dfp.va.VsrD(0) &                       \
                             0xFFFFC00000000000ULL;                 \
            dfp_clear_lmd_from_g5msb(&dfp.vt.VsrD(0));              \
            dfp.vt.VsrD(1) = 0;                                     \
        }                                                           \
    }                                                               \
                                                                    \
    set_dfp##size(t, &dfp.vt);                                      \
}

DFP_HELPER_SHIFT(DSCLI, 64, 1)
DFP_HELPER_SHIFT(DSCLIQ, 128, 1)
DFP_HELPER_SHIFT(DSCRI, 64, 0)
DFP_HELPER_SHIFT(DSCRIQ, 128, 0)

target_ulong helper_CDTBCD(target_ulong s)
{
    uint64_t res = 0;
    uint32_t dec32, declets;
    uint8_t bcd[6];
    int i, w, sh;
    decNumber a;

    for (w = 1; w >= 0; w--) {
        res <<= 32;
        declets = extract64(s, 32 * w, 20);
        if (declets) {
            /* decimal32 with zero exponent and word "w" declets */
            dec32 = (0x225ULL << 20) | declets;
            decimal32ToNumber((decimal32 *)&dec32, &a);
            decNumberGetBCD(&a, bcd);
            for (i = 0; i < a.digits; i++) {
                sh = 4 * (a.digits - 1 - i);
                res |= (uint64_t)bcd[i] << sh;
            }
        }
    }

    return res;
}

target_ulong helper_CBCDTD(target_ulong s)
{
    uint64_t res = 0;
    uint32_t dec32;
    uint8_t bcd[6];
    int w, i, offs;
    decNumber a;
    decContext context;

    decContextDefault(&context, DEC_INIT_DECIMAL32);

    for (w = 1; w >= 0; w--) {
        res <<= 32;
        decNumberZero(&a);
        /* Extract each BCD field of word "w" */
        for (i = 5; i >= 0; i--) {
            offs = 4 * (5 - i) + 32 * w;
            bcd[i] = extract64(s, offs, 4);
            if (bcd[i] > 9) {
                /*
                 * If the field value is greater than 9, the results are
                 * undefined. We could use a fixed value like 0 or 9, but
                 * an and with 9 seems to better match the hardware behavior.
                 */
                bcd[i] &= 9;
            }
        }

        /* Create a decNumber with the BCD values and convert to decimal32 */
        decNumberSetBCD(&a, bcd, 6);
        decimal32FromNumber((decimal32 *)&dec32, &a, &context);

        /* Extract the two declets from the decimal32 value */
        res |= dec32 & 0xfffff;
    }

    return res;
}
