diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 83cece7b..949e50ac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -163,6 +163,7 @@ if ("A32" IN_LIST DYNARMIC_FRONTENDS) frontend/A32/translate/impl/thumb32_data_processing_shifted_register.cpp frontend/A32/translate/impl/thumb32_load_byte.cpp frontend/A32/translate/impl/thumb32_load_halfword.cpp + frontend/A32/translate/impl/thumb32_load_store_multiple.cpp frontend/A32/translate/impl/thumb32_load_word.cpp frontend/A32/translate/impl/thumb32_long_multiply.cpp frontend/A32/translate/impl/thumb32_misc.cpp diff --git a/src/frontend/A32/decoder/thumb32.inc b/src/frontend/A32/decoder/thumb32.inc index a2737352..e5def4ca 100644 --- a/src/frontend/A32/decoder/thumb32.inc +++ b/src/frontend/A32/decoder/thumb32.inc @@ -1,12 +1,12 @@ // Load/Store Multiple //INST(thumb32_SRS_1, "SRS", "1110100000-0--------------------") //INST(thumb32_RFE_2, "RFE", "1110100000-1--------------------") -//INST(thumb32_STMIA, "STMIA/STMEA", "1110100010-0--------------------") -//INST(thumb32_POP, "POP", "1110100010111101----------------") -//INST(thumb32_LDMIA, "LDMIA/LDMFD", "1110100010-1--------------------") -//INST(thumb32_PUSH, "PUSH", "1110100100101101----------------") -//INST(thumb32_STMDB, "STMDB/STMFD", "1110100100-0--------------------") -//INST(thumb32_LDMDB, "LDMDB/LDMEA", "1110100100-1--------------------") +INST(thumb32_STMIA, "STMIA/STMEA", "1110100010W0nnnn0iiiiiiiiiiiiiii") +INST(thumb32_POP, "POP", "1110100010111101iiiiiiiiiiiiiiii") +INST(thumb32_LDMIA, "LDMIA/LDMFD", "1110100010W1nnnniiiiiiiiiiiiiiii") +INST(thumb32_PUSH, "PUSH", "11101001001011010iiiiiiiiiiiiiii") +INST(thumb32_STMDB, "STMDB/STMFD", "1110100100W0nnnn0iiiiiiiiiiiiiii") +INST(thumb32_LDMDB, "LDMDB/LDMEA", "1110100100W1nnnniiiiiiiiiiiiiiii") //INST(thumb32_SRS_1, "SRS", "1110100110-0--------------------") //INST(thumb32_RFE_2, "RFE", "1110100110-1--------------------") diff --git a/src/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp b/src/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp new file mode 100644 index 00000000..49af9d76 --- /dev/null +++ b/src/frontend/A32/translate/impl/thumb32_load_store_multiple.cpp @@ -0,0 +1,150 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2021 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include "common/bit_util.h" +#include "frontend/A32/translate/impl/translate_thumb.h" + +namespace Dynarmic::A32 { +static bool ITBlockCheck(const A32::IREmitter& ir) { + return ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock(); +} + +static bool LDMHelper(A32::IREmitter& ir, bool W, Reg n, u32 list, + const IR::U32& start_address, const IR::U32& writeback_address) { + auto address = start_address; + for (size_t i = 0; i <= 14; i++) { + if (Common::Bit(i, list)) { + ir.SetRegister(static_cast(i), ir.ReadMemory32(address)); + address = ir.Add(address, ir.Imm32(4)); + } + } + if (W && !Common::Bit(RegNumber(n), list)) { + ir.SetRegister(n, writeback_address); + } + if (Common::Bit<15>(list)) { + ir.UpdateUpperLocationDescriptor(); + ir.LoadWritePC(ir.ReadMemory32(address)); + if (n == Reg::R13) { + ir.SetTerm(IR::Term::PopRSBHint{}); + } else { + ir.SetTerm(IR::Term::FastDispatchHint{}); + } + return false; + } + return true; +} + +static bool STMHelper(A32::IREmitter& ir, bool W, Reg n, u32 list, + const IR::U32& start_address, const IR::U32& writeback_address) { + auto address = start_address; + for (size_t i = 0; i <= 14; i++) { + if (Common::Bit(i, list)) { + ir.WriteMemory32(address, ir.GetRegister(static_cast(i))); + address = ir.Add(address, ir.Imm32(4)); + } + } + if (W) { + ir.SetRegister(n, writeback_address); + } + return true; +} + +bool ThumbTranslatorVisitor::thumb32_LDMDB(bool W, Reg n, Imm<16> reg_list) { + const auto regs_imm = reg_list.ZeroExtend(); + const auto num_regs = static_cast(Common::BitCount(regs_imm)); + + if (n == Reg::PC || num_regs < 2) { + return UnpredictableInstruction(); + } + if (reg_list.Bit<15>() && reg_list.Bit<14>()) { + return UnpredictableInstruction(); + } + if (W && Common::Bit(static_cast(n), regs_imm)) { + return UnpredictableInstruction(); + } + if (reg_list.Bit<13>()) { + return UnpredictableInstruction(); + } + if (reg_list.Bit<15>() && ITBlockCheck(ir)) { + return UnpredictableInstruction(); + } + + // Start address is the same as the writeback address. + const IR::U32 start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(4 * num_regs)); + return LDMHelper(ir, W, n, regs_imm, start_address, start_address); +} + +bool ThumbTranslatorVisitor::thumb32_LDMIA(bool W, Reg n, Imm<16> reg_list) { + const auto regs_imm = reg_list.ZeroExtend(); + const auto num_regs = static_cast(Common::BitCount(regs_imm)); + + if (n == Reg::PC || num_regs < 2) { + return UnpredictableInstruction(); + } + if (reg_list.Bit<15>() && reg_list.Bit<14>()) { + return UnpredictableInstruction(); + } + if (W && Common::Bit(static_cast(n), regs_imm)) { + return UnpredictableInstruction(); + } + if (reg_list.Bit<13>()) { + return UnpredictableInstruction(); + } + if (reg_list.Bit<15>() && ITBlockCheck(ir)) { + return UnpredictableInstruction(); + } + + const auto start_address = ir.GetRegister(n); + const auto writeback_address = ir.Add(start_address, ir.Imm32(num_regs * 4)); + return LDMHelper(ir, W, n, regs_imm, start_address, writeback_address); +} + +bool ThumbTranslatorVisitor::thumb32_POP(Imm<16> reg_list) { + return thumb32_LDMIA(true, Reg::SP, reg_list); +} + +bool ThumbTranslatorVisitor::thumb32_PUSH(Imm<15> reg_list) { + return thumb32_STMDB(true, Reg::SP, reg_list); +} + +bool ThumbTranslatorVisitor::thumb32_STMIA(bool W, Reg n, Imm<15> reg_list) { + const auto regs_imm = reg_list.ZeroExtend(); + const auto num_regs = static_cast(Common::BitCount(regs_imm)); + + if (n == Reg::PC || num_regs < 2) { + return UnpredictableInstruction(); + } + if (W && Common::Bit(static_cast(n), regs_imm)) { + return UnpredictableInstruction(); + } + if (reg_list.Bit<13>()) { + return UnpredictableInstruction(); + } + + const auto start_address = ir.GetRegister(n); + const auto writeback_address = ir.Add(start_address, ir.Imm32(num_regs * 4)); + return STMHelper(ir, W, n, regs_imm, start_address, writeback_address); +} + +bool ThumbTranslatorVisitor::thumb32_STMDB(bool W, Reg n, Imm<15> reg_list) { + const auto regs_imm = reg_list.ZeroExtend(); + const auto num_regs = static_cast(Common::BitCount(regs_imm)); + + if (n == Reg::PC || num_regs < 2) { + return UnpredictableInstruction(); + } + if (W && Common::Bit(static_cast(n), regs_imm)) { + return UnpredictableInstruction(); + } + if (reg_list.Bit<13>()) { + return UnpredictableInstruction(); + } + + // Start address is the same as the writeback address. + const IR::U32 start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(4 * num_regs)); + return STMHelper(ir, W, n, regs_imm, start_address, start_address); +} + +} // namespace Dynarmic::A32 diff --git a/src/frontend/A32/translate/impl/translate_thumb.h b/src/frontend/A32/translate/impl/translate_thumb.h index 0999f59a..174f601c 100644 --- a/src/frontend/A32/translate/impl/translate_thumb.h +++ b/src/frontend/A32/translate/impl/translate_thumb.h @@ -171,6 +171,14 @@ struct ThumbTranslatorVisitor final { bool thumb16_B_t1(Cond cond, Imm<8> imm8); bool thumb16_B_t2(Imm<11> imm11); + // thumb32 load/store multiple instructions + bool thumb32_LDMDB(bool W, Reg n, Imm<16> reg_list); + bool thumb32_LDMIA(bool W, Reg n, Imm<16> reg_list); + bool thumb32_POP(Imm<16> reg_list); + bool thumb32_PUSH(Imm<15> reg_list); + bool thumb32_STMIA(bool W, Reg n, Imm<15> reg_list); + bool thumb32_STMDB(bool W, Reg n, Imm<15> reg_list); + // thumb32 data processing (shifted register) instructions bool thumb32_TST_reg(Reg n, Imm<3> imm3, Imm<2> imm2, ShiftType type, Reg m); bool thumb32_AND_reg(bool S, Reg n, Imm<3> imm3, Reg d, Imm<2> imm2, ShiftType type, Reg m);