Commit | Line | Data |
---|---|---|
7d9884b9 JG |
1 | /* Print instructions for the Motorola 88000, for GDB and GNU Binutils. |
2 | Copyright 1986, 1987, 1988, 1989, 1990, 1991 Free Software Foundation, Inc. | |
3 | Contributed by Data General Corporation, November 1989. | |
4 | Partially derived from an earlier printcmd.c. | |
5 | ||
6 | This file is part of GDB and the GNU Binutils. | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program; if not, write to the Free Software | |
20 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
8aa13b87 JK |
21 | |
22 | #include <stdio.h> | |
aab77d5f | 23 | #include "opcode/m88k.h" |
8aa13b87 JK |
24 | #include "defs.h" |
25 | #include "symtab.h" | |
26 | ||
27 | void sprint_address (); | |
28 | ||
8aa13b87 JK |
29 | INSTAB *hashtable[HASHVAL] = {0}; |
30 | ||
31 | /* | |
32 | * Disassemble an M88000 Instruction | |
33 | * | |
34 | * | |
35 | * This module decodes the first instruction in inbuf. It uses the pc | |
36 | * to display pc-relative displacements. It writes the disassembled | |
37 | * instruction in outbuf. | |
38 | * | |
39 | * Revision History | |
40 | * | |
41 | * Revision 1.0 11/08/85 Creation date by Motorola | |
42 | * 05/11/89 R. Trawick adapted to GDB interface. | |
43 | */ | |
44 | #define MAXLEN 20 | |
45 | ||
46 | print_insn (memaddr, stream) | |
47 | CORE_ADDR memaddr; | |
48 | FILE *stream; | |
49 | { | |
50 | unsigned char buffer[MAXLEN]; | |
51 | /* should be expanded if disassembler prints symbol names */ | |
52 | char outbuf[100]; | |
53 | int n; | |
54 | ||
55 | /* Instruction addresses may have low two bits set. Clear them. */ | |
56 | memaddr&= 0xfffffffc; | |
57 | read_memory (memaddr, buffer, MAXLEN); | |
58 | ||
59 | n = m88kdis ((int)memaddr, buffer, outbuf); | |
60 | ||
61 | fputs (outbuf, stream); | |
62 | ||
63 | return (n); | |
64 | } | |
65 | ||
66 | /* | |
67 | * disassemble the first instruction in 'inbuf'. | |
68 | * 'pc' should be the address of this instruction, it will | |
69 | * be used to print the target address if this is a relative jump or call | |
70 | * 'outbuf' gets filled in with the disassembled instruction. It should | |
71 | * be long enough to hold the longest disassembled instruction. | |
72 | * 100 bytes is certainly enough, unless symbol printing is added later | |
73 | * The function returns the length of this instruction in bytes. | |
74 | */ | |
75 | ||
76 | int m88kdis( pc, inbuf, outbuf ) | |
77 | ||
78 | int pc; | |
79 | int *inbuf; | |
80 | char *outbuf; | |
81 | ||
82 | { static ihashtab_initialized = 0; | |
83 | int instruction; | |
84 | unsigned int opcode; | |
85 | INSTAB *entry_ptr; | |
86 | int opmask; | |
87 | int class; | |
88 | ||
89 | instruction= *inbuf; | |
90 | ||
91 | if (!ihashtab_initialized) { | |
92 | init_disasm(); | |
93 | } | |
94 | ||
95 | /* create a the appropriate mask to isolate the opcode */ | |
96 | opmask= DEFMASK; | |
97 | class= instruction & DEFMASK; | |
98 | if ((class >= SFU0) && (class <= SFU7)) { | |
99 | if (instruction < SFU1) { | |
100 | opmask= CTRLMASK; | |
101 | } else { | |
102 | opmask= SFUMASK; | |
103 | } | |
104 | } else if (class == RRR) { | |
105 | opmask= RRRMASK; | |
106 | } else if (class == RRI10) { | |
107 | opmask= RRI10MASK; | |
108 | } | |
109 | ||
110 | /* isolate the opcode */ | |
111 | opcode= instruction & opmask; | |
112 | ||
113 | /* search the hash table with the isolated opcode */ | |
114 | for (entry_ptr= hashtable[ opcode % HASHVAL ]; | |
115 | (entry_ptr != NULL) && (entry_ptr->opcode != opcode); | |
116 | entry_ptr= entry_ptr->next) { | |
117 | } | |
118 | ||
119 | if (entry_ptr == NULL) { | |
120 | sprintf( outbuf, "word\t%08x", instruction ); | |
121 | } else { | |
122 | sprintf( outbuf, "%s\t", entry_ptr->mnemonic ); | |
123 | sprintop( &outbuf[strlen(outbuf)], &(entry_ptr->op1), instruction, pc, 1 ); | |
124 | sprintop( &outbuf[strlen(outbuf)], &(entry_ptr->op2), instruction, pc, 0 ); | |
125 | sprintop( &outbuf[strlen(outbuf)], &(entry_ptr->op3), instruction, pc, 0 ); | |
126 | } | |
127 | ||
128 | ||
129 | return 4; | |
130 | } | |
131 | \f | |
132 | ||
133 | /* | |
134 | * Decode an Operand of an Instruction | |
135 | * | |
136 | * Functional Description | |
137 | * | |
138 | * This module formats and writes an operand of an instruction to buf | |
139 | * based on the operand specification. When the first flag is set this | |
140 | * is the first operand of an instruction. Undefined operand types | |
141 | * cause a <dis error> message. | |
142 | * | |
143 | * Parameters | |
144 | * char *buf buffer where the operand may be printed | |
145 | * OPSPEC *opptr Pointer to an operand specification | |
146 | * UINT inst Instruction from which operand is extracted | |
147 | * UINT pc PC of instruction; used for pc-relative disp. | |
148 | * int first Flag which if nonzero indicates the first | |
149 | * operand of an instruction | |
150 | * | |
151 | * Output | |
152 | * | |
153 | * The operand specified is extracted from the instruction and is | |
154 | * written to buf in the format specified. The operand is preceded | |
155 | * by a comma if it is not the first operand of an instruction and it | |
156 | * is not a register indirect form. Registers are preceded by 'r' and | |
157 | * hex values by '0x'. | |
158 | * | |
159 | * Revision History | |
160 | * | |
161 | * Revision 1.0 11/08/85 Creation date | |
162 | */ | |
163 | ||
164 | sprintop( buf, opptr, inst, pc, first ) | |
165 | ||
166 | char *buf; | |
167 | OPSPEC *opptr; | |
168 | UINT inst; | |
169 | int pc; | |
170 | int first; | |
171 | ||
172 | { int extracted_field; | |
173 | char *cond_mask_sym; | |
174 | char cond_mask_sym_buf[6]; | |
175 | ||
176 | if (opptr->width == 0) | |
177 | return; | |
178 | ||
179 | switch(opptr->type) { | |
180 | case CRREG: | |
181 | if (!first) | |
182 | *buf++= ','; | |
183 | sprintf( buf, "cr%d", UEXT(inst,opptr->offset,opptr->width)); | |
184 | break; | |
185 | ||
186 | case FCRREG: | |
187 | if (!first) | |
188 | *buf++= ','; | |
189 | sprintf( buf, "fcr%d", UEXT(inst,opptr->offset,opptr->width)); | |
190 | break; | |
191 | ||
192 | case REGSC: | |
193 | sprintf( buf, "[r%d]", UEXT(inst,opptr->offset,opptr->width)); | |
194 | break; | |
195 | ||
196 | case REG: | |
197 | if (!first) | |
198 | *buf++= ','; | |
199 | sprintf( buf, "r%d", UEXT(inst,opptr->offset,opptr->width)); | |
200 | break; | |
201 | ||
202 | case HEX: | |
203 | if (!first) | |
204 | *buf++= ','; | |
205 | extracted_field= UEXT(inst, opptr->offset, opptr->width); | |
206 | if (extracted_field == 0) { | |
207 | sprintf( buf, "0" ); | |
208 | } else { | |
209 | sprintf( buf, "0x%02x", extracted_field ); | |
210 | } | |
211 | break; | |
212 | ||
213 | case CONDMASK: | |
214 | if (!first) | |
215 | *buf++= ','; | |
216 | extracted_field= UEXT(inst, opptr->offset, opptr->width); | |
217 | switch (extracted_field & 0x0f) { | |
218 | case 0x1: cond_mask_sym= "gt0"; | |
219 | break; | |
220 | case 0x2: cond_mask_sym= "eq0"; | |
221 | break; | |
222 | case 0x3: cond_mask_sym= "ge0"; | |
223 | break; | |
224 | case 0xc: cond_mask_sym= "lt0"; | |
225 | break; | |
226 | case 0xd: cond_mask_sym= "ne0"; | |
227 | break; | |
228 | case 0xe: cond_mask_sym= "le0"; | |
229 | break; | |
230 | default: cond_mask_sym= cond_mask_sym_buf; | |
231 | sprintf( cond_mask_sym_buf, | |
232 | "%x", | |
233 | extracted_field ); | |
234 | break; | |
235 | } | |
236 | strcpy( buf, cond_mask_sym ); | |
237 | break; | |
238 | ||
239 | case PCREL: | |
240 | if (!first) | |
241 | *buf++= ','; | |
242 | sprint_address( pc + 4*(SEXT(inst,opptr->offset,opptr->width)), | |
243 | buf ); | |
244 | break; | |
245 | ||
246 | case CONT: | |
247 | sprintf( buf, | |
248 | "%d,r%d", | |
249 | UEXT(inst,opptr->offset,5), | |
250 | UEXT(inst,(opptr->offset)+5,5) ); | |
251 | break; | |
252 | ||
253 | case BF: | |
254 | if (!first) | |
255 | *buf++= ','; | |
256 | sprintf( buf, | |
257 | "%d<%d>", | |
258 | UEXT(inst,(opptr->offset)+5,5), | |
259 | UEXT(inst,opptr->offset,5)); | |
260 | break; | |
261 | ||
262 | default: | |
263 | sprintf( buf, "<dis error: %08x>", inst ); | |
264 | } | |
265 | ||
266 | } | |
267 | ||
268 | /* | |
269 | * Initialize the Disassembler Instruction Table | |
270 | * | |
271 | * Initialize the hash table and instruction table for the disassembler. | |
272 | * This should be called once before the first call to disasm(). | |
273 | * | |
274 | * Parameters | |
275 | * | |
276 | * Output | |
277 | * | |
278 | * If the debug option is selected, certain statistics about the hashing | |
279 | * distribution are written to stdout. | |
280 | * | |
281 | * Revision History | |
282 | * | |
283 | * Revision 1.0 11/08/85 Creation date | |
284 | */ | |
285 | ||
286 | init_disasm() | |
287 | { | |
288 | int i,size; | |
289 | ||
290 | for (i=0 ; i < HASHVAL ; i++) | |
291 | hashtable[i] = NULL; | |
292 | ||
293 | for (i=0, size = sizeof(instructions) / sizeof(INSTAB) ; i < size ; | |
294 | install(&instructions[i++])); | |
295 | ||
296 | } | |
297 | ||
298 | /* | |
299 | * Insert an instruction into the disassembler table by hashing the | |
300 | * opcode and inserting it into the linked list for that hash value. | |
301 | * | |
302 | * Parameters | |
303 | * | |
304 | * INSTAB *instptr Pointer to the entry in the instruction table | |
305 | * to be installed | |
306 | * | |
307 | * Revision 1.0 11/08/85 Creation date | |
308 | * 05/11/89 R. TRAWICK ADAPTED FROM MOTOROLA | |
309 | */ | |
310 | ||
311 | install(instptr) | |
312 | INSTAB *instptr; | |
313 | { | |
314 | UINT i; | |
315 | ||
316 | i = (instptr->opcode) % HASHVAL; | |
317 | instptr->next = hashtable[i]; | |
318 | hashtable[i] = instptr; | |
319 | } | |
320 | \f | |
321 | ||
322 | /* adapted from print_address in printcmd by R. Trawick 5/15/89. The two should | |
323 | be combined. | |
324 | */ | |
325 | ||
326 | void sprint_address (addr, buffer) | |
327 | ||
328 | CORE_ADDR addr; | |
329 | char *buffer; | |
330 | ||
331 | { | |
1ab3bf1b | 332 | struct minimal_symbol *msymbol; |
8aa13b87 JK |
333 | struct symbol *fs; |
334 | char *name; | |
335 | int name_location; | |
336 | ||
337 | sprintf ( buffer, "0x%x", addr); | |
338 | ||
339 | fs = find_pc_function (addr); | |
340 | ||
341 | if (!fs) { | |
1ab3bf1b | 342 | msymbol = lookup_minimal_symbol_by_pc (addr); |
8aa13b87 | 343 | |
1ab3bf1b | 344 | if (i == NULL) return;/* If nothing comes through, don't |
8aa13b87 JK |
345 | print anything symbolic */ |
346 | ||
1ab3bf1b JG |
347 | name = msymbol -> name; |
348 | name_location = msymbol -> address; | |
8aa13b87 JK |
349 | } else { |
350 | name = fs->name; | |
351 | name_location = BLOCK_START (SYMBOL_BLOCK_VALUE (fs)); | |
352 | } | |
353 | ||
354 | if (addr - name_location) | |
355 | sprintf (buffer, " <%s+%d>", name, addr - name_location); | |
356 | else | |
357 | sprintf (buffer, " <%s>", name); | |
358 | } |