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 | ||
05671f2d MW |
378 | #define CF (1 << 0) |
379 | #define PF (1 << 2) | |
380 | #define AF (1 << 4) | |
381 | #define ZF (1 << 6) | |
382 | #define SF (1 << 7) | |
383 | #define DF (1 << 10) | |
384 | #define OF (1 << 11) | |
385 | ||
4bc8424a MW |
386 | void regdump_init(void) { ; } |
387 | ||
388 | static void dump_flags(const char *lbl, const char *reg, gpreg f) | |
389 | { | |
390 | printf(";; "); | |
391 | if (lbl) printf("%s: ", lbl); | |
392 | if (reg) printf("%s = ", reg); | |
393 | printf(""P_HEX_GP"\n", GP(f)); | |
394 | printf(";;\t\tstatus: %ccf %cpf %caf %czf %csf %cdf %cof\n", | |
395 | (GP(f) >> 0)&1u ? '+' : '-', | |
396 | (GP(f) >> 2)&1u ? '+' : '-', | |
397 | (GP(f) >> 4)&1u ? '+' : '-', | |
398 | (GP(f) >> 6)&1u ? '+' : '-', | |
399 | (GP(f) >> 7)&1u ? '+' : '-', | |
400 | (GP(f) >> 10)&1u ? '+' : '-', | |
401 | (GP(f) >> 11)&1u ? '+' : '-'); | |
05671f2d MW |
402 | printf(";;\t\tcond:"); |
403 | if (GP(f)&CF) printf(" c/b/nae"); else printf(" nc/ae/nb"); | |
404 | if (GP(f)&ZF) printf(" e/z"); else printf(" ne/nz"); | |
405 | if (GP(f)&SF) printf(" s"); else printf(" ns"); | |
406 | if (GP(f)&OF) printf(" o"); else printf(" no"); | |
407 | if (GP(f)&PF) printf(" p"); else printf(" np"); | |
408 | if ((GP(f)&CF) || (GP(f)&ZF)) printf(" be/na"); else printf(" a/nbe"); | |
409 | if (!(GP(f)&OF) == !(GP(f)&SF)) printf(" ge/nl"); else printf(" l/nge"); | |
410 | if (!(GP(f)&OF) == !(GP(f)&SF) && !(GP(f)&ZF)) | |
411 | printf(" g/nle"); else printf(" le/ng"); | |
412 | putchar('\n'); | |
4bc8424a MW |
413 | printf(";;\t\tsystem: %ctf %cif iopl=%d %cnt " |
414 | "%crf %cvm %cac %cvif %cvip %cid\n", | |
415 | (GP(f) >> 8)&1u ? '+' : '-', | |
416 | (GP(f) >> 9)&1u ? '+' : '-', | |
417 | (int)((GP(f) >> 12)&1u), | |
418 | (GP(f) >> 14)&1u ? '+' : '-', | |
419 | (GP(f) >> 16)&1u ? '+' : '-', | |
420 | (GP(f) >> 17)&1u ? '+' : '-', | |
421 | (GP(f) >> 18)&1u ? '+' : '-', | |
422 | (GP(f) >> 19)&1u ? '+' : '-', | |
423 | (GP(f) >> 20)&1u ? '+' : '-', | |
424 | (GP(f) >> 21)&1u ? '+' : '-'); | |
425 | } | |
426 | ||
427 | static const char | |
428 | *pcmap[] = { "sgl", "???", "dbl", "ext" }, | |
429 | *rcmap[] = { "nr", "-∞", "+∞", "0" }; | |
430 | ||
431 | static void dump_fpflags(const char *lbl, const struct fxsave *fx) | |
432 | { | |
433 | unsigned top = (fx->fsw >> 11)&7u; | |
434 | unsigned tag = fx->ftw; | |
435 | int skip = lbl ? strlen(lbl) + 2 : 0; | |
436 | ||
437 | printf(";; "); | |
438 | if (lbl) printf("%s: ", lbl); | |
439 | ||
440 | printf(" fcw = 0x%04x: " | |
441 | "%cim %cdm %czm %com %cum %cpm pc=%s rc=%s %cx\n", | |
442 | fx->fcw, | |
443 | (fx->fcw >> 0)&1u ? '+' : '-', | |
444 | (fx->fcw >> 1)&1u ? '+' : '-', | |
445 | (fx->fcw >> 2)&1u ? '+' : '-', | |
446 | (fx->fcw >> 3)&1u ? '+' : '-', | |
447 | (fx->fcw >> 4)&1u ? '+' : '-', | |
448 | (fx->fcw >> 5)&1u ? '+' : '-', | |
449 | pcmap[(fx->fcw >> 8)&3u], | |
450 | rcmap[(fx->fcw >> 10)&3u], | |
451 | (fx->fcw >> 12)&1u ? '+' : '-'); | |
452 | printf(";; %*s fsw = 0x%04x: " | |
453 | "%cie %cde %cze %coe %cue %cpe %csf %ces %cc0 %cc1 %cc2 %cc3 " | |
454 | "top=%d %cb\n", | |
455 | skip, "", | |
456 | fx->fsw, | |
457 | (fx->fsw >> 0)&1u ? '+' : '-', | |
458 | (fx->fsw >> 1)&1u ? '+' : '-', | |
459 | (fx->fsw >> 2)&1u ? '+' : '-', | |
460 | (fx->fsw >> 3)&1u ? '+' : '-', | |
461 | (fx->fsw >> 4)&1u ? '+' : '-', | |
462 | (fx->fsw >> 5)&1u ? '+' : '-', | |
463 | (fx->fsw >> 6)&1u ? '+' : '-', | |
464 | (fx->fsw >> 7)&1u ? '+' : '-', | |
465 | (fx->fsw >> 8)&1u ? '+' : '-', | |
466 | (fx->fsw >> 9)&1u ? '+' : '-', | |
467 | (fx->fsw >> 10)&1u ? '+' : '-', | |
468 | (fx->fsw >> 14)&1u ? '+' : '-', | |
469 | top, | |
470 | (fx->fsw >> 15)&1u ? '+' : '-'); | |
471 | printf(";; %*s ftw = 0x%02x\n", skip, "", tag); | |
472 | } | |
473 | ||
474 | static void dump_mxflags(const char *lbl, const struct fxsave *fx) | |
475 | { | |
476 | printf(";; "); | |
477 | if (lbl) printf("%s: ", lbl); | |
478 | ||
479 | printf(" mxcsr = 0x%08x\n" | |
480 | ";;\t\tmask = %cim %cdm %czm %com %cum %cpm\n" | |
481 | ";;\t\t exc = %cie %cde %cze %coe %cue %cpe\n" | |
482 | ";;\t\tmisc = %cdaz %cftz rc=%s\n", | |
483 | fx->mxcsr, | |
484 | (fx->mxcsr >> 7)&1u ? '+' : '-', | |
485 | (fx->mxcsr >> 8)&1u ? '+' : '-', | |
486 | (fx->mxcsr >> 9)&1u ? '+' : '-', | |
487 | (fx->mxcsr >> 10)&1u ? '+' : '-', | |
488 | (fx->mxcsr >> 11)&1u ? '+' : '-', | |
489 | (fx->mxcsr >> 12)&1u ? '+' : '-', | |
490 | (fx->mxcsr >> 0)&1u ? '+' : '-', | |
491 | (fx->mxcsr >> 1)&1u ? '+' : '-', | |
492 | (fx->mxcsr >> 2)&1u ? '+' : '-', | |
493 | (fx->mxcsr >> 3)&1u ? '+' : '-', | |
494 | (fx->mxcsr >> 4)&1u ? '+' : '-', | |
495 | (fx->mxcsr >> 5)&1u ? '+' : '-', | |
496 | (fx->mxcsr >> 6)&1u ? '+' : '-', | |
497 | (fx->mxcsr >> 15)&1u ? '+' : '-', | |
498 | rcmap[(fx->mxcsr >> 13)&3u]); | |
499 | } | |
500 | ||
501 | #if CPUFAM_X86 | |
502 | # define REGF_GPWD REGF_32 | |
503 | #endif | |
504 | #if CPUFAM_AMD64 | |
505 | # define REGF_GPWD REGF_64 | |
506 | #endif | |
507 | ||
508 | void regdump_gp(const struct regmap *map) | |
509 | { | |
510 | unsigned i; | |
511 | ||
512 | printf(";; General-purpose registers:\n"); | |
513 | for (i = REGIX_AX; i < REGIX_GPLIM; i++) | |
514 | regdump(map, 0, | |
515 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_GPWD | REGSRC_GP | i); | |
516 | regdump(map, 0, REGF_HEX | REGF_GPWD | REGSRC_GP | REGIX_IP); | |
517 | ||
518 | printf(";; Segment registers:\n"); | |
519 | for (i = 0; i < REGIX_SEGLIM; i++) | |
520 | regdump(map, 0, REGF_HEX | REGF_16 | REGSRC_SEG | i); | |
521 | ||
522 | printf(";; Flags:\n"); | |
523 | regdump(map, 0, REGSRC_GP | REGF_GPWD | REGIX_FLAGS); | |
524 | } | |
525 | ||
526 | void regdump_fp(const struct regmap *map) | |
527 | { | |
528 | unsigned top = (map->fx->fsw >> 11)&7u; | |
529 | unsigned tag = map->fx->ftw; | |
530 | unsigned i; | |
531 | ||
532 | printf(";; Floating-point/MMX registers:\n"); | |
533 | if (!top && tag == 0xff) | |
534 | for (i = 0; i < 8; i++) | |
535 | regdump(map, 0, | |
536 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_CHR | | |
537 | REGF_32 | REGF_16 | REGF_8 | | |
538 | REGSRC_STMMX | i | (6 << REGF_WDSHIFT)); | |
539 | if (tag) | |
540 | for (i = 0; i < 8; i++) | |
541 | regdump(map, 0, REGF_FLT | REGF_80 | REGSRC_STMMX | i); | |
542 | ||
543 | printf(";; Floating-point state:\n"); | |
544 | dump_fpflags(0, map->fx); | |
545 | } | |
546 | ||
547 | void regdump_simd(const struct regmap *map) | |
548 | { | |
549 | unsigned f = REGF_HEX | REGF_FLT | REGF_UNSGN | REGF_SGN | REGF_CHR | | |
550 | REGF_64 | REGF_32 | REGF_16 | REGF_8 | | |
551 | REGSRC_SIMD; | |
552 | unsigned i; | |
553 | ||
554 | if (map->avx) f |= 8 << REGF_WDSHIFT; | |
555 | else f |= 7 << REGF_WDSHIFT; | |
556 | ||
557 | printf(";; SSE/AVX registers:\n"); | |
558 | for (i = 0; i < N(map->fx->xmm); i++) | |
559 | regdump(map, 0, f | i); | |
560 | ||
561 | printf(";; SSE/AVX floating-point state:\n"); | |
562 | dump_mxflags(0, map->fx); | |
563 | } | |
564 | ||
565 | #endif | |
566 | ||
567 | /*----- ARM32 -------------------------------------------------------------*/ | |
568 | ||
569 | #if CPUFAM_ARMEL | |
570 | ||
05671f2d MW |
571 | #define NF (1u << 31) |
572 | #define ZF (1u << 30) | |
573 | #define CF (1u << 29) | |
574 | #define VF (1u << 28) | |
575 | ||
4bc8424a MW |
576 | unsigned regdump__flags = 0; |
577 | ||
578 | void regdump_init(void) | |
579 | { | |
580 | if (cpu_feature_p(CPUFEAT_ARM_VFP)) regdump__flags |= REGF_VFP; | |
581 | if (cpu_feature_p(CPUFEAT_ARM_D32)) regdump__flags |= REGF_D32; | |
582 | } | |
583 | ||
05671f2d MW |
584 | static void dump_conditions(unsigned f) |
585 | { | |
586 | if (f&NF) printf(" mi"); else printf(" pl"); | |
587 | if (f&ZF) printf(" eq"); else printf(" ne"); | |
588 | if (f&CF) printf(" cs/hs"); else printf(" cc/lo"); | |
589 | if (f&VF) printf(" vs"); else printf(" vc"); | |
590 | if ((f&CF) && !(f&ZF)) printf(" hi"); else printf(" ls"); | |
591 | if (!(f&VF) == !(f&NF)) printf(" ge"); else printf(" lt"); | |
592 | if (!(f&VF) == !(f&NF) && !(f&ZF)) printf(" gt"); else printf(" le"); | |
593 | } | |
594 | ||
4bc8424a MW |
595 | static void dump_flags(const char *lbl, unsigned f) |
596 | { | |
597 | static const char | |
598 | *modetab[] = { "?00", "?01", "?02", "?03", "?04", "?05", "?06", "?07", | |
599 | "?08", "?09", "?10", "?11", "?12", "?13", "?14", "?15", | |
600 | "usr", "fiq", "irq", "svc", "?20", "?21", "mon", "abt", | |
601 | "?24", "?25", "hyp", "und", "?28", "?29", "?30", "sys" }, | |
602 | *condtab[] = { "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", | |
603 | "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" }; | |
604 | ||
605 | printf(";; "); | |
606 | if (lbl) printf("%s: ", lbl); | |
607 | printf(" cpsr = 0x%08x\n", f); | |
05671f2d | 608 | printf(";;\t\tuser: %cn %cz %cc %cv %cq ge=%c%c%c%c;", |
4bc8424a MW |
609 | (f >> 31)&1u ? '+' : '-', |
610 | (f >> 30)&1u ? '+' : '-', | |
611 | (f >> 29)&1u ? '+' : '-', | |
612 | (f >> 28)&1u ? '+' : '-', | |
613 | (f >> 27)&1u ? '+' : '-', | |
614 | (f >> 19)&1u ? '1' : '0', | |
615 | (f >> 18)&1u ? '1' : '0', | |
616 | (f >> 17)&1u ? '1' : '0', | |
617 | (f >> 16)&1u ? '1' : '0'); | |
05671f2d | 618 | dump_conditions(f); putchar('\n'); |
4bc8424a MW |
619 | printf(";;\t\tsystem: %cj it=%s:%c%c%c%c %ce %ca %ci %cf %ct m=%s\n", |
620 | (f >> 24)&1u ? '+' : '-', | |
621 | condtab[(f >> 12)&15u], | |
622 | (f >> 11)&1u ? '1' : '0', | |
623 | (f >> 10)&1u ? '1' : '0', | |
624 | (f >> 26)&1u ? '1' : '0', | |
625 | (f >> 25)&1u ? '1' : '0', | |
626 | (f >> 9)&1u ? '+' : '-', | |
627 | (f >> 8)&1u ? '+' : '-', | |
628 | (f >> 7)&1u ? '+' : '-', | |
629 | (f >> 6)&1u ? '+' : '-', | |
630 | (f >> 5)&1u ? '+' : '-', | |
631 | modetab[(f >> 0)&31u]); | |
632 | } | |
633 | ||
634 | static void dump_fpflags(const char *lbl, unsigned f) | |
635 | { | |
636 | static const char *rcmap[] = { "nr", "+∞", "-∞", "0" }; | |
637 | ||
638 | printf(";; "); | |
639 | if (lbl) printf("%s: ", lbl); | |
640 | printf(" fpscr = 0x%08x\n", f); | |
05671f2d | 641 | printf(";;\t\tcond: %cn %cz %cc %cv %cqc;", |
4bc8424a MW |
642 | (f >> 31)&1u ? '+' : '-', |
643 | (f >> 30)&1u ? '+' : '-', | |
644 | (f >> 29)&1u ? '+' : '-', | |
645 | (f >> 28)&1u ? '+' : '-', | |
646 | (f >> 27)&1u ? '+' : '-'); | |
05671f2d | 647 | dump_conditions(f); putchar('\n'); |
4bc8424a MW |
648 | printf(";;\t\ttrap: %cide %cixe %cufe %cofe %cdze %cioe\n", |
649 | (f >> 15)&1u ? '+' : '-', | |
650 | (f >> 12)&1u ? '+' : '-', | |
651 | (f >> 11)&1u ? '+' : '-', | |
652 | (f >> 10)&1u ? '+' : '-', | |
653 | (f >> 9)&1u ? '+' : '-', | |
654 | (f >> 8)&1u ? '+' : '-'); | |
655 | printf(";;\t\terror: %cide %cixe %cufe %cofe %cdze %cioe\n", | |
656 | (f >> 7)&1u ? '+' : '-', | |
657 | (f >> 4)&1u ? '+' : '-', | |
658 | (f >> 3)&1u ? '+' : '-', | |
659 | (f >> 2)&1u ? '+' : '-', | |
660 | (f >> 1)&1u ? '+' : '-', | |
661 | (f >> 0)&1u ? '+' : '-'); | |
662 | printf(";;\t\tcontrol: %cahp %cdn %cfz rm=%s str=%d len=%d\n", | |
663 | (f >> 26)&1u ? '+' : '-', | |
664 | (f >> 25)&1u ? '+' : '-', | |
665 | (f >> 24)&1u ? '+' : '-', | |
666 | rcmap[(f >> 22)&3u], | |
667 | (f >> 20)&3u, | |
668 | (f >> 16)&7u); | |
669 | } | |
670 | ||
671 | void regdump_gp(const struct regmap *map) | |
672 | { | |
673 | unsigned i; | |
674 | ||
675 | printf(";; General-purpose registers:\n"); | |
676 | for (i = 0; i < 16; i++) | |
677 | regdump(map, 0, | |
678 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_32 | REGSRC_GP | i); | |
679 | ||
680 | printf(";; Flags:\n"); | |
681 | regdump(map, 0, REGSRC_GP | REGF_32 | REGIX_CPSR); | |
682 | } | |
683 | ||
684 | void regdump_fp(const struct regmap *map) | |
685 | { | |
686 | unsigned i, n; | |
687 | ||
688 | if (!(regdump__flags®F_VFP)) { | |
689 | printf(";; Floating-point and SIMD not available\n"); | |
690 | return; | |
691 | } | |
692 | ||
693 | printf(";; Floating-point/SIMD registers:\n"); | |
694 | if (regdump__flags®F_D32) n = 32; | |
695 | else n = 16; | |
696 | for (i = 0; i < n; i++) | |
697 | regdump(map, 0, | |
698 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_FLT | REGF_CHR | | |
699 | REGF_64 | REGF_32 | REGF_16 | REGF_8 | | |
24099531 | 700 | REGSRC_FP | i | (6 << REGF_WDSHIFT)); |
4bc8424a MW |
701 | |
702 | printf(";; Floating-point state:\n"); | |
703 | dump_fpflags(0, map->fp->fpscr); | |
704 | } | |
705 | ||
706 | void regdump_simd(const struct regmap *map) { ; } | |
707 | ||
708 | #endif | |
709 | ||
710 | /*----- ARM64 -------------------------------------------------------------*/ | |
711 | ||
712 | #if CPUFAM_ARM64 | |
713 | ||
05671f2d MW |
714 | #define NF (1u << 31) |
715 | #define ZF (1u << 30) | |
716 | #define CF (1u << 29) | |
717 | #define VF (1u << 28) | |
718 | ||
4bc8424a MW |
719 | void regdump_init(void) { ; } |
720 | ||
05671f2d MW |
721 | static void dump_conditions(unsigned f) |
722 | { | |
723 | if (f&NF) printf(" mi"); else printf(" pl"); | |
724 | if (f&ZF) printf(" eq"); else printf(" ne"); | |
725 | if (f&CF) printf(" cs/hs"); else printf(" cc/lo"); | |
726 | if (f&VF) printf(" vs"); else printf(" vc"); | |
727 | if ((f&CF) && !(f&ZF)) printf(" hi"); else printf(" ls"); | |
728 | if (!(f&VF) == !(f&NF)) printf(" ge"); else printf(" lt"); | |
729 | if (!(f&VF) == !(f&NF) && !(f&ZF)) printf(" gt"); else printf(" le"); | |
730 | } | |
731 | ||
4bc8424a MW |
732 | static void dump_flags(const char *lbl, unsigned f) |
733 | { | |
734 | printf(";; "); | |
735 | if (lbl) printf("%s: ", lbl); | |
736 | printf(" nzcv = 0x%08x\n", f); | |
05671f2d | 737 | printf(";;\t\tuser: %cn %cz %cc %cv;", |
4bc8424a MW |
738 | (f >> 31)&1u ? '+' : '-', |
739 | (f >> 30)&1u ? '+' : '-', | |
740 | (f >> 29)&1u ? '+' : '-', | |
741 | (f >> 28)&1u ? '+' : '-'); | |
05671f2d | 742 | dump_conditions(f); putchar('\n'); |
4bc8424a MW |
743 | } |
744 | ||
745 | static void dump_fpflags(const char *lbl, const struct fpsave *fp) | |
746 | { | |
747 | static const char *rcmap[] = { "nr", "+∞", "-∞", "0" }; | |
748 | int skip = lbl ? strlen(lbl) + 2 : 0; | |
749 | ||
750 | printf(";; "); | |
751 | if (lbl) printf("%s: ", lbl); | |
752 | printf(" fpsr = 0x%08x\n", fp->fpsr); | |
05671f2d | 753 | printf(";;\t\tcond_a32: %cn %cz %cc %cv %cqc;", |
4bc8424a MW |
754 | (fp->fpsr >> 31)&1u ? '+' : '-', |
755 | (fp->fpsr >> 30)&1u ? '+' : '-', | |
756 | (fp->fpsr >> 29)&1u ? '+' : '-', | |
757 | (fp->fpsr >> 28)&1u ? '+' : '-', | |
758 | (fp->fpsr >> 27)&1u ? '+' : '-'); | |
05671f2d | 759 | dump_conditions(fp->fpsr); putchar('\n'); |
4bc8424a MW |
760 | printf(";;\t\terror: %cidc %cixc %cufc %cofc %cdzc %cioc\n", |
761 | (fp->fpsr >> 7)&1u ? '+' : '-', | |
762 | (fp->fpsr >> 4)&1u ? '+' : '-', | |
763 | (fp->fpsr >> 3)&1u ? '+' : '-', | |
764 | (fp->fpsr >> 2)&1u ? '+' : '-', | |
765 | (fp->fpsr >> 1)&1u ? '+' : '-', | |
766 | (fp->fpsr >> 0)&1u ? '+' : '-'); | |
767 | printf(";; %*s fpcr = 0x%08x\n", skip, "", fp->fpcr); | |
768 | printf(";;\t\ttrap: %cide %cixe %cufe %cofe %cdze %cioe\n", | |
769 | (fp->fpcr >> 15)&1u ? '+' : '-', | |
770 | (fp->fpcr >> 12)&1u ? '+' : '-', | |
771 | (fp->fpcr >> 11)&1u ? '+' : '-', | |
772 | (fp->fpcr >> 10)&1u ? '+' : '-', | |
773 | (fp->fpcr >> 9)&1u ? '+' : '-', | |
774 | (fp->fpcr >> 8)&1u ? '+' : '-'); | |
775 | printf(";;\t\tcontrol: %cahp %cdn %cfz rm=%s str=%d len=%d\n", | |
776 | (fp->fpcr >> 26)&1u ? '+' : '-', | |
777 | (fp->fpcr >> 25)&1u ? '+' : '-', | |
778 | (fp->fpcr >> 24)&1u ? '+' : '-', | |
779 | rcmap[(fp->fpcr >> 22)&3u], | |
780 | (fp->fpcr >> 20)&3u, | |
781 | (fp->fpcr >> 16)&7u); | |
782 | } | |
783 | ||
784 | void regdump_gp(const struct regmap *map) | |
785 | { | |
786 | unsigned i; | |
787 | ||
788 | printf(";; General-purpose registers:\n"); | |
789 | for (i = 0; i < 32; i++) | |
790 | regdump(map, 0, | |
791 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_64 | REGSRC_GP | i); | |
792 | regdump(map, 0, REGF_HEX | REGF_64 | REGSRC_GP | REGIX_PC); | |
793 | ||
794 | printf(";; Flags:\n"); | |
795 | regdump(map, 0, REGSRC_GP | REGF_32 | REGIX_NZCV); | |
796 | } | |
797 | ||
798 | void regdump_fp(const struct regmap *map) | |
799 | { | |
800 | unsigned i; | |
801 | ||
802 | printf(";; Floating-point/SIMD registers:\n"); | |
803 | for (i = 0; i < 32; i++) | |
804 | regdump(map, 0, | |
805 | REGF_HEX | REGF_UNSGN | REGF_SGN | REGF_FLT | REGF_CHR | | |
806 | REGF_64 | REGF_32 | REGF_16 | REGF_8 | | |
807 | REGSRC_SIMD | i | (7 << REGF_WDSHIFT)); | |
808 | ||
809 | printf(";; Floating-point state:\n"); | |
810 | dump_fpflags(0, map->fp); | |
811 | } | |
812 | ||
813 | void regdump_simd(const struct regmap *map) { ; } | |
814 | ||
815 | #endif | |
816 | ||
817 | /*----- The main entry point ----------------------------------------------*/ | |
818 | ||
819 | /* --- @regdump@ --- * | |
820 | * | |
821 | * Arguments: @const void *base@ = pointer to base structure, corresponding | |
822 | * to the @REGF_SRCMASK@ part of @f@ | |
823 | * @const char *lbl@ = label to print | |
824 | * @uint32 f@ = format control word; see @REGF_...@ | |
825 | * | |
826 | * Returns: --- | |
827 | * | |
828 | * Use: Dump a register value, or chunk of memory. | |
829 | * | |
830 | * This function is not usually called directly; instead, use | |
831 | * the `reg' or `mem' assembler macros. | |
832 | */ | |
833 | ||
834 | void regdump(const void *base, const char *lbl, uint32 f) | |
835 | { | |
836 | unsigned ix = (f®F_IXMASK) >> REGF_IXSHIFT; | |
837 | unsigned wd = 1 << ((f®F_WDMASK) >> REGF_WDSHIFT); | |
838 | unsigned fmt, ty; | |
839 | uint32 fmtbit, tybit; | |
840 | const void *p; | |
841 | char regbuf[8]; const char *reg = regname(regbuf, f); | |
842 | const struct regmap *map; | |
843 | const struct fmttab *tab; | |
844 | struct fmtinfo fi; | |
845 | int firstp = 1; | |
846 | int skip; | |
847 | size_t n; | |
848 | ||
849 | #if CPUFAM_X86 || CPUFAM_AMD64 | |
850 | union vreg vr; | |
851 | #endif | |
852 | ||
853 | if (reg) { | |
854 | n = strlen(reg); | |
855 | if (n < 7) { | |
856 | memmove(regbuf + 7 - n, reg, n + 1); | |
857 | memset(regbuf, ' ', 7 - n); | |
858 | } | |
859 | } | |
860 | ||
861 | switch (f®F_SRCMASK) { | |
cd303963 MW |
862 | |
863 | case REGSRC_NONE: | |
864 | printf(";; %s\n", lbl); | |
865 | return; | |
866 | ||
4bc8424a MW |
867 | case REGSRC_ABS: |
868 | p = base; | |
869 | break; | |
870 | ||
871 | #if CPUFAM_X86 || CPUFAM_AMD64 | |
872 | case REGSRC_GP: | |
873 | map = (const struct regmap *)base; | |
874 | if (ix == REGIX_FLAGS && !(f®F_FMTMASK)) | |
875 | { dump_flags(lbl, reg, map->gp->gp[REGIX_FLAGS]); return; } | |
876 | p = &map->gp->gp[ix]; | |
877 | break; | |
878 | case REGSRC_SEG: | |
879 | map = (const struct regmap *)base; | |
880 | assert(wd == 1); assert((f®F_TYMASK) == REGF_16); | |
881 | p = &map->gp->seg[ix]; | |
882 | break; | |
883 | case REGSRC_STMMX: | |
884 | map = (const struct regmap *)base; | |
885 | if (ix == REGIX_FPFLAGS) | |
886 | { assert(!(f®F_FMTMASK)); dump_fpflags(lbl, map->fx); return; } | |
887 | if (!((map->fx->ftw << ix)&128u)) { | |
888 | printf(";; "); | |
889 | if (lbl) printf("%s: ", lbl); | |
890 | if (reg) printf("%s = ", reg); | |
891 | printf(" dead\n"); | |
892 | return; | |
893 | } | |
894 | p = &map->fx->stmmx[ix]; | |
895 | break; | |
896 | case REGSRC_SIMD: | |
897 | map = (const struct regmap *)base; | |
898 | if (ix == REGIX_FPFLAGS) | |
899 | { assert(!(f®F_FMTMASK)); dump_mxflags(lbl, map->fx); return; } | |
900 | if (wd <= 128) | |
901 | p = &map->fx->xmm[ix]; | |
902 | else { | |
903 | vr.v128[0] = map->fx->xmm[ix]; | |
904 | vr.v128[1] = map->avx->ymmh[ix]; | |
905 | assert(wd == 256); | |
906 | p = &vr; | |
907 | } | |
908 | break; | |
909 | #endif | |
910 | ||
911 | #if CPUFAM_ARMEL | |
912 | case REGSRC_GP: | |
913 | map = (const struct regmap *)base; | |
914 | if (ix == REGIX_CPSR && !(f®F_FMTMASK)) | |
915 | { dump_flags(lbl, map->gp->r[REGIX_CPSR].u32); return; } | |
916 | p = &map->gp->r[ix]; | |
917 | break; | |
918 | case REGSRC_FP: | |
919 | case REGSRC_SIMD: | |
920 | map = (const struct regmap *)base; | |
b26c7863 MW |
921 | if (!map->fp) { |
922 | printf(";;"); | |
923 | if (lbl) printf(" %s:", lbl); | |
924 | if (reg) printf(" %s =", reg); | |
925 | printf(" #<not available -- regdump_init?>\n"); | |
926 | return; | |
927 | } | |
4bc8424a MW |
928 | if (ix == REGIX_FPSCR) { |
929 | assert(!(f®F_FMTMASK)); | |
930 | dump_fpflags(lbl, map->fp->fpscr); | |
931 | return; | |
932 | } | |
933 | switch (regwd(f)) { | |
934 | case 32: p = &map->fp->u.s[ix]; break; | |
935 | case 64: p = &map->fp->u.d[ix]; break; | |
936 | case 128: p = &map->fp->u.q[ix]; break; | |
937 | default: assert(0); | |
938 | } | |
939 | break; | |
940 | #endif | |
941 | ||
942 | #if CPUFAM_ARM64 | |
943 | case REGSRC_GP: | |
944 | map = (const struct regmap *)base; | |
945 | if (ix == REGIX_NZCV && !(f®F_FMTMASK)) | |
946 | { dump_flags(lbl, map->gp->r[REGIX_NZCV].u64); return; } | |
947 | p = &map->gp->r[ix]; | |
948 | break; | |
949 | case REGSRC_FP: | |
950 | case REGSRC_SIMD: | |
951 | map = (const struct regmap *)base; | |
952 | if (ix == REGIX_FPFLAGS) | |
953 | { assert(!(f®F_FMTMASK)); dump_fpflags(lbl, map->fp); return; } | |
954 | p = &map->fp->v[ix]; | |
955 | break; | |
956 | #endif | |
957 | ||
958 | default: | |
959 | assert(0); | |
960 | } | |
961 | ||
962 | skip = (lbl ? strlen(lbl) + 2 : 0) + (reg ? strlen(reg) : 0); | |
963 | fi.f = 0; if (wd > 1) fi.f |= FMTF_VECTOR; | |
964 | ||
965 | for (ty = (f®F_TYMASK) >> REGF_TYSHIFT, | |
966 | tybit = 1 << REGF_TYSHIFT; | |
967 | ty; | |
968 | ty >>= 1, tybit <<= 1) { | |
969 | if (!(ty&1u)) continue; | |
970 | ||
971 | for (fmt = (f®F_FMTMASK) >> REGF_FMTSHIFT, | |
972 | fmtbit = 1 << REGF_FMTSHIFT; | |
973 | fmt; | |
974 | fmt >>= 1, fmtbit <<= 1) { | |
975 | ||
976 | if (!(fmt&1u)) continue; | |
977 | ||
978 | for (tab = fmttab; tab->mask; tab++) | |
979 | if (tab->mask == (fmtbit | tybit)) goto found; | |
980 | continue; | |
981 | found: | |
982 | ||
983 | if (firstp) { | |
984 | printf(";;"); | |
985 | if (lbl) printf(" %s:", lbl); | |
986 | if (reg) printf(" %s =", reg); | |
987 | firstp = 0; | |
988 | } else if (wd > 1) | |
989 | printf("\n;; %*s =", skip, ""); | |
990 | else | |
991 | fputs(" =", stdout); | |
992 | ||
993 | fi.p = p; fi.wd = 0; | |
994 | while (fi.wd < wd) { putchar(' '); tab->fmt(&fi); } | |
995 | } | |
996 | } | |
997 | putchar('\n'); | |
998 | } | |
999 | ||
1000 | /*----- Other random utilities --------------------------------------------*/ | |
1001 | ||
1002 | /* --- @regdump_freshline@ --- * | |
1003 | * | |
1004 | * Arguments: --- | |
1005 | * | |
1006 | * Returns: --- | |
1007 | * | |
1008 | * Use: Begin a fresh line of output. | |
1009 | */ | |
1010 | ||
1011 | void regdump_freshline(void) { putchar('\n'); } | |
1012 | ||
1013 | /*----- That's all, folks -------------------------------------------------*/ |