blob: 1f565e504a4c93f1e978fe66f01f5f1b07237ae4 [file] [log] [blame]
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "disassembler_arm.h"
18
Ian Rogersef7d42f2014-01-06 12:55:46 -080019#include <inttypes.h>
20
Ian Rogers3a5c1ce2012-02-29 10:06:46 -080021#include <iostream>
22
Elliott Hughes07ed66b2012-12-12 18:34:25 -080023#include "base/logging.h"
Elliott Hughese222ee02012-12-13 14:41:43 -080024#include "base/stringprintf.h"
Elliott Hughes28fa76d2012-04-09 17:31:46 -070025#include "thread.h"
Elliott Hughes0f3c5532012-03-30 14:51:51 -070026
Ian Rogers3a5c1ce2012-02-29 10:06:46 -080027namespace art {
28namespace arm {
29
Ian Rogersb23a7722012-10-09 16:54:26 -070030size_t DisassemblerArm::Dump(std::ostream& os, const uint8_t* begin) {
31 if ((reinterpret_cast<intptr_t>(begin) & 1) == 0) {
32 DumpArm(os, begin);
33 return 4;
34 } else {
35 // remove thumb specifier bits
36 begin = reinterpret_cast<const uint8_t*>(reinterpret_cast<uintptr_t>(begin) & ~1);
37 return DumpThumb16(os, begin);
38 }
39}
40
Ian Rogers3a5c1ce2012-02-29 10:06:46 -080041void DisassemblerArm::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) {
42 if ((reinterpret_cast<intptr_t>(begin) & 1) == 0) {
43 for (const uint8_t* cur = begin; cur < end; cur += 4) {
44 DumpArm(os, cur);
45 }
46 } else {
47 // remove thumb specifier bits
48 begin = reinterpret_cast<const uint8_t*>(reinterpret_cast<uintptr_t>(begin) & ~1);
49 end = reinterpret_cast<const uint8_t*>(reinterpret_cast<uintptr_t>(end) & ~1);
50 for (const uint8_t* cur = begin; cur < end;) {
51 cur += DumpThumb16(os, cur);
52 }
53 }
54}
55
Elliott Hughes77405792012-03-15 15:22:12 -070056static const char* kConditionCodeNames[] = {
Elliott Hughescbf0b612012-03-15 16:23:47 -070057 "eq", // 0000 - equal
58 "ne", // 0001 - not-equal
59 "cs", // 0010 - carry-set, greater than, equal or unordered
60 "cc", // 0011 - carry-clear, less than
61 "mi", // 0100 - minus, negative
62 "pl", // 0101 - plus, positive or zero
63 "vs", // 0110 - overflow
64 "vc", // 0111 - no overflow
65 "hi", // 1000 - unsigned higher
66 "ls", // 1001 - unsigned lower or same
67 "ge", // 1010 - signed greater than or equal
68 "lt", // 1011 - signed less than
69 "gt", // 1100 - signed greater than
70 "le", // 1101 - signed less than or equal
71 "", // 1110 - always
72 "nv", // 1111 - never (mostly obsolete, but might be a clue that we're mistranslating)
Ian Rogers40627db2012-03-04 17:31:09 -080073};
74
75void DisassemblerArm::DumpCond(std::ostream& os, uint32_t cond) {
76 if (cond < 15) {
Elliott Hughes77405792012-03-15 15:22:12 -070077 os << kConditionCodeNames[cond];
Ian Rogers40627db2012-03-04 17:31:09 -080078 } else {
79 os << "Unexpected condition: " << cond;
80 }
81}
82
Ian Rogersb122a4b2013-11-19 18:00:50 -080083void DisassemblerArm::DumpMemoryDomain(std::ostream& os, uint32_t domain) {
84 switch (domain) {
85 case 0b1111: os << "sy"; break;
86 case 0b1110: os << "st"; break;
87 case 0b1011: os << "ish"; break;
88 case 0b1010: os << "ishst"; break;
89 case 0b0111: os << "nsh"; break;
90 case 0b0110: os << "nshst"; break;
91 case 0b0011: os << "osh"; break;
92 case 0b0010: os << "oshst"; break;
93 }
94}
95
Ian Rogers40627db2012-03-04 17:31:09 -080096void DisassemblerArm::DumpBranchTarget(std::ostream& os, const uint8_t* instr_ptr, int32_t imm32) {
Elliott Hughes1ca98492012-04-12 17:21:02 -070097 os << StringPrintf("%+d (%p)", imm32, instr_ptr + imm32);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -080098}
99
100static uint32_t ReadU16(const uint8_t* ptr) {
101 return ptr[0] | (ptr[1] << 8);
102}
103
104static uint32_t ReadU32(const uint8_t* ptr) {
105 return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
106}
107
Elliott Hughes77405792012-03-15 15:22:12 -0700108static const char* kDataProcessingOperations[] = {
Elliott Hughescbf0b612012-03-15 16:23:47 -0700109 "and", "eor", "sub", "rsb", "add", "adc", "sbc", "rsc",
110 "tst", "teq", "cmp", "cmn", "orr", "mov", "bic", "mvn",
Elliott Hughes77405792012-03-15 15:22:12 -0700111};
112
Ian Rogersad03ef52012-03-18 19:34:47 -0700113static const char* kThumbDataProcessingOperations[] = {
114 "and", "eor", "lsl", "lsr", "asr", "adc", "sbc", "ror",
115 "tst", "rsb", "cmp", "cmn", "orr", "mul", "bic", "mvn",
116};
117
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100118static const char* const kThumb2ShiftOperations[] = {
119 "lsl", "lsr", "asr", "ror"
120};
121
Vladimir Markoa8b4caf2013-10-24 15:08:57 +0100122static const char* kThumbReverseOperations[] = {
123 "rev", "rev16", "rbit", "revsh"
124};
125
Elliott Hughes77405792012-03-15 15:22:12 -0700126struct ArmRegister {
Elliott Hughes74847412012-06-20 18:10:21 -0700127 explicit ArmRegister(uint32_t r) : r(r) { CHECK_LE(r, 15U); }
Elliott Hughes630e77d2012-03-22 19:20:56 -0700128 ArmRegister(uint32_t instruction, uint32_t at_bit) : r((instruction >> at_bit) & 0xf) { CHECK_LE(r, 15U); }
Elliott Hughes77405792012-03-15 15:22:12 -0700129 uint32_t r;
130};
131std::ostream& operator<<(std::ostream& os, const ArmRegister& r) {
132 if (r.r == 13) {
Elliott Hughescbf0b612012-03-15 16:23:47 -0700133 os << "sp";
Elliott Hughes77405792012-03-15 15:22:12 -0700134 } else if (r.r == 14) {
Elliott Hughescbf0b612012-03-15 16:23:47 -0700135 os << "lr";
Elliott Hughes77405792012-03-15 15:22:12 -0700136 } else if (r.r == 15) {
Elliott Hughescbf0b612012-03-15 16:23:47 -0700137 os << "pc";
Elliott Hughes77405792012-03-15 15:22:12 -0700138 } else {
Elliott Hughescbf0b612012-03-15 16:23:47 -0700139 os << "r" << r.r;
Elliott Hughes77405792012-03-15 15:22:12 -0700140 }
141 return os;
142}
143
Elliott Hughes630e77d2012-03-22 19:20:56 -0700144struct ThumbRegister : ArmRegister {
145 ThumbRegister(uint16_t instruction, uint16_t at_bit) : ArmRegister((instruction >> at_bit) & 0x7) {}
Elliott Hughes77405792012-03-15 15:22:12 -0700146};
147
148struct Rm {
Elliott Hughes74847412012-06-20 18:10:21 -0700149 explicit Rm(uint32_t instruction) : shift((instruction >> 4) & 0xff), rm(instruction & 0xf) {}
Elliott Hughes77405792012-03-15 15:22:12 -0700150 uint32_t shift;
151 ArmRegister rm;
152};
153std::ostream& operator<<(std::ostream& os, const Rm& r) {
154 os << r.rm;
155 if (r.shift != 0) {
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700156 os << "-shift-" << r.shift; // TODO
Elliott Hughes77405792012-03-15 15:22:12 -0700157 }
158 return os;
159}
160
Elliott Hughes1ca98492012-04-12 17:21:02 -0700161struct ShiftedImmediate {
Elliott Hughes74847412012-06-20 18:10:21 -0700162 explicit ShiftedImmediate(uint32_t instruction) {
Elliott Hughes3d71d072012-04-10 18:28:35 -0700163 uint32_t rotate = ((instruction >> 8) & 0xf);
164 uint32_t imm = (instruction & 0xff);
165 value = (imm >> (2 * rotate)) | (imm << (32 - (2 * rotate)));
166 }
167 uint32_t value;
Elliott Hughes77405792012-03-15 15:22:12 -0700168};
Elliott Hughes1ca98492012-04-12 17:21:02 -0700169std::ostream& operator<<(std::ostream& os, const ShiftedImmediate& rhs) {
Elliott Hughes3d71d072012-04-10 18:28:35 -0700170 os << "#" << rhs.value;
Elliott Hughes77405792012-03-15 15:22:12 -0700171 return os;
172}
173
174struct RegisterList {
Elliott Hughes74847412012-06-20 18:10:21 -0700175 explicit RegisterList(uint32_t instruction) : register_list(instruction & 0xffff) {}
Elliott Hughes77405792012-03-15 15:22:12 -0700176 uint32_t register_list;
177};
178std::ostream& operator<<(std::ostream& os, const RegisterList& rhs) {
179 if (rhs.register_list == 0) {
180 os << "<no register list?>";
181 return os;
182 }
Elliott Hughes630e77d2012-03-22 19:20:56 -0700183 os << "{";
Elliott Hughes77405792012-03-15 15:22:12 -0700184 bool first = true;
185 for (size_t i = 0; i < 16; i++) {
186 if ((rhs.register_list & (1 << i)) != 0) {
187 if (first) {
Elliott Hughes77405792012-03-15 15:22:12 -0700188 first = false;
189 } else {
190 os << ", ";
191 }
192 os << ArmRegister(i);
193 }
194 }
195 os << "}";
196 return os;
197}
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800198
Vladimir Markodd577a32013-11-07 19:25:24 +0000199struct FpRegister {
200 explicit FpRegister(uint32_t instr, uint16_t at_bit, uint16_t extra_at_bit) {
201 size = (instr >> 8) & 1;
202 uint32_t Vn = (instr >> at_bit) & 0xF;
203 uint32_t N = (instr >> extra_at_bit) & 1;
204 r = (size != 0 ? ((N << 4) | Vn) : ((Vn << 1) | N));
205 }
Zheng Xue19649a2014-02-27 13:30:55 +0000206 explicit FpRegister(uint32_t instr, uint16_t at_bit, uint16_t extra_at_bit,
207 uint32_t forced_size) {
208 size = forced_size;
209 uint32_t Vn = (instr >> at_bit) & 0xF;
210 uint32_t N = (instr >> extra_at_bit) & 1;
211 r = (size != 0 ? ((N << 4) | Vn) : ((Vn << 1) | N));
212 }
Vladimir Markodd577a32013-11-07 19:25:24 +0000213 FpRegister(const FpRegister& other, uint32_t offset)
214 : size(other.size), r(other.r + offset) {}
215
216 uint32_t size; // 0 = f32, 1 = f64
217 uint32_t r;
218};
219std::ostream& operator<<(std::ostream& os, const FpRegister& rhs) {
220 return os << ((rhs.size != 0) ? "d" : "s") << rhs.r;
221}
222
223struct FpRegisterRange {
224 explicit FpRegisterRange(uint32_t instr)
225 : first(instr, 12, 22), imm8(instr & 0xFF) {}
226 FpRegister first;
227 uint32_t imm8;
228};
229std::ostream& operator<<(std::ostream& os, const FpRegisterRange& rhs) {
230 os << "{" << rhs.first;
231 int count = (rhs.first.size != 0 ? ((rhs.imm8 + 1u) >> 1) : rhs.imm8);
232 if (count > 1) {
233 os << "-" << FpRegister(rhs.first, count - 1);
234 }
235 if (rhs.imm8 == 0) {
236 os << " (EMPTY)";
237 } else if (rhs.first.size != 0 && (rhs.imm8 & 1) != 0) {
238 os << rhs.first << " (HALF)";
239 }
240 os << "}";
241 return os;
242}
243
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800244void DisassemblerArm::DumpArm(std::ostream& os, const uint8_t* instr_ptr) {
Elliott Hughes77405792012-03-15 15:22:12 -0700245 uint32_t instruction = ReadU32(instr_ptr);
246 uint32_t cond = (instruction >> 28) & 0xf;
247 uint32_t op1 = (instruction >> 25) & 0x7;
Elliott Hughes3d71d072012-04-10 18:28:35 -0700248 std::string opcode;
249 std::string suffixes;
Elliott Hughescbf0b612012-03-15 16:23:47 -0700250 std::ostringstream args;
Elliott Hughes77405792012-03-15 15:22:12 -0700251 switch (op1) {
252 case 0:
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700253 case 1: // Data processing instructions.
Elliott Hughes77405792012-03-15 15:22:12 -0700254 {
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700255 if ((instruction & 0x0ff000f0) == 0x01200070) { // BKPT
Elliott Hughes3d71d072012-04-10 18:28:35 -0700256 opcode = "bkpt";
257 uint32_t imm12 = (instruction >> 8) & 0xfff;
258 uint32_t imm4 = (instruction & 0xf);
259 args << '#' << ((imm12 << 4) | imm4);
260 break;
261 }
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700262 if ((instruction & 0x0fffffd0) == 0x012fff10) { // BX and BLX (register)
Elliott Hughes3d71d072012-04-10 18:28:35 -0700263 opcode = (((instruction >> 5) & 1) ? "blx" : "bx");
Elliott Hughescbf0b612012-03-15 16:23:47 -0700264 args << ArmRegister(instruction & 0xf);
Elliott Hughes77405792012-03-15 15:22:12 -0700265 break;
266 }
267 bool i = (instruction & (1 << 25)) != 0;
268 bool s = (instruction & (1 << 20)) != 0;
Elliott Hughes3d71d072012-04-10 18:28:35 -0700269 uint32_t op = (instruction >> 21) & 0xf;
270 opcode = kDataProcessingOperations[op];
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700271 bool implicit_s = ((op & ~3) == 8); // TST, TEQ, CMP, and CMN.
Dave Allison20dfc792014-06-16 20:44:29 -0700272 bool is_mov = op == 0b1101 || op == 0b1111;
273 if (is_mov) {
274 // Show only Rd and Rm.
Elliott Hughes3d71d072012-04-10 18:28:35 -0700275 if (s) {
Dave Allison20dfc792014-06-16 20:44:29 -0700276 suffixes += 's';
277 }
278 args << ArmRegister(instruction, 12) << ", ";
279 if (i) {
280 args << ShiftedImmediate(instruction);
281 } else {
282 // TODO: Shifted register.
283 args << ArmRegister(instruction, 16) << ", " << ArmRegister(instruction, 0);
284 }
Elliott Hughes77405792012-03-15 15:22:12 -0700285 } else {
Dave Allison20dfc792014-06-16 20:44:29 -0700286 if (implicit_s) {
287 // Rd is unused (and not shown), and we don't show the 's' suffix either.
288 } else {
289 if (s) {
290 suffixes += 's';
291 }
292 args << ArmRegister(instruction, 12) << ", ";
293 }
294 if (i) {
295 args << ArmRegister(instruction, 16) << ", " << ShiftedImmediate(instruction);
296 } else {
297 // TODO: Shifted register.
298 args << ArmRegister(instruction, 16) << ", " << ArmRegister(instruction, 0);
299 }
Elliott Hughes77405792012-03-15 15:22:12 -0700300 }
301 }
302 break;
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700303 case 2: // Load/store word and unsigned byte.
Elliott Hughes77405792012-03-15 15:22:12 -0700304 {
305 bool p = (instruction & (1 << 24)) != 0;
306 bool b = (instruction & (1 << 22)) != 0;
307 bool w = (instruction & (1 << 21)) != 0;
308 bool l = (instruction & (1 << 20)) != 0;
Elliott Hughes3d71d072012-04-10 18:28:35 -0700309 opcode = StringPrintf("%s%s", (l ? "ldr" : "str"), (b ? "b" : ""));
Elliott Hughes630e77d2012-03-22 19:20:56 -0700310 args << ArmRegister(instruction, 12) << ", ";
311 ArmRegister rn(instruction, 16);
312 if (rn.r == 0xf) {
Elliott Hughes77405792012-03-15 15:22:12 -0700313 UNIMPLEMENTED(FATAL) << "literals";
314 } else {
315 bool wback = !p || w;
Elliott Hughes1ca98492012-04-12 17:21:02 -0700316 uint32_t offset = (instruction & 0xfff);
Elliott Hughes77405792012-03-15 15:22:12 -0700317 if (p && !wback) {
Elliott Hughes1ca98492012-04-12 17:21:02 -0700318 args << "[" << rn << ", #" << offset << "]";
Elliott Hughes77405792012-03-15 15:22:12 -0700319 } else if (p && wback) {
Elliott Hughes1ca98492012-04-12 17:21:02 -0700320 args << "[" << rn << ", #" << offset << "]!";
Elliott Hughes77405792012-03-15 15:22:12 -0700321 } else if (!p && wback) {
Elliott Hughes1ca98492012-04-12 17:21:02 -0700322 args << "[" << rn << "], #" << offset;
Elliott Hughes77405792012-03-15 15:22:12 -0700323 } else {
324 LOG(FATAL) << p << " " << w;
325 }
Elliott Hughes3d71d072012-04-10 18:28:35 -0700326 if (rn.r == 9) {
327 args << " ; ";
Ian Rogersdd7624d2014-03-14 17:43:00 -0700328 Thread::DumpThreadOffset<4>(args, offset);
Elliott Hughes3d71d072012-04-10 18:28:35 -0700329 }
Elliott Hughes77405792012-03-15 15:22:12 -0700330 }
331 }
332 break;
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700333 case 4: // Load/store multiple.
Elliott Hughes77405792012-03-15 15:22:12 -0700334 {
335 bool p = (instruction & (1 << 24)) != 0;
336 bool u = (instruction & (1 << 23)) != 0;
337 bool w = (instruction & (1 << 21)) != 0;
338 bool l = (instruction & (1 << 20)) != 0;
Elliott Hughes3d71d072012-04-10 18:28:35 -0700339 opcode = StringPrintf("%s%c%c", (l ? "ldm" : "stm"), (u ? 'i' : 'd'), (p ? 'b' : 'a'));
Elliott Hughes630e77d2012-03-22 19:20:56 -0700340 args << ArmRegister(instruction, 16) << (w ? "!" : "") << ", " << RegisterList(instruction);
Elliott Hughes77405792012-03-15 15:22:12 -0700341 }
342 break;
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700343 case 5: // Branch/branch with link.
Elliott Hughes3d71d072012-04-10 18:28:35 -0700344 {
345 bool bl = (instruction & (1 << 24)) != 0;
346 opcode = (bl ? "bl" : "b");
Elliott Hughesd86261e2012-04-11 11:23:23 -0700347 int32_t imm26 = (instruction & 0xffffff) << 2;
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700348 int32_t imm32 = (imm26 << 6) >> 6; // Sign extend.
Elliott Hughes3d71d072012-04-10 18:28:35 -0700349 DumpBranchTarget(args, instr_ptr + 8, imm32);
350 }
351 break;
Elliott Hughes77405792012-03-15 15:22:12 -0700352 default:
Elliott Hughes3d71d072012-04-10 18:28:35 -0700353 opcode = "???";
Elliott Hughes77405792012-03-15 15:22:12 -0700354 break;
355 }
Elliott Hughes3d71d072012-04-10 18:28:35 -0700356 opcode += kConditionCodeNames[cond];
357 opcode += suffixes;
Elliott Hughescbf0b612012-03-15 16:23:47 -0700358 // TODO: a more complete ARM disassembler could generate wider opcodes.
Ian Rogers2bcb4a42012-11-08 10:39:18 -0800359 os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n';
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800360}
361
Ian Rogersa9650dd2013-10-04 08:23:32 -0700362int32_t ThumbExpand(int32_t imm12) {
363 if ((imm12 & 0xC00) == 0) {
364 switch ((imm12 >> 8) & 3) {
365 case 0:
366 return imm12 & 0xFF;
367 case 1:
368 return ((imm12 & 0xFF) << 16) | (imm12 & 0xFF);
369 case 2:
370 return ((imm12 & 0xFF) << 24) | ((imm12 & 0xFF) << 8);
371 default: // 3
372 return ((imm12 & 0xFF) << 24) | ((imm12 & 0xFF) << 16) | ((imm12 & 0xFF) << 8) |
373 (imm12 & 0xFF);
374 }
375 } else {
376 uint32_t val = 0x80 | (imm12 & 0x7F);
377 int32_t rotate = (imm12 >> 7) & 0x1F;
378 return (val >> rotate) | (val << (32 - rotate));
379 }
380}
381
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100382uint32_t VFPExpand32(uint32_t imm8) {
383 CHECK_EQ(imm8 & 0xffu, imm8);
384 uint32_t bit_a = (imm8 >> 7) & 1;
385 uint32_t bit_b = (imm8 >> 6) & 1;
386 uint32_t slice = imm8 & 0x3f;
387 return (bit_a << 31) | ((1 << 30) - (bit_b << 25)) | (slice << 19);
388}
389
390uint64_t VFPExpand64(uint32_t imm8) {
391 CHECK_EQ(imm8 & 0xffu, imm8);
392 uint64_t bit_a = (imm8 >> 7) & 1;
393 uint64_t bit_b = (imm8 >> 6) & 1;
394 uint64_t slice = imm8 & 0x3f;
395 return (bit_a << 31) | ((UINT64_C(1) << 62) - (bit_b << 54)) | (slice << 48);
396}
397
398uint64_t AdvSIMDExpand(uint32_t op, uint32_t cmode, uint32_t imm8) {
399 CHECK_EQ(op & 1, op);
400 CHECK_EQ(cmode & 0xf, cmode);
401 CHECK_EQ(imm8 & 0xff, imm8);
402 int32_t cmode321 = cmode >> 1;
403 if (imm8 == 0 && cmode321 != 0 && cmode321 != 4 && cmode321 != 7) {
404 return INT64_C(0x00000000deadbeef); // UNPREDICTABLE
405 }
406 uint64_t imm = imm8;
407 switch (cmode321) {
408 case 3: imm <<= 8; // Fall through.
409 case 2: imm <<= 8; // Fall through.
410 case 1: imm <<= 8; // Fall through.
411 case 0: return static_cast<int64_t>((imm << 32) | imm);
412 case 5: imm <<= 8; // Fall through.
413 case 4: return static_cast<int64_t>((imm << 48) | (imm << 32) | (imm << 16) | imm);
414 case 6:
415 imm = ((imm + 1u) << ((cmode & 1) != 0 ? 16 : 8)) - 1u; // Add 8 or 16 ones.
416 return static_cast<int64_t>((imm << 32) | imm);
417 default:
418 CHECK_EQ(cmode321, 7);
419 if ((cmode & 1) == 0 && op == 0) {
420 imm = (imm << 8) | imm;
421 return static_cast<int64_t>((imm << 48) | (imm << 32) | (imm << 16) | imm);
422 } else if ((cmode & 1) == 0 && op != 0) {
423 for (int i = 1; i != 8; ++i) {
424 imm |= ((imm >> i) & UINT64_C(1)) << (i * 8);
425 }
426 imm = imm & ~UINT64_C(0xfe);
427 return static_cast<int64_t>((imm << 8) - imm);
428 } else if ((cmode & 1) != 0 && op == 0) {
429 imm = static_cast<uint32_t>(VFPExpand32(imm8));
430 return static_cast<int64_t>((imm << 32) | imm);
431 } else {
432 return INT64_C(0xdeadbeef00000000); // UNDEFINED
433 }
434 }
435}
436
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800437size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) {
438 uint32_t instr = (ReadU16(instr_ptr) << 16) | ReadU16(instr_ptr + 2);
439 // |111|1 1|1000000|0000|1111110000000000|
440 // |5 3|2 1|0987654|3 0|5 0 5 0|
441 // |---|---|-------|----|----------------|
442 // |332|2 2|2222222|1111|1111110000000000|
443 // |1 9|8 7|6543210|9 6|5 0 5 0|
444 // |---|---|-------|----|----------------|
445 // |111|op1| op2 | | |
446 uint32_t op1 = (instr >> 27) & 3;
Elliott Hughes77405792012-03-15 15:22:12 -0700447 if (op1 == 0) {
448 return DumpThumb16(os, instr_ptr);
449 }
450
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800451 uint32_t op2 = (instr >> 20) & 0x7F;
Elliott Hughescbf0b612012-03-15 16:23:47 -0700452 std::ostringstream opcode;
453 std::ostringstream args;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800454 switch (op1) {
455 case 0:
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800456 break;
457 case 1:
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700458 if ((op2 & 0x64) == 0) { // 00x x0xx
459 // |111|11|10|00|0|00|0000|1111110000000000|
460 // |5 3|21|09|87|6|54|3 0|5 0 5 0|
461 // |---|--|--|--|-|--|----|----------------|
462 // |332|22|22|22|2|22|1111|1111110000000000|
463 // |1 9|87|65|43|2|10|9 6|5 0 5 0|
464 // |---|--|--|--|-|--|----|----------------|
465 // |111|01|00|op|0|WL| Rn | |
466 // |111|01| op2 | | |
467 // STM - 111 01 00-01-0-W0 nnnn rrrrrrrrrrrrrrrr
468 // LDM - 111 01 00-01-0-W1 nnnn rrrrrrrrrrrrrrrr
469 // PUSH- 111 01 00-01-0-10 1101 0M0rrrrrrrrrrrrr
470 // POP - 111 01 00-01-0-11 1101 PM0rrrrrrrrrrrrr
471 uint32_t op = (instr >> 23) & 3;
472 uint32_t W = (instr >> 21) & 1;
473 uint32_t L = (instr >> 20) & 1;
474 ArmRegister Rn(instr, 16);
475 if (op == 1 || op == 2) {
476 if (op == 1) {
477 if (L == 0) {
478 opcode << "stm";
479 args << Rn << (W == 0 ? "" : "!") << ", ";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800480 } else {
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700481 if (Rn.r != 13) {
482 opcode << "ldm";
Elliott Hughes630e77d2012-03-22 19:20:56 -0700483 args << Rn << (W == 0 ? "" : "!") << ", ";
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700484 } else {
485 opcode << "pop";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800486 }
487 }
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700488 } else {
489 if (L == 0) {
490 if (Rn.r != 13) {
491 opcode << "stmdb";
492 args << Rn << (W == 0 ? "" : "!") << ", ";
493 } else {
494 opcode << "push";
495 }
496 } else {
497 opcode << "ldmdb";
498 args << Rn << (W == 0 ? "" : "!") << ", ";
499 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800500 }
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700501 args << RegisterList(instr);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -0800502 }
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700503 } else if ((op2 & 0x64) == 4) { // 00x x1xx
Ian Rogers9af89402012-09-07 11:29:35 -0700504 uint32_t op3 = (instr >> 23) & 3;
505 uint32_t op4 = (instr >> 20) & 3;
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700506 // uint32_t op5 = (instr >> 4) & 0xF;
Ian Rogers9af89402012-09-07 11:29:35 -0700507 ArmRegister Rn(instr, 16);
508 ArmRegister Rt(instr, 12);
Dave Allison70202782013-10-22 17:52:19 -0700509 ArmRegister Rd(instr, 8);
Ian Rogers9af89402012-09-07 11:29:35 -0700510 uint32_t imm8 = instr & 0xFF;
Dave Allison70202782013-10-22 17:52:19 -0700511 if ((op3 & 2) == 2) { // 1x
512 int W = (instr >> 21) & 1;
513 int U = (instr >> 23) & 1;
514 int P = (instr >> 24) & 1;
515
516 if ((op4 & 1) == 1) {
517 opcode << "ldrd";
518 } else {
519 opcode << "strd";
520 }
521 args << Rt << "," << Rd << ", [" << Rn;
522 const char *sign = U ? "+" : "-";
523 if (P == 0 && W == 1) {
Vladimir Markoad435eb2013-11-15 15:21:25 +0000524 args << "], #" << sign << (imm8 << 2);
Dave Allison70202782013-10-22 17:52:19 -0700525 } else {
Vladimir Markoad435eb2013-11-15 15:21:25 +0000526 args << ", #" << sign << (imm8 << 2) << "]";
Dave Allison70202782013-10-22 17:52:19 -0700527 if (W == 1) {
528 args << "!";
529 }
530 }
531 } else { // 0x
532 switch (op4) {
533 case 0:
534 if (op3 == 0) { // op3 is 00, op4 is 00
535 opcode << "strex";
536 args << Rd << ", " << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]";
Vladimir Marko3e5af822013-11-21 15:01:20 +0000537 if (Rd.r == 13 || Rd.r == 15 || Rt.r == 13 || Rt.r == 15 || Rn.r == 15 ||
538 Rd.r == Rn.r || Rd.r == Rt.r) {
539 args << " (UNPREDICTABLE)";
540 }
Dave Allison70202782013-10-22 17:52:19 -0700541 } else { // op3 is 01, op4 is 00
542 // this is one of strexb, strexh or strexd
543 int op5 = (instr >> 4) & 0xf;
544 switch (op5) {
545 case 4:
Dave Allison70202782013-10-22 17:52:19 -0700546 case 5:
Vladimir Marko3e5af822013-11-21 15:01:20 +0000547 opcode << ((op5 == 4) ? "strexb" : "strexh");
548 Rd = ArmRegister(instr, 0);
549 args << Rd << ", " << Rt << ", [" << Rn << "]";
550 if (Rd.r == 13 || Rd.r == 15 || Rt.r == 13 || Rt.r == 15 || Rn.r == 15 ||
551 Rd.r == Rn.r || Rd.r == Rt.r || (instr & 0xf00) != 0xf00) {
552 args << " (UNPREDICTABLE)";
553 }
Dave Allison70202782013-10-22 17:52:19 -0700554 break;
555 case 7:
556 opcode << "strexd";
Vladimir Marko3e5af822013-11-21 15:01:20 +0000557 ArmRegister Rt2 = Rd;
558 Rd = ArmRegister(instr, 0);
559 args << Rd << ", " << Rt << ", " << Rt2 << ", [" << Rn << "]";
560 if (Rd.r == 13 || Rd.r == 15 || Rt.r == 13 || Rt.r == 15 ||
561 Rt2.r == 13 || Rt2.r == 15 || Rn.r == 15 ||
562 Rd.r == Rn.r || Rd.r == Rt.r || Rd.r == Rt2.r) {
563 args << " (UNPREDICTABLE)";
564 }
Dave Allison70202782013-10-22 17:52:19 -0700565 break;
566 }
567 }
568 break;
569 case 1:
570 if (op3 == 0) { // op3 is 00, op4 is 01
571 opcode << "ldrex";
572 args << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]";
Vladimir Marko3e5af822013-11-21 15:01:20 +0000573 if (Rt.r == 13 || Rt.r == 15 || Rn.r == 15 || (instr & 0xf00) != 0xf00) {
574 args << " (UNPREDICTABLE)";
575 }
Dave Allison70202782013-10-22 17:52:19 -0700576 } else { // op3 is 01, op4 is 01
577 // this is one of strexb, strexh or strexd
578 int op5 = (instr >> 4) & 0xf;
579 switch (op5) {
580 case 0:
581 opcode << "tbb";
582 break;
583 case 1:
584 opcode << "tbh";
585 break;
586 case 4:
Dave Allison70202782013-10-22 17:52:19 -0700587 case 5:
Vladimir Marko3e5af822013-11-21 15:01:20 +0000588 opcode << ((op5 == 4) ? "ldrexb" : "ldrexh");
589 args << Rt << ", [" << Rn << "]";
590 if (Rt.r == 13 || Rt.r == 15 || Rn.r == 15 || (instr & 0xf0f) != 0xf0f) {
591 args << " (UNPREDICTABLE)";
592 }
Dave Allison70202782013-10-22 17:52:19 -0700593 break;
594 case 7:
595 opcode << "ldrexd";
Vladimir Marko3e5af822013-11-21 15:01:20 +0000596 args << Rt << ", " << Rd /* Rt2 */ << ", [" << Rn << "]";
597 if (Rt.r == 13 || Rt.r == 15 || Rd.r == 13 /* Rt2 */ || Rd.r == 15 /* Rt2 */ ||
598 Rn.r == 15 || (instr & 0x00f) != 0x00f) {
599 args << " (UNPREDICTABLE)";
600 }
Dave Allison70202782013-10-22 17:52:19 -0700601 break;
602 }
603 }
604 break;
605 case 2: // op3 is 0x, op4 is 10
606 case 3: // op3 is 0x, op4 is 11
607 if (op4 == 2) {
608 opcode << "strd";
609 } else {
610 opcode << "ldrd";
611 }
612 int W = (instr >> 21) & 1;
613 int U = (instr >> 23) & 1;
614 int P = (instr >> 24) & 1;
615
616 args << Rt << "," << Rd << ", [" << Rn;
617 const char *sign = U ? "+" : "-";
618 if (P == 0 && W == 1) {
619 args << "], #" << sign << imm8;
620 } else {
621 args << ", #" << sign << imm8 << "]";
622 if (W == 1) {
623 args << "!";
624 }
625 }
626 break;
627 }
628 }
629
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700630 } else if ((op2 & 0x60) == 0x20) { // 01x xxxx
631 // Data-processing (shifted register)
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100632 // |111|1110|0000|0|0000|1111|1100|00|00|0000|
633 // |5 3|2109|8765|4|3 0|5 |10 8|7 |5 |3 0|
634 // |---|----|----|-|----|----|----|--|--|----|
635 // |332|2222|2222|2|1111|1111|1100|00|00|0000|
636 // |1 9|8765|4321|0|9 6|5 |10 8|7 |5 |3 0|
637 // |---|----|----|-|----|----|----|--|--|----|
638 // |111|0101| op3|S| Rn |imm3| Rd |i2|ty| Rm |
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700639 uint32_t op3 = (instr >> 21) & 0xF;
640 uint32_t S = (instr >> 20) & 1;
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100641 uint32_t imm3 = ((instr >> 12) & 0x7);
642 uint32_t imm2 = ((instr >> 6) & 0x3);
Dmitriy Ivanov7d180cb2014-03-25 10:31:04 -0700643 uint32_t imm5 = ((imm3 << 2) | imm2);
644 uint32_t shift_type = ((instr >> 4) & 0x3);
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700645 ArmRegister Rd(instr, 8);
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100646 ArmRegister Rn(instr, 16);
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700647 ArmRegister Rm(instr, 0);
648 switch (op3) {
649 case 0x0:
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100650 if (Rd.r != 0xF) {
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700651 opcode << "and";
652 } else {
Brian Carlstrom4a999e22013-03-11 16:57:09 -0700653 if (S != 1U) {
654 opcode << "UNKNOWN TST-" << S;
655 break;
656 }
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700657 opcode << "tst";
658 S = 0; // don't print 's'
659 }
660 break;
661 case 0x1: opcode << "bic"; break;
662 case 0x2:
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100663 if (Rn.r != 0xF) {
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700664 opcode << "orr";
665 } else {
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100666 // TODO: use canonical form if there is a shift (lsl, ...).
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700667 opcode << "mov";
668 }
669 break;
670 case 0x3:
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100671 if (Rn.r != 0xF) {
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700672 opcode << "orn";
673 } else {
674 opcode << "mvn";
675 }
676 break;
677 case 0x4:
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100678 if (Rd.r != 0xF) {
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700679 opcode << "eor";
680 } else {
Brian Carlstrom4a999e22013-03-11 16:57:09 -0700681 if (S != 1U) {
682 opcode << "UNKNOWN TEQ-" << S;
683 break;
684 }
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700685 opcode << "teq";
686 S = 0; // don't print 's'
687 }
688 break;
689 case 0x6: opcode << "pkh"; break;
690 case 0x8:
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100691 if (Rd.r != 0xF) {
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700692 opcode << "add";
693 } else {
Brian Carlstrom4a999e22013-03-11 16:57:09 -0700694 if (S != 1U) {
695 opcode << "UNKNOWN CMN-" << S;
696 break;
697 }
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700698 opcode << "cmn";
699 S = 0; // don't print 's'
700 }
701 break;
702 case 0xA: opcode << "adc"; break;
703 case 0xB: opcode << "sbc"; break;
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100704 case 0xD:
705 if (Rd.r != 0xF) {
706 opcode << "sub";
707 } else {
Brian Carlstrom4a999e22013-03-11 16:57:09 -0700708 if (S != 1U) {
709 opcode << "UNKNOWN CMP-" << S;
710 break;
711 }
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100712 opcode << "cmp";
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100713 S = 0; // don't print 's'
714 }
715 break;
716 case 0xE: opcode << "rsb"; break;
717 default: opcode << "UNKNOWN DPSR-" << op3; break;
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700718 }
Ian Rogers087b2412012-03-21 01:30:32 -0700719
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700720 if (S == 1) {
721 opcode << "s";
Ian Rogers087b2412012-03-21 01:30:32 -0700722 }
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700723 opcode << ".w";
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100724
725 if (Rd.r != 0xF) {
726 args << Rd << ", ";
727 }
728 if (Rn.r != 0xF) {
729 args << Rn << ", ";
730 }
731 args << Rm;
732
733 // Shift operand.
734 bool noShift = (imm5 == 0 && shift_type != 0x3);
735 if (!noShift) {
736 args << ", ";
737 switch (shift_type) {
738 case 0x0: args << "lsl"; break;
739 case 0x1: args << "lsr"; break;
740 case 0x2: args << "asr"; break;
741 case 0x3:
742 if (imm5 == 0) {
743 args << "rrx";
744 } else {
745 args << "ror";
746 }
747 break;
748 }
749 if (shift_type != 0x3 /* rrx */) {
Dmitriy Ivanov7d180cb2014-03-25 10:31:04 -0700750 args << StringPrintf(" #%d", (0 != imm5 || 0 == shift_type) ? imm5 : 32);
Sebastien Hertzd9e63c02013-02-22 15:19:55 +0100751 }
752 }
753
Ian Rogersc7fe4e02012-03-29 21:36:21 -0700754 } else if ((op2 & 0x40) == 0x40) { // 1xx xxxx
755 // Co-processor instructions
756 // |111|1|11|000000|0000|1111|1100|000|0 |0000|
757 // |5 3|2|10|987654|3 0|54 2|10 8|7 5|4 | 0|
758 // |---|-|--|------|----|----|----|---|---|----|
759 // |332|2|22|222222|1111|1111|1100|000|0 |0000|
760 // |1 9|8|76|543210|9 6|54 2|10 8|7 5|4 | 0|
761 // |---|-|--|------|----|----|----|---|---|----|
762 // |111| |11| op3 | Rn | |copr| |op4| |
763 uint32_t op3 = (instr >> 20) & 0x3F;
764 uint32_t coproc = (instr >> 8) & 0xF;
765 uint32_t op4 = (instr >> 4) & 0x1;
Dave Allison70202782013-10-22 17:52:19 -0700766
Ian Rogersef6a7762013-12-19 17:58:05 -0800767 if (coproc == 0xA || coproc == 0xB) { // 101x
Vladimir Markodd577a32013-11-07 19:25:24 +0000768 if (op3 < 0x20 && (op3 & ~5) != 0) { // 0xxxxx and not 000x0x
769 // Extension register load/store instructions
770 // |1111|110|00000|0000|1111|110|0|00000000|
771 // |5 2|1 9|87654|3 0|5 2|1 9|8|7 0|
772 // |----|---|-----|----|----|---|-|--------|
773 // |3322|222|22222|1111|1111|110|0|00000000|
774 // |1 8|7 5|4 0|9 6|5 2|1 9|8|7 0|
775 // |----|---|-----|----|----|---|-|--------|
776 // |1110|110|PUDWL| Rn | Vd |101|S| imm8 |
Ian Rogers9af89402012-09-07 11:29:35 -0700777 uint32_t P = (instr >> 24) & 1;
778 uint32_t U = (instr >> 23) & 1;
Ian Rogers9af89402012-09-07 11:29:35 -0700779 uint32_t W = (instr >> 21) & 1;
Vladimir Markodd577a32013-11-07 19:25:24 +0000780 if (P == U && W == 1) {
781 opcode << "UNDEFINED";
782 } else {
783 uint32_t L = (instr >> 20) & 1;
784 uint32_t S = (instr >> 8) & 1;
785 ArmRegister Rn(instr, 16);
786 if (P == 1 && W == 0) { // VLDR
787 FpRegister d(instr, 12, 22);
788 uint32_t imm8 = instr & 0xFF;
789 opcode << (L == 1 ? "vldr" : "vstr");
790 args << d << ", [" << Rn << ", #" << ((U == 1) ? "" : "-")
791 << (imm8 << 2) << "]";
Ian Rogersef6a7762013-12-19 17:58:05 -0800792 if (Rn.r == 15 && U == 1) {
793 intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
794 lit_adr = RoundDown(lit_adr, 4) + 4 + (imm8 << 2);
Brian Carlstromc2687ef2014-03-13 15:12:11 -0700795 typedef const int64_t unaligned_int64_t __attribute__ ((aligned (2)));
796 args << StringPrintf(" ; 0x%" PRIx64, *reinterpret_cast<unaligned_int64_t*>(lit_adr));
Ian Rogersef6a7762013-12-19 17:58:05 -0800797 }
Vladimir Markodd577a32013-11-07 19:25:24 +0000798 } else if (Rn.r == 13 && W == 1 && U == L) { // VPUSH/VPOP
799 opcode << (L == 1 ? "vpop" : "vpush");
800 args << FpRegisterRange(instr);
801 } else { // VLDM
802 opcode << (L == 1 ? "vldm" : "vstm");
803 args << Rn << ((W == 1) ? "!" : "") << ", "
804 << FpRegisterRange(instr);
Dave Allison70202782013-10-22 17:52:19 -0700805 }
Vladimir Markodd577a32013-11-07 19:25:24 +0000806 opcode << (S == 1 ? ".f64" : ".f32");
Ian Rogers9af89402012-09-07 11:29:35 -0700807 }
Dave Allison70202782013-10-22 17:52:19 -0700808 } else if ((op3 >> 1) == 2) { // 00010x
Vladimir Markodd577a32013-11-07 19:25:24 +0000809 if ((instr & 0xD0) == 0x10) {
810 // 64bit transfers between ARM core and extension registers.
811 uint32_t L = (instr >> 20) & 1;
812 uint32_t S = (instr >> 8) & 1;
813 ArmRegister Rt2(instr, 16);
814 ArmRegister Rt(instr, 12);
815 FpRegister m(instr, 0, 5);
816 opcode << "vmov" << (S ? ".f64" : ".f32");
817 if (L == 1) {
818 args << Rt << ", " << Rt2 << ", ";
819 }
820 if (S) {
821 args << m;
822 } else {
823 args << m << ", " << FpRegister(m, 1);
824 }
825 if (L == 0) {
826 args << ", " << Rt << ", " << Rt2;
827 }
828 if (Rt.r == 15 || Rt.r == 13 || Rt2.r == 15 || Rt2.r == 13 ||
829 (S == 0 && m.r == 31) || (L == 1 && Rt.r == Rt2.r)) {
830 args << " (UNPREDICTABLE)";
831 }
832 }
Dave Allison70202782013-10-22 17:52:19 -0700833 } else if ((op3 >> 4) == 2 && op4 == 0) { // 10xxxx, op = 0
834 // fp data processing
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100835 // VMLA, VMLS, VMUL, VNMUL, VADD, VSUB, VDIV, VMOV, ...
836 // |1111|1100|0|0|00|0000|1111|110|0|0|0|0|0|0000|
837 // |5 2|1 8|7|6|54|3 0|5 2|1 9|8|7|6|5|4|3 0|
838 // |----|----|-|-|--|----|----|---|-|-|-|-|-|----|
839 // |3322|2222|2|2|22|1111|1111|110|0|0|0|0|0|0000|
840 // |1 8|7 4|3|2|10|9 6|5 2|1 9|8|7|6|5|4|3 0|
841 // |----|----|-|-|--|----|----|---|-|-|-|-|-|----|
842 // |1110|1110| op3 | Vn | Vd |101|S|N|Q|M|0| Vm |
843 // |1110|1110|0|D|00| Vn | Vd |101|S|N|0|M|0| Vm | VMLA
844 // |1110|1110|0|D|00| Vn | Vd |101|S|N|1|M|0| Vm | VMLS
845 // |1110|1110|0|D|10| Vn | Vd |101|S|N|0|M|0| Vm | VMUL
846 // |1110|1110|0|D|10| Vn | Vd |101|S|N|1|M|0| Vm | VNMUL
847 // |1110|1110|0|D|11| Vn | Vd |101|S|N|0|M|0| Vm | VADD
848 // |1110|1110|0|D|11| Vn | Vd |101|S|N|1|M|0| Vm | VSUB
849 // |1110|1110|1|D|00| Vn | Vd |101|S|N|0|M|0| Vm | VDIV
850 // |1110|1110|1|D|11| iH | Vd |101|S|0|0|0|0| iL | VMOV (imm)
851 // |1110|1110|1|D|11|op5 | Vd |101|S|.|1|M|0| Vm | ... (see below)
852 uint32_t S = (instr >> 8) & 1;
853 uint32_t Q = (instr >> 6) & 1;
854 FpRegister d(instr, 12, 22);
855 FpRegister n(instr, 16, 7);
856 FpRegister m(instr, 0, 5);
Zheng Xue19649a2014-02-27 13:30:55 +0000857 if ((op3 & 0xB) == 0) { // 100x00
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100858 opcode << (Q == 0 ? "vmla" : "vmls") << (S != 0 ? ".f64" : ".f32");
Zheng Xue19649a2014-02-27 13:30:55 +0000859 args << d << ", " << n << ", " << m;
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100860 } else if ((op3 & 0xB) == 0x2) { // 100x10
861 opcode << (Q == 0 ? "vmul" : "vnmul") << (S != 0 ? ".f64" : ".f32");
862 args << d << ", " << n << ", " << m;
863 } else if ((op3 & 0xB) == 0x3) { // 100x11
864 opcode << (Q == 0 ? "vadd" : "vsub") << (S != 0 ? ".f64" : ".f32");
865 args << d << ", " << n << ", " << m;
866 } else if ((op3 & 0xB) == 0x8 && Q == 0) { // 101x00, Q == 0
867 opcode << "vdiv" << (S != 0 ? ".f64" : ".f32");
868 args << d << ", " << n << ", " << m;
869 } else if ((op3 & 0xB) == 0xB && Q == 0) { // 101x11, Q == 0
870 uint32_t imm8 = ((instr & 0xf0000u) >> 12) | (instr & 0xfu);
871 opcode << "vmov" << (S != 0 ? ".f64" : ".f32");
872 args << d << ", " << (S != 0 ? StringPrintf("0x%016" PRIx64, VFPExpand64(imm8))
873 : StringPrintf("0x%08x", VFPExpand32(imm8)));
874 if ((instr & 0xa0) != 0) {
875 args << " (UNPREDICTABLE)";
876 }
877 } else if ((op3 & 0xB) == 0xB && Q == 1) { // 101x11, Q == 1
878 // VNEG, VSQRT, VCMP, VCMPE, VCVT (floating-point conversion)
879 // |1111|1100|0|0|00|0000|1111|110|0|0 |0|0|0|0000|
880 // |5 2|1 8|7|6|54|3 0|5 2|1 9|8|7 |6|5|4|3 0|
881 // |----|----|-|-|--|----|----|---|-|- |-|-|-|----|
882 // |3322|2222|2|2|22|1111|1111|110|0|0 |0|0|0|0000|
883 // |1 8|7 4|3|2|10|9 6|5 2|1 9|8|7 |6|5|4|3 0|
884 // |----|----|-|-|--|----|----|---|-|- |-|-|-|----|
885 // |1110|1110|1|D|11|0000| Vd |101|S|0 |1|M|0| Vm | VMOV (reg)
886 // |1110|1110|1|D|11|0000| Vd |101|S|1 |1|M|0| Vm | VABS
887 // |1110|1110|1|D|11|0001| Vd |101|S|0 |1|M|0| Vm | VNEG
888 // |1110|1110|1|D|11|0001| Vd |101|S|1 |1|M|0| Vm | VSQRT
889 // |1110|1110|1|D|11|0100| Vd |101|S|op|1|M|0| Vm | VCMP
890 // |1110|1110|1|D|11|0101| Vd |101|S|op|1|0|0|0000| VCMPE
891 // |1110|1110|1|D|11|op5 | Vd |101|S|op|1|M|0| Vm | VCVT
892 uint32_t op5 = (instr >> 16) & 0xF;
893 uint32_t op = (instr >> 7) & 1;
894 // Register types in VCVT instructions rely on the combination of op5 and S.
895 FpRegister Dd(instr, 12, 22, 1);
896 FpRegister Sd(instr, 12, 22, 0);
897 FpRegister Dm(instr, 0, 5, 1);
898 FpRegister Sm(instr, 0, 5, 0);
899 if (op5 == 0) {
900 opcode << (op == 0 ? "vmov" : "vabs") << (S != 0 ? ".f64" : ".f32");
901 args << d << ", " << m;
902 } else if (op5 == 1) {
903 opcode << (op != 0 ? "vsqrt" : "vneg") << (S != 0 ? ".f64" : ".f32");
904 args << d << ", " << m;
905 } else if (op5 == 4) {
906 opcode << "vcmp" << (S != 0 ? ".f64" : ".f32");
907 args << d << ", " << m;
908 if (op != 0) {
909 args << " (quiet nan)";
910 }
911 } else if (op5 == 5) {
912 opcode << "vcmpe" << (S != 0 ? ".f64" : ".f32");
913 args << d << ", #0.0";
914 if (op != 0) {
915 args << " (quiet nan)";
916 }
917 if ((instr & 0x2f) != 0) {
918 args << " (UNPREDICTABLE)";
919 }
920 } else if (op5 == 0xD) {
921 if (S == 1) {
922 // vcvt{r}.s32.f64
923 opcode << "vcvt" << (op == 0 ? "r" : "") << ".s32.f64";
924 args << Sd << ", " << Dm;
925 } else {
926 // vcvt{r}.s32.f32
927 opcode << "vcvt" << (op == 0 ? "r" : "") << ".s32.f32";
928 args << Sd << ", " << Sm;
929 }
930 } else if (op5 == 0xC) {
931 if (S == 1) {
932 // vcvt{r}.u32.f64
933 opcode << "vcvt" << (op == 0 ? "r" : "") << ".u32.f64";
934 args << Sd << ", " << Dm;
935 } else {
936 // vcvt{r}.u32.f32
937 opcode << "vcvt" << (op == 0 ? "r" : "") << ".u32.f32";
938 args << Sd << ", " << Sm;
939 }
940 } else if (op5 == 0x8) {
941 if (S == 1) {
942 // vcvt.f64.<Tm>
943 opcode << "vcvt.f64." << (op == 0 ? "u" : "s") << "32";
944 args << Dd << ", " << Sm;
945 } else {
946 // vcvt.f32.<Tm>
947 opcode << "vcvt.f32." << (op == 0 ? "u" : "s") << "32";
948 args << Sd << ", " << Sm;
949 }
950 } else if (op5 == 0x7) {
951 if (op == 1) {
Zheng Xue19649a2014-02-27 13:30:55 +0000952 if (S == 1) {
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100953 // vcvt.f64.f32
954 opcode << "vcvt.f64.f32";
Zheng Xue19649a2014-02-27 13:30:55 +0000955 args << Dd << ", " << Sm;
956 } else {
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100957 // vcvt.f32.f64
958 opcode << "vcvt.f32.f64";
959 args << Sd << ", " << Dm;
Zheng Xue19649a2014-02-27 13:30:55 +0000960 }
961 }
Vladimir Markoc777e0d2014-04-03 17:59:02 +0100962 } else if ((op5 & 0xa) == 0xa) {
963 opcode << "vcvt";
964 args << "[undecoded: floating <-> fixed]";
Zheng Xue19649a2014-02-27 13:30:55 +0000965 }
966 }
Dave Allison70202782013-10-22 17:52:19 -0700967 } else if ((op3 >> 4) == 2 && op4 == 1) { // 10xxxx, op = 1
Vladimir Markodd577a32013-11-07 19:25:24 +0000968 if (coproc == 10 && (op3 & 0xE) == 0) {
969 // VMOV (between ARM core register and single-precision register)
970 // |1111|1100|000|0 |0000|1111|1100|0|00|0|0000|
971 // |5 |1 8|7 5|4 |3 0|5 2|1 8|7|65|4|3 0|
972 // |----|----|---|- |----|----|----|-|--|-|----|
973 // |3322|2222|222|2 |1111|1111|1100|0|00|0|0000|
974 // |1 8|7 4|3 1|0 |9 6|5 2|1 8|7|65|4|3 0|
975 // |----|----|---|- |----|----|----|-|--|-|----|
976 // |1110|1110|000|op| Vn | Rt |1010|N|00|1|0000|
977 uint32_t op = op3 & 1;
978 ArmRegister Rt(instr, 12);
979 FpRegister n(instr, 16, 7);
980 opcode << "vmov.f32";
981 if (op) {
982 args << Rt << ", " << n;
983 } else {
984 args << n << ", " << Rt;
985 }
986 if (Rt.r == 13 || Rt.r == 15 || (instr & 0x6F) != 0) {
987 args << " (UNPREDICTABLE)";
988 }
989 } else if (coproc == 10 && op3 == 0x2F) {
990 // VMRS
991 // |1111|11000000|0000|1111|1100|000|0|0000|
992 // |5 |1 4|3 0|5 2|1 8|7 5|4|3 0|
993 // |----|--------|----|----|----|---|-|----|
994 // |3322|22222222|1111|1111|1100|000|0|0000|
995 // |1 8|7 0|9 6|5 2|1 8|7 5|4|3 0|
996 // |----|--------|----|----|----|---|-|----|
997 // |1110|11101111|reg | Rt |1010|000|1|0000| - last 7 0s are (0)
998 uint32_t spec_reg = (instr >> 16) & 0xF;
999 ArmRegister Rt(instr, 12);
1000 opcode << "vmrs";
1001 if (spec_reg == 1) {
1002 if (Rt.r == 15) {
1003 args << "APSR_nzcv, FPSCR";
1004 } else if (Rt.r == 13) {
1005 args << Rt << ", FPSCR (UNPREDICTABLE)";
1006 } else {
1007 args << Rt << ", FPSCR";
1008 }
1009 } else {
1010 args << "(PRIVILEGED)";
1011 }
1012 } else if (coproc == 11 && (op3 & 0x9) != 8) {
1013 // VMOV (ARM core register to scalar or vice versa; 8/16/32-bit)
1014 }
Ian Rogers9af89402012-09-07 11:29:35 -07001015 }
Dave Allison70202782013-10-22 17:52:19 -07001016 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001017 }
1018 break;
Ian Rogers40627db2012-03-04 17:31:09 -08001019 case 2:
1020 if ((instr & 0x8000) == 0 && (op2 & 0x20) == 0) {
1021 // Data-processing (modified immediate)
1022 // |111|11|10|0000|0|0000|1|111|1100|00000000|
1023 // |5 3|21|09|8765|4|3 0|5|4 2|10 8|7 5 0|
1024 // |---|--|--|----|-|----|-|---|----|--------|
1025 // |332|22|22|2222|2|1111|1|111|1100|00000000|
1026 // |1 9|87|65|4321|0|9 6|5|4 2|10 8|7 5 0|
1027 // |---|--|--|----|-|----|-|---|----|--------|
1028 // |111|10|i0| op3|S| Rn |0|iii| Rd |iiiiiiii|
1029 // 111 10 x0 xxxx x xxxx opxxx xxxx xxxxxxxx
Ian Rogers40627db2012-03-04 17:31:09 -08001030 uint32_t i = (instr >> 26) & 1;
1031 uint32_t op3 = (instr >> 21) & 0xF;
1032 uint32_t S = (instr >> 20) & 1;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001033 ArmRegister Rn(instr, 16);
Ian Rogers40627db2012-03-04 17:31:09 -08001034 uint32_t imm3 = (instr >> 12) & 7;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001035 ArmRegister Rd(instr, 8);
Ian Rogers40627db2012-03-04 17:31:09 -08001036 uint32_t imm8 = instr & 0xFF;
Jeff Hao7cb0f9c2013-02-04 16:15:27 -08001037 int32_t imm32 = (i << 11) | (imm3 << 8) | imm8;
1038 if (Rn.r == 0xF && (op3 == 0x2 || op3 == 0x3)) {
1039 if (op3 == 0x2) {
1040 opcode << "mov";
1041 if (S == 1) {
1042 opcode << "s";
1043 }
1044 opcode << ".w";
1045 } else {
1046 opcode << "mvn";
1047 if (S == 1) {
1048 opcode << "s";
1049 }
1050 }
Ian Rogersa9650dd2013-10-04 08:23:32 -07001051 args << Rd << ", #" << ThumbExpand(imm32);
Jeff Hao7cb0f9c2013-02-04 16:15:27 -08001052 } else if (Rd.r == 0xF && S == 1 &&
1053 (op3 == 0x0 || op3 == 0x4 || op3 == 0x8 || op3 == 0xD)) {
1054 if (op3 == 0x0) {
1055 opcode << "tst";
1056 } else if (op3 == 0x4) {
1057 opcode << "teq";
1058 } else if (op3 == 0x8) {
Vladimir Marko22479842013-11-19 17:04:50 +00001059 opcode << "cmn.w";
Jeff Hao7cb0f9c2013-02-04 16:15:27 -08001060 } else {
1061 opcode << "cmp.w";
1062 }
Ian Rogersa9650dd2013-10-04 08:23:32 -07001063 args << Rn << ", #" << ThumbExpand(imm32);
Jeff Hao7cb0f9c2013-02-04 16:15:27 -08001064 } else {
1065 switch (op3) {
1066 case 0x0: opcode << "and"; break;
1067 case 0x1: opcode << "bic"; break;
1068 case 0x2: opcode << "orr"; break;
1069 case 0x3: opcode << "orn"; break;
1070 case 0x4: opcode << "eor"; break;
1071 case 0x8: opcode << "add"; break;
1072 case 0xA: opcode << "adc"; break;
1073 case 0xB: opcode << "sbc"; break;
1074 case 0xD: opcode << "sub"; break;
1075 case 0xE: opcode << "rsb"; break;
1076 default: opcode << "UNKNOWN DPMI-" << op3; break;
1077 }
1078 if (S == 1) {
1079 opcode << "s";
1080 }
Ian Rogersa9650dd2013-10-04 08:23:32 -07001081 args << Rd << ", " << Rn << ", #" << ThumbExpand(imm32);
Ian Rogers40627db2012-03-04 17:31:09 -08001082 }
Ian Rogers40627db2012-03-04 17:31:09 -08001083 } else if ((instr & 0x8000) == 0 && (op2 & 0x20) != 0) {
1084 // Data-processing (plain binary immediate)
1085 // |111|11|10|00000|0000|1|111110000000000|
1086 // |5 3|21|09|87654|3 0|5|4 0 5 0|
1087 // |---|--|--|-----|----|-|---------------|
1088 // |332|22|22|22222|1111|1|111110000000000|
1089 // |1 9|87|65|43210|9 6|5|4 0 5 0|
1090 // |---|--|--|-----|----|-|---------------|
1091 // |111|10|x1| op3 | Rn |0|xxxxxxxxxxxxxxx|
1092 uint32_t op3 = (instr >> 20) & 0x1F;
Ian Rogers40627db2012-03-04 17:31:09 -08001093 switch (op3) {
Ian Rogers55019132013-02-08 01:05:23 -08001094 case 0x00: case 0x0A: {
1095 // ADD/SUB.W Rd, Rn #imm12 - 111 10 i1 0101 0 nnnn 0 iii dddd iiiiiiii
Ian Rogers66a3fca2012-04-09 19:51:34 -07001096 ArmRegister Rd(instr, 8);
1097 ArmRegister Rn(instr, 16);
1098 uint32_t i = (instr >> 26) & 1;
1099 uint32_t imm3 = (instr >> 12) & 0x7;
1100 uint32_t imm8 = instr & 0xFF;
1101 uint32_t imm12 = (i << 11) | (imm3 << 8) | imm8;
1102 if (Rn.r != 0xF) {
Ian Rogers55019132013-02-08 01:05:23 -08001103 opcode << (op3 == 0 ? "addw" : "subw");
Ian Rogers66a3fca2012-04-09 19:51:34 -07001104 args << Rd << ", " << Rn << ", #" << imm12;
1105 } else {
1106 opcode << "adr";
1107 args << Rd << ", ";
Ian Rogers55019132013-02-08 01:05:23 -08001108 DumpBranchTarget(args, instr_ptr + 4, (op3 == 0) ? imm12 : -imm12);
Ian Rogers66a3fca2012-04-09 19:51:34 -07001109 }
1110 break;
1111 }
Ian Rogers55019132013-02-08 01:05:23 -08001112 case 0x04: case 0x0C: {
1113 // MOVW/T Rd, #imm16 - 111 10 i0 0010 0 iiii 0 iii dddd iiiiiiii
Elliott Hughes630e77d2012-03-22 19:20:56 -07001114 ArmRegister Rd(instr, 8);
Ian Rogers40627db2012-03-04 17:31:09 -08001115 uint32_t i = (instr >> 26) & 1;
1116 uint32_t imm3 = (instr >> 12) & 0x7;
1117 uint32_t imm8 = instr & 0xFF;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001118 uint32_t Rn = (instr >> 16) & 0xF;
Ian Rogers40627db2012-03-04 17:31:09 -08001119 uint32_t imm16 = (Rn << 12) | (i << 11) | (imm3 << 8) | imm8;
Ian Rogers55019132013-02-08 01:05:23 -08001120 opcode << (op3 == 0x04 ? "movw" : "movt");
Elliott Hughes630e77d2012-03-22 19:20:56 -07001121 args << Rd << ", #" << imm16;
Ian Rogers40627db2012-03-04 17:31:09 -08001122 break;
1123 }
jeffhaoeae26912013-01-28 16:29:54 -08001124 case 0x16: {
1125 // BFI Rd, Rn, #lsb, #width - 111 10 0 11 011 0 nnnn 0 iii dddd ii 0 iiiii
1126 ArmRegister Rd(instr, 8);
1127 ArmRegister Rn(instr, 16);
1128 uint32_t msb = instr & 0x1F;
1129 uint32_t imm2 = (instr >> 6) & 0x3;
1130 uint32_t imm3 = (instr >> 12) & 0x7;
1131 uint32_t lsb = (imm3 << 2) | imm2;
1132 uint32_t width = msb - lsb + 1;
1133 if (Rn.r != 0xF) {
1134 opcode << "bfi";
1135 args << Rd << ", " << Rn << ", #" << lsb << ", #" << width;
1136 } else {
1137 opcode << "bfc";
1138 args << Rd << ", #" << lsb << ", #" << width;
1139 }
1140 break;
1141 }
Ian Rogers40627db2012-03-04 17:31:09 -08001142 default:
1143 break;
1144 }
1145 } else {
1146 // Branches and miscellaneous control
1147 // |111|11|1000000|0000|1|111|1100|00000000|
1148 // |5 3|21|0987654|3 0|5|4 2|10 8|7 5 0|
1149 // |---|--|-------|----|-|---|----|--------|
1150 // |332|22|2222222|1111|1|111|1100|00000000|
1151 // |1 9|87|6543210|9 6|5|4 2|10 8|7 5 0|
1152 // |---|--|-------|----|-|---|----|--------|
1153 // |111|10| op2 | |1|op3|op4 | |
1154
1155 uint32_t op3 = (instr >> 12) & 7;
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001156 // uint32_t op4 = (instr >> 8) & 0xF;
Ian Rogers40627db2012-03-04 17:31:09 -08001157 switch (op3) {
1158 case 0:
1159 if ((op2 & 0x38) != 0x38) {
1160 // Conditional branch
1161 // |111|11|1|0000|000000|1|1|1 |1|1 |10000000000|
1162 // |5 3|21|0|9876|543 0|5|4|3 |2|1 |0 5 0|
1163 // |---|--|-|----|------|-|-|--|-|--|-----------|
1164 // |332|22|2|2222|221111|1|1|1 |1|1 |10000000000|
1165 // |1 9|87|6|5432|109 6|5|4|3 |2|1 |0 5 0|
1166 // |---|--|-|----|------|-|-|--|-|--|-----------|
1167 // |111|10|S|cond| imm6 |1|0|J1|0|J2| imm11 |
1168 uint32_t S = (instr >> 26) & 1;
1169 uint32_t J2 = (instr >> 11) & 1;
1170 uint32_t J1 = (instr >> 13) & 1;
1171 uint32_t imm6 = (instr >> 16) & 0x3F;
1172 uint32_t imm11 = instr & 0x7FF;
1173 uint32_t cond = (instr >> 22) & 0xF;
1174 int32_t imm32 = (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1);
1175 imm32 = (imm32 << 11) >> 11; // sign extend 21bit immediate
Elliott Hughescbf0b612012-03-15 16:23:47 -07001176 opcode << "b";
1177 DumpCond(opcode, cond);
1178 opcode << ".w";
1179 DumpBranchTarget(args, instr_ptr + 4, imm32);
Ian Rogers9af89402012-09-07 11:29:35 -07001180 } else if (op2 == 0x3B) {
1181 // Miscellaneous control instructions
1182 uint32_t op5 = (instr >> 4) & 0xF;
1183 switch (op5) {
Ian Rogersb122a4b2013-11-19 18:00:50 -08001184 case 4: opcode << "dsb"; DumpMemoryDomain(args, instr & 0xF); break;
1185 case 5: opcode << "dmb"; DumpMemoryDomain(args, instr & 0xF); break;
1186 case 6: opcode << "isb"; DumpMemoryDomain(args, instr & 0xF); break;
Ian Rogers9af89402012-09-07 11:29:35 -07001187 }
Ian Rogers40627db2012-03-04 17:31:09 -08001188 }
1189 break;
1190 case 2:
Ian Rogersd0876a92013-02-08 11:30:38 -08001191 if ((op2 & 0x38) == 0x38) {
1192 if (op2 == 0x7F) {
1193 opcode << "udf";
1194 }
1195 break;
1196 }
1197 // Else deliberate fall-through to B.
1198 case 1: case 3: {
1199 // B
1200 // |111|11|1|0000|000000|11|1 |1|1 |10000000000|
1201 // |5 3|21|0|9876|543 0|54|3 |2|1 |0 5 0|
1202 // |---|--|-|----|------|--|--|-|--|-----------|
1203 // |332|22|2|2222|221111|11|1 |1|1 |10000000000|
1204 // |1 9|87|6|5 2|10 6|54|3 |2|1 |0 5 0|
1205 // |---|--|-|----|------|--|--|-|--|-----------|
1206 // |111|10|S|cond| imm6 |10|J1|0|J2| imm11 |
1207 // |111|10|S| imm10 |10|J1|1|J2| imm11 |
1208 uint32_t S = (instr >> 26) & 1;
1209 uint32_t cond = (instr >> 22) & 0xF;
1210 uint32_t J2 = (instr >> 11) & 1;
1211 uint32_t form = (instr >> 12) & 1;
1212 uint32_t J1 = (instr >> 13) & 1;
1213 uint32_t imm10 = (instr >> 16) & 0x3FF;
1214 uint32_t imm6 = (instr >> 16) & 0x3F;
1215 uint32_t imm11 = instr & 0x7FF;
1216 opcode << "b";
1217 int32_t imm32;
1218 if (form == 0) {
1219 DumpCond(opcode, cond);
1220 imm32 = (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1);
1221 imm32 = (imm32 << 11) >> 11; // sign extend 21 bit immediate.
1222 } else {
1223 uint32_t I1 = ~(J1 ^ S);
1224 uint32_t I2 = ~(J2 ^ S);
1225 imm32 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1);
1226 imm32 = (imm32 << 8) >> 8; // sign extend 24 bit immediate.
1227 }
1228 opcode << ".w";
1229 DumpBranchTarget(args, instr_ptr + 4, imm32);
Ian Rogers40627db2012-03-04 17:31:09 -08001230 break;
Ian Rogersd0876a92013-02-08 11:30:38 -08001231 }
Ian Rogers40627db2012-03-04 17:31:09 -08001232 case 4: case 6: case 5: case 7: {
1233 // BL, BLX (immediate)
1234 // |111|11|1|0000000000|11|1 |1|1 |10000000000|
1235 // |5 3|21|0|9876543 0|54|3 |2|1 |0 5 0|
1236 // |---|--|-|----------|--|--|-|--|-----------|
1237 // |332|22|2|2222221111|11|1 |1|1 |10000000000|
1238 // |1 9|87|6|5 0 6|54|3 |2|1 |0 5 0|
1239 // |---|--|-|----------|--|--|-|--|-----------|
Dave Allisond6ed6422014-04-09 23:36:15 +00001240 // |111|10|S| imm10 |11|J1|L|J2| imm11 |
Ian Rogers40627db2012-03-04 17:31:09 -08001241 uint32_t S = (instr >> 26) & 1;
1242 uint32_t J2 = (instr >> 11) & 1;
Dave Allisond6ed6422014-04-09 23:36:15 +00001243 uint32_t L = (instr >> 12) & 1;
Ian Rogers40627db2012-03-04 17:31:09 -08001244 uint32_t J1 = (instr >> 13) & 1;
1245 uint32_t imm10 = (instr >> 16) & 0x3FF;
1246 uint32_t imm11 = instr & 0x7FF;
Dave Allisond6ed6422014-04-09 23:36:15 +00001247 if (L == 0) {
1248 opcode << "bx";
Dave Allisonf9487c02014-04-08 23:08:12 +00001249 } else {
Dave Allisond6ed6422014-04-09 23:36:15 +00001250 opcode << "blx";
Ian Rogers40627db2012-03-04 17:31:09 -08001251 }
1252 uint32_t I1 = ~(J1 ^ S);
1253 uint32_t I2 = ~(J2 ^ S);
1254 int32_t imm32 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1);
1255 imm32 = (imm32 << 8) >> 8; // sign extend 24 bit immediate.
Elliott Hughescbf0b612012-03-15 16:23:47 -07001256 DumpBranchTarget(args, instr_ptr + 4, imm32);
Ian Rogers40627db2012-03-04 17:31:09 -08001257 break;
1258 }
1259 }
1260 }
1261 break;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001262 case 3:
1263 switch (op2) {
1264 case 0x00: case 0x02: case 0x04: case 0x06: // 000xxx0
1265 case 0x08: case 0x0A: case 0x0C: case 0x0E: {
1266 // Store single data item
Ian Rogers40627db2012-03-04 17:31:09 -08001267 // |111|11|100|000|0|0000|1111|110000|000000|
1268 // |5 3|21|098|765|4|3 0|5 2|10 6|5 0|
1269 // |---|--|---|---|-|----|----|------|------|
1270 // |332|22|222|222|2|1111|1111|110000|000000|
1271 // |1 9|87|654|321|0|9 6|5 2|10 6|5 0|
1272 // |---|--|---|---|-|----|----|------|------|
1273 // |111|11|000|op3|0| | | op4 | |
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001274 uint32_t op3 = (instr >> 21) & 7;
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001275 // uint32_t op4 = (instr >> 6) & 0x3F;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001276 switch (op3) {
Ian Rogers087b2412012-03-21 01:30:32 -07001277 case 0x0: case 0x4: {
1278 // STRB Rt,[Rn,#+/-imm8] - 111 11 00 0 0 00 0 nnnn tttt 1 PUWii ii iiii
1279 // STRB Rt,[Rn,Rm,lsl #imm2] - 111 11 00 0 0 00 0 nnnn tttt 0 00000 ii mmmm
Elliott Hughes630e77d2012-03-22 19:20:56 -07001280 ArmRegister Rn(instr, 16);
1281 ArmRegister Rt(instr, 12);
Ian Rogers087b2412012-03-21 01:30:32 -07001282 opcode << "strb";
1283 if ((instr & 0x800) != 0) {
1284 uint32_t imm8 = instr & 0xFF;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001285 args << Rt << ", [" << Rn << ",#" << imm8 << "]";
Ian Rogers087b2412012-03-21 01:30:32 -07001286 } else {
1287 uint32_t imm2 = (instr >> 4) & 3;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001288 ArmRegister Rm(instr, 0);
1289 args << Rt << ", [" << Rn << ", " << Rm;
Ian Rogers087b2412012-03-21 01:30:32 -07001290 if (imm2 != 0) {
1291 args << ", " << "lsl #" << imm2;
1292 }
1293 args << "]";
1294 }
1295 break;
1296 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001297 case 0x2: case 0x6: {
Elliott Hughes630e77d2012-03-22 19:20:56 -07001298 ArmRegister Rn(instr, 16);
1299 ArmRegister Rt(instr, 12);
Ian Rogers40627db2012-03-04 17:31:09 -08001300 if (op3 == 2) {
Ian Rogers66a3fca2012-04-09 19:51:34 -07001301 if ((instr & 0x800) != 0) {
1302 // STR Rt, [Rn, #imm8] - 111 11 000 010 0 nnnn tttt 1PUWiiiiiiii
1303 uint32_t P = (instr >> 10) & 1;
1304 uint32_t U = (instr >> 9) & 1;
1305 uint32_t W = (instr >> 8) & 1;
1306 uint32_t imm8 = instr & 0xFF;
1307 int32_t imm32 = (imm8 << 24) >> 24; // sign-extend imm8
1308 if (Rn.r == 13 && P == 1 && U == 0 && W == 1 && imm32 == 4) {
1309 opcode << "push";
Dave Allison20dfc792014-06-16 20:44:29 -07001310 args << "{" << Rt << "}";
Ian Rogers66a3fca2012-04-09 19:51:34 -07001311 } else if (Rn.r == 15 || (P == 0 && W == 0)) {
1312 opcode << "UNDEFINED";
Ian Rogers40627db2012-03-04 17:31:09 -08001313 } else {
Ian Rogers66a3fca2012-04-09 19:51:34 -07001314 if (P == 1 && U == 1 && W == 0) {
1315 opcode << "strt";
1316 } else {
1317 opcode << "str";
1318 }
1319 args << Rt << ", [" << Rn;
1320 if (P == 0 && W == 1) {
1321 args << "], #" << imm32;
1322 } else {
1323 args << ", #" << imm32 << "]";
1324 if (W == 1) {
1325 args << "!";
1326 }
Ian Rogers40627db2012-03-04 17:31:09 -08001327 }
1328 }
Ian Rogers66a3fca2012-04-09 19:51:34 -07001329 } else {
1330 // STR Rt, [Rn, Rm, LSL #imm2] - 111 11 000 010 0 nnnn tttt 000000iimmmm
1331 ArmRegister Rn(instr, 16);
1332 ArmRegister Rt(instr, 12);
1333 ArmRegister Rm(instr, 0);
1334 uint32_t imm2 = (instr >> 4) & 3;
1335 opcode << "str.w";
1336 args << Rt << ", [" << Rn << ", " << Rm;
1337 if (imm2 != 0) {
1338 args << ", lsl #" << imm2;
1339 }
1340 args << "]";
Ian Rogers40627db2012-03-04 17:31:09 -08001341 }
1342 } else if (op3 == 6) {
Ian Rogers66a3fca2012-04-09 19:51:34 -07001343 // STR.W Rt, [Rn, #imm12] - 111 11 000 110 0 nnnn tttt iiiiiiiiiiii
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001344 uint32_t imm12 = instr & 0xFFF;
Elliott Hughescbf0b612012-03-15 16:23:47 -07001345 opcode << "str.w";
Elliott Hughes630e77d2012-03-22 19:20:56 -07001346 args << Rt << ", [" << Rn << ", #" << imm12 << "]";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001347 }
Ian Rogers40627db2012-03-04 17:31:09 -08001348 break;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001349 }
1350 }
1351
1352 break;
1353 }
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001354 case 0x03: case 0x0B: case 0x13: case 0x1B: { // 00xx011
jeffhaoeae26912013-01-28 16:29:54 -08001355 // Load halfword
1356 // |111|11|10|0 0|00|0|0000|1111|110000|000000|
1357 // |5 3|21|09|8 7|65|4|3 0|5 2|10 6|5 0|
1358 // |---|--|--|---|--|-|----|----|------|------|
1359 // |332|22|22|2 2|22|2|1111|1111|110000|000000|
1360 // |1 9|87|65|4 3|21|0|9 6|5 2|10 6|5 0|
1361 // |---|--|--|---|--|-|----|----|------|------|
1362 // |111|11|00|op3|01|1| Rn | Rt | op4 | |
1363 // |111|11| op2 | | | imm12 |
1364 uint32_t op3 = (instr >> 23) & 3;
1365 ArmRegister Rn(instr, 16);
1366 ArmRegister Rt(instr, 12);
1367 if (Rt.r != 15) {
1368 if (op3 == 1) {
1369 // LDRH.W Rt, [Rn, #imm12] - 111 11 00 01 011 nnnn tttt iiiiiiiiiiii
1370 uint32_t imm12 = instr & 0xFFF;
1371 opcode << "ldrh.w";
1372 args << Rt << ", [" << Rn << ", #" << imm12 << "]";
1373 if (Rn.r == 9) {
1374 args << " ; ";
Ian Rogersdd7624d2014-03-14 17:43:00 -07001375 Thread::DumpThreadOffset<4>(args, imm12);
jeffhaoeae26912013-01-28 16:29:54 -08001376 } else if (Rn.r == 15) {
1377 intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
1378 lit_adr = RoundDown(lit_adr, 4) + 4 + imm12;
Ian Rogersff093b32014-04-30 19:04:27 -07001379 args << StringPrintf(" ; 0x%08x", *reinterpret_cast<int32_t*>(lit_adr));
jeffhaoeae26912013-01-28 16:29:54 -08001380 }
1381 } else if (op3 == 3) {
1382 // LDRSH.W Rt, [Rn, #imm12] - 111 11 00 11 011 nnnn tttt iiiiiiiiiiii
1383 uint32_t imm12 = instr & 0xFFF;
1384 opcode << "ldrsh.w";
1385 args << Rt << ", [" << Rn << ", #" << imm12 << "]";
1386 if (Rn.r == 9) {
1387 args << " ; ";
Ian Rogersdd7624d2014-03-14 17:43:00 -07001388 Thread::DumpThreadOffset<4>(args, imm12);
jeffhaoeae26912013-01-28 16:29:54 -08001389 } else if (Rn.r == 15) {
1390 intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
1391 lit_adr = RoundDown(lit_adr, 4) + 4 + imm12;
Ian Rogersff093b32014-04-30 19:04:27 -07001392 args << StringPrintf(" ; 0x%08x", *reinterpret_cast<int32_t*>(lit_adr));
jeffhaoeae26912013-01-28 16:29:54 -08001393 }
1394 }
1395 }
1396 break;
1397 }
Vladimir Markoa8b4caf2013-10-24 15:08:57 +01001398 case 0x29: { // 0101001
1399 // |111|11|1000000|0000|1111|1100|00|0 0|0000|
1400 // |5 3|21|0 4|3 0|5 2|1 8|76|5 4|3 0|
1401 // |---|--|-------|----|----|----|--|---|----|
1402 // |332|22|2222222|1111|1111|1100|00|0 0|0000|
1403 // |1 9|87|6 0|9 6|5 2|1 8|76|5 4|3 0|
1404 // |---|--|-------|----|----|----|--|---|----|
1405 // |111|11|0101001| Rm |1111| Rd |11|op3| Rm |
1406 // REV - 111 11 0101001 mmmm 1111 dddd 1000 mmmm
1407 // REV16 - 111 11 0101001 mmmm 1111 dddd 1001 mmmm
1408 // RBIT - 111 11 0101001 mmmm 1111 dddd 1010 mmmm
1409 // REVSH - 111 11 0101001 mmmm 1111 dddd 1011 mmmm
1410 if ((instr & 0xf0c0) == 0xf080) {
1411 uint32_t op3 = (instr >> 4) & 3;
1412 opcode << kThumbReverseOperations[op3];
1413 ArmRegister Rm(instr, 0);
1414 ArmRegister Rd(instr, 8);
1415 args << Rd << ", " << Rm;
1416 ArmRegister Rm2(instr, 16);
1417 if (Rm.r != Rm2.r || Rm.r == 13 || Rm.r == 15 || Rd.r == 13 || Rd.r == 15) {
1418 args << " (UNPREDICTABLE)";
1419 }
Vladimir Marko1f6754d2013-10-28 20:27:17 +00001420 } // else unknown instruction
Vladimir Markoa8b4caf2013-10-24 15:08:57 +01001421 break;
1422 }
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001423 case 0x05: case 0x0D: case 0x15: case 0x1D: { // 00xx101
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001424 // Load word
1425 // |111|11|10|0 0|00|0|0000|1111|110000|000000|
1426 // |5 3|21|09|8 7|65|4|3 0|5 2|10 6|5 0|
1427 // |---|--|--|---|--|-|----|----|------|------|
1428 // |332|22|22|2 2|22|2|1111|1111|110000|000000|
1429 // |1 9|87|65|4 3|21|0|9 6|5 2|10 6|5 0|
1430 // |---|--|--|---|--|-|----|----|------|------|
1431 // |111|11|00|op3|10|1| Rn | Rt | op4 | |
1432 // |111|11| op2 | | | imm12 |
1433 uint32_t op3 = (instr >> 23) & 3;
1434 uint32_t op4 = (instr >> 6) & 0x3F;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001435 ArmRegister Rn(instr, 16);
1436 ArmRegister Rt(instr, 12);
1437 if (op3 == 1 || Rn.r == 15) {
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001438 // LDR.W Rt, [Rn, #imm12] - 111 11 00 00 101 nnnn tttt iiiiiiiiiiii
1439 // LDR.W Rt, [PC, #imm12] - 111 11 00 0x 101 1111 tttt iiiiiiiiiiii
1440 uint32_t imm12 = instr & 0xFFF;
Elliott Hughescbf0b612012-03-15 16:23:47 -07001441 opcode << "ldr.w";
Elliott Hughes630e77d2012-03-22 19:20:56 -07001442 args << Rt << ", [" << Rn << ", #" << imm12 << "]";
Elliott Hughes28fa76d2012-04-09 17:31:46 -07001443 if (Rn.r == 9) {
1444 args << " ; ";
Ian Rogersdd7624d2014-03-14 17:43:00 -07001445 Thread::DumpThreadOffset<4>(args, imm12);
Ian Rogers5b9b1bc2012-04-09 22:51:43 -07001446 } else if (Rn.r == 15) {
1447 intptr_t lit_adr = reinterpret_cast<intptr_t>(instr_ptr);
1448 lit_adr = RoundDown(lit_adr, 4) + 4 + imm12;
Ian Rogersff093b32014-04-30 19:04:27 -07001449 args << StringPrintf(" ; 0x%08x", *reinterpret_cast<int32_t*>(lit_adr));
Elliott Hughes28fa76d2012-04-09 17:31:46 -07001450 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001451 } else if (op4 == 0) {
1452 // LDR.W Rt, [Rn, Rm{, LSL #imm2}] - 111 11 00 00 101 nnnn tttt 000000iimmmm
1453 uint32_t imm2 = (instr >> 4) & 0xF;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001454 ArmRegister rm(instr, 0);
Elliott Hughescbf0b612012-03-15 16:23:47 -07001455 opcode << "ldr.w";
Elliott Hughes630e77d2012-03-22 19:20:56 -07001456 args << Rt << ", [" << Rn << ", " << rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001457 if (imm2 != 0) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001458 args << ", lsl #" << imm2;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001459 }
Elliott Hughescbf0b612012-03-15 16:23:47 -07001460 args << "]";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001461 } else {
Dave Allison20dfc792014-06-16 20:44:29 -07001462 bool p = (instr & (1 << 10)) != 0;
1463 bool w = (instr & (1 << 8)) != 0;
1464 bool u = (instr & (1 << 9)) != 0;
1465 if (p && u && !w) {
1466 // LDRT Rt, [Rn, #imm8] - 111 11 00 00 101 nnnn tttt 1110iiiiiiii
1467 uint32_t imm8 = instr & 0xFF;
1468 opcode << "ldrt";
1469 args << Rt << ", [" << Rn << ", #" << imm8 << "]";
1470 } else if (Rn.r == 13 && !p && u && w && (instr & 0xff) == 4) {
1471 // POP
1472 opcode << "pop";
1473 args << "{" << Rt << "}";
1474 } else {
1475 bool wback = !p || w;
1476 uint32_t offset = (instr & 0xff);
1477 opcode << "ldr.w";
1478 args << Rt << ",";
1479 if (p && !wback) {
1480 args << "[" << Rn << ", #" << offset << "]";
1481 } else if (p && wback) {
1482 args << "[" << Rn << ", #" << offset << "]!";
1483 } else if (!p && wback) {
1484 args << "[" << Rn << "], #" << offset;
1485 } else {
1486 LOG(FATAL) << p << " " << w;
1487 }
1488 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001489 }
1490 break;
1491 }
Dave Allison70202782013-10-22 17:52:19 -07001492 default: // more formats
1493 if ((op2 >> 4) == 2) { // 010xxxx
1494 // data processing (register)
Vladimir Markoc777e0d2014-04-03 17:59:02 +01001495 if ((instr & 0x0080f0f0) == 0x0000f000) {
1496 // LSL, LSR, ASR, ROR
1497 uint32_t shift_op = (instr >> 21) & 3;
1498 uint32_t S = (instr >> 20) & 1;
1499 ArmRegister Rd(instr, 8);
1500 ArmRegister Rn(instr, 16);
1501 ArmRegister Rm(instr, 0);
1502 opcode << kThumb2ShiftOperations[shift_op] << (S != 0 ? "s" : "");
1503 args << Rd << ", " << Rn << ", " << Rm;
1504 }
Dave Allison70202782013-10-22 17:52:19 -07001505 } else if ((op2 >> 3) == 6) { // 0110xxx
1506 // Multiply, multiply accumulate, and absolute difference
1507 op1 = (instr >> 20) & 0x7;
1508 op2 = (instr >> 4) & 0x2;
1509 ArmRegister Ra(instr, 12);
1510 ArmRegister Rn(instr, 16);
1511 ArmRegister Rm(instr, 0);
1512 ArmRegister Rd(instr, 8);
1513 switch (op1) {
1514 case 0:
1515 if (op2 == 0) {
1516 if (Ra.r == 0xf) {
1517 opcode << "mul";
1518 args << Rd << ", " << Rn << ", " << Rm;
1519 } else {
1520 opcode << "mla";
1521 args << Rd << ", " << Rn << ", " << Rm << ", " << Ra;
1522 }
1523 } else {
1524 opcode << "mls";
1525 args << Rd << ", " << Rn << ", " << Rm << ", " << Ra;
1526 }
1527 break;
1528 case 1:
1529 case 2:
1530 case 3:
1531 case 4:
1532 case 5:
1533 case 6:
1534 break; // do these sometime
1535 }
1536 } else if ((op2 >> 3) == 7) { // 0111xxx
1537 // Long multiply, long multiply accumulate, and divide
1538 op1 = (instr >> 20) & 0x7;
1539 op2 = (instr >> 4) & 0xf;
1540 ArmRegister Rn(instr, 16);
1541 ArmRegister Rm(instr, 0);
1542 ArmRegister Rd(instr, 8);
1543 ArmRegister RdHi(instr, 8);
1544 ArmRegister RdLo(instr, 12);
1545 switch (op1) {
1546 case 0:
1547 opcode << "smull";
1548 args << RdLo << ", " << RdHi << ", " << Rn << ", " << Rm;
1549 break;
1550 case 1:
1551 opcode << "sdiv";
1552 args << Rd << ", " << Rn << ", " << Rm;
1553 break;
1554 case 2:
1555 opcode << "umull";
1556 args << RdLo << ", " << RdHi << ", " << Rn << ", " << Rm;
1557 break;
1558 case 3:
1559 opcode << "udiv";
1560 args << Rd << ", " << Rn << ", " << Rm;
1561 break;
1562 case 4:
1563 case 5:
1564 case 6:
1565 break; // TODO: when we generate these...
1566 }
1567 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001568 }
1569 default:
1570 break;
1571 }
Ian Rogers9af89402012-09-07 11:29:35 -07001572
1573 // Apply any IT-block conditions to the opcode if necessary.
1574 if (!it_conditions_.empty()) {
1575 opcode << it_conditions_.back();
1576 it_conditions_.pop_back();
1577 }
1578
Ian Rogers2bcb4a42012-11-08 10:39:18 -08001579 os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n';
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001580 return 4;
Brian Carlstrom1895ea32013-07-18 13:28:37 -07001581} // NOLINT(readability/fn_size)
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001582
1583size_t DisassemblerArm::DumpThumb16(std::ostream& os, const uint8_t* instr_ptr) {
1584 uint16_t instr = ReadU16(instr_ptr);
1585 bool is_32bit = ((instr & 0xF000) == 0xF000) || ((instr & 0xF800) == 0xE800);
1586 if (is_32bit) {
1587 return DumpThumb32(os, instr_ptr);
1588 } else {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001589 std::ostringstream opcode;
1590 std::ostringstream args;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001591 uint16_t opcode1 = instr >> 10;
1592 if (opcode1 < 0x10) {
1593 // shift (immediate), add, subtract, move, and compare
1594 uint16_t opcode2 = instr >> 9;
1595 switch (opcode2) {
1596 case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
1597 case 0x8: case 0x9: case 0xA: case 0xB: {
Sebastien Hertze78500c2013-02-19 14:29:52 +01001598 // Logical shift left - 00 000xx iii mmm ddd
1599 // Logical shift right - 00 001xx iii mmm ddd
1600 // Arithmetic shift right - 00 010xx iii mmm ddd
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001601 uint16_t imm5 = (instr >> 6) & 0x1F;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001602 ThumbRegister rm(instr, 3);
Sebastien Hertze78500c2013-02-19 14:29:52 +01001603 ThumbRegister Rd(instr, 0);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001604 if (opcode2 <= 3) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001605 opcode << "lsls";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001606 } else if (opcode2 <= 7) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001607 opcode << "lsrs";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001608 } else {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001609 opcode << "asrs";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001610 }
Elliott Hughes630e77d2012-03-22 19:20:56 -07001611 args << Rd << ", " << rm << ", #" << imm5;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001612 break;
1613 }
1614 case 0xC: case 0xD: case 0xE: case 0xF: {
1615 // Add register - 00 01100 mmm nnn ddd
1616 // Sub register - 00 01101 mmm nnn ddd
1617 // Add 3-bit immediate - 00 01110 iii nnn ddd
1618 // Sub 3-bit immediate - 00 01111 iii nnn ddd
1619 uint16_t imm3_or_Rm = (instr >> 6) & 7;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001620 ThumbRegister Rn(instr, 3);
1621 ThumbRegister Rd(instr, 0);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001622 if ((opcode2 & 2) != 0 && imm3_or_Rm == 0) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001623 opcode << "mov";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001624 } else {
1625 if ((opcode2 & 1) == 0) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001626 opcode << "adds";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001627 } else {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001628 opcode << "subs";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001629 }
1630 }
Elliott Hughes630e77d2012-03-22 19:20:56 -07001631 args << Rd << ", " << Rn;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001632 if ((opcode2 & 2) == 0) {
Elliott Hughes630e77d2012-03-22 19:20:56 -07001633 ArmRegister Rm(imm3_or_Rm);
1634 args << ", " << Rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001635 } else if (imm3_or_Rm != 0) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001636 args << ", #" << imm3_or_Rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001637 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001638 break;
1639 }
1640 case 0x10: case 0x11: case 0x12: case 0x13:
1641 case 0x14: case 0x15: case 0x16: case 0x17:
1642 case 0x18: case 0x19: case 0x1A: case 0x1B:
1643 case 0x1C: case 0x1D: case 0x1E: case 0x1F: {
1644 // MOVS Rd, #imm8 - 00100 ddd iiiiiiii
1645 // CMP Rn, #imm8 - 00101 nnn iiiiiiii
1646 // ADDS Rn, #imm8 - 00110 nnn iiiiiiii
1647 // SUBS Rn, #imm8 - 00111 nnn iiiiiiii
Elliott Hughes630e77d2012-03-22 19:20:56 -07001648 ThumbRegister Rn(instr, 8);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001649 uint16_t imm8 = instr & 0xFF;
1650 switch (opcode2 >> 2) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001651 case 4: opcode << "movs"; break;
1652 case 5: opcode << "cmp"; break;
1653 case 6: opcode << "adds"; break;
1654 case 7: opcode << "subs"; break;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001655 }
Elliott Hughes630e77d2012-03-22 19:20:56 -07001656 args << Rn << ", #" << imm8;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001657 break;
1658 }
1659 default:
1660 break;
1661 }
Ian Rogersad03ef52012-03-18 19:34:47 -07001662 } else if (opcode1 == 0x10) {
1663 // Data-processing
1664 uint16_t opcode2 = (instr >> 6) & 0xF;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001665 ThumbRegister rm(instr, 3);
1666 ThumbRegister rdn(instr, 0);
Ian Rogersad03ef52012-03-18 19:34:47 -07001667 opcode << kThumbDataProcessingOperations[opcode2];
Elliott Hughes630e77d2012-03-22 19:20:56 -07001668 args << rdn << ", " << rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001669 } else if (opcode1 == 0x11) {
1670 // Special data instructions and branch and exchange
1671 uint16_t opcode2 = (instr >> 6) & 0x0F;
1672 switch (opcode2) {
1673 case 0x0: case 0x1: case 0x2: case 0x3: {
1674 // Add low registers - 010001 0000 xxxxxx
1675 // Add high registers - 010001 0001/001x xxxxxx
1676 uint16_t DN = (instr >> 7) & 1;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001677 ArmRegister rm(instr, 3);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001678 uint16_t Rdn = instr & 7;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001679 ArmRegister DN_Rdn((DN << 3) | Rdn);
Elliott Hughescbf0b612012-03-15 16:23:47 -07001680 opcode << "add";
Elliott Hughes630e77d2012-03-22 19:20:56 -07001681 args << DN_Rdn << ", " << rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001682 break;
1683 }
1684 case 0x8: case 0x9: case 0xA: case 0xB: {
1685 // Move low registers - 010001 1000 xxxxxx
1686 // Move high registers - 010001 1001/101x xxxxxx
1687 uint16_t DN = (instr >> 7) & 1;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001688 ArmRegister rm(instr, 3);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001689 uint16_t Rdn = instr & 7;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001690 ArmRegister DN_Rdn((DN << 3) | Rdn);
Elliott Hughescbf0b612012-03-15 16:23:47 -07001691 opcode << "mov";
Elliott Hughes630e77d2012-03-22 19:20:56 -07001692 args << DN_Rdn << ", " << rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001693 break;
1694 }
1695 case 0x5: case 0x6: case 0x7: {
1696 // Compare high registers - 010001 0101/011x xxxxxx
1697 uint16_t N = (instr >> 7) & 1;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001698 ArmRegister rm(instr, 3);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001699 uint16_t Rn = instr & 7;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001700 ArmRegister N_Rn((N << 3) | Rn);
Elliott Hughescbf0b612012-03-15 16:23:47 -07001701 opcode << "cmp";
Elliott Hughes630e77d2012-03-22 19:20:56 -07001702 args << N_Rn << ", " << rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001703 break;
1704 }
1705 case 0xC: case 0xD: case 0xE: case 0xF: {
1706 // Branch and exchange - 010001 110x xxxxxx
1707 // Branch with link and exchange - 010001 111x xxxxxx
Elliott Hughes630e77d2012-03-22 19:20:56 -07001708 ArmRegister rm(instr, 3);
1709 opcode << ((opcode2 & 0x2) == 0 ? "bx" : "blx");
1710 args << rm;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001711 break;
1712 }
1713 default:
1714 break;
1715 }
jeffhaoeae26912013-01-28 16:29:54 -08001716 } else if (opcode1 == 0x12 || opcode1 == 0x13) { // 01001x
1717 ThumbRegister Rt(instr, 8);
1718 uint16_t imm8 = instr & 0xFF;
1719 opcode << "ldr";
1720 args << Rt << ", [pc, #" << (imm8 << 2) << "]";
Ian Rogersd83bc362012-09-07 17:43:13 -07001721 } else if ((opcode1 >= 0x14 && opcode1 <= 0x17) || // 0101xx
1722 (opcode1 >= 0x18 && opcode1 <= 0x1f) || // 011xxx
1723 (opcode1 >= 0x20 && opcode1 <= 0x27)) { // 100xxx
1724 // Load/store single data item
1725 uint16_t opA = (instr >> 12) & 0xF;
1726 if (opA == 0x5) {
1727 uint16_t opB = (instr >> 9) & 0x7;
1728 ThumbRegister Rm(instr, 6);
1729 ThumbRegister Rn(instr, 3);
1730 ThumbRegister Rt(instr, 0);
Brian Carlstromdf629502013-07-17 22:39:56 -07001731 switch (opB) {
Ian Rogersd83bc362012-09-07 17:43:13 -07001732 case 0: opcode << "str"; break;
1733 case 1: opcode << "strh"; break;
1734 case 2: opcode << "strb"; break;
1735 case 3: opcode << "ldrsb"; break;
1736 case 4: opcode << "ldr"; break;
1737 case 5: opcode << "ldrh"; break;
1738 case 6: opcode << "ldrb"; break;
1739 case 7: opcode << "ldrsh"; break;
1740 }
1741 args << Rt << ", [" << Rn << ", " << Rm << "]";
1742 } else if (opA == 9) {
1743 uint16_t opB = (instr >> 11) & 1;
1744 ThumbRegister Rt(instr, 8);
1745 uint16_t imm8 = instr & 0xFF;
1746 opcode << (opB == 0 ? "str" : "ldr");
Ian Rogers137e88f2012-10-08 17:46:47 -07001747 args << Rt << ", [sp, #" << (imm8 << 2) << "]";
Ian Rogersd83bc362012-09-07 17:43:13 -07001748 } else {
1749 uint16_t imm5 = (instr >> 6) & 0x1F;
1750 uint16_t opB = (instr >> 11) & 1;
1751 ThumbRegister Rn(instr, 3);
1752 ThumbRegister Rt(instr, 0);
Brian Carlstromdf629502013-07-17 22:39:56 -07001753 switch (opA) {
Ian Rogersd83bc362012-09-07 17:43:13 -07001754 case 6:
1755 imm5 <<= 2;
1756 opcode << (opB == 0 ? "str" : "ldr");
1757 break;
1758 case 7:
1759 imm5 <<= 0;
1760 opcode << (opB == 0 ? "strb" : "ldrb");
1761 break;
1762 case 8:
1763 imm5 <<= 1;
1764 opcode << (opB == 0 ? "strh" : "ldrh");
1765 break;
1766 }
1767 args << Rt << ", [" << Rn << ", #" << imm5 << "]";
1768 }
jeffhaoeae26912013-01-28 16:29:54 -08001769 } else if (opcode1 >= 0x34 && opcode1 <= 0x37) { // 1101xx
Ian Rogers7761cb62013-06-17 14:10:46 -07001770 int8_t imm8 = instr & 0xFF;
jeffhaoeae26912013-01-28 16:29:54 -08001771 uint32_t cond = (instr >> 8) & 0xF;
1772 opcode << "b";
1773 DumpCond(opcode, cond);
1774 DumpBranchTarget(args, instr_ptr + 4, (imm8 << 1));
Ian Rogers9af89402012-09-07 11:29:35 -07001775 } else if ((instr & 0xF800) == 0xA800) {
1776 // Generate SP-relative address
1777 ThumbRegister rd(instr, 8);
1778 int imm8 = instr & 0xFF;
1779 opcode << "add";
1780 args << rd << ", sp, #" << (imm8 << 2);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001781 } else if ((instr & 0xF000) == 0xB000) {
1782 // Miscellaneous 16-bit instructions
1783 uint16_t opcode2 = (instr >> 5) & 0x7F;
1784 switch (opcode2) {
1785 case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: {
1786 // Add immediate to SP - 1011 00000 ii iiiii
1787 // Subtract immediate from SP - 1011 00001 ii iiiii
1788 int imm7 = instr & 0x7F;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001789 opcode << ((opcode2 & 4) == 0 ? "add" : "sub");
Elliott Hughescbf0b612012-03-15 16:23:47 -07001790 args << "sp, sp, #" << (imm7 << 2);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001791 break;
1792 }
Ian Rogers087b2412012-03-21 01:30:32 -07001793 case 0x08: case 0x09: case 0x0A: case 0x0B: // 0001xxx
Ian Rogersebbc5772012-04-11 17:00:08 -07001794 case 0x0C: case 0x0D: case 0x0E: case 0x0F:
Ian Rogers55019132013-02-08 01:05:23 -08001795 case 0x18: case 0x19: case 0x1A: case 0x1B: // 0011xxx
1796 case 0x1C: case 0x1D: case 0x1E: case 0x1F:
Ian Rogersebbc5772012-04-11 17:00:08 -07001797 case 0x48: case 0x49: case 0x4A: case 0x4B: // 1001xxx
Ian Rogers55019132013-02-08 01:05:23 -08001798 case 0x4C: case 0x4D: case 0x4E: case 0x4F:
1799 case 0x58: case 0x59: case 0x5A: case 0x5B: // 1011xxx
1800 case 0x5C: case 0x5D: case 0x5E: case 0x5F: {
Ian Rogers087b2412012-03-21 01:30:32 -07001801 // CBNZ, CBZ
1802 uint16_t op = (instr >> 11) & 1;
1803 uint16_t i = (instr >> 9) & 1;
1804 uint16_t imm5 = (instr >> 3) & 0x1F;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001805 ThumbRegister Rn(instr, 0);
Ian Rogers087b2412012-03-21 01:30:32 -07001806 opcode << (op != 0 ? "cbnz" : "cbz");
Ian Rogers828a07f2013-06-18 22:27:34 -07001807 uint32_t imm32 = (i << 6) | (imm5 << 1);
Elliott Hughes630e77d2012-03-22 19:20:56 -07001808 args << Rn << ", ";
Ian Rogers087b2412012-03-21 01:30:32 -07001809 DumpBranchTarget(args, instr_ptr + 4, imm32);
1810 break;
1811 }
Vladimir Markoa8b4caf2013-10-24 15:08:57 +01001812 case 0x50: case 0x51: // 101000x
1813 case 0x52: case 0x53: // 101001x
1814 case 0x56: case 0x57: { // 101011x
1815 uint16_t op = (instr >> 6) & 3;
1816 opcode << kThumbReverseOperations[op];
1817 ThumbRegister Rm(instr, 3);
1818 ThumbRegister Rd(instr, 0);
1819 args << Rd << ", " << Rm;
1820 break;
1821 }
Ian Rogers40627db2012-03-04 17:31:09 -08001822 case 0x78: case 0x79: case 0x7A: case 0x7B: // 1111xxx
1823 case 0x7C: case 0x7D: case 0x7E: case 0x7F: {
1824 // If-Then, and hints
1825 uint16_t opA = (instr >> 4) & 0xF;
1826 uint16_t opB = instr & 0xF;
1827 if (opB == 0) {
1828 switch (opA) {
Elliott Hughescbf0b612012-03-15 16:23:47 -07001829 case 0: opcode << "nop"; break;
1830 case 1: opcode << "yield"; break;
1831 case 2: opcode << "wfe"; break;
1832 case 3: opcode << "sev"; break;
Ian Rogers40627db2012-03-04 17:31:09 -08001833 default: break;
1834 }
1835 } else {
Elliott Hughes105afd22012-04-10 15:04:25 -07001836 uint32_t first_cond = opA;
1837 uint32_t mask = opB;
Elliott Hughescbf0b612012-03-15 16:23:47 -07001838 opcode << "it";
Elliott Hughes105afd22012-04-10 15:04:25 -07001839
1840 // Flesh out the base "it" opcode with the specific collection of 't's and 'e's,
1841 // and store up the actual condition codes we'll want to add to the next few opcodes.
1842 size_t count = 3 - CTZ(mask);
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001843 it_conditions_.resize(count + 2); // Plus the implicit 't', plus the "" for the IT itself.
Elliott Hughes105afd22012-04-10 15:04:25 -07001844 for (size_t i = 0; i < count; ++i) {
1845 bool positive_cond = ((first_cond & 1) != 0);
1846 bool positive_mask = ((mask & (1 << (3 - i))) != 0);
1847 if (positive_mask == positive_cond) {
1848 opcode << 't';
1849 it_conditions_[i] = kConditionCodeNames[first_cond];
1850 } else {
1851 opcode << 'e';
1852 it_conditions_[i] = kConditionCodeNames[first_cond ^ 1];
1853 }
1854 }
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001855 it_conditions_[count] = kConditionCodeNames[first_cond]; // The implicit 't'.
Elliott Hughes105afd22012-04-10 15:04:25 -07001856
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001857 it_conditions_[count + 1] = ""; // No condition code for the IT itself...
1858 DumpCond(args, first_cond); // ...because it's considered an argument.
Ian Rogers40627db2012-03-04 17:31:09 -08001859 }
1860 break;
1861 }
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001862 default:
1863 break;
1864 }
1865 } else if (((instr & 0xF000) == 0x5000) || ((instr & 0xE000) == 0x6000) ||
1866 ((instr & 0xE000) == 0x8000)) {
1867 // Load/store single data item
1868 uint16_t opA = instr >> 12;
Brian Carlstrom7934ac22013-07-26 10:54:15 -07001869 // uint16_t opB = (instr >> 9) & 7;
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001870 switch (opA) {
1871 case 0x6: {
Elliott Hughes28fa76d2012-04-09 17:31:46 -07001872 // STR Rt, [Rn, #imm] - 01100 iiiii nnn ttt
1873 // LDR Rt, [Rn, #imm] - 01101 iiiii nnn ttt
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001874 uint16_t imm5 = (instr >> 6) & 0x1F;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001875 ThumbRegister Rn(instr, 3);
Elliott Hughes28fa76d2012-04-09 17:31:46 -07001876 ThumbRegister Rt(instr, 0);
Elliott Hughes630e77d2012-03-22 19:20:56 -07001877 opcode << ((instr & 0x800) == 0 ? "str" : "ldr");
1878 args << Rt << ", [" << Rn << ", #" << (imm5 << 2) << "]";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001879 break;
1880 }
1881 case 0x9: {
1882 // STR Rt, [SP, #imm] - 01100 ttt iiiiiiii
1883 // LDR Rt, [SP, #imm] - 01101 ttt iiiiiiii
1884 uint16_t imm8 = instr & 0xFF;
Elliott Hughes630e77d2012-03-22 19:20:56 -07001885 ThumbRegister Rt(instr, 8);
1886 opcode << ((instr & 0x800) == 0 ? "str" : "ldr");
1887 args << Rt << ", [sp, #" << (imm8 << 2) << "]";
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001888 break;
1889 }
1890 default:
1891 break;
1892 }
Ian Rogers40627db2012-03-04 17:31:09 -08001893 } else if (opcode1 == 0x38 || opcode1 == 0x39) {
1894 uint16_t imm11 = instr & 0x7FFF;
1895 int32_t imm32 = imm11 << 1;
1896 imm32 = (imm32 << 20) >> 20; // sign extend 12 bit immediate
Elliott Hughescbf0b612012-03-15 16:23:47 -07001897 opcode << "b";
1898 DumpBranchTarget(args, instr_ptr + 4, imm32);
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001899 }
Elliott Hughes105afd22012-04-10 15:04:25 -07001900
1901 // Apply any IT-block conditions to the opcode if necessary.
1902 if (!it_conditions_.empty()) {
1903 opcode << it_conditions_.back();
1904 it_conditions_.pop_back();
1905 }
1906
Ian Rogers2bcb4a42012-11-08 10:39:18 -08001907 os << StringPrintf("%p: %04x \t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n';
Ian Rogers3a5c1ce2012-02-29 10:06:46 -08001908 }
1909 return 2;
1910}
1911
1912} // namespace arm
1913} // namespace art