root/arch/arm/nwfpe/fpa11_cpdt.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
    NetWinder Floating Point Emulator
    (c) Rebel.com, 1998-1999
    (c) Philip Blundell, 1998, 2001

    Direct questions, comments to Scott Bambrough <scottb@netwinder.org>

*/

#include "fpa11.h"
#include "softfloat.h"
#include "fpopcode.h"
#include "fpmodule.h"
#include "fpmodule.inl"

#include <linux/uaccess.h>

static inline void loadSingle(const unsigned int Fn, const unsigned int __user *pMem)
{
        FPA11 *fpa11 = GET_FPA11();
        fpa11->fType[Fn] = typeSingle;
        get_user(fpa11->fpreg[Fn].fSingle, pMem);
}

static inline void loadDouble(const unsigned int Fn, const unsigned int __user *pMem)
{
        FPA11 *fpa11 = GET_FPA11();
        unsigned int *p;
        p = (unsigned int *) &fpa11->fpreg[Fn].fDouble;
        fpa11->fType[Fn] = typeDouble;
#ifdef __ARMEB__
        get_user(p[0], &pMem[0]);       /* sign & exponent */
        get_user(p[1], &pMem[1]);
#else
        get_user(p[0], &pMem[1]);
        get_user(p[1], &pMem[0]);       /* sign & exponent */
#endif
}

#ifdef CONFIG_FPE_NWFPE_XP
static inline void loadExtended(const unsigned int Fn, const unsigned int __user *pMem)
{
        FPA11 *fpa11 = GET_FPA11();
        unsigned int *p;
        p = (unsigned int *) &fpa11->fpreg[Fn].fExtended;
        fpa11->fType[Fn] = typeExtended;
        get_user(p[0], &pMem[0]);       /* sign & exponent */
#ifdef __ARMEB__
        get_user(p[1], &pMem[1]);       /* ms bits */
        get_user(p[2], &pMem[2]);       /* ls bits */
#else
        get_user(p[1], &pMem[2]);       /* ls bits */
        get_user(p[2], &pMem[1]);       /* ms bits */
#endif
}
#endif

static inline void loadMultiple(const unsigned int Fn, const unsigned int __user *pMem)
{
        FPA11 *fpa11 = GET_FPA11();
        register unsigned int *p;
        unsigned long x;

        p = (unsigned int *) &(fpa11->fpreg[Fn]);
        get_user(x, &pMem[0]);
        fpa11->fType[Fn] = (x >> 14) & 0x00000003;

        switch (fpa11->fType[Fn]) {
        case typeSingle:
        case typeDouble:
                {
                        get_user(p[0], &pMem[2]);       /* Single */
                        get_user(p[1], &pMem[1]);       /* double msw */
                        p[2] = 0;                       /* empty */
                }
                break;

#ifdef CONFIG_FPE_NWFPE_XP
        case typeExtended:
                {
                        get_user(p[1], &pMem[2]);
                        get_user(p[2], &pMem[1]);       /* msw */
                        p[0] = (x & 0x80003fff);
                }
                break;
#endif
        }
}

static inline void storeSingle(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem)
{
        FPA11 *fpa11 = GET_FPA11();
        union {
                float32 f;
                unsigned int i[1];
        } val;

        switch (fpa11->fType[Fn]) {
        case typeDouble:
                val.f = float64_to_float32(roundData, fpa11->fpreg[Fn].fDouble);
                break;

#ifdef CONFIG_FPE_NWFPE_XP
        case typeExtended:
                val.f = floatx80_to_float32(roundData, fpa11->fpreg[Fn].fExtended);
                break;
#endif

        default:
                val.f = fpa11->fpreg[Fn].fSingle;
        }

        put_user(val.i[0], pMem);
}

