diff --git a/include/dynarmic/A64/a64.h b/include/dynarmic/A64/a64.h index afe70d8c..189e2857 100644 --- a/include/dynarmic/A64/a64.h +++ b/include/dynarmic/A64/a64.h @@ -83,6 +83,11 @@ public: /// Modify FPCR. void SetFpcr(std::uint32_t value); + /// View PSTATE + std::uint32_t GetPstate() const; + /// Modify PSTATE + void SetPstate(std::uint32_t value); + /** * Returns true if Jit::Run was called but hasn't returned yet. * i.e.: We're in a callback. diff --git a/src/backend_x64/a64_emit_x64.cpp b/src/backend_x64/a64_emit_x64.cpp index 1719a0f1..aadd950e 100644 --- a/src/backend_x64/a64_emit_x64.cpp +++ b/src/backend_x64/a64_emit_x64.cpp @@ -128,7 +128,8 @@ void A64EmitX64::EmitA64SetNZCV(A64EmitContext& ctx, IR::Inst* inst) { Xbyak::Reg32 to_store = ctx.reg_alloc.UseGpr(args[0]).cvt32(); code->and_(to_store, 0b11000001'00000001); code->imul(to_store, to_store, 0b00010000'00100001); - code->and_(to_store, 0xFF000000); + code->shl(to_store, 16); + code->and_(to_store, 0xF0000000); code->mov(dword[r15 + offsetof(A64JitState, CPSR_nzcv)], to_store); } diff --git a/src/backend_x64/a64_interface.cpp b/src/backend_x64/a64_interface.cpp index 0d5d164f..9208c516 100644 --- a/src/backend_x64/a64_interface.cpp +++ b/src/backend_x64/a64_interface.cpp @@ -125,6 +125,14 @@ public: jit_state.SetFpcr(value); } + u32 GetPstate() const { + return jit_state.GetPstate(); + } + + void SetPstate(u32 value) { + jit_state.SetPstate(value); + } + bool IsExecuting() const { return is_executing; } @@ -257,6 +265,14 @@ void Jit::SetFpcr(u32 value) { impl->SetFpcr(value); } +u32 Jit::GetPstate() const { + return impl->GetPstate(); +} + +void Jit::SetPstate(u32 value) { + impl->SetPstate(value); +} + bool Jit::IsExecuting() const { return impl->IsExecuting(); } diff --git a/src/backend_x64/a64_jitstate.h b/src/backend_x64/a64_jitstate.h index fe830b23..d5375c68 100644 --- a/src/backend_x64/a64_jitstate.h +++ b/src/backend_x64/a64_jitstate.h @@ -33,6 +33,12 @@ struct A64JitState { u32 CPSR_nzcv = 0; u32 FPSCR_nzcv = 0; + u32 GetPstate() const { + return CPSR_nzcv; + } + void SetPstate(u32 new_pstate) { + CPSR_nzcv = new_pstate & 0xF0000000; + } alignas(16) std::array vec{}; // Extension registers. diff --git a/src/frontend/ir/microinstruction.cpp b/src/frontend/ir/microinstruction.cpp index dda2de02..3481ffbc 100644 --- a/src/frontend/ir/microinstruction.cpp +++ b/src/frontend/ir/microinstruction.cpp @@ -124,6 +124,7 @@ bool Inst::WritesToCPSR() const { case Opcode::A32OrQFlag: case Opcode::A32SetGEFlags: case Opcode::A32SetGEFlagsCompressed: + case Opcode::A64SetNZCV: return true; default: @@ -387,7 +388,7 @@ void Inst::Use(const Value& value) { break; case Opcode::GetNZCVFromOp: ASSERT_MSG(!value.GetInst()->nzcv_inst, "Only one of each type of pseudo-op allowed"); - ASSERT_MSG(MayGetNZCVFromOp(), "This instruction doesn't support the GetNZCVFromOp pseduo-op"); + ASSERT_MSG(value.GetInst()->MayGetNZCVFromOp(), "This value doesn't support the GetNZCVFromOp pseduo-op"); value.GetInst()->nzcv_inst = this; break; default: diff --git a/tests/A64/a64.cpp b/tests/A64/a64.cpp index 809a9264..dde62907 100644 --- a/tests/A64/a64.cpp +++ b/tests/A64/a64.cpp @@ -113,3 +113,58 @@ TEST_CASE("A64: Bitmasks", "[a64]") { REQUIRE(jit.GetRegister(2) == 1); REQUIRE(jit.GetPC() == 12); } + +TEST_CASE("A64: ANDS NZCV", "[a64]") { + TestEnv env; + Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; + + env.code_mem[0] = 0x6a020020; // ANDS W0, W1, W2 + env.code_mem[1] = 0x14000000; // B . + + SECTION("N=1, Z=0") { + jit.SetRegister(0, 0); + jit.SetRegister(1, 0xFFFFFFFF); + jit.SetRegister(2, 0xFFFFFFFF); + jit.SetPC(0); + + env.ticks_left = 2; + jit.Run(); + + REQUIRE(jit.GetRegister(0) == 0xFFFFFFFF); + REQUIRE(jit.GetRegister(1) == 0xFFFFFFFF); + REQUIRE(jit.GetRegister(2) == 0xFFFFFFFF); + REQUIRE(jit.GetPC() == 4); + REQUIRE((jit.GetPstate() & 0xF0000000) == 0x80000000); + } + + SECTION("N=0, Z=1") { + jit.SetRegister(0, 0); + jit.SetRegister(1, 0xFFFFFFFF); + jit.SetRegister(2, 0x00000000); + jit.SetPC(0); + + env.ticks_left = 2; + jit.Run(); + + REQUIRE(jit.GetRegister(0) == 0x00000000); + REQUIRE(jit.GetRegister(1) == 0xFFFFFFFF); + REQUIRE(jit.GetRegister(2) == 0x00000000); + REQUIRE(jit.GetPC() == 4); + REQUIRE((jit.GetPstate() & 0xF0000000) == 0x40000000); + } + SECTION("N=0, Z=0") { + jit.SetRegister(0, 0); + jit.SetRegister(1, 0x12345678); + jit.SetRegister(2, 0x7324a993); + jit.SetPC(0); + + env.ticks_left = 2; + jit.Run(); + + REQUIRE(jit.GetRegister(0) == 0x12240010); + REQUIRE(jit.GetRegister(1) == 0x12345678); + REQUIRE(jit.GetRegister(2) == 0x7324a993); + REQUIRE(jit.GetPC() == 4); + REQUIRE((jit.GetPstate() & 0xF0000000) == 0x00000000); + } +}