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

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)