// Negate static bool neg(PPCEmuAssembler& a, Instruction instr) { a.mov(a.eax, a.ppcgpr[instr.rA]); a.neg(a.eax); a.mov(a.ppcgpr[instr.rD], a.eax); if (instr.oe) { a.mov(a.ecx, 0); a.seto(a.ecx.r8()); // Reset overflow a.mov(a.edx, a.ppcxer); a.and_(a.edx, ~XERegisterBits::Overflow); a.shiftTo(a.ecx, 0, XERegisterBits::Overflow); a.or_(a.edx, a.ecx); a.shiftTo(a.ecx, XERegisterBits::Overflow, XERegisterBits::StickyOV); a.or_(a.edx, a.ecx); a.mov(a.ppcxer, a.edx); } if (instr.rc) { updateConditionRegister(a, a.eax, a.ecx, a.edx); } return true; }
static bool rlwGeneric(PPCEmuAssembler& a, Instruction instr) { a.mov(a.eax, a.ppcgpr[instr.rS]); if (flags & RlwImmediate) { a.rol(a.eax, instr.sh); } else { a.mov(a.ecx, a.ppcgpr[instr.rB]); a.and_(a.ecx, 0x1f); a.rol(a.eax, a.ecx.r8()); } auto m = make_ppc_bitmask(instr.mb, instr.me); if (flags & RlwAnd) { a.and_(a.eax, m); } else if (flags & RlwInsert) { a.and_(a.eax, m); a.mov(a.ecx, a.ppcgpr[instr.rA]); a.and_(a.ecx, ~m); a.or_(a.eax, a.ecx); } a.mov(a.ppcgpr[instr.rA], a.eax); if (instr.rc) { updateConditionRegister(a, a.eax, a.ecx, a.edx); } return true; }
static bool orGeneric(PPCEmuAssembler& a, Instruction instr) { a.mov(a.eax, a.ppcgpr[instr.rS]); if (flags & OrImmediate) { a.mov(a.ecx, instr.uimm); } else { a.mov(a.ecx, a.ppcgpr[instr.rB]); } if (flags & OrShifted) { a.shl(a.ecx, 16); } if (flags & OrComplement) { a.not_(a.ecx); } a.or_(a.eax, a.ecx); a.mov(a.ppcgpr[instr.rA], a.eax); if (flags & OrAlwaysRecord) { updateConditionRegister(a, a.eax, a.ecx, a.edx); } else if (flags & OrCheckRecord) { if (instr.rc) { updateConditionRegister(a, a.eax, a.ecx, a.edx); } } return true; }
// Move to Condition Register from XER static bool mcrxr(PPCEmuAssembler& a, Instruction instr) { uint32_t crshift = (7 - instr.crfD) * 4; a.mov(a.eax, a.ppcxer); a.mov(a.ecx, a.eax); // Grab CRXR a.shr(a.ecx, XERegisterBits::XRShift); a.and_(a.ecx, 0xF); // Clear XER CRXR a.and_(a.eax, ~XERegisterBits::XR); a.mov(a.ppcxer, a.eax); // Set CRF a.shl(a.ecx, crshift); a.mov(a.eax, a.ppccr); a.and_(a.eax, ~(0xF << crshift)); a.or_(a.eax, a.ecx); a.mov(a.ppccr, a.eax); return true; }
// Condition Register OR static bool cror(PPCEmuAssembler& a, Instruction instr) { getTwoCRB(a, instr.crbA, a.eax, instr.crbB, a.ecx); a.or_(a.eax, a.ecx); setCRB(a, instr.crbD, a.eax, a.ecx, a.edx); return true; }
void setCRB(PPCEmuAssembler& a, uint32_t bit, const asmjit::X86GpReg& value, const asmjit::X86GpReg& tmp, const asmjit::X86GpReg& tmp2) { auto shift = 31 - bit; a.mov(tmp, a.ppccr); a.and_(tmp, ~(1 << shift)); a.mov(tmp2, value); a.and_(tmp2, 1); a.shl(tmp2, shift); a.or_(tmp, tmp2); a.mov(a.ppccr, tmp); }
// Update cr0 with value static void updateConditionRegister(PPCEmuAssembler& a, const asmjit::X86GpReg& value, const asmjit::X86GpReg& tmp, const asmjit::X86GpReg& tmp2) { auto crtarget = 0; auto crshift = (7 - crtarget) * 4; a.mov(tmp, a.ppccr); a.and_(tmp, ~(0xF << crshift)); a.cmp(value, 0); a.lahf(); a.mov(tmp2, 0); a.sete(tmp2.r8()); a.shl(tmp2, crshift + ConditionRegisterFlag::ZeroShift); a.or_(tmp, tmp2); a.sahf(); a.mov(tmp2, 0); a.setg(tmp2.r8()); a.shl(tmp2, crshift + ConditionRegisterFlag::PositiveShift); a.or_(tmp, tmp2); a.sahf(); a.mov(tmp2, 0); a.setl(tmp2.r8()); a.shl(tmp2, crshift + ConditionRegisterFlag::NegativeShift); a.or_(tmp, tmp2); a.mov(tmp2, a.ppcxer); a.and_(tmp2, XERegisterBits::StickyOV); a.shiftTo(tmp2, XERegisterBits::StickyOVShift, crshift + ConditionRegisterFlag::SummaryOverflowShift); a.or_(tmp, tmp2); a.mov(a.ppccr, tmp); }
// NOR static bool nor(PPCEmuAssembler& a, Instruction instr) { a.mov(a.eax, a.ppcgpr[instr.rS]); a.mov(a.ecx, a.ppcgpr[instr.rB]); a.or_(a.eax, a.ecx); a.not_(a.eax); a.mov(a.ppcgpr[instr.rA], a.eax); if (instr.rc) { updateConditionRegister(a, a.eax, a.ecx, a.edx); } return true; }
// Move Condition Register Field static bool mcrf(PPCEmuAssembler& a, Instruction instr) { uint32_t crshifts = (7 - instr.crfS) * 4; uint32_t crshiftd = (7 - instr.crfD) * 4; a.mov(a.eax, a.ppccr); a.mov(a.ecx, a.eax); a.and_(a.ecx, ~(0xF << crshifts)); a.shr(a.ecx, crshifts); a.shl(a.ecx, crshiftd); a.and_(a.eax, ~(0xF << crshiftd)); a.or_(a.eax, a.ecx); a.mov(a.ppccr, a.eax); return true; }
// Move to Condition Register Fields static bool mtcrf(PPCEmuAssembler& a, Instruction instr) { uint32_t crm = instr.crm; uint32_t mask = 0; for (auto i = 0u; i < 8; ++i) { if (crm & (1 << i)) { mask |= 0xf << (i * 4); } } a.mov(a.eax, a.ppcgpr[instr.rS]); a.and_(a.eax, mask); a.mov(a.ecx, a.ppccr); a.and_(a.ecx, ~mask); a.or_(a.eax, a.ecx); a.mov(a.ppccr, a.eax); return true; }
static bool addGeneric(PPCEmuAssembler& a, Instruction instr) { if (flags & AddSubtract) { return jit_fallback(a, instr); } bool recordCarry = false; bool recordOverflow = false; bool recordCond = false; if (flags & AddCarry) { recordCarry = true; } if (flags & AddAlwaysRecord) { recordOverflow = true; recordCond = true; } else if (flags & AddCheckRecord) { if (instr.oe) { recordOverflow = true; } if (instr.rc) { recordCond = true; } } if ((flags & AddZeroRA) && instr.rA == 0) { a.mov(a.eax, 0); } else { a.mov(a.eax, a.ppcgpr[instr.rA]); } if (flags & AddSubtract) { a.not_(a.eax); } if (flags & AddImmediate) { a.mov(a.ecx, sign_extend<16>(instr.simm)); } else if (flags & AddToZero) { a.mov(a.ecx, 0); } else if (flags & AddToMinusOne) { a.mov(a.ecx, -1); } else { a.mov(a.ecx, a.ppcgpr[instr.rB]); } if (flags & AddShifted) { a.shl(a.ecx, 16); } // Mark x64 CF based on PPC CF if (flags & AddExtended) { a.mov(a.edx, a.ppcxer); a.and_(a.edx, XERegisterBits::Carry); a.add(a.edx, 0xffffffff); a.adc(a.eax, a.ecx); } else if (flags & AddSubtract) { a.stc(); a.adc(a.eax, a.ecx); } else { a.add(a.eax, a.ecx); } if (recordCarry && recordOverflow) { a.mov(a.ecx, 0); a.setc(a.ecx.r8()); a.mov(a.edx, 0); a.seto(a.edx.r8()); a.shl(a.ecx, XERegisterBits::CarryShift); a.shl(a.edx, XERegisterBits::OverflowShift); a.or_(a.ecx, a.edx); } else if (recordCarry) { a.mov(a.ecx, 0); a.setc(a.ecx.r8()); a.shl(a.ecx, XERegisterBits::CarryShift); } else if (recordOverflow) { a.mov(a.ecx, 0); a.seto(a.ecx.r8()); a.shl(a.ecx, XERegisterBits::OverflowShift); } if (recordCarry || recordOverflow) { uint32_t mask = 0xFFFFFFFF; if (recordCarry) { mask &= ~XERegisterBits::Carry; } if (recordOverflow) { mask &= ~XERegisterBits::Overflow; } a.mov(a.edx, a.ppcxer); a.and_(a.edx, mask); a.or_(a.edx, a.ecx); a.mov(a.ppcxer, a.edx); } a.mov(a.ppcgpr[instr.rD], a.eax); if (recordCond) { updateConditionRegister(a, a.eax, a.ecx, a.edx); } return true; }
static bool cmpGeneric(PPCEmuAssembler& a, Instruction instr) { uint32_t crshift = (7 - instr.crfD) * 4; // Load CRF a.mov(a.edx, a.ppccr); // Mask CRF uint32_t mask = 0xFFFFFFFF; mask &= ~(0xF << crshift); a.and_(a.edx, mask); // Summary Overflow a.mov(a.eax, a.ppcxer); a.and_(a.eax, XERegisterBits::StickyOV); a.shr(a.eax, XERegisterBits::StickyOVShift); a.shl(a.eax, ConditionRegisterFlag::SummaryOverflowShift << crshift); a.or_(a.edx, a.eax); // Perform Comparison a.mov(a.eax, a.ppcgpr[instr.rA]); if (flags & CmpImmediate) { if (std::is_signed<Type>::value) { a.mov(a.ecx, sign_extend<16>(instr.simm)); } else { a.mov(a.ecx, instr.uimm); } } else { a.mov(a.ecx, a.ppcgpr[instr.rB]); } a.cmp(a.eax, a.ecx); a.mov(a.r8d, 0); if (std::is_unsigned<Type>::value) { a.seta(a.r8d.r8()); } else { a.setg(a.r8d.r8()); } a.mov(a.r9d, 0); if (std::is_unsigned<Type>::value) { a.setb(a.r9d.r8()); } else { a.setl(a.r9d.r8()); } a.mov(a.ecx, 0); a.sete(a.ecx.r8()); a.shl(a.ecx, crshift + ConditionRegisterFlag::ZeroShift); a.or_(a.edx, a.ecx); a.mov(a.ecx, a.r8d); a.shl(a.ecx, crshift + ConditionRegisterFlag::PositiveShift); a.or_(a.edx, a.ecx); a.mov(a.ecx, a.r9d); a.shl(a.ecx, crshift + ConditionRegisterFlag::NegativeShift); a.or_(a.edx, a.ecx); a.mov(a.ppccr, a.edx); return true; }