3bf57d21 |
1 | /* Print 32000 instructions for GDB, the GNU debugger. |
2 | Copyright (C) 1986,1988 Free Software Foundation, Inc. |
3 | |
4 | GDB is distributed in the hope that it will be useful, but WITHOUT ANY |
5 | WARRANTY. No author or distributor accepts responsibility to anyone |
6 | for the consequences of using it or for whether it serves any |
7 | particular purpose or works at all, unless he says so in writing. |
8 | Refer to the GDB General Public License for full details. |
9 | |
10 | Everyone is granted permission to copy, modify and redistribute GDB, |
11 | but only under the conditions described in the GDB General Public |
12 | License. A copy of this license is supposed to have been given to you |
13 | along with GDB so you can know your rights and responsibilities. It |
14 | should be in a file named COPYING. Among other things, the copyright |
15 | notice and this notice must be preserved on all copies. |
16 | |
17 | In other words, go ahead and share GDB, but don't try to stop |
18 | anyone else from sharing it farther. Help stamp out software hoarding! |
19 | */ |
20 | |
21 | #include <stdio.h> |
22 | |
23 | #include "defs.h" |
24 | #include "param.h" |
25 | #include "symtab.h" |
26 | #include "opcode.h" |
27 | |
28 | /* 32000 instructions are never longer than this. */ |
29 | #define MAXLEN 62 |
30 | |
31 | /* Number of elements in the opcode table. */ |
32 | #define NOPCODES (sizeof notstrs / sizeof notstrs[0]) |
33 | |
34 | extern char *reg_names[]; |
35 | |
36 | #define NEXT_IS_ADDR '|' |
37 | |
38 | /* |
39 | * extract "count" bits starting "offset" bits |
40 | * into buffer |
41 | */ |
42 | |
43 | int |
44 | bit_extract (buffer, offset, count) |
45 | char *buffer; |
46 | int offset; |
47 | int count; |
48 | { |
49 | int result; |
50 | int mask; |
51 | int bit; |
52 | |
53 | buffer += offset >> 3; |
54 | offset &= 7; |
55 | bit = 1; |
56 | result = 0; |
57 | while (count--) |
58 | { |
59 | if ((*buffer & (1 << offset))) |
60 | result |= bit; |
61 | if (++offset == 8) |
62 | { |
63 | offset = 0; |
64 | buffer++; |
65 | } |
66 | bit <<= 1; |
67 | } |
68 | return result; |
69 | } |
70 | |
71 | float |
72 | fbit_extract (buffer, offset, count) |
73 | { |
74 | union { |
75 | int ival; |
76 | float fval; |
77 | } foo; |
78 | |
79 | foo.ival = bit_extract (buffer, offset, 32); |
80 | return foo.fval; |
81 | } |
82 | |
83 | double |
84 | dbit_extract (buffer, offset, count) |
85 | { |
86 | union { |
87 | struct {int low, high; } ival; |
88 | double dval; |
89 | } foo; |
90 | |
91 | foo.ival.low = bit_extract (buffer, offset, 32); |
92 | foo.ival.high = bit_extract (buffer, offset+32, 32); |
93 | return foo.dval; |
94 | } |
95 | |
96 | sign_extend (value, bits) |
97 | { |
98 | value = value & ((1 << bits) - 1); |
99 | return (value & (1 << (bits-1)) |
100 | ? value | (~((1 << bits) - 1)) |
101 | : value); |
102 | } |
103 | |
104 | flip_bytes (ptr, count) |
105 | char *ptr; |
106 | int count; |
107 | { |
108 | char tmp; |
109 | |
110 | while (count > 0) |
111 | { |
112 | tmp = *ptr; |
113 | ptr[0] = ptr[count-1]; |
114 | ptr[count-1] = tmp; |
115 | ptr++; |
116 | count -= 2; |
117 | } |
118 | } |
4187119d |
119 | \f |
120 | /* Given a character C, does it represent a general addressing mode? */ |
121 | #define Is_gen(c) \ |
122 | ((c) == 'F' || (c) == 'L' || (c) == 'B' \ |
123 | || (c) == 'W' || (c) == 'D' || (c) == 'A') |
124 | |
125 | /* Adressing modes. */ |
126 | #define Adrmod_index_byte 0x1c |
127 | #define Adrmod_index_word 0x1d |
128 | #define Adrmod_index_doubleword 0x1e |
129 | #define Adrmod_index_quadword 0x1f |
130 | |
131 | /* Is MODE an indexed addressing mode? */ |
132 | #define Adrmod_is_index(mode) \ |
133 | (mode == Adrmod_index_byte \ |
134 | || mode == Adrmod_index_word \ |
135 | || mode == Adrmod_index_doubleword \ |
136 | || mode == Adrmod_index_quadword) |
3bf57d21 |
137 | |
138 | \f |
139 | /* Print the 32000 instruction at address MEMADDR in debugged memory, |
140 | on STREAM. Returns length of the instruction, in bytes. */ |
141 | |
142 | int |
143 | print_insn (memaddr, stream) |
4187119d |
144 | CORE_ADDR memaddr; |
145 | FILE *stream; |
3bf57d21 |
146 | { |
147 | unsigned char buffer[MAXLEN]; |
148 | register int i; |
149 | register unsigned char *p; |
150 | register char *d; |
151 | unsigned short first_word; |
152 | int gen, disp; |
153 | int ioffset; /* bits into instruction */ |
154 | int aoffset; /* bits into arguments */ |
155 | char arg_bufs[MAX_ARGS+1][ARG_LEN]; |
156 | int argnum; |
157 | int maxarg; |
158 | |
159 | read_memory (memaddr, buffer, MAXLEN); |
160 | |
161 | first_word = *(unsigned short *) buffer; |
162 | for (i = 0; i < NOPCODES; i++) |
163 | if ((first_word & ((1 << notstrs[i].detail.obits) - 1)) |
164 | == notstrs[i].detail.code) |
165 | break; |
166 | |
167 | /* Handle undefined instructions. */ |
168 | if (i == NOPCODES) |
169 | { |
170 | fprintf (stream, "0%o", buffer[0]); |
171 | return 1; |
172 | } |
173 | |
174 | fprintf (stream, "%s", notstrs[i].name); |
175 | |
176 | ioffset = notstrs[i].detail.ibits; |
177 | aoffset = notstrs[i].detail.ibits; |
178 | d = notstrs[i].detail.args; |
179 | |
180 | if (*d) |
181 | { |
4187119d |
182 | /* Offset in bits of the first thing beyond each index byte. |
183 | Element 0 is for operand A and element 1 is for operand B. |
184 | The rest are irrelevant, but we put them here so we don't |
185 | index outside the array. */ |
186 | int index_offset[MAX_ARGS]; |
187 | |
188 | /* 0 for operand A, 1 for operand B, greater for other args. */ |
189 | int whicharg = 0; |
190 | |
3bf57d21 |
191 | fputc ('\t', stream); |
192 | |
193 | maxarg = 0; |
4187119d |
194 | |
195 | /* First we have to find and keep track of the index bytes, |
196 | if we are using scaled indexed addressing mode, since the index |
197 | bytes occur right after the basic instruction, not as part |
198 | of the addressing extension. */ |
199 | if (Is_gen(d[1])) |
200 | { |
201 | int addr_mode = bit_extract (buffer, ioffset - 5, 5); |
202 | |
203 | if (Adrmod_is_index (addr_mode)) |
204 | { |
205 | aoffset += 8; |
206 | index_offset[0] = aoffset; |
207 | } |
208 | } |
209 | if (d[2] && Is_gen(d[3])) |
210 | { |
211 | int addr_mode = bit_extract (buffer, ioffset - 10, 5); |
212 | |
213 | if (Adrmod_is_index (addr_mode)) |
214 | { |
215 | aoffset += 8; |
216 | index_offset[1] = aoffset; |
217 | } |
218 | } |
219 | |
3bf57d21 |
220 | while (*d) |
221 | { |
222 | argnum = *d - '1'; |
223 | d++; |
224 | if (argnum > maxarg && argnum < MAX_ARGS) |
225 | maxarg = argnum; |
226 | ioffset = print_insn_arg (*d, ioffset, &aoffset, buffer, |
4187119d |
227 | memaddr, arg_bufs[argnum], |
228 | index_offset[whicharg]); |
3bf57d21 |
229 | d++; |
4187119d |
230 | whicharg++; |
3bf57d21 |
231 | } |
232 | for (argnum = 0; argnum <= maxarg; argnum++) |
233 | { |
234 | CORE_ADDR addr; |
235 | char *ch, *index (); |
236 | for (ch = arg_bufs[argnum]; *ch;) |
237 | { |
238 | if (*ch == NEXT_IS_ADDR) |
239 | { |
240 | ++ch; |
241 | addr = atoi (ch); |
242 | print_address (addr, stream); |
243 | while (*ch && *ch != NEXT_IS_ADDR) |
244 | ++ch; |
245 | if (*ch) |
246 | ++ch; |
247 | } |
248 | else |
249 | putc (*ch++, stream); |
250 | } |
251 | if (argnum < maxarg) |
252 | fprintf (stream, ", "); |
253 | } |
254 | } |
255 | return aoffset / 8; |
256 | } |
257 | |
4187119d |
258 | /* Print an instruction operand of category given by d. IOFFSET is |
259 | the bit position below which small (<1 byte) parts of the operand can |
260 | be found (usually in the basic instruction, but for indexed |
261 | addressing it can be in the index byte). AOFFSETP is a pointer to the |
262 | bit position of the addressing extension. BUFFER contains the |
263 | instruction. ADDR is where BUFFER was read from. Put the disassembled |
264 | version of the operand in RESULT. INDEX_OFFSET is the bit position |
265 | of the index byte (it contains garbage if this operand is not a |
266 | general operand using scaled indexed addressing mode). */ |
267 | |
268 | print_insn_arg (d, ioffset, aoffsetp, buffer, addr, result, index_offset) |
3bf57d21 |
269 | char d; |
270 | int ioffset, *aoffsetp; |
271 | char *buffer; |
272 | CORE_ADDR addr; |
273 | char *result; |
4187119d |
274 | int index_offset; |
3bf57d21 |
275 | { |
276 | int addr_mode; |
277 | float Fvalue; |
278 | double Lvalue; |
279 | int Ivalue; |
280 | int disp1, disp2; |
281 | int index; |
282 | |
283 | switch (d) |
284 | { |
285 | case 'F': |
286 | case 'L': |
287 | case 'B': |
288 | case 'W': |
289 | case 'D': |
290 | case 'A': |
291 | addr_mode = bit_extract (buffer, ioffset-5, 5); |
292 | ioffset -= 5; |
293 | switch (addr_mode) |
294 | { |
295 | case 0x0: case 0x1: case 0x2: case 0x3: |
296 | case 0x4: case 0x5: case 0x6: case 0x7: |
297 | switch (d) |
298 | { |
299 | case 'F': |
300 | case 'L': |
301 | sprintf (result, "f%d", addr_mode); |
302 | break; |
303 | default: |
304 | sprintf (result, "r%d", addr_mode); |
305 | } |
306 | break; |
307 | case 0x8: case 0x9: case 0xa: case 0xb: |
308 | case 0xc: case 0xd: case 0xe: case 0xf: |
309 | disp1 = get_displacement (buffer, aoffsetp); |
310 | sprintf (result, "%d(r%d)", disp1, addr_mode & 7); |
311 | break; |
312 | case 0x10: |
313 | case 0x11: |
314 | case 0x12: |
315 | disp1 = get_displacement (buffer, aoffsetp); |
316 | disp2 = get_displacement (buffer, aoffsetp); |
317 | sprintf (result, "%d(%d(%s))", disp2, disp1, |
318 | addr_mode==0x10?"fp":addr_mode==0x11?"sp":"sb"); |
319 | break; |
320 | case 0x13: |
321 | sprintf (result, "reserved"); |
322 | break; |
323 | case 0x14: |
324 | switch (d) |
325 | { |
326 | case 'B': |
327 | Ivalue = bit_extract (buffer, *aoffsetp, 8); |
328 | Ivalue = sign_extend (Ivalue, 8); |
329 | *aoffsetp += 8; |
330 | sprintf (result, "$%d", Ivalue); |
331 | break; |
332 | case 'W': |
333 | Ivalue = bit_extract (buffer, *aoffsetp, 16); |
334 | flip_bytes (&Ivalue, 2); |
335 | *aoffsetp += 16; |
336 | Ivalue = sign_extend (Ivalue, 16); |
337 | sprintf (result, "$%d", Ivalue); |
338 | break; |
339 | case 'D': |
340 | Ivalue = bit_extract (buffer, *aoffsetp, 32); |
341 | flip_bytes (&Ivalue, 4); |
342 | *aoffsetp += 32; |
343 | sprintf (result, "$%d", Ivalue); |
344 | break; |
345 | case 'A': |
346 | Ivalue = bit_extract (buffer, *aoffsetp, 32); |
347 | flip_bytes (&Ivalue, 4); |
348 | *aoffsetp += 32; |
349 | sprintf (result, "$|%d|", Ivalue); |
350 | break; |
351 | case 'F': |
352 | Fvalue = fbit_extract (buffer, *aoffsetp, 32); |
353 | flip_bytes (&Fvalue, 4); |
354 | *aoffsetp += 32; |
355 | sprintf (result, "$%g", Fvalue); |
356 | break; |
357 | case 'L': |
358 | Lvalue = dbit_extract (buffer, *aoffsetp, 64); |
359 | flip_bytes (&Lvalue, 8); |
360 | *aoffsetp += 64; |
361 | sprintf (result, "$%g", Lvalue); |
362 | break; |
363 | } |
364 | break; |
365 | case 0x15: |
366 | disp1 = get_displacement (buffer, aoffsetp); |
367 | sprintf (result, "@|%d|", disp1); |
368 | break; |
369 | case 0x16: |
370 | disp1 = get_displacement (buffer, aoffsetp); |
371 | disp2 = get_displacement (buffer, aoffsetp); |
372 | sprintf (result, "EXT(%d) + %d", disp1, disp2); |
373 | break; |
374 | case 0x17: |
375 | sprintf (result, "tos"); |
376 | break; |
377 | case 0x18: |
378 | disp1 = get_displacement (buffer, aoffsetp); |
379 | sprintf (result, "%d(fp)", disp1); |
380 | break; |
381 | case 0x19: |
382 | disp1 = get_displacement (buffer, aoffsetp); |
383 | sprintf (result, "%d(sp)", disp1); |
384 | break; |
385 | case 0x1a: |
386 | disp1 = get_displacement (buffer, aoffsetp); |
387 | sprintf (result, "%d(sb)", disp1); |
388 | break; |
389 | case 0x1b: |
390 | disp1 = get_displacement (buffer, aoffsetp); |
391 | sprintf (result, "|%d|", addr + disp1); |
392 | break; |
393 | case 0x1c: |
394 | case 0x1d: |
395 | case 0x1e: |
396 | case 0x1f: |
4187119d |
397 | index = bit_extract (buffer, index_offset - 8, 3); |
398 | print_insn_arg (d, index_offset, aoffsetp, buffer, addr, |
399 | result, 0); |
3bf57d21 |
400 | { |
401 | static char *ind[] = {"b", "w", "d", "q"}; |
402 | char *off; |
403 | |
404 | off = result + strlen (result); |
4187119d |
405 | sprintf (off, "[r%d:%s]", index, |
3bf57d21 |
406 | ind[addr_mode & 3]); |
407 | } |
408 | break; |
409 | } |
410 | break; |
411 | case 'q': |
412 | Ivalue = bit_extract (buffer, ioffset-4, 4); |
413 | Ivalue = sign_extend (Ivalue, 4); |
414 | sprintf (result, "%d", Ivalue); |
415 | ioffset -= 4; |
416 | break; |
417 | case 'r': |
418 | Ivalue = bit_extract (buffer, ioffset-3, 3); |
419 | sprintf (result, "r%d", Ivalue&7); |
420 | ioffset -= 3; |
421 | break; |
422 | case 'd': |
423 | sprintf (result, "%d", get_displacement (buffer, aoffsetp)); |
424 | break; |
425 | case 'p': |
426 | sprintf (result, "%c%d%c", NEXT_IS_ADDR, |
427 | addr + get_displacement (buffer, aoffsetp), |
428 | NEXT_IS_ADDR); |
429 | break; |
430 | case 'i': |
431 | Ivalue = bit_extract (buffer, *aoffsetp, 8); |
432 | *aoffsetp += 8; |
433 | sprintf (result, "0x%x", Ivalue); |
434 | break; |
435 | } |
436 | return ioffset; |
437 | } |
438 | |
439 | get_displacement (buffer, aoffsetp) |
440 | char *buffer; |
441 | int *aoffsetp; |
442 | { |
443 | int Ivalue; |
444 | |
445 | Ivalue = bit_extract (buffer, *aoffsetp, 8); |
446 | switch (Ivalue & 0xc0) |
447 | { |
448 | case 0x00: |
449 | case 0x40: |
450 | Ivalue = sign_extend (Ivalue, 7); |
451 | *aoffsetp += 8; |
452 | break; |
453 | case 0x80: |
454 | Ivalue = bit_extract (buffer, *aoffsetp, 16); |
455 | flip_bytes (&Ivalue, 2); |
456 | Ivalue = sign_extend (Ivalue, 14); |
457 | *aoffsetp += 16; |
458 | break; |
459 | case 0xc0: |
460 | Ivalue = bit_extract (buffer, *aoffsetp, 32); |
461 | flip_bytes (&Ivalue, 4); |
462 | Ivalue = sign_extend (Ivalue, 30); |
463 | *aoffsetp += 32; |
464 | break; |
465 | } |
466 | return Ivalue; |
467 | } |
468 | \f |
469 | /* Return the number of locals in the current frame given a pc |
470 | pointing to the enter instruction. This is used in the macro |
471 | FRAME_FIND_SAVED_REGS. */ |
472 | |
473 | ns32k_localcount (enter_pc) |
474 | CORE_ADDR enter_pc; |
475 | { |
476 | unsigned char localtype; |
477 | int localcount; |
478 | |
479 | localtype = read_memory_integer (enter_pc+2, 1); |
480 | if ((localtype & 0x80) == 0) |
481 | localcount = localtype; |
482 | else if ((localtype & 0xc0) == 0x80) |
483 | localcount = (((localtype & 0x3f) << 8) |
484 | | (read_memory_integer (enter_pc+3, 1) & 0xff)); |
485 | else |
486 | localcount = (((localtype & 0x3f) << 24) |
487 | | ((read_memory_integer (enter_pc+3, 1) & 0xff) << 16) |
488 | | ((read_memory_integer (enter_pc+4, 1) & 0xff) << 8 ) |
489 | | (read_memory_integer (enter_pc+5, 1) & 0xff)); |
490 | return localcount; |
491 | } |
492 | |
493 | /* |
494 | * Get the address of the enter opcode for the function |
495 | * containing PC, if there is an enter for the function, |
496 | * and if the pc is between the enter and exit. |
497 | * Returns positive address if pc is between enter/exit, |
498 | * 1 if pc before enter or after exit, 0 otherwise. |
499 | */ |
500 | |
501 | CORE_ADDR |
502 | ns32k_get_enter_addr (pc) |
503 | CORE_ADDR pc; |
504 | { |
505 | CORE_ADDR enter_addr; |
506 | unsigned char op; |
507 | |
508 | if (ABOUT_TO_RETURN (pc)) |
509 | return 1; /* after exit */ |
510 | |
511 | enter_addr = get_pc_function_start (pc); |
512 | |
513 | if (pc == enter_addr) |
514 | return 1; /* before enter */ |
515 | |
516 | op = read_memory_integer (enter_addr, 1); |
517 | |
518 | if (op != 0x82) |
519 | return 0; /* function has no enter/exit */ |
520 | |
521 | return enter_addr; /* pc is between enter and exit */ |
522 | } |