Commit | Line | Data |
---|---|---|
7ba29e2a NC |
1 | /* Disassemble Xilinx microblaze instructions. |
2 | ||
82704155 | 3 | Copyright (C) 2009-2019 Free Software Foundation, Inc. |
7ba29e2a NC |
4 | |
5 | This file is part of the GNU opcodes library. | |
6 | ||
7 | This library 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 3, or (at your option) | |
10 | any later version. | |
11 | ||
12 | It is distributed in the hope that it will be useful, but WITHOUT | |
13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public | |
15 | License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this file; see the file COPYING. If not, write to the | |
19 | Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, | |
20 | MA 02110-1301, USA. */ | |
21 | ||
22 | ||
23 | #include "sysdep.h" | |
24 | #define STATIC_TABLE | |
25 | #define DEFINE_TABLE | |
26 | ||
88c1242d | 27 | #include "disassemble.h" |
7ba29e2a | 28 | #include <strings.h> |
ef299415 ME |
29 | #include "microblaze-opc.h" |
30 | #include "microblaze-dis.h" | |
7ba29e2a | 31 | |
378fd436 AM |
32 | #define get_field_rd(buf, instr) get_field (buf, instr, RD_MASK, RD_LOW) |
33 | #define get_field_r1(buf, instr) get_field (buf, instr, RA_MASK, RA_LOW) | |
34 | #define get_field_r2(buf, instr) get_field (buf, instr, RB_MASK, RB_LOW) | |
7ba29e2a NC |
35 | #define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW) |
36 | #define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) | |
37 | ||
378fd436 AM |
38 | #define NUM_STRBUFS 3 |
39 | #define STRBUF_SIZE 25 | |
fe2d172c | 40 | |
378fd436 AM |
41 | struct string_buf |
42 | { | |
43 | unsigned int which; | |
44 | char str[NUM_STRBUFS][STRBUF_SIZE]; | |
45 | }; | |
46 | ||
47 | static inline char * | |
48 | strbuf (struct string_buf *buf) | |
49 | { | |
50 | #ifdef ENABLE_CHECKING | |
51 | if (buf->which >= NUM_STRBUFS) | |
52 | abort (); | |
53 | #endif | |
54 | return buf->str[buf->which++]; | |
55 | } | |
fe2d172c | 56 | |
7ba29e2a | 57 | static char * |
378fd436 | 58 | get_field (struct string_buf *buf, long instr, long mask, unsigned short low) |
7ba29e2a | 59 | { |
378fd436 | 60 | char *p = strbuf (buf); |
7ba29e2a | 61 | |
378fd436 AM |
62 | sprintf (p, "%s%d", register_prefix, (int)((instr & mask) >> low)); |
63 | return p; | |
7ba29e2a NC |
64 | } |
65 | ||
66 | static char * | |
378fd436 | 67 | get_field_imm (struct string_buf *buf, long instr) |
7ba29e2a | 68 | { |
378fd436 | 69 | char *p = strbuf (buf); |
7ba29e2a | 70 | |
378fd436 AM |
71 | sprintf (p, "%d", (short)((instr & IMM_MASK) >> IMM_LOW)); |
72 | return p; | |
7ba29e2a NC |
73 | } |
74 | ||
75 | static char * | |
378fd436 | 76 | get_field_imm5 (struct string_buf *buf, long instr) |
7ba29e2a | 77 | { |
378fd436 | 78 | char *p = strbuf (buf); |
7ba29e2a | 79 | |
378fd436 AM |
80 | sprintf (p, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW)); |
81 | return p; | |
7ba29e2a NC |
82 | } |
83 | ||
d3da7741 | 84 | static char * |
378fd436 | 85 | get_field_imm5_mbar (struct string_buf *buf, long instr) |
d3da7741 | 86 | { |
378fd436 | 87 | char *p = strbuf (buf); |
d3da7741 | 88 | |
378fd436 AM |
89 | sprintf (p, "%d", (short)((instr & IMM5_MBAR_MASK) >> IMM_MBAR)); |
90 | return p; | |
d3da7741 ME |
91 | } |
92 | ||
7ba29e2a | 93 | static char * |
378fd436 | 94 | get_field_rfsl (struct string_buf *buf, long instr) |
7ba29e2a | 95 | { |
378fd436 | 96 | char *p = strbuf (buf); |
7ba29e2a | 97 | |
378fd436 | 98 | sprintf (p, "%s%d", fsl_register_prefix, |
7ba29e2a | 99 | (short)((instr & RFSL_MASK) >> IMM_LOW)); |
378fd436 | 100 | return p; |
7ba29e2a NC |
101 | } |
102 | ||
103 | static char * | |
378fd436 | 104 | get_field_imm15 (struct string_buf *buf, long instr) |
7ba29e2a | 105 | { |
378fd436 | 106 | char *p = strbuf (buf); |
7ba29e2a | 107 | |
378fd436 AM |
108 | sprintf (p, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW)); |
109 | return p; | |
7ba29e2a NC |
110 | } |
111 | ||
112 | static char * | |
378fd436 AM |
113 | get_field_special (struct string_buf *buf, long instr, |
114 | struct op_code_struct *op) | |
7ba29e2a | 115 | { |
378fd436 AM |
116 | char *p = strbuf (buf); |
117 | char *spr; | |
7ba29e2a NC |
118 | |
119 | switch ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask)) | |
120 | { | |
121 | case REG_MSR_MASK : | |
378fd436 | 122 | spr = "msr"; |
7ba29e2a NC |
123 | break; |
124 | case REG_PC_MASK : | |
378fd436 | 125 | spr = "pc"; |
7ba29e2a NC |
126 | break; |
127 | case REG_EAR_MASK : | |
378fd436 | 128 | spr = "ear"; |
7ba29e2a NC |
129 | break; |
130 | case REG_ESR_MASK : | |
378fd436 | 131 | spr = "esr"; |
7ba29e2a NC |
132 | break; |
133 | case REG_FSR_MASK : | |
378fd436 | 134 | spr = "fsr"; |
7ba29e2a NC |
135 | break; |
136 | case REG_BTR_MASK : | |
378fd436 | 137 | spr = "btr"; |
7ba29e2a NC |
138 | break; |
139 | case REG_EDR_MASK : | |
378fd436 | 140 | spr = "edr"; |
7ba29e2a NC |
141 | break; |
142 | case REG_PID_MASK : | |
378fd436 | 143 | spr = "pid"; |
7ba29e2a NC |
144 | break; |
145 | case REG_ZPR_MASK : | |
378fd436 | 146 | spr = "zpr"; |
7ba29e2a NC |
147 | break; |
148 | case REG_TLBX_MASK : | |
378fd436 | 149 | spr = "tlbx"; |
7ba29e2a NC |
150 | break; |
151 | case REG_TLBLO_MASK : | |
378fd436 | 152 | spr = "tlblo"; |
7ba29e2a NC |
153 | break; |
154 | case REG_TLBHI_MASK : | |
378fd436 | 155 | spr = "tlbhi"; |
7ba29e2a NC |
156 | break; |
157 | case REG_TLBSX_MASK : | |
378fd436 | 158 | spr = "tlbsx"; |
7ba29e2a | 159 | break; |
0db4b326 | 160 | case REG_SHR_MASK : |
378fd436 | 161 | spr = "shr"; |
0db4b326 ME |
162 | break; |
163 | case REG_SLR_MASK : | |
378fd436 | 164 | spr = "slr"; |
0db4b326 | 165 | break; |
7ba29e2a NC |
166 | default : |
167 | if (((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000) | |
378fd436 AM |
168 | == REG_PVR_MASK) |
169 | { | |
170 | sprintf (p, "%spvr%d", register_prefix, | |
7ba29e2a | 171 | (unsigned short)(((instr & IMM_MASK) >> IMM_LOW) |
378fd436 AM |
172 | ^ op->immval_mask) ^ REG_PVR_MASK); |
173 | return p; | |
174 | } | |
7ba29e2a | 175 | else |
378fd436 | 176 | spr = "pc"; |
7ba29e2a NC |
177 | break; |
178 | } | |
179 | ||
378fd436 AM |
180 | sprintf (p, "%s%s", register_prefix, spr); |
181 | return p; | |
7ba29e2a NC |
182 | } |
183 | ||
184 | static unsigned long | |
185 | read_insn_microblaze (bfd_vma memaddr, | |
186 | struct disassemble_info *info, | |
187 | struct op_code_struct **opr) | |
188 | { | |
189 | unsigned char ibytes[4]; | |
190 | int status; | |
191 | struct op_code_struct * op; | |
192 | unsigned long inst; | |
193 | ||
194 | status = info->read_memory_func (memaddr, ibytes, 4, info); | |
195 | ||
196 | if (status != 0) | |
197 | { | |
198 | info->memory_error_func (status, memaddr, info); | |
199 | return 0; | |
200 | } | |
201 | ||
202 | if (info->endian == BFD_ENDIAN_BIG) | |
203 | inst = (ibytes[0] << 24) | (ibytes[1] << 16) | (ibytes[2] << 8) | ibytes[3]; | |
204 | else if (info->endian == BFD_ENDIAN_LITTLE) | |
205 | inst = (ibytes[3] << 24) | (ibytes[2] << 16) | (ibytes[1] << 8) | ibytes[0]; | |
206 | else | |
207 | abort (); | |
208 | ||
209 | /* Just a linear search of the table. */ | |
210 | for (op = opcodes; op->name != 0; op ++) | |
211 | if (op->bit_sequence == (inst & op->opcode_mask)) | |
212 | break; | |
213 | ||
214 | *opr = op; | |
215 | return inst; | |
216 | } | |
217 | ||
218 | ||
219 | int | |
220 | print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info) | |
221 | { | |
91d6fa6a | 222 | fprintf_ftype print_func = info->fprintf_func; |
7ba29e2a NC |
223 | void * stream = info->stream; |
224 | unsigned long inst, prev_inst; | |
225 | struct op_code_struct * op, *pop; | |
226 | int immval = 0; | |
227 | bfd_boolean immfound = FALSE; | |
228 | static bfd_vma prev_insn_addr = -1; /* Init the prev insn addr. */ | |
229 | static int prev_insn_vma = -1; /* Init the prev insn vma. */ | |
230 | int curr_insn_vma = info->buffer_vma; | |
378fd436 | 231 | struct string_buf buf; |
7ba29e2a | 232 | |
378fd436 | 233 | buf.which = 0; |
7ba29e2a NC |
234 | info->bytes_per_chunk = 4; |
235 | ||
236 | inst = read_insn_microblaze (memaddr, info, &op); | |
237 | if (inst == 0) | |
238 | return -1; | |
239 | ||
240 | if (prev_insn_vma == curr_insn_vma) | |
241 | { | |
242 | if (memaddr-(info->bytes_per_chunk) == prev_insn_addr) | |
378fd436 AM |
243 | { |
244 | prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop); | |
7ba29e2a NC |
245 | if (prev_inst == 0) |
246 | return -1; | |
247 | if (pop->instr == imm) | |
248 | { | |
249 | immval = (get_int_field_imm (prev_inst) << 16) & 0xffff0000; | |
250 | immfound = TRUE; | |
251 | } | |
252 | else | |
253 | { | |
254 | immval = 0; | |
255 | immfound = FALSE; | |
256 | } | |
257 | } | |
258 | } | |
259 | ||
260 | /* Make curr insn as prev insn. */ | |
261 | prev_insn_addr = memaddr; | |
262 | prev_insn_vma = curr_insn_vma; | |
263 | ||
264 | if (op->name == NULL) | |
d908c8af | 265 | print_func (stream, ".short 0x%04x", (unsigned int) inst); |
7ba29e2a NC |
266 | else |
267 | { | |
91d6fa6a | 268 | print_func (stream, "%s", op->name); |
7ba29e2a NC |
269 | |
270 | switch (op->inst_type) | |
271 | { | |
378fd436 AM |
272 | case INST_TYPE_RD_R1_R2: |
273 | print_func (stream, "\t%s, %s, %s", get_field_rd (&buf, inst), | |
274 | get_field_r1 (&buf, inst), get_field_r2 (&buf, inst)); | |
275 | break; | |
276 | case INST_TYPE_RD_R1_IMM: | |
277 | print_func (stream, "\t%s, %s, %s", get_field_rd (&buf, inst), | |
278 | get_field_r1 (&buf, inst), get_field_imm (&buf, inst)); | |
7ba29e2a NC |
279 | if (info->print_address_func && get_int_field_r1 (inst) == 0 |
280 | && info->symbol_at_address_func) | |
281 | { | |
282 | if (immfound) | |
378fd436 | 283 | immval |= (get_int_field_imm (inst) & 0x0000ffff); |
7ba29e2a NC |
284 | else |
285 | { | |
378fd436 AM |
286 | immval = get_int_field_imm (inst); |
287 | if (immval & 0x8000) | |
7ba29e2a | 288 | immval |= 0xFFFF0000; |
378fd436 | 289 | } |
7ba29e2a NC |
290 | if (immval > 0 && info->symbol_at_address_func (immval, info)) |
291 | { | |
378fd436 AM |
292 | print_func (stream, "\t// "); |
293 | info->print_address_func (immval, info); | |
294 | } | |
7ba29e2a NC |
295 | } |
296 | break; | |
297 | case INST_TYPE_RD_R1_IMM5: | |
378fd436 AM |
298 | print_func (stream, "\t%s, %s, %s", get_field_rd (&buf, inst), |
299 | get_field_r1 (&buf, inst), get_field_imm5 (&buf, inst)); | |
7ba29e2a NC |
300 | break; |
301 | case INST_TYPE_RD_RFSL: | |
378fd436 AM |
302 | print_func (stream, "\t%s, %s", get_field_rd (&buf, inst), |
303 | get_field_rfsl (&buf, inst)); | |
7ba29e2a NC |
304 | break; |
305 | case INST_TYPE_R1_RFSL: | |
378fd436 AM |
306 | print_func (stream, "\t%s, %s", get_field_r1 (&buf, inst), |
307 | get_field_rfsl (&buf, inst)); | |
7ba29e2a NC |
308 | break; |
309 | case INST_TYPE_RD_SPECIAL: | |
378fd436 AM |
310 | print_func (stream, "\t%s, %s", get_field_rd (&buf, inst), |
311 | get_field_special (&buf, inst, op)); | |
7ba29e2a NC |
312 | break; |
313 | case INST_TYPE_SPECIAL_R1: | |
378fd436 AM |
314 | print_func (stream, "\t%s, %s", get_field_special (&buf, inst, op), |
315 | get_field_r1 (&buf, inst)); | |
7ba29e2a NC |
316 | break; |
317 | case INST_TYPE_RD_R1: | |
378fd436 AM |
318 | print_func (stream, "\t%s, %s", get_field_rd (&buf, inst), |
319 | get_field_r1 (&buf, inst)); | |
7ba29e2a NC |
320 | break; |
321 | case INST_TYPE_R1_R2: | |
378fd436 AM |
322 | print_func (stream, "\t%s, %s", get_field_r1 (&buf, inst), |
323 | get_field_r2 (&buf, inst)); | |
7ba29e2a NC |
324 | break; |
325 | case INST_TYPE_R1_IMM: | |
378fd436 AM |
326 | print_func (stream, "\t%s, %s", get_field_r1 (&buf, inst), |
327 | get_field_imm (&buf, inst)); | |
7ba29e2a NC |
328 | /* The non-pc relative instructions are returns, which shouldn't |
329 | have a label printed. */ | |
330 | if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET | |
331 | && info->symbol_at_address_func) | |
332 | { | |
333 | if (immfound) | |
378fd436 | 334 | immval |= (get_int_field_imm (inst) & 0x0000ffff); |
7ba29e2a NC |
335 | else |
336 | { | |
378fd436 AM |
337 | immval = get_int_field_imm (inst); |
338 | if (immval & 0x8000) | |
7ba29e2a | 339 | immval |= 0xFFFF0000; |
378fd436 | 340 | } |
7ba29e2a NC |
341 | immval += memaddr; |
342 | if (immval > 0 && info->symbol_at_address_func (immval, info)) | |
343 | { | |
378fd436 AM |
344 | print_func (stream, "\t// "); |
345 | info->print_address_func (immval, info); | |
346 | } | |
7ba29e2a NC |
347 | else |
348 | { | |
378fd436 AM |
349 | print_func (stream, "\t\t// "); |
350 | print_func (stream, "%x", immval); | |
351 | } | |
7ba29e2a NC |
352 | } |
353 | break; | |
378fd436 AM |
354 | case INST_TYPE_RD_IMM: |
355 | print_func (stream, "\t%s, %s", get_field_rd (&buf, inst), | |
356 | get_field_imm (&buf, inst)); | |
7ba29e2a NC |
357 | if (info->print_address_func && info->symbol_at_address_func) |
358 | { | |
378fd436 AM |
359 | if (immfound) |
360 | immval |= (get_int_field_imm (inst) & 0x0000ffff); | |
361 | else | |
362 | { | |
363 | immval = get_int_field_imm (inst); | |
364 | if (immval & 0x8000) | |
365 | immval |= 0xFFFF0000; | |
366 | } | |
367 | if (op->inst_offset_type == INST_PC_OFFSET) | |
368 | immval += (int) memaddr; | |
369 | if (info->symbol_at_address_func (immval, info)) | |
370 | { | |
371 | print_func (stream, "\t// "); | |
372 | info->print_address_func (immval, info); | |
373 | } | |
7ba29e2a NC |
374 | } |
375 | break; | |
378fd436 AM |
376 | case INST_TYPE_IMM: |
377 | print_func (stream, "\t%s", get_field_imm (&buf, inst)); | |
7ba29e2a NC |
378 | if (info->print_address_func && info->symbol_at_address_func |
379 | && op->instr != imm) | |
380 | { | |
381 | if (immfound) | |
378fd436 | 382 | immval |= (get_int_field_imm (inst) & 0x0000ffff); |
7ba29e2a NC |
383 | else |
384 | { | |
378fd436 AM |
385 | immval = get_int_field_imm (inst); |
386 | if (immval & 0x8000) | |
7ba29e2a | 387 | immval |= 0xFFFF0000; |
378fd436 | 388 | } |
7ba29e2a | 389 | if (op->inst_offset_type == INST_PC_OFFSET) |
378fd436 | 390 | immval += (int) memaddr; |
7ba29e2a NC |
391 | if (immval > 0 && info->symbol_at_address_func (immval, info)) |
392 | { | |
378fd436 AM |
393 | print_func (stream, "\t// "); |
394 | info->print_address_func (immval, info); | |
395 | } | |
7ba29e2a NC |
396 | else if (op->inst_offset_type == INST_PC_OFFSET) |
397 | { | |
378fd436 AM |
398 | print_func (stream, "\t\t// "); |
399 | print_func (stream, "%x", immval); | |
400 | } | |
7ba29e2a NC |
401 | } |
402 | break; | |
378fd436 AM |
403 | case INST_TYPE_RD_R2: |
404 | print_func (stream, "\t%s, %s", get_field_rd (&buf, inst), | |
405 | get_field_r2 (&buf, inst)); | |
7ba29e2a NC |
406 | break; |
407 | case INST_TYPE_R2: | |
378fd436 | 408 | print_func (stream, "\t%s", get_field_r2 (&buf, inst)); |
7ba29e2a NC |
409 | break; |
410 | case INST_TYPE_R1: | |
378fd436 | 411 | print_func (stream, "\t%s", get_field_r1 (&buf, inst)); |
7ba29e2a | 412 | break; |
94dda8b7 | 413 | case INST_TYPE_R1_R2_SPECIAL: |
378fd436 AM |
414 | print_func (stream, "\t%s, %s", get_field_r1 (&buf, inst), |
415 | get_field_r2 (&buf, inst)); | |
7ba29e2a NC |
416 | break; |
417 | case INST_TYPE_RD_IMM15: | |
378fd436 AM |
418 | print_func (stream, "\t%s, %s", get_field_rd (&buf, inst), |
419 | get_field_imm15 (&buf, inst)); | |
420 | break; | |
421 | /* For mbar insn. */ | |
422 | case INST_TYPE_IMM5: | |
423 | print_func (stream, "\t%s", get_field_imm5_mbar (&buf, inst)); | |
424 | break; | |
425 | /* For mbar 16 or sleep insn. */ | |
426 | case INST_TYPE_NONE: | |
7ba29e2a | 427 | break; |
378fd436 | 428 | /* For tuqula instruction */ |
7ba29e2a | 429 | case INST_TYPE_RD: |
378fd436 | 430 | print_func (stream, "\t%s", get_field_rd (&buf, inst)); |
7ba29e2a NC |
431 | break; |
432 | case INST_TYPE_RFSL: | |
378fd436 | 433 | print_func (stream, "\t%s", get_field_rfsl (&buf, inst)); |
7ba29e2a NC |
434 | break; |
435 | default: | |
436 | /* If the disassembler lags the instruction set. */ | |
378fd436 AM |
437 | print_func (stream, "\tundecoded operands, inst is 0x%04x", |
438 | (unsigned int) inst); | |
7ba29e2a NC |
439 | break; |
440 | } | |
441 | } | |
442 | ||
443 | /* Say how many bytes we consumed. */ | |
444 | return 4; | |
445 | } | |
fe2d172c ME |
446 | |
447 | enum microblaze_instr | |
7ba29e2a NC |
448 | get_insn_microblaze (long inst, |
449 | bfd_boolean *isunsignedimm, | |
450 | enum microblaze_instr_type *insn_type, | |
451 | short *delay_slots) | |
452 | { | |
453 | struct op_code_struct * op; | |
454 | *isunsignedimm = FALSE; | |
455 | ||
456 | /* Just a linear search of the table. */ | |
457 | for (op = opcodes; op->name != 0; op ++) | |
458 | if (op->bit_sequence == (inst & op->opcode_mask)) | |
459 | break; | |
460 | ||
461 | if (op->name == 0) | |
462 | return invalid_inst; | |
463 | else | |
464 | { | |
465 | *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM); | |
466 | *insn_type = op->instr_type; | |
467 | *delay_slots = op->delay_slots; | |
468 | return op->instr; | |
469 | } | |
470 | } | |
471 | ||
7ba29e2a | 472 | enum microblaze_instr |
91d6fa6a | 473 | microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *immed) |
7ba29e2a NC |
474 | { |
475 | enum microblaze_instr op; | |
476 | bfd_boolean t1; | |
477 | enum microblaze_instr_type t2; | |
478 | short t3; | |
479 | ||
480 | op = get_insn_microblaze (insn, &t1, &t2, &t3); | |
481 | *rd = (insn & RD_MASK) >> RD_LOW; | |
482 | *ra = (insn & RA_MASK) >> RA_LOW; | |
483 | *rb = (insn & RB_MASK) >> RB_LOW; | |
484 | t3 = (insn & IMM_MASK) >> IMM_LOW; | |
91d6fa6a | 485 | *immed = (int) t3; |
7ba29e2a NC |
486 | return (op); |
487 | } | |
488 | ||
489 | unsigned long | |
490 | microblaze_get_target_address (long inst, bfd_boolean immfound, int immval, | |
491 | long pcval, long r1val, long r2val, | |
492 | bfd_boolean *targetvalid, | |
493 | bfd_boolean *unconditionalbranch) | |
494 | { | |
495 | struct op_code_struct * op; | |
496 | long targetaddr = 0; | |
497 | ||
498 | *unconditionalbranch = FALSE; | |
499 | /* Just a linear search of the table. */ | |
500 | for (op = opcodes; op->name != 0; op ++) | |
501 | if (op->bit_sequence == (inst & op->opcode_mask)) | |
502 | break; | |
503 | ||
504 | if (op->name == 0) | |
505 | { | |
506 | *targetvalid = FALSE; | |
507 | } | |
508 | else if (op->instr_type == branch_inst) | |
509 | { | |
510 | switch (op->inst_type) | |
511 | { | |
512 | case INST_TYPE_R2: | |
513 | *unconditionalbranch = TRUE; | |
514 | /* Fall through. */ | |
515 | case INST_TYPE_RD_R2: | |
516 | case INST_TYPE_R1_R2: | |
517 | targetaddr = r2val; | |
518 | *targetvalid = TRUE; | |
519 | if (op->inst_offset_type == INST_PC_OFFSET) | |
520 | targetaddr += pcval; | |
521 | break; | |
522 | case INST_TYPE_IMM: | |
523 | *unconditionalbranch = TRUE; | |
524 | /* Fall through. */ | |
525 | case INST_TYPE_RD_IMM: | |
526 | case INST_TYPE_R1_IMM: | |
527 | if (immfound) | |
528 | { | |
529 | targetaddr = (immval << 16) & 0xffff0000; | |
530 | targetaddr |= (get_int_field_imm (inst) & 0x0000ffff); | |
531 | } | |
532 | else | |
533 | { | |
534 | targetaddr = get_int_field_imm (inst); | |
535 | if (targetaddr & 0x8000) | |
536 | targetaddr |= 0xFFFF0000; | |
537 | } | |
538 | if (op->inst_offset_type == INST_PC_OFFSET) | |
539 | targetaddr += pcval; | |
540 | *targetvalid = TRUE; | |
541 | break; | |
542 | default: | |
543 | *targetvalid = FALSE; | |
544 | break; | |
545 | } | |
546 | } | |
547 | else if (op->instr_type == return_inst) | |
548 | { | |
549 | if (immfound) | |
550 | { | |
551 | targetaddr = (immval << 16) & 0xffff0000; | |
552 | targetaddr |= (get_int_field_imm (inst) & 0x0000ffff); | |
553 | } | |
554 | else | |
555 | { | |
556 | targetaddr = get_int_field_imm (inst); | |
557 | if (targetaddr & 0x8000) | |
558 | targetaddr |= 0xFFFF0000; | |
559 | } | |
560 | targetaddr += r1val; | |
561 | *targetvalid = TRUE; | |
562 | } | |
563 | else | |
564 | *targetvalid = FALSE; | |
565 | return targetaddr; | |
566 | } |