Commit | Line | Data |
---|---|---|
4bc8424a MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Register dumping and other diagnostic tools for assembler code | |
4 | * | |
5 | * (c) 2016 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of Catacomb. | |
11 | * | |
12 | * Catacomb is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU Library General Public License as | |
14 | * published by the Free Software Foundation; either version 2 of the | |
15 | * License, or (at your option) any later version. | |
16 | * | |
17 | * Catacomb is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU Library General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU Library General Public | |
23 | * License along with Catacomb; if not, write to the Free | |
24 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | |
25 | * MA 02111-1307, USA. | |
26 | */ | |
27 | ||
28 | /*----- Header files ------------------------------------------------------*/ | |
29 | ||
30 | #include "config.h" | |
31 | ||
32 | #include <assert.h> | |
33 | #include <limits.h> | |
34 | #include <stdio.h> | |
35 | #include <string.h> | |
36 | ||
37 | #include <mLib/bits.h> | |
38 | #include <mLib/macros.h> | |
39 | ||
40 | #include "dispatch.h" | |
41 | #include "regdump.h" | |
42 | ||
43 | /*----- Low-level printing ------------------------------------------------*/ | |
44 | ||
45 | /* Currently these are good for all of our targets. */ | |
46 | #define STEP_8 1 | |
47 | #define TY_HEX_8 uint8 | |
48 | #define P_HEX_8 "0x%02x" | |
49 | #define TY_UNSGN_8 uint8 | |
50 | #define P_UNSGN_8 "%3u" | |
51 | #define PV_CHR_8 " `%c'" | |
52 | #define PV_HEX_8 " %02x" | |
53 | #define PV_UNSGN_8 "%4u" | |
54 | ||
55 | #define STEP_16 2 | |
56 | #define TY_HEX_16 uint16 | |
57 | #define P_HEX_16 "0x%04x" | |
58 | #define TY_UNSGN_16 uint16 | |
59 | #define P_UNSGN_16 "%5u" | |
60 | #define TY_SGN_16 int16 | |
61 | #define P_SGN_16 "%6d" | |
62 | #define PV_HEX_16 " 0x%04x" | |
63 | #define PV_UNSGN_16 "%9u" | |
64 | #define PV_SGN_16 "%9d" | |
65 | ||
66 | #define STEP_32 4 | |
67 | #define TY_HEX_32 uint32 | |
68 | #define P_HEX_32 "0x%08x" | |
69 | #define TY_UNSGN_32 uint32 | |
70 | #define P_UNSGN_32 "%10u" | |
71 | #define TY_SGN_32 int32 | |
72 | #define P_SGN_32 "%11d" | |
73 | #define TY_FLT_32 float | |
74 | #define P_FLT_32 "%15.9g" | |
75 | #define PV_HEX_32 " 0x%08x" | |
76 | #define PV_UNSGN_32 "%19u" | |
77 | #define PV_SGN_32 "%19d" | |
78 | #define PV_FLT_32 "%19.9g" | |
79 | ||
80 | #if ULONG_MAX >> 31 > 0xffffffff | |
81 | # define PL64 "l" | |
82 | #else | |
83 | # define PL64 "ll" | |
84 | #endif | |
85 | #define STEP_64 8 | |
86 | #define TY_HEX_64 uint64 | |
87 | #define P_HEX_64 "0x%016"PL64"x" | |
88 | #define TY_UNSGN_64 uint64 | |
89 | #define P_UNSGN_64 "%20"PL64"u" | |
90 | #define TY_SGN_64 int64 | |
91 | #define P_SGN_64 "%20"PL64"d" | |
92 | #define TY_FLT_64 double | |
93 | #define P_FLT_64 "%24.17g" | |
94 | #define PV_HEX_64 " 0x%016"PL64"x" | |
95 | #define PV_UNSGN_64 "%39"PL64"u" | |
96 | #define PV_SGN_64 "%39"PL64"d" | |
97 | #define PV_FLT_64 "%39.17g" | |
98 | ||
99 | #if CPUFAM_X86 | |
100 | # define STEP_80 12 | |
101 | #endif | |
102 | #if CPUFAM_AMD64 | |
103 | # define STEP_80 16 | |
104 | #endif | |
105 | #define TY_FLT_80 long double | |
106 | #define P_FLT_80 "%29.21Lg" | |
107 | #define PV_FLT_80 P_FLT_80 | |
108 | ||
109 | #if CPUFAM_X86 || CPUFAM_AMD64 | |
110 | # define ARCH_FORMATS(_) \ | |
111 | _(80, FLT) | |
112 | #endif | |
113 | #ifndef ARCH_FORMATS | |
114 | # define ARCH_FORMATS(_) | |
115 | #endif | |
116 | ||
117 | #define FORMATS(_) \ | |
118 | ARCH_FORMATS(_) \ | |
119 | _(64, HEX) _(64, FLT) _(64, UNSGN) _(64, SGN) \ | |
120 | _(32, HEX) _(32, FLT) _(32, UNSGN) _(32, SGN) \ | |
121 | _(16, HEX) _(16, UNSGN) _(16, SGN) \ | |
122 | _(8, HEX) _(8, CHR) _(8, UNSGN) | |
123 | ||
124 | struct fmtinfo { | |
125 | const unsigned char *p; | |
126 | unsigned wd, f; | |
127 | #define FMTF_VECTOR 1u | |
128 | }; | |
129 | ||
130 | #define FMTFUNC_STD(w, fmt) \ | |
131 | static void dump_##fmt##_##w(struct fmtinfo *fmt) \ | |
132 | { \ | |
133 | TY_##fmt##_##w x = *(const TY_##fmt##_##w *)fmt->p; \ | |
134 | \ | |
135 | if (fmt->f&FMTF_VECTOR) printf(PV_##fmt##_##w, x); \ | |
136 | else printf(P_##fmt##_##w, x); \ | |
137 | fmt->p += STEP_##w; fmt->wd += 8*STEP_##w; \ | |
138 | } | |
139 | ||
140 | #define FMTFUNC_HEX(w) FMTFUNC_STD(w, HEX) | |
141 | #define FMTFUNC_UNSGN(w) FMTFUNC_STD(w, UNSGN) | |
142 | #define FMTFUNC_SGN(w) FMTFUNC_STD(w, SGN) | |
143 | #define FMTFUNC_FLT(w) FMTFUNC_STD(w, FLT) | |
144 | #define FMTFUNC_CHR(w) | |
145 | ||
146 | static void dump_CHR_8(struct fmtinfo *fmt) | |
147 | { | |
148 | unsigned char x = *(const unsigned char *)fmt->p; | |
149 | ||
150 | if (x < 32 || x > 126) printf("\\x%02x", x); | |
151 | else printf(" `%c'", x); | |
152 | fmt->p += 1; fmt->wd += 8; | |
153 | } | |
154 | ||
155 | #define FMTFUNC(w, fmt) FMTFUNC_##fmt(w) | |
156 | FORMATS(FMTFUNC) | |
157 | #undef FMTFUNC | |
158 | ||
159 | static const struct fmttab { | |
160 | uint32 mask; | |
161 | void (*fmt)(struct fmtinfo *); | |
162 | } fmttab[] = { | |
163 | #define FMTTAB(wd, fmt) { REGF_##fmt | REGF_##wd, dump_##fmt##_##wd }, | |
164 | FORMATS(FMTTAB) | |
165 | #undef FMTTAB | |
166 | { 0, 0 } | |
167 | }; | |
168 | ||
169 | /*----- Common subroutines ------------------------------------------------*/ | |
170 | ||
171 | /* --- @regwd@ --- * | |
172 | * | |
173 | * Arguments: @uint32 f@ = format control word; see @REGF_...@ | |
174 | * | |
175 | * Returns: The actual width of the operand, in bits. | |
176 | * | |
177 | * Use: If the operand is a vector (the @REGF_WDMASK@ field is | |
178 | * nonzero) then return the width it denotes; otherwise, return | |
179 | * the largest width implied by the @REGF_TYMASK@ field. | |
180 | */ | |
181 | ||
182 | static unsigned regwd(uint32 f) | |
183 | { | |
184 | unsigned wd = 1 << ((f®F_WDMASK) >> REGF_WDSHIFT); | |
185 | ||
186 | if (wd > 1) return (wd); | |
187 | else if (f®F_80) return (80); | |
188 | else if (f®F_64) return (64); | |
189 | else if (f®F_32) return (32); | |
190 | else if (f®F_16) return (16); | |
191 | else if (f®F_8) return (8); | |
192 | else { assert(0); return (1); } | |
193 | } | |
194 | ||
195 | /* --- @regname@ --- * | |
196 | * | |
197 | * Arguments: @char *buf = pointer to output buffer@ | |
198 | * @uint32 f@ = format control word; see @REGF_...@ | |
199 | * | |
200 | * Returns: Pointer to name string. | |
201 | * | |
202 | * Use: Return a pointer to the name of the register implied by @f@, | |
203 | * or null if there is no register. Systematic register names | |
204 | * can be built in the provided buffer. | |
205 | */ | |
206 | ||
207 | static const char *regname(char *buf, uint32 f) | |
208 | { | |
209 | unsigned wd = regwd(f); | |
210 | unsigned src = f®F_SRCMASK; | |
211 | unsigned ix = (f®F_IXMASK) >> REGF_IXSHIFT; | |
212 | char *p = buf; | |
213 | ||
214 | switch (src) { | |
215 | ||
cd303963 | 216 | case REGSRC_NONE: |
4bc8424a MW |
217 | case REGSRC_ABS: |
218 | return (0); | |
219 | ||
220 | #if CPUFAM_X86 || CPUFAM_AMD64 | |
221 | case REGSRC_GP: | |
222 | if (ix == REGIX_FLAGS) { | |
223 | if (wd == 64) *p++ = 'r'; | |
224 | else if (wd == 32) *p++ = 'e'; | |
225 | else if (wd != 16) assert(0); | |
226 | p += sprintf(p, "flags"); | |
227 | #if CPUFAM_AMD64 | |
228 | } else if (REGIX_R8 <= ix && ix <= REGIX_R15) { | |
229 | p += sprintf(p, "r%u", ix - REGIX_R8 + 8); | |
230 | switch (wd) { | |
231 | case 64: break; | |
232 | case 32: *p++ = 'd'; break; | |
233 | case 16: *p++ = 'w'; break; | |
234 | case 8: *p++ = 'l'; break; | |
235 | default: assert(0); | |
236 | } | |
237 | # endif | |
238 | } else { | |
239 | if (wd == 64) *p++ = 'r'; | |
240 | else if (wd == 32) *p++ = 'e'; | |
241 | switch (ix) { | |
242 | case REGIX_IP: *p++ = 'i'; *p++ = 'p'; goto longreg; | |
243 | case REGIX_AX: *p++ = 'a'; goto shortreg; | |
244 | case REGIX_BX: *p++ = 'b'; goto shortreg; | |
245 | case REGIX_CX: *p++ = 'c'; goto shortreg; | |
246 | case REGIX_DX: *p++ = 'd'; goto shortreg; | |
247 | case REGIX_SI: *p++ = 's'; *p++ = 'i'; goto longreg; | |
248 | case REGIX_DI: *p++ = 'd'; *p++ = 'i'; goto longreg; | |
249 | case REGIX_BP: *p++ = 'b'; *p++ = 'p'; goto longreg; | |
250 | case REGIX_SP: *p++ = 's'; *p++ = 'p'; goto longreg; | |
251 | default: assert(0); | |
252 | } | |
253 | if (0) { | |
254 | shortreg: | |
255 | switch (wd) { | |
256 | case 64: | |
257 | case 32: | |
258 | case 16: *p++ = 'x'; break; | |
259 | case 8: *p++ = 'l'; break; | |
260 | default: assert(0); | |
261 | } | |
262 | } else { | |
263 | longreg: | |
264 | switch (wd) { | |
265 | case 64: | |
266 | case 32: | |
267 | case 16: break; | |
268 | case 8: *p++ = 'l'; break; | |
269 | default: assert(0); | |
270 | } | |
271 | } | |
272 | } | |
273 | *p++ = 0; | |
274 | return (buf); | |
275 | ||
276 | case REGSRC_SEG: | |
277 | assert(wd == 16); | |
278 | switch (ix) { | |
279 | case REGIX_CS: sprintf(buf, "cs"); break; | |
280 | case REGIX_DS: sprintf(buf, "ds"); break; | |
281 | case REGIX_SS: sprintf(buf, "ss"); break; | |
282 | case REGIX_ES: sprintf(buf, "es"); break; | |
283 | case REGIX_FS: sprintf(buf, "fs"); break; | |
284 | case REGIX_GS: sprintf(buf, "gs"); break; | |
285 | default: assert(0); | |
286 | } | |
287 | return (buf); | |
288 | ||
289 | case REGSRC_STMMX: | |
290 | if (ix == REGIX_FPFLAGS) return (0); | |
291 | if (f®F_80) sprintf(buf, "st(%u)", ix); | |
292 | else sprintf(buf, "mm%u", ix); | |
293 | return (buf); | |
294 | ||
295 | case REGSRC_SIMD: | |
296 | if (ix == REGIX_FPFLAGS) return (0); | |
297 | switch (wd) { | |
298 | case 32: case 64: case 128: sprintf(buf, "xmm%u", ix); break; | |
299 | case 256: sprintf(buf, "ymm%u", ix); break; | |
300 | default: assert(0); | |
301 | } | |
302 | return (buf); | |
303 | #endif | |
304 | ||
305 | #if CPUFAM_ARMEL | |
306 | case REGSRC_GP: | |
307 | if (ix == REGIX_CPSR) sprintf(buf, "cpsr"); | |
308 | else if (ix == 15) sprintf(buf, "pc"); | |
309 | else sprintf(buf, "r%u", ix); | |
310 | return (buf); | |
311 | case REGSRC_FP: | |
312 | if (ix == REGIX_FPSCR) sprintf(buf, "fpscr"); | |
313 | else { | |
314 | switch (wd) { | |
315 | case 32: *p++ = 's'; break; | |
316 | case 64: *p++ = 'd'; break; | |
317 | case 128: *p++ = 'q'; break; | |
318 | default: assert(0); | |
319 | } | |
320 | p += sprintf(p, "%u", ix); | |
321 | *p++ = 0; | |
322 | } | |
323 | return (buf); | |
324 | #endif | |
325 | ||
326 | #if CPUFAM_ARM64 | |
327 | case REGSRC_GP: | |
328 | if (ix == REGIX_PC) sprintf(buf, "pc"); | |
329 | else if (ix == REGIX_NZCV) sprintf(buf, "nzcv"); | |
330 | else if (ix == 31 && wd == 64) sprintf(buf, "sp"); | |
331 | else { | |
332 | switch (wd) { | |
333 | case 32: *p++ = 'w'; break; | |
334 | case 64: *p++ = 'x'; break; | |
335 | default: assert(0); | |
336 | } | |
337 | p += sprintf(p, "%u", ix); | |
338 | *p++ = 0; | |
339 | } | |
340 | return (buf); | |
341 | case REGSRC_FP: | |
342 | if (ix == REGIX_FPFLAGS) sprintf(buf, "fpflags"); | |
343 | else { | |
344 | if (f®F_WDMASK) | |
345 | *p++ = 'v'; | |
346 | else switch (wd) { | |
347 | case 8: *p++ = 'b'; break; | |
348 | case 16: *p++ = 'h'; break; | |
349 | case 32: *p++ = 's'; break; | |
350 | case 64: *p++ = 'd'; break; | |
351 | default: assert(0); | |
352 | } | |
353 | p += sprintf(p, "%u", ix); | |
354 | *p++ = 0; | |
355 | } | |
356 | return (buf); | |
357 | #endif | |
358 | ||
359 | default: | |
360 | assert(0); | |
361 | return ("???"); | |
362 | } | |
363 | } | |
364 | ||
365 | /*----- x86 and AMD64 -----------------------------------------------------*/ | |
366 | ||
367 | #if CPUFAM_X86 || CPUFAM_AMD64 | |
368 | ||
369 | #if CPUFAM_X86 | |
370 | # define P_HEX_GP "0x%08x" | |
371 | # define GP(gp) (gp).u32 | |
372 | #endif | |
373 | #if CPUFAM_AMD64 | |
374 | # define P_HEX_GP "0x%016"PL64"x" | |
375 | # define GP(gp) (gp).u64 | |
376 | #endif | |
377 | ||
378 | void regdump_init(void) { ; } | |
379 | ||
380 | static void dump_flags(const char *lbl, const char *reg, gpreg f) | |
381 | { | |
382 | printf(";; "); | |
383 | if (lbl) printf("%s: ", lbl); | |
384 | if (reg) printf("%s = ", reg); | |
385 | printf(""P_HEX_GP"\n", GP(f)); | |
386 | printf(";;\t\tstatus: %ccf %cpf %caf %czf %csf %cdf %cof\n", | |
387 | (GP(f) >> 0)&1u ? '+' : '-', | |
388 | (GP(f) >> 2)&1u ? '+' : '-', | |
389 | (GP(f) >> 4)&1u ? '+' : '-', | |
390 | (GP(f) >> 6)&1u ? '+' : '-', | |
391 | (GP(f) >> 7)&1u ? '+' : '-', | |
392 | (GP(f) >> 10)&1u ? '+' : '-', | |
393 | (GP(f) >> 11)&1u ? '+' : '-'); | |
394 | printf(";;\t\tsystem: %ctf %cif iopl=%d %cnt " | |
395 | "%crf %cvm %cac %cvif %cvip %cid\n", | |
396 | (GP(f) >> 8)&1u ? '+' : '-', | |
397 | (GP(f) >> 9)&1u ? '+' : '-', | |
398 | (int)((GP(f) >> 12)&1u), | |
399 | (GP(f) >> 14)&1u ? '+' : '-', | |
400 | (GP(f) >> 16)&1u ? '+' : '-', | |
401 | (GP(f) >> 17)&1u ? '+' : '-', | |
402 | (GP(f) >> 18)&1u ? '+' : '-', | |
403 | (GP(f) >> 19)&1u ? '+' : '-', | |
404 | (GP(f) >> 20)&1u ? '+' : '-', | |
405 | (GP(f) >> 21)&1u ? '+' : '-'); | |
406 | } | |
407 | ||
408 | static const char | |
409 | *pcmap[] = { "sgl", "???", "dbl", "ext" }, | |
410 | *rcmap[] = { "nr", "-∞", "+∞", "0" }; | |
411 | ||
412 | static void dump_fpflags(const char *lbl, const struct fxsave *fx) | |
413 | { | |
414 | unsigned top = (fx->fsw >> 11)&7u; | |
415 | unsigned tag = fx->ftw; | |
416 | int skip = lbl ? strlen(lbl) + 2 : 0; | |
417 | ||
418 | printf(";; "); | |
419 | if (lbl) printf("%s: ", lbl); | |
420 | ||
421 | printf(" fcw = 0x%04x: " | |
422 | "%cim %cdm %czm %com %cum %cpm pc=%s rc=%s %cx\n", | |
423 | fx->fcw, | |
424 | (fx->fcw >> 0)&1u ? '+' : '-', | |
425 | (fx->fcw >> 1)&1u ? '+' : '-', | |
426 | (fx->fcw >> 2)&1u ? '+' : '-', | |
427 | (fx->fcw >> 3)&1u ? '+' : '-', | |
428 | (fx->fcw >> 4)&1u ? '+' : '-', | |
429 | (fx->fcw >> 5)&1u ? '+' : '-', | |
430 | pcmap[(fx->fcw >> 8)&3u], | |
431 | rcmap[(fx->fcw >> 10)&3u], | |
432 | (fx->fcw >> 12)&1u ? '+' : '-'); | |
433 | printf(";; %*s fsw = 0x%04x: " | |
434 | "%cie %cde %cze %coe %cue %cpe %csf %ces %cc0 %cc1 %cc2 %cc3 " | |
435 | "top=%d %cb\n", | |
436 | skip, "", | |
437 | fx->fsw, | |
438 | (fx->fsw >> 0)&1u ? '+' : '-', | |
439 | (fx->fsw >> 1)&1u ? '+' : '-', | |
440 | (fx->fsw >> 2)&1u ? '+' : '-', | |
441 | (fx->fsw >> 3)&1u ? '+' : '-', | |
442 | (fx->fsw >> 4)&1u ? '+' : '-', | |
443 | (fx->fsw >> 5)&1u ? '+' : '-', | |
444 | (fx->fsw >> 6)&1u ? '+' : '-', | |
445 | (fx->fsw >> 7)&1u ? '+' : '-', | |
446 | (fx->fsw >> 8)&1u ? '+' : '-', | |
447 | (fx->fsw >> 9)&1u ? '+' : '-', | |
448 | (fx->fsw >> 10)&1u ? '+' : '-', | |
449 | (fx->fsw >> 14)&1u ? '+' : '-', | |
450 | top, | |
451 | (fx->fsw >> 15)&1u ? '+' : '-'); | |
452 | printf(";; %*s ftw = 0x%02x\n", skip, "", tag); | |
453 | } | |
454 | ||
455 | static void dump_mxflags(const char *lbl, const struct fxsave *fx) | |
456 | { | |
457 | printf(";; "); | |
458 | if (lbl) printf("%s: ", lbl); | |
459 | ||
460 | printf(" mxcsr = 0x%08x\n" | |
461 | ";;\t\tmask = %cim %cdm %czm %com %cum %cpm\n" | |
462 | ";;\t\t exc = %cie %cde %cze %coe %cue %cpe\n" | |
463 | ";;\t\tmisc = %cdaz %cftz rc=%s\n", | |
464 | fx->mxcsr, | |
465 | (fx->mxcsr >> 7)&1u ? '+' : '-', | |
466 | (fx->mxcsr >> 8)&1u ? '+' : '-', | |
467 | (fx->mxcsr >> 9)&1u ? '+' : '-', | |
468 | (fx->mxcsr >> 10)&1u ? '+' : '-', | |
469 | (fx->mxcsr >> 11)&1u ? '+' : '-', | |
470 | (fx->mxcsr >> 12)&1u ? '+' : '-', | |
471 | (fx->mxcsr >> 0)&1u ? '+' : '-', | |
472 | (fx->mxcsr >> 1)&1u ? '+' : '-', | |
473 | (fx->mxcsr >> 2)&1u ? '+' : '-', | |
474 | (fx->mxcsr >> 3)&1u ? '+' : '-', | |
475 | (fx->mxcsr >> 4)&1u ? '+' : '-', | |
476 | (fx->mxcsr >> 5)&1u ? '+' : '-', | |
477 | (fx->mxcsr >> 6)&1u ? '+' : '-', | |
478 | (fx->mxcsr >> 15)&1u ? '+' : '-', | |
479 | rcmap[(fx->mxcsr >> 13)&3u]); | |
480 | } | |
481 | ||
482 | #if CPUFAM_X86 | |
483 | # define REGF_GPWD REGF_32 | |
484 | #endif | |
485 | #if CPUFAM_AMD64 | |
486 | # define REGF_GPWD REGF_64 | |
487 | #endif | |
488 | ||
489 | void regdump_gp(const struct regmap *map) | |
490 | { | |
491 | unsigned i; | |
492 | ||
493 | printf(";; General-purpose registers:\n"); | |
494 | for (i = REGIX_AX; i < REGIX_GPLIM; i++) | |
495 | regdump(map, 0, | |
496 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_GPWD | REGSRC_GP | i); | |
497 | regdump(map, 0, REGF_HEX | REGF_GPWD | REGSRC_GP | REGIX_IP); | |
498 | ||
499 | printf(";; Segment registers:\n"); | |
500 | for (i = 0; i < REGIX_SEGLIM; i++) | |
501 | regdump(map, 0, REGF_HEX | REGF_16 | REGSRC_SEG | i); | |
502 | ||
503 | printf(";; Flags:\n"); | |
504 | regdump(map, 0, REGSRC_GP | REGF_GPWD | REGIX_FLAGS); | |
505 | } | |
506 | ||
507 | void regdump_fp(const struct regmap *map) | |
508 | { | |
509 | unsigned top = (map->fx->fsw >> 11)&7u; | |
510 | unsigned tag = map->fx->ftw; | |
511 | unsigned i; | |
512 | ||
513 | printf(";; Floating-point/MMX registers:\n"); | |
514 | if (!top && tag == 0xff) | |
515 | for (i = 0; i < 8; i++) | |
516 | regdump(map, 0, | |
517 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_CHR | | |
518 | REGF_32 | REGF_16 | REGF_8 | | |
519 | REGSRC_STMMX | i | (6 << REGF_WDSHIFT)); | |
520 | if (tag) | |
521 | for (i = 0; i < 8; i++) | |
522 | regdump(map, 0, REGF_FLT | REGF_80 | REGSRC_STMMX | i); | |
523 | ||
524 | printf(";; Floating-point state:\n"); | |
525 | dump_fpflags(0, map->fx); | |
526 | } | |
527 | ||
528 | void regdump_simd(const struct regmap *map) | |
529 | { | |
530 | unsigned f = REGF_HEX | REGF_FLT | REGF_UNSGN | REGF_SGN | REGF_CHR | | |
531 | REGF_64 | REGF_32 | REGF_16 | REGF_8 | | |
532 | REGSRC_SIMD; | |
533 | unsigned i; | |
534 | ||
535 | if (map->avx) f |= 8 << REGF_WDSHIFT; | |
536 | else f |= 7 << REGF_WDSHIFT; | |
537 | ||
538 | printf(";; SSE/AVX registers:\n"); | |
539 | for (i = 0; i < N(map->fx->xmm); i++) | |
540 | regdump(map, 0, f | i); | |
541 | ||
542 | printf(";; SSE/AVX floating-point state:\n"); | |
543 | dump_mxflags(0, map->fx); | |
544 | } | |
545 | ||
546 | #endif | |
547 | ||
548 | /*----- ARM32 -------------------------------------------------------------*/ | |
549 | ||
550 | #if CPUFAM_ARMEL | |
551 | ||
552 | unsigned regdump__flags = 0; | |
553 | ||
554 | void regdump_init(void) | |
555 | { | |
556 | if (cpu_feature_p(CPUFEAT_ARM_VFP)) regdump__flags |= REGF_VFP; | |
557 | if (cpu_feature_p(CPUFEAT_ARM_D32)) regdump__flags |= REGF_D32; | |
558 | } | |
559 | ||
560 | static void dump_flags(const char *lbl, unsigned f) | |
561 | { | |
562 | static const char | |
563 | *modetab[] = { "?00", "?01", "?02", "?03", "?04", "?05", "?06", "?07", | |
564 | "?08", "?09", "?10", "?11", "?12", "?13", "?14", "?15", | |
565 | "usr", "fiq", "irq", "svc", "?20", "?21", "mon", "abt", | |
566 | "?24", "?25", "hyp", "und", "?28", "?29", "?30", "sys" }, | |
567 | *condtab[] = { "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", | |
568 | "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" }; | |
569 | ||
570 | printf(";; "); | |
571 | if (lbl) printf("%s: ", lbl); | |
572 | printf(" cpsr = 0x%08x\n", f); | |
573 | printf(";;\t\tuser: %cn %cz %cc %cv %cq ge=%c%c%c%c\n", | |
574 | (f >> 31)&1u ? '+' : '-', | |
575 | (f >> 30)&1u ? '+' : '-', | |
576 | (f >> 29)&1u ? '+' : '-', | |
577 | (f >> 28)&1u ? '+' : '-', | |
578 | (f >> 27)&1u ? '+' : '-', | |
579 | (f >> 19)&1u ? '1' : '0', | |
580 | (f >> 18)&1u ? '1' : '0', | |
581 | (f >> 17)&1u ? '1' : '0', | |
582 | (f >> 16)&1u ? '1' : '0'); | |
583 | printf(";;\t\tsystem: %cj it=%s:%c%c%c%c %ce %ca %ci %cf %ct m=%s\n", | |
584 | (f >> 24)&1u ? '+' : '-', | |
585 | condtab[(f >> 12)&15u], | |
586 | (f >> 11)&1u ? '1' : '0', | |
587 | (f >> 10)&1u ? '1' : '0', | |
588 | (f >> 26)&1u ? '1' : '0', | |
589 | (f >> 25)&1u ? '1' : '0', | |
590 | (f >> 9)&1u ? '+' : '-', | |
591 | (f >> 8)&1u ? '+' : '-', | |
592 | (f >> 7)&1u ? '+' : '-', | |
593 | (f >> 6)&1u ? '+' : '-', | |
594 | (f >> 5)&1u ? '+' : '-', | |
595 | modetab[(f >> 0)&31u]); | |
596 | } | |
597 | ||
598 | static void dump_fpflags(const char *lbl, unsigned f) | |
599 | { | |
600 | static const char *rcmap[] = { "nr", "+∞", "-∞", "0" }; | |
601 | ||
602 | printf(";; "); | |
603 | if (lbl) printf("%s: ", lbl); | |
604 | printf(" fpscr = 0x%08x\n", f); | |
605 | printf(";;\t\tcond: %cn %cz %cc %cv %cqc\n", | |
606 | (f >> 31)&1u ? '+' : '-', | |
607 | (f >> 30)&1u ? '+' : '-', | |
608 | (f >> 29)&1u ? '+' : '-', | |
609 | (f >> 28)&1u ? '+' : '-', | |
610 | (f >> 27)&1u ? '+' : '-'); | |
611 | printf(";;\t\ttrap: %cide %cixe %cufe %cofe %cdze %cioe\n", | |
612 | (f >> 15)&1u ? '+' : '-', | |
613 | (f >> 12)&1u ? '+' : '-', | |
614 | (f >> 11)&1u ? '+' : '-', | |
615 | (f >> 10)&1u ? '+' : '-', | |
616 | (f >> 9)&1u ? '+' : '-', | |
617 | (f >> 8)&1u ? '+' : '-'); | |
618 | printf(";;\t\terror: %cide %cixe %cufe %cofe %cdze %cioe\n", | |
619 | (f >> 7)&1u ? '+' : '-', | |
620 | (f >> 4)&1u ? '+' : '-', | |
621 | (f >> 3)&1u ? '+' : '-', | |
622 | (f >> 2)&1u ? '+' : '-', | |
623 | (f >> 1)&1u ? '+' : '-', | |
624 | (f >> 0)&1u ? '+' : '-'); | |
625 | printf(";;\t\tcontrol: %cahp %cdn %cfz rm=%s str=%d len=%d\n", | |
626 | (f >> 26)&1u ? '+' : '-', | |
627 | (f >> 25)&1u ? '+' : '-', | |
628 | (f >> 24)&1u ? '+' : '-', | |
629 | rcmap[(f >> 22)&3u], | |
630 | (f >> 20)&3u, | |
631 | (f >> 16)&7u); | |
632 | } | |
633 | ||
634 | void regdump_gp(const struct regmap *map) | |
635 | { | |
636 | unsigned i; | |
637 | ||
638 | printf(";; General-purpose registers:\n"); | |
639 | for (i = 0; i < 16; i++) | |
640 | regdump(map, 0, | |
641 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_32 | REGSRC_GP | i); | |
642 | ||
643 | printf(";; Flags:\n"); | |
644 | regdump(map, 0, REGSRC_GP | REGF_32 | REGIX_CPSR); | |
645 | } | |
646 | ||
647 | void regdump_fp(const struct regmap *map) | |
648 | { | |
649 | unsigned i, n; | |
650 | ||
651 | if (!(regdump__flags®F_VFP)) { | |
652 | printf(";; Floating-point and SIMD not available\n"); | |
653 | return; | |
654 | } | |
655 | ||
656 | printf(";; Floating-point/SIMD registers:\n"); | |
657 | if (regdump__flags®F_D32) n = 32; | |
658 | else n = 16; | |
659 | for (i = 0; i < n; i++) | |
660 | regdump(map, 0, | |
661 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_FLT | REGF_CHR | | |
662 | REGF_64 | REGF_32 | REGF_16 | REGF_8 | | |
24099531 | 663 | REGSRC_FP | i | (6 << REGF_WDSHIFT)); |
4bc8424a MW |
664 | |
665 | printf(";; Floating-point state:\n"); | |
666 | dump_fpflags(0, map->fp->fpscr); | |
667 | } | |
668 | ||
669 | void regdump_simd(const struct regmap *map) { ; } | |
670 | ||
671 | #endif | |
672 | ||
673 | /*----- ARM64 -------------------------------------------------------------*/ | |
674 | ||
675 | #if CPUFAM_ARM64 | |
676 | ||
677 | void regdump_init(void) { ; } | |
678 | ||
679 | static void dump_flags(const char *lbl, unsigned f) | |
680 | { | |
681 | printf(";; "); | |
682 | if (lbl) printf("%s: ", lbl); | |
683 | printf(" nzcv = 0x%08x\n", f); | |
684 | printf(";;\t\tuser: %cn %cz %cc %cv\n", | |
685 | (f >> 31)&1u ? '+' : '-', | |
686 | (f >> 30)&1u ? '+' : '-', | |
687 | (f >> 29)&1u ? '+' : '-', | |
688 | (f >> 28)&1u ? '+' : '-'); | |
689 | } | |
690 | ||
691 | static void dump_fpflags(const char *lbl, const struct fpsave *fp) | |
692 | { | |
693 | static const char *rcmap[] = { "nr", "+∞", "-∞", "0" }; | |
694 | int skip = lbl ? strlen(lbl) + 2 : 0; | |
695 | ||
696 | printf(";; "); | |
697 | if (lbl) printf("%s: ", lbl); | |
698 | printf(" fpsr = 0x%08x\n", fp->fpsr); | |
699 | printf(";;\t\tcond: %cn %cz %cc %cv %cqc\n", | |
700 | (fp->fpsr >> 31)&1u ? '+' : '-', | |
701 | (fp->fpsr >> 30)&1u ? '+' : '-', | |
702 | (fp->fpsr >> 29)&1u ? '+' : '-', | |
703 | (fp->fpsr >> 28)&1u ? '+' : '-', | |
704 | (fp->fpsr >> 27)&1u ? '+' : '-'); | |
705 | printf(";;\t\terror: %cidc %cixc %cufc %cofc %cdzc %cioc\n", | |
706 | (fp->fpsr >> 7)&1u ? '+' : '-', | |
707 | (fp->fpsr >> 4)&1u ? '+' : '-', | |
708 | (fp->fpsr >> 3)&1u ? '+' : '-', | |
709 | (fp->fpsr >> 2)&1u ? '+' : '-', | |
710 | (fp->fpsr >> 1)&1u ? '+' : '-', | |
711 | (fp->fpsr >> 0)&1u ? '+' : '-'); | |
712 | printf(";; %*s fpcr = 0x%08x\n", skip, "", fp->fpcr); | |
713 | printf(";;\t\ttrap: %cide %cixe %cufe %cofe %cdze %cioe\n", | |
714 | (fp->fpcr >> 15)&1u ? '+' : '-', | |
715 | (fp->fpcr >> 12)&1u ? '+' : '-', | |
716 | (fp->fpcr >> 11)&1u ? '+' : '-', | |
717 | (fp->fpcr >> 10)&1u ? '+' : '-', | |
718 | (fp->fpcr >> 9)&1u ? '+' : '-', | |
719 | (fp->fpcr >> 8)&1u ? '+' : '-'); | |
720 | printf(";;\t\tcontrol: %cahp %cdn %cfz rm=%s str=%d len=%d\n", | |
721 | (fp->fpcr >> 26)&1u ? '+' : '-', | |
722 | (fp->fpcr >> 25)&1u ? '+' : '-', | |
723 | (fp->fpcr >> 24)&1u ? '+' : '-', | |
724 | rcmap[(fp->fpcr >> 22)&3u], | |
725 | (fp->fpcr >> 20)&3u, | |
726 | (fp->fpcr >> 16)&7u); | |
727 | } | |
728 | ||
729 | void regdump_gp(const struct regmap *map) | |
730 | { | |
731 | unsigned i; | |
732 | ||
733 | printf(";; General-purpose registers:\n"); | |
734 | for (i = 0; i < 32; i++) | |
735 | regdump(map, 0, | |
736 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_64 | REGSRC_GP | i); | |
737 | regdump(map, 0, REGF_HEX | REGF_64 | REGSRC_GP | REGIX_PC); | |
738 | ||
739 | printf(";; Flags:\n"); | |
740 | regdump(map, 0, REGSRC_GP | REGF_32 | REGIX_NZCV); | |
741 | } | |
742 | ||
743 | void regdump_fp(const struct regmap *map) | |
744 | { | |
745 | unsigned i; | |
746 | ||
747 | printf(";; Floating-point/SIMD registers:\n"); | |
748 | for (i = 0; i < 32; i++) | |
749 | regdump(map, 0, | |
750 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_FLT | REGF_CHR | | |
751 | REGF_64 | REGF_32 | REGF_16 | REGF_8 | | |
752 | REGSRC_SIMD | i | (7 << REGF_WDSHIFT)); | |
753 | ||
754 | printf(";; Floating-point state:\n"); | |
755 | dump_fpflags(0, map->fp); | |
756 | } | |
757 | ||
758 | void regdump_simd(const struct regmap *map) { ; } | |
759 | ||
760 | #endif | |
761 | ||
762 | /*----- The main entry point ----------------------------------------------*/ | |
763 | ||
764 | /* --- @regdump@ --- * | |
765 | * | |
766 | * Arguments: @const void *base@ = pointer to base structure, corresponding | |
767 | * to the @REGF_SRCMASK@ part of @f@ | |
768 | * @const char *lbl@ = label to print | |
769 | * @uint32 f@ = format control word; see @REGF_...@ | |
770 | * | |
771 | * Returns: --- | |
772 | * | |
773 | * Use: Dump a register value, or chunk of memory. | |
774 | * | |
775 | * This function is not usually called directly; instead, use | |
776 | * the `reg' or `mem' assembler macros. | |
777 | */ | |
778 | ||
779 | void regdump(const void *base, const char *lbl, uint32 f) | |
780 | { | |
781 | unsigned ix = (f®F_IXMASK) >> REGF_IXSHIFT; | |
782 | unsigned wd = 1 << ((f®F_WDMASK) >> REGF_WDSHIFT); | |
783 | unsigned fmt, ty; | |
784 | uint32 fmtbit, tybit; | |
785 | const void *p; | |
786 | char regbuf[8]; const char *reg = regname(regbuf, f); | |
787 | const struct regmap *map; | |
788 | const struct fmttab *tab; | |
789 | struct fmtinfo fi; | |
790 | int firstp = 1; | |
791 | int skip; | |
792 | size_t n; | |
793 | ||
794 | #if CPUFAM_X86 || CPUFAM_AMD64 | |
795 | union vreg vr; | |
796 | #endif | |
797 | ||
798 | if (reg) { | |
799 | n = strlen(reg); | |
800 | if (n < 7) { | |
801 | memmove(regbuf + 7 - n, reg, n + 1); | |
802 | memset(regbuf, ' ', 7 - n); | |
803 | } | |
804 | } | |
805 | ||
806 | switch (f®F_SRCMASK) { | |
cd303963 MW |
807 | |
808 | case REGSRC_NONE: | |
809 | printf(";; %s\n", lbl); | |
810 | return; | |
811 | ||
4bc8424a MW |
812 | case REGSRC_ABS: |
813 | p = base; | |
814 | break; | |
815 | ||
816 | #if CPUFAM_X86 || CPUFAM_AMD64 | |
817 | case REGSRC_GP: | |
818 | map = (const struct regmap *)base; | |
819 | if (ix == REGIX_FLAGS && !(f®F_FMTMASK)) | |
820 | { dump_flags(lbl, reg, map->gp->gp[REGIX_FLAGS]); return; } | |
821 | p = &map->gp->gp[ix]; | |
822 | break; | |
823 | case REGSRC_SEG: | |
824 | map = (const struct regmap *)base; | |
825 | assert(wd == 1); assert((f®F_TYMASK) == REGF_16); | |
826 | p = &map->gp->seg[ix]; | |
827 | break; | |
828 | case REGSRC_STMMX: | |
829 | map = (const struct regmap *)base; | |
830 | if (ix == REGIX_FPFLAGS) | |
831 | { assert(!(f®F_FMTMASK)); dump_fpflags(lbl, map->fx); return; } | |
832 | if (!((map->fx->ftw << ix)&128u)) { | |
833 | printf(";; "); | |
834 | if (lbl) printf("%s: ", lbl); | |
835 | if (reg) printf("%s = ", reg); | |
836 | printf(" dead\n"); | |
837 | return; | |
838 | } | |
839 | p = &map->fx->stmmx[ix]; | |
840 | break; | |
841 | case REGSRC_SIMD: | |
842 | map = (const struct regmap *)base; | |
843 | if (ix == REGIX_FPFLAGS) | |
844 | { assert(!(f®F_FMTMASK)); dump_mxflags(lbl, map->fx); return; } | |
845 | if (wd <= 128) | |
846 | p = &map->fx->xmm[ix]; | |
847 | else { | |
848 | vr.v128[0] = map->fx->xmm[ix]; | |
849 | vr.v128[1] = map->avx->ymmh[ix]; | |
850 | assert(wd == 256); | |
851 | p = &vr; | |
852 | } | |
853 | break; | |
854 | #endif | |
855 | ||
856 | #if CPUFAM_ARMEL | |
857 | case REGSRC_GP: | |
858 | map = (const struct regmap *)base; | |
859 | if (ix == REGIX_CPSR && !(f®F_FMTMASK)) | |
860 | { dump_flags(lbl, map->gp->r[REGIX_CPSR].u32); return; } | |
861 | p = &map->gp->r[ix]; | |
862 | break; | |
863 | case REGSRC_FP: | |
864 | case REGSRC_SIMD: | |
865 | map = (const struct regmap *)base; | |
b26c7863 MW |
866 | if (!map->fp) { |
867 | printf(";;"); | |
868 | if (lbl) printf(" %s:", lbl); | |
869 | if (reg) printf(" %s =", reg); | |
870 | printf(" #<not available -- regdump_init?>\n"); | |
871 | return; | |
872 | } | |
4bc8424a MW |
873 | if (ix == REGIX_FPSCR) { |
874 | assert(!(f®F_FMTMASK)); | |
875 | dump_fpflags(lbl, map->fp->fpscr); | |
876 | return; | |
877 | } | |
878 | switch (regwd(f)) { | |
879 | case 32: p = &map->fp->u.s[ix]; break; | |
880 | case 64: p = &map->fp->u.d[ix]; break; | |
881 | case 128: p = &map->fp->u.q[ix]; break; | |
882 | default: assert(0); | |
883 | } | |
884 | break; | |
885 | #endif | |
886 | ||
887 | #if CPUFAM_ARM64 | |
888 | case REGSRC_GP: | |
889 | map = (const struct regmap *)base; | |
890 | if (ix == REGIX_NZCV && !(f®F_FMTMASK)) | |
891 | { dump_flags(lbl, map->gp->r[REGIX_NZCV].u64); return; } | |
892 | p = &map->gp->r[ix]; | |
893 | break; | |
894 | case REGSRC_FP: | |
895 | case REGSRC_SIMD: | |
896 | map = (const struct regmap *)base; | |
897 | if (ix == REGIX_FPFLAGS) | |
898 | { assert(!(f®F_FMTMASK)); dump_fpflags(lbl, map->fp); return; } | |
899 | p = &map->fp->v[ix]; | |
900 | break; | |
901 | #endif | |
902 | ||
903 | default: | |
904 | assert(0); | |
905 | } | |
906 | ||
907 | skip = (lbl ? strlen(lbl) + 2 : 0) + (reg ? strlen(reg) : 0); | |
908 | fi.f = 0; if (wd > 1) fi.f |= FMTF_VECTOR; | |
909 | ||
910 | for (ty = (f®F_TYMASK) >> REGF_TYSHIFT, | |
911 | tybit = 1 << REGF_TYSHIFT; | |
912 | ty; | |
913 | ty >>= 1, tybit <<= 1) { | |
914 | if (!(ty&1u)) continue; | |
915 | ||
916 | for (fmt = (f®F_FMTMASK) >> REGF_FMTSHIFT, | |
917 | fmtbit = 1 << REGF_FMTSHIFT; | |
918 | fmt; | |
919 | fmt >>= 1, fmtbit <<= 1) { | |
920 | ||
921 | if (!(fmt&1u)) continue; | |
922 | ||
923 | for (tab = fmttab; tab->mask; tab++) | |
924 | if (tab->mask == (fmtbit | tybit)) goto found; | |
925 | continue; | |
926 | found: | |
927 | ||
928 | if (firstp) { | |
929 | printf(";;"); | |
930 | if (lbl) printf(" %s:", lbl); | |
931 | if (reg) printf(" %s =", reg); | |
932 | firstp = 0; | |
933 | } else if (wd > 1) | |
934 | printf("\n;; %*s =", skip, ""); | |
935 | else | |
936 | fputs(" =", stdout); | |
937 | ||
938 | fi.p = p; fi.wd = 0; | |
939 | while (fi.wd < wd) { putchar(' '); tab->fmt(&fi); } | |
940 | } | |
941 | } | |
942 | putchar('\n'); | |
943 | } | |
944 | ||
945 | /*----- Other random utilities --------------------------------------------*/ | |
946 | ||
947 | /* --- @regdump_freshline@ --- * | |
948 | * | |
949 | * Arguments: --- | |
950 | * | |
951 | * Returns: --- | |
952 | * | |
953 | * Use: Begin a fresh line of output. | |
954 | */ | |
955 | ||
956 | void regdump_freshline(void) { putchar('\n'); } | |
957 | ||
958 | /*----- That's all, folks -------------------------------------------------*/ |