static inline void storeExtended(const unsigned int Fn, unsigned int __user *pMem) { FPA11 *fpa11 = GET_FPA11(); union { floatx80 f; unsigned int i[3]; } val; switch (fpa11->fType[Fn]) { case typeSingle: val.f = float32_to_floatx80(fpa11->fpreg[Fn].fSingle); break; case typeDouble: val.f = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); break; default: val.f = fpa11->fpreg[Fn].fExtended; } put_user(val.i[0], &pMem[0]); /* sign & exp */ #ifdef __ARMEB__ put_user(val.i[1], &pMem[1]); /* msw */ put_user(val.i[2], &pMem[2]); #else put_user(val.i[1], &pMem[2]); put_user(val.i[2], &pMem[1]); /* msw */ #endif }
BX_INSF_TYPE BX_CPP_AttrRegparmN(1) BX_CPU_C::FLD_SINGLE_REAL(bxInstruction_c *i) { BX_CPU_THIS_PTR prepareFPU(i); RMAddr(i) = BX_CPU_CALL_METHODR(i->ResolveModrm, (i)); float32 load_reg = read_virtual_dword(i->seg(), RMAddr(i)); FPU_update_last_instruction(i); clear_C1(); if (! IS_TAG_EMPTY(-1)) { FPU_stack_overflow(); BX_NEXT_INSTR(i); } float_status_t status = FPU_pre_exception_handling(BX_CPU_THIS_PTR the_i387.get_control_word()); // convert to floatx80 format floatx80 result = float32_to_floatx80(load_reg, status); unsigned unmasked = FPU_exception(status.float_exception_flags); if (! (unmasked & FPU_CW_Invalid)) { BX_CPU_THIS_PTR the_i387.FPU_push(); BX_WRITE_FPU_REG(result, 0); } BX_NEXT_INSTR(i); }
void BX_CPP_AttrRegparmN(1) BX_CPU_C::FLD_SINGLE_REAL(bxInstruction_c *i) { #if BX_SUPPORT_FPU BX_CPU_THIS_PTR prepareFPU(i); float32 load_reg = read_virtual_dword(i->seg(), RMAddr(i)); clear_C1(); if (! IS_TAG_EMPTY(-1)) { BX_CPU_THIS_PTR FPU_stack_overflow(); return; } float_status_t status = FPU_pre_exception_handling(BX_CPU_THIS_PTR the_i387.get_control_word()); // convert to floatx80 format floatx80 result = float32_to_floatx80(load_reg, status); if (BX_CPU_THIS_PTR FPU_exception(status.float_exception_flags)) return; BX_CPU_THIS_PTR the_i387.FPU_push(); BX_WRITE_FPU_REG(result, 0); #else BX_INFO(("FLD_SINGLE_REAL: required FPU, configure --enable-fpu")); #endif }
extern __inline__ void storeExtended(const unsigned int Fn,unsigned int *pMem) { floatx80 val; register unsigned int *p = (unsigned int*)&val; switch (fpa11->fType[Fn]) { case typeSingle: val = float32_to_floatx80(fpa11->fpreg[Fn].fSingle); break; case typeDouble: val = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); break; default: val = fpa11->fpreg[Fn].fExtended; } put_user(p[0], &pMem[0]); /* sign & exp */ #ifdef __ARMEB__ put_user(p[1], &pMem[1]); put_user(p[2], &pMem[2]); /* msw */ #else put_user(p[1], &pMem[2]); put_user(p[2], &pMem[1]); /* msw */ #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 */ }
BX_INSF_TYPE BX_CPP_AttrRegparmN(1) BX_CPU_C::FCOM_SINGLE_REAL(bxInstruction_c *i) { BX_CPU_THIS_PTR prepareFPU(i); int pop_stack = i->nnn() & 1, rc; RMAddr(i) = BX_CPU_CALL_METHODR(i->ResolveModrm, (i)); float32 load_reg = read_virtual_dword(i->seg(), RMAddr(i)); BX_CPU_THIS_PTR FPU_update_last_instruction(i); clear_C1(); if (IS_TAG_EMPTY(0)) { FPU_exception(FPU_EX_Stack_Underflow); setcc(FPU_SW_C0|FPU_SW_C2|FPU_SW_C3); if(BX_CPU_THIS_PTR the_i387.is_IA_masked()) { if (pop_stack) BX_CPU_THIS_PTR the_i387.FPU_pop(); } BX_NEXT_INSTR(i); } float_status_t status = FPU_pre_exception_handling(BX_CPU_THIS_PTR the_i387.get_control_word()); floatx80 a = BX_READ_FPU_REG(0); if (floatx80_is_nan(a) || floatx80_is_unsupported(a) || float32_is_nan(load_reg)) { rc = float_relation_unordered; float_raise(status, float_flag_invalid); } else { rc = floatx80_compare(a, float32_to_floatx80(load_reg, status), status); } setcc(status_word_flags_fpu_compare(rc)); if (! FPU_exception(status.float_exception_flags)) { if (pop_stack) BX_CPU_THIS_PTR the_i387.FPU_pop(); } BX_NEXT_INSTR(i); }
static inline void storeExtended(const unsigned int Fn,unsigned int *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); break; case typeDouble: val = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); break; default: val = fpa11->fpreg[Fn].fExtended; } put_user(p[0], &pMem[0]); /* sign & exp */ put_user(p[1], &pMem[2]); put_user(p[2], &pMem[1]); /* msw */ }
unsigned int EmulateCPDO(const unsigned int opcode) { FPA11 *fpa11 = GET_FPA11(); FPREG *rFd; unsigned int nType, nDest, nRc; struct roundingData roundData; /* Get the destination size. If not valid let Linux perform an invalid instruction trap. */ nDest = getDestinationSize(opcode); if (typeNone == nDest) return 0; roundData.mode = SetRoundingMode(opcode); roundData.precision = SetRoundingPrecision(opcode); roundData.exception = 0; /* 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]; } } rFd = &fpa11->fpreg[getFd(opcode)]; switch (nType) { case typeSingle: nRc = SingleCPDO(&roundData, opcode, rFd); break; case typeDouble: nRc = DoubleCPDO(&roundData, opcode, rFd); break; #ifdef CONFIG_FPE_NWFPE_XP case typeExtended: nRc = ExtendedCPDO(&roundData, opcode, rFd); break; #endif default: nRc = 0; } /* The CPDO functions used to always set the destination type to be the same as their working size. */ if (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. */ fpa11->fType[getFd(opcode)] = nDest; #ifdef CONFIG_FPE_NWFPE_XP if (nDest != nType) { switch (nDest) { case typeSingle: { if (typeDouble == nType) rFd->fSingle = float64_to_float32(&roundData, rFd->fDouble); else rFd->fSingle = floatx80_to_float32(&roundData, rFd->fExtended); } break; case typeDouble: { if (typeSingle == nType) rFd->fDouble = float32_to_float64(rFd->fSingle); else rFd->fDouble = floatx80_to_float64(&roundData, rFd->fExtended); } break; case typeExtended: { if (typeSingle == nType) rFd->fExtended = float32_to_floatx80(rFd->fSingle); else rFd->fExtended = float64_to_floatx80(rFd->fDouble); } break; } } #else if (nDest != nType) { if (nDest == typeSingle) rFd->fSingle = float64_to_float32(&roundData, rFd->fDouble); else rFd->fDouble = float32_to_float64(rFd->fSingle); } #endif } if (roundData.exception) float_raise(roundData.exception); return nRc; }
/* 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 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; }
static void fpgen_rm_reg(m68000_base_device *m68k, UINT16 w2) { int ea = m68k->ir & 0x3f; int rm = (w2 >> 14) & 0x1; int src = (w2 >> 10) & 0x7; int dst = (w2 >> 7) & 0x7; int opmode = w2 & 0x7f; floatx80 source; // fmovecr #$f, fp0 f200 5c0f if (rm) { switch (src) { case 0: // Long-Word Integer { INT32 d = READ_EA_32(m68k, ea); source = int32_to_floatx80(d); break; } case 1: // Single-precision Real { UINT32 d = READ_EA_32(m68k, ea); source = float32_to_floatx80(d); break; } case 2: // Extended-precision Real { source = READ_EA_FPE(m68k, ea); break; } case 3: // Packed-decimal Real { source = READ_EA_PACK(m68k, ea); break; } case 4: // Word Integer { INT16 d = READ_EA_16(m68k, ea); source = int32_to_floatx80((INT32)d); break; } case 5: // Double-precision Real { UINT64 d = READ_EA_64(m68k, ea); source = float64_to_floatx80(d); break; } case 6: // Byte Integer { INT8 d = READ_EA_8(m68k, ea); source = int32_to_floatx80((INT32)d); break; } case 7: // FMOVECR load from constant ROM { switch (w2 & 0x7f) { case 0x0: // Pi source.high = 0x4000; source.low = U64(0xc90fdaa22168c235); break; case 0xb: // log10(2) source.high = 0x3ffd; source.low = U64(0x9a209a84fbcff798); break; case 0xc: // e source.high = 0x4000; source.low = U64(0xadf85458a2bb4a9b); break; case 0xd: // log2(e) source.high = 0x3fff; source.low = U64(0xb8aa3b295c17f0bc); break; case 0xe: // log10(e) source.high = 0x3ffd; source.low = U64(0xde5bd8a937287195); break; case 0xf: // 0.0 source = int32_to_floatx80((INT32)0); break; case 0x30: // ln(2) source.high = 0x3ffe; source.low = U64(0xb17217f7d1cf79ac); break; case 0x31: // ln(10) source.high = 0x4000; source.low = U64(0x935d8dddaaa8ac17); break; case 0x32: // 1 (or 100? manuals are unclear, but 1 would make more sense) source = int32_to_floatx80((INT32)1); break; case 0x33: // 10^1 source = int32_to_floatx80((INT32)10); break; case 0x34: // 10^2 source = int32_to_floatx80((INT32)10*10); break; case 0x35: // 10^4 source = int32_to_floatx80((INT32)1000*10); break; case 0x36: // 1.0e8 source = int32_to_floatx80((INT32)10000000*10); break; case 0x37: // 1.0e16 - can't get the right precision from INT32 so go "direct" with constants from h/w source.high = 0x4034; source.low = U64(0x8e1bc9bf04000000); break; case 0x38: // 1.0e32 source.high = 0x4069; source.low = U64(0x9dc5ada82b70b59e); break; case 0x39: // 1.0e64 source.high = 0x40d3; source.low = U64(0xc2781f49ffcfa6d5); break; case 0x3a: // 1.0e128 source.high = 0x41a8; source.low = U64(0x93ba47c980e98ce0); break; case 0x3b: // 1.0e256 source.high = 0x4351; source.low = U64(0xaa7eebfb9df9de8e); break; case 0x3c: // 1.0e512 source.high = 0x46a3; source.low = U64(0xe319a0aea60e91c7); break; case 0x3d: // 1.0e1024 source.high = 0x4d48; source.low = U64(0xc976758681750c17); break; case 0x3e: // 1.0e2048 source.high = 0x5a92; source.low = U64(0x9e8b3b5dc53d5de5); break; case 0x3f: // 1.0e4096 source.high = 0x7525; source.low = U64(0xc46052028a20979b); break; default: fatalerror("fmove_rm_reg: unknown constant ROM offset %x at %08x\n", w2&0x7f, REG_PC(m68k)-4); break; } // handle it right here, the usual opmode bits aren't valid in the FMOVECR case REG_FP(m68k)[dst] = source; m68k->remaining_cycles -= 4; return; } default: fatalerror("fmove_rm_reg: invalid source specifier %x at %08X\n", src, REG_PC(m68k)-4); } } else { source = REG_FP(m68k)[src]; } switch (opmode) { case 0x00: // FMOVE { REG_FP(m68k)[dst] = source; SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 4; break; } case 0x01: // FINT { INT32 temp; temp = floatx80_to_int32(source); REG_FP(m68k)[dst] = int32_to_floatx80(temp); break; } case 0x03: // FINTRZ { INT32 temp; temp = floatx80_to_int32_round_to_zero(source); REG_FP(m68k)[dst] = int32_to_floatx80(temp); break; } case 0x04: // FSQRT { REG_FP(m68k)[dst] = floatx80_sqrt(source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 109; break; } case 0x06: // FLOGNP1 { REG_FP(m68k)[dst] = floatx80_flognp1 (source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 594; // for MC68881 break; } case 0x0e: // FSIN { REG_FP(m68k)[dst] = source; floatx80_fsin(REG_FP(m68k)[dst]); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 75; break; } case 0x0f: // FTAN { REG_FP(m68k)[dst] = source; floatx80_ftan(REG_FP(m68k)[dst]); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 75; break; } case 0x14: // FLOGN { REG_FP(m68k)[dst] = floatx80_flogn (source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 548; // for MC68881 break; } case 0x15: // FLOG10 { REG_FP(m68k)[dst] = floatx80_flog10 (source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 604; // for MC68881 break; } case 0x16: // FLOG2 { REG_FP(m68k)[dst] = floatx80_flog2 (source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 604; // for MC68881 break; } case 0x18: // FABS { REG_FP(m68k)[dst] = source; REG_FP(m68k)[dst].high &= 0x7fff; SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 3; break; } case 0x1a: // FNEG { REG_FP(m68k)[dst] = source; REG_FP(m68k)[dst].high ^= 0x8000; SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 3; break; } case 0x1d: // FCOS { REG_FP(m68k)[dst] = source; floatx80_fcos(REG_FP(m68k)[dst]); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 75; break; } case 0x1e: // FGETEXP { INT16 temp2; temp2 = source.high; // get the exponent temp2 -= 0x3fff; // take off the bias REG_FP(m68k)[dst] = double_to_fx80((double)temp2); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 6; break; } case 0x20: // FDIV { REG_FP(m68k)[dst] = floatx80_div(REG_FP(m68k)[dst], source); m68k->remaining_cycles -= 43; break; } case 0x22: // FADD { REG_FP(m68k)[dst] = floatx80_add(REG_FP(m68k)[dst], source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 9; break; } case 0x23: // FMUL { REG_FP(m68k)[dst] = floatx80_mul(REG_FP(m68k)[dst], source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 11; break; } case 0x24: // FSGLDIV { float32 a = floatx80_to_float32( REG_FP(m68k)[dst] ); float32 b = floatx80_to_float32( source ); REG_FP(m68k)[dst] = float32_to_floatx80( float32_div(a, b) ); m68k->remaining_cycles -= 43; // // ? (value is from FDIV) break; } case 0x25: // FREM { REG_FP(m68k)[dst] = floatx80_rem(REG_FP(m68k)[dst], source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 43; // guess break; } case 0x27: // FSGLMUL { float32 a = floatx80_to_float32( REG_FP(m68k)[dst] ); float32 b = floatx80_to_float32( source ); REG_FP(m68k)[dst] = float32_to_floatx80( float32_mul(a, b) ); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 11; // ? (value is from FMUL) break; } case 0x28: // FSUB { REG_FP(m68k)[dst] = floatx80_sub(REG_FP(m68k)[dst], source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 9; break; } case 0x38: // FCMP { floatx80 res; res = floatx80_sub(REG_FP(m68k)[dst], source); SET_CONDITION_CODES(m68k, res); m68k->remaining_cycles -= 7; break; } case 0x3a: // FTST { floatx80 res; res = source; SET_CONDITION_CODES(m68k, res); m68k->remaining_cycles -= 7; break; } default: fatalerror("fpgen_rm_reg: unimplemented opmode %02X at %08X\n", opmode, REG_PPC(m68k)); } }
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; }
static void fpgen_rm_reg(m68ki_cpu_core *m68k, UINT16 w2) { int ea = m68k->ir & 0x3f; int rm = (w2 >> 14) & 0x1; int src = (w2 >> 10) & 0x7; int dst = (w2 >> 7) & 0x7; int opmode = w2 & 0x7f; floatx80 source; // fmovecr #$f, fp0 f200 5c0f if (rm) { switch (src) { case 0: // Long-Word Integer { INT32 d = READ_EA_32(m68k, ea); source = int32_to_floatx80(d); break; } case 1: // Single-precision Real { UINT32 d = READ_EA_32(m68k, ea); source = float32_to_floatx80(d); break; } case 2: // Extended-precision Real { source = READ_EA_FPE(m68k, ea); break; } case 3: // Packed-decimal Real { source = READ_EA_PACK(m68k, ea); break; } case 4: // Word Integer { INT16 d = READ_EA_16(m68k, ea); source = int32_to_floatx80((INT32)d); break; } case 5: // Double-precision Real { UINT64 d = READ_EA_64(m68k, ea); source = float64_to_floatx80(d); break; } case 6: // Byte Integer { INT8 d = READ_EA_8(m68k, ea); source = int32_to_floatx80((INT32)d); break; } case 7: // FMOVECR load from constant ROM { switch (w2 & 0x7f) { case 0x0: // Pi source.high = 0x4000; source.low = U64(0xc90fdaa22168c235); break; case 0xb: // log10(2) source.high = 0x3ffd; source.low = U64(0x9a209a84fbcff798); break; case 0xc: // e source.high = 0x4000; source.low = U64(0xadf85458a2bb4a9b); break; case 0xd: // log2(e) source.high = 0x3fff; source.low = U64(0xb8aa3b295c17f0bc); break; case 0xe: // log10(e) source.high = 0x3ffd; source.low = U64(0xde5bd8a937287195); break; case 0xf: // 0.0 source = int32_to_floatx80((INT32)0); break; case 0x30: // ln(2) source.high = 0x3ffe; source.low = U64(0xb17217f7d1cf79ac); break; case 0x31: // ln(10) source.high = 0x4000; source.low = U64(0x935d8dddaaa8ac17); break; case 0x32: // 1 (or 100? manuals are unclear, but 1 would make more sense) source = int32_to_floatx80((INT32)1); break; case 0x33: // 10^1 source = int32_to_floatx80((INT32)10); break; case 0x34: // 10^2 source = int32_to_floatx80((INT32)10*10); break; default: fatalerror("fmove_rm_reg: unknown constant ROM offset %x at %08x\n", w2&0x7f, REG_PC(m68k)-4); break; } // handle it right here, the usual opmode bits aren't valid in the FMOVECR case REG_FP(m68k)[dst] = source; m68k->remaining_cycles -= 4; return; } default: fatalerror("fmove_rm_reg: invalid source specifier %x at %08X\n", src, REG_PC(m68k)-4); } } else { source = REG_FP(m68k)[src]; } switch (opmode) { case 0x00: // FMOVE { REG_FP(m68k)[dst] = source; SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 4; break; } case 0x01: // FINT { INT32 temp; temp = floatx80_to_int32(source); REG_FP(m68k)[dst] = int32_to_floatx80(temp); break; } case 0x03: // FINTRZ { INT32 temp; temp = floatx80_to_int32_round_to_zero(source); REG_FP(m68k)[dst] = int32_to_floatx80(temp); break; } case 0x04: // FSQRT { REG_FP(m68k)[dst] = floatx80_sqrt(source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 109; break; } case 0x0e: // FSIN { REG_FP(m68k)[dst] = source; floatx80_fsin(REG_FP(m68k)[dst]); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 75; break; } case 0x0f: // FTAN { REG_FP(m68k)[dst] = source; floatx80_ftan(REG_FP(m68k)[dst]); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 75; break; } case 0x18: // FABS { REG_FP(m68k)[dst] = source; REG_FP(m68k)[dst].high &= 0x7fff; SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 3; break; } case 0x1a: // FNEG { REG_FP(m68k)[dst] = source; REG_FP(m68k)[dst].high ^= 0x8000; SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 3; break; } case 0x1d: // FCOS { REG_FP(m68k)[dst] = source; floatx80_fcos(REG_FP(m68k)[dst]); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 75; break; } case 0x1e: // FGETEXP { INT16 temp2; temp2 = source.high; // get the exponent temp2 -= 0x3fff; // take off the bias REG_FP(m68k)[dst] = double_to_fx80((double)temp2); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 6; } case 0x20: // FDIV { REG_FP(m68k)[dst] = floatx80_div(REG_FP(m68k)[dst], source); m68k->remaining_cycles -= 43; break; } case 0x22: // FADD { REG_FP(m68k)[dst] = floatx80_add(REG_FP(m68k)[dst], source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 9; break; } case 0x23: // FMUL { REG_FP(m68k)[dst] = floatx80_mul(REG_FP(m68k)[dst], source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 11; break; } case 0x24: // FSGLDIV { REG_FP(m68k)[dst] = floatx80_div(REG_FP(m68k)[dst], source); m68k->remaining_cycles -= 43; // // ? (value is from FDIV) break; } case 0x25: // FREM { REG_FP(m68k)[dst] = floatx80_rem(REG_FP(m68k)[dst], source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 43; // guess break; } case 0x27: // FSGLMUL { REG_FP(m68k)[dst] = floatx80_mul(REG_FP(m68k)[dst], source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 11; // ? (value is from FMUL) break; } case 0x28: // FSUB { REG_FP(m68k)[dst] = floatx80_sub(REG_FP(m68k)[dst], source); SET_CONDITION_CODES(m68k, REG_FP(m68k)[dst]); m68k->remaining_cycles -= 9; break; } case 0x38: // FCMP { floatx80 res; res = floatx80_sub(REG_FP(m68k)[dst], source); SET_CONDITION_CODES(m68k, res); m68k->remaining_cycles -= 7; break; } case 0x3a: // FTST { floatx80 res; res = source; SET_CONDITION_CODES(m68k, res); m68k->remaining_cycles -= 7; break; } default: fatalerror("fpgen_rm_reg: unimplemented opmode %02X at %08X\n", opmode, REG_PPC(m68k)); } }