From cb481a3a4851ea9b84b5627bc65e122d3f78acfd Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 7 Jan 2018 16:33:02 +0000 Subject: [PATCH] A64: Implement compare and branch --- src/backend_x64/a32_emit_x64.cpp | 4 + src/backend_x64/a32_emit_x64.h | 1 + src/backend_x64/a64_emit_x64.cpp | 15 ++++ src/backend_x64/a64_emit_x64.h | 1 + src/backend_x64/a64_jitstate.h | 1 + src/backend_x64/emit_x64.cpp | 13 +++- src/backend_x64/emit_x64.h | 1 + src/common/bit_util.h | 2 +- .../A32/translate/translate_arm/multiply.cpp | 8 +- src/frontend/A64/decoder/a64.h | 8 +- src/frontend/A64/ir_emitter.cpp | 4 + src/frontend/A64/ir_emitter.h | 1 + src/frontend/A64/translate/impl/branch.cpp | 62 +++++++++++++++ src/frontend/ir/basic_block.cpp | 4 + src/frontend/ir/ir_emitter.cpp | 30 +++++++- src/frontend/ir/ir_emitter.h | 6 +- src/frontend/ir/microinstruction.cpp | 15 ++-- src/frontend/ir/opcodes.inc | 4 +- src/frontend/ir/terminal.h | 13 ++++ tests/A64/a64.cpp | 77 +++++++++++++++++++ 20 files changed, 249 insertions(+), 21 deletions(-) diff --git a/src/backend_x64/a32_emit_x64.cpp b/src/backend_x64/a32_emit_x64.cpp index 037899d0..7291743c 100644 --- a/src/backend_x64/a32_emit_x64.cpp +++ b/src/backend_x64/a32_emit_x64.cpp @@ -1161,6 +1161,10 @@ void A32EmitX64::EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor EmitTerminal(terminal.then_, initial_location); } +void A32EmitX64::EmitTerminalImpl(IR::Term::CheckBit, IR::LocationDescriptor) { + ASSERT_MSG(false, "Term::CheckBit should never be emitted by the A32 frontend"); +} + void A32EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) { code->cmp(code->byte[r15 + offsetof(A32JitState, halt_requested)], u8(0)); code->jne(code->GetForceReturnFromRunCodeAddress()); diff --git a/src/backend_x64/a32_emit_x64.h b/src/backend_x64/a32_emit_x64.h index 958265ce..6d075c52 100644 --- a/src/backend_x64/a32_emit_x64.h +++ b/src/backend_x64/a32_emit_x64.h @@ -77,6 +77,7 @@ protected: void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) override; void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location) override; void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) override; + void EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) override; void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) override; // Patching diff --git a/src/backend_x64/a64_emit_x64.cpp b/src/backend_x64/a64_emit_x64.cpp index aadd950e..78701559 100644 --- a/src/backend_x64/a64_emit_x64.cpp +++ b/src/backend_x64/a64_emit_x64.cpp @@ -115,6 +115,12 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) { return block_desc; } +void A64EmitX64::EmitA64SetCheckBit(A64EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + Xbyak::Reg8 to_store = ctx.reg_alloc.UseGpr(args[0]).cvt8(); + code->mov(code->byte[r15 + offsetof(A64JitState, check_bit)], to_store); +} + void A64EmitX64::EmitA64GetCFlag(A64EmitContext& ctx, IR::Inst* inst) { Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32(); code->mov(result, dword[r15 + offsetof(A64JitState, CPSR_nzcv)]); @@ -267,6 +273,15 @@ void A64EmitX64::EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor EmitTerminal(terminal.then_, initial_location); } +void A64EmitX64::EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) { + Xbyak::Label fail; + code->cmp(code->byte[r15 + offsetof(A64JitState, check_bit)], u8(0)); + code->jz(fail); + EmitTerminal(terminal.then_, initial_location); + code->L(fail); + EmitTerminal(terminal.else_, initial_location); +} + void A64EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) { code->cmp(code->byte[r15 + offsetof(A64JitState, halt_requested)], u8(0)); code->jne(code->GetForceReturnFromRunCodeAddress()); diff --git a/src/backend_x64/a64_emit_x64.h b/src/backend_x64/a64_emit_x64.h index adaa2f79..c95ac04d 100644 --- a/src/backend_x64/a64_emit_x64.h +++ b/src/backend_x64/a64_emit_x64.h @@ -66,6 +66,7 @@ protected: void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) override; void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location) override; void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) override; + void EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) override; void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) override; // Patching diff --git a/src/backend_x64/a64_jitstate.h b/src/backend_x64/a64_jitstate.h index d5375c68..9b549655 100644 --- a/src/backend_x64/a64_jitstate.h +++ b/src/backend_x64/a64_jitstate.h @@ -55,6 +55,7 @@ struct A64JitState { s64 cycles_to_run = 0; s64 cycles_remaining = 0; bool halt_requested = false; + bool check_bit = false; static constexpr size_t RSBSize = 8; // MUST be a power of 2. static constexpr size_t RSBPtrMask = RSBSize - 1; diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 27b94587..1dda7d08 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -218,7 +218,7 @@ void EmitX64::EmitMostSignificantBit(EmitContext& ctx, IR::Inst* inst) { } template -void EmitX64::EmitIsZero(EmitContext& ctx, IR::Inst* inst) { +void EmitX64::EmitIsZero32(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); Xbyak::Reg32 result = ctx.reg_alloc.UseScratchGpr(args[0]).cvt32(); // TODO: Flag optimization @@ -239,6 +239,17 @@ void EmitX64::EmitIsZero64(EmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.DefineValue(inst, result); } +template +void EmitX64::EmitTestBit(EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + Xbyak::Reg64 result = ctx.reg_alloc.UseScratchGpr(args[0]); + ASSERT(args[1].IsImmediate()); + // TODO: Flag optimization + code->bt(result, args[1].GetImmediateU8()); + code->setc(result.cvt8()); + ctx.reg_alloc.DefineValue(inst, result); +} + template void EmitX64::EmitLogicalShiftLeft32(EmitContext& ctx, IR::Inst* inst) { auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp); diff --git a/src/backend_x64/emit_x64.h b/src/backend_x64/emit_x64.h index 541d3e15..1ced7a7e 100644 --- a/src/backend_x64/emit_x64.h +++ b/src/backend_x64/emit_x64.h @@ -92,6 +92,7 @@ protected: virtual void EmitTerminalImpl(IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location) = 0; virtual void EmitTerminalImpl(IR::Term::PopRSBHint terminal, IR::LocationDescriptor initial_location) = 0; virtual void EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor initial_location) = 0; + virtual void EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) = 0; virtual void EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) = 0; // Patching diff --git a/src/common/bit_util.h b/src/common/bit_util.h index 7bd09fff..40de5d12 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h @@ -25,7 +25,7 @@ constexpr size_t BitSize() { /// Extract bits [begin_bit, end_bit] inclusive from value of type T. template constexpr T Bits(const T value) { - static_assert(begin_bit < end_bit, + static_assert(begin_bit <= end_bit, "invalid bit range (position of beginning bit cannot be greater than that of end bit)"); static_assert(begin_bit < BitSize(), "begin_bit must be smaller than size of T"); static_assert(end_bit < BitSize(), "begin_bit must be smaller than size of T"); diff --git a/src/frontend/A32/translate/translate_arm/multiply.cpp b/src/frontend/A32/translate/translate_arm/multiply.cpp index 6c1dd436..650ea92e 100644 --- a/src/frontend/A32/translate/translate_arm/multiply.cpp +++ b/src/frontend/A32/translate/translate_arm/multiply.cpp @@ -57,7 +57,7 @@ bool ArmTranslatorVisitor::arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, ir.SetRegister(dHi, hi); if (S) { ir.SetNFlag(ir.MostSignificantBit(hi)); - ir.SetZFlag(ir.IsZero64(result)); + ir.SetZFlag(ir.IsZero(result)); } } return true; @@ -78,7 +78,7 @@ bool ArmTranslatorVisitor::arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, ir.SetRegister(dHi, hi); if (S) { ir.SetNFlag(ir.MostSignificantBit(hi)); - ir.SetZFlag(ir.IsZero64(result)); + ir.SetZFlag(ir.IsZero(result)); } } return true; @@ -117,7 +117,7 @@ bool ArmTranslatorVisitor::arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, ir.SetRegister(dHi, hi); if (S) { ir.SetNFlag(ir.MostSignificantBit(hi)); - ir.SetZFlag(ir.IsZero64(result)); + ir.SetZFlag(ir.IsZero(result)); } } return true; @@ -138,7 +138,7 @@ bool ArmTranslatorVisitor::arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, ir.SetRegister(dHi, hi); if (S) { ir.SetNFlag(ir.MostSignificantBit(hi)); - ir.SetZFlag(ir.IsZero64(result)); + ir.SetZFlag(ir.IsZero(result)); } } return true; diff --git a/src/frontend/A64/decoder/a64.h b/src/frontend/A64/decoder/a64.h index 70f5b233..6cd3e6bb 100644 --- a/src/frontend/A64/decoder/a64.h +++ b/src/frontend/A64/decoder/a64.h @@ -117,10 +117,10 @@ std::vector> GetDecodeTable() { INST(&V::BL, "BL", "100101iiiiiiiiiiiiiiiiiiiiiiiiii"), // Compare and branch (immediate) - //INST(&V::CBZ, "CBZ", "z0110100iiiiiiiiiiiiiiiiiiittttt"), - //INST(&V::CBNZ, "CBNZ", "z0110101iiiiiiiiiiiiiiiiiiittttt"), - //INST(&V::TBZ, "TBZ", "b0110110bbbbbiiiiiiiiiiiiiittttt"), - //INST(&V::TBNZ, "TBNZ", "b0110111bbbbbiiiiiiiiiiiiiittttt"), + INST(&V::CBZ, "CBZ", "z0110100iiiiiiiiiiiiiiiiiiittttt"), + INST(&V::CBNZ, "CBNZ", "z0110101iiiiiiiiiiiiiiiiiiittttt"), + INST(&V::TBZ, "TBZ", "b0110110bbbbbiiiiiiiiiiiiiittttt"), + INST(&V::TBNZ, "TBNZ", "b0110111bbbbbiiiiiiiiiiiiiittttt"), // Loads and stores - Advanced SIMD Load/Store multiple structures //INST(&V::ST4_mult_1, "ST4 (multiple structures)", "0Q001100000000000000zznnnnnttttt"), diff --git a/src/frontend/A64/ir_emitter.cpp b/src/frontend/A64/ir_emitter.cpp index 41e6d4af..46425e78 100644 --- a/src/frontend/A64/ir_emitter.cpp +++ b/src/frontend/A64/ir_emitter.cpp @@ -22,6 +22,10 @@ u64 IREmitter::AlignPC(size_t alignment) { return static_cast(pc - pc % alignment); } +void IREmitter::SetCheckBit(const IR::U1& value) { + Inst(Opcode::A64SetCheckBit, value); +} + IR::U1 IREmitter::GetCFlag() { return Inst(Opcode::A64GetCFlag); } diff --git a/src/frontend/A64/ir_emitter.h b/src/frontend/A64/ir_emitter.h index d840aaa6..e3cb6c14 100644 --- a/src/frontend/A64/ir_emitter.h +++ b/src/frontend/A64/ir_emitter.h @@ -31,6 +31,7 @@ public: u64 PC(); u64 AlignPC(size_t alignment); + void SetCheckBit(const IR::U1& value); IR::U1 GetCFlag(); void SetNZCV(const IR::NZCV& nzcv); diff --git a/src/frontend/A64/translate/impl/branch.cpp b/src/frontend/A64/translate/impl/branch.cpp index d32340cc..59cb39ca 100644 --- a/src/frontend/A64/translate/impl/branch.cpp +++ b/src/frontend/A64/translate/impl/branch.cpp @@ -62,5 +62,67 @@ bool TranslatorVisitor::RET(Reg Rn) { return false; } +bool TranslatorVisitor::CBZ(bool sf, Imm<19> imm19, Reg Rt) { + size_t datasize = sf ? 64 : 32; + s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend(); + + auto operand1 = X(datasize, Rt); + + ir.SetCheckBit(ir.IsZero(operand1)); + + u64 target = ir.PC() + offset; + auto cond_pass = IR::Term::LinkBlock{ir.current_location.SetPC(target)}; + auto cond_fail = IR::Term::LinkBlock{ir.current_location.AdvancePC(4)}; + ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail}); + return false; +} + +bool TranslatorVisitor::CBNZ(bool sf, Imm<19> imm19, Reg Rt) { + size_t datasize = sf ? 64 : 32; + s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend(); + + auto operand1 = X(datasize, Rt); + + ir.SetCheckBit(ir.IsZero(operand1)); + + u64 target = ir.PC() + offset; + auto cond_pass = IR::Term::LinkBlock{ir.current_location.AdvancePC(4)}; + auto cond_fail = IR::Term::LinkBlock{ir.current_location.SetPC(target)}; + ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail}); + return false; +} + +bool TranslatorVisitor::TBZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) { + size_t datasize = b5 == 1 ? 64 : 32; + size_t bit_pos = concatenate(b5, b40).ZeroExtend(); + s64 offset = concatenate(imm14, Imm<2>{0}).SignExtend(); + + auto operand = X(datasize, Rt); + + ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos))); + + u64 target = ir.PC() + offset; + auto cond_1 = IR::Term::LinkBlock{ir.current_location.AdvancePC(4)}; + auto cond_0 = IR::Term::LinkBlock{ir.current_location.SetPC(target)}; + ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0}); + return false; +} + +bool TranslatorVisitor::TBNZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) { + size_t datasize = b5 == 1 ? 64 : 32; + size_t bit_pos = concatenate(b5, b40).ZeroExtend(); + s64 offset = concatenate(imm14, Imm<2>{0}).SignExtend(); + + auto operand = X(datasize, Rt); + + ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos))); + + u64 target = ir.PC() + offset; + auto cond_1 = IR::Term::LinkBlock{ir.current_location.SetPC(target)}; + auto cond_0 = IR::Term::LinkBlock{ir.current_location.AdvancePC(4)}; + ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0}); + return false; +} + } // namespace A64 } // namespace Dynarmic diff --git a/src/frontend/ir/basic_block.cpp b/src/frontend/ir/basic_block.cpp index bb12674d..b3a91c55 100644 --- a/src/frontend/ir/basic_block.cpp +++ b/src/frontend/ir/basic_block.cpp @@ -127,6 +127,10 @@ static std::string TerminalToString(const Terminal& terminal_variant) { return fmt::format("If{{{}, {}, {}}}", A32::CondToString(terminal.if_), TerminalToString(terminal.then_), TerminalToString(terminal.else_)); } case 7: { + auto terminal = boost::get(terminal_variant); + return fmt::format("CheckBit{{{}, {}}}", TerminalToString(terminal.then_), TerminalToString(terminal.else_)); + } + case 8: { auto terminal = boost::get(terminal_variant); return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_)); } diff --git a/src/frontend/ir/ir_emitter.cpp b/src/frontend/ir/ir_emitter.cpp index 88e8044d..9268ce08 100644 --- a/src/frontend/ir/ir_emitter.cpp +++ b/src/frontend/ir/ir_emitter.cpp @@ -68,13 +68,29 @@ U1 IREmitter::MostSignificantBit(const U32& value) { } U1 IREmitter::IsZero(const U32& value) { - return Inst(Opcode::IsZero, value); + return Inst(Opcode::IsZero32, value); } -U1 IREmitter::IsZero64(const U64& value) { +U1 IREmitter::IsZero(const U64& value) { return Inst(Opcode::IsZero64, value); } +U1 IREmitter::IsZero(const U32U64& value) { + if (value.GetType() == Type::U32) { + return Inst(Opcode::IsZero32, value); + } else { + return Inst(Opcode::IsZero64, value); + } +} + +U1 IREmitter::TestBit(const U32U64& value, const U8& bit) { + if (value.GetType() == Type::U32) { + return Inst(Opcode::TestBit, IndeterminateExtendToLong(value), bit); + } else { + return Inst(Opcode::TestBit, value, bit); + } +} + NZCV IREmitter::NZCVFrom(const Value& value) { return Inst(Opcode::GetNZCVFromOp, value); } @@ -359,6 +375,16 @@ U32 IREmitter::ZeroExtendByteToWord(const U8& a) { return Inst(Opcode::ZeroExtendByteToWord, a); } +U32 IREmitter::IndeterminateExtendToWord(const UAny& a) { + // TODO: Implement properly + return ZeroExtendToWord(a); +} + +U64 IREmitter::IndeterminateExtendToLong(const UAny& a) { + // TODO: Implement properly + return ZeroExtendToLong(a); +} + U32 IREmitter::ByteReverseWord(const U32& a) { return Inst(Opcode::ByteReverseWord, a); } diff --git a/src/frontend/ir/ir_emitter.h b/src/frontend/ir/ir_emitter.h index 8a831d2b..da83a063 100644 --- a/src/frontend/ir/ir_emitter.h +++ b/src/frontend/ir/ir_emitter.h @@ -80,7 +80,9 @@ public: U8 LeastSignificantByte(U32U64 value); U1 MostSignificantBit(const U32& value); U1 IsZero(const U32& value); - U1 IsZero64(const U64& value); + U1 IsZero(const U64& value); + U1 IsZero(const U32U64& value); + U1 TestBit(const U32U64& value, const U8& bit); // This pseudo-instruction may only be added to instructions that support it. NZCV NZCVFrom(const Value& value); @@ -125,6 +127,8 @@ public: U32 ZeroExtendByteToWord(const U8& a); U32 ZeroExtendHalfToWord(const U16& a); U64 ZeroExtendWordToLong(const U32& a); + U32 IndeterminateExtendToWord(const UAny& a); + U64 IndeterminateExtendToLong(const UAny& a); U32 ByteReverseWord(const U32& a); U16 ByteReverseHalf(const U16& a); U64 ByteReverseDual(const U64& a); diff --git a/src/frontend/ir/microinstruction.cpp b/src/frontend/ir/microinstruction.cpp index 3481ffbc..63b358f2 100644 --- a/src/frontend/ir/microinstruction.cpp +++ b/src/frontend/ir/microinstruction.cpp @@ -246,13 +246,14 @@ bool Inst::IsCoprocessorInstruction() const { } bool Inst::MayHaveSideEffects() const { - return op == Opcode::PushRSB || - CausesCPUException() || - WritesToCoreRegister() || - WritesToCPSR() || - WritesToFPSCR() || - AltersExclusiveState() || - IsMemoryWrite() || + return op == Opcode::PushRSB || + op == Opcode::A64SetCheckBit || + CausesCPUException() || + WritesToCoreRegister() || + WritesToCPSR() || + WritesToFPSCR() || + AltersExclusiveState() || + IsMemoryWrite() || IsCoprocessorInstruction(); } diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index 23170529..e9ece807 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -35,6 +35,7 @@ A32OPC(GetFpscrNZCV, T::U32, A32OPC(SetFpscrNZCV, T::Void, T::U32, ) // A64 Context getters/setters +A64OPC(SetCheckBit, T::Void, T::U1 ) A64OPC(GetCFlag, T::U1, ) A64OPC(SetNZCV, T::Void, T::NZCVFlags ) A64OPC(GetW, T::U32, T::A64Reg ) @@ -61,8 +62,9 @@ OPCODE(MostSignificantWord, T::U32, T::U64 OPCODE(LeastSignificantHalf, T::U16, T::U32 ) OPCODE(LeastSignificantByte, T::U8, T::U32 ) OPCODE(MostSignificantBit, T::U1, T::U32 ) -OPCODE(IsZero, T::U1, T::U32 ) +OPCODE(IsZero32, T::U1, T::U32 ) OPCODE(IsZero64, T::U1, T::U64 ) +OPCODE(TestBit, T::U1, T::U64, T::U8 ) 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 ) diff --git a/src/frontend/ir/terminal.h b/src/frontend/ir/terminal.h index 7cccdd3d..48deaec3 100644 --- a/src/frontend/ir/terminal.h +++ b/src/frontend/ir/terminal.h @@ -66,6 +66,7 @@ struct LinkBlockFast { struct PopRSBHint {}; struct If; +struct CheckBit; struct CheckHalt; /// A Terminal is the terminal instruction in a MicroBlock. using Terminal = boost::variant< @@ -76,6 +77,7 @@ using Terminal = boost::variant< LinkBlockFast, PopRSBHint, boost::recursive_wrapper, + boost::recursive_wrapper, boost::recursive_wrapper >; @@ -90,6 +92,17 @@ struct If { Terminal else_; }; +/** + * This terminal instruction conditionally executes one terminal or another depending + * on the run-time state of the check bit. + * then_ is executed if the check bit is non-zero, otherwise else_ is executed. + */ +struct CheckBit { + CheckBit(Terminal then_, Terminal else_) : then_(then_), else_(else_) {} + Terminal then_; + Terminal else_; +}; + /** * This terminal instruction checks if a halt was requested. If it wasn't, else_ is * executed. diff --git a/tests/A64/a64.cpp b/tests/A64/a64.cpp index dde62907..dad967fb 100644 --- a/tests/A64/a64.cpp +++ b/tests/A64/a64.cpp @@ -168,3 +168,80 @@ TEST_CASE("A64: ANDS NZCV", "[a64]") { REQUIRE((jit.GetPstate() & 0xF0000000) == 0x00000000); } } + +TEST_CASE("A64: CBZ", "[a64]") { + TestEnv env; + Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; + + env.code_mem[0] = 0x34000060; // CBZ X0, label + env.code_mem[1] = 0x320003e2; // MOV X2, 1 + env.code_mem[2] = 0x14000000; // B. + env.code_mem[3] = 0x321f03e2; // label: MOV X2, 2 + env.code_mem[4] = 0x14000000; // B . + + SECTION("no branch") { + jit.SetPC(0); + jit.SetRegister(0, 1); + + env.ticks_left = 4; + jit.Run(); + + REQUIRE(jit.GetRegister(2) == 1); + REQUIRE(jit.GetPC() == 8); + } + + SECTION("branch") { + jit.SetPC(0); + jit.SetRegister(0, 0); + + env.ticks_left = 4; + jit.Run(); + + REQUIRE(jit.GetRegister(2) == 2); + REQUIRE(jit.GetPC() == 16); + } +} + +TEST_CASE("A64: TBZ", "[a64]") { + TestEnv env; + Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; + + env.code_mem[0] = 0x36180060; // TBZ X0, 3, label + env.code_mem[1] = 0x320003e2; // MOV X2, 1 + env.code_mem[2] = 0x14000000; // B . + env.code_mem[3] = 0x321f03e2; // label: MOV X2, 2 + env.code_mem[4] = 0x14000000; // B . + + SECTION("no branch") { + jit.SetPC(0); + jit.SetRegister(0, 0xFF); + + env.ticks_left = 4; + jit.Run(); + + REQUIRE(jit.GetRegister(2) == 1); + REQUIRE(jit.GetPC() == 8); + } + + SECTION("branch with zero") { + jit.SetPC(0); + jit.SetRegister(0, 0); + + env.ticks_left = 4; + jit.Run(); + + REQUIRE(jit.GetRegister(2) == 2); + REQUIRE(jit.GetPC() == 16); + } + + SECTION("branch with non-zero") { + jit.SetPC(0); + jit.SetRegister(0, 1); + + env.ticks_left = 4; + jit.Run(); + + REQUIRE(jit.GetRegister(2) == 2); + REQUIRE(jit.GetPC() == 16); + } +}