static inline void storeDouble(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem)
{
        FPA11 *fpa11 = GET_FPA11();
        union {
                float64 f;
                unsigned int i[2];
        } val;

        switch (fpa11->fType[Fn]) {
        case typeSingle:
                val.f = float32_to_float64(fpa11->fpreg[Fn].fSingle);
                break;

#ifdef CONFIG_FPE_NWFPE_XP
        case typeExtended:
                val.f = floatx80_to_float64(roundData, fpa11->fpreg[Fn].fExtended);
                break;
#endif

        default:
                val.f = fpa11->fpreg[Fn].fDouble;
        }

#ifdef __ARMEB__
        put_user(val.i[0], &pMem[0]);   /* msw */
        put_user(val.i[1], &pMem[1]);   /* lsw */
#else
        put_user(val.i[1], &pMem[0]);   /* msw */
        put_user(val.i[0], &pMem[1]);   /* lsw */
#endif
}

#ifdef CONFIG_FPE_NWFPE_XP
static inline void storeExtended(const unsigned int Fn, unsigned int __user *pMem)
{
        FPA11 *fpa11 = GET_FPA11();
        union {
                floatx80 f;
                unsigned int i[3];
        } val;

        switch (fpa11->fType[Fn]) {
        case typeSingle:
                val.f = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
                break;

        case typeDouble:
                val.f = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
                break;

        default:
                val.f = fpa11->fpreg[Fn].fExtended;
        }

        put_user(val.i[0], &pMem[0]);   /* sign & exp */
#ifdef __ARMEB__
        put_user(val.i[1], &pMem[1]);   /* msw */
        put_user(val.i[2], &pMem[2]);
#else
        put_user(val.i[1], &pMem[2]);
        put_user(val.i[2], &pMem[1]);   /* msw */
#endif
}
#endif

static inline void storeMultiple(const unsigned int Fn, unsigned int __user *pMem)
{
        FPA11 *fpa11 = GET_FPA11();
        register unsigned int nType, *p;

        p = (unsigned int *) &(fpa11->fpreg[Fn]);
        nType = fpa11->fType[Fn];

        switch (nType) {
        case typeSingle:
        case typeDouble:
                {
                        put_user(p[0], &pMem[2]);       /* single */
                        put_user(p[1], &pMem[1]);       /* double msw */
                        put_user(nType << 14, &pMem[0]);
                }
                break;

#ifdef CONFIG_FPE_NWFPE_XP
        case typeExtended:
                {
                        put_user(p[2], &pMem[1]);       /* msw */
                        put_user(p[1], &pMem[2]);
                        put_user((p[0] & 0x80003fff) | (nType << 14), &pMem[0]);
                }
                break;
#endif
        }
}

unsigned int PerformLDF(const unsigned int opcode)
{
        unsigned int __user *pBase, *pAddress, *pFinal;
        unsigned int nRc = 1, write_back = WRITE_BACK(opcode);

        pBase = (unsigned int __user *) readRegister(getRn(opcode));
        if (REG_PC == getRn(opcode)) {
                pBase += 2;
                write_back = 0;
        }

        pFinal = pBase;
        if (BIT_UP_SET(opcode))
                pFinal += getOffset(opcode);
        else
                pFinal -= getOffset(opcode);

        if (PREINDEXED(opcode))
                pAddress = pFinal;
        else
                pAddress = pBase;

        switch (opcode & MASK_TRANSFER_LENGTH) {
        case TRANSFER_SINGLE:
                loadSingle(getFd(opcode), pAddress);
                break;
        case TRANSFER_DOUBLE:
                loadDouble(getFd(opcode), pAddress);
                break;
#ifdef CONFIG_FPE_NWFPE_XP
        case TRANSFER_EXTENDED:
                loadExtended(getFd(opcode), pAddress);
                break;
#endif
        default:
                nRc = 0;
        }

        if (write_back)
                writeRegister(getRn(opcode), (unsigned long) pFinal);
        return nRc;
}

