diff --git a/src/backend_x64/a64_emit_x64.cpp b/src/backend_x64/a64_emit_x64.cpp index 188cf0f0..26aec5ea 100644 --- a/src/backend_x64/a64_emit_x64.cpp +++ b/src/backend_x64/a64_emit_x64.cpp @@ -522,6 +522,7 @@ void A64EmitX64::EmitA64DataMemoryBarrier(A64EmitContext&, IR::Inst*) { void A64EmitX64::EmitA64GetCNTPCT(A64EmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.HostCall(inst); + code.UpdateTicks(); DEVIRT(conf.callbacks, &A64::UserCallbacks::GetCNTPCT).EmitCall(code); } diff --git a/src/backend_x64/block_of_code.cpp b/src/backend_x64/block_of_code.cpp index 505998ea..b7c4035f 100644 --- a/src/backend_x64/block_of_code.cpp +++ b/src/backend_x64/block_of_code.cpp @@ -189,6 +189,17 @@ void BlockOfCode::SwitchMxcsrOnExit() { ldmxcsr(dword[r15 + jsi.offsetof_save_host_MXCSR]); } +void BlockOfCode::UpdateTicks() { + cb.AddTicks->EmitCall(*this, [this](RegList param) { + mov(param[0], qword[r15 + jsi.offsetof_cycles_to_run]); + sub(param[0], qword[r15 + jsi.offsetof_cycles_remaining]); + }); + + cb.GetTicksRemaining->EmitCall(*this); + mov(qword[r15 + jsi.offsetof_cycles_to_run], ABI_RETURN); + mov(qword[r15 + jsi.offsetof_cycles_remaining], ABI_RETURN); +} + Xbyak::Address BlockOfCode::MConst(const Xbyak::AddressFrame& frame, u64 lower, u64 upper) { return constant_pool.GetConstant(frame, lower, upper); } diff --git a/src/backend_x64/block_of_code.h b/src/backend_x64/block_of_code.h index 6b6eb6e0..95a247e3 100644 --- a/src/backend_x64/block_of_code.h +++ b/src/backend_x64/block_of_code.h @@ -51,6 +51,9 @@ public: void SwitchMxcsrOnEntry(); /// Code emitter: Makes saved host MXCSR the current MXCSR void SwitchMxcsrOnExit(); + /// Code emitter: Updates cycles remaining my calling cb.AddTicks and cb.GetTicksRemaining + /// @note this clobbers ABI callee-save registers + void UpdateTicks(); /// Code emitter: Calls the function template diff --git a/src/frontend/A64/translate/impl/system.cpp b/src/frontend/A64/translate/impl/system.cpp index 9368d537..859bc44e 100644 --- a/src/frontend/A64/translate/impl/system.cpp +++ b/src/frontend/A64/translate/impl/system.cpp @@ -107,6 +107,7 @@ bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3 case SystemRegisterEncoding::CNTPCT_EL0: // HACK: Ensure that this is the first instruction in the block it's emitted in, so the cycle count is most up-to-date. if (!ir.block.empty()) { + ir.block.CycleCount()--; ir.SetTerm(IR::Term::LinkBlock{*ir.current_location}); return false; } diff --git a/tests/A64/a64.cpp b/tests/A64/a64.cpp index 9f71eb95..90c38f63 100644 --- a/tests/A64/a64.cpp +++ b/tests/A64/a64.cpp @@ -313,3 +313,24 @@ TEST_CASE("A64: 128-bit exclusive read/write", "[a64]") { REQUIRE(env.MemoryRead64(0x1234567812345678) == 0xaf00d1e5badcafe0); REQUIRE(env.MemoryRead64(0x1234567812345680) == 0xd0d0cacad0d0caca); } + +TEST_CASE("A64: CNTPCT_EL0", "[a64]") { + TestEnv env; + Dynarmic::A64::Jit jit{Dynarmic::A64::UserConfig{&env}}; + + env.code_mem[0] = 0xd53be021; // MRS X1, CNTPCT_EL0 + env.code_mem[1] = 0xd503201f; // NOP + env.code_mem[2] = 0xd503201f; // NOP + env.code_mem[3] = 0xd503201f; // NOP + env.code_mem[4] = 0xd503201f; // NOP + env.code_mem[5] = 0xd503201f; // NOP + env.code_mem[6] = 0xd503201f; // NOP + env.code_mem[7] = 0xd53be022; // MRS X2, CNTPCT_EL0 + env.code_mem[8] = 0xcb010043; // SUB X3, X2, X1 + env.code_mem[9] = 0x14000000; // B . + + env.ticks_left = 10; + jit.Run(); + + REQUIRE(jit.GetRegister(3) == 7); +} diff --git a/tests/A64/testenv.h b/tests/A64/testenv.h index 10a32b05..5c2bb29e 100644 --- a/tests/A64/testenv.h +++ b/tests/A64/testenv.h @@ -94,6 +94,6 @@ public: return ticks_left; } std::uint64_t GetCNTPCT() override { - ASSERT_MSG(false, "GetCNTPCT()"); + return 0x10000000000 - ticks_left; } };