From 3d7e81e7d1fb6a2c1733074860efb42720025a1f Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Sat, 6 Mar 2021 22:43:37 -0500
Subject: [PATCH] thumb32: Implement LDR variants

---
 src/frontend/A32/decoder/thumb32.inc          |  10 +-
 .../A32/translate/impl/thumb32_load_word.cpp  | 123 ++++++++++++++++++
 .../A32/translate/impl/translate_thumb.h      |   7 +
 3 files changed, 135 insertions(+), 5 deletions(-)

diff --git a/src/frontend/A32/decoder/thumb32.inc b/src/frontend/A32/decoder/thumb32.inc
index bd09a041..0179bce3 100644
--- a/src/frontend/A32/decoder/thumb32.inc
+++ b/src/frontend/A32/decoder/thumb32.inc
@@ -177,11 +177,11 @@ INST(thumb32_LDRSB_imm12,    "LDRSB (imm12)",            "111110011001nnnnttttii
 //INST(thumb32_NOP,            "NOP",                      "111110011011----1111------------")
 
 // Load Word
-//INST(thumb32_LDR_lit,        "LDR (lit)",                "11111000-1011111----------------")
-//INST(thumb32_LDRT,           "LDRT",                     "111110000101--------1110--------")
-//INST(thumb32_LDR_reg,        "LDR (reg)",                "111110000101--------000000------")
-//INST(thumb32_LDR_imm8,       "LDR (imm8)",               "111110000101--------1-----------")
-//INST(thumb32_LDR_imm12,      "LDR (imm12)",              "111110001101--------------------")
+INST(thumb32_LDR_lit,        "LDR (lit)",                "11111000U1011111ttttiiiiiiiiiiii")
+INST(thumb32_LDRT,           "LDRT",                     "111110000101nnnntttt1110iiiiiiii")
+INST(thumb32_LDR_reg,        "LDR (reg)",                "111110000101nnnntttt000000iimmmm")
+INST(thumb32_LDR_imm8,       "LDR (imm8)",               "111110000101nnnntttt1PUWiiiiiiii")
+INST(thumb32_LDR_imm12,      "LDR (imm12)",              "111110001101nnnnttttiiiiiiiiiiii")
 
 // Data Processing (register)
 INST(thumb32_LSL_reg,        "LSL (reg)",                "111110100000mmmm1111dddd0000ssss")
diff --git a/src/frontend/A32/translate/impl/thumb32_load_word.cpp b/src/frontend/A32/translate/impl/thumb32_load_word.cpp
index 5f4e4104..81dc66b8 100644
--- a/src/frontend/A32/translate/impl/thumb32_load_word.cpp
+++ b/src/frontend/A32/translate/impl/thumb32_load_word.cpp
@@ -6,6 +6,129 @@
 #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();
+}
 
