1 |
dpavlin |
1 |
/* |
2 |
|
|
* HT Editor |
3 |
|
|
* ppcdis.cc |
4 |
|
|
* |
5 |
|
|
* Copyright (C) 1999-2002 Sebastian Biallas (sb@web-productions.de) |
6 |
|
|
* Copyright 1994 Free Software Foundation, Inc. |
7 |
|
|
* Written by Ian Lance Taylor, Cygnus Support |
8 |
|
|
* |
9 |
|
|
* This program is free software; you can redistribute it and/or modify |
10 |
|
|
* it under the terms of the GNU General Public License version 2 as |
11 |
|
|
* published by the Free Software Foundation. |
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. |
21 |
|
|
*/ |
22 |
|
|
|
23 |
|
|
#include <cstdlib> |
24 |
|
|
#include <cstring> |
25 |
|
|
|
26 |
|
|
#include "tools/endianess.h" |
27 |
|
|
#include "tools/snprintf.h" |
28 |
|
|
#include "ppcdis.h" |
29 |
|
|
#include "ppcopc.h" |
30 |
|
|
|
31 |
|
|
PPCDisassembler::PPCDisassembler() |
32 |
|
|
{ |
33 |
|
|
} |
34 |
|
|
|
35 |
|
|
dis_insn *PPCDisassembler::decode(const byte *code, int maxlen, CPU_ADDR addr) |
36 |
|
|
{ |
37 |
|
|
const struct powerpc_opcode *opcode; |
38 |
|
|
const struct powerpc_opcode *opcode_end; |
39 |
|
|
uint32 op; |
40 |
|
|
int dialect = -1; |
41 |
|
|
|
42 |
|
|
insn.data = createHostInt(code, 4, little_endian); |
43 |
|
|
|
44 |
|
|
if (maxlen<4) { |
45 |
|
|
insn.valid = false; |
46 |
|
|
insn.size = maxlen; |
47 |
|
|
return &insn; |
48 |
|
|
} |
49 |
|
|
|
50 |
|
|
insn.size = 4; |
51 |
|
|
|
52 |
|
|
/* Get the major opcode of the instruction. */ |
53 |
|
|
op = PPC_OP(insn.data); |
54 |
|
|
|
55 |
|
|
/* Find the first match in the opcode table. We could speed this up |
56 |
|
|
a bit by doing a binary search on the major opcode. */ |
57 |
|
|
opcode_end = powerpc_opcodes + powerpc_num_opcodes; |
58 |
|
|
|
59 |
|
|
for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++) { |
60 |
|
|
uint32 table_op; |
61 |
|
|
const byte *opindex; |
62 |
|
|
const struct powerpc_operand *operand; |
63 |
|
|
bool invalid; |
64 |
|
|
bool need_comma; |
65 |
|
|
bool need_paren; |
66 |
|
|
|
67 |
|
|
table_op = PPC_OP (opcode->opcode); |
68 |
|
|
if (op < table_op) break; |
69 |
|
|
if (op > table_op) continue; |
70 |
|
|
|
71 |
|
|
if ((insn.data & opcode->mask) != opcode->opcode || (opcode->flags & dialect) == 0) { |
72 |
|
|
continue; |
73 |
|
|
} |
74 |
|
|
|
75 |
|
|
/* Make two passes over the operands. First see if any of them |
76 |
|
|
have extraction functions, and, if they do, make sure the |
77 |
|
|
instruction is valid. */ |
78 |
|
|
invalid = false; |
79 |
|
|
for (opindex = opcode->operands; *opindex != 0; opindex++) { |
80 |
|
|
operand = powerpc_operands + *opindex; |
81 |
|
|
if (operand->extract) (*operand->extract)(insn.data, &invalid); |
82 |
|
|
} |
83 |
|
|
if (invalid) continue; |
84 |
|
|
|
85 |
|
|
/* The instruction is valid. */ |
86 |
|
|
// fprintf(out, "%s", opcode->name); |
87 |
|
|
insn.name = opcode->name; |
88 |
|
|
// if (opcode->operands[0] != 0) fprintf(out, "\t"); |
89 |
|
|
|
90 |
|
|
/* Now extract and print the operands. */ |
91 |
|
|
need_comma = false; |
92 |
|
|
need_paren = false; |
93 |
|
|
int opidx = 0; |
94 |
|
|
for (opindex = opcode->operands; *opindex != 0; opindex++) { |
95 |
|
|
uint32 value; |
96 |
|
|
|
97 |
|
|
operand = powerpc_operands + *opindex; |
98 |
|
|
|
99 |
|
|
/* Operands that are marked FAKE are simply ignored. We |
100 |
|
|
already made sure that the extract function considered |
101 |
|
|
the instruction to be valid. */ |
102 |
|
|
if ((operand->flags & PPC_OPERAND_FAKE) != 0) continue; |
103 |
|
|
|
104 |
|
|
insn.op[opidx].op = operand; |
105 |
|
|
insn.op[opidx].flags = operand->flags; |
106 |
|
|
/* Extract the value from the instruction. */ |
107 |
|
|
if (operand->extract) { |
108 |
|
|
value = (*operand->extract)(insn.data, NULL); |
109 |
|
|
} else { |
110 |
|
|
value = (insn.data >> operand->shift) & ((1 << operand->bits) - 1); |
111 |
|
|
if ((operand->flags & PPC_OPERAND_SIGNED) != 0 && (value & (1 << (operand->bits - 1))) != 0) { |
112 |
|
|
value -= 1 << operand->bits; |
113 |
|
|
} |
114 |
|
|
} |
115 |
|
|
|
116 |
|
|
/* If the operand is optional, and the value is zero, don't |
117 |
|
|
print anything. */ |
118 |
|
|
if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 && (operand->flags & PPC_OPERAND_NEXT) == 0 && value == 0) { |
119 |
|
|
insn.op[opidx++].imm = 0; |
120 |
|
|
continue; |
121 |
|
|
} |
122 |
|
|
|
123 |
|
|
if (need_comma) { |
124 |
|
|
// fprintf(out, ", "); |
125 |
|
|
need_comma = false; |
126 |
|
|
} |
127 |
|
|
|
128 |
|
|
/* Print the operand as directed by the flags. */ |
129 |
|
|
if ((operand->flags & PPC_OPERAND_GPR) != 0) { |
130 |
|
|
insn.op[opidx++].reg = value; |
131 |
|
|
} else if ((operand->flags & PPC_OPERAND_FPR) != 0) { |
132 |
|
|
insn.op[opidx++].freg = value; |
133 |
|
|
} else if ((operand->flags & PPC_OPERAND_VR) != 0) { |
134 |
|
|
insn.op[opidx++].vreg = value; |
135 |
|
|
} else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0) { |
136 |
|
|
insn.op[opidx++].rel.mem = addr.addr32.offset + value; |
137 |
|
|
} else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) { |
138 |
|
|
insn.op[opidx++].abs.mem = value; |
139 |
|
|
} else if ((operand->flags & PPC_OPERAND_CR) == 0 || (dialect & PPC_OPCODE_PPC) == 0) { |
140 |
|
|
insn.op[opidx++].imm = value; |
141 |
|
|
} else { |
142 |
|
|
insn.op[opidx++].creg = value; |
143 |
|
|
if (operand->bits == 3) { |
144 |
|
|
// fprintf(out, "cr%d", value); |
145 |
|
|
} else { |
146 |
|
|
|
147 |
|
|
// static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; |
148 |
|
|
int cr; |
149 |
|
|
int cc; |
150 |
|
|
cr = value >> 2; |
151 |
|
|
// if (cr != 0) fprintf(out, "4*cr%d", cr); |
152 |
|
|
cc = value & 3; |
153 |
|
|
if (cc != 0) { |
154 |
|
|
// if (cr != 0) fprintf(out, "+"); |
155 |
|
|
// fprintf(out, "%s", cbnames[cc]); |
156 |
|
|
} |
157 |
|
|
} |
158 |
|
|
} |
159 |
|
|
|
160 |
|
|
if (need_paren) { |
161 |
|
|
// fprintf(out, ")"); |
162 |
|
|
need_paren = false; |
163 |
|
|
} |
164 |
|
|
|
165 |
|
|
if ((operand->flags & PPC_OPERAND_PARENS) == 0) { |
166 |
|
|
need_comma = true; |
167 |
|
|
} else { |
168 |
|
|
// fprintf(out, "("); |
169 |
|
|
need_paren = true; |
170 |
|
|
} |
171 |
|
|
} |
172 |
|
|
insn.ops = opidx; |
173 |
|
|
|
174 |
|
|
/* We have found and printed an instruction; return. */ |
175 |
|
|
insn.valid = true; |
176 |
|
|
return &insn; |
177 |
|
|
} |
178 |
|
|
|
179 |
|
|
insn.valid = false; |
180 |
|
|
return &insn; |
181 |
|
|
} |
182 |
|
|
|
183 |
|
|
dis_insn *PPCDisassembler::duplicateInsn(dis_insn *disasm_insn) |
184 |
|
|
{ |
185 |
|
|
ppcdis_insn *insn = (ppcdis_insn *)malloc(sizeof (ppcdis_insn)); |
186 |
|
|
*insn = *(ppcdis_insn *)disasm_insn; |
187 |
|
|
return insn; |
188 |
|
|
} |
189 |
|
|
|
190 |
|
|
void PPCDisassembler::getOpcodeMetrics(int &min_length, int &max_length, int &min_look_ahead, int &avg_look_ahead, int &addr_align) |
191 |
|
|
{ |
192 |
|
|
min_length = max_length = min_look_ahead = avg_look_ahead = addr_align = 4; |
193 |
|
|
} |
194 |
|
|
|
195 |
|
|
byte PPCDisassembler::getSize(dis_insn *disasm_insn) |
196 |
|
|
{ |
197 |
|
|
return ((ppcdis_insn*)disasm_insn)->size; |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
char *PPCDisassembler::getName() |
201 |
|
|
{ |
202 |
|
|
return "PPC/Disassembler"; |
203 |
|
|
} |
204 |
|
|
|
205 |
|
|
char *PPCDisassembler::str(dis_insn *disasm_insn, int style) |
206 |
|
|
{ |
207 |
|
|
return strf(disasm_insn, style, ""); |
208 |
|
|
} |
209 |
|
|
|
210 |
|
|
char *PPCDisassembler::strf(dis_insn *disasm_insn, int style, char *format) |
211 |
|
|
{ |
212 |
|
|
if (style & DIS_STYLE_HIGHLIGHT) enable_highlighting(); |
213 |
|
|
|
214 |
|
|
const char *cs_default = get_cs(e_cs_default); |
215 |
|
|
const char *cs_number = get_cs(e_cs_number); |
216 |
|
|
const char *cs_symbol = get_cs(e_cs_symbol); |
217 |
|
|
|
218 |
|
|
ppcdis_insn *ppc_insn = (ppcdis_insn *) disasm_insn; |
219 |
|
|
if (!ppc_insn->valid) { |
220 |
|
|
switch (ppc_insn->size) { |
221 |
|
|
case 1: |
222 |
|
|
strcpy(insnstr, "db ?"); |
223 |
|
|
break; |
224 |
|
|
case 2: |
225 |
|
|
strcpy(insnstr, "dw ?"); |
226 |
|
|
break; |
227 |
|
|
case 3: |
228 |
|
|
strcpy(insnstr, "db ? * 3"); |
229 |
|
|
break; |
230 |
|
|
case 4: |
231 |
|
|
ht_snprintf(insnstr, sizeof insnstr, "dd %s0x%08x", cs_number, ppc_insn->data); |
232 |
|
|
break; |
233 |
|
|
default: { /* braces for empty assert */ |
234 |
|
|
} |
235 |
|
|
} |
236 |
|
|
} else { |
237 |
|
|
char *is = insnstr+sprintf(insnstr, "%-10s", ppc_insn->name); |
238 |
|
|
int dialect=-1; |
239 |
|
|
|
240 |
|
|
bool need_comma = false; |
241 |
|
|
bool need_paren = false; |
242 |
|
|
for (int opidx = 0; opidx < ppc_insn->ops; opidx++) { |
243 |
|
|
int flags = ppc_insn->op[opidx].flags; |
244 |
|
|
/* if ((flags & PPC_OPERAND_OPTIONAL) != 0 && (flags & PPC_OPERAND_NEXT) == 0 && ppc_insn->op[opidx].imm == 0) { |
245 |
|
|
continue; |
246 |
|
|
}*/ |
247 |
|
|
if (need_comma) { |
248 |
|
|
is += sprintf(is, "%s, ", cs_symbol); |
249 |
|
|
need_comma = false; |
250 |
|
|
} |
251 |
|
|
if ((flags & PPC_OPERAND_GPR) != 0) { |
252 |
|
|
is += sprintf(is, "%sr%d", cs_default, ppc_insn->op[opidx].reg); |
253 |
|
|
} else if ((flags & PPC_OPERAND_FPR) != 0) { |
254 |
|
|
is += sprintf(is, "%sf%d", cs_default, ppc_insn->op[opidx].freg); |
255 |
|
|
} else if ((flags & PPC_OPERAND_VR) != 0) { |
256 |
|
|
is += sprintf(is, "%svr%d", cs_default, ppc_insn->op[opidx].vreg); |
257 |
|
|
} else if ((flags & PPC_OPERAND_RELATIVE) != 0) { |
258 |
|
|
CPU_ADDR caddr; |
259 |
|
|
caddr.addr32.offset = (uint32)ppc_insn->op[opidx].mem.disp; |
260 |
|
|
int slen; |
261 |
|
|
char *s = (addr_sym_func) ? addr_sym_func(caddr, &slen, addr_sym_func_context) : 0; |
262 |
|
|
if (s) { |
263 |
|
|
is += sprintf(is, "%s", cs_default); |
264 |
|
|
memmove(is, s, slen); |
265 |
|
|
is[slen] = 0; |
266 |
|
|
is += slen; |
267 |
|
|
} else { |
268 |
|
|
is += sprintf(is, "%s0x%x", cs_number, ppc_insn->op[opidx].rel.mem); |
269 |
|
|
} |
270 |
|
|
} else if ((flags & PPC_OPERAND_ABSOLUTE) != 0) { |
271 |
|
|
is += sprintf(is, "%s0x%x", cs_number, ppc_insn->op[opidx].abs.mem); |
272 |
|
|
} else if ((flags & PPC_OPERAND_CR) == 0 || (dialect & PPC_OPCODE_PPC) == 0) { |
273 |
|
|
is += sprintf(is, "%s%d", cs_number, ppc_insn->op[opidx].imm); |
274 |
|
|
} else if (ppc_insn->op[opidx].op->bits == 3) { |
275 |
|
|
is += sprintf(is, "%scr%d", cs_default, ppc_insn->op[opidx].creg); |
276 |
|
|
} else { |
277 |
|
|
static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; |
278 |
|
|
int cr; |
279 |
|
|
int cc; |
280 |
|
|
cr = ppc_insn->op[opidx].creg >> 2; |
281 |
|
|
if (cr != 0) is += sprintf(is, "%s4%s*%scr%d", cs_number, cs_symbol, cs_default, cr); |
282 |
|
|
cc = ppc_insn->op[opidx].creg & 3; |
283 |
|
|
if (cc != 0) { |
284 |
|
|
if (cr != 0) is += sprintf(is, "%s+", cs_symbol); |
285 |
|
|
is += sprintf(is, "%s%s", cs_default, cbnames[cc]); |
286 |
|
|
} |
287 |
|
|
} |
288 |
|
|
|
289 |
|
|
if (need_paren) { |
290 |
|
|
is += sprintf(is, "%s)", cs_symbol); |
291 |
|
|
need_paren = false; |
292 |
|
|
} |
293 |
|
|
|
294 |
|
|
if ((flags & PPC_OPERAND_PARENS) == 0) { |
295 |
|
|
need_comma = true; |
296 |
|
|
} else { |
297 |
|
|
is += sprintf(is, "%s(", cs_symbol); |
298 |
|
|
need_paren = true; |
299 |
|
|
} |
300 |
|
|
} |
301 |
|
|
} |
302 |
|
|
disable_highlighting(); |
303 |
|
|
return insnstr; |
304 |
|
|
} |
305 |
|
|
|
306 |
|
|
ObjectID PPCDisassembler::getObjectID() const |
307 |
|
|
{ |
308 |
|
|
return 0; |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
bool PPCDisassembler::validInsn(dis_insn *disasm_insn) |
312 |
|
|
{ |
313 |
|
|
return ((ppcdis_insn*)disasm_insn)->valid; |
314 |
|
|
} |
315 |
|
|
|
316 |
|
|
|