/* This file is part of the dynarmic project. * Copyright (c) 2016 MerryMage * SPDX-License-Identifier: 0BSD */ #include "frontend/A32/translate/impl/translate_arm.h" namespace Dynarmic::A32 { // CLREX bool ArmTranslatorVisitor::arm_CLREX() { ir.ClearExclusive(); return true; } // SWP , , [] // TODO: UNDEFINED if current mode is Hypervisor bool ArmTranslatorVisitor::arm_SWP(Cond cond, Reg n, Reg t, Reg t2) { if (t == Reg::PC || t2 == Reg::PC || n == Reg::PC || n == t || n == t2) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto data = ir.ReadMemory32(ir.GetRegister(n)); ir.WriteMemory32(ir.GetRegister(n), ir.GetRegister(t2)); // TODO: Alignment check ir.SetRegister(t, data); return true; } // SWPB , , [] // TODO: UNDEFINED if current mode is Hypervisor bool ArmTranslatorVisitor::arm_SWPB(Cond cond, Reg n, Reg t, Reg t2) { if (t == Reg::PC || t2 == Reg::PC || n == Reg::PC || n == t || n == t2) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto data = ir.ReadMemory8(ir.GetRegister(n)); ir.WriteMemory8(ir.GetRegister(n), ir.LeastSignificantByte(ir.GetRegister(t2))); // TODO: Alignment check ir.SetRegister(t, ir.ZeroExtendByteToWord(data)); return true; } // LDA , [] bool ArmTranslatorVisitor::arm_LDA(Cond cond, Reg n, Reg t) { if (t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); ir.SetRegister(t, ir.ReadMemory32(address)); // AccType::Ordered return true; } // LDAB , [] bool ArmTranslatorVisitor::arm_LDAB(Cond cond, Reg n, Reg t) { if (t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); ir.SetRegister(t, ir.ZeroExtendToWord(ir.ReadMemory8(address))); // AccType::Ordered return true; } // LDAH , [] bool ArmTranslatorVisitor::arm_LDAH(Cond cond, Reg n, Reg t) { if (t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); ir.SetRegister(t, ir.ZeroExtendToWord(ir.ReadMemory16(address))); // AccType::Ordered return true; } // LDAEX , [] bool ArmTranslatorVisitor::arm_LDAEX(Cond cond, Reg n, Reg t) { if (t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); ir.SetRegister(t, ir.ExclusiveReadMemory32(address)); // AccType::Ordered return true; } // LDAEXB , [] bool ArmTranslatorVisitor::arm_LDAEXB(Cond cond, Reg n, Reg t) { if (t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); ir.SetRegister(t, ir.ZeroExtendByteToWord(ir.ExclusiveReadMemory8(address))); // AccType::Ordered return true; } // LDAEXD , , [] bool ArmTranslatorVisitor::arm_LDAEXD(Cond cond, Reg n, Reg t) { if (t == Reg::LR || t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); const auto [lo, hi] = ir.ExclusiveReadMemory64(address); // AccType::Ordered // DO NOT SWAP hi AND lo IN BIG ENDIAN MODE, THIS IS CORRECT BEHAVIOUR ir.SetRegister(t, lo); ir.SetRegister(t+1, hi); return true; } // LDAEXH , [] bool ArmTranslatorVisitor::arm_LDAEXH(Cond cond, Reg n, Reg t) { if (t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); ir.SetRegister(t, ir.ZeroExtendHalfToWord(ir.ExclusiveReadMemory16(address))); // AccType::Ordered return true; } // STL , [] bool ArmTranslatorVisitor::arm_STL(Cond cond, Reg n, Reg t) { if (t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); ir.WriteMemory32(address, ir.GetRegister(t)); // AccType::Ordered return true; } // STLB , [] bool ArmTranslatorVisitor::arm_STLB(Cond cond, Reg n, Reg t) { if (t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); ir.WriteMemory8(address, ir.LeastSignificantByte(ir.GetRegister(t))); // AccType::Ordered return true; } // STLH , , [] bool ArmTranslatorVisitor::arm_STLH(Cond cond, Reg n, Reg t) { if (t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); ir.WriteMemory16(address, ir.LeastSignificantHalf(ir.GetRegister(t))); // AccType::Ordered return true; } // STLEXB , , [] bool ArmTranslatorVisitor::arm_STLEXB(Cond cond, Reg n, Reg d, Reg t) { if (n == Reg::PC || d == Reg::PC || t == Reg::PC) { return UnpredictableInstruction(); } if (d == n || d == t) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); const auto value = ir.LeastSignificantByte(ir.GetRegister(t)); const auto passed = ir.ExclusiveWriteMemory8(address, value); // AccType::Ordered ir.SetRegister(d, passed); return true; } // STLEXD , , , [] bool ArmTranslatorVisitor::arm_STLEXD(Cond cond, Reg n, Reg d, Reg t) { if (n == Reg::PC || d == Reg::PC || t == Reg::LR || static_cast(t) % 2 == 1) { return UnpredictableInstruction(); } if (d == n || d == t || d == t+1) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const Reg t2 = t + 1; const auto address = ir.GetRegister(n); const auto value_lo = ir.GetRegister(t); const auto value_hi = ir.GetRegister(t2); const auto passed = ir.ExclusiveWriteMemory64(address, value_lo, value_hi); // AccType::Ordered ir.SetRegister(d, passed); return true; } // STLEXH , , [] bool ArmTranslatorVisitor::arm_STLEXH(Cond cond, Reg n, Reg d, Reg t) { if (n == Reg::PC || d == Reg::PC || t == Reg::PC) { return UnpredictableInstruction(); } if (d == n || d == t) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); const auto value = ir.LeastSignificantHalf(ir.GetRegister(t)); const auto passed = ir.ExclusiveWriteMemory16(address, value); // AccType::Ordered ir.SetRegister(d, passed); return true; } // STLEX , , [] bool ArmTranslatorVisitor::arm_STLEX(Cond cond, Reg n, Reg d, Reg t) { if (n == Reg::PC || d == Reg::PC || t == Reg::PC) { return UnpredictableInstruction(); } if (d == n || d == t) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); const auto value = ir.GetRegister(t); const auto passed = ir.ExclusiveWriteMemory32(address, value); ir.SetRegister(d, passed); return true; } // LDREX , [] bool ArmTranslatorVisitor::arm_LDREX(Cond cond, Reg n, Reg t) { if (t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); ir.SetRegister(t, ir.ExclusiveReadMemory32(address)); return true; } // LDREXB , [] bool ArmTranslatorVisitor::arm_LDREXB(Cond cond, Reg n, Reg t) { if (t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); ir.SetRegister(t, ir.ZeroExtendByteToWord(ir.ExclusiveReadMemory8(address))); return true; } // LDREXD , , [] bool ArmTranslatorVisitor::arm_LDREXD(Cond cond, Reg n, Reg t) { if (t == Reg::LR || t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); const auto [lo, hi] = ir.ExclusiveReadMemory64(address); // DO NOT SWAP hi AND lo IN BIG ENDIAN MODE, THIS IS CORRECT BEHAVIOUR ir.SetRegister(t, lo); ir.SetRegister(t+1, hi); return true; } // LDREXH , [] bool ArmTranslatorVisitor::arm_LDREXH(Cond cond, Reg n, Reg t) { if (t == Reg::PC || n == Reg::PC) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); ir.SetRegister(t, ir.ZeroExtendHalfToWord(ir.ExclusiveReadMemory16(address))); return true; } // STREX , , [] bool ArmTranslatorVisitor::arm_STREX(Cond cond, Reg n, Reg d, Reg t) { if (n == Reg::PC || d == Reg::PC || t == Reg::PC) { return UnpredictableInstruction(); } if (d == n || d == t) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); const auto value = ir.GetRegister(t); const auto passed = ir.ExclusiveWriteMemory32(address, value); ir.SetRegister(d, passed); return true; } // STREXB , , [] bool ArmTranslatorVisitor::arm_STREXB(Cond cond, Reg n, Reg d, Reg t) { if (n == Reg::PC || d == Reg::PC || t == Reg::PC) { return UnpredictableInstruction(); } if (d == n || d == t) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); const auto value = ir.LeastSignificantByte(ir.GetRegister(t)); const auto passed = ir.ExclusiveWriteMemory8(address, value); ir.SetRegister(d, passed); return true; } // STREXD , , , [] bool ArmTranslatorVisitor::arm_STREXD(Cond cond, Reg n, Reg d, Reg t) { if (n == Reg::PC || d == Reg::PC || t == Reg::LR || static_cast(t) % 2 == 1) { return UnpredictableInstruction(); } if (d == n || d == t || d == t+1) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const Reg t2 = t + 1; const auto address = ir.GetRegister(n); const auto value_lo = ir.GetRegister(t); const auto value_hi = ir.GetRegister(t2); const auto passed = ir.ExclusiveWriteMemory64(address, value_lo, value_hi); ir.SetRegister(d, passed); return true; } // STREXH , , [] bool ArmTranslatorVisitor::arm_STREXH(Cond cond, Reg n, Reg d, Reg t) { if (n == Reg::PC || d == Reg::PC || t == Reg::PC) { return UnpredictableInstruction(); } if (d == n || d == t) { return UnpredictableInstruction(); } if (!ConditionPassed(cond)) { return true; } const auto address = ir.GetRegister(n); const auto value = ir.LeastSignificantHalf(ir.GetRegister(t)); const auto passed = ir.ExclusiveWriteMemory16(address, value); ir.SetRegister(d, passed); return true; } } // namespace Dynarmic::A32