unsigned int SingleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd) { FPA11 *fpa11 = GET_FPA11(); float32 rFm; unsigned int Fm, opc_mask_shift; Fm = getFm(opcode); if (CONSTANT_FM(opcode)) { rFm = getSingleConstant(Fm); } else if (fpa11->fType[Fm] == typeSingle) { rFm = fpa11->fpreg[Fm].fSingle; } else { return 0; } opc_mask_shift = (opcode & MASK_ARITHMETIC_OPCODE) >> 20; if (!MONADIC_INSTRUCTION(opcode)) { unsigned int Fn = getFn(opcode); float32 rFn; if (fpa11->fType[Fn] == typeSingle && dyadic_single[opc_mask_shift]) { rFn = fpa11->fpreg[Fn].fSingle; rFd->fSingle = dyadic_single[opc_mask_shift](roundData, rFn, rFm); } else { return 0; } } else { if (monadic_single[opc_mask_shift]) { rFd->fSingle = monadic_single[opc_mask_shift](roundData, rFm); } else { return 0; } } 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 = getFn(opcode), Fm = getFm(opcode); int e_flag = opcode & 0x400000; /* 1 if CxFE */ int n_flag = opcode & 0x200000; /* 1 if CNxx */ unsigned int flags = 0; #ifdef CONFIG_FPE_NWFPE_XP floatx80 rFn, rFm; /* 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); break; case typeDouble: //printk("double.\n"); if (float64_is_nan(fpa11->fpreg[Fn].fDouble)) goto unordered; rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); 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); break; case typeDouble: //printk("double.\n"); if (float64_is_nan(fpa11->fpreg[Fm].fDouble)) goto unordered; rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble); 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; /* test for less than condition */ if (floatx80_lt(rFn, rFm)) flags |= CC_NEGATIVE; /* test for equal condition */ if (floatx80_eq(rFn, rFm)) flags |= CC_ZERO; /* test for greater than or equal condition */ if (floatx80_lt(rFm, rFn)) flags |= CC_CARRY; #else if (CONSTANT_FM(opcode)) { /* Fm is a constant. Do the comparison in whatever precision Fn happens to be stored in. */ if (fpa11->fType[Fn] == typeSingle) { float32 rFm = getSingleConstant(Fm); float32 rFn = fpa11->fpreg[Fn].fSingle; if (float32_is_nan(rFn)) goto unordered; if (n_flag) rFm ^= 0x80000000; /* test for less than condition */ if (float32_lt_nocheck(rFn, rFm)) flags |= CC_NEGATIVE; /* test for equal condition */ if (float32_eq_nocheck(rFn, rFm)) flags |= CC_ZERO; /* test for greater than or equal condition */ if (float32_lt_nocheck(rFm, rFn)) flags |= CC_CARRY; } else { float64 rFm = getDoubleConstant(Fm); float64 rFn = fpa11->fpreg[Fn].fDouble; if (float64_is_nan(rFn)) goto unordered; if (n_flag) rFm ^= 0x8000000000000000ULL; /* test for less than condition */ if (float64_lt_nocheck(rFn, rFm)) flags |= CC_NEGATIVE; /* test for equal condition */ if (float64_eq_nocheck(rFn, rFm)) flags |= CC_ZERO; /* test for greater than or equal condition */ if (float64_lt_nocheck(rFm, rFn)) flags |= CC_CARRY; } } else { /* Both operands are in registers. */ if (fpa11->fType[Fn] == typeSingle && fpa11->fType[Fm] == typeSingle) { float32 rFm = fpa11->fpreg[Fm].fSingle; float32 rFn = fpa11->fpreg[Fn].fSingle; if (float32_is_nan(rFn) || float32_is_nan(rFm)) goto unordered; if (n_flag) rFm ^= 0x80000000; /* test for less than condition */ if (float32_lt_nocheck(rFn, rFm)) flags |= CC_NEGATIVE; /* test for equal condition */ if (float32_eq_nocheck(rFn, rFm)) flags |= CC_ZERO; /* test for greater than or equal condition */ if (float32_lt_nocheck(rFm, rFn)) flags |= CC_CARRY; } else { /* Promote 32-bit operand to 64 bits. */ float64 rFm, rFn; rFm = (fpa11->fType[Fm] == typeSingle) ? float32_to_float64(fpa11->fpreg[Fm].fSingle) : fpa11->fpreg[Fm].fDouble; rFn = (fpa11->fType[Fn] == typeSingle) ? float32_to_float64(fpa11->fpreg[Fn].fSingle) : fpa11->fpreg[Fn].fDouble; if (float64_is_nan(rFn) || float64_is_nan(rFm)) goto unordered; if (n_flag) rFm ^= 0x8000000000000000ULL; /* test for less than condition */ if (float64_lt_nocheck(rFn, rFm)) flags |= CC_NEGATIVE; /* test for equal condition */ if (float64_eq_nocheck(rFn, rFm)) flags |= CC_ZERO; /* test for greater than or equal condition */ if (float64_lt_nocheck(rFm, rFn)) flags |= CC_CARRY; } } #endif writeConditionCodes(flags); return 1; 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); writeConditionCodes(flags); return 1; }
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; }
float32 float32_log(float32 rFm) { return float32_div(float32_ln(rFm),getSingleConstant(7)); }