]>
Commit | Line | Data |
---|---|---|
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 | |
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
20 | ||
21 | #include <ansidecl.h> | |
22 | #include "sysdep.h" | |
23 | #include "dis-asm.h" | |
24 | #include "opcode/mips.h" | |
25 | ||
26 | /* FIXME: we need direct access to the swapping functions. */ | |
27 | #include "libbfd.h" | |
28 | ||
29 | /* Mips instructions are never longer than this many bytes. */ | |
30 | #define MAXLEN 4 | |
31 | \f | |
32 | /* FIXME: This should be shared with gdb somehow. */ | |
33 | #define REGISTER_NAMES \ | |
34 | { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", \ | |
35 | "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", \ | |
36 | "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", \ | |
37 | "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", \ | |
38 | "sr", "lo", "hi", "bad", "cause","pc", \ | |
39 | "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ | |
40 | "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", \ | |
41 | "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",\ | |
42 | "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",\ | |
43 | "fsr", "fir", "fp", "inx", "rand", "tlblo","ctxt", "tlbhi",\ | |
44 | "epc", "prid"\ | |
45 | } | |
46 | ||
47 | static CONST char * CONST reg_names[] = REGISTER_NAMES; | |
48 | \f | |
49 | /* subroutine */ | |
50 | static void | |
51 | print_insn_arg (d, l, pc, info) | |
52 | const char *d; | |
53 | register unsigned long int l; | |
54 | bfd_vma pc; | |
55 | struct disassemble_info *info; | |
56 | { | |
57 | int delta; | |
58 | ||
59 | switch (*d) | |
60 | { | |
61 | case ',': | |
62 | case '(': | |
63 | case ')': | |
64 | (*info->fprintf_func) (info->stream, "%c", *d); | |
65 | break; | |
66 | ||
67 | case 's': | |
68 | case 'b': | |
69 | case 'r': | |
70 | case 'v': | |
71 | (*info->fprintf_func) (info->stream, "$%s", | |
72 | reg_names[(l >> OP_SH_RS) & OP_MASK_RS]); | |
73 | break; | |
74 | ||
75 | case 't': | |
76 | case 'w': | |
77 | (*info->fprintf_func) (info->stream, "$%s", | |
78 | reg_names[(l >> OP_SH_RT) & OP_MASK_RT]); | |
79 | break; | |
80 | ||
81 | case 'i': | |
82 | case 'u': | |
83 | (*info->fprintf_func) (info->stream, "%d", | |
84 | (l >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE); | |
85 | break; | |
86 | ||
87 | case 'j': /* same as i, but sign-extended */ | |
88 | case 'o': | |
89 | delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA; | |
90 | if (delta & 0x8000) | |
91 | delta |= ~0xffff; | |
92 | (*info->fprintf_func) (info->stream, "%d", | |
93 | delta); | |
94 | break; | |
95 | ||
96 | case 'a': | |
97 | (*info->print_address_func) | |
98 | (((pc & 0xF0000000) | (((l >> OP_SH_TARGET) & OP_MASK_TARGET) << 2)), | |
99 | info); | |
100 | break; | |
101 | ||
102 | case 'p': | |
103 | /* sign extend the displacement */ | |
104 | delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA; | |
105 | if (delta & 0x8000) | |
106 | delta |= ~0xffff; | |
107 | (*info->print_address_func) | |
108 | ((delta << 2) + pc + 4, | |
109 | info); | |
110 | break; | |
111 | ||
112 | case 'd': | |
113 | (*info->fprintf_func) (info->stream, "$%s", | |
114 | reg_names[(l >> OP_SH_RD) & OP_MASK_RD]); | |
115 | break; | |
116 | ||
117 | case 'z': | |
118 | (*info->fprintf_func) (info->stream, "$%s", reg_names[0]); | |
119 | break; | |
120 | ||
121 | case '<': | |
122 | (*info->fprintf_func) (info->stream, "0x%x", | |
123 | (l >> OP_SH_SHAMT) & OP_MASK_SHAMT); | |
124 | break; | |
125 | ||
126 | case 'c': | |
127 | (*info->fprintf_func) (info->stream, "0x%x", | |
128 | (l >> OP_SH_CODE) & OP_MASK_CODE); | |
129 | break; | |
130 | ||
131 | case 'C': | |
132 | (*info->fprintf_func) (info->stream, "0x%x", | |
133 | (l >> OP_SH_COPZ) & OP_MASK_COPZ); | |
134 | break; | |
135 | ||
136 | case 'B': | |
137 | (*info->fprintf_func) (info->stream, "0x%x", | |
138 | (l >> OP_SH_SYSCALL) & OP_MASK_SYSCALL); | |
139 | break; | |
140 | ||
141 | case 'S': | |
142 | case 'V': | |
143 | (*info->fprintf_func) (info->stream, "$f%d", | |
144 | (l >> OP_SH_FS) & OP_MASK_FS); | |
145 | break; | |
146 | ||
147 | case 'T': | |
148 | case 'W': | |
149 | (*info->fprintf_func) (info->stream, "$f%d", | |
150 | (l >> OP_SH_FT) & OP_MASK_FT); | |
151 | break; | |
152 | ||
153 | case 'D': | |
154 | (*info->fprintf_func) (info->stream, "$f%d", | |
155 | (l >> OP_SH_FD) & OP_MASK_FD); | |
156 | break; | |
157 | ||
158 | case 'E': | |
159 | (*info->fprintf_func) (info->stream, "$%d", | |
160 | (l >> OP_SH_RT) & OP_MASK_RT); | |
161 | break; | |
162 | ||
163 | case 'G': | |
164 | (*info->fprintf_func) (info->stream, "$%d", | |
165 | (l >> OP_SH_RD) & OP_MASK_RD); | |
166 | break; | |
167 | ||
168 | default: | |
169 | (*info->fprintf_func) (info->stream, | |
170 | "# internal error, undefined modifier(%c)", *d); | |
171 | break; | |
172 | } | |
173 | } | |
174 | \f | |
175 | /* Print the mips instruction at address MEMADDR in debugged memory, | |
176 | on using INFO. Returns length of the instruction, in bytes, which is | |
177 | always 4. BIGENDIAN must be 1 if this is big-endian code, 0 if | |
178 | this is little-endian code. */ | |
179 | ||
180 | int | |
181 | _print_insn_mips (memaddr, word, info) | |
182 | bfd_vma memaddr; | |
183 | struct disassemble_info *info; | |
184 | unsigned long int word; | |
185 | { | |
186 | register int i; | |
187 | register const char *d; | |
188 | ||
189 | for (i = 0; i < NUMOPCODES; i++) | |
190 | { | |
191 | if (mips_opcodes[i].pinfo != INSN_MACRO) | |
192 | { | |
193 | register unsigned int match = mips_opcodes[i].match; | |
194 | register unsigned int mask = mips_opcodes[i].mask; | |
195 | if ((word & mask) == match) | |
196 | break; | |
197 | } | |
198 | } | |
199 | ||
200 | /* Handle undefined instructions. */ | |
201 | if (i == NUMOPCODES) | |
202 | { | |
203 | (*info->fprintf_func) (info->stream, "0x%x", word); | |
204 | return 4; | |
205 | } | |
206 | ||
207 | (*info->fprintf_func) (info->stream, "%s", mips_opcodes[i].name); | |
208 | ||
209 | if (!(d = mips_opcodes[i].args)) | |
210 | return 4; | |
211 | ||
212 | (*info->fprintf_func) (info->stream, " "); | |
213 | ||
214 | while (*d) | |
215 | print_insn_arg (d++, word, memaddr, info); | |
216 | ||
217 | return 4; | |
218 | } | |
219 | ||
220 | int | |
221 | print_insn_big_mips (memaddr, info) | |
222 | bfd_vma memaddr; | |
223 | struct disassemble_info *info; | |
224 | { | |
225 | bfd_byte buffer[4]; | |
226 | int status = (*info->read_memory_func) (memaddr, buffer, 4, info); | |
227 | if (status == 0) | |
228 | return _print_insn_mips (memaddr, _do_getb32 (buffer), info); | |
229 | else | |
230 | { | |
231 | (*info->memory_error_func) (status, memaddr, info); | |
232 | return -1; | |
233 | } | |
234 | } | |
235 | ||
236 | int | |
237 | print_insn_little_mips (memaddr, info) | |
238 | bfd_vma memaddr; | |
239 | struct disassemble_info *info; | |
240 | { | |
241 | bfd_byte buffer[4]; | |
242 | int status = (*info->read_memory_func) (memaddr, buffer, 4, info); | |
243 | if (status == 0) | |
244 | return _print_insn_mips (memaddr, _do_getl32 (buffer), info); | |
245 | else | |
246 | { | |
247 | (*info->memory_error_func) (status, memaddr, info); | |
248 | return -1; | |
249 | } | |
250 | } |