953 lines
23 KiB
C++
953 lines
23 KiB
C++
/*
|
||
******************************************************************************
|
||
* Xenia : Xbox 360 Emulator Research Project *
|
||
******************************************************************************
|
||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||
******************************************************************************
|
||
*/
|
||
|
||
#include <xenia/cpu/x64/x64_emit.h>
|
||
|
||
#include <xenia/cpu/cpu-private.h>
|
||
|
||
|
||
using namespace xe::cpu;
|
||
using namespace xe::cpu::ppc;
|
||
|
||
using namespace AsmJit;
|
||
|
||
|
||
// Good source of information:
|
||
// http://mamedev.org/source/src/emu/cpu/powerpc/ppc_ops.c
|
||
// The correctness of that code is not reflected here yet -_-
|
||
|
||
|
||
// 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 {
|
||
|
||
|
||
// Floating-point arithmetic (A-8)
|
||
|
||
XEEMITTER(faddx, 0xFC00002A, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// 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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(faddsx, 0xEC00002A, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// 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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fdivx, 0xFC000024, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// 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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fdivsx, 0xEC000024, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// 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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fmulx, 0xFC000032, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// 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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRC);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fmulsx, 0xEC000032, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// 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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRC);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fresx, 0xEC000030, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
XEINSTRNOTIMPLEMENTED();
|
||
return 1;
|
||
}
|
||
|
||
XEEMITTER(frsqrtex, 0xFC000034, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
XEINSTRNOTIMPLEMENTED();
|
||
return 1;
|
||
}
|
||
|
||
XEEMITTER(fsubx, 0xFC000028, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// 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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fsubsx, 0xEC000028, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// 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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fselx, 0xFC00002E, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// 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.comisd(e.fpr_value(i.A.FRA), v);
|
||
|
||
// 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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fsqrtx, 0xFC00002C, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// Double precision:
|
||
// frD <- sqrt(frB)
|
||
|
||
XmmVar v(c.newXmmVar());
|
||
c.movq(v, e.fpr_value(i.A.FRA));
|
||
c.sqrtsd(v, v);
|
||
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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fsqrtsx, 0xEC00002C, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// Single precision:
|
||
// frD <- sqrt(frB)
|
||
|
||
XmmVar v(c.newXmmVar());
|
||
c.movq(v, e.fpr_value(i.A.FRA));
|
||
c.sqrtsd(v, v);
|
||
#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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
// Floating-point multiply-add (A-9)
|
||
|
||
XEEMITTER(fmaddx, 0xFC00003A, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// frD <- (frA x frC) + frB
|
||
|
||
XmmVar v(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(v, e.fpr_value(i.A.FRA));
|
||
c.mulsd(v, e.fpr_value(i.A.FRC));
|
||
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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fmaddsx, 0xEC00003A, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// frD <- (frA x frC) + frB
|
||
|
||
XmmVar v(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(v, e.fpr_value(i.A.FRA));
|
||
c.mulsd(v, e.fpr_value(i.A.FRC));
|
||
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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fmsubx, 0xFC000038, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// frD <- (frA x frC) - frB
|
||
|
||
XmmVar v(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(v, e.fpr_value(i.A.FRA));
|
||
c.mulsd(v, e.fpr_value(i.A.FRC));
|
||
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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fmsubsx, 0xEC000038, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// frD <- (frA x frC) - frB
|
||
|
||
XmmVar v(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(v, e.fpr_value(i.A.FRA));
|
||
c.mulsd(v, e.fpr_value(i.A.FRC));
|
||
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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fnmaddx, 0xFC00003E, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
XEINSTRNOTIMPLEMENTED();
|
||
return 1;
|
||
}
|
||
|
||
XEEMITTER(fnmaddsx, 0xEC00003E, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
XEINSTRNOTIMPLEMENTED();
|
||
return 1;
|
||
}
|
||
|
||
XEEMITTER(fnmsubx, 0xFC00003C, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// 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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fnmsubsx, 0xEC00003C, A )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// 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;
|
||
}
|
||
|
||
e.TraceFPR(i.A.FRT, i.A.FRA, i.A.FRB, i.A.FRC);
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
// Floating-point rounding and conversion (A-10)
|
||
|
||
XEEMITTER(fcfidx, 0xFC00069C, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// frD <- signed_int64_to_double( frB )
|
||
|
||
XmmVar frb(c.newXmmVar());
|
||
c.movq(frb, e.fpr_value(i.A.FRB));
|
||
c.save(frb);
|
||
XmmVar v(c.newXmmVar());
|
||
c.cvtsi2sd(v, frb.m64());
|
||
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;
|
||
}
|
||
|
||
e.TraceFPR(i.X.RT, i.X.RB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fctidx, 0xFC00065C, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// frD <- double_to_signed_int64( frB )
|
||
|
||
// UNTESTED: ensure this is correct.
|
||
//XEASSERTALWAYS();
|
||
//c.int3();
|
||
|
||
Label over_max(c.newLabel());
|
||
Label under_min(c.newLabel());
|
||
Label done(c.newLabel());
|
||
GpVar tmp(c.newGpVar());
|
||
XmmVar xmm_tmp(c.newXmmVar());
|
||
|
||
// TODO(benvanik): pull from FPSCR[RN]
|
||
// http://www.rz.uni-karlsruhe.de/rz/docs/VTune/reference/vc148.htm
|
||
// Round to zero (truncate).
|
||
GpVar mxcsr(c.newGpVar());
|
||
c.save(mxcsr);
|
||
c.stmxcsr(mxcsr.m32());
|
||
c.or_(mxcsr, imm(0x6000));
|
||
c.save(mxcsr);
|
||
c.ldmxcsr(mxcsr.m32());
|
||
|
||
XmmVar v(c.newXmmVar());
|
||
c.movq(v, e.fpr_value(i.X.RB));
|
||
// Max value: 2^63 - 1
|
||
c.mov(tmp, imm(0x43e0000000000000));
|
||
c.movq(xmm_tmp, tmp);
|
||
c.comisd(v, xmm_tmp);
|
||
c.jl(over_max);
|
||
// Min value: -2^63
|
||
c.mov(tmp, imm(0xc3e0000000000000));
|
||
c.movq(xmm_tmp, tmp);
|
||
c.comisd(v, xmm_tmp);
|
||
c.jl(under_min);
|
||
c.save(v);
|
||
c.cvtsd2si(tmp, v.m64());
|
||
c.movq(v, tmp);
|
||
c.jmp(done);
|
||
c.bind(over_max);
|
||
c.mov(tmp, imm(0x7FFFFFFFFFFFFFFF));
|
||
c.movq(v, tmp);
|
||
c.jmp(done);
|
||
c.bind(under_min);
|
||
c.mov(tmp, imm(0x8000000000000000));
|
||
c.movq(v, tmp);
|
||
c.bind(done);
|
||
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;
|
||
}
|
||
|
||
e.TraceFPR(i.X.RT, i.X.RB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fctidzx, 0xFC00065E, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// TODO(benvanik): assuming round to zero is always set, is that ok?
|
||
return InstrEmit_fctidx(e, c, i);
|
||
}
|
||
|
||
XEEMITTER(fctiwx, 0xFC00001C, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// frD <- double_to_signed_int32( frB )
|
||
|
||
// UNTESTED: ensure this is correct.
|
||
//XEASSERTALWAYS();
|
||
//c.int3();
|
||
|
||
Label over_max(c.newLabel());
|
||
Label under_min(c.newLabel());
|
||
Label done(c.newLabel());
|
||
GpVar tmp(c.newGpVar());
|
||
XmmVar xmm_tmp(c.newXmmVar());
|
||
|
||
// TODO(benvanik): pull from FPSCR[RN]
|
||
// http://www.rz.uni-karlsruhe.de/rz/docs/VTune/reference/vc148.htm
|
||
// Round to zero (truncate).
|
||
GpVar mxcsr(c.newGpVar());
|
||
c.save(mxcsr);
|
||
c.stmxcsr(mxcsr.m32());
|
||
c.or_(mxcsr, imm(0x6000));
|
||
c.save(mxcsr);
|
||
c.ldmxcsr(mxcsr.m32());
|
||
|
||
XmmVar v(c.newXmmVar());
|
||
c.movq(v, e.fpr_value(i.X.RB));
|
||
// Max value: 2^31 - 1
|
||
c.mov(tmp, imm(0x41efffffffe00000));
|
||
c.movq(xmm_tmp, tmp);
|
||
c.comisd(v, xmm_tmp);
|
||
c.jl(over_max);
|
||
// Min value: -2^31
|
||
c.mov(tmp, imm(0xc1e0000000000000));
|
||
c.movq(xmm_tmp, tmp);
|
||
c.comisd(v, xmm_tmp);
|
||
c.jg(under_min);
|
||
c.save(v);
|
||
c.cvtsd2si(tmp, v.m64());
|
||
c.movq(v, tmp);
|
||
c.jmp(done);
|
||
c.bind(over_max);
|
||
c.mov(tmp, imm(0x7FFFFFFF));
|
||
c.movq(v, tmp);
|
||
c.jmp(done);
|
||
c.bind(under_min);
|
||
c.mov(tmp, imm(0x80000000));
|
||
c.movq(v, tmp);
|
||
c.bind(done);
|
||
e.update_fpr_value(i.X.RT, 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;
|
||
}
|
||
|
||
e.TraceFPR(i.X.RT, i.X.RB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fctiwzx, 0xFC00001E, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// TODO(benvanik): assuming round to zero is always set, is that ok?
|
||
return InstrEmit_fctiwx(e, c, i);
|
||
}
|
||
|
||
XEEMITTER(frspx, 0xFC000018, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// 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;
|
||
}
|
||
|
||
e.TraceFPR(i.X.RT, i.X.RB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
// Floating-point compare (A-11)
|
||
|
||
int InstrEmit_fcmpx_(X64Emitter& e, X86Compiler& c, InstrData& i, bool ordered) {
|
||
// if (FRA) is a NaN or (FRB) is a NaN then
|
||
// c <- 0b0001
|
||
// else if (FRA) < (FRB) then
|
||
// c <- 0b1000
|
||
// else if (FRA) > (FRB) then
|
||
// c <- 0b0100
|
||
// else {
|
||
// c <- 0b0010
|
||
// }
|
||
// FPCC <- c
|
||
// CR[4*BF:4*BF+3] <- c
|
||
// if (FRA) is an SNaN or (FRB) is an SNaN then
|
||
// VXSNAN <- 1
|
||
|
||
GpVar cc(c.newGpVar());
|
||
c.xor_(cc, cc);
|
||
|
||
c.ucomisd(e.fpr_value(i.X.RA), e.fpr_value(i.X.RB));
|
||
|
||
GpVar gt(c.newGpVar());
|
||
c.mov(gt, imm(0x1)); // nan/etc via PF
|
||
c.cmovp(cc, gt);
|
||
c.mov(gt, imm(0x2)); // ==
|
||
c.cmove(cc, gt);
|
||
if (i.X.RA != i.X.RB) {
|
||
c.mov(gt, imm(0x8)); // <
|
||
c.cmovl(cc, gt);
|
||
c.mov(gt, imm(0x4)); // >
|
||
c.cmovg(cc, gt);
|
||
}
|
||
|
||
// TODO(benvanik): update FPCC for mffsx/etc
|
||
|
||
const uint32_t crf = i.X.RT >> 2;
|
||
e.update_cr_value(crf, cc);
|
||
|
||
// TODO(benvanik): update VXSNAN
|
||
|
||
return 0;
|
||
}
|
||
XEEMITTER(fcmpo, 0xFC000040, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
return InstrEmit_fcmpx_(e, c, i, true);
|
||
}
|
||
XEEMITTER(fcmpu, 0xFC000000, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
return InstrEmit_fcmpx_(e, c, i, false);
|
||
}
|
||
|
||
|
||
// Floating-point status and control register (A
|
||
|
||
XEEMITTER(mcrfs, 0xFC000080, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
XEINSTRNOTIMPLEMENTED();
|
||
return 1;
|
||
}
|
||
|
||
XEEMITTER(mffsx, 0xFC00048E, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
XEINSTRNOTIMPLEMENTED();
|
||
return 1;
|
||
}
|
||
|
||
XEEMITTER(mtfsb0x, 0xFC00008C, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
XEINSTRNOTIMPLEMENTED();
|
||
return 1;
|
||
}
|
||
|
||
XEEMITTER(mtfsb1x, 0xFC00004C, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
XEINSTRNOTIMPLEMENTED();
|
||
return 1;
|
||
}
|
||
|
||
XEEMITTER(mtfsfx, 0xFC00058E, XFL)(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
XEINSTRNOTIMPLEMENTED();
|
||
return 1;
|
||
}
|
||
|
||
XEEMITTER(mtfsfix, 0xFC00010C, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
XEINSTRNOTIMPLEMENTED();
|
||
return 1;
|
||
}
|
||
|
||
|
||
// Floating-point move (A-21)
|
||
|
||
XEEMITTER(fabsx, 0xFC000210, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// frD <- abs(frB)
|
||
|
||
XmmVar v(c.newXmmVar());
|
||
c.movq(v, e.fpr_value(i.X.RB));
|
||
// AND with 0 in the sign bit and 1 everywhere else.
|
||
GpVar gp_bit(c.newGpVar());
|
||
c.mov(gp_bit, imm(0x7FFFFFFFFFFFFFFF));
|
||
XmmVar bit(c.newXmmVar());
|
||
c.movq(bit, gp_bit);
|
||
c.andpd(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;
|
||
}
|
||
|
||
e.TraceFPR(i.X.RT, i.X.RB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fmrx, 0xFC000090, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// 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;
|
||
}
|
||
|
||
e.TraceFPR(i.X.RT, i.X.RB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
XEEMITTER(fnabsx, 0xFC000110, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
XEINSTRNOTIMPLEMENTED();
|
||
return 1;
|
||
}
|
||
|
||
XEEMITTER(fnegx, 0xFC000050, X )(X64Emitter& e, X86Compiler& c, InstrData& i) {
|
||
// frD <- <20> 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;
|
||
}
|
||
|
||
e.TraceFPR(i.X.RT, i.X.RB);
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
void X64RegisterEmitCategoryFPU() {
|
||
XEREGISTERINSTR(faddx, 0xFC00002A);
|
||
XEREGISTERINSTR(faddsx, 0xEC00002A);
|
||
XEREGISTERINSTR(fdivx, 0xFC000024);
|
||
XEREGISTERINSTR(fdivsx, 0xEC000024);
|
||
XEREGISTERINSTR(fmulx, 0xFC000032);
|
||
XEREGISTERINSTR(fmulsx, 0xEC000032);
|
||
XEREGISTERINSTR(fresx, 0xEC000030);
|
||
XEREGISTERINSTR(frsqrtex, 0xFC000034);
|
||
XEREGISTERINSTR(fsubx, 0xFC000028);
|
||
XEREGISTERINSTR(fsubsx, 0xEC000028);
|
||
XEREGISTERINSTR(fselx, 0xFC00002E);
|
||
XEREGISTERINSTR(fsqrtx, 0xFC00002C);
|
||
XEREGISTERINSTR(fsqrtsx, 0xEC00002C);
|
||
XEREGISTERINSTR(fmaddx, 0xFC00003A);
|
||
XEREGISTERINSTR(fmaddsx, 0xEC00003A);
|
||
XEREGISTERINSTR(fmsubx, 0xFC000038);
|
||
XEREGISTERINSTR(fmsubsx, 0xEC000038);
|
||
XEREGISTERINSTR(fnmaddx, 0xFC00003E);
|
||
XEREGISTERINSTR(fnmaddsx, 0xEC00003E);
|
||
XEREGISTERINSTR(fnmsubx, 0xFC00003C);
|
||
XEREGISTERINSTR(fnmsubsx, 0xEC00003C);
|
||
XEREGISTERINSTR(fcfidx, 0xFC00069C);
|
||
XEREGISTERINSTR(fctidx, 0xFC00065C);
|
||
XEREGISTERINSTR(fctidzx, 0xFC00065E);
|
||
XEREGISTERINSTR(fctiwx, 0xFC00001C);
|
||
XEREGISTERINSTR(fctiwzx, 0xFC00001E);
|
||
XEREGISTERINSTR(frspx, 0xFC000018);
|
||
XEREGISTERINSTR(fcmpo, 0xFC000040);
|
||
XEREGISTERINSTR(fcmpu, 0xFC000000);
|
||
XEREGISTERINSTR(mcrfs, 0xFC000080);
|
||
XEREGISTERINSTR(mffsx, 0xFC00048E);
|
||
XEREGISTERINSTR(mtfsb0x, 0xFC00008C);
|
||
XEREGISTERINSTR(mtfsb1x, 0xFC00004C);
|
||
XEREGISTERINSTR(mtfsfx, 0xFC00058E);
|
||
XEREGISTERINSTR(mtfsfix, 0xFC00010C);
|
||
XEREGISTERINSTR(fabsx, 0xFC000210);
|
||
XEREGISTERINSTR(fmrx, 0xFC000090);
|
||
XEREGISTERINSTR(fnabsx, 0xFC000110);
|
||
XEREGISTERINSTR(fnegx, 0xFC000050);
|
||
}
|
||
|
||
|
||
} // namespace x64
|
||
} // namespace cpu
|
||
} // namespace xe
|