|  | /* | 
|  | 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 |