- merged XScale branch back into trunk
[openocd.git] / src / target / arm_disassembler.c
1 /***************************************************************************
2 * Copyright (C) 2006 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "arm_disassembler.h"
25
26 #include "log.h"
27
28 #include <string.h>
29
30 /* textual represenation of the condition field */
31 /* ALways (default) is ommitted (empty string) */
32 char *arm_condition_strings[] =
33 {
34 "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "", "NV"
35 };
36
37 /* make up for C's missing ROR */
38 u32 ror(u32 value, int places)
39 {
40 return (value >> places) | (value << (32 - places));
41 }
42
43 int evaluate_pld(u32 opcode, u32 address, arm_instruction_t *instruction)
44 {
45 /* PLD */
46 if ((opcode & 0x0d70f0000) == 0x0550f000)
47 {
48 instruction->type = ARM_PLD;
49
50 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tPLD ...TODO...", address, opcode);
51
52 return ERROR_OK;
53 }
54 else
55 {
56 instruction->type = ARM_UNDEFINED_INSTRUCTION;
57 return ERROR_OK;
58 }
59
60 ERROR("should never reach this point");
61 return -1;
62 }
63
64 int evaluate_swi(u32 opcode, u32 address, arm_instruction_t *instruction)
65 {
66 instruction->type = ARM_SWI;
67
68 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tSWI 0x%6.6x", address, opcode, (opcode & 0xffffff));
69
70 return ERROR_OK;
71 }
72
73 int evaluate_blx_imm(u32 opcode, u32 address, arm_instruction_t *instruction)
74 {
75 int offset;
76 u32 immediate;
77 u32 target_address;
78
79 instruction->type = ARM_BLX;
80 immediate = opcode & 0x00ffffff;
81
82 /* sign extend 24-bit immediate */
83 if (immediate & 0x00800000)
84 offset = 0xff000000 | immediate;
85 else
86 offset = immediate;
87
88 /* shift two bits left */
89 offset <<= 2;
90
91 /* odd/event halfword */
92 if (opcode & 0x01000000)
93 offset |= 0x2;
94
95 target_address = address + 8 + offset;
96
97 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tBLX 0x%8.8x", address, opcode, target_address);
98
99 instruction->info.b_bl_bx_blx.reg_operand = -1;
100 instruction->info.b_bl_bx_blx.target_address = target_address;
101
102 return ERROR_OK;
103 }
104
105 int evaluate_b_bl(u32 opcode, u32 address, arm_instruction_t *instruction)
106 {
107 u8 L;
108 u32 immediate;
109 int offset;
110 u32 target_address;
111
112 immediate = opcode & 0x00ffffff;
113 L = (opcode & 0x01000000) >> 24;
114
115 /* sign extend 24-bit immediate */
116 if (immediate & 0x00800000)
117 offset = 0xff000000 | immediate;
118 else
119 offset = immediate;
120
121 /* shift two bits left */
122 offset <<= 2;
123
124 target_address = address + 8 + offset;
125
126 if (L)
127 instruction->type = ARM_BL;
128 else
129 instruction->type = ARM_B;
130
131 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tB%s%s 0x%8.8x", address, opcode,
132 (L) ? "L" : "", COND(opcode), target_address);
133
134 instruction->info.b_bl_bx_blx.reg_operand = -1;
135 instruction->info.b_bl_bx_blx.target_address = target_address;
136
137 return ERROR_OK;
138 }
139
140 /* Coprocessor load/store and double register transfers */
141 /* both normal and extended instruction space (condition field b1111) */
142 int evaluate_ldc_stc_mcrr_mrrc(u32 opcode, u32 address, arm_instruction_t *instruction)
143 {
144 u8 cp_num = (opcode & 0xf00) >> 8;
145
146 /* MCRR or MRRC */
147 if (((opcode & 0x0ff00000) == 0x0c400000) || ((opcode & 0x0ff00000) == 0x0c400000))
148 {
149 u8 cp_opcode, Rd, Rn, CRm;
150 char *mnemonic;
151
152 cp_opcode = (opcode & 0xf0) >> 4;
153 Rd = (opcode & 0xf000) >> 12;
154 Rn = (opcode & 0xf0000) >> 16;
155 CRm = (opcode & 0xf);
156
157 /* MCRR */
158 if ((opcode & 0x0ff00000) == 0x0c400000)
159 {
160 instruction->type = ARM_MCRR;
161 mnemonic = "MCRR";
162 }
163
164 /* MRRC */
165 if ((opcode & 0x0ff00000) == 0x0c500000)
166 {
167 instruction->type = ARM_MRRC;
168 mnemonic = "MRRC";
169 }
170
171 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s p%i, %x, r%i, r%i, c%i",
172 address, opcode, mnemonic, COND(opcode), cp_num, cp_opcode, Rd, Rn, CRm);
173 }
174 else /* LDC or STC */
175 {
176 u8 CRd, Rn, offset;
177 u8 U, N;
178 char *mnemonic;
179 char addressing_mode[32];
180
181 CRd = (opcode & 0xf000) >> 12;
182 Rn = (opcode & 0xf0000) >> 16;
183 offset = (opcode & 0xff);
184
185 /* load/store */
186 if (opcode & 0x00100000)
187 {
188 instruction->type = ARM_LDC;
189 mnemonic = "LDC";
190 }
191 else
192 {
193 instruction->type = ARM_STC;
194 mnemonic = "STC";
195 }
196
197 U = (opcode & 0x00800000) >> 23;
198 N = (opcode & 0x00400000) >> 22;
199
200 /* addressing modes */
201 if ((opcode & 0x01200000) == 0x01000000) /* immediate offset */
202 snprintf(addressing_mode, 32, "[r%i, #%s0x%2.2x*4]", Rn, (U) ? "" : "-", offset);
203 else if ((opcode & 0x01200000) == 0x01200000) /* immediate pre-indexed */
204 snprintf(addressing_mode, 32, "[r%i, #%s0x%2.2x*4]!", Rn, (U) ? "" : "-", offset);
205 else if ((opcode & 0x01200000) == 0x00200000) /* immediate post-indexed */
206 snprintf(addressing_mode, 32, "[r%i], #%s0x%2.2x*4", Rn, (U) ? "" : "-", offset);
207 else if ((opcode & 0x01200000) == 0x00000000) /* unindexed */
208 snprintf(addressing_mode, 32, "[r%i], #0x%2.2x", Rn, offset);
209
210 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s p%i, c%i, %s",
211 address, opcode, mnemonic, ((opcode & 0xf0000000) == 0xf0000000) ? COND(opcode) : "2",
212 (N) ? "L" : "",
213 cp_num, CRd, addressing_mode);
214 }
215
216 return ERROR_OK;
217 }
218
219 /* Coprocessor data processing instructions */
220 /* Coprocessor register transfer instructions */
221 /* both normal and extended instruction space (condition field b1111) */
222 int evaluate_cdp_mcr_mrc(u32 opcode, u32 address, arm_instruction_t *instruction)
223 {
224 char* cond;
225 char* mnemonic;
226 u8 cp_num, opcode_1, CRd_Rd, CRn, CRm, opcode_2;
227
228 cond = ((opcode & 0xf0000000) == 0xf0000000) ? "2" : COND(opcode);
229 cp_num = (opcode & 0xf00) >> 8;
230 CRd_Rd = (opcode & 0xf000) >> 12;
231 CRn = (opcode & 0xf0000) >> 16;
232 CRm = (opcode & 0xf);
233 opcode_2 = (opcode & 0xe0) >> 5;
234
235 /* CDP or MRC/MCR */
236 if (opcode & 0x00000010) /* bit 4 set -> MRC/MCR */
237 {
238 if (opcode & 0x00100000) /* bit 20 set -> MRC */
239 {
240 instruction->type = ARM_MRC;
241 mnemonic = "MRC";
242 }
243 else /* bit 20 not set -> MCR */
244 {
245 instruction->type = ARM_MCR;
246 mnemonic = "MCR";
247 }
248
249 opcode_1 = (opcode & 0x00e00000) >> 21;
250
251 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s p%i, 0x%2.2x, r%i, c%i, c%i, 0x%2.2x",
252 address, opcode, mnemonic, cond,
253 cp_num, opcode_1, CRd_Rd, CRn, CRm, opcode_2);
254 }
255 else /* bit 4 not set -> CDP */
256 {
257 instruction->type = ARM_CDP;
258 mnemonic = "CDP";
259
260 opcode_1 = (opcode & 0x00f00000) >> 20;
261
262 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s p%i, 0x%2.2x, c%i, c%i, c%i, 0x%2.2x",
263 address, opcode, mnemonic, cond,
264 cp_num, opcode_1, CRd_Rd, CRn, CRm, opcode_2);
265 }
266
267 return ERROR_OK;
268 }
269
270 /* Load/store instructions */
271 int evaluate_load_store(u32 opcode, u32 address, arm_instruction_t *instruction)
272 {
273 u8 I, P, U, B, W, L;
274 u8 Rn, Rd;
275 char *operation; /* "LDR" or "STR" */
276 char *suffix; /* "", "B", "T", "BT" */
277 char offset[32];
278
279 /* examine flags */
280 I = (opcode & 0x02000000) >> 25;
281 P = (opcode & 0x01000000) >> 24;
282 U = (opcode & 0x00800000) >> 23;
283 B = (opcode & 0x00400000) >> 22;
284 W = (opcode & 0x00200000) >> 21;
285 L = (opcode & 0x00100000) >> 20;
286
287 /* target register */
288 Rd = (opcode & 0xf000) >> 12;
289
290 /* base register */
291 Rn = (opcode & 0xf0000) >> 16;
292
293 instruction->info.load_store.Rd = Rd;
294 instruction->info.load_store.Rn = Rn;
295 instruction->info.load_store.U = U;
296
297 /* determine operation */
298 if (L)
299 operation = "LDR";
300 else
301 operation = "STR";
302
303 /* determine instruction type and suffix */
304 if (B)
305 {
306 if ((P == 0) && (W == 1))
307 {
308 if (L)
309 instruction->type = ARM_LDRBT;
310 else
311 instruction->type = ARM_STRBT;
312 suffix = "BT";
313 }
314 else
315 {
316 if (L)
317 instruction->type = ARM_LDRB;
318 else
319 instruction->type = ARM_STRB;
320 suffix = "B";
321 }
322 }
323 else
324 {
325 if ((P == 0) && (W == 1))
326 {
327 if (L)
328 instruction->type = ARM_LDRT;
329 else
330 instruction->type = ARM_STRT;
331 suffix = "T";
332 }
333 else
334 {
335 if (L)
336 instruction->type = ARM_LDR;
337 else
338 instruction->type = ARM_STR;
339 suffix = "";
340 }
341 }
342
343 if (!I) /* #+-<offset_12> */
344 {
345 u32 offset_12 = (opcode & 0xfff);
346 snprintf(offset, 32, "#%s0x%x", (U) ? "" : "-", offset_12);
347
348 instruction->info.load_store.offset_mode = 0;
349 instruction->info.load_store.offset.offset = offset_12;
350 }
351 else /* either +-<Rm> or +-<Rm>, <shift>, #<shift_imm> */
352 {
353 u8 shift_imm, shift;
354 u8 Rm;
355
356 shift_imm = (opcode & 0xf80) >> 7;
357 shift = (opcode & 0x60) >> 5;
358 Rm = (opcode & 0xf);
359
360 /* LSR encodes a shift by 32 bit as 0x0 */
361 if ((shift == 0x1) && (shift_imm == 0x0))
362 shift_imm = 0x20;
363
364 /* ASR encodes a shift by 32 bit as 0x0 */
365 if ((shift == 0x2) && (shift_imm == 0x0))
366 shift_imm = 0x20;
367
368 /* ROR by 32 bit is actually a RRX */
369 if ((shift == 0x3) && (shift_imm == 0x0))
370 shift = 0x4;
371
372 instruction->info.load_store.offset_mode = 1;
373 instruction->info.load_store.offset.reg.Rm = Rm;
374 instruction->info.load_store.offset.reg.shift = shift;
375 instruction->info.load_store.offset.reg.shift_imm = shift_imm;
376
377 if ((shift_imm == 0x0) && (shift == 0x0)) /* +-<Rm> */
378 {
379 snprintf(offset, 32, "%sr%i", (U) ? "" : "-", Rm);
380 }
381 else /* +-<Rm>, <Shift>, #<shift_imm> */
382 {
383 switch (shift)
384 {
385 case 0x0: /* LSL */
386 snprintf(offset, 32, "%sr%i, LSL #0x%x", (U) ? "" : "-", Rm, shift_imm);
387 break;
388 case 0x1: /* LSR */
389 snprintf(offset, 32, "%sr%i, LSR #0x%x", (U) ? "" : "-", Rm, shift_imm);
390 break;
391 case 0x2: /* ASR */
392 snprintf(offset, 32, "%sr%i, ASR #0x%x", (U) ? "" : "-", Rm, shift_imm);
393 break;
394 case 0x3: /* ROR */
395 snprintf(offset, 32, "%sr%i, ROR #0x%x", (U) ? "" : "-", Rm, shift_imm);
396 break;
397 case 0x4: /* RRX */
398 snprintf(offset, 32, "%sr%i, RRX", (U) ? "" : "-", Rm);
399 break;
400 }
401 }
402 }
403
404 if (P == 1)
405 {
406 if (W == 0) /* offset */
407 {
408 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i, %s]",
409 address, opcode, operation, COND(opcode), suffix,
410 Rd, Rn, offset);
411
412 instruction->info.load_store.index_mode = 0;
413 }
414 else /* pre-indexed */
415 {
416 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i, %s]!",
417 address, opcode, operation, COND(opcode), suffix,
418 Rd, Rn, offset);
419
420 instruction->info.load_store.index_mode = 1;
421 }
422 }
423 else /* post-indexed */
424 {
425 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i], %s",
426 address, opcode, operation, COND(opcode), suffix,
427 Rd, Rn, offset);
428
429 instruction->info.load_store.index_mode = 2;
430 }
431
432 return ERROR_OK;
433 }
434
435 /* Miscellaneous load/store instructions */
436 int evaluate_misc_load_store(u32 opcode, u32 address, arm_instruction_t *instruction)
437 {
438 u8 P, U, I, W, L, S, H;
439 u8 Rn, Rd;
440 char *operation; /* "LDR" or "STR" */
441 char *suffix; /* "H", "SB", "SH", "D" */
442 char offset[32];
443
444 /* examine flags */
445 P = (opcode & 0x01000000) >> 24;
446 U = (opcode & 0x00800000) >> 23;
447 I = (opcode & 0x00400000) >> 22;
448 W = (opcode & 0x00200000) >> 21;
449 L = (opcode & 0x00100000) >> 20;
450 S = (opcode & 0x00000040) >> 6;
451 H = (opcode & 0x00000020) >> 5;
452
453 /* target register */
454 Rd = (opcode & 0xf000) >> 12;
455
456 /* base register */
457 Rn = (opcode & 0xf0000) >> 16;
458
459 instruction->info.load_store.Rd = Rd;
460 instruction->info.load_store.Rn = Rn;
461 instruction->info.load_store.U = U;
462
463 /* determine instruction type and suffix */
464 if (S) /* signed */
465 {
466 if (L) /* load */
467 {
468 if (H)
469 {
470 operation = "LDR";
471 instruction->type = ARM_LDRSH;
472 suffix = "SH";
473 }
474 else
475 {
476 operation = "LDR";
477 instruction->type = ARM_LDRSB;
478 suffix = "SB";
479 }
480 }
481 else /* there are no signed stores, so this is used to encode double-register load/stores */
482 {
483 suffix = "D";
484 if (H)
485 {
486 operation = "STR";
487 instruction->type = ARM_STRD;
488 }
489 else
490 {
491 operation = "LDR";
492 instruction->type = ARM_LDRD;
493 }
494 }
495 }
496 else /* unsigned */
497 {
498 suffix = "H";
499 if (L) /* load */
500 {
501 operation = "LDR";
502 instruction->type = ARM_LDRH;
503 }
504 else /* store */
505 {
506 operation = "STR";
507 instruction->type = ARM_STRH;
508 }
509 }
510
511 if (I) /* Immediate offset/index (#+-<offset_8>)*/
512 {
513 u32 offset_8 = ((opcode & 0xf00) >> 4) | (opcode & 0xf);
514 snprintf(offset, 32, "#%s0x%x", (U) ? "" : "-", offset_8);
515
516 instruction->info.load_store.offset_mode = 0;
517 instruction->info.load_store.offset.offset = offset_8;
518 }
519 else /* Register offset/index (+-<Rm>) */
520 {
521 u8 Rm;
522 Rm = (opcode & 0xf);
523 snprintf(offset, 32, "%sr%i", (U) ? "" : "-", Rm);
524
525 instruction->info.load_store.offset_mode = 1;
526 instruction->info.load_store.offset.reg.Rm = Rm;
527 instruction->info.load_store.offset.reg.shift = 0x0;
528 instruction->info.load_store.offset.reg.shift_imm = 0x0;
529 }
530
531 if (P == 1)
532 {
533 if (W == 0) /* offset */
534 {
535 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i, %s]",
536 address, opcode, operation, COND(opcode), suffix,
537 Rd, Rn, offset);
538
539 instruction->info.load_store.index_mode = 0;
540 }
541 else /* pre-indexed */
542 {
543 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i, %s]!",
544 address, opcode, operation, COND(opcode), suffix,
545 Rd, Rn, offset);
546
547 instruction->info.load_store.index_mode = 1;
548 }
549 }
550 else /* post-indexed */
551 {
552 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, [r%i], %s",
553 address, opcode, operation, COND(opcode), suffix,
554 Rd, Rn, offset);
555
556 instruction->info.load_store.index_mode = 2;
557 }
558
559 return ERROR_OK;
560 }
561
562 /* Load/store multiples instructions */
563 int evaluate_ldm_stm(u32 opcode, u32 address, arm_instruction_t *instruction)
564 {
565 u8 P, U, S, W, L, Rn;
566 u32 register_list;
567 char *addressing_mode;
568 char *mnemonic;
569 char reg_list[69];
570 char *reg_list_p;
571 int i;
572 int first_reg = 1;
573
574 P = (opcode & 0x01000000) >> 24;
575 U = (opcode & 0x00800000) >> 23;
576 S = (opcode & 0x00400000) >> 22;
577 W = (opcode & 0x00200000) >> 21;
578 L = (opcode & 0x00100000) >> 20;
579 register_list = (opcode & 0xffff);
580 Rn = (opcode & 0xf0000) >> 16;
581
582 instruction->info.load_store_multiple.Rn = Rn;
583 instruction->info.load_store_multiple.register_list = register_list;
584 instruction->info.load_store_multiple.S = S;
585 instruction->info.load_store_multiple.W = W;
586
587 if (L)
588 {
589 instruction->type = ARM_LDM;
590 mnemonic = "LDM";
591 }
592 else
593 {
594 instruction->type = ARM_STM;
595 mnemonic = "STM";
596 }
597
598 if (P)
599 {
600 if (U)
601 {
602 instruction->info.load_store_multiple.addressing_mode = 1;
603 addressing_mode = "IB";
604 }
605 else
606 {
607 instruction->info.load_store_multiple.addressing_mode = 3;
608 addressing_mode = "DB";
609 }
610 }
611 else
612 {
613 if (U)
614 {
615 instruction->info.load_store_multiple.addressing_mode = 0;
616 addressing_mode = "IA";
617 }
618 else
619 {
620 instruction->info.load_store_multiple.addressing_mode = 2;
621 addressing_mode = "DA";
622 }
623 }
624
625 reg_list_p = reg_list;
626 for (i = 0; i <= 15; i++)
627 {
628 if ((register_list >> i) & 1)
629 {
630 if (first_reg)
631 {
632 first_reg = 0;
633 reg_list_p += snprintf(reg_list_p, (reg_list + 69 - reg_list_p), "r%i", i);
634 }
635 else
636 {
637 reg_list_p += snprintf(reg_list_p, (reg_list + 69 - reg_list_p), ", r%i", i);
638 }
639 }
640 }
641
642 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i%s, {%s}%s",
643 address, opcode, mnemonic, COND(opcode), addressing_mode,
644 Rn, (W) ? "!" : "", reg_list, (S) ? "^" : "");
645
646 return ERROR_OK;
647 }
648
649 /* Multiplies, extra load/stores */
650 int evaluate_mul_and_extra_ld_st(u32 opcode, u32 address, arm_instruction_t *instruction)
651 {
652 /* Multiply (accumulate) (long) and Swap/swap byte */
653 if ((opcode & 0x000000f0) == 0x00000090)
654 {
655 /* Multiply (accumulate) */
656 if ((opcode & 0x0f800000) == 0x00000000)
657 {
658 u8 Rm, Rs, Rn, Rd, S;
659 Rm = opcode & 0xf;
660 Rs = (opcode & 0xf00) >> 8;
661 Rn = (opcode & 0xf000) >> 12;
662 Rd = (opcode & 0xf0000) >> 16;
663 S = (opcode & 0x00100000) >> 20;
664
665 /* examine A bit (accumulate) */
666 if (opcode & 0x00200000)
667 {
668 instruction->type = ARM_MLA;
669 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tMLA%s%s r%i, r%i, r%i, r%i",
670 address, opcode, COND(opcode), (S) ? "S" : "", Rd, Rm, Rs, Rn);
671 }
672 else
673 {
674 instruction->type = ARM_MUL;
675 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tMUL%s%s r%i, r%i, r%i",
676 address, opcode, COND(opcode), (S) ? "S" : "", Rd, Rm, Rs);
677 }
678
679 return ERROR_OK;
680 }
681
682 /* Multiply (accumulate) long */
683 if ((opcode & 0x0f800000) == 0x00800000)
684 {
685 char* mnemonic = NULL;
686 u8 Rm, Rs, RdHi, RdLow, S;
687 Rm = opcode & 0xf;
688 Rs = (opcode & 0xf00) >> 8;
689 RdHi = (opcode & 0xf000) >> 12;
690 RdLow = (opcode & 0xf0000) >> 16;
691 S = (opcode & 0x00100000) >> 20;
692
693 switch ((opcode & 0x00600000) >> 21)
694 {
695 case 0x0:
696 instruction->type = ARM_UMULL;
697 mnemonic = "UMULL";
698 break;
699 case 0x1:
700 instruction->type = ARM_UMLAL;
701 mnemonic = "UMLAL";
702 break;
703 case 0x2:
704 instruction->type = ARM_SMULL;
705 mnemonic = "SMULL";
706 break;
707 case 0x3:
708 instruction->type = ARM_SMLAL;
709 mnemonic = "SMLAL";
710 break;
711 }
712
713 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, r%i, r%i, r%i",
714 address, opcode, mnemonic, COND(opcode), (S) ? "S" : "",
715 RdLow, RdHi, Rm, Rs);
716
717 return ERROR_OK;
718 }
719
720 /* Swap/swap byte */
721 if ((opcode & 0x0f800000) == 0x01000000)
722 {
723 u8 Rm, Rd, Rn;
724 Rm = opcode & 0xf;
725 Rd = (opcode & 0xf000) >> 12;
726 Rn = (opcode & 0xf0000) >> 16;
727
728 /* examine B flag */
729 instruction->type = (opcode & 0x00400000) ? ARM_SWPB : ARM_SWP;
730
731 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s r%i, r%i, [r%i]",
732 address, opcode, (opcode & 0x00400000) ? "SWPB" : "SWP", COND(opcode), Rd, Rm, Rn);
733 return ERROR_OK;
734 }
735
736 }
737
738 return evaluate_misc_load_store(opcode, address, instruction);
739 }
740
741 int evaluate_mrs_msr(u32 opcode, u32 address, arm_instruction_t *instruction)
742 {
743 int R = (opcode & 0x00400000) >> 22;
744 char *PSR = (R) ? "SPSR" : "CPSR";
745
746 /* Move register to status register (MSR) */
747 if (opcode & 0x00200000)
748 {
749 instruction->type = ARM_MSR;
750
751 /* immediate variant */
752 if (opcode & 0x02000000)
753 {
754 u8 immediate = (opcode & 0xff);
755 u8 rotate = (opcode & 0xf00);
756
757 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tMSR%s %s_%s%s%s%s, 0x%8.8x",
758 address, opcode, COND(opcode), PSR,
759 (opcode & 0x10000) ? "c" : "",
760 (opcode & 0x20000) ? "x" : "",
761 (opcode & 0x40000) ? "s" : "",
762 (opcode & 0x80000) ? "f" : "",
763 ror(immediate, (rotate * 2))
764 );
765 }
766 else /* register variant */
767 {
768 u8 Rm = opcode & 0xf;
769 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tMSR%s %s_%s%s%s%s, r%i",
770 address, opcode, COND(opcode), PSR,
771 (opcode & 0x10000) ? "c" : "",
772 (opcode & 0x20000) ? "x" : "",
773 (opcode & 0x40000) ? "s" : "",
774 (opcode & 0x80000) ? "f" : "",
775 Rm
776 );
777 }
778
779 }
780 else /* Move status register to register (MRS) */
781 {
782 u8 Rd;
783
784 instruction->type = ARM_MRS;
785 Rd = (opcode & 0x0000f000) >> 12;
786
787 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tMRS%s r%i, %s",
788 address, opcode, COND(opcode), Rd, PSR);
789 }
790
791 return ERROR_OK;
792 }
793
794 /* Miscellaneous instructions */
795 int evaluate_misc_instr(u32 opcode, u32 address, arm_instruction_t *instruction)
796 {
797 /* MRS/MSR */
798 if ((opcode & 0x000000f0) == 0x00000000)
799 {
800 evaluate_mrs_msr(opcode, address, instruction);
801 }
802
803 /* BX */
804 if ((opcode & 0x006000f0) == 0x00200010)
805 {
806 u8 Rm;
807 instruction->type = ARM_BX;
808 Rm = opcode & 0xf;
809
810 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tBX%s r%i",
811 address, opcode, COND(opcode), Rm);
812
813 instruction->info.b_bl_bx_blx.reg_operand = Rm;
814 instruction->info.b_bl_bx_blx.target_address = -1;
815 }
816
817 /* CLZ */
818 if ((opcode & 0x0060000f0) == 0x00300010)
819 {
820 u8 Rm, Rd;
821 instruction->type = ARM_CLZ;
822 Rm = opcode & 0xf;
823 Rd = (opcode & 0xf000) >> 12;
824
825 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tCLZ%s r%i, r%i",
826 address, opcode, COND(opcode), Rd, Rm);
827 }
828
829 /* BLX */
830 if ((opcode & 0x0060000f0) == 0x00200030)
831 {
832 u8 Rm;
833 instruction->type = ARM_BLX;
834 Rm = opcode & 0xf;
835
836 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tBLX%s r%i",
837 address, opcode, COND(opcode), Rm);
838
839 instruction->info.b_bl_bx_blx.reg_operand = Rm;
840 instruction->info.b_bl_bx_blx.target_address = -1;
841 }
842
843 /* Enhanced DSP add/subtracts */
844 if ((opcode & 0x0000000f0) == 0x00000050)
845 {
846 u8 Rm, Rd, Rn;
847 char *mnemonic = NULL;
848 Rm = opcode & 0xf;
849 Rd = (opcode & 0xf000) >> 12;
850 Rn = (opcode & 0xf0000) >> 16;
851
852 switch ((opcode & 0x00600000) >> 21)
853 {
854 case 0x0:
855 instruction->type = ARM_QADD;
856 mnemonic = "QADD";
857 break;
858 case 0x1:
859 instruction->type = ARM_QSUB;
860 mnemonic = "QSUB";
861 break;
862 case 0x2:
863 instruction->type = ARM_QDADD;
864 mnemonic = "QDADD";
865 break;
866 case 0x3:
867 instruction->type = ARM_QDSUB;
868 mnemonic = "QDSUB";
869 break;
870 }
871
872 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s r%i, r%i, r%i",
873 address, opcode, mnemonic, COND(opcode), Rd, Rm, Rn);
874 }
875
876 /* Software breakpoints */
877 if ((opcode & 0x0000000f0) == 0x00000070)
878 {
879 u32 immediate;
880 instruction->type = ARM_BKPT;
881 immediate = ((opcode & 0x000fff00) >> 4) | (opcode & 0xf);
882
883 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tBKPT 0x%4.4x",
884 address, opcode, immediate);
885 }
886
887 /* Enhanced DSP multiplies */
888 if ((opcode & 0x000000090) == 0x00000080)
889 {
890 int x = (opcode & 0x20) >> 5;
891 int y = (opcode & 0x40) >> 6;
892
893 /* SMLA<x><y> */
894 if ((opcode & 0x00600000) == 0x00000000)
895 {
896 u8 Rd, Rm, Rs, Rn;
897 instruction->type = ARM_SMLAxy;
898 Rd = (opcode & 0xf0000) >> 16;
899 Rm = (opcode & 0xf);
900 Rs = (opcode & 0xf00) >> 8;
901 Rn = (opcode & 0xf000) >> 12;
902
903 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tSMLA%s%s%s r%i, r%i, r%i, r%i",
904 address, opcode, (x) ? "T" : "B", (y) ? "T" : "B", COND(opcode),
905 Rd, Rm, Rs, Rn);
906 }
907
908 /* SMLAL<x><y> */
909 if ((opcode & 0x00600000) == 0x00400000)
910 {
911 u8 RdLow, RdHi, Rm, Rs;
912 instruction->type = ARM_SMLAxy;
913 RdHi = (opcode & 0xf0000) >> 16;
914 RdLow = (opcode & 0xf000) >> 12;
915 Rm = (opcode & 0xf);
916 Rs = (opcode & 0xf00) >> 8;
917
918 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tSMLA%s%s%s r%i, r%i, r%i, r%i",
919 address, opcode, (x) ? "T" : "B", (y) ? "T" : "B", COND(opcode),
920 RdLow, RdHi, Rm, Rs);
921 }
922
923 /* SMLAW<y> */
924 if (((opcode & 0x00600000) == 0x00100000) && (x == 0))
925 {
926 u8 Rd, Rm, Rs, Rn;
927 instruction->type = ARM_SMLAWy;
928 Rd = (opcode & 0xf0000) >> 16;
929 Rm = (opcode & 0xf);
930 Rs = (opcode & 0xf00) >> 8;
931 Rn = (opcode & 0xf000) >> 12;
932
933 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tSMLAW%s%s r%i, r%i, r%i, r%i",
934 address, opcode, (y) ? "T" : "B", COND(opcode),
935 Rd, Rm, Rs, Rn);
936 }
937
938 /* SMUL<x><y> */
939 if ((opcode & 0x00600000) == 0x00300000)
940 {
941 u8 Rd, Rm, Rs;
942 instruction->type = ARM_SMULxy;
943 Rd = (opcode & 0xf0000) >> 16;
944 Rm = (opcode & 0xf);
945 Rs = (opcode & 0xf00) >> 8;
946
947 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tSMULW%s%s%s r%i, r%i, r%i",
948 address, opcode, (x) ? "T" : "B", (y) ? "T" : "B", COND(opcode),
949 Rd, Rm, Rs);
950 }
951
952 /* SMULW<y> */
953 if (((opcode & 0x00600000) == 0x00100000) && (x == 1))
954 {
955 u8 Rd, Rm, Rs;
956 instruction->type = ARM_SMULWy;
957 Rd = (opcode & 0xf0000) >> 16;
958 Rm = (opcode & 0xf);
959 Rs = (opcode & 0xf00) >> 8;
960
961 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tSMULW%s%s r%i, r%i, r%i",
962 address, opcode, (y) ? "T" : "B", COND(opcode),
963 Rd, Rm, Rs);
964 }
965 }
966
967 return ERROR_OK;
968 }
969
970 int evaluate_data_proc(u32 opcode, u32 address, arm_instruction_t *instruction)
971 {
972 u8 I, op, S, Rn, Rd;
973 char *mnemonic = NULL;
974 char shifter_operand[32];
975
976 I = (opcode & 0x02000000) >> 25;
977 op = (opcode & 0x01e00000) >> 21;
978 S = (opcode & 0x00100000) >> 20;
979
980 Rd = (opcode & 0xf000) >> 12;
981 Rn = (opcode & 0xf0000) >> 16;
982
983 instruction->info.data_proc.Rd = Rd;
984 instruction->info.data_proc.Rn = Rn;
985 instruction->info.data_proc.S = S;
986
987 switch (op)
988 {
989 case 0x0:
990 instruction->type = ARM_AND;
991 mnemonic = "AND";
992 break;
993 case 0x1:
994 instruction->type = ARM_EOR;
995 mnemonic = "EOR";
996 break;
997 case 0x2:
998 instruction->type = ARM_SUB;
999 mnemonic = "SUB";
1000 break;
1001 case 0x3:
1002 instruction->type = ARM_RSB;
1003 mnemonic = "RSB";
1004 break;
1005 case 0x4:
1006 instruction->type = ARM_ADD;
1007 mnemonic = "ADD";
1008 break;
1009 case 0x5:
1010 instruction->type = ARM_ADC;
1011 mnemonic = "ADC";
1012 break;
1013 case 0x6:
1014 instruction->type = ARM_SBC;
1015 mnemonic = "SBC";
1016 break;
1017 case 0x7:
1018 instruction->type = ARM_RSC;
1019 mnemonic = "RSC";
1020 break;
1021 case 0x8:
1022 instruction->type = ARM_TST;
1023 mnemonic = "TST";
1024 break;
1025 case 0x9:
1026 instruction->type = ARM_TEQ;
1027 mnemonic = "TEQ";
1028 break;
1029 case 0xa:
1030 instruction->type = ARM_CMP;
1031 mnemonic = "CMP";
1032 break;
1033 case 0xb:
1034 instruction->type = ARM_CMN;
1035 mnemonic = "CMN";
1036 break;
1037 case 0xc:
1038 instruction->type = ARM_ORR;
1039 mnemonic = "ORR";
1040 break;
1041 case 0xd:
1042 instruction->type = ARM_MOV;
1043 mnemonic = "MOV";
1044 break;
1045 case 0xe:
1046 instruction->type = ARM_BIC;
1047 mnemonic = "BIC";
1048 break;
1049 case 0xf:
1050 instruction->type = ARM_MVN;
1051 mnemonic = "MVN";
1052 break;
1053 }
1054
1055 if (I) /* immediate shifter operand (#<immediate>)*/
1056 {
1057 u8 immed_8 = opcode & 0xff;
1058 u8 rotate_imm = (opcode & 0xf00) >> 8;
1059 u32 immediate;
1060
1061 immediate = ror(immed_8, rotate_imm * 2);
1062
1063 snprintf(shifter_operand, 32, "#0x%x", immediate);
1064
1065 instruction->info.data_proc.variant = 0;
1066 instruction->info.data_proc.shifter_operand.immediate.immediate = immediate;
1067 }
1068 else /* register-based shifter operand */
1069 {
1070 u8 shift, Rm;
1071 shift = (opcode & 0x60) >> 5;
1072 Rm = (opcode & 0xf);
1073
1074 if ((opcode & 0x10) != 0x10) /* Immediate shifts ("<Rm>" or "<Rm>, <shift> #<shift_immediate>") */
1075 {
1076 u8 shift_imm;
1077 shift_imm = (opcode & 0xf80) >> 7;
1078
1079 instruction->info.data_proc.variant = 1;
1080 instruction->info.data_proc.shifter_operand.immediate_shift.Rm = Rm;
1081 instruction->info.data_proc.shifter_operand.immediate_shift.shift_imm = shift_imm;
1082 instruction->info.data_proc.shifter_operand.immediate_shift.shift = shift;
1083
1084 /* LSR encodes a shift by 32 bit as 0x0 */
1085 if ((shift == 0x1) && (shift_imm == 0x0))
1086 shift_imm = 0x20;
1087
1088 /* ASR encodes a shift by 32 bit as 0x0 */
1089 if ((shift == 0x2) && (shift_imm == 0x0))
1090 shift_imm = 0x20;
1091
1092 /* ROR by 32 bit is actually a RRX */
1093 if ((shift == 0x3) && (shift_imm == 0x0))
1094 shift = 0x4;
1095
1096 if ((shift_imm == 0x0) && (shift == 0x0))
1097 {
1098 snprintf(shifter_operand, 32, "r%i", Rm);
1099 }
1100 else
1101 {
1102 if (shift == 0x0) /* LSL */
1103 {
1104 snprintf(shifter_operand, 32, "r%i, LSL #0x%x", Rm, shift_imm);
1105 }
1106 else if (shift == 0x1) /* LSR */
1107 {
1108 snprintf(shifter_operand, 32, "r%i, LSR #0x%x", Rm, shift_imm);
1109 }
1110 else if (shift == 0x2) /* ASR */
1111 {
1112 snprintf(shifter_operand, 32, "r%i, ASR #0x%x", Rm, shift_imm);
1113 }
1114 else if (shift == 0x3) /* ROR */
1115 {
1116 snprintf(shifter_operand, 32, "r%i, ROR #0x%x", Rm, shift_imm);
1117 }
1118 else if (shift == 0x4) /* RRX */
1119 {
1120 snprintf(shifter_operand, 32, "r%i, RRX", Rm);
1121 }
1122 }
1123 }
1124 else /* Register shifts ("<Rm>, <shift> <Rs>") */
1125 {
1126 u8 Rs = (opcode & 0xf00) >> 8;
1127
1128 instruction->info.data_proc.variant = 2;
1129 instruction->info.data_proc.shifter_operand.register_shift.Rm = Rm;
1130 instruction->info.data_proc.shifter_operand.register_shift.Rs = Rs;
1131 instruction->info.data_proc.shifter_operand.register_shift.shift = shift;
1132
1133 if (shift == 0x0) /* LSL */
1134 {
1135 snprintf(shifter_operand, 32, "r%i, LSL r%i", Rm, Rs);
1136 }
1137 else if (shift == 0x1) /* LSR */
1138 {
1139 snprintf(shifter_operand, 32, "r%i, LSR r%i", Rm, Rs);
1140 }
1141 else if (shift == 0x2) /* ASR */
1142 {
1143 snprintf(shifter_operand, 32, "r%i, ASR r%i", Rm, Rs);
1144 }
1145 else if (shift == 0x3) /* ROR */
1146 {
1147 snprintf(shifter_operand, 32, "r%i, ROR r%i", Rm, Rs);
1148 }
1149 }
1150 }
1151
1152 if ((op < 0x8) || (op == 0xc) || (op == 0xe)) /* <opcode3>{<cond>}{S} <Rd>, <Rn>, <shifter_operand> */
1153 {
1154 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, r%i, %s",
1155 address, opcode, mnemonic, COND(opcode),
1156 (S) ? "S" : "", Rd, Rn, shifter_operand);
1157 }
1158 else if ((op == 0xd) || (op == 0xf)) /* <opcode1>{<cond>}{S} <Rd>, <shifter_operand> */
1159 {
1160 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s%s r%i, %s",
1161 address, opcode, mnemonic, COND(opcode),
1162 (S) ? "S" : "", Rd, shifter_operand);
1163 }
1164 else /* <opcode2>{<cond>} <Rn>, <shifter_operand> */
1165 {
1166 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\t%s%s r%i, %s",
1167 address, opcode, mnemonic, COND(opcode),
1168 Rn, shifter_operand);
1169 }
1170
1171 return ERROR_OK;
1172 }
1173
1174 int arm_evaluate_opcode(u32 opcode, u32 address, arm_instruction_t *instruction)
1175 {
1176 /* clear fields, to avoid confusion */
1177 memset(instruction, 0, sizeof(arm_instruction_t));
1178 instruction->opcode = opcode;
1179
1180 /* catch opcodes with condition field [31:28] = b1111 */
1181 if ((opcode & 0xf0000000) == 0xf0000000)
1182 {
1183 /* Undefined instruction (or ARMv5E cache preload PLD) */
1184 if ((opcode & 0x08000000) == 0x00000000)
1185 return evaluate_pld(opcode, address, instruction);
1186
1187 /* Undefined instruction */
1188 if ((opcode & 0x0e000000) == 0x08000000)
1189 {
1190 instruction->type = ARM_UNDEFINED_INSTRUCTION;
1191 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tUNDEFINED INSTRUCTION", address, opcode);
1192 return ERROR_OK;
1193 }
1194
1195 /* Branch and branch with link and change to Thumb */
1196 if ((opcode & 0x0e000000) == 0x0a000000)
1197 return evaluate_blx_imm(opcode, address, instruction);
1198
1199 /* Extended coprocessor opcode space (ARMv5 and higher )*/
1200 /* Coprocessor load/store and double register transfers */
1201 if ((opcode & 0x0e000000) == 0x0c000000)
1202 return evaluate_ldc_stc_mcrr_mrrc(opcode, address, instruction);
1203
1204 /* Coprocessor data processing */
1205 if ((opcode & 0x0f000100) == 0x0c000000)
1206 return evaluate_cdp_mcr_mrc(opcode, address, instruction);
1207
1208 /* Coprocessor register transfers */
1209 if ((opcode & 0x0f000010) == 0x0c000010)
1210 return evaluate_cdp_mcr_mrc(opcode, address, instruction);
1211
1212 /* Undefined instruction */
1213 if ((opcode & 0x0f000000) == 0x0f000000)
1214 {
1215 instruction->type = ARM_UNDEFINED_INSTRUCTION;
1216 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tUNDEFINED INSTRUCTION", address, opcode);
1217 return ERROR_OK;
1218 }
1219 }
1220
1221 /* catch opcodes with [27:25] = b000 */
1222 if ((opcode & 0x0e000000) == 0x00000000)
1223 {
1224 /* Multiplies, extra load/stores */
1225 if ((opcode & 0x00000090) == 0x00000090)
1226 return evaluate_mul_and_extra_ld_st(opcode, address, instruction);
1227
1228 /* Miscellaneous instructions */
1229 if ((opcode & 0x0f900000) == 0x01000000)
1230 return evaluate_misc_instr(opcode, address, instruction);
1231
1232 return evaluate_data_proc(opcode, address, instruction);
1233 }
1234
1235 /* catch opcodes with [27:25] = b001 */
1236 if ((opcode & 0x0e000000) == 0x02000000)
1237 {
1238 /* Undefined instruction */
1239 if ((opcode & 0x0fb00000) == 0x03000000)
1240 {
1241 instruction->type = ARM_UNDEFINED_INSTRUCTION;
1242 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tUNDEFINED INSTRUCTION", address, opcode);
1243 return ERROR_OK;
1244 }
1245
1246 /* Move immediate to status register */
1247 if ((opcode & 0x0fb00000) == 0x03200000)
1248 return evaluate_mrs_msr(opcode, address, instruction);
1249
1250 return evaluate_data_proc(opcode, address, instruction);
1251
1252 }
1253
1254 /* catch opcodes with [27:25] = b010 */
1255 if ((opcode & 0x0e000000) == 0x04000000)
1256 {
1257 /* Load/store immediate offset */
1258 return evaluate_load_store(opcode, address, instruction);
1259 }
1260
1261 /* catch opcodes with [27:25] = b011 */
1262 if ((opcode & 0x0e000000) == 0x06000000)
1263 {
1264 /* Undefined instruction */
1265 if ((opcode & 0x00000010) == 0x00000010)
1266 {
1267 instruction->type = ARM_UNDEFINED_INSTRUCTION;
1268 snprintf(instruction->text, 128, "0x%8.8x\t0x%8.8x\tUNDEFINED INSTRUCTION", address, opcode);
1269 return ERROR_OK;
1270 }
1271
1272 /* Load/store register offset */
1273 return evaluate_load_store(opcode, address, instruction);
1274
1275 }
1276
1277 /* catch opcodes with [27:25] = b100 */
1278 if ((opcode & 0x0e000000) == 0x08000000)
1279 {
1280 /* Load/store multiple */
1281 return evaluate_ldm_stm(opcode, address, instruction);
1282 }
1283
1284 /* catch opcodes with [27:25] = b101 */
1285 if ((opcode & 0x0e000000) == 0x0a000000)
1286 {
1287 /* Branch and branch with link */
1288 return evaluate_b_bl(opcode, address, instruction);
1289 }
1290
1291 /* catch opcodes with [27:25] = b110 */
1292 if ((opcode & 0x0e000000) == 0x0a000000)
1293 {
1294 /* Coprocessor load/store and double register transfers */
1295 return evaluate_ldc_stc_mcrr_mrrc(opcode, address, instruction);
1296 }
1297
1298 /* catch opcodes with [27:25] = b111 */
1299 if ((opcode & 0x0e000000) == 0x0e000000)
1300 {
1301 /* Software interrupt */
1302 if ((opcode & 0x0f000000) == 0x0f000000)
1303 return evaluate_swi(opcode, address, instruction);
1304
1305 /* Coprocessor data processing */
1306 if ((opcode & 0x0f000010) == 0x0e000000)
1307 return evaluate_cdp_mcr_mrc(opcode, address, instruction);
1308
1309 /* Coprocessor register transfers */
1310 if ((opcode & 0x0f000010) == 0x0e000010)
1311 return evaluate_cdp_mcr_mrc(opcode, address, instruction);
1312 }
1313
1314 ERROR("should never reach this point");
1315 return -1;
1316 }
1317

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)