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(hikichi@sra.co.jp). | |
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 | ||
5d0734a7 | 21 | #include <ansidecl.h> |
d0ba1cea | 22 | #include "sysdep.h" |
5d0734a7 | 23 | #include "dis-asm.h" |
d0ba1cea ILT |
24 | #include "opcode/mips.h" |
25 | ||
26 | /* FIXME: we need direct access to the swapping functions. */ | |
27 | #include "libbfd.h" | |
28 | ||
29 | /* We use bfd_vma in a couple of places where gdb expects CORE_ADDR. */ | |
30 | #ifdef HOST_64_BIT | |
31 | #error FIXME: bfd_vma will not match gdb expectations | |
32 | #endif | |
33 | ||
34 | /* This file is used both by gdb and by objdump. A program which | |
35 | wants to use this code must provide an external function | |
36 | print_address. */ | |
37 | extern int print_address PARAMS ((bfd_vma, FILE *)); | |
38 | ||
39 | /* Mips instructions are never longer than this many bytes. */ | |
40 | #define MAXLEN 4 | |
41 | ||
42 | /* Number of elements in the opcode table. */ | |
43 | #define NOPCODES (sizeof mips_opcodes / sizeof mips_opcodes[0]) | |
44 | \f | |
45 | /* FIXME: This should be shared with gdb somehow. */ | |
46 | #define REGISTER_NAMES \ | |
47 | { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", \ | |
48 | "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", \ | |
49 | "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", \ | |
50 | "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", \ | |
51 | "sr", "lo", "hi", "bad", "cause","pc", \ | |
52 | "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ | |
53 | "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", \ | |
54 | "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",\ | |
55 | "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",\ | |
56 | "fsr", "fir", "fp", "inx", "rand", "tlblo","ctxt", "tlbhi",\ | |
57 | "epc", "prid"\ | |
58 | } | |
59 | ||
60 | static CONST char * CONST reg_names[] = REGISTER_NAMES; | |
61 | \f | |
62 | /* subroutine */ | |
5d0734a7 JK |
63 | static void |
64 | print_insn_arg (d, l, pc, info) | |
d0ba1cea | 65 | char *d; |
5d0734a7 | 66 | register unsigned long int l; |
d0ba1cea | 67 | bfd_vma pc; |
5d0734a7 | 68 | struct disassemble_info *info; |
d0ba1cea ILT |
69 | { |
70 | switch (*d) | |
71 | { | |
72 | case ',': | |
73 | case '(': | |
74 | case ')': | |
5d0734a7 | 75 | (*info->fprintf_func) (info->stream, "%c", *d); |
d0ba1cea ILT |
76 | break; |
77 | ||
78 | case 's': | |
5d0734a7 JK |
79 | (*info->fprintf_func) (info->stream, "$%s", |
80 | reg_names[(l >> OP_SH_RS) & OP_MASK_RS]); | |
d0ba1cea ILT |
81 | break; |
82 | ||
83 | case 't': | |
5d0734a7 JK |
84 | (*info->fprintf_func) (info->stream, "$%s", |
85 | reg_names[(l >> OP_SH_RT) & OP_MASK_RT]); | |
d0ba1cea ILT |
86 | break; |
87 | ||
88 | case 'i': | |
5d0734a7 JK |
89 | (*info->fprintf_func) (info->stream, "%d", |
90 | (l >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE); | |
d0ba1cea ILT |
91 | break; |
92 | ||
93 | case 'j': /* same as i, but sign-extended */ | |
5d0734a7 JK |
94 | (*info->fprintf_func) (info->stream, "%d", |
95 | (l >> OP_SH_DELTA) & OP_MASK_DELTA); | |
d0ba1cea ILT |
96 | break; |
97 | ||
98 | case 'a': | |
5d0734a7 JK |
99 | print_address (((pc & 0xF0000000) |
100 | | (((l >> OP_SH_TARGET) & OP_MASK_TARGET) << 2)), | |
101 | info->stream); | |
d0ba1cea ILT |
102 | break; |
103 | ||
104 | case 'b': | |
5d0734a7 JK |
105 | print_address ((((l >> OP_SH_DELTA) & OP_MASK_DELTA) << 2) + pc + 4, |
106 | info->stream); | |
d0ba1cea ILT |
107 | break; |
108 | ||
109 | case 'd': | |
5d0734a7 JK |
110 | (*info->fprintf_func) (info->stream, "$%s", |
111 | reg_names[(l >> OP_SH_RD) & OP_MASK_RD]); | |
d0ba1cea ILT |
112 | break; |
113 | ||
114 | case 'h': | |
5d0734a7 JK |
115 | (*info->fprintf_func) (info->stream, "0x%x", |
116 | (l >> OP_SH_SHAMT) & OP_MASK_SHAMT); | |
d0ba1cea ILT |
117 | break; |
118 | ||
119 | case 'B': | |
5d0734a7 JK |
120 | (*info->fprintf_func) (info->stream, "0x%x", |
121 | (l >> OP_SH_CODE) & OP_MASK_CODE); | |
d0ba1cea ILT |
122 | break; |
123 | ||
124 | case 'S': | |
5d0734a7 JK |
125 | (*info->fprintf_func) (info->stream, "$f%d", |
126 | (l >> OP_SH_FS) & OP_MASK_FS); | |
d0ba1cea ILT |
127 | break; |
128 | ||
129 | case 'T': | |
5d0734a7 JK |
130 | (*info->fprintf_func) (info->stream, "$f%d", |
131 | (l >> OP_SH_FT) & OP_MASK_FT); | |
d0ba1cea ILT |
132 | break; |
133 | ||
134 | case 'D': | |
5d0734a7 JK |
135 | (*info->fprintf_func) (info->stream, "$f%d", |
136 | (l >> OP_SH_FD) & OP_MASK_FD); | |
d0ba1cea ILT |
137 | break; |
138 | ||
139 | default: | |
5d0734a7 JK |
140 | (*info->fprintf_func) (info->stream, |
141 | "# internal error, undefined modifier(%c)", *d); | |
d0ba1cea ILT |
142 | break; |
143 | } | |
144 | } | |
145 | \f | |
146 | /* Print the mips instruction at address MEMADDR in debugged memory, | |
5d0734a7 | 147 | on using INFO. Returns length of the instruction, in bytes, which is |
d0ba1cea ILT |
148 | always 4. BIGENDIAN must be 1 if this is big-endian code, 0 if |
149 | this is little-endian code. */ | |
150 | ||
151 | int | |
5d0734a7 | 152 | _print_insn_mips (memaddr, word, info) |
d0ba1cea | 153 | bfd_vma memaddr; |
5d0734a7 JK |
154 | struct disassemble_info *info; |
155 | unsigned long int word; | |
d0ba1cea ILT |
156 | { |
157 | register int i; | |
158 | register char *d; | |
d0ba1cea ILT |
159 | |
160 | for (i = 0; i < NOPCODES; i++) | |
161 | { | |
162 | register unsigned int opcode = mips_opcodes[i].opcode; | |
163 | register unsigned int match = mips_opcodes[i].match; | |
5d0734a7 | 164 | if ((word & match) == opcode) |
d0ba1cea ILT |
165 | break; |
166 | } | |
167 | ||
168 | /* Handle undefined instructions. */ | |
169 | if (i == NOPCODES) | |
170 | { | |
5d0734a7 | 171 | (*info->fprintf_func) (info->stream, "0x%x", word); |
d0ba1cea ILT |
172 | return 4; |
173 | } | |
174 | ||
5d0734a7 | 175 | (*info->fprintf_func) (info->stream, "%s", mips_opcodes[i].name); |
d0ba1cea ILT |
176 | |
177 | if (!(d = mips_opcodes[i].args)) | |
178 | return 4; | |
179 | ||
5d0734a7 | 180 | (*info->fprintf_func) (info->stream, " "); |
d0ba1cea ILT |
181 | |
182 | while (*d) | |
5d0734a7 | 183 | print_insn_arg (d++, word, memaddr, info); |
d0ba1cea ILT |
184 | |
185 | return 4; | |
186 | } | |
5d0734a7 JK |
187 | |
188 | int | |
189 | print_insn_big_mips (memaddr, info) | |
190 | bfd_vma memaddr; | |
191 | struct disassemble_info *info; | |
192 | { | |
193 | bfd_byte buffer[4]; | |
194 | int status = (*info->read_memory_func) (memaddr, buffer, 4, info); | |
195 | if (status == 0) | |
196 | return _print_insn_mips (memaddr, _do_getb32 (buffer), info); | |
197 | else | |
198 | { | |
199 | (*info->memory_error_func) (status, memaddr, info); | |
200 | return -1; | |
201 | } | |
202 | } | |
203 | ||
204 | int | |
205 | print_insn_little_mips (memaddr, info) | |
206 | bfd_vma memaddr; | |
207 | struct disassemble_info *info; | |
208 | { | |
209 | bfd_byte buffer[4]; | |
210 | int status = (*info->read_memory_func) (memaddr, buffer, 4, info); | |
211 | if (status == 0) | |
212 | return _print_insn_mips (memaddr, _do_getl32 (buffer), info); | |
213 | else | |
214 | { | |
215 | (*info->memory_error_func) (status, memaddr, info); | |
216 | return -1; | |
217 | } | |
218 | } |