diff options
Diffstat (limited to 'linux-user/arm')
-rw-r--r-- | linux-user/arm/nwfpe/double_cpdo.c | 296 | ||||
-rw-r--r-- | linux-user/arm/nwfpe/extended_cpdo.c | 273 | ||||
-rw-r--r-- | linux-user/arm/nwfpe/fpa11.c | 244 | ||||
-rw-r--r-- | linux-user/arm/nwfpe/fpa11.h | 122 | ||||
-rw-r--r-- | linux-user/arm/nwfpe/fpa11.inl | 51 | ||||
-rw-r--r-- | linux-user/arm/nwfpe/fpa11_cpdo.c | 117 | ||||
-rw-r--r-- | linux-user/arm/nwfpe/fpa11_cpdt.c | 386 | ||||
-rw-r--r-- | linux-user/arm/nwfpe/fpa11_cprt.c | 286 | ||||
-rw-r--r-- | linux-user/arm/nwfpe/fpopcode.c | 148 | ||||
-rw-r--r-- | linux-user/arm/nwfpe/fpopcode.h | 390 | ||||
-rw-r--r-- | linux-user/arm/nwfpe/fpsr.h | 108 | ||||
-rw-r--r-- | linux-user/arm/nwfpe/single_cpdo.c | 253 |
12 files changed, 2674 insertions, 0 deletions
diff --git a/linux-user/arm/nwfpe/double_cpdo.c b/linux-user/arm/nwfpe/double_cpdo.c new file mode 100644 index 0000000000..b5320c82a3 --- /dev/null +++ b/linux-user/arm/nwfpe/double_cpdo.c @@ -0,0 +1,296 @@ +/* + NetWinder Floating Point Emulator + (c) Rebel.COM, 1998,1999 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "fpa11.h" +#include "softfloat.h" +#include "fpopcode.h" + +float64 float64_exp(float64 Fm); +float64 float64_ln(float64 Fm); +float64 float64_sin(float64 rFm); +float64 float64_cos(float64 rFm); +float64 float64_arcsin(float64 rFm); +float64 float64_arctan(float64 rFm); +float64 float64_log(float64 rFm); +float64 float64_tan(float64 rFm); +float64 float64_arccos(float64 rFm); +float64 float64_pow(float64 rFn,float64 rFm); +float64 float64_pol(float64 rFn,float64 rFm); + +unsigned int DoubleCPDO(const unsigned int opcode) +{ + FPA11 *fpa11 = GET_FPA11(); + float64 rFm, rFn = float64_zero; + unsigned int Fd, Fm, Fn, nRc = 1; + + //printk("DoubleCPDO(0x%08x)\n",opcode); + + Fm = getFm(opcode); + if (CONSTANT_FM(opcode)) + { + rFm = getDoubleConstant(Fm); + } + else + { + switch (fpa11->fType[Fm]) + { + case typeSingle: + rFm = float32_to_float64(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status); + break; + + case typeDouble: + rFm = fpa11->fpreg[Fm].fDouble; + break; + + case typeExtended: + // !! patb + //printk("not implemented! why not?\n"); + //!! ScottB + // should never get here, if extended involved + // then other operand should be promoted then + // ExtendedCPDO called. + break; + + default: return 0; + } + } + + if (!MONADIC_INSTRUCTION(opcode)) + { + Fn = getFn(opcode); + switch (fpa11->fType[Fn]) + { + case typeSingle: + rFn = float32_to_float64(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status); + break; + + case typeDouble: + rFn = fpa11->fpreg[Fn].fDouble; + break; + + default: return 0; + } + } + + Fd = getFd(opcode); + /* !! this switch isn't optimized; better (opcode & MASK_ARITHMETIC_OPCODE)>>24, sort of */ + switch (opcode & MASK_ARITHMETIC_OPCODE) + { + /* dyadic opcodes */ + case ADF_CODE: + fpa11->fpreg[Fd].fDouble = float64_add(rFn,rFm, &fpa11->fp_status); + break; + + case MUF_CODE: + case FML_CODE: + fpa11->fpreg[Fd].fDouble = float64_mul(rFn,rFm, &fpa11->fp_status); + break; + + case SUF_CODE: + fpa11->fpreg[Fd].fDouble = float64_sub(rFn,rFm, &fpa11->fp_status); + break; + + case RSF_CODE: + fpa11->fpreg[Fd].fDouble = float64_sub(rFm,rFn, &fpa11->fp_status); + break; + + case DVF_CODE: + case FDV_CODE: + fpa11->fpreg[Fd].fDouble = float64_div(rFn,rFm, &fpa11->fp_status); + break; + + case RDF_CODE: + case FRD_CODE: + fpa11->fpreg[Fd].fDouble = float64_div(rFm,rFn, &fpa11->fp_status); + break; + +#if 0 + case POW_CODE: + fpa11->fpreg[Fd].fDouble = float64_pow(rFn,rFm); + break; + + case RPW_CODE: + fpa11->fpreg[Fd].fDouble = float64_pow(rFm,rFn); + break; +#endif + + case RMF_CODE: + fpa11->fpreg[Fd].fDouble = float64_rem(rFn,rFm, &fpa11->fp_status); + break; + +#if 0 + case POL_CODE: + fpa11->fpreg[Fd].fDouble = float64_pol(rFn,rFm); + break; +#endif + + /* monadic opcodes */ + case MVF_CODE: + fpa11->fpreg[Fd].fDouble = rFm; + break; + + case MNF_CODE: + { + unsigned int *p = (unsigned int*)&rFm; +#ifdef WORDS_BIGENDIAN + p[0] ^= 0x80000000; +#else + p[1] ^= 0x80000000; +#endif + fpa11->fpreg[Fd].fDouble = rFm; + } + break; + + case ABS_CODE: + { + unsigned int *p = (unsigned int*)&rFm; +#ifdef WORDS_BIGENDIAN + p[0] &= 0x7fffffff; +#else + p[1] &= 0x7fffffff; +#endif + fpa11->fpreg[Fd].fDouble = rFm; + } + break; + + case RND_CODE: + case URD_CODE: + fpa11->fpreg[Fd].fDouble = float64_round_to_int(rFm, &fpa11->fp_status); + break; + + case SQT_CODE: + fpa11->fpreg[Fd].fDouble = float64_sqrt(rFm, &fpa11->fp_status); + break; + +#if 0 + case LOG_CODE: + fpa11->fpreg[Fd].fDouble = float64_log(rFm); + break; + + case LGN_CODE: + fpa11->fpreg[Fd].fDouble = float64_ln(rFm); + break; + + case EXP_CODE: + fpa11->fpreg[Fd].fDouble = float64_exp(rFm); + break; + + case SIN_CODE: + fpa11->fpreg[Fd].fDouble = float64_sin(rFm); + break; + + case COS_CODE: + fpa11->fpreg[Fd].fDouble = float64_cos(rFm); + break; + + case TAN_CODE: + fpa11->fpreg[Fd].fDouble = float64_tan(rFm); + break; + + case ASN_CODE: + fpa11->fpreg[Fd].fDouble = float64_arcsin(rFm); + break; + + case ACS_CODE: + fpa11->fpreg[Fd].fDouble = float64_arccos(rFm); + break; + + case ATN_CODE: + fpa11->fpreg[Fd].fDouble = float64_arctan(rFm); + break; +#endif + + case NRM_CODE: + break; + + default: + { + nRc = 0; + } + } + + if (0 != nRc) fpa11->fType[Fd] = typeDouble; + return nRc; +} + +#if 0 +float64 float64_exp(float64 rFm) +{ + return rFm; +//series +} + +float64 float64_ln(float64 rFm) +{ + return rFm; +//series +} + +float64 float64_sin(float64 rFm) +{ + return rFm; +//series +} + +float64 float64_cos(float64 rFm) +{ + return rFm; + //series +} + +#if 0 +float64 float64_arcsin(float64 rFm) +{ +//series +} + +float64 float64_arctan(float64 rFm) +{ + //series +} +#endif + +float64 float64_log(float64 rFm) +{ + return float64_div(float64_ln(rFm),getDoubleConstant(7)); +} + +float64 float64_tan(float64 rFm) +{ + return float64_div(float64_sin(rFm),float64_cos(rFm)); +} + +float64 float64_arccos(float64 rFm) +{ +return rFm; + //return float64_sub(halfPi,float64_arcsin(rFm)); +} + +float64 float64_pow(float64 rFn,float64 rFm) +{ + return float64_exp(float64_mul(rFm,float64_ln(rFn))); +} + +float64 float64_pol(float64 rFn,float64 rFm) +{ + return float64_arctan(float64_div(rFn,rFm)); +} +#endif diff --git a/linux-user/arm/nwfpe/extended_cpdo.c b/linux-user/arm/nwfpe/extended_cpdo.c new file mode 100644 index 0000000000..05e32b0734 --- /dev/null +++ b/linux-user/arm/nwfpe/extended_cpdo.c @@ -0,0 +1,273 @@ +/* + NetWinder Floating Point Emulator + (c) Rebel.COM, 1998,1999 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "fpa11.h" +#include "softfloat.h" +#include "fpopcode.h" + +floatx80 floatx80_exp(floatx80 Fm); +floatx80 floatx80_ln(floatx80 Fm); +floatx80 floatx80_sin(floatx80 rFm); +floatx80 floatx80_cos(floatx80 rFm); +floatx80 floatx80_arcsin(floatx80 rFm); +floatx80 floatx80_arctan(floatx80 rFm); +floatx80 floatx80_log(floatx80 rFm); +floatx80 floatx80_tan(floatx80 rFm); +floatx80 floatx80_arccos(floatx80 rFm); +floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm); +floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm); + +unsigned int ExtendedCPDO(const unsigned int opcode) +{ + FPA11 *fpa11 = GET_FPA11(); + floatx80 rFm, rFn; + unsigned int Fd, Fm, Fn, nRc = 1; + + //printk("ExtendedCPDO(0x%08x)\n",opcode); + + Fm = getFm(opcode); + if (CONSTANT_FM(opcode)) + { + rFm = getExtendedConstant(Fm); + } + else + { + switch (fpa11->fType[Fm]) + { + case typeSingle: + rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status); + break; + + case typeDouble: + rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status); + break; + + case typeExtended: + rFm = fpa11->fpreg[Fm].fExtended; + break; + + default: return 0; + } + } + + if (!MONADIC_INSTRUCTION(opcode)) + { + Fn = getFn(opcode); + switch (fpa11->fType[Fn]) + { + case typeSingle: + rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status); + break; + + case typeDouble: + rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status); + break; + + case typeExtended: + rFn = fpa11->fpreg[Fn].fExtended; + break; + + default: return 0; + } + } + + Fd = getFd(opcode); + switch (opcode & MASK_ARITHMETIC_OPCODE) + { + /* dyadic opcodes */ + case ADF_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_add(rFn,rFm, &fpa11->fp_status); + break; + + case MUF_CODE: + case FML_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_mul(rFn,rFm, &fpa11->fp_status); + break; + + case SUF_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_sub(rFn,rFm, &fpa11->fp_status); + break; + + case RSF_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_sub(rFm,rFn, &fpa11->fp_status); + break; + + case DVF_CODE: + case FDV_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_div(rFn,rFm, &fpa11->fp_status); + break; + + case RDF_CODE: + case FRD_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_div(rFm,rFn, &fpa11->fp_status); + break; + +#if 0 + case POW_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_pow(rFn,rFm); + break; + + case RPW_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_pow(rFm,rFn); + break; +#endif + + case RMF_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_rem(rFn,rFm, &fpa11->fp_status); + break; + +#if 0 + case POL_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_pol(rFn,rFm); + break; +#endif + + /* monadic opcodes */ + case MVF_CODE: + fpa11->fpreg[Fd].fExtended = rFm; + break; + + case MNF_CODE: + rFm.high ^= 0x8000; + fpa11->fpreg[Fd].fExtended = rFm; + break; + + case ABS_CODE: + rFm.high &= 0x7fff; + fpa11->fpreg[Fd].fExtended = rFm; + break; + + case RND_CODE: + case URD_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_round_to_int(rFm, &fpa11->fp_status); + break; + + case SQT_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_sqrt(rFm, &fpa11->fp_status); + break; + +#if 0 + case LOG_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_log(rFm); + break; + + case LGN_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_ln(rFm); + break; + + case EXP_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_exp(rFm); + break; + + case SIN_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_sin(rFm); + break; + + case COS_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_cos(rFm); + break; + + case TAN_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_tan(rFm); + break; + + case ASN_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_arcsin(rFm); + break; + + case ACS_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_arccos(rFm); + break; + + case ATN_CODE: + fpa11->fpreg[Fd].fExtended = floatx80_arctan(rFm); + break; +#endif + + case NRM_CODE: + break; + + default: + { + nRc = 0; + } + } + + if (0 != nRc) fpa11->fType[Fd] = typeExtended; + return nRc; +} + +#if 0 +floatx80 floatx80_exp(floatx80 Fm) +{ +//series +} + +floatx80 floatx80_ln(floatx80 Fm) +{ +//series +} + +floatx80 floatx80_sin(floatx80 rFm) +{ +//series +} + +floatx80 floatx80_cos(floatx80 rFm) +{ +//series +} + +floatx80 floatx80_arcsin(floatx80 rFm) +{ +//series +} + +floatx80 floatx80_arctan(floatx80 rFm) +{ + //series +} + +floatx80 floatx80_log(floatx80 rFm) +{ + return floatx80_div(floatx80_ln(rFm),getExtendedConstant(7)); +} + +floatx80 floatx80_tan(floatx80 rFm) +{ + return floatx80_div(floatx80_sin(rFm),floatx80_cos(rFm)); +} + +floatx80 floatx80_arccos(floatx80 rFm) +{ + //return floatx80_sub(halfPi,floatx80_arcsin(rFm)); +} + +floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm) +{ + return floatx80_exp(floatx80_mul(rFm,floatx80_ln(rFn))); +} + +floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm) +{ + return floatx80_arctan(floatx80_div(rFn,rFm)); +} +#endif diff --git a/linux-user/arm/nwfpe/fpa11.c b/linux-user/arm/nwfpe/fpa11.c new file mode 100644 index 0000000000..626a87acee --- /dev/null +++ b/linux-user/arm/nwfpe/fpa11.c @@ -0,0 +1,244 @@ +/* + NetWinder Floating Point Emulator + (c) Rebel.COM, 1998,1999 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "fpa11.h" + +#include "fpopcode.h" + +//#include "fpmodule.h" +//#include "fpmodule.inl" + +//#include <asm/system.h> + +#include <stdio.h> + +/* forward declarations */ +unsigned int EmulateCPDO(const unsigned int); +unsigned int EmulateCPDT(const unsigned int); +unsigned int EmulateCPRT(const unsigned int); + +FPA11* qemufpa=0; +CPUARMState* user_registers; + +/* Reset the FPA11 chip. Called to initialize and reset the emulator. */ +void resetFPA11(void) +{ + int i; + FPA11 *fpa11 = GET_FPA11(); + + /* initialize the register type array */ + for (i=0;i<=7;i++) + { + fpa11->fType[i] = typeNone; + } + + /* FPSR: set system id to FP_EMULATOR, set AC, clear all other bits */ + fpa11->fpsr = FP_EMULATOR | BIT_AC; + + /* FPCR: set SB, AB and DA bits, clear all others */ +#if MAINTAIN_FPCR + fpa11->fpcr = MASK_RESET; +#endif +} + +void SetRoundingMode(const unsigned int opcode) +{ + int rounding_mode; + FPA11 *fpa11 = GET_FPA11(); + +#if MAINTAIN_FPCR + fpa11->fpcr &= ~MASK_ROUNDING_MODE; +#endif + switch (opcode & MASK_ROUNDING_MODE) + { + default: + case ROUND_TO_NEAREST: + rounding_mode = float_round_nearest_even; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_TO_NEAREST; +#endif + break; + + case ROUND_TO_PLUS_INFINITY: + rounding_mode = float_round_up; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_TO_PLUS_INFINITY; +#endif + break; + + case ROUND_TO_MINUS_INFINITY: + rounding_mode = float_round_down; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_TO_MINUS_INFINITY; +#endif + break; + + case ROUND_TO_ZERO: + rounding_mode = float_round_to_zero; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_TO_ZERO; +#endif + break; + } + set_float_rounding_mode(rounding_mode, &fpa11->fp_status); +} + +void SetRoundingPrecision(const unsigned int opcode) +{ + int rounding_precision; + FPA11 *fpa11 = GET_FPA11(); +#if MAINTAIN_FPCR + fpa11->fpcr &= ~MASK_ROUNDING_PRECISION; +#endif + switch (opcode & MASK_ROUNDING_PRECISION) + { + case ROUND_SINGLE: + rounding_precision = 32; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_SINGLE; +#endif + break; + + case ROUND_DOUBLE: + rounding_precision = 64; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_DOUBLE; +#endif + break; + + case ROUND_EXTENDED: + rounding_precision = 80; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_EXTENDED; +#endif + break; + + default: rounding_precision = 80; + } + set_floatx80_rounding_precision(rounding_precision, &fpa11->fp_status); +} + +/* Emulate the instruction in the opcode. */ +/* ??? This is not thread safe. */ +unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa, CPUARMState* qregs) +{ + unsigned int nRc = 0; +// unsigned long flags; + FPA11 *fpa11; +// save_flags(flags); sti(); + + qemufpa=qfpa; + user_registers=qregs; + +#if 0 + fprintf(stderr,"emulating FP insn 0x%08x, PC=0x%08x\n", + opcode, qregs[REG_PC]); +#endif + fpa11 = GET_FPA11(); + + if (fpa11->initflag == 0) /* good place for __builtin_expect */ + { + resetFPA11(); + SetRoundingMode(ROUND_TO_NEAREST); + SetRoundingPrecision(ROUND_EXTENDED); + fpa11->initflag = 1; + } + + set_float_exception_flags(0, &fpa11->fp_status); + + if (TEST_OPCODE(opcode,MASK_CPRT)) + { + //fprintf(stderr,"emulating CPRT\n"); + /* Emulate conversion opcodes. */ + /* Emulate register transfer opcodes. */ + /* Emulate comparison opcodes. */ + nRc = EmulateCPRT(opcode); + } + else if (TEST_OPCODE(opcode,MASK_CPDO)) + { + //fprintf(stderr,"emulating CPDO\n"); + /* Emulate monadic arithmetic opcodes. */ + /* Emulate dyadic arithmetic opcodes. */ + nRc = EmulateCPDO(opcode); + } + else if (TEST_OPCODE(opcode,MASK_CPDT)) + { + //fprintf(stderr,"emulating CPDT\n"); + /* Emulate load/store opcodes. */ + /* Emulate load/store multiple opcodes. */ + nRc = EmulateCPDT(opcode); + } + else + { + /* Invalid instruction detected. Return FALSE. */ + nRc = 0; + } + +// restore_flags(flags); + if(nRc == 1 && get_float_exception_flags(&fpa11->fp_status)) + { + //printf("fef 0x%x\n",float_exception_flags); + nRc=-get_float_exception_flags(&fpa11->fp_status); + } + + //printf("returning %d\n",nRc); + return(nRc); +} + +#if 0 +unsigned int EmulateAll1(unsigned int opcode) +{ + switch ((opcode >> 24) & 0xf) + { + case 0xc: + case 0xd: + if ((opcode >> 20) & 0x1) + { + switch ((opcode >> 8) & 0xf) + { + case 0x1: return PerformLDF(opcode); break; + case 0x2: return PerformLFM(opcode); break; + default: return 0; + } + } + else + { + switch ((opcode >> 8) & 0xf) + { + case 0x1: return PerformSTF(opcode); break; + case 0x2: return PerformSFM(opcode); break; + default: return 0; + } + } + break; + + case 0xe: + if (opcode & 0x10) + return EmulateCPDO(opcode); + else + return EmulateCPRT(opcode); + break; + + default: return 0; + } +} +#endif + diff --git a/linux-user/arm/nwfpe/fpa11.h b/linux-user/arm/nwfpe/fpa11.h new file mode 100644 index 0000000000..4fc0b3b886 --- /dev/null +++ b/linux-user/arm/nwfpe/fpa11.h @@ -0,0 +1,122 @@ +/* + NetWinder Floating Point Emulator + (c) Rebel.com, 1998-1999 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __FPA11_H__ +#define __FPA11_H__ + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +#include <cpu.h> + +#define GET_FPA11() (qemufpa) + +/* + * The processes registers are always at the very top of the 8K + * stack+task struct. Use the same method as 'current' uses to + * reach them. + */ +extern CPUARMState *user_registers; + +#define GET_USERREG() (user_registers) + +/* Need task_struct */ +//#include <linux/sched.h> + +/* includes */ +#include "fpsr.h" /* FP control and status register definitions */ +#include "softfloat.h" + +#define typeNone 0x00 +#define typeSingle 0x01 +#define typeDouble 0x02 +#define typeExtended 0x03 + +/* + * This must be no more and no less than 12 bytes. + */ +typedef union tagFPREG { + floatx80 fExtended; + float64 fDouble; + float32 fSingle; +} FPREG; + +/* + * FPA11 device model. + * + * This structure is exported to user space. Do not re-order. + * Only add new stuff to the end, and do not change the size of + * any element. Elements of this structure are used by user + * space, and must match struct user_fp in include/asm-arm/user.h. + * We include the byte offsets below for documentation purposes. + * + * The size of this structure and FPREG are checked by fpmodule.c + * on initialisation. If the rules have been broken, NWFPE will + * not initialise. + */ +typedef struct tagFPA11 { +/* 0 */ FPREG fpreg[8]; /* 8 floating point registers */ +/* 96 */ FPSR fpsr; /* floating point status register */ +/* 100 */ FPCR fpcr; /* floating point control register */ +/* 104 */ unsigned char fType[8]; /* type of floating point value held in + floating point registers. One of none + single, double or extended. */ +/* 112 */ int initflag; /* this is special. The kernel guarantees + to set it to 0 when a thread is launched, + so we can use it to detect whether this + instance of the emulator needs to be + initialised. */ + float_status fp_status; /* QEMU float emulator status */ +} FPA11; + +extern FPA11* qemufpa; + +extern void resetFPA11(void); +extern void SetRoundingMode(const unsigned int); +extern void SetRoundingPrecision(const unsigned int); + +static inline unsigned int readRegister(unsigned int reg) +{ + return (user_registers->regs[(reg)]); +} + +static inline void writeRegister(unsigned int x, unsigned int y) +{ +#if 0 + printf("writing %d to r%d\n",y,x); +#endif + user_registers->regs[(x)]=(y); +} + +static inline void writeConditionCodes(unsigned int x) +{ + cpsr_write(user_registers,x,CPSR_NZCV); +} + +#define REG_PC 15 + +unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa, CPUARMState* qregs); + +/* included only for get_user/put_user macros */ +#include "qemu.h" + +#endif diff --git a/linux-user/arm/nwfpe/fpa11.inl b/linux-user/arm/nwfpe/fpa11.inl new file mode 100644 index 0000000000..7183ec96a6 --- /dev/null +++ b/linux-user/arm/nwfpe/fpa11.inl @@ -0,0 +1,51 @@ +/* + NetWinder Floating Point Emulator + (c) Rebel.COM, 1998,1999 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "fpa11.h" + +/* Read and write floating point status register */ +static inline unsigned int readFPSR(void) +{ + FPA11 *fpa11 = GET_FPA11(); + return(fpa11->fpsr); +} + +static inline void writeFPSR(FPSR reg) +{ + FPA11 *fpa11 = GET_FPA11(); + /* the sysid byte in the status register is readonly */ + fpa11->fpsr = (fpa11->fpsr & MASK_SYSID) | (reg & ~MASK_SYSID); +} + +/* Read and write floating point control register */ +static inline FPCR readFPCR(void) +{ + FPA11 *fpa11 = GET_FPA11(); + /* clear SB, AB and DA bits before returning FPCR */ + return(fpa11->fpcr & ~MASK_RFC); +} + +static inline void writeFPCR(FPCR reg) +{ + FPA11 *fpa11 = GET_FPA11(); + fpa11->fpcr &= ~MASK_WFC; /* clear SB, AB and DA bits */ + fpa11->fpcr |= (reg & MASK_WFC); /* write SB, AB and DA bits */ +} diff --git a/linux-user/arm/nwfpe/fpa11_cpdo.c b/linux-user/arm/nwfpe/fpa11_cpdo.c new file mode 100644 index 0000000000..777963728f --- /dev/null +++ b/linux-user/arm/nwfpe/fpa11_cpdo.c @@ -0,0 +1,117 @@ +/* + NetWinder Floating Point Emulator + (c) Rebel.COM, 1998,1999 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "fpa11.h" +#include "fpopcode.h" + +unsigned int SingleCPDO(const unsigned int opcode); +unsigned int DoubleCPDO(const unsigned int opcode); +unsigned int ExtendedCPDO(const unsigned int opcode); + +unsigned int EmulateCPDO(const unsigned int opcode) +{ + FPA11 *fpa11 = GET_FPA11(); + unsigned int Fd, nType, nDest, nRc = 1; + + //printk("EmulateCPDO(0x%08x)\n",opcode); + + /* Get the destination size. If not valid let Linux perform + an invalid instruction trap. */ + nDest = getDestinationSize(opcode); + if (typeNone == nDest) return 0; + + SetRoundingMode(opcode); + + /* Compare the size of the operands in Fn and Fm. + Choose the largest size and perform operations in that size, + in order to make use of all the precision of the operands. + If Fm is a constant, we just grab a constant of a size + matching the size of the operand in Fn. */ + if (MONADIC_INSTRUCTION(opcode)) + nType = nDest; + else + nType = fpa11->fType[getFn(opcode)]; + + if (!CONSTANT_FM(opcode)) + { + register unsigned int Fm = getFm(opcode); + if (nType < fpa11->fType[Fm]) + { + nType = fpa11->fType[Fm]; + } + } + + switch (nType) + { + case typeSingle : nRc = SingleCPDO(opcode); break; + case typeDouble : nRc = DoubleCPDO(opcode); break; + case typeExtended : nRc = ExtendedCPDO(opcode); break; + default : nRc = 0; + } + + /* If the operation succeeded, check to see if the result in the + destination register is the correct size. If not force it + to be. */ + Fd = getFd(opcode); + nType = fpa11->fType[Fd]; + if ((0 != nRc) && (nDest != nType)) + { + switch (nDest) + { + case typeSingle: + { + if (typeDouble == nType) + fpa11->fpreg[Fd].fSingle = + float64_to_float32(fpa11->fpreg[Fd].fDouble, &fpa11->fp_status); + else + fpa11->fpreg[Fd].fSingle = + floatx80_to_float32(fpa11->fpreg[Fd].fExtended, &fpa11->fp_status); + } + break; + + case typeDouble: + { + if (typeSingle == nType) + fpa11->fpreg[Fd].fDouble = + float32_to_float64(fpa11->fpreg[Fd].fSingle, &fpa11->fp_status); + else + fpa11->fpreg[Fd].fDouble = + floatx80_to_float64(fpa11->fpreg[Fd].fExtended, &fpa11->fp_status); + } + break; + + case typeExtended: + { + if (typeSingle == nType) + fpa11->fpreg[Fd].fExtended = + float32_to_floatx80(fpa11->fpreg[Fd].fSingle, &fpa11->fp_status); + else + fpa11->fpreg[Fd].fExtended = + float64_to_floatx80(fpa11->fpreg[Fd].fDouble, &fpa11->fp_status); + } + break; + } + + fpa11->fType[Fd] = nDest; + } + + return nRc; +} diff --git a/linux-user/arm/nwfpe/fpa11_cpdt.c b/linux-user/arm/nwfpe/fpa11_cpdt.c new file mode 100644 index 0000000000..41877dfe82 --- /dev/null +++ b/linux-user/arm/nwfpe/fpa11_cpdt.c @@ -0,0 +1,386 @@ +/* + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#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,const unsigned int *pMem) +{ + target_ulong addr = (target_ulong)(long)pMem; + 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,const unsigned int *pMem) +{ + target_ulong addr = (target_ulong)(long)pMem; + FPA11 *fpa11 = GET_FPA11(); + unsigned int *p; + p = (unsigned int*)&fpa11->fpreg[Fn].fDouble; + fpa11->fType[Fn] = typeDouble; +#ifdef 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,const unsigned int *pMem) +{ + target_ulong addr = (target_ulong)(long)pMem; + 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,const unsigned int *pMem) +{ + target_ulong addr = (target_ulong)(long)pMem; + 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,unsigned int *pMem) +{ + target_ulong addr = (target_ulong)(long)pMem; + 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,unsigned int *pMem) +{ + target_ulong addr = (target_ulong)(long)pMem; + 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 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,unsigned int *pMem) +{ + target_ulong addr = (target_ulong)(long)pMem; + 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,unsigned int *pMem) +{ + target_ulong addr = (target_ulong)(long)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_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; + } +} + +unsigned int PerformLDF(const unsigned int opcode) +{ + unsigned int *pBase, *pAddress, *pFinal, nRc = 1, + write_back = WRITE_BACK(opcode); + + //printk("PerformLDF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode)); + + pBase = (unsigned int*)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; + case TRANSFER_EXTENDED: loadExtended(getFd(opcode),pAddress); break; + default: nRc = 0; + } + + if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal); + return nRc; +} + +unsigned int PerformSTF(const unsigned int opcode) +{ + unsigned int *pBase, *pAddress, *pFinal, nRc = 1, + write_back = WRITE_BACK(opcode); + + //printk("PerformSTF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode)); + SetRoundingMode(ROUND_TO_NEAREST); + + pBase = (unsigned int*)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(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; +} + +unsigned int PerformLFM(const unsigned int opcode) +{ + unsigned int i, Fd, *pBase, *pAddress, *pFinal, + write_back = WRITE_BACK(opcode); + + pBase = (unsigned int*)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 int)pFinal); + return 1; +} + +unsigned int PerformSFM(const unsigned int opcode) +{ + unsigned int i, Fd, *pBase, *pAddress, *pFinal, + write_back = WRITE_BACK(opcode); + + pBase = (unsigned int*)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 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 diff --git a/linux-user/arm/nwfpe/fpa11_cprt.c b/linux-user/arm/nwfpe/fpa11_cprt.c new file mode 100644 index 0000000000..04eae8c6fc --- /dev/null +++ b/linux-user/arm/nwfpe/fpa11_cprt.c @@ -0,0 +1,286 @@ +/* + NetWinder Floating Point Emulator + (c) Rebel.COM, 1998,1999 + (c) Philip Blundell, 1999 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "fpa11.h" +#include "softfloat.h" +#include "fpopcode.h" +#include "fpa11.inl" +//#include "fpmodule.h" +//#include "fpmodule.inl" + +void SetRoundingMode(const unsigned int opcode); + +unsigned int PerformFLT(const unsigned int opcode); +unsigned int PerformFIX(const unsigned int opcode); + +static unsigned int +PerformComparison(const unsigned int opcode); + +unsigned int EmulateCPRT(const unsigned int opcode) +{ + unsigned int nRc = 1; + + //printk("EmulateCPRT(0x%08x)\n",opcode); + + if (opcode & 0x800000) + { + /* This is some variant of a comparison (PerformComparison will + sort out which one). Since most of the other CPRT + instructions are oddball cases of some sort or other it makes + sense to pull this out into a fast path. */ + return PerformComparison(opcode); + } + + /* Hint to GCC that we'd like a jump table rather than a load of CMPs */ + switch ((opcode & 0x700000) >> 20) + { + case FLT_CODE >> 20: nRc = PerformFLT(opcode); break; + case FIX_CODE >> 20: nRc = PerformFIX(opcode); break; + + case WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break; + case RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break; + +#if 0 /* We currently have no use for the FPCR, so there's no point + in emulating it. */ + case WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode))); + case RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break; +#endif + + default: nRc = 0; + } + + return nRc; +} + +unsigned int PerformFLT(const unsigned int opcode) +{ + FPA11 *fpa11 = GET_FPA11(); + + unsigned int nRc = 1; + SetRoundingMode(opcode); + + switch (opcode & MASK_ROUNDING_PRECISION) + { + case ROUND_SINGLE: + { + fpa11->fType[getFn(opcode)] = typeSingle; + fpa11->fpreg[getFn(opcode)].fSingle = + int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status); + } + break; + + case ROUND_DOUBLE: + { + fpa11->fType[getFn(opcode)] = typeDouble; + fpa11->fpreg[getFn(opcode)].fDouble = + int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status); + } + break; + + case ROUND_EXTENDED: + { + fpa11->fType[getFn(opcode)] = typeExtended; + fpa11->fpreg[getFn(opcode)].fExtended = + int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status); + } + break; + + default: nRc = 0; + } + + return nRc; +} + +unsigned int PerformFIX(const unsigned int opcode) +{ + FPA11 *fpa11 = GET_FPA11(); + unsigned int nRc = 1; + unsigned int Fn = getFm(opcode); + + SetRoundingMode(opcode); + + switch (fpa11->fType[Fn]) + { + case typeSingle: + { + writeRegister(getRd(opcode), + float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status)); + } + break; + + case typeDouble: + { + //printf("F%d is 0x%" PRIx64 "\n",Fn,fpa11->fpreg[Fn].fDouble); + writeRegister(getRd(opcode), + float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status)); + } + break; + + case typeExtended: + { + writeRegister(getRd(opcode), + floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status)); + } + break; + + default: nRc = 0; + } + + return nRc; +} + + +static unsigned int __inline__ +PerformComparisonOperation(floatx80 Fn, floatx80 Fm) +{ + FPA11 *fpa11 = GET_FPA11(); + unsigned int flags = 0; + + /* test for less than condition */ + if (floatx80_lt(Fn,Fm, &fpa11->fp_status)) + { + flags |= CC_NEGATIVE; + } + + /* test for equal condition */ + if (floatx80_eq(Fn,Fm, &fpa11->fp_status)) + { + flags |= CC_ZERO; + } + + /* test for greater than or equal condition */ + if (floatx80_lt(Fm,Fn, &fpa11->fp_status)) + { + flags |= CC_CARRY; + } + + writeConditionCodes(flags); + return 1; +} + +/* This instruction sets the flags N, Z, C, V in the FPSR. */ + +static unsigned int PerformComparison(const unsigned int opcode) +{ + FPA11 *fpa11 = GET_FPA11(); + unsigned int Fn, Fm; + floatx80 rFn, rFm; + int e_flag = opcode & 0x400000; /* 1 if CxFE */ + int n_flag = opcode & 0x200000; /* 1 if CNxx */ + unsigned int flags = 0; + + //printk("PerformComparison(0x%08x)\n",opcode); + + Fn = getFn(opcode); + Fm = getFm(opcode); + + /* Check for unordered condition and convert all operands to 80-bit + format. + ?? Might be some mileage in avoiding this conversion if possible. + Eg, if both operands are 32-bit, detect this and do a 32-bit + comparison (cheaper than an 80-bit one). */ + switch (fpa11->fType[Fn]) + { + case typeSingle: + //printk("single.\n"); + if (float32_is_nan(fpa11->fpreg[Fn].fSingle)) + goto unordered; + rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status); + break; + + case typeDouble: + //printk("double.\n"); + if (float64_is_nan(fpa11->fpreg[Fn].fDouble)) + goto unordered; + rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status); + break; + + case typeExtended: + //printk("extended.\n"); + if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended)) + goto unordered; + rFn = fpa11->fpreg[Fn].fExtended; + break; + + default: return 0; + } + + if (CONSTANT_FM(opcode)) + { + //printk("Fm is a constant: #%d.\n",Fm); + rFm = getExtendedConstant(Fm); + if (floatx80_is_nan(rFm)) + goto unordered; + } + else + { + //printk("Fm = r%d which contains a ",Fm); + switch (fpa11->fType[Fm]) + { + case typeSingle: + //printk("single.\n"); + if (float32_is_nan(fpa11->fpreg[Fm].fSingle)) + goto unordered; + rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status); + break; + + case typeDouble: + //printk("double.\n"); + if (float64_is_nan(fpa11->fpreg[Fm].fDouble)) + goto unordered; + rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status); + break; + + case typeExtended: + //printk("extended.\n"); + if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended)) + goto unordered; + rFm = fpa11->fpreg[Fm].fExtended; + break; + + default: return 0; + } + } + + if (n_flag) + { + rFm.high ^= 0x8000; + } + + return PerformComparisonOperation(rFn,rFm); + + unordered: + /* ?? The FPA data sheet is pretty vague about this, in particular + about whether the non-E comparisons can ever raise exceptions. + This implementation is based on a combination of what it says in + the data sheet, observation of how the Acorn emulator actually + behaves (and how programs expect it to) and guesswork. */ + flags |= CC_OVERFLOW; + flags &= ~(CC_ZERO | CC_NEGATIVE); + + if (BIT_AC & readFPSR()) flags |= CC_CARRY; + + if (e_flag) float_raise(float_flag_invalid, &fpa11->fp_status); + + writeConditionCodes(flags); + return 1; +} diff --git a/linux-user/arm/nwfpe/fpopcode.c b/linux-user/arm/nwfpe/fpopcode.c new file mode 100644 index 0000000000..a733a1d1de --- /dev/null +++ b/linux-user/arm/nwfpe/fpopcode.c @@ -0,0 +1,148 @@ +/* + NetWinder Floating Point Emulator + (c) Rebel.COM, 1998,1999 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "fpa11.h" +#include "softfloat.h" +#include "fpopcode.h" +#include "fpsr.h" +//#include "fpmodule.h" +//#include "fpmodule.inl" + +const floatx80 floatx80Constant[] = { + { 0x0000000000000000ULL, 0x0000}, /* extended 0.0 */ + { 0x8000000000000000ULL, 0x3fff}, /* extended 1.0 */ + { 0x8000000000000000ULL, 0x4000}, /* extended 2.0 */ + { 0xc000000000000000ULL, 0x4000}, /* extended 3.0 */ + { 0x8000000000000000ULL, 0x4001}, /* extended 4.0 */ + { 0xa000000000000000ULL, 0x4001}, /* extended 5.0 */ + { 0x8000000000000000ULL, 0x3ffe}, /* extended 0.5 */ + { 0xa000000000000000ULL, 0x4002} /* extended 10.0 */ +}; + +const float64 float64Constant[] = { + 0x0000000000000000ULL, /* double 0.0 */ + 0x3ff0000000000000ULL, /* double 1.0 */ + 0x4000000000000000ULL, /* double 2.0 */ + 0x4008000000000000ULL, /* double 3.0 */ + 0x4010000000000000ULL, /* double 4.0 */ + 0x4014000000000000ULL, /* double 5.0 */ + 0x3fe0000000000000ULL, /* double 0.5 */ + 0x4024000000000000ULL /* double 10.0 */ +}; + +const float32 float32Constant[] = { + 0x00000000, /* single 0.0 */ + 0x3f800000, /* single 1.0 */ + 0x40000000, /* single 2.0 */ + 0x40400000, /* single 3.0 */ + 0x40800000, /* single 4.0 */ + 0x40a00000, /* single 5.0 */ + 0x3f000000, /* single 0.5 */ + 0x41200000 /* single 10.0 */ +}; + +unsigned int getTransferLength(const unsigned int opcode) +{ + unsigned int nRc; + + switch (opcode & MASK_TRANSFER_LENGTH) + { + case 0x00000000: nRc = 1; break; /* single precision */ + case 0x00008000: nRc = 2; break; /* double precision */ + case 0x00400000: nRc = 3; break; /* extended precision */ + default: nRc = 0; + } + + return(nRc); +} + +unsigned int getRegisterCount(const unsigned int opcode) +{ + unsigned int nRc; + + switch (opcode & MASK_REGISTER_COUNT) + { + case 0x00000000: nRc = 4; break; + case 0x00008000: nRc = 1; break; + case 0x00400000: nRc = 2; break; + case 0x00408000: nRc = 3; break; + default: nRc = 0; + } + + return(nRc); +} + +unsigned int getRoundingPrecision(const unsigned int opcode) +{ + unsigned int nRc; + + switch (opcode & MASK_ROUNDING_PRECISION) + { + case 0x00000000: nRc = 1; break; + case 0x00000080: nRc = 2; break; + case 0x00080000: nRc = 3; break; + default: nRc = 0; + } + + return(nRc); +} + +unsigned int getDestinationSize(const unsigned int opcode) +{ + unsigned int nRc; + + switch (opcode & MASK_DESTINATION_SIZE) + { + case 0x00000000: nRc = typeSingle; break; + case 0x00000080: nRc = typeDouble; break; + case 0x00080000: nRc = typeExtended; break; + default: nRc = typeNone; + } + + return(nRc); +} + +/* condition code lookup table + index into the table is test code: EQ, NE, ... LT, GT, AL, NV + bit position in short is condition code: NZCV */ +static const unsigned short aCC[16] = { + 0xF0F0, // EQ == Z set + 0x0F0F, // NE + 0xCCCC, // CS == C set + 0x3333, // CC + 0xFF00, // MI == N set + 0x00FF, // PL + 0xAAAA, // VS == V set + 0x5555, // VC + 0x0C0C, // HI == C set && Z clear + 0xF3F3, // LS == C clear || Z set + 0xAA55, // GE == (N==V) + 0x55AA, // LT == (N!=V) + 0x0A05, // GT == (!Z && (N==V)) + 0xF5FA, // LE == (Z || (N!=V)) + 0xFFFF, // AL always + 0 // NV +}; + +unsigned int checkCondition(const unsigned int opcode, const unsigned int ccodes) +{ + return (aCC[opcode>>28] >> (ccodes>>28)) & 1; +} diff --git a/linux-user/arm/nwfpe/fpopcode.h b/linux-user/arm/nwfpe/fpopcode.h new file mode 100644 index 0000000000..6c51067b6c --- /dev/null +++ b/linux-user/arm/nwfpe/fpopcode.h @@ -0,0 +1,390 @@ +/* + NetWinder Floating Point Emulator + (c) Rebel.COM, 1998,1999 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __FPOPCODE_H__ +#define __FPOPCODE_H__ + +/* +ARM Floating Point Instruction Classes +| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +|c o n d|1 1 0 P|U|u|W|L| Rn |v| Fd |0|0|0|1| o f f s e t | CPDT +|c o n d|1 1 0 P|U|w|W|L| Rn |x| Fd |0|0|0|1| o f f s e t | CPDT +| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +|c o n d|1 1 1 0|a|b|c|d|e| Fn |j| Fd |0|0|0|1|f|g|h|0|i| Fm | CPDO +|c o n d|1 1 1 0|a|b|c|L|e| Fn | Rd |0|0|0|1|f|g|h|1|i| Fm | CPRT +|c o n d|1 1 1 0|a|b|c|1|e| Fn |1|1|1|1|0|0|0|1|f|g|h|1|i| Fm | comparisons +| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + +CPDT data transfer instructions + LDF, STF, LFM, SFM + +CPDO dyadic arithmetic instructions + ADF, MUF, SUF, RSF, DVF, RDF, + POW, RPW, RMF, FML, FDV, FRD, POL + +CPDO monadic arithmetic instructions + MVF, MNF, ABS, RND, SQT, LOG, LGN, EXP, + SIN, COS, TAN, ASN, ACS, ATN, URD, NRM + +CPRT joint arithmetic/data transfer instructions + FIX (arithmetic followed by load/store) + FLT (load/store followed by arithmetic) + CMF, CNF CMFE, CNFE (comparisons) + WFS, RFS (write/read floating point status register) + WFC, RFC (write/read floating point control register) + +cond condition codes +P pre/post index bit: 0 = postindex, 1 = preindex +U up/down bit: 0 = stack grows down, 1 = stack grows up +W write back bit: 1 = update base register (Rn) +L load/store bit: 0 = store, 1 = load +Rn base register +Rd destination/source register +Fd floating point destination register +Fn floating point source register +Fm floating point source register or floating point constant + +uv transfer length (TABLE 1) +wx register count (TABLE 2) +abcd arithmetic opcode (TABLES 3 & 4) +ef destination size (rounding precision) (TABLE 5) +gh rounding mode (TABLE 6) +j dyadic/monadic bit: 0 = dyadic, 1 = monadic +i constant bit: 1 = constant (TABLE 6) +*/ + +/* +TABLE 1 ++-------------------------+---+---+---------+---------+ +| Precision | u | v | FPSR.EP | length | ++-------------------------+---+---+---------+---------+ +| Single | 0 ü 0 | x | 1 words | +| Double | 1 ü 1 | x | 2 words | +| Extended | 1 ü 1 | x | 3 words | +| Packed decimal | 1 ü 1 | 0 | 3 words | +| Expanded packed decimal | 1 ü 1 | 1 | 4 words | ++-------------------------+---+---+---------+---------+ +Note: x = don't care +*/ + +/* +TABLE 2 ++---+---+---------------------------------+ +| w | x | Number of registers to transfer | ++---+---+---------------------------------+ +| 0 ü 1 | 1 | +| 1 ü 0 | 2 | +| 1 ü 1 | 3 | +| 0 ü 0 | 4 | ++---+---+---------------------------------+ +*/ + +/* +TABLE 3: Dyadic Floating Point Opcodes ++---+---+---+---+----------+-----------------------+-----------------------+ +| a | b | c | d | Mnemonic | Description | Operation | ++---+---+---+---+----------+-----------------------+-----------------------+ +| 0 | 0 | 0 | 0 | ADF | Add | Fd := Fn + Fm | +| 0 | 0 | 0 | 1 | MUF | Multiply | Fd := Fn * Fm | +| 0 | 0 | 1 | 0 | SUF | Subtract | Fd := Fn - Fm | +| 0 | 0 | 1 | 1 | RSF | Reverse subtract | Fd := Fm - Fn | +| 0 | 1 | 0 | 0 | DVF | Divide | Fd := Fn / Fm | +| 0 | 1 | 0 | 1 | RDF | Reverse divide | Fd := Fm / Fn | +| 0 | 1 | 1 | 0 | POW | Power | Fd := Fn ^ Fm | +| 0 | 1 | 1 | 1 | RPW | Reverse power | Fd := Fm ^ Fn | +| 1 | 0 | 0 | 0 | RMF | Remainder | Fd := IEEE rem(Fn/Fm) | +| 1 | 0 | 0 | 1 | FML | Fast Multiply | Fd := Fn * Fm | +| 1 | 0 | 1 | 0 | FDV | Fast Divide | Fd := Fn / Fm | +| 1 | 0 | 1 | 1 | FRD | Fast reverse divide | Fd := Fm / Fn | +| 1 | 1 | 0 | 0 | POL | Polar angle (ArcTan2) | Fd := arctan2(Fn,Fm) | +| 1 | 1 | 0 | 1 | | undefined instruction | trap | +| 1 | 1 | 1 | 0 | | undefined instruction | trap | +| 1 | 1 | 1 | 1 | | undefined instruction | trap | ++---+---+---+---+----------+-----------------------+-----------------------+ +Note: POW, RPW, POL are deprecated, and are available for backwards + compatibility only. +*/ + +/* +TABLE 4: Monadic Floating Point Opcodes ++---+---+---+---+----------+-----------------------+-----------------------+ +| a | b | c | d | Mnemonic | Description | Operation | ++---+---+---+---+----------+-----------------------+-----------------------+ +| 0 | 0 | 0 | 0 | MVF | Move | Fd := Fm | +| 0 | 0 | 0 | 1 | MNF | Move negated | Fd := - Fm | +| 0 | 0 | 1 | 0 | ABS | Absolute value | Fd := abs(Fm) | +| 0 | 0 | 1 | 1 | RND | Round to integer | Fd := int(Fm) | +| 0 | 1 | 0 | 0 | SQT | Square root | Fd := sqrt(Fm) | +| 0 | 1 | 0 | 1 | LOG | Log base 10 | Fd := log10(Fm) | +| 0 | 1 | 1 | 0 | LGN | Log base e | Fd := ln(Fm) | +| 0 | 1 | 1 | 1 | EXP | Exponent | Fd := e ^ Fm | +| 1 | 0 | 0 | 0 | SIN | Sine | Fd := sin(Fm) | +| 1 | 0 | 0 | 1 | COS | Cosine | Fd := cos(Fm) | +| 1 | 0 | 1 | 0 | TAN | Tangent | Fd := tan(Fm) | +| 1 | 0 | 1 | 1 | ASN | Arc Sine | Fd := arcsin(Fm) | +| 1 | 1 | 0 | 0 | ACS | Arc Cosine | Fd := arccos(Fm) | +| 1 | 1 | 0 | 1 | ATN | Arc Tangent | Fd := arctan(Fm) | +| 1 | 1 | 1 | 0 | URD | Unnormalized round | Fd := int(Fm) | +| 1 | 1 | 1 | 1 | NRM | Normalize | Fd := norm(Fm) | ++---+---+---+---+----------+-----------------------+-----------------------+ +Note: LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN are deprecated, and are + available for backwards compatibility only. +*/ + +/* +TABLE 5 ++-------------------------+---+---+ +| Rounding Precision | e | f | ++-------------------------+---+---+ +| IEEE Single precision | 0 ü 0 | +| IEEE Double precision | 0 ü 1 | +| IEEE Extended precision | 1 ü 0 | +| undefined (trap) | 1 ü 1 | ++-------------------------+---+---+ +*/ + +/* +TABLE 5 ++---------------------------------+---+---+ +| Rounding Mode | g | h | ++---------------------------------+---+---+ +| Round to nearest (default) | 0 ü 0 | +| Round toward plus infinity | 0 ü 1 | +| Round toward negative infinity | 1 ü 0 | +| Round toward zero | 1 ü 1 | ++---------------------------------+---+---+ +*/ + +/* +=== +=== Definitions for load and store instructions +=== +*/ + +/* bit masks */ +#define BIT_PREINDEX 0x01000000 +#define BIT_UP 0x00800000 +#define BIT_WRITE_BACK 0x00200000 +#define BIT_LOAD 0x00100000 + +/* masks for load/store */ +#define MASK_CPDT 0x0c000000 /* data processing opcode */ +#define MASK_OFFSET 0x000000ff +#define MASK_TRANSFER_LENGTH 0x00408000 +#define MASK_REGISTER_COUNT MASK_TRANSFER_LENGTH +#define MASK_COPROCESSOR 0x00000f00 + +/* Tests for transfer length */ +#define TRANSFER_SINGLE 0x00000000 +#define TRANSFER_DOUBLE 0x00008000 +#define TRANSFER_EXTENDED 0x00400000 +#define TRANSFER_PACKED MASK_TRANSFER_LENGTH + +/* Get the coprocessor number from the opcode. */ +#define getCoprocessorNumber(opcode) ((opcode & MASK_COPROCESSOR) >> 8) + +/* Get the offset from the opcode. */ +#define getOffset(opcode) (opcode & MASK_OFFSET) + +/* Tests for specific data transfer load/store opcodes. */ +#define TEST_OPCODE(opcode,mask) (((opcode) & (mask)) == (mask)) + +#define LOAD_OP(opcode) TEST_OPCODE((opcode),MASK_CPDT | BIT_LOAD) +#define STORE_OP(opcode) ((opcode & (MASK_CPDT | BIT_LOAD)) == MASK_CPDT) + +#define LDF_OP(opcode) (LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 1)) +#define LFM_OP(opcode) (LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 2)) +#define STF_OP(opcode) (STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 1)) +#define SFM_OP(opcode) (STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 2)) + +#define PREINDEXED(opcode) ((opcode & BIT_PREINDEX) != 0) +#define POSTINDEXED(opcode) ((opcode & BIT_PREINDEX) == 0) +#define BIT_UP_SET(opcode) ((opcode & BIT_UP) != 0) +#define BIT_UP_CLEAR(opcode) ((opcode & BIT_DOWN) == 0) +#define WRITE_BACK(opcode) ((opcode & BIT_WRITE_BACK) != 0) +#define LOAD(opcode) ((opcode & BIT_LOAD) != 0) +#define STORE(opcode) ((opcode & BIT_LOAD) == 0) + +/* +=== +=== Definitions for arithmetic instructions +=== +*/ +/* bit masks */ +#define BIT_MONADIC 0x00008000 +#define BIT_CONSTANT 0x00000008 + +#define CONSTANT_FM(opcode) ((opcode & BIT_CONSTANT) != 0) +#define MONADIC_INSTRUCTION(opcode) ((opcode & BIT_MONADIC) != 0) + +/* instruction identification masks */ +#define MASK_CPDO 0x0e000000 /* arithmetic opcode */ +#define MASK_ARITHMETIC_OPCODE 0x00f08000 +#define MASK_DESTINATION_SIZE 0x00080080 + +/* dyadic arithmetic opcodes. */ +#define ADF_CODE 0x00000000 +#define MUF_CODE 0x00100000 +#define SUF_CODE 0x00200000 +#define RSF_CODE 0x00300000 +#define DVF_CODE 0x00400000 +#define RDF_CODE 0x00500000 +#define POW_CODE 0x00600000 +#define RPW_CODE 0x00700000 +#define RMF_CODE 0x00800000 +#define FML_CODE 0x00900000 +#define FDV_CODE 0x00a00000 +#define FRD_CODE 0x00b00000 +#define POL_CODE 0x00c00000 +/* 0x00d00000 is an invalid dyadic arithmetic opcode */ +/* 0x00e00000 is an invalid dyadic arithmetic opcode */ +/* 0x00f00000 is an invalid dyadic arithmetic opcode */ + +/* monadic arithmetic opcodes. */ +#define MVF_CODE 0x00008000 +#define MNF_CODE 0x00108000 +#define ABS_CODE 0x00208000 +#define RND_CODE 0x00308000 +#define SQT_CODE 0x00408000 +#define LOG_CODE 0x00508000 +#define LGN_CODE 0x00608000 +#define EXP_CODE 0x00708000 +#define SIN_CODE 0x00808000 +#define COS_CODE 0x00908000 +#define TAN_CODE 0x00a08000 +#define ASN_CODE 0x00b08000 +#define ACS_CODE 0x00c08000 +#define ATN_CODE 0x00d08000 +#define URD_CODE 0x00e08000 +#define NRM_CODE 0x00f08000 + +/* +=== +=== Definitions for register transfer and comparison instructions +=== +*/ + +#define MASK_CPRT 0x0e000010 /* register transfer opcode */ +#define MASK_CPRT_CODE 0x00f00000 +#define FLT_CODE 0x00000000 +#define FIX_CODE 0x00100000 +#define WFS_CODE 0x00200000 +#define RFS_CODE 0x00300000 +#define WFC_CODE 0x00400000 +#define RFC_CODE 0x00500000 +#define CMF_CODE 0x00900000 +#define CNF_CODE 0x00b00000 +#define CMFE_CODE 0x00d00000 +#define CNFE_CODE 0x00f00000 + +/* +=== +=== Common definitions +=== +*/ + +/* register masks */ +#define MASK_Rd 0x0000f000 +#define MASK_Rn 0x000f0000 +#define MASK_Fd 0x00007000 +#define MASK_Fm 0x00000007 +#define MASK_Fn 0x00070000 + +/* condition code masks */ +#define CC_MASK 0xf0000000 +#define CC_NEGATIVE 0x80000000 +#define CC_ZERO 0x40000000 +#define CC_CARRY 0x20000000 +#define CC_OVERFLOW 0x10000000 +#define CC_EQ 0x00000000 +#define CC_NE 0x10000000 +#define CC_CS 0x20000000 +#define CC_HS CC_CS +#define CC_CC 0x30000000 +#define CC_LO CC_CC +#define CC_MI 0x40000000 +#define CC_PL 0x50000000 +#define CC_VS 0x60000000 +#define CC_VC 0x70000000 +#define CC_HI 0x80000000 +#define CC_LS 0x90000000 +#define CC_GE 0xa0000000 +#define CC_LT 0xb0000000 +#define CC_GT 0xc0000000 +#define CC_LE 0xd0000000 +#define CC_AL 0xe0000000 +#define CC_NV 0xf0000000 + +/* rounding masks/values */ +#define MASK_ROUNDING_MODE 0x00000060 +#define ROUND_TO_NEAREST 0x00000000 +#define ROUND_TO_PLUS_INFINITY 0x00000020 +#define ROUND_TO_MINUS_INFINITY 0x00000040 +#define ROUND_TO_ZERO 0x00000060 + +#define MASK_ROUNDING_PRECISION 0x00080080 +#define ROUND_SINGLE 0x00000000 +#define ROUND_DOUBLE 0x00000080 +#define ROUND_EXTENDED 0x00080000 + +/* Get the condition code from the opcode. */ +#define getCondition(opcode) (opcode >> 28) + +/* Get the source register from the opcode. */ +#define getRn(opcode) ((opcode & MASK_Rn) >> 16) + +/* Get the destination floating point register from the opcode. */ +#define getFd(opcode) ((opcode & MASK_Fd) >> 12) + +/* Get the first source floating point register from the opcode. */ +#define getFn(opcode) ((opcode & MASK_Fn) >> 16) + +/* Get the second source floating point register from the opcode. */ +#define getFm(opcode) (opcode & MASK_Fm) + +/* Get the destination register from the opcode. */ +#define getRd(opcode) ((opcode & MASK_Rd) >> 12) + +/* Get the rounding mode from the opcode. */ +#define getRoundingMode(opcode) ((opcode & MASK_ROUNDING_MODE) >> 5) + +static inline const floatx80 getExtendedConstant(const unsigned int nIndex) +{ + extern const floatx80 floatx80Constant[]; + return floatx80Constant[nIndex]; +} + +static inline const float64 getDoubleConstant(const unsigned int nIndex) +{ + extern const float64 float64Constant[]; + return float64Constant[nIndex]; +} + +static inline const float32 getSingleConstant(const unsigned int nIndex) +{ + extern const float32 float32Constant[]; + return float32Constant[nIndex]; +} + +extern unsigned int getRegisterCount(const unsigned int opcode); +extern unsigned int getDestinationSize(const unsigned int opcode); + +#endif diff --git a/linux-user/arm/nwfpe/fpsr.h b/linux-user/arm/nwfpe/fpsr.h new file mode 100644 index 0000000000..0c665431eb --- /dev/null +++ b/linux-user/arm/nwfpe/fpsr.h @@ -0,0 +1,108 @@ +/* + NetWinder Floating Point Emulator + (c) Rebel.com, 1998-1999 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __FPSR_H__ +#define __FPSR_H__ + +/* +The FPSR is a 32 bit register consisting of 4 parts, each exactly +one byte. + + SYSTEM ID + EXCEPTION TRAP ENABLE BYTE + SYSTEM CONTROL BYTE + CUMULATIVE EXCEPTION FLAGS BYTE + +The FPCR is a 32 bit register consisting of bit flags. +*/ + +/* SYSTEM ID +------------ +Note: the system id byte is read only */ + +typedef unsigned int FPSR; /* type for floating point status register */ +typedef unsigned int FPCR; /* type for floating point control register */ + +#define MASK_SYSID 0xff000000 +#define BIT_HARDWARE 0x80000000 +#define FP_EMULATOR 0x01000000 /* System ID for emulator */ +#define FP_ACCELERATOR 0x81000000 /* System ID for FPA11 */ + +/* EXCEPTION TRAP ENABLE BYTE +----------------------------- */ + +#define MASK_TRAP_ENABLE 0x00ff0000 +#define MASK_TRAP_ENABLE_STRICT 0x001f0000 +#define BIT_IXE 0x00100000 /* inexact exception enable */ +#define BIT_UFE 0x00080000 /* underflow exception enable */ +#define BIT_OFE 0x00040000 /* overflow exception enable */ +#define BIT_DZE 0x00020000 /* divide by zero exception enable */ +#define BIT_IOE 0x00010000 /* invalid operation exception enable */ + +/* SYSTEM CONTROL BYTE +---------------------- */ + +#define MASK_SYSTEM_CONTROL 0x0000ff00 +#define MASK_TRAP_STRICT 0x00001f00 + +#define BIT_AC 0x00001000 /* use alternative C-flag definition + for compares */ +#define BIT_EP 0x00000800 /* use expanded packed decimal format */ +#define BIT_SO 0x00000400 /* select synchronous operation of FPA */ +#define BIT_NE 0x00000200 /* NaN exception bit */ +#define BIT_ND 0x00000100 /* no denormalized numbers bit */ + +/* CUMULATIVE EXCEPTION FLAGS BYTE +---------------------------------- */ + +#define MASK_EXCEPTION_FLAGS 0x000000ff +#define MASK_EXCEPTION_FLAGS_STRICT 0x0000001f + +#define BIT_IXC 0x00000010 /* inexact exception flag */ +#define BIT_UFC 0x00000008 /* underflow exception flag */ +#define BIT_OFC 0x00000004 /* overfloat exception flag */ +#define BIT_DZC 0x00000002 /* divide by zero exception flag */ +#define BIT_IOC 0x00000001 /* invalid operation exception flag */ + +/* Floating Point Control Register +----------------------------------*/ + +#define BIT_RU 0x80000000 /* rounded up bit */ +#define BIT_IE 0x10000000 /* inexact bit */ +#define BIT_MO 0x08000000 /* mantissa overflow bit */ +#define BIT_EO 0x04000000 /* exponent overflow bit */ +#define BIT_SB 0x00000800 /* store bounce */ +#define BIT_AB 0x00000400 /* arithmetic bounce */ +#define BIT_RE 0x00000200 /* rounding exception */ +#define BIT_DA 0x00000100 /* disable FPA */ + +#define MASK_OP 0x00f08010 /* AU operation code */ +#define MASK_PR 0x00080080 /* AU precision */ +#define MASK_S1 0x00070000 /* AU source register 1 */ +#define MASK_S2 0x00000007 /* AU source register 2 */ +#define MASK_DS 0x00007000 /* AU destination register */ +#define MASK_RM 0x00000060 /* AU rounding mode */ +#define MASK_ALU 0x9cfff2ff /* only ALU can write these bits */ +#define MASK_RESET 0x00000d00 /* bits set on reset, all others cleared */ +#define MASK_WFC MASK_RESET +#define MASK_RFC ~MASK_RESET + +#endif diff --git a/linux-user/arm/nwfpe/single_cpdo.c b/linux-user/arm/nwfpe/single_cpdo.c new file mode 100644 index 0000000000..65043bcb31 --- /dev/null +++ b/linux-user/arm/nwfpe/single_cpdo.c @@ -0,0 +1,253 @@ +/* + NetWinder Floating Point Emulator + (c) Rebel.COM, 1998,1999 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "fpa11.h" +#include "softfloat.h" +#include "fpopcode.h" + +float32 float32_exp(float32 Fm); +float32 float32_ln(float32 Fm); +float32 float32_sin(float32 rFm); +float32 float32_cos(float32 rFm); +float32 float32_arcsin(float32 rFm); +float32 float32_arctan(float32 rFm); +float32 float32_log(float32 rFm); +float32 float32_tan(float32 rFm); +float32 float32_arccos(float32 rFm); +float32 float32_pow(float32 rFn,float32 rFm); +float32 float32_pol(float32 rFn,float32 rFm); + +unsigned int SingleCPDO(const unsigned int opcode) +{ + FPA11 *fpa11 = GET_FPA11(); + float32 rFm, rFn = float32_zero; + unsigned int Fd, Fm, Fn, nRc = 1; + + Fm = getFm(opcode); + if (CONSTANT_FM(opcode)) + { + rFm = getSingleConstant(Fm); + } + else + { + switch (fpa11->fType[Fm]) + { + case typeSingle: + rFm = fpa11->fpreg[Fm].fSingle; + break; + + default: return 0; + } + } + + if (!MONADIC_INSTRUCTION(opcode)) + { + Fn = getFn(opcode); + switch (fpa11->fType[Fn]) + { + case typeSingle: + rFn = fpa11->fpreg[Fn].fSingle; + break; + + default: return 0; + } + } + + Fd = getFd(opcode); + switch (opcode & MASK_ARITHMETIC_OPCODE) + { + /* dyadic opcodes */ + case ADF_CODE: + fpa11->fpreg[Fd].fSingle = float32_add(rFn,rFm, &fpa11->fp_status); + break; + + case MUF_CODE: + case FML_CODE: + fpa11->fpreg[Fd].fSingle = float32_mul(rFn,rFm, &fpa11->fp_status); + break; + + case SUF_CODE: + fpa11->fpreg[Fd].fSingle = float32_sub(rFn,rFm, &fpa11->fp_status); + break; + + case RSF_CODE: + fpa11->fpreg[Fd].fSingle = float32_sub(rFm,rFn, &fpa11->fp_status); + break; + + case DVF_CODE: + case FDV_CODE: + fpa11->fpreg[Fd].fSingle = float32_div(rFn,rFm, &fpa11->fp_status); + break; + + case RDF_CODE: + case FRD_CODE: + fpa11->fpreg[Fd].fSingle = float32_div(rFm,rFn, &fpa11->fp_status); + break; + +#if 0 + case POW_CODE: + fpa11->fpreg[Fd].fSingle = float32_pow(rFn,rFm); + break; + + case RPW_CODE: + fpa11->fpreg[Fd].fSingle = float32_pow(rFm,rFn); + break; +#endif + + case RMF_CODE: + fpa11->fpreg[Fd].fSingle = float32_rem(rFn,rFm, &fpa11->fp_status); + break; + +#if 0 + case POL_CODE: + fpa11->fpreg[Fd].fSingle = float32_pol(rFn,rFm); + break; +#endif + + /* monadic opcodes */ + case MVF_CODE: + fpa11->fpreg[Fd].fSingle = rFm; + break; + + case MNF_CODE: + fpa11->fpreg[Fd].fSingle = float32_chs(rFm); + break; + + case ABS_CODE: + fpa11->fpreg[Fd].fSingle = float32_abs(rFm); + break; + + case RND_CODE: + case URD_CODE: + fpa11->fpreg[Fd].fSingle = float32_round_to_int(rFm, &fpa11->fp_status); + break; + + case SQT_CODE: + fpa11->fpreg[Fd].fSingle = float32_sqrt(rFm, &fpa11->fp_status); + break; + +#if 0 + case LOG_CODE: + fpa11->fpreg[Fd].fSingle = float32_log(rFm); + break; + + case LGN_CODE: + fpa11->fpreg[Fd].fSingle = float32_ln(rFm); + break; + + case EXP_CODE: + fpa11->fpreg[Fd].fSingle = float32_exp(rFm); + break; + + case SIN_CODE: + fpa11->fpreg[Fd].fSingle = float32_sin(rFm); + break; + + case COS_CODE: + fpa11->fpreg[Fd].fSingle = float32_cos(rFm); + break; + + case TAN_CODE: + fpa11->fpreg[Fd].fSingle = float32_tan(rFm); + break; + + case ASN_CODE: + fpa11->fpreg[Fd].fSingle = float32_arcsin(rFm); + break; + + case ACS_CODE: + fpa11->fpreg[Fd].fSingle = float32_arccos(rFm); + break; + + case ATN_CODE: + fpa11->fpreg[Fd].fSingle = float32_arctan(rFm); + break; +#endif + + case NRM_CODE: + break; + + default: + { + nRc = 0; + } + } + + if (0 != nRc) fpa11->fType[Fd] = typeSingle; + return nRc; +} + +#if 0 +float32 float32_exp(float32 Fm) +{ +//series +} + +float32 float32_ln(float32 Fm) +{ +//series +} + +float32 float32_sin(float32 rFm) +{ +//series +} + +float32 float32_cos(float32 rFm) +{ +//series +} + +float32 float32_arcsin(float32 rFm) +{ +//series +} + +float32 float32_arctan(float32 rFm) +{ + //series +} + +float32 float32_arccos(float32 rFm) +{ + //return float32_sub(halfPi,float32_arcsin(rFm)); +} + +float32 float32_log(float32 rFm) +{ + return float32_div(float32_ln(rFm),getSingleConstant(7)); +} + +float32 float32_tan(float32 rFm) +{ + return float32_div(float32_sin(rFm),float32_cos(rFm)); +} + +float32 float32_pow(float32 rFn,float32 rFm) +{ + return float32_exp(float32_mul(rFm,float32_ln(rFn))); +} + +float32 float32_pol(float32 rFn,float32 rFm) +{ + return float32_arctan(float32_div(rFn,rFm)); +} +#endif |