Commit | Line | Data |
---|---|---|
f02effa0 | 1 | #ifndef lint |
2222b6d9 KB |
2 | static char sccsid[] = "@(#)opset.c 4.8 (Berkeley) %G%"; |
3 | #endif | |
4 | ||
7cb4340a | 5 | /* |
2222b6d9 | 6 | * adb - instruction printing routines: VAX version |
7cb4340a BJ |
7 | */ |
8 | ||
9 | #include "defs.h" | |
7cb4340a | 10 | |
2222b6d9 KB |
11 | /* |
12 | * Get assembler definitions; declare tables that appear in optab.c. | |
13 | */ | |
14 | #define ADB | |
f02effa0 RH |
15 | #undef INSTTAB |
16 | #include "instrs.h" | |
7cb4340a | 17 | |
2222b6d9 KB |
18 | extern struct insttab insttab[]; |
19 | extern char *regname[]; | |
20 | extern char *fltimm[]; | |
21 | ||
22 | /* these are shared with the assembler: */ | |
23 | extern int ty_NORELOC[]; | |
24 | extern int ty_nbyte[]; | |
25 | #ifdef notyet | |
26 | extern int ty_float[]; /* must update assizetab.c */ | |
27 | #endif | |
28 | ||
7cb4340a | 29 | /* |
2222b6d9 | 30 | * Definitions for registers and for operand classes. |
7cb4340a | 31 | */ |
f02effa0 RH |
32 | #define R_PC 0xF |
33 | ||
2222b6d9 | 34 | #define OC_IMM0 0x0 /* literal, aka immediate */ |
f02effa0 RH |
35 | #define OC_IMM1 0x1 |
36 | #define OC_IMM2 0x2 | |
37 | #define OC_IMM3 0x3 | |
2222b6d9 KB |
38 | #define OC_INDEX 0x4 /* [rN] */ |
39 | #define OC_REG 0x5 /* rN */ | |
40 | #define OC_DREG 0x6 /* (rN) */ | |
41 | #define OC_ADREG 0x7 /* -(rN) */ | |
42 | #define OC_AIREG 0x8 /* (rN)+ */ | |
43 | #define OC_DAIREG 0x9 /* *(rN)+ */ | |
44 | #define OC_BDISP 0xA /* b(rN) */ | |
45 | #define OC_DBDISP 0xB /* *b(rN) */ | |
46 | #define OC_WDISP 0xC /* w(rN) */ | |
47 | #define OC_DWDISP 0xD /* *w(rN) */ | |
48 | #define OC_LDISP 0xE /* l(rN) */ | |
49 | #define OC_DLDISP 0xF /* *l(rN) */ | |
f02effa0 RH |
50 | |
51 | #define OC_SHIFT 4 | |
52 | #define OC_CONS(oc,reg) (((oc & 0xF) << OC_SHIFT) | (reg & 0xF)) | |
53 | #define OC_AMEXT(x) (((x) >> OC_SHIFT) & 0xF) | |
54 | #define OC_REGEXT(x) ((x) & 0xF) | |
7cb4340a BJ |
55 | |
56 | /* | |
2222b6d9 | 57 | * Definitions for special instructions. |
f02effa0 RH |
58 | */ |
59 | #define CASEB 0x8F | |
60 | #define CASEW 0xAF | |
61 | #define CASEL 0xCF | |
2222b6d9 KB |
62 | #define CHMK 0xBC |
63 | ||
f02effa0 | 64 | /* |
2222b6d9 KB |
65 | * ioptab is a two level 1-based index by opcode into insttab. |
66 | * The first level into ioptab is given by mapescbyte(). | |
67 | * Since ioptab is 1-based, references would be expected to | |
68 | * be of the form | |
69 | * | |
70 | * ptr = &insttab[ioptab[a][b] - 1]; | |
71 | * | |
72 | * but the form | |
73 | * | |
74 | * ptr = &(insttab - 1)[ioptab[a][b]] | |
75 | * | |
76 | * is equivalent and generates less code (!) (time to work on the | |
77 | * compiler again...). | |
f02effa0 | 78 | */ |
2222b6d9 KB |
79 | static short ioptab[3][256]; |
80 | #define mapescbyte(b) ((b) == ESCD ? 1 : (b) == ESCF ? 2 : 0) | |
7cb4340a | 81 | |
f02effa0 RH |
82 | mkioptab() |
83 | { | |
2222b6d9 KB |
84 | register struct insttab *p; |
85 | register int mapchar; | |
86 | register short *iop; | |
7cb4340a | 87 | |
2222b6d9 KB |
88 | /* |
89 | * The idea here is that whenever two opcodes have the same | |
90 | * codes, but different mnemonics, we want to prefer the one | |
91 | * with the `simpler' type. Here lower numbers make simpler | |
92 | * types. This seems (likely) to work reasonably well. | |
93 | * | |
94 | * At present, this affects the following opcodes: | |
95 | * | |
96 | * 7c clrq | clrd | clrg | |
97 | * 7e movaq | movad | movag | |
98 | * 7f pushaq | pushad | pushag | |
99 | * d4 clrl | clrf | |
100 | * de moval | movaf | |
101 | * df pushal | pushaf | |
102 | * | |
103 | * In each case, the leftmost mnemonics are preferred. | |
104 | */ | |
105 | #define PREFER(a, b) (A_TYPEXT((a)->argtype[0]) < A_TYPEXT((b)->argtype[0])) | |
106 | ||
107 | for (p = insttab; p->iname != NULL; p++) { | |
f02effa0 | 108 | mapchar = mapescbyte(p->eopcode); |
2222b6d9 KB |
109 | iop = &ioptab[mapchar][p->popcode]; |
110 | if (*iop == 0 || PREFER(p, &(insttab - 1)[*iop])) | |
111 | *iop = p - (insttab - 1); | |
7cb4340a | 112 | } |
2222b6d9 | 113 | #undef PREFER |
7cb4340a BJ |
114 | } |
115 | ||
f02effa0 | 116 | /* |
2222b6d9 | 117 | * Global variables for communication between the minions and printins. |
f02effa0 | 118 | */ |
2222b6d9 KB |
119 | static int idsp; /* which space we are in (INSTR or DATA) */ |
120 | static int argno; /* which argument we are working on */ | |
121 | static int dotoff; /* offset from dot for this arg */ | |
122 | static int vset[7]; /* set by savevar, cleared by clrvar */ | |
123 | ||
124 | #define savevar(v) (vset[argno] = 1, var[argno] = v) | |
125 | #define clrvar(v) (vset[argno] = 0, var[argno] = 0x80000000) | |
126 | ||
127 | /* | |
128 | * Read some bytes, checking for errors, and updating the offset. | |
129 | */ | |
130 | #define getsomebytes(ptr, nbytes) \ | |
131 | (void) adbread(idsp, inkdot(dotoff), ptr, nbytes); \ | |
132 | checkerr(); \ | |
133 | dotoff += (nbytes) | |
134 | ||
135 | /* | |
136 | * Read one byte, and advance the offset. | |
137 | */ | |
138 | static int | |
139 | getbyte() | |
f02effa0 | 140 | { |
2222b6d9 KB |
141 | u_char c; |
142 | ||
143 | getsomebytes(&c, sizeof(c)); | |
144 | return (c); | |
f02effa0 | 145 | } |
7cb4340a | 146 | |
2222b6d9 KB |
147 | /* |
148 | * adb's view: printins() prints one instruction, and sets dotinc. | |
149 | */ | |
150 | printins(space) | |
151 | int space; | |
7cb4340a | 152 | { |
2222b6d9 KB |
153 | register u_char *ap; |
154 | register struct insttab *ip; | |
155 | int ins, mode, optype, mapchar, t; | |
156 | char *lastix, *ixreg; | |
157 | char *operandout(); | |
158 | ||
159 | /* | |
160 | * Set up the module variables, pick up the instruction, and | |
161 | * find its table entry. | |
162 | */ | |
163 | idsp = space; | |
164 | dotoff = 0; | |
165 | ins = idsp == SP_NONE ? (u_char)dot : getbyte(); | |
166 | if ((mapchar = mapescbyte(ins)) != 0) { | |
167 | t = getbyte(); | |
168 | if (ioptab[mapchar][t] == 0) { | |
f02effa0 | 169 | /* |
2222b6d9 KB |
170 | * Oops; not a defined instruction; back over this |
171 | * escape byte. | |
f02effa0 | 172 | */ |
2222b6d9 | 173 | dotoff--; |
f02effa0 | 174 | mapchar = 0; |
2222b6d9 KB |
175 | } else |
176 | ins = t; | |
f02effa0 | 177 | } |
2222b6d9 KB |
178 | if ((t = ioptab[mapchar][ins]) == 0) { |
179 | adbprintf("<undefined operator byte>: %x", ins); | |
180 | dotinc = 1; | |
181 | return; | |
f02effa0 | 182 | } |
2222b6d9 KB |
183 | ip = &(insttab - 1)[t]; |
184 | adbprintf("%s%8t", ip->iname); | |
f02effa0 | 185 | |
2222b6d9 KB |
186 | /* |
187 | * For each argument, decode that argument. | |
188 | * We set t if we notice something fishy. | |
189 | */ | |
190 | t = 0; | |
191 | for (ap = ip->argtype, argno = 0; argno < ip->nargs; argno++) { | |
192 | optype = *ap++; | |
193 | clrvar(); | |
f02effa0 RH |
194 | if (argno != 0) |
195 | printc(','); | |
2222b6d9 KB |
196 | /* |
197 | * lastix and ixreg track the register indexed addressing | |
198 | * mode, which is written as <stuff>[reg] but encoded as | |
199 | * [reg]<stuff>. Only one [reg] is legal. | |
200 | */ | |
201 | lastix = NULL; | |
202 | do { | |
203 | /* check for special pc-relative (branch) */ | |
204 | if (A_ACCEXT(optype) & ACCB) { | |
205 | switch (A_TYPEXT(optype)) { | |
f02effa0 RH |
206 | case TYPB: |
207 | mode = OC_CONS(OC_BDISP, R_PC); | |
7cb4340a | 208 | break; |
f02effa0 RH |
209 | case TYPW: |
210 | mode = OC_CONS(OC_WDISP, R_PC); | |
7cb4340a | 211 | break; |
f02effa0 | 212 | } |
2222b6d9 KB |
213 | } else |
214 | mode = getbyte(); | |
215 | ixreg = operandout(mode, optype, ins == CHMK); | |
216 | if (lastix) { | |
217 | adbprintf("[%s]", lastix); | |
218 | if (ixreg) | |
219 | t = 1; | |
f02effa0 | 220 | } |
2222b6d9 | 221 | } while ((lastix = ixreg) != NULL); |
7cb4340a | 222 | } |
2222b6d9 KB |
223 | if (t) |
224 | adbprintf("%4t# not code? illegal arguments detected "); | |
225 | switch (ins) { | |
226 | case CASEB: | |
227 | case CASEW: | |
228 | case CASEL: | |
229 | if (mapchar == 0 && vset[1] && vset[2]) | |
230 | casebody(var[1], var[2]); | |
231 | else | |
232 | adbprintf("\n%4t# not code? non-constant cases "); | |
7cb4340a | 233 | } |
2222b6d9 | 234 | dotinc = dotoff; |
f02effa0 RH |
235 | } |
236 | ||
2222b6d9 KB |
237 | /* |
238 | * Print out the locations to which each of the cases branch. | |
239 | * This routine carefully allows expressions such as | |
240 | * | |
241 | * casel <val>,$<const>,$0x7fffffff | |
242 | * | |
243 | * even though they do not fit on a VAX. | |
244 | */ | |
245 | static | |
f02effa0 | 246 | casebody(base, limit) |
2222b6d9 | 247 | register expr_t base, limit; |
f02effa0 | 248 | { |
2222b6d9 KB |
249 | register expr_t i = -1; |
250 | register addr_t a, baseaddr = inkdot(dotoff); | |
251 | short displ; | |
252 | ||
f02effa0 | 253 | argno = 0; |
2222b6d9 KB |
254 | do { |
255 | i++; | |
256 | adbprintf("\n %R: ", base++); | |
257 | getsomebytes(&displ, sizeof(displ)); | |
258 | a = displ + baseaddr; | |
259 | psymoff("%R", a, SP_DATA, maxoff, ""); | |
260 | savevar(a); | |
261 | } while (i != limit); | |
7cb4340a BJ |
262 | } |
263 | ||
264 | /* | |
2222b6d9 KB |
265 | * Handle a normal operand. Return pointer to register |
266 | * name if this is an index instruction, else return NULL. | |
f02effa0 | 267 | */ |
2222b6d9 KB |
268 | static char * |
269 | operandout(mode, optype, ischmk) | |
270 | register int mode; | |
271 | int optype, ischmk; | |
f02effa0 | 272 | { |
2222b6d9 KB |
273 | register char *r; |
274 | register int regnumber, nbytes, n; | |
275 | union { | |
276 | char b; | |
277 | short w; | |
278 | int l; | |
279 | } displ; | |
280 | extern char *syscalls[]; | |
281 | extern int nsys; | |
f02effa0 | 282 | |
2222b6d9 KB |
283 | regnumber = OC_REGEXT(mode); |
284 | r = regname[regnumber]; | |
285 | switch (OC_AMEXT(mode)) { | |
286 | ||
287 | case OC_IMM0: case OC_IMM1: | |
288 | case OC_IMM2: case OC_IMM3: | |
289 | savevar(mode); | |
290 | printc('$'); | |
291 | #ifdef notyet | |
292 | if (ty_float[A_TYPEXT(optype)]) | |
293 | prints(fltimm[mode]); | |
294 | else if (ischmk && (u_int)mode < nsys && syscalls[mode]) | |
295 | prints(syscalls[mode]); | |
296 | else | |
297 | adbprintf("%V", mode); | |
298 | #else | |
299 | switch (A_TYPEXT(optype)) { | |
7cb4340a | 300 | |
2222b6d9 KB |
301 | case TYPF: |
302 | case TYPD: | |
303 | case TYPG: | |
304 | case TYPH: | |
305 | prints(fltimm[mode]); | |
306 | break; | |
307 | ||
308 | default: | |
309 | if (ischmk && (u_int)mode < nsys && syscalls[mode]) | |
310 | prints(syscalls[mode]); | |
311 | else | |
312 | adbprintf("%V", mode); | |
313 | break; | |
314 | } | |
315 | #endif | |
316 | return (0); | |
f02effa0 | 317 | |
f02effa0 | 318 | case OC_INDEX: |
2222b6d9 KB |
319 | return (r); /* will be printed later */ |
320 | ||
f02effa0 | 321 | case OC_REG: |
2222b6d9 KB |
322 | adbprintf("%s", r); |
323 | return (0); | |
324 | ||
f02effa0 | 325 | case OC_DREG: |
2222b6d9 KB |
326 | adbprintf("(%s)", r); |
327 | return (0); | |
328 | ||
f02effa0 | 329 | case OC_ADREG: |
2222b6d9 KB |
330 | adbprintf("-(%s)", r); |
331 | return (0); | |
332 | ||
f02effa0 RH |
333 | case OC_DAIREG: |
334 | printc('*'); | |
2222b6d9 KB |
335 | /* FALLTHROUGH */ |
336 | ||
f02effa0 | 337 | case OC_AIREG: |
2222b6d9 KB |
338 | if (regnumber != R_PC) { |
339 | adbprintf("(%s)+", r); | |
340 | return (0); | |
f02effa0 | 341 | } |
2222b6d9 KB |
342 | /* PC immediate */ |
343 | printc('$'); | |
344 | if (mode == OC_CONS(OC_DAIREG, R_PC)) | |
345 | /* PC absolute, always 4 bytes */ | |
346 | nbytes = 4; | |
347 | else { | |
348 | nbytes = ty_nbyte[A_TYPEXT(optype)]; | |
349 | if (ty_NORELOC[A_TYPEXT(optype)]) { | |
350 | bignumprint(nbytes, optype); | |
351 | return (0); | |
352 | } | |
353 | } | |
354 | break; | |
355 | ||
f02effa0 RH |
356 | case OC_DBDISP: |
357 | printc('*'); | |
2222b6d9 KB |
358 | /* FALLTHROUGH */ |
359 | ||
f02effa0 RH |
360 | case OC_BDISP: |
361 | nbytes = 1; | |
362 | break; | |
2222b6d9 | 363 | |
f02effa0 RH |
364 | case OC_DWDISP: |
365 | printc('*'); | |
2222b6d9 KB |
366 | /* FALLTHROUGH */ |
367 | ||
f02effa0 RH |
368 | case OC_WDISP: |
369 | nbytes = 2; | |
370 | break; | |
2222b6d9 | 371 | |
f02effa0 RH |
372 | case OC_DLDISP: |
373 | printc('*'); | |
2222b6d9 KB |
374 | /* FALLTHROUGH */ |
375 | ||
f02effa0 RH |
376 | case OC_LDISP: |
377 | nbytes = 4; | |
378 | break; | |
7cb4340a | 379 | |
2222b6d9 KB |
380 | default: |
381 | panic("operandout 1"); | |
382 | /* NOTREACHED */ | |
f02effa0 | 383 | } |
2222b6d9 KB |
384 | |
385 | /* | |
386 | * Print a displacement format. | |
387 | */ | |
388 | getsomebytes(&displ, nbytes); | |
389 | switch (nbytes) { | |
390 | case 1: | |
391 | n = displ.b; | |
f02effa0 | 392 | break; |
2222b6d9 KB |
393 | case 2: |
394 | n = displ.w; | |
395 | break; | |
396 | case 4: | |
397 | n = displ.l; | |
f02effa0 | 398 | break; |
2222b6d9 KB |
399 | default: |
400 | panic("operandout 2"); | |
401 | /* NOTREACHED */ | |
f02effa0 | 402 | } |
2222b6d9 KB |
403 | if (regnumber == R_PC) { |
404 | switch (OC_AMEXT(mode)) { | |
7cb4340a | 405 | |
2222b6d9 KB |
406 | case OC_DAIREG: |
407 | if (ischmk && (u_int)n < nsys && syscalls[n]) { | |
408 | prints(syscalls[n]); | |
409 | return (0); | |
410 | } | |
411 | break; | |
f02effa0 | 412 | |
2222b6d9 KB |
413 | case OC_BDISP: case OC_DBDISP: |
414 | case OC_WDISP: case OC_DWDISP: | |
415 | case OC_LDISP: case OC_DLDISP: | |
416 | /* PC offset */ | |
417 | n += dot + dotoff; | |
418 | } | |
419 | psymoff("%V", (addr_t)n, SP_DATA, maxoff, ""); | |
420 | } else | |
421 | adbprintf("%V(%s)", (expr_t)n, regname[regnumber]); | |
422 | savevar(n); | |
423 | return (0); | |
f02effa0 RH |
424 | } |
425 | ||
2222b6d9 KB |
426 | /* |
427 | * Print an F-float, D-float, G-float, H-float, quadword, or octaword. | |
428 | * F- and D-floating values are printed as themselves, unless they are | |
429 | * reserved operand bit patterns; these, and the others, are printed | |
430 | * instead in hex, with leading zeroes suppressed. | |
431 | */ | |
432 | static | |
f02effa0 | 433 | bignumprint(nbytes, optype) |
2222b6d9 | 434 | int nbytes, optype; |
f02effa0 | 435 | { |
2222b6d9 KB |
436 | register char *p; |
437 | register int i; | |
438 | union { | |
439 | float f; /* if f-floating */ | |
440 | double d; /* if d-floating */ | |
441 | u_char c[16]; /* if G, H, Q, or O */ | |
442 | } n; | |
443 | char expbuf[4*8+1]; /* max 4 8-character hex ints */ | |
444 | static char tohex[] = "0123456789abcdef"; | |
445 | ||
446 | /* | |
447 | * Read in the number, then figure out how to print it. | |
448 | */ | |
449 | getsomebytes(&n, nbytes); | |
450 | switch (A_TYPEXT(optype)) { | |
451 | ||
452 | case TYPF: | |
453 | if ((p = checkfloat((caddr_t)&n.f, 0)) == NULL) { | |
454 | adbprintf("0f%f", n.f); | |
455 | return; | |
5f2ce988 | 456 | } |
2222b6d9 | 457 | adbprintf("%s 0f::", p); |
f02effa0 | 458 | break; |
2222b6d9 | 459 | |
f02effa0 | 460 | case TYPD: |
2222b6d9 KB |
461 | if ((p = checkfloat((caddr_t)&n.d, 1)) == NULL) { |
462 | adbprintf("0d%f", n.d); | |
463 | return; | |
5f2ce988 | 464 | } |
2222b6d9 | 465 | adbprintf("%s 0d::", p); |
f02effa0 | 466 | break; |
2222b6d9 | 467 | |
f02effa0 | 468 | case TYPG: |
2222b6d9 KB |
469 | adbprintf("0g::"); |
470 | break; | |
471 | ||
f02effa0 | 472 | case TYPH: |
2222b6d9 KB |
473 | adbprintf("0h::"); |
474 | break; | |
475 | ||
f02effa0 RH |
476 | case TYPQ: |
477 | case TYPO: | |
f02effa0 | 478 | break; |
f02effa0 | 479 | |
2222b6d9 KB |
480 | default: |
481 | panic("bignumprint"); | |
7cb4340a | 482 | } |
f02effa0 | 483 | |
2222b6d9 KB |
484 | /* |
485 | * Expand the number into expbuf, then skip leading zeroes. | |
486 | * Be careful not to skip the entire number. | |
487 | */ | |
488 | for (p = expbuf, i = nbytes; --i >= 0;) { | |
489 | *p++ = tohex[n.c[i] >> 4]; | |
490 | *p++ = tohex[n.c[i] & 15]; | |
f02effa0 | 491 | } |
2222b6d9 KB |
492 | for (p = expbuf; *p == '0'; p++) |
493 | /* void */; | |
494 | prints(*p ? p : p - 1); | |
7cb4340a | 495 | } |