diff --git a/src/xenia/cpu/backend/x64/x64_emitter.cc b/src/xenia/cpu/backend/x64/x64_emitter.cc index ec49d3433..694c92b4e 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.cc +++ b/src/xenia/cpu/backend/x64/x64_emitter.cc @@ -1113,6 +1113,8 @@ static const vec128_t xmm_consts[] = { /* XMMInt127 */ vec128i(0x7Fu), /* XMM2To32 */ vec128f(0x1.0p32f), /* XMMFloatInf */ vec128i(0x7f800000), + /* XMMDoubleInf */ + vec128i(0x00000000u, 0x7FF00000u, 0x00000000u, 0x7FF00000u), /* XMMIntsToBytes*/ v128_setr_bytes(0, 4, 8, 12, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, diff --git a/src/xenia/cpu/backend/x64/x64_emitter.h b/src/xenia/cpu/backend/x64/x64_emitter.h index ad29c9ced..c33f8b395 100644 --- a/src/xenia/cpu/backend/x64/x64_emitter.h +++ b/src/xenia/cpu/backend/x64/x64_emitter.h @@ -151,6 +151,7 @@ enum XmmConst { XMMInt127, XMM2To32, XMMFloatInf, + XMMDoubleInf, XMMIntsToBytes, XMMShortsToBytes, XMMLVSLTableBase, diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index c2b23792c..c216a8f25 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -386,7 +386,25 @@ struct CONVERT_F32_F64 static void Emit(X64Emitter& e, const EmitArgType& i) { e.ChangeMxcsrMode(MXCSRMode::Fpu); // TODO(benvanik): saturation check? cvtt* (trunc?) - e.vcvtsd2ss(i.dest, GetInputRegOrConstant(e, i.src1, e.xmm0)); + + Xbyak::Xmm src = GetInputRegOrConstant(e, i.src1, e.xmm0); + e.vmovq(e.rax, src); + e.vcvtsd2ss(i.dest, src); + Xbyak::Label done; + e.mov(e.rcx, e.rax); + e.btr(e.rcx, 63); + e.mov(e.rdx, e.GetXmmConstPtr(XMMDoubleInf)); + e.cmp(e.rcx, e.rdx); + e.jbe(done); // finite or +/-inf + + // NaN: float quiet bit (22) -> double quiet bit (51) + e.vmovd(e.ecx, i.dest); + e.and_(e.ecx, ~(1u << 22)); + e.shr(e.rax, 51 - 22); + e.and_(e.eax, 1u << 22); + e.or_(e.ecx, e.eax); + e.vmovd(i.dest, e.ecx); + e.L(done); } }; struct CONVERT_F64_I64 @@ -407,7 +425,26 @@ struct CONVERT_F64_F32 : Sequence> { static void Emit(X64Emitter& e, const EmitArgType& i) { e.ChangeMxcsrMode(MXCSRMode::Fpu); - e.vcvtss2sd(i.dest, GetInputRegOrConstant(e, i.src1, e.xmm0)); + Xbyak::Xmm src = GetInputRegOrConstant(e, i.src1, e.xmm0); + + e.vmovd(e.eax, src); + e.vcvtss2sd(i.dest, src); + + Xbyak::Label done; + e.mov(e.ecx, e.eax); + e.and_(e.ecx, e.GetXmmConstPtr(XMMAbsMaskPS)); + e.cmp(e.ecx, e.GetXmmConstPtr(XMMFloatInf)); + e.jbe(done); + + // NaN: double quiet bit (51) -> float quiet bit (22) + e.vmovq(e.rcx, i.dest); + e.btr(e.rcx, 51); // clear the bit the convert forced to 1 + e.shr(e.eax, 22); + e.and_(e.eax, 1); + e.shl(e.rax, 51); + e.or_(e.rcx, e.rax); + e.vmovq(i.dest, e.rcx); + e.L(done); } }; EMITTER_OPCODE_TABLE(OPCODE_CONVERT, CONVERT_I32_F32, CONVERT_I32_F64,