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 |
|