// 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; }
// Kernel call static bool kc(PPCEmuAssembler& a, Instruction instr) { auto id = instr.kcn; auto kc = cpu::getKernelCall(id); decaf_assert(kc, fmt::format("Encountered invalid Kernel Call ID {}", id)); // Evict all stored register as a KC might read or modify them. a.evictAll(); // Save NIA back to memory in case KC reads/writes it a.mov(a.niaMem, a.genCia + 4); // Call the KC a.mov(a.sysArgReg[0], asmjit::Ptr(kc->func)); a.mov(a.sysArgReg[1], asmjit::Ptr(kc->user_data)); a.call(asmjit::Ptr(&kc_stub)); a.mov(a.stateReg, asmjit::x86::rax); // Check if the KC adjusted nia. If it has, we need to return // to the dispatcher. Note that we assume the cache was already // cleared before this instruction since KC requires that anyways. auto niaUnchangedLbl = a.newLabel(); a.cmp(a.niaMem, a.genCia + 4); a.je(niaUnchangedLbl); a.mov(a.finaleNiaArgReg, a.niaMem); a.mov(a.finaleJmpSrcArgReg, 0); a.jmp(asmjit::Ptr(gFinaleFn)); a.bind(niaUnchangedLbl); return true; }
// 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); }
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; }