| /* |
| NetWinder Floating Point Emulator |
| (c) Rebel.com, 1998-1999 |
| (c) Philip Blundell, 1998 |
| |
| Direct questions, comments to Scott Bambrough <scottb@netwinder.org> |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2 of the License, or |
| (at your option) any later version. |
| |
| This program 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 General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "fpa11.h" |
| #include "softfloat.h" |
| #include "fpopcode.h" |
| //#include "fpmodule.h" |
| //#include "fpmodule.inl" |
| |
| //#include <asm/uaccess.h> |
| |
| static inline |
| void loadSingle(const unsigned int Fn, target_ulong addr) |
| { |
| FPA11 *fpa11 = GET_FPA11(); |
| fpa11->fType[Fn] = typeSingle; |
| /* FIXME - handle failure of get_user() */ |
| get_user_u32(fpa11->fpreg[Fn].fSingle, addr); |
| } |
| |
| static inline |
| void loadDouble(const unsigned int Fn, target_ulong addr) |
| { |
| FPA11 *fpa11 = GET_FPA11(); |
| unsigned int *p; |
| p = (unsigned int*)&fpa11->fpreg[Fn].fDouble; |
| fpa11->fType[Fn] = typeDouble; |
| #ifdef HOST_WORDS_BIGENDIAN |
| /* FIXME - handle failure of get_user() */ |
| get_user_u32(p[0], addr); /* sign & exponent */ |
| get_user_u32(p[1], addr + 4); |
| #else |
| /* FIXME - handle failure of get_user() */ |
| get_user_u32(p[0], addr + 4); |
| get_user_u32(p[1], addr); /* sign & exponent */ |
| #endif |
| } |
| |
| static inline |
| void loadExtended(const unsigned int Fn, target_ulong addr) |
| { |
| FPA11 *fpa11 = GET_FPA11(); |
| unsigned int *p; |
| p = (unsigned int*)&fpa11->fpreg[Fn].fExtended; |
| fpa11->fType[Fn] = typeExtended; |
| /* FIXME - handle failure of get_user() */ |
| get_user_u32(p[0], addr); /* sign & exponent */ |
| get_user_u32(p[1], addr + 8); /* ls bits */ |
| get_user_u32(p[2], addr + 4); /* ms bits */ |
| } |
| |
| static inline |
| void loadMultiple(const unsigned int Fn, target_ulong addr) |
| { |
| FPA11 *fpa11 = GET_FPA11(); |
| register unsigned int *p; |
| unsigned long x; |
| |
| p = (unsigned int*)&(fpa11->fpreg[Fn]); |
| /* FIXME - handle failure of get_user() */ |
| get_user_u32(x, addr); |
| fpa11->fType[Fn] = (x >> 14) & 0x00000003; |
| |
| switch (fpa11->fType[Fn]) |
| { |
| case typeSingle: |
| case typeDouble: |
| { |
| /* FIXME - handle failure of get_user() */ |
| get_user_u32(p[0], addr + 8); /* Single */ |
| get_user_u32(p[1], addr + 4); /* double msw */ |
| p[2] = 0; /* empty */ |
| } |
| break; |
| |
| case typeExtended: |
| { |
| /* FIXME - handle failure of get_user() */ |
| get_user_u32(p[1], addr + 8); |
| get_user_u32(p[2], addr + 4); /* msw */ |
| p[0] = (x & 0x80003fff); |
| } |
| break; |
| } |
| } |
| |
| static inline |
| void storeSingle(const unsigned int Fn, target_ulong addr) |
| { |
| FPA11 *fpa11 = GET_FPA11(); |
| float32 val; |
| register unsigned int *p = (unsigned int*)&val; |
| |
| switch (fpa11->fType[Fn]) |
| { |
| case typeDouble: |
| val = float64_to_float32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status); |
| break; |
| |
| case typeExtended: |
| val = floatx80_to_float32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status); |
| break; |
| |
| default: val = fpa11->fpreg[Fn].fSingle; |
| } |
| |
| /* FIXME - handle put_user() failures */ |
| put_user_u32(p[0], addr); |
| } |
| |
| static inline |
| void storeDouble(const unsigned int Fn, target_ulong addr) |
| { |
| FPA11 *fpa11 = GET_FPA11(); |
| float64 val; |
| register unsigned int *p = (unsigned int*)&val; |
| |
| switch (fpa11->fType[Fn]) |
| { |
| case typeSingle: |
| val = float32_to_float64(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status); |
| break; |
| |
| case typeExtended: |
| val = floatx80_to_float64(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status); |
| break; |
| |
| default: val = fpa11->fpreg[Fn].fDouble; |
| } |
| /* FIXME - handle put_user() failures */ |
| #ifdef HOST_WORDS_BIGENDIAN |
| put_user_u32(p[0], addr); /* msw */ |
| put_user_u32(p[1], addr + 4); /* lsw */ |
| #else |
| put_user_u32(p[1], addr); /* msw */ |
| put_user_u32(p[0], addr + 4); /* lsw */ |
| #endif |
| } |
| |
| static inline |
| void storeExtended(const unsigned int Fn, target_ulong addr) |
| { |
| FPA11 *fpa11 = GET_FPA11(); |
| floatx80 val; |
| register unsigned int *p = (unsigned int*)&val; |
| |
| switch (fpa11->fType[Fn]) |
| { |
| case typeSingle: |
| val = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status); |
| break; |
| |
| case typeDouble: |
| val = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status); |
| break; |
| |
| default: val = fpa11->fpreg[Fn].fExtended; |
| } |
| |
| /* FIXME - handle put_user() failures */ |
| put_user_u32(p[0], addr); /* sign & exp */ |
| put_user_u32(p[1], addr + 8); |
| put_user_u32(p[2], addr + 4); /* msw */ |
| } |
| |
| static inline |
| void storeMultiple(const unsigned int Fn, target_ulong addr) |
| { |
| 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_u32(p[0], addr + 8); /* single */ |
| put_user_u32(p[1], addr + 4); /* double msw */ |
| put_user_u32(nType << 14, addr); |
| } |
| break; |
| |
| case typeExtended: |
| { |
| put_user_u32(p[2], addr + 4); /* msw */ |
| put_user_u32(p[1], addr + 8); |
| put_user_u32((p[0] & 0x80003fff) | (nType << 14), addr); |
| } |
| break; |
| } |
| } |
| |
| static unsigned int PerformLDF(const unsigned int opcode) |
| { |
| target_ulong pBase, pAddress, pFinal; |
| unsigned int nRc = 1, |
| write_back = WRITE_BACK(opcode); |
| |
| //printk("PerformLDF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode)); |
| |
| pBase = readRegister(getRn(opcode)); |
| if (REG_PC == getRn(opcode)) |
| { |
| pBase += 8; |
| write_back = 0; |
| } |
| |
| pFinal = pBase; |
| if (BIT_UP_SET(opcode)) |
| pFinal += getOffset(opcode) * 4; |
| else |
| pFinal -= getOffset(opcode) * 4; |
| |
| 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; |
| case TRANSFER_EXTENDED: loadExtended(getFd(opcode),pAddress); break; |
| default: nRc = 0; |
| } |
| |
| if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal); |
| return nRc; |
| } |
| |
| static unsigned int PerformSTF(const unsigned int opcode) |
| { |
| target_ulong pBase, pAddress, pFinal; |
| unsigned int nRc = 1, |
| write_back = WRITE_BACK(opcode); |
| |
| //printk("PerformSTF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode)); |
| SetRoundingMode(ROUND_TO_NEAREST); |
| |
| pBase = readRegister(getRn(opcode)); |
| if (REG_PC == getRn(opcode)) |
| { |
| pBase += 8; |
| write_back = 0; |
| } |
| |
| pFinal = pBase; |
| if (BIT_UP_SET(opcode)) |
| pFinal += getOffset(opcode) * 4; |
| else |
| pFinal -= getOffset(opcode) * 4; |
| |
| if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase; |
| |
| switch (opcode & MASK_TRANSFER_LENGTH) |
| { |
| case TRANSFER_SINGLE : storeSingle(getFd(opcode),pAddress); break; |
| case TRANSFER_DOUBLE : storeDouble(getFd(opcode),pAddress); break; |
| case TRANSFER_EXTENDED: storeExtended(getFd(opcode),pAddress); break; |
| default: nRc = 0; |
| } |
| |
| if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal); |
| return nRc; |
| } |
| |
| static unsigned int PerformLFM(const unsigned int opcode) |
| { |
| unsigned int i, Fd, |
| write_back = WRITE_BACK(opcode); |
| target_ulong pBase, pAddress, pFinal; |
| |
| pBase = readRegister(getRn(opcode)); |
| if (REG_PC == getRn(opcode)) |
| { |
| pBase += 8; |
| write_back = 0; |
| } |
| |
| pFinal = pBase; |
| if (BIT_UP_SET(opcode)) |
| pFinal += getOffset(opcode) * 4; |
| else |
| pFinal -= getOffset(opcode) * 4; |
| |
| if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase; |
| |
| Fd = getFd(opcode); |
| for (i=getRegisterCount(opcode);i>0;i--) |
| { |
| loadMultiple(Fd,pAddress); |
| pAddress += 12; Fd++; |
| if (Fd == 8) Fd = 0; |
| } |
| |
| if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal); |
| return 1; |
| } |
| |
| static unsigned int PerformSFM(const unsigned int opcode) |
| { |
| unsigned int i, Fd, |
| write_back = WRITE_BACK(opcode); |
| target_ulong pBase, pAddress, pFinal; |
| |
| pBase = readRegister(getRn(opcode)); |
| if (REG_PC == getRn(opcode)) |
| { |
| pBase += 8; |
| write_back = 0; |
| } |
| |
| pFinal = pBase; |
| if (BIT_UP_SET(opcode)) |
| pFinal += getOffset(opcode) * 4; |
| else |
| pFinal -= getOffset(opcode) * 4; |
| |
| if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase; |
| |
| Fd = getFd(opcode); |
| for (i=getRegisterCount(opcode);i>0;i--) |
| { |
| storeMultiple(Fd,pAddress); |
| pAddress += 12; Fd++; |
| if (Fd == 8) Fd = 0; |
| } |
| |
| if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal); |
| return 1; |
| } |
| |
| #if 1 |
| unsigned int EmulateCPDT(const unsigned int opcode) |
| { |
| unsigned int nRc = 0; |
| |
| //printk("EmulateCPDT(0x%08x)\n",opcode); |
| |
| 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; |
| } |
| #endif |