static void andGeneric(ThreadState *state, Instruction instr) { uint32_t s, a, b; s = state->gpr[instr.rS]; if (flags & AndImmediate) { b = instr.uimm; } else { b = state->gpr[instr.rB]; } if (flags & AndShifted) { b <<= 16; } if (flags & AndComplement) { b = ~b; } a = s & b; state->gpr[instr.rA] = a; if (flags & AndAlwaysRecord) { updateConditionRegister(state, a); } else if (flags & AndCheckRecord) { if (instr.rc) { updateConditionRegister(state, a); } } }
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; }
static bool mulUnsignedGeneric(PPCEmuAssembler& a, Instruction instr) { a.mov(a.eax, a.ppcgpr[instr.rA]); if (flags & MulImmediate) { a.mov(a.ecx, sign_extend<16>(instr.simm)); } else { a.mov(a.ecx, a.ppcgpr[instr.rB]); } a.mul(a.ecx); if (flags & MulLow) { a.mov(a.ppcgpr[instr.rD], a.eax); if (flags & MulCheckRecord) { if (instr.rc) { updateConditionRegister(a, a.eax, a.ecx, a.edx); } } } else if (flags & MulHigh) { a.mov(a.ppcgpr[instr.rD], a.edx); if (flags & MulCheckRecord) { if (instr.rc) { updateConditionRegister(a, a.edx, a.ecx, a.eax); } } } else { assert(0); } return true; }
static bool shiftLogical(PPCEmuAssembler& a, Instruction instr) { a.mov(a.eax, a.ppcgpr[instr.rS]); if (flags & ShiftImmediate) { if (flags & ShiftLeft) { a.shl(a.eax, instr.sh); } else if (flags & ShiftRight) { a.shr(a.eax, instr.sh); } else { assert(0); } } else { a.mov(a.ecx, a.ppcgpr[instr.rB]); if (flags & ShiftLeft) { a.shl(a.eax, a.ecx.r8()); } else if (flags & ShiftRight) { a.shr(a.eax, a.ecx.r8()); } else { assert(0); } } a.mov(a.ppcgpr[instr.rA], a.eax); 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; }
// 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; }
// Count Leading Zeroes Word static bool cntlzw(PPCEmuAssembler& a, Instruction instr) { asmjit::Label lblZero(a); a.mov(a.ecx, a.ppcgpr[instr.rS]); a.mov(a.eax, 32); a.cmp(a.ecx, 0); a.je(lblZero); a.mov(a.edx, 0); a.bsr(a.edx, a.ecx); a.dec(a.eax); a.sub(a.eax, a.edx); a.bind(lblZero); a.mov(a.ppcgpr[instr.rA], a.eax); if (instr.rc) { updateConditionRegister(a, a.eax, a.ecx, a.edx); } return true; }
// Extend Sign Half Word static void extsh(ThreadState *state, Instruction instr) { uint32_t a, s; s = state->gpr[instr.rS]; a = sign_extend<16>(s); state->gpr[instr.rA] = a; if (instr.rc) { updateConditionRegister(state, a); } }
// Equivalent static void eqv(ThreadState *state, Instruction instr) { uint32_t a, s, b; s = state->gpr[instr.rS]; b = state->gpr[instr.rB]; a = ~(s ^ b); state->gpr[instr.rA] = a; if (instr.rc) { updateConditionRegister(state, a); } }
// Extend Sign Half Word static bool extsh(PPCEmuAssembler& a, Instruction instr) { a.mov(a.eax, a.ppcgpr[instr.rS]); a.movsx(a.eax, a.eax.r16()); a.mov(a.ppcgpr[instr.rA], a.eax); if (instr.rc) { updateConditionRegister(a, a.eax, a.ecx, a.edx); } return true; }
// 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; }
// Count Leading Zeroes Word static void cntlzw(ThreadState *state, Instruction instr) { unsigned long a; uint32_t s; s = state->gpr[instr.rS]; if (!_BitScanReverse(&a, s)) { a = 32; } else { a = 31 - a; } state->gpr[instr.rA] = a; if (instr.rc) { updateConditionRegister(state, a); } }
static void divGeneric(ThreadState *state, Instruction instr) { Type a, b, d; a = state->gpr[instr.rA]; b = state->gpr[instr.rB]; d = a / b; state->gpr[instr.rD] = d; auto overflow = (b == 0); if (std::is_signed<Type>::value && (a == 0x80000000 && b == -1)) { overflow = true; } if (instr.oe) { updateOverflow(state, overflow); } if (instr.rc) { updateConditionRegister(state, d); } }
static void addGeneric(ThreadState *state, Instruction instr) { uint32_t a, b, d; // Get a value if ((flags & AddZeroRA) && instr.rA == 0) { a = 0; } else { a = state->gpr[instr.rA]; } if (flags & AddSubtract) { a = ~a; } // Get b value if (flags & AddImmediate) { b = sign_extend<16>(instr.simm); } else if (flags & AddToZero) { b = 0; } else if (flags & AddToMinusOne) { b = -1; } else { b = state->gpr[instr.rB]; } if (flags & AddShifted) { b <<= 16; } // Calculate d d = a + b; // Add xer[ca] if needed if (flags & AddExtended) { d += state->xer.ca; } else if (flags & AddSubtract) { d += 1; } // Update rD state->gpr[instr.rD] = d; // Check for carry/overflow auto carry = d < a; auto overflow = !!get_bit<31>((a ^ d) & (b ^ d)); if (flags & AddCarry) { updateCarry(state, carry); } if (flags & AddAlwaysRecord) { updateOverflow(state, overflow); updateConditionRegister(state, d); } else if (flags & AddCheckRecord) { if (instr.oe) { updateOverflow(state, overflow); } if (instr.rc) { updateConditionRegister(state, d); } } }
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; }