1 /* This file is part of the dynarmic project.
2 * Copyright (c) 2020 MerryMage
3 * SPDX-License-Identifier: 0BSD
4 */
5
6 #include "frontend/A32/translate/impl/translate_arm.h"
7
8 #include <optional>
9 #include <tuple>
10 #include "common/bit_util.h"
11
12 namespace Dynarmic::A32 {
13
14 namespace {
15
DecodeType(Imm<4> type,size_t size,size_t align)16 std::optional<std::tuple<size_t, size_t, size_t>> DecodeType(Imm<4> type, size_t size, size_t align) {
17 switch (type.ZeroExtend()) {
18 case 0b0111: // VST1 A1 / VLD1 A1
19 if (Common::Bit<1>(align)) {
20 return std::nullopt;
21 }
22 return std::tuple<size_t, size_t, size_t>{1, 1, 0};
23 case 0b1010: // VST1 A2 / VLD1 A2
24 if (align == 0b11) {
25 return std::nullopt;
26 }
27 return std::tuple<size_t, size_t, size_t>{1, 2, 0};
28 case 0b0110: // VST1 A3 / VLD1 A3
29 if (Common::Bit<1>(align)) {
30 return std::nullopt;
31 }
32 return std::tuple<size_t, size_t, size_t>{1, 3, 0};
33 case 0b0010: // VST1 A4 / VLD1 A4
34 return std::tuple<size_t, size_t, size_t>{1, 4, 0};
35 case 0b1000: // VST2 A1 / VLD2 A1
36 if (size == 0b11 || align == 0b11) {
37 return std::nullopt;
38 }
39 return std::tuple<size_t, size_t, size_t>{2, 1, 1};
40 case 0b1001: // VST2 A1 / VLD2 A1
41 if (size == 0b11 || align == 0b11) {
42 return std::nullopt;
43 }
44 return std::tuple<size_t, size_t, size_t>{2, 1, 2};
45 case 0b0011: // VST2 A2 / VLD2 A2
46 if (size == 0b11) {
47 return std::nullopt;
48 }
49 return std::tuple<size_t, size_t, size_t>{2, 2, 2};
50 case 0b0100: // VST3 / VLD3
51 if (size == 0b11 || Common::Bit<1>(align)) {
52 return std::nullopt;
53 }
54 return std::tuple<size_t, size_t, size_t>{3, 1, 1};
55 case 0b0101: // VST3 / VLD3
56 if (size == 0b11 || Common::Bit<1>(align)) {
57 return std::nullopt;
58 }
59 return std::tuple<size_t, size_t, size_t>{3, 1, 2};
60 case 0b0000: // VST4 / VLD4
61 if (size == 0b11) {
62 return std::nullopt;
63 }
64 return std::tuple<size_t, size_t, size_t>{4, 1, 1};
65 case 0b0001: // VST4 / VLD4
66 if (size == 0b11) {
67 return std::nullopt;
68 }
69 return std::tuple<size_t, size_t, size_t>{4, 1, 2};
70 }
71 ASSERT_FALSE("Decode error");
72 }
73 } // anoynmous namespace
74
v8_VST_multiple(bool D,Reg n,size_t Vd,Imm<4> type,size_t size,size_t align,Reg m)75 bool ArmTranslatorVisitor::v8_VST_multiple(bool D, Reg n, size_t Vd, Imm<4> type, size_t size, size_t align, Reg m) {
76 const auto decoded_type = DecodeType(type, size, align);
77 if (!decoded_type) {
78 return UndefinedInstruction();
79 }
80 const auto [nelem, regs, inc] = *decoded_type;
81
82 const ExtReg d = ToExtRegD(Vd, D);
83 const size_t d_last = RegNumber(d) + inc * (nelem - 1);
84 if (n == Reg::R15 || d_last + regs > 32) {
85 return UnpredictableInstruction();
86 }
87
88 [[maybe_unused]] const size_t alignment = align == 0 ? 1 : 4 << align;
89 const size_t ebytes = static_cast<size_t>(1) << size;
90 const size_t elements = 8 / ebytes;
91
92 const bool wback = m != Reg::R15;
93 const bool register_index = m != Reg::R15 && m != Reg::R13;
94
95 IR::U32 address = ir.GetRegister(n);
96 for (size_t r = 0; r < regs; r++) {
97 for (size_t e = 0; e < elements; e++) {
98 for (size_t i = 0; i < nelem; i++) {
99 const ExtReg ext_reg = d + i * inc + r;
100 const IR::U64 shifted_element = ir.LogicalShiftRight(ir.GetExtendedRegister(ext_reg), ir.Imm8(static_cast<u8>(e * ebytes * 8)));
101 const IR::UAny element = ir.LeastSignificant(8 * ebytes, shifted_element);
102 ir.WriteMemory(8 * ebytes, address, element);
103
104 address = ir.Add(address, ir.Imm32(static_cast<u32>(ebytes)));
105 }
106 }
107 }
108
109 if (wback) {
110 if (register_index) {
111 ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.GetRegister(m)));
112 } else {
113 ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.Imm32(static_cast<u32>(8 * nelem * regs))));
114 }
115 }
116
117 return true;
118 }
119
v8_VLD_multiple(bool D,Reg n,size_t Vd,Imm<4> type,size_t size,size_t align,Reg m)120 bool ArmTranslatorVisitor::v8_VLD_multiple(bool D, Reg n, size_t Vd, Imm<4> type, size_t size, size_t align, Reg m) {
121 const auto decoded_type = DecodeType(type, size, align);
122 if (!decoded_type) {
123 return UndefinedInstruction();
124 }
125 const auto [nelem, regs, inc] = *decoded_type;
126
127 const ExtReg d = ToExtRegD(Vd, D);
128 const size_t d_last = RegNumber(d) + inc * (nelem - 1);
129 if (n == Reg::R15 || d_last + regs > 32) {
130 return UnpredictableInstruction();
131 }
132
133 [[maybe_unused]] const size_t alignment = align == 0 ? 1 : 4 << align;
134 const size_t ebytes = static_cast<size_t>(1) << size;
135 const size_t elements = 8 / ebytes;
136
137 const bool wback = m != Reg::R15;
138 const bool register_index = m != Reg::R15 && m != Reg::R13;
139
140 for (size_t r = 0; r < regs; r++) {
141 for (size_t i = 0; i < nelem; i++) {
142 const ExtReg ext_reg = d + i * inc + r;
143 ir.SetExtendedRegister(ext_reg, ir.Imm64(0));
144 }
145 }
146
147 IR::U32 address = ir.GetRegister(n);
148 for (size_t r = 0; r < regs; r++) {
149 for (size_t e = 0; e < elements; e++) {
150 for (size_t i = 0; i < nelem; i++) {
151 const ExtReg ext_reg = d + i * inc + r;
152 const IR::U64 element = ir.ZeroExtendToLong(ir.ReadMemory(ebytes * 8, address));
153 const IR::U64 shifted_element = ir.LogicalShiftLeft(element, ir.Imm8(static_cast<u8>(e * ebytes * 8)));
154 ir.SetExtendedRegister(ext_reg, ir.Or(ir.GetExtendedRegister(ext_reg), shifted_element));
155
156 address = ir.Add(address, ir.Imm32(static_cast<u32>(ebytes)));
157 }
158 }
159 }
160
161 if (wback) {
162 if (register_index) {
163 ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.GetRegister(m)));
164 } else {
165 ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.Imm32(static_cast<u32>(8 * nelem * regs))));
166 }
167 }
168
169 return true;
170 }
171
172 } // namespace Dynarmic::A32
173