+bool ThumbTranslatorVisitor::thumb32_LDR_lit(bool U, Reg t, Imm<12> imm12) {
+    if (t == Reg::PC && ITBlockCheck(ir)) {
+        return UnpredictableInstruction();
+    }
+
+    const u32 imm32 = imm12.ZeroExtend();
+    const u32 base = ir.AlignPC(4);
+    const u32 address = U ? base + imm32 : base - imm32;
+    const auto data = ir.ReadMemory32(ir.Imm32(address));
+
+    if (t == Reg::PC) {
+        ir.UpdateUpperLocationDescriptor();
+        ir.LoadWritePC(data);
+        ir.SetTerm(IR::Term::FastDispatchHint{});
+        return false;
+    }
+
+    ir.SetRegister(t, data);
+    return true;
+}
+
+bool ThumbTranslatorVisitor::thumb32_LDR_imm8(Reg n, Reg t, bool P, bool U, bool W, Imm<8> imm8) {
+    if (!P && !W) {
+        return UndefinedInstruction();
+    }
+    if (W && n == t) {
+        return UnpredictableInstruction();
+    }
+    if (t == Reg::PC && ITBlockCheck(ir)) {
+        return UnpredictableInstruction();
+    }
+
+    const u32 imm32 = imm8.ZeroExtend();
+    const IR::U32 reg_n = ir.GetRegister(n);
+    const IR::U32 offset_address = U ? ir.Add(reg_n, ir.Imm32(imm32))
+                                     : ir.Sub(reg_n, ir.Imm32(imm32));
+    const IR::U32 address = P ? offset_address
+                              : reg_n;
+    const IR::U32 data = ir.ReadMemory32(address);
+
+    if (W) {
+        ir.SetRegister(n, offset_address);
+    }
+
+    if (t == Reg::PC) {
+        ir.UpdateUpperLocationDescriptor();
+        ir.LoadWritePC(data);
+
+        if (!P && W && n == Reg::R13) {
+            ir.SetTerm(IR::Term::PopRSBHint{});
+        } else {
+            ir.SetTerm(IR::Term::FastDispatchHint{});
+        }
+
+        return false;
+    }
+
+    ir.SetRegister(t, data);
+    return true;
+}
+
+bool ThumbTranslatorVisitor::thumb32_LDR_imm12(Reg n, Reg t, Imm<12> imm12) {
+    if (t == Reg::PC && ITBlockCheck(ir)) {
+        return UnpredictableInstruction();
+    }
+
+    const auto imm32 = imm12.ZeroExtend();
+    const auto reg_n = ir.GetRegister(n);
+    const auto address = ir.Add(reg_n, ir.Imm32(imm32));
+    const auto data = ir.ReadMemory32(address);
+
+    if (t == Reg::PC) {
+        ir.UpdateUpperLocationDescriptor();
+        ir.LoadWritePC(data);
+        ir.SetTerm(IR::Term::FastDispatchHint{});
+        return false;
+    }
+
+    ir.SetRegister(t, data);
+    return true;
+}
+
+bool ThumbTranslatorVisitor::thumb32_LDR_reg(Reg n, Reg t, Imm<2> imm2, Reg m) {
+    if (m == Reg::PC) {
+        return UnpredictableInstruction();
+    }
+    if (t == Reg::PC && ITBlockCheck(ir)) {
+        return UnpredictableInstruction();
+    }
+
+    const auto reg_m = ir.GetRegister(m);
+    const auto reg_n = ir.GetRegister(n);
+    const auto offset = ir.LogicalShiftLeft(reg_m, ir.Imm8(imm2.ZeroExtend<u8>()));
+    const auto address = ir.Add(reg_n, offset);
+    const auto data = ir.ReadMemory32(address);
+
+    if (t == Reg::PC) {
+        ir.UpdateUpperLocationDescriptor();
+        ir.LoadWritePC(data);
+        ir.SetTerm(IR::Term::FastDispatchHint{});
+        return false;
+    }
+
+    ir.SetRegister(t, data);
+    return true;
+}
+
+bool ThumbTranslatorVisitor::thumb32_LDRT(Reg n, Reg t, Imm<8> imm8) {
+    // TODO: Add an unpredictable instruction path if this
+    //       is executed in hypervisor mode if we ever support
+    //       privileged execution levels.
+
+    if (t == Reg::PC) {
+        return UnpredictableInstruction();
+    }
+
+    // Treat it as a normal LDR, given we don't support
+    // execution levels other than EL0 currently.
+    return thumb32_LDR_imm8(n, t, true, true, false, imm8);
+}
 
 } // namespace Dynarmic::A32
diff --git a/src/frontend/A32/translate/impl/translate_thumb.h b/src/frontend/A32/translate/impl/translate_thumb.h
index 05ff0fdb..0bbe6e28 100644
--- a/src/frontend/A32/translate/impl/translate_thumb.h
+++ b/src/frontend/A32/translate/impl/translate_thumb.h
@@ -267,6 +267,13 @@ struct ThumbTranslatorVisitor final {
     bool thumb32_LDRSB_imm12(Reg n, Reg t, Imm<12> imm12);
     bool thumb32_LDRSBT(Reg n, Reg t, Imm<8> imm8);
 
+    // thumb32 load word instructions
+    bool thumb32_LDR_lit(bool U, Reg t, Imm<12> imm12);
+    bool thumb32_LDR_reg(Reg n, Reg t, Imm<2> imm2, Reg m);
+    bool thumb32_LDR_imm8(Reg n, Reg t, bool P, bool U, bool W, Imm<8> imm8);
+    bool thumb32_LDR_imm12(Reg n, Reg t, Imm<12> imm12);
+    bool thumb32_LDRT(Reg n, Reg t, Imm<8> imm8);
+
     // thumb32 data processing (register) instructions
     bool thumb32_ASR_reg(Reg m, Reg d, Reg s);
     bool thumb32_LSL_reg(Reg m, Reg d, Reg s);