diff --git a/src/xenia/cpu/x64/x64_emit_fpu.cc b/src/xenia/cpu/x64/x64_emit_fpu.cc index d43c28014..cc7fce17e 100644 --- a/src/xenia/cpu/x64/x64_emit_fpu.cc +++ b/src/xenia/cpu/x64/x64_emit_fpu.cc @@ -18,6 +18,11 @@ using namespace xe::cpu::ppc; using namespace AsmJit; +// Enable rounding numbers to single precision as required. +// This adds a bunch of work per operation and I'm not sure it's required. +#define ROUND_TO_SINGLE + + namespace xe { namespace cpu { namespace x64 { @@ -26,33 +31,150 @@ namespace x64 { // Floating-point arithmetic (A-8) XEEMITTER(faddx, 0xFC00002A, A )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- (frA) + (frB) + + XmmVar v(c.newXmmVar()); + c.movq(v, e.fpr_value(i.A.FRA)); + c.addsd(v, e.fpr_value(i.A.FRB)); + e.update_fpr_value(i.A.FRT, v); + + // TODO(benvanik): update status/control register. + + if (i.A.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } XEEMITTER(faddsx, 0xEC00002A, A )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- (frA) + (frB) + + XmmVar v(c.newXmmVar()); + c.movq(v, e.fpr_value(i.A.FRA)); + c.addsd(v, e.fpr_value(i.A.FRB)); +#if defined(ROUND_TO_SINGLE) + // TODO(benvanik): check rounding mode? etc? + // This converts to a single then back to a double to approximate the + // rounding on the 360. + c.cvtsd2ss(v, v); + c.cvtss2sd(v, v); +#endif // ROUND_TO_SINGLE + e.update_fpr_value(i.A.FRT, v); + + // TODO(benvanik): update status/control register. + + if (i.A.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } XEEMITTER(fdivx, 0xFC000024, A )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- frA / frB + + XmmVar v(c.newXmmVar()); + c.movq(v, e.fpr_value(i.A.FRA)); + c.divsd(v, e.fpr_value(i.A.FRB)); + e.update_fpr_value(i.A.FRT, v); + + // TODO(benvanik): update status/control register. + + if (i.A.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } XEEMITTER(fdivsx, 0xEC000024, A )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- frA / frB + + XmmVar v(c.newXmmVar()); + c.movq(v, e.fpr_value(i.A.FRA)); + c.divsd(v, e.fpr_value(i.A.FRB)); +#if defined(ROUND_TO_SINGLE) + // TODO(benvanik): check rounding mode? etc? + // This converts to a single then back to a double to approximate the + // rounding on the 360. + c.cvtsd2ss(v, v); + c.cvtss2sd(v, v); +#endif // ROUND_TO_SINGLE + e.update_fpr_value(i.A.FRT, v); + + // TODO(benvanik): update status/control register. + + if (i.A.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } XEEMITTER(fmulx, 0xFC000032, A )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- (frA) x (frC) + + XmmVar v(c.newXmmVar()); + c.movq(v, e.fpr_value(i.A.FRA)); + c.mulsd(v, e.fpr_value(i.A.FRC)); + e.update_fpr_value(i.A.FRT, v); + + // TODO(benvanik): update status/control register. + + if (i.A.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } XEEMITTER(fmulsx, 0xEC000032, A )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- (frA) x (frC) + + XmmVar v(c.newXmmVar()); + c.movq(v, e.fpr_value(i.A.FRA)); + c.mulsd(v, e.fpr_value(i.A.FRC)); +#if defined(ROUND_TO_SINGLE) + // TODO(benvanik): check rounding mode? etc? + // This converts to a single then back to a double to approximate the + // rounding on the 360. + c.cvtsd2ss(v, v); + c.cvtss2sd(v, v); +#endif // ROUND_TO_SINGLE + e.update_fpr_value(i.A.FRT, v); + + // TODO(benvanik): update status/control register. + + if (i.A.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } XEEMITTER(fresx, 0xEC000030, A )(X64Emitter& e, X86Compiler& c, InstrData& i) { @@ -66,18 +188,87 @@ XEEMITTER(frsqrtex, 0xFC000034, A )(X64Emitter& e, X86Compiler& c, InstrDat } XEEMITTER(fsubx, 0xFC000028, A )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- (frA) - (frB) + + XmmVar v(c.newXmmVar()); + c.movq(v, e.fpr_value(i.A.FRA)); + c.subsd(v, e.fpr_value(i.A.FRB)); +#if defined(ROUND_TO_SINGLE) + // TODO(benvanik): check rounding mode? etc? + // This converts to a single then back to a double to approximate the + // rounding on the 360. + c.cvtsd2ss(v, v); + c.cvtss2sd(v, v); +#endif // ROUND_TO_SINGLE + e.update_fpr_value(i.A.FRT, v); + + // TODO(benvanik): update status/control register. + + if (i.A.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } XEEMITTER(fsubsx, 0xEC000028, A )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- (frA) - (frB) + + XmmVar v(c.newXmmVar()); + c.movq(v, e.fpr_value(i.A.FRA)); + c.subsd(v, e.fpr_value(i.A.FRB)); + e.update_fpr_value(i.A.FRT, v); + + // TODO(benvanik): update status/control register. + + if (i.A.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } XEEMITTER(fselx, 0xFC00002E, A )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // if (frA) >= 0.0 + // then frD <- (frC) + // else frD <- (frB) + + XmmVar v(c.newXmmVar()); + GpVar zero(c.newGpVar()); + c.mov(zero, imm(0)); + c.movq(v, zero); + c.cmpsd(e.fpr_value(i.A.FRA), v, 0); + + // TODO(benvanik): find a way to do this without jumps. + Label choose_b(c.newLabel()); + Label done(c.newLabel()); + c.jl(choose_b); + c.movq(v, e.fpr_value(i.A.FRC)); + c.jmp(done); + c.bind(choose_b); + c.movq(v, e.fpr_value(i.A.FRB)); + c.bind(done); + e.update_fpr_value(i.A.FRT, v); + + // TODO(benvanik): update status/control register. + + if (i.A.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } XEEMITTER(fsqrtx, 0xFC00002C, A )(X64Emitter& e, X86Compiler& c, InstrData& i) { @@ -124,13 +315,62 @@ XEEMITTER(fnmaddsx, 0xEC00003E, A )(X64Emitter& e, X86Compiler& c, InstrDat } XEEMITTER(fnmsubx, 0xFC00003C, A )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- -([frA x frC] - frB) + + XmmVar a_mul_c(c.newXmmVar()); + // TODO(benvanik): I'm sure there's an SSE op for this. + // NOTE: we do (frB - [frA x frC]) as that's pretty much the same. + c.movq(a_mul_c, e.fpr_value(i.A.FRA)); + c.mulsd(a_mul_c, e.fpr_value(i.A.FRC)); + XmmVar v(c.newXmmVar()); + c.movq(v, e.fpr_value(i.A.FRB)); + c.subsd(v, a_mul_c); + e.update_fpr_value(i.A.FRT, v); + + // TODO(benvanik): update status/control register. + + if (i.A.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } XEEMITTER(fnmsubsx, 0xEC00003C, A )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- -([frA x frC] - frB) + + XmmVar a_mul_c(c.newXmmVar()); + // TODO(benvanik): I'm sure there's an SSE op for this. + // NOTE: we do (frB - [frA x frC]) as that's pretty much the same. + c.movq(a_mul_c, e.fpr_value(i.A.FRA)); + c.mulsd(a_mul_c, e.fpr_value(i.A.FRC)); + XmmVar v(c.newXmmVar()); + c.movq(v, e.fpr_value(i.A.FRB)); + c.subsd(v, a_mul_c); +#if defined(ROUND_TO_SINGLE) + // TODO(benvanik): check rounding mode? etc? + // This converts to a single then back to a double to approximate the + // rounding on the 360. + c.cvtsd2ss(v, v); + c.cvtss2sd(v, v); +#endif // ROUND_TO_SINGLE + e.update_fpr_value(i.A.FRT, v); + + // TODO(benvanik): update status/control register. + + if (i.A.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } @@ -162,8 +402,31 @@ XEEMITTER(fctiwzx, 0xFC00001E, X )(X64Emitter& e, X86Compiler& c, InstrDat } XEEMITTER(frspx, 0xFC000018, X )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- Round_single(frB) + +#if defined(ROUND_TO_SINGLE) + XmmVar v(c.newXmmVar()); + // TODO(benvanik): check rounding mode? etc? + // This converts to a single then back to a double to approximate the + // rounding on the 360. + c.cvtsd2ss(v, e.fpr_value(i.X.RB)); + c.cvtss2sd(v, v); +#else + XmmVar v(e.fpr_value(i.X.RB)); +#endif // ROUND_TO_SINGLE + e.update_fpr_value(i.X.RT, v); + + // TODO(benvanik): update status/control register. + + if (i.X.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } @@ -229,13 +492,45 @@ XEEMITTER(mtfsfix, 0xFC00010C, X )(X64Emitter& e, X86Compiler& c, InstrDat // Floating-point move (A-21) XEEMITTER(fabsx, 0xFC000210, X )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- abs(frB) + + XmmVar v(c.newXmmVar()); + c.movq(v, e.fpr_value(i.X.RB)); + // XOR with 0 in the sign bit and ones everywhere else. + GpVar gp_bit(c.newGpVar()); + c.mov(gp_bit, imm(0x7FFFFFFFFFFFFFFF)); + XmmVar bit(c.newXmmVar()); + c.movq(bit, gp_bit); + c.xorpd(v, bit); + e.update_fpr_value(i.X.RT, v); + + if (i.X.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } XEEMITTER(fmrx, 0xFC000090, X )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- (frB) + + XmmVar v(c.newXmmVar()); + c.movq(v, e.fpr_value(i.X.RB)); + e.update_fpr_value(i.X.RT, v); + + if (i.X.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; } XEEMITTER(fnabsx, 0xFC000110, X )(X64Emitter& e, X86Compiler& c, InstrData& i) { @@ -244,8 +539,27 @@ XEEMITTER(fnabsx, 0xFC000110, X )(X64Emitter& e, X86Compiler& c, InstrDat } XEEMITTER(fnegx, 0xFC000050, X )(X64Emitter& e, X86Compiler& c, InstrData& i) { - XEINSTRNOTIMPLEMENTED(); - return 1; + // frD <- ¬ frB[0] || frB[1-63] + + XmmVar v(c.newXmmVar()); + c.movq(v, e.fpr_value(i.X.RB)); + // XOR with 1 in the sign bit. + GpVar gp_bit(c.newGpVar()); + c.mov(gp_bit, imm(0x8000000000000000)); + XmmVar bit(c.newXmmVar()); + c.movq(bit, gp_bit); + c.xorpd(v, bit); + e.update_fpr_value(i.X.RT, v); + + if (i.X.Rc) { + // With cr0 update. + XEASSERTALWAYS(); + //e.update_cr_with_cond(0, v); + XEINSTRNOTIMPLEMENTED(); + return 1; + } + + return 0; }