diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 606f3a9c..23e7a12b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -52,9 +52,14 @@ add_library(dynarmic frontend/A32/types.cpp frontend/A32/types.h frontend/A64/decoder/a64.h + frontend/A64/FPCR.h frontend/A64/imm.h + frontend/A64/ir_emitter.cpp + frontend/A64/ir_emitter.h frontend/A64/location_descriptor.cpp frontend/A64/location_descriptor.h + frontend/A64/translate/impl/data_processing_addsub.cpp + frontend/A64/translate/impl/impl.cpp frontend/A64/translate/impl/impl.h frontend/A64/translate/translate.cpp frontend/A64/translate/translate.h diff --git a/src/backend_x64/a32_emit_x64.h b/src/backend_x64/a32_emit_x64.h index 0bc3e3a3..958265ce 100644 --- a/src/backend_x64/a32_emit_x64.h +++ b/src/backend_x64/a32_emit_x64.h @@ -64,9 +64,11 @@ protected: // Microinstruction emitters #define OPCODE(...) #define A32OPC(name, type, ...) void EmitA32##name(A32EmitContext& ctx, IR::Inst* inst); +#define A64OPC(...) #include "frontend/ir/opcodes.inc" #undef OPCODE #undef A32OPC +#undef A64OPC // Terminal instruction emitters void EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location) override; diff --git a/src/backend_x64/a64_emit_x64.cpp b/src/backend_x64/a64_emit_x64.cpp index 37a3002f..dfca927f 100644 --- a/src/backend_x64/a64_emit_x64.cpp +++ b/src/backend_x64/a64_emit_x64.cpp @@ -115,11 +115,56 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) { return block_desc; } +void A64EmitX64::EmitA64GetW(A64EmitContext& ctx, IR::Inst* inst) { + A64::Reg reg = inst->GetArg(0).GetA64RegRef(); + + Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32(); + code->mov(result, dword[r15 + offsetof(A64JitState, reg) + sizeof(u64) * static_cast(reg)]); + ctx.reg_alloc.DefineValue(inst, result); +} + +void A64EmitX64::EmitA64GetX(A64EmitContext& ctx, IR::Inst* inst) { + A64::Reg reg = inst->GetArg(0).GetA64RegRef(); + + Xbyak::Reg64 result = ctx.reg_alloc.ScratchGpr(); + code->mov(result, qword[r15 + offsetof(A64JitState, reg) + sizeof(u64) * static_cast(reg)]); + ctx.reg_alloc.DefineValue(inst, result); +} + +void A64EmitX64::EmitA64SetW(A64EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + A64::Reg reg = inst->GetArg(0).GetA64RegRef(); + auto addr = dword[r15 + offsetof(A64JitState, reg) + sizeof(u64) * static_cast(reg)]; + if (args[1].IsImmediate()) { + code->mov(addr, args[1].GetImmediateU32()); + } else if (args[1].IsInXmm()) { + Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(args[1]); + code->movd(addr, to_store); + } else { + Xbyak::Reg32 to_store = ctx.reg_alloc.UseGpr(args[1]).cvt32(); + code->mov(addr, to_store); + } +} + +void A64EmitX64::EmitA64SetX(A64EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + A64::Reg reg = inst->GetArg(0).GetA64RegRef(); + auto addr = qword[r15 + offsetof(A64JitState, reg) + sizeof(u64) * static_cast(reg)]; + if (args[1].IsImmediate()) { + code->mov(addr, args[1].GetImmediateU64()); + } else if (args[1].IsInXmm()) { + Xbyak::Xmm to_store = ctx.reg_alloc.UseXmm(args[1]); + code->movq(addr, to_store); + } else { + Xbyak::Reg64 to_store = ctx.reg_alloc.UseGpr(args[1]); + code->mov(addr, to_store); + } +} + void A64EmitX64::EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor) { - code->mov(code->ABI_PARAM1.cvt32(), A64::LocationDescriptor{terminal.next}.PC()); - //code->mov(code->ABI_PARAM2, reinterpret_cast(jit_interface)); - //code->mov(code->ABI_PARAM3, reinterpret_cast(cb.user_arg)); - //code->mov(MJitStateReg(A64::Reg::PC), code->ABI_PARAM1.cvt32()); + code->mov(code->ABI_PARAM1, A64::LocationDescriptor{terminal.next}.PC()); + code->mov(code->ABI_PARAM2.cvt32(), 1); + code->mov(qword[r15 + offsetof(A64JitState, pc)], code->ABI_PARAM1.cvt32()); code->SwitchMxcsrOnExit(); //code->CallFunction(cb.InterpreterFallback); code->ReturnFromRunCode(true); // TODO: Check cycles @@ -144,7 +189,7 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::LinkBlock terminal, IR::LocationDesc code->SwitchToFarCode(); code->align(16); code->L(dest); - //code->mov(MJitStateReg(A64::Reg::PC), A64::LocationDescriptor{terminal.next}.PC()); + code->mov(qword[r15 + offsetof(A64JitState, pc)], A64::LocationDescriptor{terminal.next}.PC()); PushRSBHelper(rax, rbx, terminal.next); code->ForceReturnFromRunCode(); code->SwitchToNearCode(); @@ -176,23 +221,23 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDesc EmitTerminal(terminal.else_, initial_location); } -void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& /*target_desc*/, CodePtr target_code_ptr) { +void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) { const CodePtr patch_location = code->getCurr(); if (target_code_ptr) { code->jg(target_code_ptr); } else { - //code->mov(MJitStateReg(A64::Reg::PC), A64::LocationDescriptor{target_desc}.PC()); + code->mov(qword[r15 + offsetof(A64JitState, pc)], A64::LocationDescriptor{target_desc}.PC()); code->jg(code->GetReturnFromRunCodeAddress()); } code->EnsurePatchLocationSize(patch_location, 14); } -void A64EmitX64::EmitPatchJmp(const IR::LocationDescriptor& /*target_desc*/, CodePtr target_code_ptr) { +void A64EmitX64::EmitPatchJmp(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) { const CodePtr patch_location = code->getCurr(); if (target_code_ptr) { code->jmp(target_code_ptr); } else { - //code->mov(MJitStateReg(A64::Reg::PC), A64::LocationDescriptor{target_desc}.PC()); + code->mov(qword[r15 + offsetof(A64JitState, pc)], A64::LocationDescriptor{target_desc}.PC()); code->jmp(code->GetReturnFromRunCodeAddress()); } code->EnsurePatchLocationSize(patch_location, 13); diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 6c68579f..7d94c748 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -212,7 +212,7 @@ void EmitX64::EmitIsZero64(EmitContext& ctx, IR::Inst* inst) { } template -void EmitX64::EmitLogicalShiftLeft(EmitContext& ctx, IR::Inst* inst) { +void EmitX64::EmitLogicalShiftLeft32(EmitContext& ctx, IR::Inst* inst) { auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp); auto args = ctx.reg_alloc.GetArgumentInfo(inst); @@ -313,7 +313,41 @@ void EmitX64::EmitLogicalShiftLeft(EmitContext& ctx, IR::Inst* inst) { } template -void EmitX64::EmitLogicalShiftRight(EmitContext& ctx, IR::Inst* inst) { +void EmitX64::EmitLogicalShiftLeft64(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + auto& operand_arg = args[0]; + auto& shift_arg = args[1]; + + if (shift_arg.IsImmediate()) { + Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg); + u8 shift = shift_arg.GetImmediateU8(); + + if (shift < 64) { + code->shl(result, shift); + } else { + code->xor_(result.cvt32(), result.cvt32()); + } + + ctx.reg_alloc.DefineValue(inst, result); + } else { + ctx.reg_alloc.Use(shift_arg, HostLoc::RCX); + Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg); + Xbyak::Reg64 zero = ctx.reg_alloc.ScratchGpr(); + + // The x64 SHL instruction masks the shift count by 0x1F before performing the shift. + // ARM differs from the behaviour: It does not mask the count, so shifts above 31 result in zeros. + + code->shl(result, code->cl); + code->xor_(zero.cvt32(), zero.cvt32()); + code->cmp(code->cl, 64); + code->cmovnb(result, zero); + + ctx.reg_alloc.DefineValue(inst, result); + } +} + +template +void EmitX64::EmitLogicalShiftRight32(EmitContext& ctx, IR::Inst* inst) { auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp); auto args = ctx.reg_alloc.GetArgumentInfo(inst); @@ -418,19 +452,36 @@ void EmitX64::EmitLogicalShiftRight64(EmitContext& ctx, IR::Inst* inst) { auto& operand_arg = args[0]; auto& shift_arg = args[1]; - ASSERT_MSG(shift_arg.IsImmediate(), "variable 64 bit shifts are not implemented"); - ASSERT_MSG(shift_arg.GetImmediateU8() < 64, "shift width clamping is not implemented"); + if (shift_arg.IsImmediate()) { + Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg); + u8 shift = shift_arg.GetImmediateU8(); - Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg); - u8 shift = shift_arg.GetImmediateU8(); + if (shift < 64) { + code->shr(result, shift); + } else { + code->xor_(result.cvt32(), result.cvt32()); + } - code->shr(result.cvt64(), shift); + ctx.reg_alloc.DefineValue(inst, result); + } else { + ctx.reg_alloc.Use(shift_arg, HostLoc::RCX); + Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg); + Xbyak::Reg64 zero = ctx.reg_alloc.ScratchGpr(); - ctx.reg_alloc.DefineValue(inst, result); + // The x64 SHR instruction masks the shift count by 0x1F before performing the shift. + // ARM differs from the behaviour: It does not mask the count, so shifts above 31 result in zeros. + + code->shr(result, code->cl); + code->xor_(zero.cvt32(), zero.cvt32()); + code->cmp(code->cl, 64); + code->cmovnb(result, zero); + + ctx.reg_alloc.DefineValue(inst, result); + } } template -void EmitX64::EmitArithmeticShiftRight(EmitContext& ctx, IR::Inst* inst) { +void EmitX64::EmitArithmeticShiftRight32(EmitContext& ctx, IR::Inst* inst) { auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp); auto args = ctx.reg_alloc.GetArgumentInfo(inst); @@ -519,7 +570,39 @@ void EmitX64::EmitArithmeticShiftRight(EmitContext& ctx, IR::Inst* inst) { } template -void EmitX64::EmitRotateRight(EmitContext& ctx, IR::Inst* inst) { +void EmitX64::EmitArithmeticShiftRight64(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + auto& operand_arg = args[0]; + auto& shift_arg = args[1]; + + if (shift_arg.IsImmediate()) { + u8 shift = shift_arg.GetImmediateU8(); + Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg); + + code->sar(result, u8(shift < 63 ? shift : 63)); + + ctx.reg_alloc.DefineValue(inst, result); + } else { + ctx.reg_alloc.UseScratch(shift_arg, HostLoc::RCX); + Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg); + Xbyak::Reg64 const63 = ctx.reg_alloc.ScratchGpr(); + + // The 64-bit x64 SAR instruction masks the shift count by 0x3F before performing the shift. + // ARM differs from the behaviour: It does not mask the count. + + // We note that all shift values above 63 have the same behaviour as 63 does, so we saturate `shift` to 63. + code->mov(const63, 63); + code->movzx(code->ecx, code->cl); + code->cmp(code->ecx, u32(63)); + code->cmovg(code->ecx, const63); + code->sar(result, code->cl); + + ctx.reg_alloc.DefineValue(inst, result); + } +} + +template +void EmitX64::EmitRotateRight32(EmitContext& ctx, IR::Inst* inst) { auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp); auto args = ctx.reg_alloc.GetArgumentInfo(inst); @@ -598,6 +681,30 @@ void EmitX64::EmitRotateRight(EmitContext& ctx, IR::Inst* inst) { } } +template +void EmitX64::EmitRotateRight64(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + auto& operand_arg = args[0]; + auto& shift_arg = args[1]; + + if (shift_arg.IsImmediate()) { + u8 shift = shift_arg.GetImmediateU8(); + Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg); + + code->ror(result, u8(shift & 0x3F)); + + ctx.reg_alloc.DefineValue(inst, result); + } else { + ctx.reg_alloc.Use(shift_arg, HostLoc::RCX); + Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(operand_arg); + + // x64 ROR instruction does (shift & 0x3F) for us. + code->ror(result, code->cl); + + ctx.reg_alloc.DefineValue(inst, result); + } +} + template void EmitX64::EmitRotateRightExtended(EmitContext& ctx, IR::Inst* inst) { auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp); diff --git a/src/frontend/A32/translate/translate_arm/multiply.cpp b/src/frontend/A32/translate/translate_arm/multiply.cpp index 90c1e027..6c1dd436 100644 --- a/src/frontend/A32/translate/translate_arm/multiply.cpp +++ b/src/frontend/A32/translate/translate_arm/multiply.cpp @@ -48,9 +48,9 @@ bool ArmTranslatorVisitor::arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, if (ConditionPassed(cond)) { auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); - auto product = ir.Mul64(n64, m64); + auto product = ir.Mul(n64, m64); auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); - auto result = ir.Add64(product, addend); + auto result = ir.Add(product, addend); auto lo = ir.LeastSignificantWord(result); auto hi = ir.MostSignificantWord(result).result; ir.SetRegister(dLo, lo); @@ -71,7 +71,7 @@ bool ArmTranslatorVisitor::arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, if (ConditionPassed(cond)) { auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); - auto result = ir.Mul64(n64, m64); + auto result = ir.Mul(n64, m64); auto lo = ir.LeastSignificantWord(result); auto hi = ir.MostSignificantWord(result).result; ir.SetRegister(dLo, lo); @@ -94,7 +94,7 @@ bool ArmTranslatorVisitor::arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n) auto hi64 = ir.ZeroExtendWordToLong(ir.GetRegister(dHi)); auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m)); - auto result = ir.Add64(ir.Add64(ir.Mul64(n64, m64), hi64), lo64); + auto result = ir.Add(ir.Add(ir.Mul(n64, m64), hi64), lo64); ir.SetRegister(dLo, ir.LeastSignificantWord(result)); ir.SetRegister(dHi, ir.MostSignificantWord(result).result); } @@ -110,7 +110,7 @@ bool ArmTranslatorVisitor::arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m)); - auto result = ir.Add64(ir.Mul64(n64, m64), addend); + auto result = ir.Add(ir.Mul(n64, m64), addend); auto lo = ir.LeastSignificantWord(result); auto hi = ir.MostSignificantWord(result).result; ir.SetRegister(dLo, lo); @@ -131,7 +131,7 @@ bool ArmTranslatorVisitor::arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, if (ConditionPassed(cond)) { auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m)); - auto result = ir.Mul64(n64, m64); + auto result = ir.Mul(n64, m64); auto lo = ir.LeastSignificantWord(result); auto hi = ir.MostSignificantWord(result).result; ir.SetRegister(dLo, lo); @@ -160,7 +160,7 @@ bool ArmTranslatorVisitor::arm_SMLALxy(Cond cond, Reg dHi, Reg dLo, Reg m, bool : ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); auto product = ir.SignExtendWordToLong(ir.Mul(n16, m16)); auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); - auto result = ir.Add64(product, addend); + auto result = ir.Add(product, addend); ir.SetRegister(dLo, ir.LeastSignificantWord(result)); ir.SetRegister(dHi, ir.MostSignificantWord(result).result); } @@ -212,7 +212,7 @@ bool ArmTranslatorVisitor::arm_SMLAWy(Cond cond, Reg d, Reg a, Reg m, bool M, Re if (M) m32 = ir.LogicalShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; auto m16 = ir.SignExtendWordToLong(ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32))); - auto product = ir.LeastSignificantWord(ir.LogicalShiftRight64(ir.Mul64(n32, m16), ir.Imm8(16))); + auto product = ir.LeastSignificantWord(ir.LogicalShiftRight(ir.Mul(n32, m16), ir.Imm8(16))); auto result_overflow = ir.AddWithCarry(product, ir.GetRegister(a), ir.Imm1(0)); ir.SetRegister(d, result_overflow.result); ir.OrQFlag(result_overflow.overflow); @@ -229,7 +229,7 @@ bool ArmTranslatorVisitor::arm_SMULWy(Cond cond, Reg d, Reg m, bool M, Reg n) { if (M) m32 = ir.LogicalShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; auto m16 = ir.SignExtendWordToLong(ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32))); - auto result = ir.LogicalShiftRight64(ir.Mul64(n32, m16), ir.Imm8(16)); + auto result = ir.LogicalShiftRight(ir.Mul(n32, m16), ir.Imm8(16)); ir.SetRegister(d, ir.LeastSignificantWord(result)); } return true; @@ -244,7 +244,7 @@ bool ArmTranslatorVisitor::arm_SMMLA(Cond cond, Reg d, Reg a, Reg m, bool R, Reg auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); auto a64 = ir.Pack2x32To1x64(ir.Imm32(0), ir.GetRegister(a)); - auto temp = ir.Add64(a64, ir.Mul64(n64, m64)); + auto temp = ir.Add(a64, ir.Mul(n64, m64)); auto result_carry = ir.MostSignificantWord(temp); auto result = result_carry.result; if (R) @@ -261,7 +261,7 @@ bool ArmTranslatorVisitor::arm_SMMLS(Cond cond, Reg d, Reg a, Reg m, bool R, Reg auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); auto a64 = ir.Pack2x32To1x64(ir.Imm32(0), ir.GetRegister(a)); - auto temp = ir.Sub64(a64, ir.Mul64(n64, m64)); + auto temp = ir.Sub(a64, ir.Mul(n64, m64)); auto result_carry = ir.MostSignificantWord(temp); auto result = result_carry.result; if (R) @@ -277,7 +277,7 @@ bool ArmTranslatorVisitor::arm_SMMUL(Cond cond, Reg d, Reg m, bool R, Reg n) { if (ConditionPassed(cond)) { auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); - auto product = ir.Mul64(n64, m64); + auto product = ir.Mul(n64, m64); auto result_carry = ir.MostSignificantWord(product); auto result = result_carry.result; if (R) @@ -332,7 +332,7 @@ bool ArmTranslatorVisitor::arm_SMLALD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M auto product_lo = ir.SignExtendWordToLong(ir.Mul(n_lo, m_lo)); auto product_hi = ir.SignExtendWordToLong(ir.Mul(n_hi, m_hi)); auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); - auto result = ir.Add64(ir.Add64(product_lo, product_hi), addend); + auto result = ir.Add(ir.Add(product_lo, product_hi), addend); ir.SetRegister(dLo, ir.LeastSignificantWord(result)); ir.SetRegister(dHi, ir.MostSignificantWord(result).result); } @@ -380,7 +380,7 @@ bool ArmTranslatorVisitor::arm_SMLSLD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M auto product_lo = ir.SignExtendWordToLong(ir.Mul(n_lo, m_lo)); auto product_hi = ir.SignExtendWordToLong(ir.Mul(n_hi, m_hi)); auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); - auto result = ir.Add64(ir.Sub64(product_lo, product_hi), addend); + auto result = ir.Add(ir.Sub(product_lo, product_hi), addend); ir.SetRegister(dLo, ir.LeastSignificantWord(result)); ir.SetRegister(dHi, ir.MostSignificantWord(result).result); } diff --git a/src/frontend/A64/decoder/a64.h b/src/frontend/A64/decoder/a64.h index 6a0f6325..911928ed 100644 --- a/src/frontend/A64/decoder/a64.h +++ b/src/frontend/A64/decoder/a64.h @@ -373,7 +373,7 @@ std::vector> GetDecodeTable() { //INST(&V::BICS, "BICS (shifted register)", "z1101010ss1mmmmmiiiiiinnnnnddddd"), // Data Processing - Register - Add/Sub (shifted register) - //INST(&V::ADD_shift, "ADD (shifted register)", "z0001011ss0mmmmmiiiiiinnnnnddddd"), + INST(&V::ADD_shift, "ADD (shifted register)", "z0001011ss0mmmmmiiiiiinnnnnddddd"), //INST(&V::ADDS_shift, "ADDS (shifted register)", "z0101011ss0mmmmmiiiiiinnnnnddddd"), //INST(&V::SUB_shift, "SUB (shifted register)", "z1001011ss0mmmmmiiiiiinnnnnddddd"), //INST(&V::SUBS_shift, "SUBS (shifted register)", "z1101011ss0mmmmmiiiiiinnnnnddddd"), diff --git a/src/frontend/A64/imm.h b/src/frontend/A64/imm.h index 81714e72..78f137b5 100644 --- a/src/frontend/A64/imm.h +++ b/src/frontend/A64/imm.h @@ -28,13 +28,13 @@ public: template T ZeroExtend() const { - static_assert(Common::BitSize() <= bit_size); + static_assert(Common::BitSize() >= bit_size); return value; } template T SignExtend() const { - static_assert(Common::BitSize() <= bit_size); + static_assert(Common::BitSize() >= bit_size); return static_cast(Common::SignExtend(value)); } diff --git a/src/frontend/A64/translate/impl/data_processing_addsub.cpp b/src/frontend/A64/translate/impl/data_processing_addsub.cpp new file mode 100644 index 00000000..83253f0c --- /dev/null +++ b/src/frontend/A64/translate/impl/data_processing_addsub.cpp @@ -0,0 +1,31 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#include "frontend/A64/translate/impl/impl.h" + +namespace Dynarmic { +namespace A64 { + +bool TranslatorVisitor::ADD_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) { + size_t datasize = sf ? 64 : 32; + + if (shift == 0b11) return ReservedValue(); + if (!sf && imm6.Bit<5>()) return ReservedValue(); + + u8 shift_amount = imm6.ZeroExtend(); + + auto operand1 = X(datasize, Rn); + auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)); + + auto result = ir.Add(operand1, operand2); + + X(datasize, Rd, result); + + return true; +} + +} // namespace A64 +} // namespace Dynarmic diff --git a/src/frontend/A64/translate/impl/impl.cpp b/src/frontend/A64/translate/impl/impl.cpp new file mode 100644 index 00000000..0b9e24a3 --- /dev/null +++ b/src/frontend/A64/translate/impl/impl.cpp @@ -0,0 +1,70 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#include "frontend/ir/terminal.h" +#include "frontend/A64/translate/impl/impl.h" + +namespace Dynarmic { +namespace A64 { + +bool TranslatorVisitor::InterpretThisInstruction() { + ir.SetTerm(IR::Term::Interpret(ir.current_location)); + return false; +} + +bool TranslatorVisitor::UnpredictableInstruction() { + ASSERT_MSG(false, "UNPREDICTABLE"); + return false; +} + +bool TranslatorVisitor::ReservedValue() { + ASSERT_MSG(false, "RESERVEDVALUE"); + return false; +} + +IR::U32U64 TranslatorVisitor::X(size_t bitsize, Reg reg) { + switch (bitsize) { + case 32: + return ir.GetW(reg); + case 64: + return ir.GetX(reg); + default: + ASSERT_MSG(false, "X - get: Invalid bitsize"); + return {}; + } +} + +void TranslatorVisitor::X(size_t bitsize, Reg reg, IR::U32U64 value) { + switch (bitsize) { + case 32: + ir.SetW(reg, value); + return; + case 64: + ir.SetX(reg, value); + return; + default: + ASSERT_MSG(false, "X - set: Invalid bitsize"); + } +} + +IR::U32U64 TranslatorVisitor::ShiftReg(size_t bitsize, Reg reg, Imm<2> shift, IR::U8 amount) { + auto result = X(bitsize, reg); + switch (shift.ZeroExtend()) { + case 0b00: + return ir.LogicalShiftLeft(result, amount); + case 0b01: + return ir.LogicalShiftRight(result, amount); + case 0b10: + return ir.ArithmeticShiftRight(result, amount); + case 0b11: + return ir.RotateRight(result, amount); + } + ASSERT_MSG(false, "Unreachable"); + return {}; +} + +} // namespace A64 +} // namespace Dynarmic diff --git a/src/frontend/A64/translate/impl/impl.h b/src/frontend/A64/translate/impl/impl.h index 663930ff..5090e9d0 100644 --- a/src/frontend/A64/translate/impl/impl.h +++ b/src/frontend/A64/translate/impl/impl.h @@ -23,6 +23,12 @@ struct TranslatorVisitor final { bool InterpretThisInstruction(); bool UnpredictableInstruction(); + bool ReservedValue(); + + IR::U32U64 X(size_t bitsize, Reg reg); + void X(size_t bitsize, Reg reg, IR::U32U64 value); + + IR::U32U64 ShiftReg(size_t bitsize, Reg reg, Imm<2> shift, IR::U8 amount); // Data processing - Immediate - PC relative addressing bool ADR(Imm<2> immlo, Imm<19> immhi, Reg Rd); @@ -1040,4 +1046,4 @@ struct TranslatorVisitor final { }; } // namespace A64 -} // namespace Dynarmic \ No newline at end of file +} // namespace Dynarmic diff --git a/src/frontend/A64/translate/translate.cpp b/src/frontend/A64/translate/translate.cpp index 8e4ba7bc..eb998a2b 100644 --- a/src/frontend/A64/translate/translate.cpp +++ b/src/frontend/A64/translate/translate.cpp @@ -4,6 +4,7 @@ * General Public License version 2 or any later version. */ +#include "frontend/A64/decoder/a64.h" #include "frontend/A64/location_descriptor.h" #include "frontend/A64/translate/impl/impl.h" #include "frontend/A64/translate/translate.h" @@ -12,9 +13,29 @@ namespace Dynarmic { namespace A64 { -IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType /*memory_read_code*/) { - // TODO - return IR::Block{descriptor}; +IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code) { + TranslatorVisitor visitor{descriptor}; + + bool should_continue = true; + while (should_continue) { + const u64 pc = visitor.ir.current_location.PC(); + const u32 instruction = memory_read_code(pc); + + if (auto decoder = Decode(instruction)) { + should_continue = decoder->call(visitor, instruction); + } else { + should_continue = visitor.InterpretThisInstruction(); + } + + visitor.ir.current_location = visitor.ir.current_location.AdvancePC(4); + visitor.ir.block.CycleCount()++; + } + + ASSERT_MSG(visitor.ir.block.HasTerminal(), "Terminal has not been set"); + + visitor.ir.block.SetEndLocation(visitor.ir.current_location); + + return std::move(visitor.ir.block); } } // namespace A64 diff --git a/src/frontend/ir/basic_block.cpp b/src/frontend/ir/basic_block.cpp index 8fa5eb74..bb12674d 100644 --- a/src/frontend/ir/basic_block.cpp +++ b/src/frontend/ir/basic_block.cpp @@ -14,6 +14,7 @@ #include "common/assert.h" #include "frontend/A32/types.h" +#include "frontend/A64/types.h" #include "frontend/ir/basic_block.h" #include "frontend/ir/opcodes.h" @@ -22,7 +23,7 @@ namespace IR { void Block::AppendNewInst(Opcode opcode, std::initializer_list args) { IR::Inst* inst = new(instruction_alloc_pool->Alloc()) IR::Inst(opcode); - DEBUG_ASSERT(args.size() == inst->NumArgs()); + ASSERT(args.size() == inst->NumArgs()); std::for_each(args.begin(), args.end(), [&inst, index = size_t(0)](const auto& arg) mutable { inst->SetArg(index, arg); @@ -165,6 +166,10 @@ std::string DumpBlock(const IR::Block& block) { return A32::RegToString(arg.GetA32RegRef()); case Type::A32ExtReg: return A32::ExtRegToString(arg.GetA32ExtRegRef()); + case Type::A64Reg: + return A64::RegToString(arg.GetA64RegRef()); + case Type::A64Vec: + return A64::VecToString(arg.GetA64VecRef()); default: return ""; } diff --git a/src/frontend/ir/ir_emitter.cpp b/src/frontend/ir/ir_emitter.cpp index c34daeb0..2648296d 100644 --- a/src/frontend/ir/ir_emitter.cpp +++ b/src/frontend/ir/ir_emitter.cpp @@ -70,29 +70,25 @@ U1 IREmitter::IsZero64(const U64& value) { } ResultAndCarry IREmitter::LogicalShiftLeft(const U32& value_in, const U8& shift_amount, const U1& carry_in) { - auto result = Inst(Opcode::LogicalShiftLeft, value_in, shift_amount, carry_in); + auto result = Inst(Opcode::LogicalShiftLeft32, value_in, shift_amount, carry_in); auto carry_out = Inst(Opcode::GetCarryFromOp, result); return {result, carry_out}; } ResultAndCarry IREmitter::LogicalShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { - auto result = Inst(Opcode::LogicalShiftRight, value_in, shift_amount, carry_in); + auto result = Inst(Opcode::LogicalShiftRight32, value_in, shift_amount, carry_in); auto carry_out = Inst(Opcode::GetCarryFromOp, result); return {result, carry_out}; } -U64 IREmitter::LogicalShiftRight64(const U64& value_in, const U8& shift_amount) { - return Inst(Opcode::LogicalShiftRight64, value_in, shift_amount); -} - ResultAndCarry IREmitter::ArithmeticShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { - auto result = Inst(Opcode::ArithmeticShiftRight, value_in, shift_amount, carry_in); + auto result = Inst(Opcode::ArithmeticShiftRight32, value_in, shift_amount, carry_in); auto carry_out = Inst(Opcode::GetCarryFromOp, result); return {result, carry_out}; } ResultAndCarry IREmitter::RotateRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { - auto result = Inst(Opcode::RotateRight, value_in, shift_amount, carry_in); + auto result = Inst(Opcode::RotateRight32, value_in, shift_amount, carry_in); auto carry_out = Inst(Opcode::GetCarryFromOp, result); return {result, carry_out}; } @@ -103,6 +99,42 @@ ResultAndCarry IREmitter::RotateRightExtended(const U32& value_in, const U1 return {result, carry_out}; } +U64 IREmitter::LogicalShiftRight(const U64& value_in, const U8& shift_amount) { + return Inst(Opcode::LogicalShiftRight64, value_in, shift_amount); +} + +U32U64 IREmitter::LogicalShiftLeft(const U32U64& value_in, const U8& shift_amount) { + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::LogicalShiftLeft32, value_in, shift_amount, Imm1(0)); + } else { + return Inst(Opcode::LogicalShiftLeft64, value_in, shift_amount); + } +} + +U32U64 IREmitter::LogicalShiftRight(const U32U64& value_in, const U8& shift_amount) { + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::LogicalShiftRight32, value_in, shift_amount, Imm1(0)); + } else { + return Inst(Opcode::LogicalShiftRight64, value_in, shift_amount); + } +} + +U32U64 IREmitter::ArithmeticShiftRight(const U32U64& value_in, const U8& shift_amount) { + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::ArithmeticShiftRight32, value_in, shift_amount, Imm1(0)); + } else { + return Inst(Opcode::ArithmeticShiftRight64, value_in, shift_amount); + } +} + +U32U64 IREmitter::RotateRight(const U32U64& value_in, const U8& shift_amount) { + if (value_in.GetType() == Type::U32) { + return Inst(Opcode::RotateRight32, value_in, shift_amount, Imm1(0)); + } else { + return Inst(Opcode::RotateRight64, value_in, shift_amount); + } +} + ResultAndCarryAndOverflow IREmitter::AddWithCarry(const Value& a, const Value& b, const U1& carry_in) { auto result = Inst(Opcode::AddWithCarry, a, b, carry_in); auto carry_out = Inst(Opcode::GetCarryFromOp, result); @@ -114,10 +146,19 @@ U32 IREmitter::Add(const U32& a, const U32& b) { return Inst(Opcode::AddWithCarry, a, b, Imm1(0)); } -U64 IREmitter::Add64(const U64& a, const U64& b) { +U64 IREmitter::Add(const U64& a, const U64& b) { return Inst(Opcode::Add64, a, b); } +U32U64 IREmitter::Add(const U32U64& a, const U32U64& b) { + ASSERT(a.GetType() == b.GetType()); + if (a.GetType() == Type::U32) { + return Inst(Opcode::AddWithCarry, a, b, Imm1(0)); + } else { + return Inst(Opcode::Add64, a, b); + } +} + ResultAndCarryAndOverflow IREmitter::SubWithCarry(const U32& a, const U32& b, const U1& carry_in) { // This is equivalent to AddWithCarry(a, Not(b), carry_in). auto result = Inst(Opcode::SubWithCarry, a, b, carry_in); @@ -130,7 +171,7 @@ U32 IREmitter::Sub(const U32& a, const U32& b) { return Inst(Opcode::SubWithCarry, a, b, Imm1(1)); } -U64 IREmitter::Sub64(const U64& a, const U64& b) { +U64 IREmitter::Sub(const U64& a, const U64& b) { return Inst(Opcode::Sub64, a, b); } @@ -138,7 +179,7 @@ U32 IREmitter::Mul(const U32& a, const U32& b) { return Inst(Opcode::Mul, a, b); } -U64 IREmitter::Mul64(const U64& a, const U64& b) { +U64 IREmitter::Mul(const U64& a, const U64& b) { return Inst(Opcode::Mul64, a, b); } diff --git a/src/frontend/ir/ir_emitter.h b/src/frontend/ir/ir_emitter.h index e9505d8f..e0d54049 100644 --- a/src/frontend/ir/ir_emitter.h +++ b/src/frontend/ir/ir_emitter.h @@ -84,18 +84,23 @@ public: ResultAndCarry LogicalShiftLeft(const U32& value_in, const U8& shift_amount, const U1& carry_in); ResultAndCarry LogicalShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in); - U64 LogicalShiftRight64(const U64& value_in, const U8& shift_amount); ResultAndCarry ArithmeticShiftRight(const U32& value_in, const U8& shift_amount, const U1& carry_in); ResultAndCarry RotateRight(const U32& value_in, const U8& shift_amount, const U1& carry_in); + U64 LogicalShiftRight(const U64& value_in, const U8& shift_amount); + U32U64 LogicalShiftLeft(const U32U64& value_in, const U8& shift_amount); + U32U64 LogicalShiftRight(const U32U64& value_in, const U8& shift_amount); + U32U64 ArithmeticShiftRight(const U32U64& value_in, const U8& shift_amount); + U32U64 RotateRight(const U32U64& value_in, const U8& shift_amount); ResultAndCarry RotateRightExtended(const U32& value_in, const U1& carry_in); ResultAndCarryAndOverflow AddWithCarry(const Value& a, const Value& b, const U1& carry_in); U32 Add(const U32& a, const U32& b); - U64 Add64(const U64& a, const U64& b); + U64 Add(const U64& a, const U64& b); + U32U64 Add(const U32U64& a, const U32U64& b); ResultAndCarryAndOverflow SubWithCarry(const U32& a, const U32& b, const U1& carry_in); U32 Sub(const U32& a, const U32& b); - U64 Sub64(const U64& a, const U64& b); + U64 Sub(const U64& a, const U64& b); U32 Mul(const U32& a, const U32& b); - U64 Mul64(const U64& a, const U64& b); + U64 Mul(const U64& a, const U64& b); U32 And(const U32& a, const U32& b); U32 Eor(const U32& a, const U32& b); U32 Or(const U32& a, const U32& b); diff --git a/src/frontend/ir/microinstruction.cpp b/src/frontend/ir/microinstruction.cpp index dd92687c..9e0bdc8b 100644 --- a/src/frontend/ir/microinstruction.cpp +++ b/src/frontend/ir/microinstruction.cpp @@ -13,18 +13,21 @@ namespace Dynarmic { namespace IR { bool Inst::IsArithmeticShift() const { - return op == Opcode::ArithmeticShiftRight; + return op == Opcode::ArithmeticShiftRight32 || + op == Opcode::ArithmeticShiftRight64; } bool Inst::IsCircularShift() const { - return op == Opcode::RotateRight || + return op == Opcode::RotateRight32 || + op == Opcode::RotateRight64 || op == Opcode::RotateRightExtended; } bool Inst::IsLogicalShift() const { switch (op) { - case Opcode::LogicalShiftLeft: - case Opcode::LogicalShiftRight: + case Opcode::LogicalShiftLeft32: + case Opcode::LogicalShiftLeft64: + case Opcode::LogicalShiftRight32: case Opcode::LogicalShiftRight64: return true; @@ -133,6 +136,8 @@ bool Inst::ReadsFromCoreRegister() const { case Opcode::A32GetRegister: case Opcode::A32GetExtendedRegister32: case Opcode::A32GetExtendedRegister64: + case Opcode::A64GetW: + case Opcode::A64GetX: return true; default: @@ -146,6 +151,8 @@ bool Inst::WritesToCoreRegister() const { case Opcode::A32SetExtendedRegister32: case Opcode::A32SetExtendedRegister64: case Opcode::A32BXWritePC: + case Opcode::A64SetW: + case Opcode::A64SetX: return true; default: diff --git a/src/frontend/ir/opcodes.cpp b/src/frontend/ir/opcodes.cpp index c14cac96..af7d5d25 100644 --- a/src/frontend/ir/opcodes.cpp +++ b/src/frontend/ir/opcodes.cpp @@ -28,9 +28,11 @@ struct Meta { static const std::map opcode_info {{ #define OPCODE(name, type, ...) { Opcode::name, { #name, type, { __VA_ARGS__ } } }, #define A32OPC(name, type, ...) { Opcode::A32##name, { #name, type, { __VA_ARGS__ } } }, +#define A64OPC(name, type, ...) { Opcode::A64##name, { #name, type, { __VA_ARGS__ } } }, #include "opcodes.inc" #undef OPCODE #undef A32OPC +#undef A64OPC }}; } // namespace OpcodeInfo diff --git a/src/frontend/ir/opcodes.h b/src/frontend/ir/opcodes.h index fc2d0e1f..0012034d 100644 --- a/src/frontend/ir/opcodes.h +++ b/src/frontend/ir/opcodes.h @@ -18,9 +18,11 @@ namespace IR { enum class Opcode { #define OPCODE(name, type, ...) name, #define A32OPC(name, type, ...) A32##name, +#define A64OPC(name, type, ...) A64##name, #include "opcodes.inc" #undef OPCODE #undef A32OPC +#undef A64OPC NUM_OPCODE }; diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index ed04d7bc..ebb264a4 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -34,6 +34,12 @@ A32OPC(SetFpscr, T::Void, T::U32, A32OPC(GetFpscrNZCV, T::U32, ) A32OPC(SetFpscrNZCV, T::Void, T::U32, ) +// A64 Context getters/setters +A64OPC(GetW, T::U32, T::A64Reg ) +A64OPC(GetX, T::U64, T::A64Reg ) +A64OPC(SetW, T::Void, T::A64Reg, T::U32 ) +A64OPC(SetX, T::Void, T::A64Reg, T::U64 ) + // Hints OPCODE(PushRSB, T::Void, T::U64 ) @@ -51,11 +57,14 @@ OPCODE(LeastSignificantByte, T::U8, T::U32 OPCODE(MostSignificantBit, T::U1, T::U32 ) OPCODE(IsZero, T::U1, T::U32 ) OPCODE(IsZero64, T::U1, T::U64 ) -OPCODE(LogicalShiftLeft, T::U32, T::U32, T::U8, T::U1 ) -OPCODE(LogicalShiftRight, T::U32, T::U32, T::U8, T::U1 ) +OPCODE(LogicalShiftLeft32, T::U32, T::U32, T::U8, T::U1 ) +OPCODE(LogicalShiftLeft64, T::U64, T::U64, T::U8 ) +OPCODE(LogicalShiftRight32, T::U32, T::U32, T::U8, T::U1 ) OPCODE(LogicalShiftRight64, T::U64, T::U64, T::U8 ) -OPCODE(ArithmeticShiftRight, T::U32, T::U32, T::U8, T::U1 ) -OPCODE(RotateRight, T::U32, T::U32, T::U8, T::U1 ) +OPCODE(ArithmeticShiftRight32, T::U32, T::U32, T::U8, T::U1 ) +OPCODE(ArithmeticShiftRight64, T::U64, T::U64, T::U8 ) +OPCODE(RotateRight32, T::U32, T::U32, T::U8, T::U1 ) +OPCODE(RotateRight64, T::U64, T::U64, T::U8 ) OPCODE(RotateRightExtended, T::U32, T::U32, T::U1 ) OPCODE(AddWithCarry, T::U32, T::U32, T::U32, T::U1 ) OPCODE(SubWithCarry, T::U32, T::U32, T::U32, T::U1 ) diff --git a/src/frontend/ir/value.h b/src/frontend/ir/value.h index 05aa5b11..6a5404c2 100644 --- a/src/frontend/ir/value.h +++ b/src/frontend/ir/value.h @@ -6,6 +6,8 @@ #pragma once +#include + #include "common/assert.h" #include "common/common_types.h" #include "frontend/A32/types.h" @@ -76,9 +78,8 @@ class TypedValue final : public Value { public: TypedValue() : Value() {} - template + template > /* implicit */ TypedValue(const TypedValue& value) : Value(value) { - static_assert((other_type & type_) != Type::Void); ASSERT((value.GetType() & type_) != Type::Void); } @@ -92,6 +93,7 @@ using U8 = TypedValue; using U16 = TypedValue; using U32 = TypedValue; using U64 = TypedValue; +using U32U64 = TypedValue; using F32 = TypedValue; using F64 = TypedValue; using F128 = TypedValue; diff --git a/src/ir_opt/constant_propagation_pass.cpp b/src/ir_opt/constant_propagation_pass.cpp index 7e1da634..07c15971 100644 --- a/src/ir_opt/constant_propagation_pass.cpp +++ b/src/ir_opt/constant_propagation_pass.cpp @@ -23,10 +23,10 @@ void ConstantPropagation(IR::Block& block, const A32::UserCallbacks::Memory& mem } break; } - case IR::Opcode::LogicalShiftLeft: - case IR::Opcode::LogicalShiftRight: - case IR::Opcode::ArithmeticShiftRight: - case IR::Opcode::RotateRight: { + case IR::Opcode::LogicalShiftLeft32: + case IR::Opcode::LogicalShiftRight32: + case IR::Opcode::ArithmeticShiftRight32: + case IR::Opcode::RotateRight32: { if (!inst.GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)) { inst.SetArg(2, IR::Value(false)); } diff --git a/tests/A64/a64.cpp b/tests/A64/a64.cpp index 2f5dfd23..f9a555b8 100644 --- a/tests/A64/a64.cpp +++ b/tests/A64/a64.cpp @@ -1,5 +1,5 @@ /* This file is part of the dynarmic project. - * Copyright (c) 2016 MerryMage + * Copyright (c) 2018 MerryMage * This software may be used and distributed according to the terms of the GNU * General Public License version 2 or any later version. */ @@ -14,6 +14,7 @@ #include "common/common_types.h" class TestEnv final : public Dynarmic::A64::UserCallbacks { +public: u64 ticks_left = 0; std::array code_mem{}; @@ -22,7 +23,7 @@ class TestEnv final : public Dynarmic::A64::UserCallbacks { size_t index = vaddr / sizeof(u32); return code_mem[index]; } - ASSERT_MSG(false, "MemoryReadCode(%llx)", vaddr); + return 0x14000000; // B . } std::uint8_t MemoryRead8(u64 vaddr) override { ASSERT_MSG(false, "MemoryRead8(%llx)", vaddr); } @@ -49,10 +50,25 @@ class TestEnv final : public Dynarmic::A64::UserCallbacks { std::uint64_t GetTicksRemaining() override { return ticks_left; } - }; -TEST_CASE("A64", "[a64]") { +TEST_CASE("A64: ADD", "[a64]") { TestEnv env; Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; -} \ No newline at end of file + + env.code_mem[0] = 0x8b020020; // ADD X0, X1, X2 + env.code_mem[1] = 0x14000000; // B . + + jit.SetRegister(0, 0); + jit.SetRegister(1, 1); + jit.SetRegister(2, 2); + jit.SetPC(0); + + env.ticks_left = 2; + jit.Run(); + + REQUIRE(jit.GetRegister(0) == 3); + REQUIRE(jit.GetRegister(1) == 1); + REQUIRE(jit.GetRegister(2) == 2); + REQUIRE(jit.GetPC() == 4); +}