diff --git a/include/dynarmic/A32/arch_version.h b/include/dynarmic/A32/arch_version.h new file mode 100644 index 00000000..86bb9e0f --- /dev/null +++ b/include/dynarmic/A32/arch_version.h @@ -0,0 +1,23 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2020 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +namespace Dynarmic { +namespace A32 { + +enum class ArchVersion { + v3, + v4, + v4T, + v5TE, + v6K, + v6T2, + v7, + v8, +}; + +} // namespace A32 +} // namespace Dynarmic diff --git a/include/dynarmic/A32/config.h b/include/dynarmic/A32/config.h index 565e2a91..bb38be4a 100644 --- a/include/dynarmic/A32/config.h +++ b/include/dynarmic/A32/config.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace Dynarmic { @@ -105,6 +106,10 @@ struct UserConfig { size_t processor_id = 0; ExclusiveMonitor* global_monitor = nullptr; + /// Select the architecture version to use. + /// There are minor behavioural differences between versions. + ArchVersion arch_version = ArchVersion::v8; + /// This selects other optimizations than can't otherwise be disabled by setting other /// configuration options. This includes: /// - IR optimizations diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d921a63f..5e3ea5c2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(dynarmic ../include/dynarmic/A32/a32.h + ../include/dynarmic/A32/arch_version.h ../include/dynarmic/A32/config.h ../include/dynarmic/A32/coprocessor.h ../include/dynarmic/A32/coprocessor_util.h diff --git a/src/backend/x64/a32_interface.cpp b/src/backend/x64/a32_interface.cpp index b8af7510..85f934c8 100644 --- a/src/backend/x64/a32_interface.cpp +++ b/src/backend/x64/a32_interface.cpp @@ -175,7 +175,7 @@ private: PerformCacheInvalidation(); } - IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, [this](u32 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); }, {conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); + IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, [this](u32 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); }, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) { Optimization::A32GetSetElimination(ir_block); Optimization::DeadCodeElimination(ir_block); diff --git a/src/frontend/A32/ir_emitter.cpp b/src/frontend/A32/ir_emitter.cpp index 89aa93f5..ca1e91fe 100644 --- a/src/frontend/A32/ir_emitter.cpp +++ b/src/frontend/A32/ir_emitter.cpp @@ -8,10 +8,32 @@ #include "frontend/A32/types.h" #include "frontend/ir/opcodes.h" +#include + namespace Dynarmic::A32 { using Opcode = IR::Opcode; +size_t IREmitter::ArchVersion() const { + switch (arch_version) { + case ArchVersion::v3: + return 3; + case ArchVersion::v4: + case ArchVersion::v4T: + return 4; + case ArchVersion::v5TE: + return 5; + case ArchVersion::v6K: + case ArchVersion::v6T2: + return 6; + case ArchVersion::v7: + return 7; + case ArchVersion::v8: + return 8; + } + UNREACHABLE(); +} + u32 IREmitter::PC() const { const u32 offset = current_location.TFlag() ? 4 : 8; return current_location.PC() + offset; @@ -68,12 +90,16 @@ void IREmitter::SetVector(ExtReg reg, const IR::U128& value) { void IREmitter::ALUWritePC(const IR::U32& value) { // This behaviour is ARM version-dependent. - // The below implementation is for ARMv6k - BranchWritePC(value); + if (ArchVersion() >= 7 && !current_location.TFlag()) { + BXWritePC(value); + } else { + BranchWritePC(value); + } } void IREmitter::BranchWritePC(const IR::U32& value) { if (!current_location.TFlag()) { + // Note that for ArchVersion() < 6, this is UNPREDICTABLE when value<1:0> != 0b00 const auto new_pc = And(value, Imm32(0xFFFFFFFC)); Inst(Opcode::A32SetRegister, IR::Value(A32::Reg::PC), new_pc); } else { @@ -88,8 +114,11 @@ void IREmitter::BXWritePC(const IR::U32& value) { void IREmitter::LoadWritePC(const IR::U32& value) { // This behaviour is ARM version-dependent. - // The below implementation is for ARMv6k - BXWritePC(value); + if (ArchVersion() >= 5) { + BXWritePC(value); + } else { + BranchWritePC(value); + } } void IREmitter::CallSupervisor(const IR::U32& value) { diff --git a/src/frontend/A32/ir_emitter.h b/src/frontend/A32/ir_emitter.h index 11206939..57aa40d2 100644 --- a/src/frontend/A32/ir_emitter.h +++ b/src/frontend/A32/ir_emitter.h @@ -14,6 +14,7 @@ namespace Dynarmic::A32 { +enum class ArchVersion; enum class CoprocReg; enum class Exception; enum class ExtReg; @@ -26,10 +27,12 @@ enum class Reg; */ class IREmitter : public IR::IREmitter { public: - explicit IREmitter(IR::Block& block, LocationDescriptor descriptor) : IR::IREmitter(block), current_location(descriptor) {} + IREmitter(IR::Block& block, LocationDescriptor descriptor, ArchVersion arch_version) : IR::IREmitter(block), current_location(descriptor), arch_version(arch_version) {} LocationDescriptor current_location; + size_t ArchVersion() const; + u32 PC() const; u32 AlignPC(size_t alignment) const; @@ -99,6 +102,9 @@ public: IR::U64 CoprocGetTwoWords(size_t coproc_no, bool two, size_t opc, CoprocReg CRm); void CoprocLoadWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option); void CoprocStoreWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option); + +private: + enum ArchVersion arch_version; }; } // namespace Dynarmic::A32 diff --git a/src/frontend/A32/translate/impl/translate_arm.h b/src/frontend/A32/translate/impl/translate_arm.h index c433820c..f9a239b9 100644 --- a/src/frontend/A32/translate/impl/translate_arm.h +++ b/src/frontend/A32/translate/impl/translate_arm.h @@ -31,7 +31,7 @@ enum class ConditionalState { struct ArmTranslatorVisitor final { using instruction_return_type = bool; - explicit ArmTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor), options(options) { + explicit ArmTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor, options.arch_version), options(options) { ASSERT_MSG(!descriptor.TFlag(), "The processor must be in Arm mode"); } diff --git a/src/frontend/A32/translate/impl/translate_thumb.h b/src/frontend/A32/translate/impl/translate_thumb.h index 185665ad..0e61f58c 100644 --- a/src/frontend/A32/translate/impl/translate_thumb.h +++ b/src/frontend/A32/translate/impl/translate_thumb.h @@ -19,7 +19,7 @@ enum class Exception; struct ThumbTranslatorVisitor final { using instruction_return_type = bool; - explicit ThumbTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor), options(options) { + explicit ThumbTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor, options.arch_version), options(options) { ASSERT_MSG(descriptor.TFlag(), "The processor must be in Thumb mode"); } diff --git a/src/frontend/A32/translate/translate.h b/src/frontend/A32/translate/translate.h index a4928903..9ab7975b 100644 --- a/src/frontend/A32/translate/translate.h +++ b/src/frontend/A32/translate/translate.h @@ -6,6 +6,8 @@ #include "common/common_types.h" +#include + namespace Dynarmic::IR { class Block; } // namespace Dynarmic::IR @@ -17,6 +19,8 @@ class LocationDescriptor; using MemoryReadCodeFuncType = std::function; struct TranslationOptions { + ArchVersion arch_version; + /// This changes what IR we emit when we translate an unpredictable instruction. /// If this is false, the ExceptionRaised IR instruction is emitted. /// If this is true, we define some behaviour for some instructions.