]>
Commit | Line | Data |
---|---|---|
d0ba1cea ILT |
1 | /* Print mips instructions for GDB, the GNU debugger, or for objdump. |
2 | Copyright 1989, 1991, 1992 Free Software Foundation, Inc. | |
3 | Contributed by Nobuyuki Hikichi([email protected]). | |
4 | ||
5 | This file is part of GDB. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program; if not, write to the Free Software | |
30b1724c | 19 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
d0ba1cea | 20 | |
5d0734a7 | 21 | #include <ansidecl.h> |
d0ba1cea | 22 | #include "sysdep.h" |
5d0734a7 | 23 | #include "dis-asm.h" |
d0ba1cea ILT |
24 | #include "opcode/mips.h" |
25 | ||
d0ba1cea ILT |
26 | /* Mips instructions are never longer than this many bytes. */ |
27 | #define MAXLEN 4 | |
d0ba1cea ILT |
28 | \f |
29 | /* FIXME: This should be shared with gdb somehow. */ | |
30 | #define REGISTER_NAMES \ | |
31 | { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", \ | |
32 | "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", \ | |
33 | "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", \ | |
34 | "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", \ | |
35 | "sr", "lo", "hi", "bad", "cause","pc", \ | |
36 | "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ | |
37 | "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", \ | |
38 | "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",\ | |
39 | "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",\ | |
40 | "fsr", "fir", "fp", "inx", "rand", "tlblo","ctxt", "tlbhi",\ | |
41 | "epc", "prid"\ | |
42 | } | |
43 | ||
44 | static CONST char * CONST reg_names[] = REGISTER_NAMES; | |
45 | \f | |
46 | /* subroutine */ | |
5d0734a7 JK |
47 | static void |
48 | print_insn_arg (d, l, pc, info) | |
547998d2 | 49 | const char *d; |
5d0734a7 | 50 | register unsigned long int l; |
d0ba1cea | 51 | bfd_vma pc; |
5d0734a7 | 52 | struct disassemble_info *info; |
d0ba1cea | 53 | { |
fde326fb ILT |
54 | int delta; |
55 | ||
d0ba1cea ILT |
56 | switch (*d) |
57 | { | |
58 | case ',': | |
59 | case '(': | |
60 | case ')': | |
5d0734a7 | 61 | (*info->fprintf_func) (info->stream, "%c", *d); |
d0ba1cea ILT |
62 | break; |
63 | ||
64 | case 's': | |
fde326fb ILT |
65 | case 'b': |
66 | case 'r': | |
67 | case 'v': | |
5d0734a7 JK |
68 | (*info->fprintf_func) (info->stream, "$%s", |
69 | reg_names[(l >> OP_SH_RS) & OP_MASK_RS]); | |
d0ba1cea ILT |
70 | break; |
71 | ||
72 | case 't': | |
fde326fb | 73 | case 'w': |
5d0734a7 JK |
74 | (*info->fprintf_func) (info->stream, "$%s", |
75 | reg_names[(l >> OP_SH_RT) & OP_MASK_RT]); | |
d0ba1cea ILT |
76 | break; |
77 | ||
78 | case 'i': | |
fde326fb | 79 | case 'u': |
30b1724c | 80 | (*info->fprintf_func) (info->stream, "0x%x", |
5d0734a7 | 81 | (l >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE); |
d0ba1cea ILT |
82 | break; |
83 | ||
84 | case 'j': /* same as i, but sign-extended */ | |
fde326fb ILT |
85 | case 'o': |
86 | delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA; | |
87 | if (delta & 0x8000) | |
88 | delta |= ~0xffff; | |
5d0734a7 | 89 | (*info->fprintf_func) (info->stream, "%d", |
fde326fb | 90 | delta); |
d0ba1cea ILT |
91 | break; |
92 | ||
30b1724c ILT |
93 | case 'h': |
94 | (*info->fprintf_func) (info->stream, "0x%x", | |
95 | (unsigned int) ((l >> OP_SH_PREFX) | |
96 | & OP_MASK_PREFX)); | |
97 | break; | |
98 | ||
99 | case 'k': | |
100 | (*info->fprintf_func) (info->stream, "0x%x", | |
101 | (unsigned int) ((l >> OP_SH_CACHE) | |
102 | & OP_MASK_CACHE)); | |
103 | break; | |
104 | ||
d0ba1cea | 105 | case 'a': |
fde326fb ILT |
106 | (*info->print_address_func) |
107 | (((pc & 0xF0000000) | (((l >> OP_SH_TARGET) & OP_MASK_TARGET) << 2)), | |
108 | info); | |
d0ba1cea ILT |
109 | break; |
110 | ||
fde326fb ILT |
111 | case 'p': |
112 | /* sign extend the displacement */ | |
113 | delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA; | |
114 | if (delta & 0x8000) | |
115 | delta |= ~0xffff; | |
116 | (*info->print_address_func) | |
117 | ((delta << 2) + pc + 4, | |
118 | info); | |
d0ba1cea ILT |
119 | break; |
120 | ||
121 | case 'd': | |
5d0734a7 JK |
122 | (*info->fprintf_func) (info->stream, "$%s", |
123 | reg_names[(l >> OP_SH_RD) & OP_MASK_RD]); | |
d0ba1cea ILT |
124 | break; |
125 | ||
547998d2 ILT |
126 | case 'z': |
127 | (*info->fprintf_func) (info->stream, "$%s", reg_names[0]); | |
128 | break; | |
129 | ||
fde326fb | 130 | case '<': |
5d0734a7 JK |
131 | (*info->fprintf_func) (info->stream, "0x%x", |
132 | (l >> OP_SH_SHAMT) & OP_MASK_SHAMT); | |
d0ba1cea ILT |
133 | break; |
134 | ||
fde326fb | 135 | case 'c': |
5d0734a7 JK |
136 | (*info->fprintf_func) (info->stream, "0x%x", |
137 | (l >> OP_SH_CODE) & OP_MASK_CODE); | |
d0ba1cea ILT |
138 | break; |
139 | ||
fde326fb ILT |
140 | case 'C': |
141 | (*info->fprintf_func) (info->stream, "0x%x", | |
142 | (l >> OP_SH_COPZ) & OP_MASK_COPZ); | |
143 | break; | |
144 | ||
145 | case 'B': | |
146 | (*info->fprintf_func) (info->stream, "0x%x", | |
147 | (l >> OP_SH_SYSCALL) & OP_MASK_SYSCALL); | |
148 | break; | |
149 | ||
d0ba1cea | 150 | case 'S': |
fde326fb | 151 | case 'V': |
5d0734a7 JK |
152 | (*info->fprintf_func) (info->stream, "$f%d", |
153 | (l >> OP_SH_FS) & OP_MASK_FS); | |
d0ba1cea ILT |
154 | break; |
155 | ||
156 | case 'T': | |
fde326fb | 157 | case 'W': |
5d0734a7 JK |
158 | (*info->fprintf_func) (info->stream, "$f%d", |
159 | (l >> OP_SH_FT) & OP_MASK_FT); | |
d0ba1cea ILT |
160 | break; |
161 | ||
162 | case 'D': | |
5d0734a7 JK |
163 | (*info->fprintf_func) (info->stream, "$f%d", |
164 | (l >> OP_SH_FD) & OP_MASK_FD); | |
d0ba1cea ILT |
165 | break; |
166 | ||
30b1724c ILT |
167 | case 'R': |
168 | (*info->fprintf_func) (info->stream, "$f%d", | |
169 | (l >> OP_SH_FR) & OP_MASK_FR); | |
170 | break; | |
171 | ||
fde326fb ILT |
172 | case 'E': |
173 | (*info->fprintf_func) (info->stream, "$%d", | |
174 | (l >> OP_SH_RT) & OP_MASK_RT); | |
175 | break; | |
176 | ||
177 | case 'G': | |
178 | (*info->fprintf_func) (info->stream, "$%d", | |
179 | (l >> OP_SH_RD) & OP_MASK_RD); | |
180 | break; | |
181 | ||
30b1724c ILT |
182 | case 'N': |
183 | (*info->fprintf_func) (info->stream, "$fcc%d", | |
184 | (l >> OP_SH_BCC) & OP_MASK_BCC); | |
185 | break; | |
186 | ||
187 | case 'M': | |
188 | (*info->fprintf_func) (info->stream, "$fcc%d", | |
189 | (l >> OP_SH_CCC) & OP_MASK_CCC); | |
190 | break; | |
191 | ||
d0ba1cea | 192 | default: |
5d0734a7 JK |
193 | (*info->fprintf_func) (info->stream, |
194 | "# internal error, undefined modifier(%c)", *d); | |
d0ba1cea ILT |
195 | break; |
196 | } | |
197 | } | |
198 | \f | |
199 | /* Print the mips instruction at address MEMADDR in debugged memory, | |
5d0734a7 | 200 | on using INFO. Returns length of the instruction, in bytes, which is |
d0ba1cea ILT |
201 | always 4. BIGENDIAN must be 1 if this is big-endian code, 0 if |
202 | this is little-endian code. */ | |
203 | ||
30b1724c | 204 | static int |
5d0734a7 | 205 | _print_insn_mips (memaddr, word, info) |
d0ba1cea | 206 | bfd_vma memaddr; |
5d0734a7 JK |
207 | struct disassemble_info *info; |
208 | unsigned long int word; | |
d0ba1cea | 209 | { |
30b1724c ILT |
210 | register const struct mips_opcode *op; |
211 | static boolean init = 0; | |
212 | static const struct mips_opcode *mips_hash[OP_MASK_OP + 1]; | |
d0ba1cea | 213 | |
30b1724c ILT |
214 | /* Build a hash table to shorten the search time. */ |
215 | if (! init) | |
d0ba1cea | 216 | { |
30b1724c ILT |
217 | unsigned int i; |
218 | ||
219 | for (i = 0; i <= OP_MASK_OP; i++) | |
fde326fb | 220 | { |
30b1724c ILT |
221 | for (op = mips_opcodes; op < &mips_opcodes[NUMOPCODES]; op++) |
222 | { | |
223 | if (op->pinfo == INSN_MACRO) | |
224 | continue; | |
225 | if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP)) | |
226 | { | |
227 | mips_hash[i] = op; | |
228 | break; | |
229 | } | |
230 | } | |
231 | } | |
232 | ||
233 | init = 1; | |
d0ba1cea ILT |
234 | } |
235 | ||
30b1724c ILT |
236 | op = mips_hash[(word >> OP_SH_OP) & OP_MASK_OP]; |
237 | if (op != NULL) | |
d0ba1cea | 238 | { |
30b1724c ILT |
239 | for (; op < &mips_opcodes[NUMOPCODES]; op++) |
240 | { | |
241 | if (op->pinfo != INSN_MACRO && (word & op->mask) == op->match) | |
242 | { | |
243 | register const char *d; | |
244 | ||
245 | (*info->fprintf_func) (info->stream, "%s", op->name); | |
246 | ||
247 | d = op->args; | |
248 | if (d != NULL) | |
249 | { | |
250 | (*info->fprintf_func) (info->stream, " "); | |
251 | for (; *d != '\0'; d++) | |
252 | print_insn_arg (d, word, memaddr, info); | |
253 | } | |
254 | ||
255 | return 4; | |
256 | } | |
257 | } | |
d0ba1cea ILT |
258 | } |
259 | ||
30b1724c ILT |
260 | /* Handle undefined instructions. */ |
261 | (*info->fprintf_func) (info->stream, "0x%x", word); | |
d0ba1cea ILT |
262 | return 4; |
263 | } | |
5d0734a7 JK |
264 | |
265 | int | |
266 | print_insn_big_mips (memaddr, info) | |
267 | bfd_vma memaddr; | |
268 | struct disassemble_info *info; | |
269 | { | |
270 | bfd_byte buffer[4]; | |
271 | int status = (*info->read_memory_func) (memaddr, buffer, 4, info); | |
272 | if (status == 0) | |
30b1724c | 273 | return _print_insn_mips (memaddr, (unsigned long) bfd_getb32 (buffer), info); |
5d0734a7 JK |
274 | else |
275 | { | |
276 | (*info->memory_error_func) (status, memaddr, info); | |
277 | return -1; | |
278 | } | |
279 | } | |
280 | ||
281 | int | |
282 | print_insn_little_mips (memaddr, info) | |
283 | bfd_vma memaddr; | |
284 | struct disassemble_info *info; | |
285 | { | |
286 | bfd_byte buffer[4]; | |
287 | int status = (*info->read_memory_func) (memaddr, buffer, 4, info); | |
288 | if (status == 0) | |
30b1724c | 289 | return _print_insn_mips (memaddr, (unsigned long) bfd_getl32 (buffer), info); |
5d0734a7 JK |
290 | else |
291 | { | |
292 | (*info->memory_error_func) (status, memaddr, info); | |
293 | return -1; | |
294 | } | |
295 | } |