1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* ppc-dis.c -- Disassemble PowerPC instructions |
3 | Copyright (C) 1994-2016 Free Software Foundation, Inc. |
4 | Written by Ian Lance Taylor, Cygnus Support |
5 | |
6 | This file is part of GDB, GAS, and the GNU binutils. |
7 | |
8 | */ |
9 | |
10 | #include <asm/cputable.h> |
11 | #include <asm/cpu_has_feature.h> |
12 | #include "nonstdio.h" |
13 | #include "ansidecl.h" |
14 | #include "ppc.h" |
15 | #include "dis-asm.h" |
16 | |
17 | /* This file provides several disassembler functions, all of which use |
18 | the disassembler interface defined in dis-asm.h. Several functions |
19 | are provided because this file handles disassembly for the PowerPC |
20 | in both big and little endian mode and also for the POWER (RS/6000) |
21 | chip. */ |
22 | |
23 | /* Extract the operand value from the PowerPC or POWER instruction. */ |
24 | |
25 | static long |
26 | operand_value_powerpc (const struct powerpc_operand *operand, |
27 | unsigned long insn, ppc_cpu_t dialect) |
28 | { |
29 | long value; |
30 | int invalid; |
31 | /* Extract the value from the instruction. */ |
32 | if (operand->extract) |
33 | value = (*operand->extract) (insn, dialect, &invalid); |
34 | else |
35 | { |
36 | if (operand->shift >= 0) |
37 | value = (insn >> operand->shift) & operand->bitm; |
38 | else |
39 | value = (insn << -operand->shift) & operand->bitm; |
40 | if ((operand->flags & PPC_OPERAND_SIGNED) != 0) |
41 | { |
42 | /* BITM is always some number of zeros followed by some |
43 | number of ones, followed by some number of zeros. */ |
44 | unsigned long top = operand->bitm; |
45 | /* top & -top gives the rightmost 1 bit, so this |
46 | fills in any trailing zeros. */ |
47 | top |= (top & -top) - 1; |
48 | top &= ~(top >> 1); |
49 | value = (value ^ top) - top; |
50 | } |
51 | } |
52 | |
53 | return value; |
54 | } |
55 | |
56 | /* Determine whether the optional operand(s) should be printed. */ |
57 | |
58 | static int |
59 | skip_optional_operands (const unsigned char *opindex, |
60 | unsigned long insn, ppc_cpu_t dialect) |
61 | { |
62 | const struct powerpc_operand *operand; |
63 | |
64 | for (; *opindex != 0; opindex++) |
65 | { |
66 | operand = &powerpc_operands[*opindex]; |
67 | if ((operand->flags & PPC_OPERAND_NEXT) != 0 |
68 | || ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 |
69 | && operand_value_powerpc (operand, insn, dialect) != |
70 | ppc_optional_operand_value (operand))) |
71 | return 0; |
72 | } |
73 | |
74 | return 1; |
75 | } |
76 | |
77 | /* Find a match for INSN in the opcode table, given machine DIALECT. |
78 | A DIALECT of -1 is special, matching all machine opcode variations. */ |
79 | |
80 | static const struct powerpc_opcode * |
81 | lookup_powerpc (unsigned long insn, ppc_cpu_t dialect) |
82 | { |
83 | const struct powerpc_opcode *opcode; |
84 | const struct powerpc_opcode *opcode_end; |
85 | |
86 | opcode_end = powerpc_opcodes + powerpc_num_opcodes; |
87 | /* Find the first match in the opcode table for this major opcode. */ |
88 | for (opcode = powerpc_opcodes; opcode < opcode_end; ++opcode) |
89 | { |
90 | const unsigned char *opindex; |
91 | const struct powerpc_operand *operand; |
92 | int invalid; |
93 | |
94 | if ((insn & opcode->mask) != opcode->opcode |
95 | || (dialect != (ppc_cpu_t) -1 |
96 | && ((opcode->flags & dialect) == 0 |
97 | || (opcode->deprecated & dialect) != 0))) |
98 | continue; |
99 | |
100 | /* Check validity of operands. */ |
101 | invalid = 0; |
102 | for (opindex = opcode->operands; *opindex != 0; opindex++) |
103 | { |
104 | operand = powerpc_operands + *opindex; |
105 | if (operand->extract) |
106 | (*operand->extract) (insn, dialect, &invalid); |
107 | } |
108 | if (invalid) |
109 | continue; |
110 | |
111 | return opcode; |
112 | } |
113 | |
114 | return NULL; |
115 | } |
116 | |
117 | /* Print a PowerPC or POWER instruction. */ |
118 | |
119 | int print_insn_powerpc (unsigned long insn, unsigned long memaddr) |
120 | { |
121 | const struct powerpc_opcode *opcode; |
122 | bool insn_is_short; |
123 | ppc_cpu_t dialect; |
124 | |
125 | dialect = PPC_OPCODE_PPC | PPC_OPCODE_COMMON |
126 | | PPC_OPCODE_64 | PPC_OPCODE_POWER4 | PPC_OPCODE_ALTIVEC; |
127 | |
128 | if (cpu_has_feature(CPU_FTRS_POWER5)) |
129 | dialect |= PPC_OPCODE_POWER5; |
130 | |
131 | if (cpu_has_feature(CPU_FTRS_CELL)) |
132 | dialect |= (PPC_OPCODE_CELL | PPC_OPCODE_ALTIVEC); |
133 | |
134 | if (cpu_has_feature(CPU_FTRS_POWER6)) |
135 | dialect |= (PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_ALTIVEC); |
136 | |
137 | if (cpu_has_feature(CPU_FTRS_POWER7)) |
138 | dialect |= (PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_POWER7 |
139 | | PPC_OPCODE_ALTIVEC | PPC_OPCODE_VSX); |
140 | |
141 | if (cpu_has_feature(CPU_FTRS_POWER8)) |
142 | dialect |= (PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_POWER7 |
143 | | PPC_OPCODE_POWER8 | PPC_OPCODE_HTM |
144 | | PPC_OPCODE_ALTIVEC | PPC_OPCODE_ALTIVEC2 | PPC_OPCODE_VSX); |
145 | |
146 | if (cpu_has_feature(CPU_FTRS_POWER9)) |
147 | dialect |= (PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_POWER7 |
148 | | PPC_OPCODE_POWER8 | PPC_OPCODE_POWER9 | PPC_OPCODE_HTM |
149 | | PPC_OPCODE_ALTIVEC | PPC_OPCODE_ALTIVEC2 |
150 | | PPC_OPCODE_VSX | PPC_OPCODE_VSX3); |
151 | |
152 | /* Get the major opcode of the insn. */ |
153 | opcode = NULL; |
154 | insn_is_short = false; |
155 | |
156 | if (opcode == NULL) |
157 | opcode = lookup_powerpc (insn, dialect); |
158 | if (opcode == NULL && (dialect & PPC_OPCODE_ANY) != 0) |
159 | opcode = lookup_powerpc (insn, dialect: (ppc_cpu_t) -1); |
160 | |
161 | if (opcode != NULL) |
162 | { |
163 | const unsigned char *opindex; |
164 | const struct powerpc_operand *operand; |
165 | int need_comma; |
166 | int need_paren; |
167 | int skip_optional; |
168 | |
169 | if (opcode->operands[0] != 0) |
170 | printf(fmt: "%-7s " , opcode->name); |
171 | else |
172 | printf(fmt: "%s" , opcode->name); |
173 | |
174 | if (insn_is_short) |
175 | /* The operands will be fetched out of the 16-bit instruction. */ |
176 | insn >>= 16; |
177 | |
178 | /* Now extract and print the operands. */ |
179 | need_comma = 0; |
180 | need_paren = 0; |
181 | skip_optional = -1; |
182 | for (opindex = opcode->operands; *opindex != 0; opindex++) |
183 | { |
184 | long value; |
185 | |
186 | operand = powerpc_operands + *opindex; |
187 | |
188 | /* Operands that are marked FAKE are simply ignored. We |
189 | already made sure that the extract function considered |
190 | the instruction to be valid. */ |
191 | if ((operand->flags & PPC_OPERAND_FAKE) != 0) |
192 | continue; |
193 | |
194 | /* If all of the optional operands have the value zero, |
195 | then don't print any of them. */ |
196 | if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0) |
197 | { |
198 | if (skip_optional < 0) |
199 | skip_optional = skip_optional_operands (opindex, insn, |
200 | dialect); |
201 | if (skip_optional) |
202 | continue; |
203 | } |
204 | |
205 | value = operand_value_powerpc (operand, insn, dialect); |
206 | |
207 | if (need_comma) |
208 | { |
209 | printf(fmt: "," ); |
210 | need_comma = 0; |
211 | } |
212 | |
213 | /* Print the operand as directed by the flags. */ |
214 | if ((operand->flags & PPC_OPERAND_GPR) != 0 |
215 | || ((operand->flags & PPC_OPERAND_GPR_0) != 0 && value != 0)) |
216 | printf(fmt: "r%ld" , value); |
217 | else if ((operand->flags & PPC_OPERAND_FPR) != 0) |
218 | printf(fmt: "f%ld" , value); |
219 | else if ((operand->flags & PPC_OPERAND_VR) != 0) |
220 | printf(fmt: "v%ld" , value); |
221 | else if ((operand->flags & PPC_OPERAND_VSR) != 0) |
222 | printf(fmt: "vs%ld" , value); |
223 | else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0) |
224 | print_address(memaddr: memaddr + value); |
225 | else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) |
226 | print_address(memaddr: value & 0xffffffff); |
227 | else if ((operand->flags & PPC_OPERAND_FSL) != 0) |
228 | printf(fmt: "fsl%ld" , value); |
229 | else if ((operand->flags & PPC_OPERAND_FCR) != 0) |
230 | printf(fmt: "fcr%ld" , value); |
231 | else if ((operand->flags & PPC_OPERAND_UDI) != 0) |
232 | printf(fmt: "%ld" , value); |
233 | else if ((operand->flags & PPC_OPERAND_CR_REG) != 0 |
234 | && (((dialect & PPC_OPCODE_PPC) != 0) |
235 | || ((dialect & PPC_OPCODE_VLE) != 0))) |
236 | printf(fmt: "cr%ld" , value); |
237 | else if (((operand->flags & PPC_OPERAND_CR_BIT) != 0) |
238 | && (((dialect & PPC_OPCODE_PPC) != 0) |
239 | || ((dialect & PPC_OPCODE_VLE) != 0))) |
240 | { |
241 | static const char *cbnames[4] = { "lt" , "gt" , "eq" , "so" }; |
242 | int cr; |
243 | int cc; |
244 | |
245 | cr = value >> 2; |
246 | if (cr != 0) |
247 | printf(fmt: "4*cr%d+" , cr); |
248 | cc = value & 3; |
249 | printf(fmt: "%s" , cbnames[cc]); |
250 | } |
251 | else |
252 | printf(fmt: "%d" , (int) value); |
253 | |
254 | if (need_paren) |
255 | { |
256 | printf(fmt: ")" ); |
257 | need_paren = 0; |
258 | } |
259 | |
260 | if ((operand->flags & PPC_OPERAND_PARENS) == 0) |
261 | need_comma = 1; |
262 | else |
263 | { |
264 | printf(fmt: "(" ); |
265 | need_paren = 1; |
266 | } |
267 | } |
268 | |
269 | /* We have found and printed an instruction. |
270 | If it was a short VLE instruction we have more to do. */ |
271 | if (insn_is_short) |
272 | { |
273 | memaddr += 2; |
274 | return 2; |
275 | } |
276 | else |
277 | /* Otherwise, return. */ |
278 | return 4; |
279 | } |
280 | |
281 | /* We could not find a match. */ |
282 | printf(fmt: ".long 0x%lx" , insn); |
283 | |
284 | return 4; |
285 | } |
286 | |