unsigned int PerformSTF(const unsigned int opcode)
{
        unsigned int __user *pBase, *pAddress, *pFinal;
        unsigned int nRc = 1, write_back = WRITE_BACK(opcode);
        struct roundingData roundData;

        roundData.mode = SetRoundingMode(opcode);
        roundData.precision = SetRoundingPrecision(opcode);
        roundData.exception = 0;

        pBase = (unsigned int __user *) readRegister(getRn(opcode));
        if (REG_PC == getRn(opcode)) {
                pBase += 2;
                write_back = 0;
        }

        pFinal = pBase;
        if (BIT_UP_SET(opcode))
                pFinal += getOffset(opcode);
        else
                pFinal -= getOffset(opcode);

        if (PREINDEXED(opcode))
                pAddress = pFinal;
        else
                pAddress = pBase;

        switch (opcode & MASK_TRANSFER_LENGTH) {
        case TRANSFER_SINGLE:
                storeSingle(&roundData, getFd(opcode), pAddress);
                break;
        case TRANSFER_DOUBLE:
                storeDouble(&roundData, getFd(opcode), pAddress);
                break;
#ifdef CONFIG_FPE_NWFPE_XP
        case TRANSFER_EXTENDED:
                storeExtended(getFd(opcode), pAddress);
                break;
#endif
        default:
                nRc = 0;
        }

        if (roundData.exception)
                float_raise(roundData.exception);

        if (write_back)
                writeRegister(getRn(opcode), (unsigned long) pFinal);
        return nRc;
}

unsigned int PerformLFM(const unsigned int opcode)
{
        unsigned int __user *pBase, *pAddress, *pFinal;
        unsigned int i, Fd, write_back = WRITE_BACK(opcode);

        pBase = (unsigned int __user *) readRegister(getRn(opcode));
        if (REG_PC == getRn(opcode)) {
                pBase += 2;
                write_back = 0;
        }

        pFinal = pBase;
        if (BIT_UP_SET(opcode))
                pFinal += getOffset(opcode);
        else
                pFinal -= getOffset(opcode);

        if (PREINDEXED(opcode))
                pAddress = pFinal;
        else
                pAddress = pBase;

        Fd = getFd(opcode);
        for (i = getRegisterCount(opcode); i > 0; i--) {
                loadMultiple(Fd, pAddress);
                pAddress += 3;
                Fd++;
                if (Fd == 8)
                        Fd = 0;
        }

        if (write_back)
                writeRegister(getRn(opcode), (unsigned long) pFinal);
        return 1;
}

unsigned int PerformSFM(const unsigned int opcode)
{
        unsigned int __user *pBase, *pAddress, *pFinal;
        unsigned int i, Fd, write_back = WRITE_BACK(opcode);

        pBase = (unsigned int __user *) readRegister(getRn(opcode));
        if (REG_PC == getRn(opcode)) {
                pBase += 2;
                write_back = 0;
        }

        pFinal = pBase;
        if (BIT_UP_SET(opcode))
                pFinal += getOffset(opcode);
        else
                pFinal -= getOffset(opcode);

        if (PREINDEXED(opcode))
                pAddress = pFinal;
        else
                pAddress = pBase;

        Fd = getFd(opcode);
        for (i = getRegisterCount(opcode); i > 0; i--) {
                storeMultiple(Fd, pAddress);
                pAddress += 3;
                Fd++;
                if (Fd == 8)
                        Fd = 0;
        }

        if (write_back)
                writeRegister(getRn(opcode), (unsigned long) pFinal);
        return 1;
}

unsigned int EmulateCPDT(const unsigned int opcode)
{
        unsigned int nRc = 0;

        if (LDF_OP(opcode)) {
                nRc = PerformLDF(opcode);
        } else if (LFM_OP(opcode)) {
                nRc = PerformLFM(opcode);
        } else if (STF_OP(opcode)) {
                nRc = PerformSTF(opcode);
        } else if (SFM_OP(opcode)) {
                nRc = PerformSFM(opcode);
        } else {
                nRc = 0;
        }

        return nRc;
}