1 /* Disassembler for RISC-V.
2 Copyright (C) 2019 Red Hat, Inc.
3 This file is part of elfutils.
4 Written by Ulrich Drepper <drepper@redhat.com>, 2019.
5
6 This file is free software; you can redistribute it and/or modify
7 it under the terms of either
8
9 * the GNU Lesser General Public License as published by the Free
10 Software Foundation; either version 3 of the License, or (at
11 your option) any later version
12
13 or
14
15 * the GNU General Public License as published by the Free
16 Software Foundation; either version 2 of the License, or (at
17 your option) any later version
18
19 or both in parallel, as here.
20
21 elfutils is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
25
26 You should have received copies of the GNU General Public License and
27 the GNU Lesser General Public License along with this program. If
28 not, see <http://www.gnu.org/licenses/>. */
29
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33
34 #include <assert.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <inttypes.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include "libeblP.h"
43
44 #define MACHINE_ENCODING LITTLE_ENDIAN
45 #include "memory-access.h"
46
47
48 #define ADD_CHAR(ch) \
49 do { \
50 if (unlikely (bufcnt == bufsize)) \
51 goto enomem; \
52 buf[bufcnt++] = (ch); \
53 } while (0)
54
55 #define ADD_STRING(str) \
56 do { \
57 const char *_str0 = (str); \
58 size_t _len0 = strlen (_str0); \
59 ADD_NSTRING (_str0, _len0); \
60 } while (0)
61
62 #define ADD_NSTRING(str, len) \
63 do { \
64 const char *_str = (str); \
65 size_t _len = (len); \
66 if (unlikely (bufcnt + _len > bufsize)) \
67 goto enomem; \
68 memcpy (buf + bufcnt, _str, _len); \
69 bufcnt += _len; \
70 } while (0)
71
72
73 static const char *regnames[32] =
74 {
75 "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
76 "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
77 "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7",
78 "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"
79 };
80 #define REG(nr) ((char *) regnames[nr])
81 #define REGP(nr) REG (8 + (nr))
82
83
84 static const char *fregnames[32] =
85 {
86 "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7",
87 "fs0", "fs1", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5",
88 "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7",
89 "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11"
90 };
91 #define FREG(nr) ((char *) fregnames[nr])
92 #define FREGP(nr) FREG (8 + (nr))
93
94
95 struct known_csrs
96 {
97 uint16_t nr;
98 const char *name;
99 };
100
compare_csr(const void * a,const void * b)101 static int compare_csr (const void *a, const void *b)
102 {
103 const struct known_csrs *ka = (const struct known_csrs *) a;
104 const struct known_csrs *kb = (const struct known_csrs *) b;
105 if (ka->nr < kb->nr)
106 return -1;
107 return ka->nr == kb->nr ? 0 : 1;
108 }
109
110
111 int
riscv_disasm(Ebl * ebl,const uint8_t ** startp,const uint8_t * end,GElf_Addr addr,const char * fmt,DisasmOutputCB_t outcb,DisasmGetSymCB_t symcb,void * outcbarg,void * symcbarg)112 riscv_disasm (Ebl *ebl,
113 const uint8_t **startp, const uint8_t *end, GElf_Addr addr,
114 const char *fmt, DisasmOutputCB_t outcb,
115 DisasmGetSymCB_t symcb __attribute__((unused)),
116 void *outcbarg, void *symcbarg __attribute__((unused)))
117 {
118 const char *const save_fmt = fmt;
119
120 #define BUFSIZE 512
121 char initbuf[BUFSIZE];
122 size_t bufcnt;
123 size_t bufsize = BUFSIZE;
124 char *buf = initbuf;
125
126 int retval = 0;
127 while (1)
128 {
129 const uint8_t *data = *startp;
130 assert (data <= end);
131 if (data + 2 > end)
132 {
133 if (data != end)
134 retval = -1;
135 break;
136 }
137 uint16_t first = read_2ubyte_unaligned (data);
138
139 // Determine length.
140 size_t length;
141 if ((first & 0x3) != 0x3)
142 length = 2;
143 else if ((first & 0x1f) != 0x1f)
144 length = 4;
145 else if ((first & 0x3f) != 0x3f)
146 length = 6;
147 else if ((first & 0x7f) != 0x7f)
148 length = 8;
149 else
150 {
151 uint16_t nnn = (first >> 12) & 0x7;
152 if (nnn != 0x7)
153 length = 10 + 2 * nnn;
154 else
155 // This is invalid as of the RISC-V spec on 2019-06-21.
156 // The instruction is at least 192 bits in size so use
157 // this minimum size.
158 length = 24;
159 }
160 if (data + length > end)
161 {
162 retval = -1;
163 break;
164 }
165
166 char *mne = NULL;
167 /* Max length is 24, which is "illegal", so we print it as
168 "0x<48 hex chars>"
169 See: No instruction encodings defined for these sizes yet, below */
170 char mnebuf[50];
171 char *op[5] = { NULL, NULL, NULL, NULL, NULL };
172 char immbuf[32];
173 size_t len;
174 char *strp = NULL;
175 char addrbuf[32];
176 bufcnt = 0;
177 int64_t opaddr;
178 if (length == 2)
179 {
180 size_t idx = (first >> 13) * 3 + (first & 0x3);
181 switch (idx)
182 {
183 uint16_t rd;
184 uint16_t rs1;
185 uint16_t rs2;
186
187 case 0:
188 if ((first & 0x1fe0) != 0)
189 {
190 mne = "addi";
191 op[0] = REGP ((first & 0x1c) >> 2);
192 op[1] = REG (2);
193 opaddr = (((first >> 1) & 0x3c0)
194 | ((first >> 7) & 0x30)
195 | ((first >> 2) & 0x8)
196 | ((first >> 4) & 0x4));
197 snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64, opaddr);
198 op[2] = addrbuf;
199 }
200 else if (first == 0)
201 mne = "unimp";
202 break;
203 case 1:
204 rs1 = (first >> 7) & 0x1f;
205 int16_t nzimm = ((0 - ((first >> 7) & 0x20))
206 | ((first >> 2) & 0x1f));
207 if (rs1 == 0)
208 mne = nzimm == 0 ? "nop" : "c.nop";
209 else
210 {
211 mne = nzimm == 0 ? "c.addi" : "addi";
212 op[0] = op[1] = REG (rs1);
213 snprintf (addrbuf, sizeof (addrbuf), "%" PRId16, nzimm);
214 op[2] = addrbuf;
215 }
216 break;
217 case 2:
218 rs1 = (first >> 7) & 0x1f;
219 op[0] = op[1] = REG (rs1);
220 opaddr = ((first >> 7) & 0x20) | ((first >> 2) & 0x1f);
221 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
222 op[2] = addrbuf;
223 mne = rs1 == 0 ? "c.slli" : "slli";
224 break;
225 case 3:
226 op[0] = FREGP ((first >> 2) & 0x7);
227 opaddr = ((first << 1) & 0xc0) | ((first >> 7) & 0x38);
228 snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)",
229 opaddr, REGP ((first >> 7) & 0x7));
230 op[1] = addrbuf;
231 mne = "fld";
232 break;
233 case 4:
234 if (ebl->class == ELFCLASS32)
235 {
236 mne = "jal";
237 opaddr = (((first << 3) & 0x20) | ((first >> 2) & 0xe)
238 | ((first << 1) & 0x80) | ((first >> 1) | 0x40)
239 | ((first << 2) & 0x400) | (first & 0xb00)
240 | ((first >> 6) & 0x10));
241 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
242 op[0] = addrbuf;
243 }
244 else
245 {
246 int32_t imm = (((UINT32_C (0) - ((first >> 12) & 0x1)) << 5)
247 | ((first >> 2) & 0x1f));
248 uint16_t reg = (first >> 7) & 0x1f;
249 if (reg == 0)
250 {
251 // Reserved
252 len = snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx16, first);
253 strp = addrbuf;
254 }
255 else
256 {
257 if (imm == 0)
258 mne = "sext.w";
259 else
260 {
261 mne = "addiw";
262 snprintf (addrbuf, sizeof (addrbuf), "%" PRId32, imm);
263 op[2] = addrbuf;
264 }
265 op[0] = op[1] = REG (reg);
266 }
267 }
268 break;
269 case 5:
270 op[0] = FREG ((first >> 7) & 0x1f);
271 opaddr = ((first << 4) & 0x1c0) | ((first >> 7) & 0x20) | ((first >> 2) & 0x18);
272 snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)", opaddr, REG (2));
273 op[1] = addrbuf;
274 mne = "fld";
275 break;
276 case 6:
277 case 18:
278 mne = idx == 6 ? "lw" : "sw";
279 op[0] = REGP ((first >> 2) & 0x7);
280 opaddr = (((first >> 7) & 0x38) | ((first << 1) & 0x40)
281 | ((first >> 4) & 0x4));
282 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
283 opaddr, REGP ((first >> 7) & 0x7));
284 op[1] = addrbuf;
285 break;
286 case 7:
287 mne = (first & 0xf80) == 0 ? "c.li" : "li";
288 op[0] = REG((first >> 7) & 0x1f);
289 snprintf (addrbuf, sizeof (addrbuf), "%" PRId16,
290 (UINT16_C (0) - ((first >> 7) & 0x20)) | ((first >> 2) & 0x1f));
291 op[1] = addrbuf;
292 break;
293 case 8:
294 rd = ((first >> 7) & 0x1f);
295 if (rd == 0)
296 {
297 len = snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx16, first);
298 strp = addrbuf;
299 }
300 else
301 {
302 uint16_t uimm = (((first << 4) & 0xc0)
303 | ((first >> 7) & 0x20)
304 | ((first >> 2) & 0x1c));
305 mne = "lw";
306 op[0] = REG (rd);
307 snprintf (addrbuf, sizeof (addrbuf), "%" PRIu16 "(%s)", uimm, REG (2));
308 op[1] = addrbuf;
309 }
310 break;
311 case 9:
312 if (ebl->class == ELFCLASS32)
313 {
314 mne = "flw";
315 op[0] = FREGP ((first >> 2) & 0x7);
316 opaddr = (((first << 1) & 0x40)
317 | ((first >> 7) & 0x38)
318 | ((first >> 4) & 0x4));
319 }
320 else
321 {
322 mne = "ld";
323 op[0] = REGP ((first >> 2) & 0x7);
324 opaddr = ((first >> 7) & 0x38) | ((first << 1) & 0xc0);
325 }
326 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
327 opaddr, REGP ((first >> 7) & 0x7));
328 op[1] = addrbuf;
329 break;
330 case 10:
331 if ((first & 0xf80) == (2 << 7))
332 {
333 mne = "addi";
334 op[0] = op[1] = REG (2);
335 opaddr = (((first >> 2) & 0x10) | ((first << 3) & 0x20)
336 | ((first << 1) & 0x40) | ((first << 4) & 0x180)
337 | ((UINT64_C (0) - ((first >> 12) & 0x1)) << 9));
338 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64, opaddr);
339 op[2] = addrbuf;
340 }
341 else
342 {
343 mne = "lui";
344 op[0] = REG((first & 0xf80) >> 7);
345 opaddr = (((UINT64_C (0) - ((first >> 12) & 0x1)) & ~0x1f)
346 | ((first >> 2) & 0x1f));
347 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr & 0xfffff);
348 op[1] = addrbuf;
349 }
350 break;
351 case 11:
352 if (ebl->class == ELFCLASS32)
353 {
354 mne = "flw";
355 op[0] = FREG ((first >> 7) & 0x1f);
356 opaddr = (((first << 4) & 0xc0)
357 | ((first >> 7) & 0x20)
358 | ((first >> 2) & 0x1c));
359 }
360 else
361 {
362 mne = "ld";
363 op[0] = REG ((first >> 7) & 0x1f);
364 opaddr = (((first << 4) & 0x1c0)
365 | ((first >> 7) & 0x20)
366 | ((first >> 2) & 0x18));
367 }
368 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (2));
369 op[1] = addrbuf;
370 break;
371 case 13:
372 if ((first & 0xc00) != 0xc00)
373 {
374 int16_t imm = ((first >> 7) & 0x20) | ((first >> 2) & 0x1f);
375 if ((first & 0xc00) == 0x800)
376 {
377 imm |= 0 - (imm & 0x20);
378 mne = "andi";
379 snprintf (addrbuf, sizeof (addrbuf), "%" PRId16, imm);
380 }
381 else
382 {
383 if (ebl->class != ELFCLASS32 || imm < 32)
384 {
385 mne = (first & 0x400) ? "srai" : "srli";
386 if (imm == 0)
387 {
388 strcpy (stpcpy (mnebuf, "c."), mne);
389 mne = mnebuf;
390 }
391 }
392 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx16, imm);
393 }
394 op[2] = addrbuf;
395 }
396 else
397 {
398 op[2] = REGP ((first >> 2) & 0x7);
399 static const char *const arithmne[8] =
400 {
401 "sub", "xor", "or", "and", "subw", "addw", NULL, NULL
402 };
403 mne = (char *) arithmne[((first >> 10) & 0x4) | ((first >> 5) & 0x3)];
404 }
405 op[0] = op[1] = REGP ((first >> 7) & 0x7);
406 break;
407 case 14:
408 rs1 = (first >> 7) & 0x1f;
409 rs2 = (first >> 2) & 0x1f;
410 op[0] = REG (rs1);
411 if ((first & 0x1000) == 0)
412 {
413 if (rs2 == 0)
414 {
415 op[1] = NULL;
416 if (rs1 == 1)
417 {
418 mne = "ret";
419 op[0] = NULL;
420 }
421 else
422 mne = "jr";
423 }
424 else
425 {
426 mne = rs1 != 0 ? "mv" : "c.mv";
427 op[1] = REG (rs2);
428 }
429 }
430 else
431 {
432 if (rs2 == 0)
433 {
434 if (rs1 == 0)
435 {
436 mne = "ebreak";
437 op[0] = op[1] = NULL;
438 }
439 else
440 mne = "jalr";
441 }
442 else
443 {
444 mne = rs1 != 0 ? "add" : "c.add";
445 op[2] = REG (rs2);
446 op[1] = op[0];
447 }
448 }
449 break;
450 case 15:
451 op[0] = FREGP ((first >> 2) & 0x7);
452 opaddr = ((first << 1) & 0xc0) | ((first >> 7) & 0x38);
453 snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)",
454 opaddr, REGP ((first >> 7) & 0x7));
455 op[1] = addrbuf;
456 mne = "fsd";
457 break;
458 case 16:
459 opaddr = (((UINT64_C (0) - ((first >> 12) & 0x1)) << 11)
460 | ((first << 2) & 0x400)
461 | ((first >> 1) & 0x300)
462 | ((first << 1) & 0x80)
463 | ((first >> 1) & 0x40)
464 | ((first << 3) & 0x20)
465 | ((first >> 7) & 0x10)
466 | ((first >> 2) & 0xe));
467 mne = "j";
468 // TODO translate address
469 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, addr + opaddr);
470 op[0] = addrbuf;
471 break;
472 case 17:
473 op[0] = FREG ((first >> 2) & 0x1f);
474 opaddr = ((first >> 1) & 0x1c0) | ((first >> 7) & 0x38);
475 snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)", opaddr, REG (2));
476 op[1] = addrbuf;
477 mne = "fsd";
478 break;
479 case 19:
480 case 22:
481 mne = idx == 19 ? "beqz" : "bnez";
482 op[0] = REG (8 + ((first >> 7) & 0x7));
483 opaddr = addr + (((UINT64_C (0) - ((first >> 12) & 0x1)) & ~0xff)
484 | ((first << 1) & 0xc0) | ((first << 3) & 0x20)
485 | ((first >> 7) & 0x18) | ((first >> 2) & 0x6));
486 // TODO translate address
487 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
488 op[1] = addrbuf;
489 break;
490 case 20:
491 op[0] = REG ((first >> 2) & 0x1f);
492 opaddr = ((first >> 1) & 0xc0) | ((first >> 7) & 0x3c);
493 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (2));
494 op[1] = addrbuf;
495 mne = "sw";
496 break;
497 case 21:
498 if (idx == 18 || ebl->class == ELFCLASS32)
499 {
500 mne = "fsw";
501 op[0] = FREGP ((first >> 2) & 0x7);
502 opaddr = (((first >> 7) & 0x38) | ((first << 1) & 0x40)
503 | ((first >> 4) & 0x4));
504 }
505 else
506 {
507 mne = "sd";
508 op[0] = REGP ((first >> 2) & 0x7);
509 opaddr = ((first >> 7) & 0x38) | ((first << 1) & 0xc0);
510 }
511 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
512 opaddr, REGP ((first >> 7) & 0x7));
513 op[1] = addrbuf;
514 break;
515 case 23:
516 if (idx == 18 || ebl->class == ELFCLASS32)
517 {
518 mne = "fsw";
519 op[0] = FREG ((first & 0x7c) >> 2);
520 opaddr = ((first & 0x1e00) >> 7) | ((first & 0x180) >> 1);
521 }
522 else
523 {
524 mne = "sd";
525 op[0] = REG ((first & 0x7c) >> 2);
526 opaddr = ((first & 0x1c00) >> 7) | ((first & 0x380) >> 1);
527 }
528 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (2));
529 op[1] = addrbuf;
530 break;
531 default:
532 break;
533 }
534
535 if (strp == NULL && mne == NULL)
536 {
537 len = snprintf (immbuf, sizeof (immbuf), "0x%04" PRIx16, first);
538 strp = immbuf;
539 }
540 }
541 else if (length == 4)
542 {
543 uint32_t word = read_4ubyte_unaligned (data);
544 size_t idx = (word >> 2) & 0x1f;
545
546 switch (idx)
547 {
548 static const char widthchar[4] = { 's', 'd', '\0', 'q' };
549 static const char intwidthchar[4] = { 'w', 'd', '\0', 'q' };
550 static const char *const rndmode[8] = { "rne", "rtz", "rdn", "rup", "rmm", "???", "???", "dyn" };
551 uint32_t rd;
552 uint32_t rs1;
553 uint32_t rs2;
554 uint32_t rs3;
555 uint32_t func;
556
557 case 0x00:
558 case 0x01:
559 // LOAD and LOAD-FP
560 rd = (word >> 7) & 0x1f;
561 op[0] = idx == 0x00 ? REG (rd) : FREG (rd);
562 opaddr = ((int32_t) word) >> 20;
563 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
564 opaddr, REG ((word >> 15) & 0x1f));
565 op[1] = addrbuf;
566 func = (word >> 12) & 0x7;
567 static const char *const loadmne[8] =
568 {
569 "lb", "lh", "lw", "ld", "lbu", "lhu", "lwu", NULL
570 };
571 static const char *const floadmne[8] =
572 {
573 NULL, NULL, "flw", "fld", "flq", NULL, NULL, NULL
574 };
575 mne = (char *) (idx == 0x00 ? loadmne[func] : floadmne[func]);
576 break;
577 case 0x03:
578 // MISC-MEM
579 rd = (word >> 7) & 0x1f;
580 rs1 = (word >> 15) & 0x1f;
581 func = (word >> 12) & 0x7;
582
583 if (word == 0x8330000f)
584 mne = "fence.tso";
585 else if (word == 0x0000100f)
586 mne = "fence.i";
587 else if (func == 0 && rd == 0 && rs1 == 0 && (word & 0xf0000000) == 0)
588 {
589 static const char *const order[16] =
590 {
591 "unknown", "w", "r", "rw", "o", "ow", "or", "orw",
592 "i", "iw", "ir", "irw", "io", "iow", "ior", "iorw"
593 };
594 uint32_t pred = (word >> 20) & 0xf;
595 uint32_t succ = (word >> 24) & 0xf;
596 if (pred != 0xf || succ != 0xf)
597 {
598 op[0] = (char *) order[succ];
599 op[1] = (char *) order[pred];
600 }
601 mne = "fence";
602 }
603 break;
604 case 0x04:
605 case 0x06:
606 // OP-IMM and OP-IMM32
607 rd = (word >> 7) & 0x1f;
608 op[0] = REG (rd);
609 rs1 = (word >> 15) & 0x1f;
610 op[1] = REG (rs1);
611 opaddr = ((int32_t) word) >> 20;
612 static const char *const opimmmne[8] =
613 {
614 "addi", NULL, "slti", "sltiu", "xori", NULL, "ori", "andi"
615 };
616 func = (word >> 12) & 0x7;
617 mne = (char *) opimmmne[func];
618 if (mne == NULL)
619 {
620 const uint64_t shiftmask = ebl->class == ELFCLASS32 ? 0x1f : 0x3f;
621 if (func == 0x1 && (opaddr & ~shiftmask) == 0)
622 mne = "slli";
623 else if (func == 0x5 && (opaddr & ~shiftmask) == 0)
624 mne = "srli";
625 else if (func == 0x5 && (opaddr & ~shiftmask) == 0x400)
626 mne = "srai";
627 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr & shiftmask);
628 op[2] = addrbuf;
629 }
630 else if (func == 0x0 && (rd != 0 || idx == 0x06) && rs1 == 0 && rd != 0)
631 {
632 mne = "li";
633 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64, opaddr);
634 op[1] = addrbuf;
635 }
636 else if (func == 0x00 && opaddr == 0)
637 {
638 if (idx == 0x06)
639 mne ="sext.";
640 else if (rd == 0)
641 {
642 mne = "nop";
643 op[0] = op[1] = NULL;
644 }
645 else
646 mne = "mv";
647 }
648 else if (func == 0x3 && opaddr == 1)
649 mne = "seqz";
650 else if (func == 0x4 && opaddr == -1)
651 {
652 mne = "not";
653 op[2] = NULL;
654 }
655 else
656 {
657 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64, opaddr);
658 op[2] = addrbuf;
659
660 if (func == 0x0 && rs1 == 0 && rd != 0)
661 {
662 op[1] = op[2];
663 op[2] = NULL;
664 mne = "li";
665 }
666 }
667 if (mne != NULL && idx == 0x06)
668 {
669 mne = strcpy (mnebuf, mne);
670 strcat (mnebuf, "w");
671 }
672 break;
673 case 0x05:
674 case 0x0d:
675 // LUI and AUIPC
676 mne = idx == 0x05 ? "auipc" : "lui";
677 op[0] = REG ((word >> 7) & 0x1f);
678 opaddr = word >> 12;
679 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
680 op[1] = addrbuf;
681 break;
682 case 0x08:
683 case 0x09:
684 // STORE and STORE-FP
685 rs2 = (word >> 20) & 0x1f;
686 op[0] = idx == 0x08 ? REG (rs2) : FREG (rs2);
687 opaddr = ((((int64_t) ((int32_t) word) >> 20)) & ~0x1f) | ((word >> 7) & 0x1f);
688 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)",
689 opaddr, REG ((word >> 15) & 0x1f));
690 op[1] = addrbuf;
691 func = (word >> 12) & 0x7;
692 static const char *const storemne[8] =
693 {
694 "sb", "sh", "sw", "sd", NULL, NULL, NULL, NULL
695 };
696 static const char *const fstoremne[8] =
697 {
698 NULL, NULL, "fsw", "fsd", "fsq", NULL, NULL, NULL
699 };
700 mne = (char *) (idx == 0x08 ? storemne[func] : fstoremne[func]);
701 break;
702 case 0x0b:
703 // AMO
704 op[0] = REG ((word >> 7) & 0x1f);
705 rs1 = (word >> 15) & 0x1f;
706 rs2 = (word >> 20) & 0x1f;
707 snprintf (addrbuf, sizeof (addrbuf), "(%s)", REG (rs1));
708 op[2] = addrbuf;
709 size_t width = (word >> 12) & 0x7;
710 func = word >> 27;
711 static const char *const amomne[32] =
712 {
713 "amoadd", "amoswap", "lr", "sc", "amoxor", NULL, NULL, NULL,
714 "amoor", NULL, NULL, NULL, "amoand", NULL, NULL, NULL,
715 "amomin", NULL, NULL, NULL, "amomax", NULL, NULL, NULL,
716 "amominu", NULL, NULL, NULL, "amomaxu", NULL, NULL, NULL
717 };
718 if (amomne[func] != NULL && width >= 2 && width <= 3
719 && (func != 0x02 || rs2 == 0))
720 {
721 if (func == 0x02)
722 {
723 op[1] = op[2];
724 op[2] = NULL;
725 }
726 else
727 op[1] = REG (rs2);
728
729 char *cp = stpcpy (mnebuf, amomne[func]);
730 *cp++ = '.';
731 *cp++ = " wd "[width];
732 assert (cp[-1] != ' ');
733 static const char *const aqrlstr[4] =
734 {
735 "", ".rl", ".aq", ".aqrl"
736 };
737 strcpy (cp, aqrlstr[(word >> 25) & 0x3]);
738 mne = mnebuf;
739 }
740 break;
741 case 0x0c:
742 case 0x0e:
743 // OP and OP-32
744 if ((word & 0xbc000000) == 0)
745 {
746 rs1 = (word >> 15) & 0x1f;
747 rs2 = (word >> 20) & 0x1f;
748 op[0] = REG ((word >> 7) & 0x1f);
749 func = ((word >> 21) & 0x10) | ((word >> 27) & 0x8) | ((word >> 12) & 0x7);
750 static const char *const arithmne2[32] =
751 {
752 "add", "sll", "slt", "sltu", "xor", "srl", "or", "and",
753 "sub", NULL, NULL, NULL, NULL, "sra", NULL, NULL,
754 "mul", "mulh", "mulhsu", "mulhu", "div", "divu", "rem", "remu",
755 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
756 };
757 static const char *const arithmne3[32] =
758 {
759 "addw", "sllw", NULL, NULL, NULL, "srlw", NULL, NULL,
760 "subw", NULL, NULL, NULL, NULL, "sraw", NULL, NULL,
761 "mulw", NULL, NULL, NULL, "divw", "divuw", "remw", "remuw",
762 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
763 };
764 if (func == 8 && rs1 == 0)
765 {
766 mne = idx == 0x0c ? "neg" : "negw";
767 op[1] = REG (rs2);
768 }
769 else if (idx == 0x0c && rs2 == 0 && func == 2)
770 {
771 op[1] = REG (rs1);
772 mne = "sltz";
773 }
774 else if (idx == 0x0c && rs1 == 0 && (func == 2 || func == 3))
775 {
776 op[1] = REG (rs2);
777 mne = func == 2 ? "sgtz" : "snez";
778 }
779 else
780 {
781 mne = (char *) (idx == 0x0c ? arithmne2[func] : arithmne3[func]);
782 op[1] = REG (rs1);
783 op[2] = REG (rs2);
784 }
785 }
786 break;
787 case 0x10:
788 case 0x11:
789 case 0x12:
790 case 0x13:
791 // MADD, MSUB, NMSUB, NMADD
792 if ((word & 0x06000000) != 0x04000000)
793 {
794 rd = (word >> 7) & 0x1f;
795 rs1 = (word >> 15) & 0x1f;
796 rs2 = (word >> 20) & 0x1f;
797 rs3 = (word >> 27) & 0x1f;
798 uint32_t rm = (word >> 12) & 0x7;
799 width = (word >> 25) & 0x3;
800
801 static const char *const fmamne[4] =
802 {
803 "fmadd.", "fmsub.", "fnmsub.", "fnmadd."
804 };
805 char *cp = stpcpy (mnebuf, fmamne[idx & 0x3]);
806 *cp++ = widthchar[width];
807 *cp = '\0';
808 mne = mnebuf;
809 op[0] = FREG (rd);
810 op[1] = FREG (rs1);
811 op[2] = FREG (rs2);
812 op[3] = FREG (rs3);
813 if (rm != 0x7)
814 op[4] = (char *) rndmode[rm];
815 }
816 break;
817 case 0x14:
818 // OP-FP
819 if ((word & 0x06000000) != 0x04000000)
820 {
821 width = (word >> 25) & 0x3;
822 rd = (word >> 7) & 0x1f;
823 rs1 = (word >> 15) & 0x1f;
824 rs2 = (word >> 20) & 0x1f;
825 func = word >> 27;
826 uint32_t rm = (word >> 12) & 0x7;
827 if (func < 4)
828 {
829 static const char *const fpop[4] =
830 {
831 "fadd", "fsub", "fmul", "fdiv"
832 };
833 char *cp = stpcpy (mnebuf, fpop[func]);
834 *cp++ = '.';
835 *cp++ = widthchar[width];
836 *cp = '\0';
837 mne = mnebuf;
838 op[0] = FREG (rd);
839 op[1] = FREG (rs1);
840 op[2] = FREG (rs2);
841 if (rm != 0x7)
842 op[3] = (char *) rndmode[rm];
843 }
844 else if (func == 0x1c && width != 2 && rs2 == 0 && rm <= 1)
845 {
846 char *cp;
847 if (rm == 0)
848 {
849 cp = stpcpy (mnebuf, "fmv.x.");
850 *cp++ = intwidthchar[width];
851 }
852 else
853 {
854 cp = stpcpy (mnebuf, "fclass.");
855 *cp++ = widthchar[width];
856 }
857 *cp = '\0';
858 mne = mnebuf;
859 op[0] = REG (rd);
860 op[1] = FREG (rs1);
861 }
862 else if (func == 0x1e && width != 2 && rs2 == 0 && rm == 0)
863 {
864 char *cp = stpcpy (mnebuf, "fmv.");
865 *cp++ = intwidthchar[width];
866 strcpy (cp, ".x");
867 mne = mnebuf;
868 op[0] = FREG (rd);
869 op[1] = REG (rs1);
870 }
871 else if (func == 0x14)
872 {
873 uint32_t cmpop = (word >> 12) & 0x7;
874 if (cmpop < 3)
875 {
876 static const char *const mnefpcmp[3] =
877 {
878 "fle", "flt", "feq"
879 };
880 char *cp = stpcpy (mnebuf, mnefpcmp[cmpop]);
881 *cp++ = '.';
882 *cp++ = widthchar[width];
883 *cp = '\0';
884 mne = mnebuf;
885 op[0] = REG (rd);
886 op[1] = FREG (rs1);
887 op[2] = FREG (rs2);
888 }
889 }
890 else if (func == 0x04)
891 {
892 uint32_t cmpop = (word >> 12) & 0x7;
893 if (cmpop < 3)
894 {
895 op[0] = FREG (rd);
896 op[1] = FREG (rs1);
897
898 static const char *const mnefpcmp[3] =
899 {
900 "fsgnj.", "fsgnjn.", "fsgnjx."
901 };
902 static const char *const altsignmne[3] =
903 {
904 "fmv.", "fneg.", "fabs."
905 };
906 char *cp = stpcpy (mnebuf, rs1 == rs2 ? altsignmne[cmpop] : mnefpcmp[cmpop]);
907 *cp++ = widthchar[width];
908 *cp = '\0';
909 mne = mnebuf;
910
911 if (rs1 != rs2)
912 op[2] = FREG (rs2);
913 }
914 }
915 else if (func == 0x08 && width != 2 && rs2 <= 3 && rs2 != 2 && rs2 != width)
916 {
917 op[0] = FREG (rd);
918 op[1] = FREG (rs1);
919 char *cp = stpcpy (mnebuf, "fcvt.");
920 *cp++ = widthchar[width];
921 *cp++ = '.';
922 *cp++ = widthchar[rs2];
923 *cp = '\0';
924 mne = mnebuf;
925 }
926 else if ((func & 0x1d) == 0x18 && width != 2 && rs2 < 4)
927 {
928 char *cp = stpcpy (mnebuf, "fcvt.");
929 if (func == 0x18)
930 {
931 *cp++ = rs2 >= 2 ? 'l' : 'w';
932 if ((rs2 & 1) == 1)
933 *cp++ = 'u';
934 *cp++ = '.';
935 *cp++ = widthchar[width];
936 *cp = '\0';
937 op[0] = REG (rd);
938 op[1] = FREG (rs1);
939 }
940 else
941 {
942 *cp++ = widthchar[width];
943 *cp++ = '.';
944 *cp++ = rs2 >= 2 ? 'l' : 'w';
945 if ((rs2 & 1) == 1)
946 *cp++ = 'u';
947 *cp = '\0';
948 op[0] = FREG (rd);
949 op[1] = REG (rs1);
950 }
951 mne = mnebuf;
952 if (rm != 0x7 && (func == 0x18 || width == 0 || rs2 >= 2))
953 op[2] = (char *) rndmode[rm];
954 }
955 else if (func == 0x0b && rs2 == 0)
956 {
957 op[0] = FREG (rd);
958 op[1] = FREG (rs1);
959 char *cp = stpcpy (mnebuf, "fsqrt.");
960 *cp++ = widthchar[width];
961 *cp = '\0';
962 mne = mnebuf;
963 if (rm != 0x7)
964 op[2] = (char *) rndmode[rm];
965 }
966 else if (func == 0x05 && rm < 2)
967 {
968 op[0] = FREG (rd);
969 op[1] = FREG (rs1);
970 op[2] = FREG (rs2);
971 char *cp = stpcpy (mnebuf, rm == 0 ? "fmin." : "fmax.");
972 *cp++ = widthchar[width];
973 *cp = '\0';
974 mne = mnebuf;
975 }
976 else if (func == 0x14 && rm <= 0x2)
977 {
978 op[0] = REG (rd);
979 op[1] = FREG (rs1);
980 op[2] = FREG (rs2);
981 static const char *const fltcmpmne[3] =
982 {
983 "fle.", "flt.", "feq."
984 };
985 char *cp = stpcpy (mnebuf, fltcmpmne[rm]);
986 *cp++ = widthchar[width];
987 *cp = '\0';
988 mne = mnebuf;
989 }
990 }
991 break;
992 case 0x18:
993 // BRANCH
994 rs1 = (word >> 15) & 0x1f;
995 op[0] = REG (rs1);
996 rs2 = (word >> 20) & 0x1f;
997 op[1] = REG (rs2);
998 opaddr = addr + (((UINT64_C (0) - (word >> 31)) << 12)
999 + ((word << 4) & 0x800)
1000 + ((word >> 20) & 0x7e0)
1001 + ((word >> 7) & 0x1e));
1002 // TODO translate address
1003 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
1004 op[2] = addrbuf;
1005 static const char *const branchmne[8] =
1006 {
1007 "beq", "bne", NULL, NULL, "blt", "bge", "bltu", "bgeu"
1008 };
1009 func = (word >> 12) & 0x7;
1010 mne = (char *) branchmne[func];
1011 if (rs1 == 0 && func == 5)
1012 {
1013 op[0] = op[1];
1014 op[1] = op[2];
1015 op[2] = NULL;
1016 mne = "blez";
1017 }
1018 else if (rs1 == 0 && func == 4)
1019 {
1020 op[0] = op[1];
1021 op[1] = op[2];
1022 op[2] = NULL;
1023 mne = "bgtz";
1024 }
1025 else if (rs2 == 0)
1026 {
1027 if (func == 0 || func == 1 || func == 4 || func == 5)
1028 {
1029 op[1] = op[2];
1030 op[2] = NULL;
1031 strcpy (stpcpy (mnebuf, mne), "z");
1032 mne = mnebuf;
1033 }
1034 }
1035 else if (func == 5 || func == 7)
1036 {
1037 // binutils use these opcodes and the reverse parameter order
1038 char *tmp = op[0];
1039 op[0] = op[1];
1040 op[1] = tmp;
1041 mne = func == 5 ? "ble" : "bleu";
1042 }
1043 break;
1044 case 0x19:
1045 // JALR
1046 if ((word & 0x7000) == 0)
1047 {
1048 rd = (word >> 7) & 0x1f;
1049 rs1 = (word >> 15) & 0x1f;
1050 opaddr = (int32_t) word >> 20;
1051 size_t next = 0;
1052 if (rd > 1)
1053 op[next++] = REG (rd);
1054 if (opaddr == 0)
1055 {
1056 if (rs1 != 0 || next == 0)
1057 op[next] = REG (rs1);
1058 }
1059 else
1060 {
1061 snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (rs1));
1062 op[next] = addrbuf;
1063 }
1064 mne = rd == 0 ? "jr" : "jalr";
1065 }
1066 break;
1067 case 0x1b:
1068 // JAL
1069 rd = (word >> 7) & 0x1f;
1070 if (rd != 0)
1071 op[0] = REG (rd);
1072 opaddr = addr + ((UINT64_C (0) - ((word >> 11) & 0x100000))
1073 | (word & 0xff000)
1074 | ((word >> 9) & 0x800)
1075 | ((word >> 20) & 0x7fe));
1076 // TODO translate address
1077 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
1078 op[rd != 0] = addrbuf;
1079 mne = rd == 0 ? "j" : "jal";
1080 break;
1081 case 0x1c:
1082 // SYSTEM
1083 rd = (word >> 7) & 0x1f;
1084 rs1 = (word >> 15) & 0x1f;
1085 if (word == 0x00000073)
1086 mne = "ecall";
1087 else if (word == 0x00100073)
1088 mne = "ebreak";
1089 else if (word == 0x00200073)
1090 mne = "uret";
1091 else if (word == 0x10200073)
1092 mne = "sret";
1093 else if (word == 0x30200073)
1094 mne = "mret";
1095 else if (word == 0x10500073)
1096 mne = "wfi";
1097 else if ((word & 0x3000) == 0x2000 && rs1 == 0)
1098 {
1099 uint32_t csr = word >> 20;
1100 if (/* csr >= 0x000 && */ csr <= 0x007)
1101 {
1102 static const char *const unprivrw[4] =
1103 {
1104 NULL, "frflags", "frrm", "frsr",
1105 };
1106 mne = (char *) unprivrw[csr - 0x000];
1107 }
1108 else if (csr >= 0xc00 && csr <= 0xc03)
1109 {
1110 static const char *const unprivrolow[3] =
1111 {
1112 "rdcycle", "rdtime", "rdinstret"
1113 };
1114 mne = (char *) unprivrolow[csr - 0xc00];
1115 }
1116 op[0] = REG ((word >> 7) & 0x1f);
1117 }
1118 else if ((word & 0x3000) == 0x1000 && rd == 0)
1119 {
1120 uint32_t csr = word >> 20;
1121 if (/* csr >= 0x000 && */ csr <= 0x003)
1122 {
1123 static const char *const unprivrs[4] =
1124 {
1125 NULL, "fsflags", "fsrm", "fssr",
1126 };
1127 static const char *const unprivrsi[4] =
1128 {
1129 NULL, "fsflagsi", "fsrmi", NULL
1130 };
1131 mne = (char *) ((word & 0x4000) == 0 ? unprivrs : unprivrsi)[csr - 0x000];
1132
1133 if ((word & 0x4000) == 0)
1134 op[0] = REG ((word >> 15) & 0x1f);
1135 else
1136 {
1137 snprintf (immbuf, sizeof (immbuf), "%" PRIu32, (word >> 15) & 0x1f);
1138 op[0] = immbuf;
1139 }
1140 }
1141 }
1142 if (mne == NULL && (word & 0x3000) != 0)
1143 {
1144 static const char *const mnecsr[8] =
1145 {
1146 NULL, "csrrw", "csrrs", "csrrc",
1147 NULL, "csrrwi", "csrrsi", "csrrci"
1148 };
1149 static const struct known_csrs known[] =
1150 {
1151 // This list must remain sorted by NR.
1152 { 0x000, "ustatus" },
1153 { 0x001, "fflags" },
1154 { 0x002, "fram" },
1155 { 0x003, "fcsr" },
1156 { 0x004, "uie" },
1157 { 0x005, "utvec" },
1158 { 0x040, "uscratch" },
1159 { 0x041, "uepc" },
1160 { 0x042, "ucause" },
1161 { 0x043, "utval" },
1162 { 0x044, "uip" },
1163 { 0x100, "sstatus" },
1164 { 0x102, "sedeleg" },
1165 { 0x103, "sideleg" },
1166 { 0x104, "sie" },
1167 { 0x105, "stvec" },
1168 { 0x106, "scounteren" },
1169 { 0x140, "sscratch" },
1170 { 0x141, "sepc" },
1171 { 0x142, "scause" },
1172 { 0x143, "stval" },
1173 { 0x144, "sip" },
1174 { 0x180, "satp" },
1175 { 0x200, "vsstatus" },
1176 { 0x204, "vsie" },
1177 { 0x205, "vstvec" },
1178 { 0x240, "vsscratch" },
1179 { 0x241, "vsepc" },
1180 { 0x242, "vscause" },
1181 { 0x243, "vstval" },
1182 { 0x244, "vsip" },
1183 { 0x280, "vsatp" },
1184 { 0x600, "hstatus" },
1185 { 0x602, "hedeleg" },
1186 { 0x603, "hideleg" },
1187 { 0x605, "htimedelta" },
1188 { 0x606, "hcounteren" },
1189 { 0x615, "htimedeltah" },
1190 { 0x680, "hgatp" },
1191 { 0xc00, "cycle" },
1192 { 0xc01, "time" },
1193 { 0xc02, "instret" },
1194 { 0xc03, "hpmcounter3" },
1195 { 0xc04, "hpmcounter4" },
1196 { 0xc05, "hpmcounter5" },
1197 { 0xc06, "hpmcounter6" },
1198 { 0xc07, "hpmcounter7" },
1199 { 0xc08, "hpmcounter8" },
1200 { 0xc09, "hpmcounter9" },
1201 { 0xc0a, "hpmcounter10" },
1202 { 0xc0b, "hpmcounter11" },
1203 { 0xc0c, "hpmcounter12" },
1204 { 0xc0d, "hpmcounter13" },
1205 { 0xc0e, "hpmcounter14" },
1206 { 0xc0f, "hpmcounter15" },
1207 { 0xc10, "hpmcounter16" },
1208 { 0xc11, "hpmcounter17" },
1209 { 0xc12, "hpmcounter18" },
1210 { 0xc13, "hpmcounter19" },
1211 { 0xc14, "hpmcounter20" },
1212 { 0xc15, "hpmcounter21" },
1213 { 0xc16, "hpmcounter22" },
1214 { 0xc17, "hpmcounter23" },
1215 { 0xc18, "hpmcounter24" },
1216 { 0xc19, "hpmcounter25" },
1217 { 0xc1a, "hpmcounter26" },
1218 { 0xc1b, "hpmcounter27" },
1219 { 0xc1c, "hpmcounter28" },
1220 { 0xc1d, "hpmcounter29" },
1221 { 0xc1e, "hpmcounter30" },
1222 { 0xc1f, "hpmcounter31" },
1223 { 0xc80, "cycleh" },
1224 { 0xc81, "timeh" },
1225 { 0xc82, "instreth" },
1226 { 0xc83, "hpmcounter3h" },
1227 { 0xc84, "hpmcounter4h" },
1228 { 0xc85, "hpmcounter5h" },
1229 { 0xc86, "hpmcounter6h" },
1230 { 0xc87, "hpmcounter7h" },
1231 { 0xc88, "hpmcounter8h" },
1232 { 0xc89, "hpmcounter9h" },
1233 { 0xc8a, "hpmcounter10h" },
1234 { 0xc8b, "hpmcounter11h" },
1235 { 0xc8c, "hpmcounter12h" },
1236 { 0xc8d, "hpmcounter13h" },
1237 { 0xc8e, "hpmcounter14h" },
1238 { 0xc8f, "hpmcounter15h" },
1239 { 0xc90, "hpmcounter16h" },
1240 { 0xc91, "hpmcounter17h" },
1241 { 0xc92, "hpmcounter18h" },
1242 { 0xc93, "hpmcounter19h" },
1243 { 0xc94, "hpmcounter20h" },
1244 { 0xc95, "hpmcounter21h" },
1245 { 0xc96, "hpmcounter22h" },
1246 { 0xc97, "hpmcounter23h" },
1247 { 0xc98, "hpmcounter24h" },
1248 { 0xc99, "hpmcounter25h" },
1249 { 0xc9a, "hpmcounter26h" },
1250 { 0xc9b, "hpmcounter27h" },
1251 { 0xc9c, "hpmcounter28h" },
1252 { 0xc9d, "hpmcounter29h" },
1253 { 0xc9e, "hpmcounter30h" },
1254 { 0xc9f, "hpmcounter31h" },
1255 };
1256 uint32_t csr = word >> 20;
1257 uint32_t instr = (word >> 12) & 0x7;
1258 size_t last = 0;
1259 if (rd != 0)
1260 op[last++] = REG (rd);
1261 struct known_csrs key = { csr, NULL };
1262 struct known_csrs *found = bsearch (&key, known,
1263 sizeof (known) / sizeof (known[0]),
1264 sizeof (known[0]),
1265 compare_csr);
1266 if (found)
1267 op[last] = (char *) found->name;
1268 else
1269 {
1270 snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx32, csr);
1271 op[last] = addrbuf;
1272 }
1273 ++last;
1274 if ((word & 0x4000) == 0)
1275 op[last] = REG ((word >> 15) & 0x1f);
1276 else
1277 {
1278 snprintf (immbuf, sizeof (immbuf), "%" PRIu32, (word >> 15) & UINT32_C(0x1f));
1279 op[last] = immbuf;
1280 }
1281 if (instr == 1 && rd == 0)
1282 mne = "csrw";
1283 else if (instr == 2 && rd == 0)
1284 mne = "csrs";
1285 else if (instr == 6 && rd == 0)
1286 mne = "csrsi";
1287 else if (instr == 2 && rs1 == 0)
1288 mne = "csrr";
1289 else if (instr == 3 && rd == 0)
1290 mne = "csrc";
1291 else
1292 mne = (char *) mnecsr[instr];
1293 }
1294 break;
1295 default:
1296 break;
1297 }
1298
1299 if (strp == NULL && mne == NULL)
1300 {
1301 len = snprintf (addrbuf, sizeof (addrbuf), "0x%08" PRIx32, word);
1302 strp = addrbuf;
1303 }
1304 }
1305 else
1306 {
1307 // No instruction encodings defined for these sizes yet.
1308 char *cp = stpcpy (mnebuf, "0x");
1309 assert (length % 2 == 0);
1310 for (size_t i = 0; i < length; i += 2)
1311 cp += snprintf (cp, mnebuf + sizeof (mnebuf) - cp, "%04" PRIx16,
1312 read_2ubyte_unaligned (data + i));
1313 strp = mnebuf;
1314 len = cp - mnebuf;
1315 }
1316
1317 if (strp == NULL)
1318 {
1319
1320 if (0)
1321 {
1322 /* Resize the buffer. */
1323 char *oldbuf;
1324 enomem:
1325 oldbuf = buf;
1326 if (buf == initbuf)
1327 buf = malloc (2 * bufsize);
1328 else
1329 buf = realloc (buf, 2 * bufsize);
1330 if (buf == NULL)
1331 {
1332 buf = oldbuf;
1333 retval = ENOMEM;
1334 goto do_ret;
1335 }
1336 bufsize *= 2;
1337
1338 bufcnt = 0;
1339 }
1340
1341 unsigned long string_end_idx = 0;
1342 fmt = save_fmt;
1343 const char *deferred_start = NULL;
1344 size_t deferred_len = 0;
1345 // XXX Can we get this from color.c?
1346 static const char color_off[] = "\e[0m";
1347 while (*fmt != '\0')
1348 {
1349 if (*fmt != '%')
1350 {
1351 char ch = *fmt++;
1352 if (ch == '\\')
1353 {
1354 switch ((ch = *fmt++))
1355 {
1356 case '0' ... '7':
1357 {
1358 int val = ch - '0';
1359 ch = *fmt;
1360 if (ch >= '0' && ch <= '7')
1361 {
1362 val *= 8;
1363 val += ch - '0';
1364 ch = *++fmt;
1365 if (ch >= '0' && ch <= '7' && val < 32)
1366 {
1367 val *= 8;
1368 val += ch - '0';
1369 ++fmt;
1370 }
1371 }
1372 ch = val;
1373 }
1374 break;
1375
1376 case 'n':
1377 ch = '\n';
1378 break;
1379
1380 case 't':
1381 ch = '\t';
1382 break;
1383
1384 default:
1385 retval = EINVAL;
1386 goto do_ret;
1387 }
1388 }
1389 else if (ch == '\e' && *fmt == '[')
1390 {
1391 deferred_start = fmt - 1;
1392 do
1393 ++fmt;
1394 while (*fmt != 'm' && *fmt != '\0');
1395
1396 if (*fmt == 'm')
1397 {
1398 deferred_len = ++fmt - deferred_start;
1399 continue;
1400 }
1401
1402 fmt = deferred_start + 1;
1403 deferred_start = NULL;
1404 }
1405 ADD_CHAR (ch);
1406 continue;
1407 }
1408 ++fmt;
1409
1410 int width = 0;
1411 while (isdigit (*fmt))
1412 width = width * 10 + (*fmt++ - '0');
1413
1414 int prec = 0;
1415 if (*fmt == '.')
1416 while (isdigit (*++fmt))
1417 prec = prec * 10 + (*fmt - '0');
1418
1419 size_t start_idx = bufcnt;
1420 size_t non_printing = 0;
1421 switch (*fmt++)
1422 {
1423 case 'm':
1424 if (deferred_start != NULL)
1425 {
1426 ADD_NSTRING (deferred_start, deferred_len);
1427 non_printing += deferred_len;
1428 }
1429
1430 ADD_STRING (mne);
1431
1432 if (deferred_start != NULL)
1433 {
1434 ADD_STRING (color_off);
1435 non_printing += strlen (color_off);
1436 }
1437
1438 string_end_idx = bufcnt;
1439 break;
1440
1441 case 'o':
1442 if (op[prec - 1] != NULL)
1443 {
1444 if (deferred_start != NULL)
1445 {
1446 ADD_NSTRING (deferred_start, deferred_len);
1447 non_printing += deferred_len;
1448 }
1449
1450 ADD_STRING (op[prec - 1]);
1451
1452 if (deferred_start != NULL)
1453 {
1454 ADD_STRING (color_off);
1455 non_printing += strlen (color_off);
1456 }
1457
1458 string_end_idx = bufcnt;
1459 }
1460 else
1461 bufcnt = string_end_idx;
1462 break;
1463
1464 case 'e':
1465 string_end_idx = bufcnt;
1466 break;
1467
1468 case 'a':
1469 /* Pad to requested column. */
1470 while (bufcnt - non_printing < (size_t) width)
1471 ADD_CHAR (' ');
1472 width = 0;
1473 break;
1474
1475 case 'l':
1476 // TODO
1477 break;
1478
1479 default:
1480 abort();
1481 }
1482
1483 /* Pad according to the specified width. */
1484 while (bufcnt - non_printing < start_idx + width)
1485 ADD_CHAR (' ');
1486 }
1487
1488 strp = buf;
1489 len = bufcnt;
1490 }
1491
1492 addr += length;
1493 *startp = data + length;
1494 retval = outcb (strp, len, outcbarg);
1495 if (retval != 0)
1496 break;
1497 }
1498
1499 do_ret:
1500 if (buf != initbuf)
1501 free (buf);
1502
1503 return retval;
1504 }
1505