diff options
Diffstat (limited to 'opcodes/d30v-dis.c')
-rw-r--r-- | opcodes/d30v-dis.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/opcodes/d30v-dis.c b/opcodes/d30v-dis.c new file mode 100644 index 00000000000..9358b753235 --- /dev/null +++ b/opcodes/d30v-dis.c @@ -0,0 +1,399 @@ +/* Disassemble D30V instructions. + Copyright (C) 1997, 1998 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <stdio.h> +#include "opcode/d30v.h" +#include "dis-asm.h" +#include "opintl.h" + +#define PC_MASK 0xFFFFFFFF + +static int lookup_opcode PARAMS (( struct d30v_insn *insn, long num, int is_long )); +static void print_insn PARAMS (( struct disassemble_info *info, bfd_vma memaddr, long long num, + struct d30v_insn *insn, int is_long, int show_ext )); +static int extract_value PARAMS (( long long num, struct d30v_operand *oper, int is_long )); + +int +print_insn_d30v (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + int status, result; + bfd_byte buffer[12]; + unsigned long in1,in2; + struct d30v_insn insn; + long long num; + + insn.form = (struct d30v_format *)NULL; + + info->bytes_per_line = 8; + info->bytes_per_chunk = 4; + info->display_endian = BFD_ENDIAN_BIG; + + status = (*info->read_memory_func) (memaddr, buffer, 4, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + in1 = bfd_getb32 (buffer); + + status = (*info->read_memory_func) (memaddr+4, buffer, 4, info); + if (status != 0) + { + info->bytes_per_line = 8; + if (!(result = lookup_opcode(&insn, in1, 0))) + (*info->fprintf_func) (info->stream, ".long\t0x%x",in1); + else + print_insn(info, memaddr, (long long) in1, &insn, 0, result); + return 4; + } + in2 = bfd_getb32 (buffer); + + if (in1 & in2 & FM01) + { + /* LONG instruction */ + if (!(result = lookup_opcode(&insn, in1, 1))) + { + (*info->fprintf_func) (info->stream, ".long\t0x%x,0x%x",in1,in2); + return 8; + } + num = (long long)in1 << 32 | in2; + print_insn(info, memaddr, num, &insn, 1, result); + } + else + { + num = in1; + if (!(result = lookup_opcode(&insn, in1, 0))) + (*info->fprintf_func) (info->stream, ".long\t0x%x",in1); + else + print_insn(info, memaddr, num, &insn, 0, result); + + switch ( ((in1>>31)<<1) | (in2>>31) ) + { + case 0: + (*info->fprintf_func) (info->stream, "\t||\t"); + break; + case 1: + (*info->fprintf_func) (info->stream, "\t->\t"); + break; + case 2: + (*info->fprintf_func) (info->stream, "\t<-\t"); + default: + break; + } + + insn.form = (struct d30v_format *)NULL; + num = in2; + if (!(result = lookup_opcode(&insn, in2, 0))) + (*info->fprintf_func) (info->stream, ".long\t0x%x",in2); + else + print_insn(info, memaddr, num, &insn, 0, result); + + } + return 8; +} + + +/* returns 0 if lookup fails */ +/* 1 if found and only one form */ +/* 2 if found and there are short and long forms */ +static int +lookup_opcode (insn, num, is_long) + struct d30v_insn *insn; + long num; + int is_long; +{ + int i=0, index; + struct d30v_format *f; + struct d30v_opcode *op = (struct d30v_opcode *)d30v_opcode_table; + int op1 = (num >> 25) & 0x7; + int op2 = (num >> 20) & 0x1f; + int mod = (num >> 18) & 0x3; + + /* find the opcode */ + do { + if ((op->op1 == op1) && (op->op2 == op2)) + break; + op++; + } while (op->name); + + if (!op || !op->name) + return 0; + + while (op->op1 == op1 && op->op2 == op2) + { + /* scan through all the formats for the opcode */ + index = op->format[i++]; + do + { + f = (struct d30v_format *)&d30v_format_table[index]; + while (f->form == index) + { + if ((!is_long || f->form >= LONG) && (f->modifier == mod)) + { + insn->form = f; + break; + } + f++; + } + if (insn->form) + break; + } while ((index = op->format[i++]) != 0); + if (insn->form) + break; + op++; + i=0; + } + if (insn->form == NULL) + return 0; + + insn->op = op; + insn->ecc = (num >> 28) & 0x7; + if (op->format[1]) + return 2; + else + return 1; +} + + +static void +print_insn ( info, memaddr, num, insn, is_long, show_ext ) + struct disassemble_info *info; + bfd_vma memaddr; + long long num; + struct d30v_insn *insn; + int is_long; + int show_ext; +{ + int val, opnum, need_comma=0; + struct d30v_operand *oper; + int i, match, opind=0, need_paren=0, found_control=0; + + (*info->fprintf_func) (info->stream, "%s",insn->op->name); + + /* check for CMP or CMPU */ + if (d30v_operand_table[insn->form->operands[0]].flags & OPERAND_NAME) + { + opind++; + val = extract_value(num,(struct d30v_operand *)&d30v_operand_table[insn->form->operands[0]],is_long); + (*info->fprintf_func) (info->stream, "%s",d30v_cc_names[val]); + } + + /* add in ".s" or ".l" */ + if (show_ext == 2) + { + if (is_long) + (*info->fprintf_func) (info->stream, ".l"); + else + (*info->fprintf_func) (info->stream, ".s"); + } + + if (insn->ecc) + (*info->fprintf_func) (info->stream, "/%s",d30v_ecc_names[insn->ecc]); + + (*info->fprintf_func) (info->stream, "\t"); + + while ((opnum = insn->form->operands[opind++]) != 0) + { + int bits; + oper = (struct d30v_operand *)&d30v_operand_table[opnum]; + bits = oper->bits; + if (oper->flags & OPERAND_SHIFT) + bits += 3; + + if (need_comma && oper->flags != OPERAND_PLUS && oper->flags != OPERAND_MINUS) + { + need_comma=0; + (*info->fprintf_func) (info->stream, ", "); + } + + if (oper->flags == OPERAND_ATMINUS) + { + (*info->fprintf_func) (info->stream, "@-"); + continue; + } + if (oper->flags == OPERAND_MINUS) + { + (*info->fprintf_func) (info->stream, "-"); + continue; + } + if (oper->flags == OPERAND_PLUS) + { + (*info->fprintf_func) (info->stream, "+"); + continue; + } + if (oper->flags == OPERAND_ATSIGN) + { + (*info->fprintf_func) (info->stream, "@"); + continue; + } + if (oper->flags == OPERAND_ATPAR) + { + (*info->fprintf_func) (info->stream, "@("); + need_paren = 1; + continue; + } + + if (oper->flags == OPERAND_SPECIAL) + continue; + + val = extract_value(num, oper, is_long); + + if (oper->flags & OPERAND_REG) + { + match = 0; + if (oper->flags & OPERAND_CONTROL) + { + struct d30v_operand *oper3 = + (struct d30v_operand *)&d30v_operand_table[insn->form->operands[2]]; + int id = extract_value (num, oper3, is_long ); + found_control = 1; + switch ( id ) + { + case 0: + val |= OPERAND_CONTROL; + break; + case 1: + case 2: + val = OPERAND_CONTROL + MAX_CONTROL_REG + id; + break; + case 3: + val |= OPERAND_FLAG; + break; + default: + fprintf(stderr,"illegal id (%d)\n",id); + } + } + else if (oper->flags & OPERAND_ACC) + val |= OPERAND_ACC; + else if (oper->flags & OPERAND_FLAG) + val |= OPERAND_FLAG; + for (i=0;i<reg_name_cnt();i++) + { + if (val == pre_defined_registers[i].value) + { + if (pre_defined_registers[i].pname) + (*info->fprintf_func) + (info->stream, "%s",pre_defined_registers[i].pname); + else + (*info->fprintf_func) + (info->stream, "%s",pre_defined_registers[i].name); + match=1; + break; + } + } + if (match==0) + { + /* this would only get executed if a register was not in the + register table */ + (*info->fprintf_func) + (info->stream, _("<unknown register %d>"), val & 0x3F); + } + } + else if (insn->op->reloc_flag == RELOC_PCREL || + (opind == 1 && (insn->form->form == SHORT_D2 || insn->form->form == LONG_D))) + { + long max; + int neg=0; + max = (1 << (bits - 1)); + if (val & max) + { + if (bits == 32) + val = -val; + else + val = -val & ((1 << bits)-1); + neg = 1; + } + if (opind == 1 && (insn->form->form == SHORT_D2 || insn->form->form == LONG_D)) + { + (*info->fprintf_func) (info->stream, "%x",val); + } + else { + if (neg) + { + (*info->fprintf_func) (info->stream, "-%x\t(",val); + (*info->print_address_func) ((memaddr - val) & PC_MASK, info); + (*info->fprintf_func) (info->stream, ")"); + } + else + { + (*info->fprintf_func) (info->stream, "%x\t(",val); + (*info->print_address_func) ((memaddr + val) & PC_MASK, info); + (*info->fprintf_func) (info->stream, ")"); + } + } + } + else if (insn->op->reloc_flag == RELOC_ABS) + { + (*info->print_address_func) (val, info); + } + else + { + if (oper->flags & OPERAND_SIGNED) + { + int max = (1 << (bits - 1)); + if (val & max) + { + val = -val; + if (bits < 32) + val &= ((1 << bits) - 1); + (*info->fprintf_func) (info->stream, "-"); + } + } + (*info->fprintf_func) (info->stream, "0x%x",val); + } + /* if there is another operand, then write a comma and space */ + if (insn->form->operands[opind] && !(found_control && opind == 2)) + need_comma = 1; + } + if (need_paren) + (*info->fprintf_func) (info->stream, ")"); +} + + + +static int +extract_value (num, oper, is_long) + long long num; + struct d30v_operand *oper; + int is_long; +{ + int val; + int shift = 12 - oper->position; + int mask = (0xFFFFFFFF >> (32 - oper->bits)); + + if (is_long) + { + if (oper->bits == 32) + { + /* piece together 32-bit constant */ + val = ((num & 0x3FFFF) + | ((num & 0xFF00000) >> 2) + | ((num & 0x3F00000000LL) >> 6)); + } + else + val = (num >> (32 + shift)) & mask; + } + else + val = (num >> shift) & mask; + + if (oper->flags & OPERAND_SHIFT) + val <<= 3; + + return val; +} |