1 /* Disassemble D30V instructions.
2 Copyright 1997, 1998, 2000, 2001 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20 #include "opcode/d30v.h"
24 #define PC_MASK 0xFFFFFFFF
26 static int lookup_opcode
PARAMS ((struct d30v_insn
*insn
, long num
, int is_long
));
27 static void print_insn
PARAMS ((struct disassemble_info
*info
, bfd_vma memaddr
, long long num
,
28 struct d30v_insn
*insn
, int is_long
, int show_ext
));
29 static int extract_value
PARAMS ((long long num
, struct d30v_operand
*oper
, int is_long
));
32 print_insn_d30v (memaddr
, info
)
34 struct disassemble_info
*info
;
38 unsigned long in1
, in2
;
39 struct d30v_insn insn
;
42 insn
.form
= (struct d30v_format
*) NULL
;
44 info
->bytes_per_line
= 8;
45 info
->bytes_per_chunk
= 4;
46 info
->display_endian
= BFD_ENDIAN_BIG
;
48 status
= (*info
->read_memory_func
) (memaddr
, buffer
, 4, info
);
51 (*info
->memory_error_func
) (status
, memaddr
, info
);
54 in1
= bfd_getb32 (buffer
);
56 status
= (*info
->read_memory_func
) (memaddr
+ 4, buffer
, 4, info
);
59 info
->bytes_per_line
= 8;
60 if (!(result
= lookup_opcode (&insn
, in1
, 0)))
61 (*info
->fprintf_func
) (info
->stream
, ".long\t0x%x", in1
);
63 print_insn (info
, memaddr
, (long long) in1
, &insn
, 0, result
);
66 in2
= bfd_getb32 (buffer
);
70 /* LONG instruction. */
71 if (!(result
= lookup_opcode (&insn
, in1
, 1)))
73 (*info
->fprintf_func
) (info
->stream
, ".long\t0x%x,0x%x", in1
, in2
);
76 num
= (long long) in1
<< 32 | in2
;
77 print_insn (info
, memaddr
, num
, &insn
, 1, result
);
82 if (!(result
= lookup_opcode (&insn
, in1
, 0)))
83 (*info
->fprintf_func
) (info
->stream
, ".long\t0x%x", in1
);
85 print_insn (info
, memaddr
, num
, &insn
, 0, result
);
87 switch (((in1
>> 31) << 1) | (in2
>> 31))
90 (*info
->fprintf_func
) (info
->stream
, "\t||\t");
93 (*info
->fprintf_func
) (info
->stream
, "\t->\t");
96 (*info
->fprintf_func
) (info
->stream
, "\t<-\t");
101 insn
.form
= (struct d30v_format
*) NULL
;
103 if (!(result
= lookup_opcode (&insn
, in2
, 0)))
104 (*info
->fprintf_func
) (info
->stream
, ".long\t0x%x", in2
);
106 print_insn (info
, memaddr
, num
, &insn
, 0, result
);
111 /* Return 0 if lookup fails,
112 1 if found and only one form,
113 2 if found and there are short and long forms. */
116 lookup_opcode (insn
, num
, is_long
)
117 struct d30v_insn
*insn
;
122 struct d30v_format
*f
;
123 struct d30v_opcode
*op
= (struct d30v_opcode
*) d30v_opcode_table
;
124 int op1
= (num
>> 25) & 0x7;
125 int op2
= (num
>> 20) & 0x1f;
126 int mod
= (num
>> 18) & 0x3;
128 /* Find the opcode. */
131 if ((op
->op1
== op1
) && (op
->op2
== op2
))
137 if (!op
|| !op
->name
)
140 while (op
->op1
== op1
&& op
->op2
== op2
)
142 /* Scan through all the formats for the opcode. */
143 index
= op
->format
[i
++];
146 f
= (struct d30v_format
*) &d30v_format_table
[index
];
147 while (f
->form
== index
)
149 if ((!is_long
|| f
->form
>= LONG
) && (f
->modifier
== mod
))
159 while ((index
= op
->format
[i
++]) != 0);
165 if (insn
->form
== NULL
)
169 insn
->ecc
= (num
>> 28) & 0x7;
177 print_insn (info
, memaddr
, num
, insn
, is_long
, show_ext
)
178 struct disassemble_info
*info
;
181 struct d30v_insn
*insn
;
185 int val
, opnum
, need_comma
= 0;
186 struct d30v_operand
*oper
;
187 int i
, match
, opind
= 0, need_paren
= 0, found_control
= 0;
189 (*info
->fprintf_func
) (info
->stream
, "%s", insn
->op
->name
);
191 /* Check for CMP or CMPU. */
192 if (d30v_operand_table
[insn
->form
->operands
[0]].flags
& OPERAND_NAME
)
197 (struct d30v_operand
*) &d30v_operand_table
[insn
->form
->operands
[0]],
199 (*info
->fprintf_func
) (info
->stream
, "%s", d30v_cc_names
[val
]);
202 /* Add in ".s" or ".l". */
206 (*info
->fprintf_func
) (info
->stream
, ".l");
208 (*info
->fprintf_func
) (info
->stream
, ".s");
212 (*info
->fprintf_func
) (info
->stream
, "/%s", d30v_ecc_names
[insn
->ecc
]);
214 (*info
->fprintf_func
) (info
->stream
, "\t");
216 while ((opnum
= insn
->form
->operands
[opind
++]) != 0)
219 oper
= (struct d30v_operand
*) &d30v_operand_table
[opnum
];
221 if (oper
->flags
& OPERAND_SHIFT
)
225 && oper
->flags
!= OPERAND_PLUS
226 && oper
->flags
!= OPERAND_MINUS
)
229 (*info
->fprintf_func
) (info
->stream
, ", ");
232 if (oper
->flags
== OPERAND_ATMINUS
)
234 (*info
->fprintf_func
) (info
->stream
, "@-");
237 if (oper
->flags
== OPERAND_MINUS
)
239 (*info
->fprintf_func
) (info
->stream
, "-");
242 if (oper
->flags
== OPERAND_PLUS
)
244 (*info
->fprintf_func
) (info
->stream
, "+");
247 if (oper
->flags
== OPERAND_ATSIGN
)
249 (*info
->fprintf_func
) (info
->stream
, "@");
252 if (oper
->flags
== OPERAND_ATPAR
)
254 (*info
->fprintf_func
) (info
->stream
, "@(");
259 if (oper
->flags
== OPERAND_SPECIAL
)
262 val
= extract_value (num
, oper
, is_long
);
264 if (oper
->flags
& OPERAND_REG
)
267 if (oper
->flags
& OPERAND_CONTROL
)
269 struct d30v_operand
*oper3
=
270 (struct d30v_operand
*) &d30v_operand_table
[insn
->form
->operands
[2]];
271 int id
= extract_value (num
, oper3
, is_long
);
276 val
|= OPERAND_CONTROL
;
280 val
= OPERAND_CONTROL
+ MAX_CONTROL_REG
+ id
;
286 fprintf (stderr
, "illegal id (%d)\n", id
);
289 else if (oper
->flags
& OPERAND_ACC
)
291 else if (oper
->flags
& OPERAND_FLAG
)
293 for (i
= 0; i
< reg_name_cnt (); i
++)
295 if (val
== pre_defined_registers
[i
].value
)
297 if (pre_defined_registers
[i
].pname
)
298 (*info
->fprintf_func
)
299 (info
->stream
, "%s", pre_defined_registers
[i
].pname
);
301 (*info
->fprintf_func
)
302 (info
->stream
, "%s", pre_defined_registers
[i
].name
);
309 /* This would only get executed if a register was not in
310 the register table. */
311 (*info
->fprintf_func
)
312 (info
->stream
, _("<unknown register %d>"), val
& 0x3F);
315 /* repeati has a relocation, but its first argument is a plain
316 immediate. OTOH instructions like djsri have a pc-relative
317 delay target, but an absolute jump target. Therefore, a test
318 of insn->op->reloc_flag is not specific enough; we must test
319 if the actual operand we are handling now is pc-relative. */
320 else if (oper
->flags
& OPERAND_PCREL
)
324 /* IMM6S3 is unsigned. */
325 if (oper
->flags
& OPERAND_SIGNED
|| bits
== 32)
328 max
= (1 << (bits
- 1));
334 val
= -val
& ((1 << bits
) - 1);
340 (*info
->fprintf_func
) (info
->stream
, "-%x\t(", val
);
341 (*info
->print_address_func
) ((memaddr
- val
) & PC_MASK
, info
);
342 (*info
->fprintf_func
) (info
->stream
, ")");
346 (*info
->fprintf_func
) (info
->stream
, "%x\t(", val
);
347 (*info
->print_address_func
) ((memaddr
+ val
) & PC_MASK
, info
);
348 (*info
->fprintf_func
) (info
->stream
, ")");
351 else if (insn
->op
->reloc_flag
== RELOC_ABS
)
353 (*info
->print_address_func
) (val
, info
);
357 if (oper
->flags
& OPERAND_SIGNED
)
359 int max
= (1 << (bits
- 1));
364 val
&= ((1 << bits
) - 1);
365 (*info
->fprintf_func
) (info
->stream
, "-");
368 (*info
->fprintf_func
) (info
->stream
, "0x%x", val
);
370 /* If there is another operand, then write a comma and space. */
371 if (insn
->form
->operands
[opind
] && !(found_control
&& opind
== 2))
375 (*info
->fprintf_func
) (info
->stream
, ")");
379 extract_value (num
, oper
, is_long
)
381 struct d30v_operand
*oper
;
385 int shift
= 12 - oper
->position
;
386 int mask
= (0xFFFFFFFF >> (32 - oper
->bits
));
390 if (oper
->bits
== 32)
392 /* Piece together 32-bit constant. */
393 val
= ((num
& 0x3FFFF)
394 | ((num
& 0xFF00000) >> 2)
395 | ((num
& 0x3F00000000LL
) >> 6));
398 val
= (num
>> (32 + shift
)) & mask
;
401 val
= (num
>> shift
) & mask
;
403 if (oper
->flags
& OPERAND_SHIFT
)