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 | ||
216 | case REGSRC_ABS: | |
217 | return (0); | |
218 | ||
219 | #if CPUFAM_X86 || CPUFAM_AMD64 | |
220 | case REGSRC_GP: | |
221 | if (ix == REGIX_FLAGS) { | |
222 | if (wd == 64) *p++ = 'r'; | |
223 | else if (wd == 32) *p++ = 'e'; | |
224 | else if (wd != 16) assert(0); | |
225 | p += sprintf(p, "flags"); | |
226 | #if CPUFAM_AMD64 | |
227 | } else if (REGIX_R8 <= ix && ix <= REGIX_R15) { | |
228 | p += sprintf(p, "r%u", ix - REGIX_R8 + 8); | |
229 | switch (wd) { | |
230 | case 64: break; | |
231 | case 32: *p++ = 'd'; break; | |
232 | case 16: *p++ = 'w'; break; | |
233 | case 8: *p++ = 'l'; break; | |
234 | default: assert(0); | |
235 | } | |
236 | # endif | |
237 | } else { | |
238 | if (wd == 64) *p++ = 'r'; | |
239 | else if (wd == 32) *p++ = 'e'; | |
240 | switch (ix) { | |
241 | case REGIX_IP: *p++ = 'i'; *p++ = 'p'; goto longreg; | |
242 | case REGIX_AX: *p++ = 'a'; goto shortreg; | |
243 | case REGIX_BX: *p++ = 'b'; goto shortreg; | |
244 | case REGIX_CX: *p++ = 'c'; goto shortreg; | |
245 | case REGIX_DX: *p++ = 'd'; goto shortreg; | |
246 | case REGIX_SI: *p++ = 's'; *p++ = 'i'; goto longreg; | |
247 | case REGIX_DI: *p++ = 'd'; *p++ = 'i'; goto longreg; | |
248 | case REGIX_BP: *p++ = 'b'; *p++ = 'p'; goto longreg; | |
249 | case REGIX_SP: *p++ = 's'; *p++ = 'p'; goto longreg; | |
250 | default: assert(0); | |
251 | } | |
252 | if (0) { | |
253 | shortreg: | |
254 | switch (wd) { | |
255 | case 64: | |
256 | case 32: | |
257 | case 16: *p++ = 'x'; break; | |
258 | case 8: *p++ = 'l'; break; | |
259 | default: assert(0); | |
260 | } | |
261 | } else { | |
262 | longreg: | |
263 | switch (wd) { | |
264 | case 64: | |
265 | case 32: | |
266 | case 16: break; | |
267 | case 8: *p++ = 'l'; break; | |
268 | default: assert(0); | |
269 | } | |
270 | } | |
271 | } | |
272 | *p++ = 0; | |
273 | return (buf); | |
274 | ||
275 | case REGSRC_SEG: | |
276 | assert(wd == 16); | |
277 | switch (ix) { | |
278 | case REGIX_CS: sprintf(buf, "cs"); break; | |
279 | case REGIX_DS: sprintf(buf, "ds"); break; | |
280 | case REGIX_SS: sprintf(buf, "ss"); break; | |
281 | case REGIX_ES: sprintf(buf, "es"); break; | |
282 | case REGIX_FS: sprintf(buf, "fs"); break; | |
283 | case REGIX_GS: sprintf(buf, "gs"); break; | |
284 | default: assert(0); | |
285 | } | |
286 | return (buf); | |
287 | ||
288 | case REGSRC_STMMX: | |
289 | if (ix == REGIX_FPFLAGS) return (0); | |
290 | if (f®F_80) sprintf(buf, "st(%u)", ix); | |
291 | else sprintf(buf, "mm%u", ix); | |
292 | return (buf); | |
293 | ||
294 | case REGSRC_SIMD: | |
295 | if (ix == REGIX_FPFLAGS) return (0); | |
296 | switch (wd) { | |
297 | case 32: case 64: case 128: sprintf(buf, "xmm%u", ix); break; | |
298 | case 256: sprintf(buf, "ymm%u", ix); break; | |
299 | default: assert(0); | |
300 | } | |
301 | return (buf); | |
302 | #endif | |
303 | ||
304 | #if CPUFAM_ARMEL | |
305 | case REGSRC_GP: | |
306 | if (ix == REGIX_CPSR) sprintf(buf, "cpsr"); | |
307 | else if (ix == 15) sprintf(buf, "pc"); | |
308 | else sprintf(buf, "r%u", ix); | |
309 | return (buf); | |
310 | case REGSRC_FP: | |
311 | if (ix == REGIX_FPSCR) sprintf(buf, "fpscr"); | |
312 | else { | |
313 | switch (wd) { | |
314 | case 32: *p++ = 's'; break; | |
315 | case 64: *p++ = 'd'; break; | |
316 | case 128: *p++ = 'q'; break; | |
317 | default: assert(0); | |
318 | } | |
319 | p += sprintf(p, "%u", ix); | |
320 | *p++ = 0; | |
321 | } | |
322 | return (buf); | |
323 | #endif | |
324 | ||
325 | #if CPUFAM_ARM64 | |
326 | case REGSRC_GP: | |
327 | if (ix == REGIX_PC) sprintf(buf, "pc"); | |
328 | else if (ix == REGIX_NZCV) sprintf(buf, "nzcv"); | |
329 | else if (ix == 31 && wd == 64) sprintf(buf, "sp"); | |
330 | else { | |
331 | switch (wd) { | |
332 | case 32: *p++ = 'w'; break; | |
333 | case 64: *p++ = 'x'; break; | |
334 | default: assert(0); | |
335 | } | |
336 | p += sprintf(p, "%u", ix); | |
337 | *p++ = 0; | |
338 | } | |
339 | return (buf); | |
340 | case REGSRC_FP: | |
341 | if (ix == REGIX_FPFLAGS) sprintf(buf, "fpflags"); | |
342 | else { | |
343 | if (f®F_WDMASK) | |
344 | *p++ = 'v'; | |
345 | else switch (wd) { | |
346 | case 8: *p++ = 'b'; break; | |
347 | case 16: *p++ = 'h'; break; | |
348 | case 32: *p++ = 's'; break; | |
349 | case 64: *p++ = 'd'; break; | |
350 | default: assert(0); | |
351 | } | |
352 | p += sprintf(p, "%u", ix); | |
353 | *p++ = 0; | |
354 | } | |
355 | return (buf); | |
356 | #endif | |
357 | ||
358 | default: | |
359 | assert(0); | |
360 | return ("???"); | |
361 | } | |
362 | } | |
363 | ||
364 | /*----- x86 and AMD64 -----------------------------------------------------*/ | |
365 | ||
366 | #if CPUFAM_X86 || CPUFAM_AMD64 | |
367 | ||
368 | #if CPUFAM_X86 | |
369 | # define P_HEX_GP "0x%08x" | |
370 | # define GP(gp) (gp).u32 | |
371 | #endif | |
372 | #if CPUFAM_AMD64 | |
373 | # define P_HEX_GP "0x%016"PL64"x" | |
374 | # define GP(gp) (gp).u64 | |
375 | #endif | |
376 | ||
377 | void regdump_init(void) { ; } | |
378 | ||
379 | static void dump_flags(const char *lbl, const char *reg, gpreg f) | |
380 | { | |
381 | printf(";; "); | |
382 | if (lbl) printf("%s: ", lbl); | |
383 | if (reg) printf("%s = ", reg); | |
384 | printf(""P_HEX_GP"\n", GP(f)); | |
385 | printf(";;\t\tstatus: %ccf %cpf %caf %czf %csf %cdf %cof\n", | |
386 | (GP(f) >> 0)&1u ? '+' : '-', | |
387 | (GP(f) >> 2)&1u ? '+' : '-', | |
388 | (GP(f) >> 4)&1u ? '+' : '-', | |
389 | (GP(f) >> 6)&1u ? '+' : '-', | |
390 | (GP(f) >> 7)&1u ? '+' : '-', | |
391 | (GP(f) >> 10)&1u ? '+' : '-', | |
392 | (GP(f) >> 11)&1u ? '+' : '-'); | |
393 | printf(";;\t\tsystem: %ctf %cif iopl=%d %cnt " | |
394 | "%crf %cvm %cac %cvif %cvip %cid\n", | |
395 | (GP(f) >> 8)&1u ? '+' : '-', | |
396 | (GP(f) >> 9)&1u ? '+' : '-', | |
397 | (int)((GP(f) >> 12)&1u), | |
398 | (GP(f) >> 14)&1u ? '+' : '-', | |
399 | (GP(f) >> 16)&1u ? '+' : '-', | |
400 | (GP(f) >> 17)&1u ? '+' : '-', | |
401 | (GP(f) >> 18)&1u ? '+' : '-', | |
402 | (GP(f) >> 19)&1u ? '+' : '-', | |
403 | (GP(f) >> 20)&1u ? '+' : '-', | |
404 | (GP(f) >> 21)&1u ? '+' : '-'); | |
405 | } | |
406 | ||
407 | static const char | |
408 | *pcmap[] = { "sgl", "???", "dbl", "ext" }, | |
409 | *rcmap[] = { "nr", "-∞", "+∞", "0" }; | |
410 | ||
411 | static void dump_fpflags(const char *lbl, const struct fxsave *fx) | |
412 | { | |
413 | unsigned top = (fx->fsw >> 11)&7u; | |
414 | unsigned tag = fx->ftw; | |
415 | int skip = lbl ? strlen(lbl) + 2 : 0; | |
416 | ||
417 | printf(";; "); | |
418 | if (lbl) printf("%s: ", lbl); | |
419 | ||
420 | printf(" fcw = 0x%04x: " | |
421 | "%cim %cdm %czm %com %cum %cpm pc=%s rc=%s %cx\n", | |
422 | fx->fcw, | |
423 | (fx->fcw >> 0)&1u ? '+' : '-', | |
424 | (fx->fcw >> 1)&1u ? '+' : '-', | |
425 | (fx->fcw >> 2)&1u ? '+' : '-', | |
426 | (fx->fcw >> 3)&1u ? '+' : '-', | |
427 | (fx->fcw >> 4)&1u ? '+' : '-', | |
428 | (fx->fcw >> 5)&1u ? '+' : '-', | |
429 | pcmap[(fx->fcw >> 8)&3u], | |
430 | rcmap[(fx->fcw >> 10)&3u], | |
431 | (fx->fcw >> 12)&1u ? '+' : '-'); | |
432 | printf(";; %*s fsw = 0x%04x: " | |
433 | "%cie %cde %cze %coe %cue %cpe %csf %ces %cc0 %cc1 %cc2 %cc3 " | |
434 | "top=%d %cb\n", | |
435 | skip, "", | |
436 | fx->fsw, | |
437 | (fx->fsw >> 0)&1u ? '+' : '-', | |
438 | (fx->fsw >> 1)&1u ? '+' : '-', | |
439 | (fx->fsw >> 2)&1u ? '+' : '-', | |
440 | (fx->fsw >> 3)&1u ? '+' : '-', | |
441 | (fx->fsw >> 4)&1u ? '+' : '-', | |
442 | (fx->fsw >> 5)&1u ? '+' : '-', | |
443 | (fx->fsw >> 6)&1u ? '+' : '-', | |
444 | (fx->fsw >> 7)&1u ? '+' : '-', | |
445 | (fx->fsw >> 8)&1u ? '+' : '-', | |
446 | (fx->fsw >> 9)&1u ? '+' : '-', | |
447 | (fx->fsw >> 10)&1u ? '+' : '-', | |
448 | (fx->fsw >> 14)&1u ? '+' : '-', | |
449 | top, | |
450 | (fx->fsw >> 15)&1u ? '+' : '-'); | |
451 | printf(";; %*s ftw = 0x%02x\n", skip, "", tag); | |
452 | } | |
453 | ||
454 | static void dump_mxflags(const char *lbl, const struct fxsave *fx) | |
455 | { | |
456 | printf(";; "); | |
457 | if (lbl) printf("%s: ", lbl); | |
458 | ||
459 | printf(" mxcsr = 0x%08x\n" | |
460 | ";;\t\tmask = %cim %cdm %czm %com %cum %cpm\n" | |
461 | ";;\t\t exc = %cie %cde %cze %coe %cue %cpe\n" | |
462 | ";;\t\tmisc = %cdaz %cftz rc=%s\n", | |
463 | fx->mxcsr, | |
464 | (fx->mxcsr >> 7)&1u ? '+' : '-', | |
465 | (fx->mxcsr >> 8)&1u ? '+' : '-', | |
466 | (fx->mxcsr >> 9)&1u ? '+' : '-', | |
467 | (fx->mxcsr >> 10)&1u ? '+' : '-', | |
468 | (fx->mxcsr >> 11)&1u ? '+' : '-', | |
469 | (fx->mxcsr >> 12)&1u ? '+' : '-', | |
470 | (fx->mxcsr >> 0)&1u ? '+' : '-', | |
471 | (fx->mxcsr >> 1)&1u ? '+' : '-', | |
472 | (fx->mxcsr >> 2)&1u ? '+' : '-', | |
473 | (fx->mxcsr >> 3)&1u ? '+' : '-', | |
474 | (fx->mxcsr >> 4)&1u ? '+' : '-', | |
475 | (fx->mxcsr >> 5)&1u ? '+' : '-', | |
476 | (fx->mxcsr >> 6)&1u ? '+' : '-', | |
477 | (fx->mxcsr >> 15)&1u ? '+' : '-', | |
478 | rcmap[(fx->mxcsr >> 13)&3u]); | |
479 | } | |
480 | ||
481 | #if CPUFAM_X86 | |
482 | # define REGF_GPWD REGF_32 | |
483 | #endif | |
484 | #if CPUFAM_AMD64 | |
485 | # define REGF_GPWD REGF_64 | |
486 | #endif | |
487 | ||
488 | void regdump_gp(const struct regmap *map) | |
489 | { | |
490 | unsigned i; | |
491 | ||
492 | printf(";; General-purpose registers:\n"); | |
493 | for (i = REGIX_AX; i < REGIX_GPLIM; i++) | |
494 | regdump(map, 0, | |
495 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_GPWD | REGSRC_GP | i); | |
496 | regdump(map, 0, REGF_HEX | REGF_GPWD | REGSRC_GP | REGIX_IP); | |
497 | ||
498 | printf(";; Segment registers:\n"); | |
499 | for (i = 0; i < REGIX_SEGLIM; i++) | |
500 | regdump(map, 0, REGF_HEX | REGF_16 | REGSRC_SEG | i); | |
501 | ||
502 | printf(";; Flags:\n"); | |
503 | regdump(map, 0, REGSRC_GP | REGF_GPWD | REGIX_FLAGS); | |
504 | } | |
505 | ||
506 | void regdump_fp(const struct regmap *map) | |
507 | { | |
508 | unsigned top = (map->fx->fsw >> 11)&7u; | |
509 | unsigned tag = map->fx->ftw; | |
510 | unsigned i; | |
511 | ||
512 | printf(";; Floating-point/MMX registers:\n"); | |
513 | if (!top && tag == 0xff) | |
514 | for (i = 0; i < 8; i++) | |
515 | regdump(map, 0, | |
516 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_CHR | | |
517 | REGF_32 | REGF_16 | REGF_8 | | |
518 | REGSRC_STMMX | i | (6 << REGF_WDSHIFT)); | |
519 | if (tag) | |
520 | for (i = 0; i < 8; i++) | |
521 | regdump(map, 0, REGF_FLT | REGF_80 | REGSRC_STMMX | i); | |
522 | ||
523 | printf(";; Floating-point state:\n"); | |
524 | dump_fpflags(0, map->fx); | |
525 | } | |
526 | ||
527 | void regdump_simd(const struct regmap *map) | |
528 | { | |
529 | unsigned f = REGF_HEX | REGF_FLT | REGF_UNSGN | REGF_SGN | REGF_CHR | | |
530 | REGF_64 | REGF_32 | REGF_16 | REGF_8 | | |
531 | REGSRC_SIMD; | |
532 | unsigned i; | |
533 | ||
534 | if (map->avx) f |= 8 << REGF_WDSHIFT; | |
535 | else f |= 7 << REGF_WDSHIFT; | |
536 | ||
537 | printf(";; SSE/AVX registers:\n"); | |
538 | for (i = 0; i < N(map->fx->xmm); i++) | |
539 | regdump(map, 0, f | i); | |
540 | ||
541 | printf(";; SSE/AVX floating-point state:\n"); | |
542 | dump_mxflags(0, map->fx); | |
543 | } | |
544 | ||
545 | #endif | |
546 | ||
547 | /*----- ARM32 -------------------------------------------------------------*/ | |
548 | ||
549 | #if CPUFAM_ARMEL | |
550 | ||
551 | unsigned regdump__flags = 0; | |
552 | ||
553 | void regdump_init(void) | |
554 | { | |
555 | if (cpu_feature_p(CPUFEAT_ARM_VFP)) regdump__flags |= REGF_VFP; | |
556 | if (cpu_feature_p(CPUFEAT_ARM_D32)) regdump__flags |= REGF_D32; | |
557 | } | |
558 | ||
559 | static void dump_flags(const char *lbl, unsigned f) | |
560 | { | |
561 | static const char | |
562 | *modetab[] = { "?00", "?01", "?02", "?03", "?04", "?05", "?06", "?07", | |
563 | "?08", "?09", "?10", "?11", "?12", "?13", "?14", "?15", | |
564 | "usr", "fiq", "irq", "svc", "?20", "?21", "mon", "abt", | |
565 | "?24", "?25", "hyp", "und", "?28", "?29", "?30", "sys" }, | |
566 | *condtab[] = { "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", | |
567 | "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" }; | |
568 | ||
569 | printf(";; "); | |
570 | if (lbl) printf("%s: ", lbl); | |
571 | printf(" cpsr = 0x%08x\n", f); | |
572 | printf(";;\t\tuser: %cn %cz %cc %cv %cq ge=%c%c%c%c\n", | |
573 | (f >> 31)&1u ? '+' : '-', | |
574 | (f >> 30)&1u ? '+' : '-', | |
575 | (f >> 29)&1u ? '+' : '-', | |
576 | (f >> 28)&1u ? '+' : '-', | |
577 | (f >> 27)&1u ? '+' : '-', | |
578 | (f >> 19)&1u ? '1' : '0', | |
579 | (f >> 18)&1u ? '1' : '0', | |
580 | (f >> 17)&1u ? '1' : '0', | |
581 | (f >> 16)&1u ? '1' : '0'); | |
582 | printf(";;\t\tsystem: %cj it=%s:%c%c%c%c %ce %ca %ci %cf %ct m=%s\n", | |
583 | (f >> 24)&1u ? '+' : '-', | |
584 | condtab[(f >> 12)&15u], | |
585 | (f >> 11)&1u ? '1' : '0', | |
586 | (f >> 10)&1u ? '1' : '0', | |
587 | (f >> 26)&1u ? '1' : '0', | |
588 | (f >> 25)&1u ? '1' : '0', | |
589 | (f >> 9)&1u ? '+' : '-', | |
590 | (f >> 8)&1u ? '+' : '-', | |
591 | (f >> 7)&1u ? '+' : '-', | |
592 | (f >> 6)&1u ? '+' : '-', | |
593 | (f >> 5)&1u ? '+' : '-', | |
594 | modetab[(f >> 0)&31u]); | |
595 | } | |
596 | ||
597 | static void dump_fpflags(const char *lbl, unsigned f) | |
598 | { | |
599 | static const char *rcmap[] = { "nr", "+∞", "-∞", "0" }; | |
600 | ||
601 | printf(";; "); | |
602 | if (lbl) printf("%s: ", lbl); | |
603 | printf(" fpscr = 0x%08x\n", f); | |
604 | printf(";;\t\tcond: %cn %cz %cc %cv %cqc\n", | |
605 | (f >> 31)&1u ? '+' : '-', | |
606 | (f >> 30)&1u ? '+' : '-', | |
607 | (f >> 29)&1u ? '+' : '-', | |
608 | (f >> 28)&1u ? '+' : '-', | |
609 | (f >> 27)&1u ? '+' : '-'); | |
610 | printf(";;\t\ttrap: %cide %cixe %cufe %cofe %cdze %cioe\n", | |
611 | (f >> 15)&1u ? '+' : '-', | |
612 | (f >> 12)&1u ? '+' : '-', | |
613 | (f >> 11)&1u ? '+' : '-', | |
614 | (f >> 10)&1u ? '+' : '-', | |
615 | (f >> 9)&1u ? '+' : '-', | |
616 | (f >> 8)&1u ? '+' : '-'); | |
617 | printf(";;\t\terror: %cide %cixe %cufe %cofe %cdze %cioe\n", | |
618 | (f >> 7)&1u ? '+' : '-', | |
619 | (f >> 4)&1u ? '+' : '-', | |
620 | (f >> 3)&1u ? '+' : '-', | |
621 | (f >> 2)&1u ? '+' : '-', | |
622 | (f >> 1)&1u ? '+' : '-', | |
623 | (f >> 0)&1u ? '+' : '-'); | |
624 | printf(";;\t\tcontrol: %cahp %cdn %cfz rm=%s str=%d len=%d\n", | |
625 | (f >> 26)&1u ? '+' : '-', | |
626 | (f >> 25)&1u ? '+' : '-', | |
627 | (f >> 24)&1u ? '+' : '-', | |
628 | rcmap[(f >> 22)&3u], | |
629 | (f >> 20)&3u, | |
630 | (f >> 16)&7u); | |
631 | } | |
632 | ||
633 | void regdump_gp(const struct regmap *map) | |
634 | { | |
635 | unsigned i; | |
636 | ||
637 | printf(";; General-purpose registers:\n"); | |
638 | for (i = 0; i < 16; i++) | |
639 | regdump(map, 0, | |
640 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_32 | REGSRC_GP | i); | |
641 | ||
642 | printf(";; Flags:\n"); | |
643 | regdump(map, 0, REGSRC_GP | REGF_32 | REGIX_CPSR); | |
644 | } | |
645 | ||
646 | void regdump_fp(const struct regmap *map) | |
647 | { | |
648 | unsigned i, n; | |
649 | ||
650 | if (!(regdump__flags®F_VFP)) { | |
651 | printf(";; Floating-point and SIMD not available\n"); | |
652 | return; | |
653 | } | |
654 | ||
655 | printf(";; Floating-point/SIMD registers:\n"); | |
656 | if (regdump__flags®F_D32) n = 32; | |
657 | else n = 16; | |
658 | for (i = 0; i < n; i++) | |
659 | regdump(map, 0, | |
660 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_FLT | REGF_CHR | | |
661 | REGF_64 | REGF_32 | REGF_16 | REGF_8 | | |
24099531 | 662 | REGSRC_FP | i | (6 << REGF_WDSHIFT)); |
4bc8424a MW |
663 | |
664 | printf(";; Floating-point state:\n"); | |
665 | dump_fpflags(0, map->fp->fpscr); | |
666 | } | |
667 | ||
668 | void regdump_simd(const struct regmap *map) { ; } | |
669 | ||
670 | #endif | |
671 | ||
672 | /*----- ARM64 -------------------------------------------------------------*/ | |
673 | ||
674 | #if CPUFAM_ARM64 | |
675 | ||
676 | void regdump_init(void) { ; } | |
677 | ||
678 | static void dump_flags(const char *lbl, unsigned f) | |
679 | { | |
680 | printf(";; "); | |
681 | if (lbl) printf("%s: ", lbl); | |
682 | printf(" nzcv = 0x%08x\n", f); | |
683 | printf(";;\t\tuser: %cn %cz %cc %cv\n", | |
684 | (f >> 31)&1u ? '+' : '-', | |
685 | (f >> 30)&1u ? '+' : '-', | |
686 | (f >> 29)&1u ? '+' : '-', | |
687 | (f >> 28)&1u ? '+' : '-'); | |
688 | } | |
689 | ||
690 | static void dump_fpflags(const char *lbl, const struct fpsave *fp) | |
691 | { | |
692 | static const char *rcmap[] = { "nr", "+∞", "-∞", "0" }; | |
693 | int skip = lbl ? strlen(lbl) + 2 : 0; | |
694 | ||
695 | printf(";; "); | |
696 | if (lbl) printf("%s: ", lbl); | |
697 | printf(" fpsr = 0x%08x\n", fp->fpsr); | |
698 | printf(";;\t\tcond: %cn %cz %cc %cv %cqc\n", | |
699 | (fp->fpsr >> 31)&1u ? '+' : '-', | |
700 | (fp->fpsr >> 30)&1u ? '+' : '-', | |
701 | (fp->fpsr >> 29)&1u ? '+' : '-', | |
702 | (fp->fpsr >> 28)&1u ? '+' : '-', | |
703 | (fp->fpsr >> 27)&1u ? '+' : '-'); | |
704 | printf(";;\t\terror: %cidc %cixc %cufc %cofc %cdzc %cioc\n", | |
705 | (fp->fpsr >> 7)&1u ? '+' : '-', | |
706 | (fp->fpsr >> 4)&1u ? '+' : '-', | |
707 | (fp->fpsr >> 3)&1u ? '+' : '-', | |
708 | (fp->fpsr >> 2)&1u ? '+' : '-', | |
709 | (fp->fpsr >> 1)&1u ? '+' : '-', | |
710 | (fp->fpsr >> 0)&1u ? '+' : '-'); | |
711 | printf(";; %*s fpcr = 0x%08x\n", skip, "", fp->fpcr); | |
712 | printf(";;\t\ttrap: %cide %cixe %cufe %cofe %cdze %cioe\n", | |
713 | (fp->fpcr >> 15)&1u ? '+' : '-', | |
714 | (fp->fpcr >> 12)&1u ? '+' : '-', | |
715 | (fp->fpcr >> 11)&1u ? '+' : '-', | |
716 | (fp->fpcr >> 10)&1u ? '+' : '-', | |
717 | (fp->fpcr >> 9)&1u ? '+' : '-', | |
718 | (fp->fpcr >> 8)&1u ? '+' : '-'); | |
719 | printf(";;\t\tcontrol: %cahp %cdn %cfz rm=%s str=%d len=%d\n", | |
720 | (fp->fpcr >> 26)&1u ? '+' : '-', | |
721 | (fp->fpcr >> 25)&1u ? '+' : '-', | |
722 | (fp->fpcr >> 24)&1u ? '+' : '-', | |
723 | rcmap[(fp->fpcr >> 22)&3u], | |
724 | (fp->fpcr >> 20)&3u, | |
725 | (fp->fpcr >> 16)&7u); | |
726 | } | |
727 | ||
728 | void regdump_gp(const struct regmap *map) | |
729 | { | |
730 | unsigned i; | |
731 | ||
732 | printf(";; General-purpose registers:\n"); | |
733 | for (i = 0; i < 32; i++) | |
734 | regdump(map, 0, | |
735 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_64 | REGSRC_GP | i); | |
736 | regdump(map, 0, REGF_HEX | REGF_64 | REGSRC_GP | REGIX_PC); | |
737 | ||
738 | printf(";; Flags:\n"); | |
739 | regdump(map, 0, REGSRC_GP | REGF_32 | REGIX_NZCV); | |
740 | } | |
741 | ||
742 | void regdump_fp(const struct regmap *map) | |
743 | { | |
744 | unsigned i; | |
745 | ||
746 | printf(";; Floating-point/SIMD registers:\n"); | |
747 | for (i = 0; i < 32; i++) | |
748 | regdump(map, 0, | |
749 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_FLT | REGF_CHR | | |
750 | REGF_64 | REGF_32 | REGF_16 | REGF_8 | | |
751 | REGSRC_SIMD | i | (7 << REGF_WDSHIFT)); | |
752 | ||
753 | printf(";; Floating-point state:\n"); | |
754 | dump_fpflags(0, map->fp); | |
755 | } | |
756 | ||
757 | void regdump_simd(const struct regmap *map) { ; } | |
758 | ||
759 | #endif | |
760 | ||
761 | /*----- The main entry point ----------------------------------------------*/ | |
762 | ||
763 | /* --- @regdump@ --- * | |
764 | * | |
765 | * Arguments: @const void *base@ = pointer to base structure, corresponding | |
766 | * to the @REGF_SRCMASK@ part of @f@ | |
767 | * @const char *lbl@ = label to print | |
768 | * @uint32 f@ = format control word; see @REGF_...@ | |
769 | * | |
770 | * Returns: --- | |
771 | * | |
772 | * Use: Dump a register value, or chunk of memory. | |
773 | * | |
774 | * This function is not usually called directly; instead, use | |
775 | * the `reg' or `mem' assembler macros. | |
776 | */ | |
777 | ||
778 | void regdump(const void *base, const char *lbl, uint32 f) | |
779 | { | |
780 | unsigned ix = (f®F_IXMASK) >> REGF_IXSHIFT; | |
781 | unsigned wd = 1 << ((f®F_WDMASK) >> REGF_WDSHIFT); | |
782 | unsigned fmt, ty; | |
783 | uint32 fmtbit, tybit; | |
784 | const void *p; | |
785 | char regbuf[8]; const char *reg = regname(regbuf, f); | |
786 | const struct regmap *map; | |
787 | const struct fmttab *tab; | |
788 | struct fmtinfo fi; | |
789 | int firstp = 1; | |
790 | int skip; | |
791 | size_t n; | |
792 | ||
793 | #if CPUFAM_X86 || CPUFAM_AMD64 | |
794 | union vreg vr; | |
795 | #endif | |
796 | ||
797 | if (reg) { | |
798 | n = strlen(reg); | |
799 | if (n < 7) { | |
800 | memmove(regbuf + 7 - n, reg, n + 1); | |
801 | memset(regbuf, ' ', 7 - n); | |
802 | } | |
803 | } | |
804 | ||
805 | switch (f®F_SRCMASK) { | |
806 | case REGSRC_ABS: | |
807 | p = base; | |
808 | break; | |
809 | ||
810 | #if CPUFAM_X86 || CPUFAM_AMD64 | |
811 | case REGSRC_GP: | |
812 | map = (const struct regmap *)base; | |
813 | if (ix == REGIX_FLAGS && !(f®F_FMTMASK)) | |
814 | { dump_flags(lbl, reg, map->gp->gp[REGIX_FLAGS]); return; } | |
815 | p = &map->gp->gp[ix]; | |
816 | break; | |
817 | case REGSRC_SEG: | |
818 | map = (const struct regmap *)base; | |
819 | assert(wd == 1); assert((f®F_TYMASK) == REGF_16); | |
820 | p = &map->gp->seg[ix]; | |
821 | break; | |
822 | case REGSRC_STMMX: | |
823 | map = (const struct regmap *)base; | |
824 | if (ix == REGIX_FPFLAGS) | |
825 | { assert(!(f®F_FMTMASK)); dump_fpflags(lbl, map->fx); return; } | |
826 | if (!((map->fx->ftw << ix)&128u)) { | |
827 | printf(";; "); | |
828 | if (lbl) printf("%s: ", lbl); | |
829 | if (reg) printf("%s = ", reg); | |
830 | printf(" dead\n"); | |
831 | return; | |
832 | } | |
833 | p = &map->fx->stmmx[ix]; | |
834 | break; | |
835 | case REGSRC_SIMD: | |
836 | map = (const struct regmap *)base; | |
837 | if (ix == REGIX_FPFLAGS) | |
838 | { assert(!(f®F_FMTMASK)); dump_mxflags(lbl, map->fx); return; } | |
839 | if (wd <= 128) | |
840 | p = &map->fx->xmm[ix]; | |
841 | else { | |
842 | vr.v128[0] = map->fx->xmm[ix]; | |
843 | vr.v128[1] = map->avx->ymmh[ix]; | |
844 | assert(wd == 256); | |
845 | p = &vr; | |
846 | } | |
847 | break; | |
848 | #endif | |
849 | ||
850 | #if CPUFAM_ARMEL | |
851 | case REGSRC_GP: | |
852 | map = (const struct regmap *)base; | |
853 | if (ix == REGIX_CPSR && !(f®F_FMTMASK)) | |
854 | { dump_flags(lbl, map->gp->r[REGIX_CPSR].u32); return; } | |
855 | p = &map->gp->r[ix]; | |
856 | break; | |
857 | case REGSRC_FP: | |
858 | case REGSRC_SIMD: | |
859 | map = (const struct regmap *)base; | |
b26c7863 MW |
860 | if (!map->fp) { |
861 | printf(";;"); | |
862 | if (lbl) printf(" %s:", lbl); | |
863 | if (reg) printf(" %s =", reg); | |
864 | printf(" #<not available -- regdump_init?>\n"); | |
865 | return; | |
866 | } | |
4bc8424a MW |
867 | if (ix == REGIX_FPSCR) { |
868 | assert(!(f®F_FMTMASK)); | |
869 | dump_fpflags(lbl, map->fp->fpscr); | |
870 | return; | |
871 | } | |
872 | switch (regwd(f)) { | |
873 | case 32: p = &map->fp->u.s[ix]; break; | |
874 | case 64: p = &map->fp->u.d[ix]; break; | |
875 | case 128: p = &map->fp->u.q[ix]; break; | |
876 | default: assert(0); | |
877 | } | |
878 | break; | |
879 | #endif | |
880 | ||
881 | #if CPUFAM_ARM64 | |
882 | case REGSRC_GP: | |
883 | map = (const struct regmap *)base; | |
884 | if (ix == REGIX_NZCV && !(f®F_FMTMASK)) | |
885 | { dump_flags(lbl, map->gp->r[REGIX_NZCV].u64); return; } | |
886 | p = &map->gp->r[ix]; | |
887 | break; | |
888 | case REGSRC_FP: | |
889 | case REGSRC_SIMD: | |
890 | map = (const struct regmap *)base; | |
891 | if (ix == REGIX_FPFLAGS) | |
892 | { assert(!(f®F_FMTMASK)); dump_fpflags(lbl, map->fp); return; } | |
893 | p = &map->fp->v[ix]; | |
894 | break; | |
895 | #endif | |
896 | ||
897 | default: | |
898 | assert(0); | |
899 | } | |
900 | ||
901 | skip = (lbl ? strlen(lbl) + 2 : 0) + (reg ? strlen(reg) : 0); | |
902 | fi.f = 0; if (wd > 1) fi.f |= FMTF_VECTOR; | |
903 | ||
904 | for (ty = (f®F_TYMASK) >> REGF_TYSHIFT, | |
905 | tybit = 1 << REGF_TYSHIFT; | |
906 | ty; | |
907 | ty >>= 1, tybit <<= 1) { | |
908 | if (!(ty&1u)) continue; | |
909 | ||
910 | for (fmt = (f®F_FMTMASK) >> REGF_FMTSHIFT, | |
911 | fmtbit = 1 << REGF_FMTSHIFT; | |
912 | fmt; | |
913 | fmt >>= 1, fmtbit <<= 1) { | |
914 | ||
915 | if (!(fmt&1u)) continue; | |
916 | ||
917 | for (tab = fmttab; tab->mask; tab++) | |
918 | if (tab->mask == (fmtbit | tybit)) goto found; | |
919 | continue; | |
920 | found: | |
921 | ||
922 | if (firstp) { | |
923 | printf(";;"); | |
924 | if (lbl) printf(" %s:", lbl); | |
925 | if (reg) printf(" %s =", reg); | |
926 | firstp = 0; | |
927 | } else if (wd > 1) | |
928 | printf("\n;; %*s =", skip, ""); | |
929 | else | |
930 | fputs(" =", stdout); | |
931 | ||
932 | fi.p = p; fi.wd = 0; | |
933 | while (fi.wd < wd) { putchar(' '); tab->fmt(&fi); } | |
934 | } | |
935 | } | |
936 | putchar('\n'); | |
937 | } | |
938 | ||
939 | /*----- Other random utilities --------------------------------------------*/ | |
940 | ||
941 | /* --- @regdump_freshline@ --- * | |
942 | * | |
943 | * Arguments: --- | |
944 | * | |
945 | * Returns: --- | |
946 | * | |
947 | * Use: Begin a fresh line of output. | |
948 | */ | |
949 | ||
950 | void regdump_freshline(void) { putchar('\n'); } | |
951 | ||
952 | /*----- That's all, folks -------------------------------------------------*/ |