diff --git a/include/dynarmic/A32/a32.h b/include/dynarmic/A32/a32.h index 88f3cf9c..9dab9dc1 100644 --- a/include/dynarmic/A32/a32.h +++ b/include/dynarmic/A32/a32.h @@ -35,6 +35,12 @@ public: */ void Run(); + /** + * Steps the emulated CPU. + * Cannot be recursively called. + */ + void Step(); + /** * Clears the code cache of all compiled code. * Can be called at any time. Halts execution if called within a callback. diff --git a/src/backend/x64/a32_interface.cpp b/src/backend/x64/a32_interface.cpp index 8041ff19..18b04e81 100644 --- a/src/backend/x64/a32_interface.cpp +++ b/src/backend/x64/a32_interface.cpp @@ -41,7 +41,7 @@ static RunCodeCallbacks GenRunCodeCallbacks(A32::UserCallbacks* cb, CodePtr (*Lo struct Jit::Impl { Impl(Jit* jit, A32::UserConfig config) - : block_of_code(GenRunCodeCallbacks(config.callbacks, &GetCurrentBlock, this), JitStateInfo{jit_state}) + : block_of_code(GenRunCodeCallbacks(config.callbacks, &GetCurrentBlockThunk, this), JitStateInfo{jit_state}) , emitter(block_of_code, config, jit) , config(std::move(config)) , jit_interface(jit) @@ -59,13 +59,22 @@ struct Jit::Impl { bool invalidate_entire_cache = false; void Execute() { - const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSBPtrMask; - if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) { - jit_state.rsb_ptr = new_rsb_ptr; - block_of_code.RunCodeFrom(&jit_state, reinterpret_cast(jit_state.rsb_codeptrs[new_rsb_ptr])); - } else { - block_of_code.RunCode(&jit_state); - } + const CodePtr current_codeptr = [this]{ + // RSB optimization + const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSBPtrMask; + if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) { + jit_state.rsb_ptr = new_rsb_ptr; + return reinterpret_cast(jit_state.rsb_codeptrs[new_rsb_ptr]); + } + + return GetCurrentBlock(); + }(); + + block_of_code.RunCodeFrom(&jit_state, current_codeptr); + } + + void Step() { + block_of_code.StepCode(&jit_state, GetCurrentSingleStep()); } std::string Disassemble(const IR::LocationDescriptor& descriptor) { @@ -109,16 +118,21 @@ struct Jit::Impl { private: Jit* jit_interface; - static CodePtr GetCurrentBlock(void* this_voidptr) { + static CodePtr GetCurrentBlockThunk(void* this_voidptr) { Jit::Impl& this_ = *static_cast(this_voidptr); - A32JitState& jit_state = this_.jit_state; + return this_.GetCurrentBlock(); + } - u32 pc = jit_state.Reg[15]; - A32::PSR cpsr{jit_state.Cpsr()}; - A32::FPSCR fpscr{jit_state.upper_location_descriptor}; - A32::LocationDescriptor descriptor{pc, cpsr, fpscr}; + IR::LocationDescriptor GetCurrentLocation() const { + return IR::LocationDescriptor{jit_state.GetUniqueHash()}; + } - return this_.GetBasicBlock(descriptor).entrypoint; + CodePtr GetCurrentBlock() { + return GetBasicBlock(GetCurrentLocation()).entrypoint; + } + + CodePtr GetCurrentSingleStep() { + return GetBasicBlock(A32::LocationDescriptor{GetCurrentLocation()}.SetSingleStepping(true)).entrypoint; } A32EmitX64::BlockDescriptor GetBasicBlock(IR::LocationDescriptor descriptor) { @@ -159,6 +173,18 @@ void Jit::Run() { impl->PerformCacheInvalidation(); } +void Jit::Step() { + ASSERT(!is_executing); + is_executing = true; + SCOPE_EXIT { this->is_executing = false; }; + + impl->jit_state.halt_requested = true; + + impl->Step(); + + impl->PerformCacheInvalidation(); +} + void Jit::ClearCache() { impl->invalidate_entire_cache = true; impl->RequestCacheInvalidation(); diff --git a/src/frontend/A32/location_descriptor.cpp b/src/frontend/A32/location_descriptor.cpp index 08391a27..0c20247e 100644 --- a/src/frontend/A32/location_descriptor.cpp +++ b/src/frontend/A32/location_descriptor.cpp @@ -11,11 +11,12 @@ namespace Dynarmic::A32 { std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor) { - o << fmt::format("{{{:08x},{},{},{:08x}}}", + o << fmt::format("{{{:08x},{},{},{:08x}{}}}", descriptor.PC(), descriptor.TFlag() ? "T" : "!T", descriptor.EFlag() ? "E" : "!E", - descriptor.FPSCR().Value()); + descriptor.FPSCR().Value(), + descriptor.SingleStepping() ? ",step" : ""); return o; } diff --git a/src/frontend/A32/location_descriptor.h b/src/frontend/A32/location_descriptor.h index 80d38cff..78c056d4 100644 --- a/src/frontend/A32/location_descriptor.h +++ b/src/frontend/A32/location_descriptor.h @@ -29,8 +29,12 @@ public: static constexpr u32 CPSR_MODE_MASK = 0x0600FE20; static constexpr u32 FPSCR_MODE_MASK = 0x07F70000; - LocationDescriptor(u32 arm_pc, PSR cpsr, FPSCR fpscr) - : arm_pc(arm_pc), cpsr(cpsr.Value() & CPSR_MODE_MASK), fpscr(fpscr.Value() & FPSCR_MODE_MASK) {} + LocationDescriptor(u32 arm_pc, PSR cpsr, FPSCR fpscr, bool single_stepping = false) + : arm_pc(arm_pc) + , cpsr(cpsr.Value() & CPSR_MODE_MASK) + , fpscr(fpscr.Value() & FPSCR_MODE_MASK) + , single_stepping(single_stepping) + {} explicit LocationDescriptor(const IR::LocationDescriptor& o) { arm_pc = static_cast(o.Value()); @@ -38,6 +42,7 @@ public: cpsr.E((o.Value() >> 32) & 2); fpscr = (o.Value() >> 32) & FPSCR_MODE_MASK; cpsr.IT(ITState{static_cast(o.Value() >> 40)}); + single_stepping = (o.Value() >> 32) & 4; } u32 PC() const { return arm_pc; } @@ -48,8 +53,10 @@ public: A32::PSR CPSR() const { return cpsr; } A32::FPSCR FPSCR() const { return fpscr; } + bool SingleStepping() const { return single_stepping; } + bool operator == (const LocationDescriptor& o) const { - return std::tie(arm_pc, cpsr, fpscr) == std::tie(o.arm_pc, o.cpsr, o.fpscr); + return std::tie(arm_pc, cpsr, fpscr, single_stepping) == std::tie(o.arm_pc, o.cpsr, o.fpscr, single_stepping); } bool operator != (const LocationDescriptor& o) const { @@ -57,36 +64,40 @@ public: } LocationDescriptor SetPC(u32 new_arm_pc) const { - return LocationDescriptor(new_arm_pc, cpsr, fpscr); + return LocationDescriptor(new_arm_pc, cpsr, fpscr, single_stepping); } LocationDescriptor AdvancePC(int amount) const { - return LocationDescriptor(static_cast(arm_pc + amount), cpsr, fpscr); + return LocationDescriptor(static_cast(arm_pc + amount), cpsr, fpscr, single_stepping); } LocationDescriptor SetTFlag(bool new_tflag) const { PSR new_cpsr = cpsr; new_cpsr.T(new_tflag); - return LocationDescriptor(arm_pc, new_cpsr, fpscr); + return LocationDescriptor(arm_pc, new_cpsr, fpscr, single_stepping); } LocationDescriptor SetEFlag(bool new_eflag) const { PSR new_cpsr = cpsr; new_cpsr.E(new_eflag); - return LocationDescriptor(arm_pc, new_cpsr, fpscr); + return LocationDescriptor(arm_pc, new_cpsr, fpscr, single_stepping); } LocationDescriptor SetFPSCR(u32 new_fpscr) const { - return LocationDescriptor(arm_pc, cpsr, A32::FPSCR{new_fpscr & FPSCR_MODE_MASK}); + return LocationDescriptor(arm_pc, cpsr, A32::FPSCR{new_fpscr & FPSCR_MODE_MASK}, single_stepping); } LocationDescriptor AdvanceIT() const { PSR new_cpsr = cpsr; new_cpsr.IT(new_cpsr.IT().Advance()); - return LocationDescriptor(arm_pc, new_cpsr, fpscr); + return LocationDescriptor(arm_pc, new_cpsr, fpscr, single_stepping); + } + + LocationDescriptor SetSingleStepping(bool new_single_stepping) const { + return LocationDescriptor(arm_pc, cpsr, fpscr, new_single_stepping); } u64 UniqueHash() const noexcept { @@ -96,8 +107,9 @@ public: const u64 fpscr_u64 = fpscr.Value(); const u64 t_u64 = cpsr.T() ? 1 : 0; const u64 e_u64 = cpsr.E() ? 2 : 0; + const u64 single_stepping_u64 = single_stepping ? 4 : 0; const u64 it_u64 = u64(cpsr.IT().Value()) << 8; - const u64 upper = (fpscr_u64 | t_u64 | e_u64 | it_u64) << 32; + const u64 upper = (fpscr_u64 | t_u64 | e_u64 | single_stepping_u64 | it_u64) << 32; return pc_u64 | upper; } @@ -109,6 +121,7 @@ private: u32 arm_pc; ///< Current program counter value. PSR cpsr; ///< Current program status register. A32::FPSCR fpscr; ///< Floating point status control register. + bool single_stepping; }; /** diff --git a/src/frontend/A32/translate/translate_arm.cpp b/src/frontend/A32/translate/translate_arm.cpp index a0d29b36..8dd85983 100644 --- a/src/frontend/A32/translate/translate_arm.cpp +++ b/src/frontend/A32/translate/translate_arm.cpp @@ -33,8 +33,9 @@ IR::Block TranslateArm(LocationDescriptor descriptor, MemoryReadCodeFuncType mem IR::Block block{descriptor}; ArmTranslatorVisitor visitor{block, descriptor, options}; + const bool single_step = descriptor.SingleStepping(); bool should_continue = true; - while (should_continue && CondCanContinue(visitor.cond_state, visitor.ir)) { + do { const u32 arm_pc = visitor.ir.current_location.PC(); const u32 arm_instruction = memory_read_code(arm_pc); @@ -52,11 +53,15 @@ IR::Block TranslateArm(LocationDescriptor descriptor, MemoryReadCodeFuncType mem visitor.ir.current_location = visitor.ir.current_location.AdvancePC(4); block.CycleCount()++; - } + } while (should_continue && CondCanContinue(visitor.cond_state, visitor.ir) && !single_step); - if (visitor.cond_state == ConditionalState::Translating || visitor.cond_state == ConditionalState::Trailing) { + if (visitor.cond_state == ConditionalState::Translating || visitor.cond_state == ConditionalState::Trailing || single_step) { if (should_continue) { - visitor.ir.SetTerm(IR::Term::LinkBlockFast{visitor.ir.current_location}); + if (single_step) { + visitor.ir.SetTerm(IR::Term::LinkBlock{visitor.ir.current_location}); + } else { + visitor.ir.SetTerm(IR::Term::LinkBlockFast{visitor.ir.current_location}); + } } } diff --git a/src/frontend/A32/translate/translate_thumb.cpp b/src/frontend/A32/translate/translate_thumb.cpp index 21b0949c..0dd6c1c9 100644 --- a/src/frontend/A32/translate/translate_thumb.cpp +++ b/src/frontend/A32/translate/translate_thumb.cpp @@ -59,8 +59,9 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType m IR::Block block{descriptor}; ThumbTranslatorVisitor visitor{block, descriptor, options}; + const bool single_step = descriptor.SingleStepping(); bool should_continue = true; - while (should_continue) { + do { const u32 arm_pc = visitor.ir.current_location.PC(); const auto [thumb_instruction, inst_size] = ReadThumbInstruction(arm_pc, memory_read_code); @@ -81,6 +82,10 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType m const s32 advance_pc = (inst_size == ThumbInstSize::Thumb16) ? 2 : 4; visitor.ir.current_location = visitor.ir.current_location.AdvancePC(advance_pc); block.CycleCount()++; + } while (should_continue && !single_step); + + if (single_step && should_continue) { + visitor.ir.SetTerm(IR::Term::LinkBlock{visitor.ir.current_location}); } block.SetEndLocation(visitor.ir.current_location);