mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
395 lines
8.2 KiB
395 lines
8.2 KiB
// 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 <[email protected]> |
|
|
|
*/ |
|
|
|
#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; |
|
}
|
|
|