1/* Disassemble D30V instructions. 2 Copyright 1997, 1998, 2000, 2001, 2005, 2007 3 Free Software Foundation, Inc. 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 program; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 20 MA 02110-1301, USA. */ 21 22#include <stdio.h> 23#include "sysdep.h" 24#include "opcode/d30v.h" 25#include "dis-asm.h" 26#include "opintl.h" 27 28#define PC_MASK 0xFFFFFFFF 29 30/* Return 0 if lookup fails, 31 1 if found and only one form, 32 2 if found and there are short and long forms. */ 33 34static int 35lookup_opcode (struct d30v_insn *insn, long num, int is_long) 36{ 37 int i = 0, index; 38 struct d30v_format *f; 39 struct d30v_opcode *op = (struct d30v_opcode *) d30v_opcode_table; 40 int op1 = (num >> 25) & 0x7; 41 int op2 = (num >> 20) & 0x1f; 42 int mod = (num >> 18) & 0x3; 43 44 /* Find the opcode. */ 45 do 46 { 47 if ((op->op1 == op1) && (op->op2 == op2)) 48 break; 49 op++; 50 } 51 while (op->name); 52 53 if (!op || !op->name) 54 return 0; 55 56 while (op->op1 == op1 && op->op2 == op2) 57 { 58 /* Scan through all the formats for the opcode. */ 59 index = op->format[i++]; 60 do 61 { 62 f = (struct d30v_format *) &d30v_format_table[index]; 63 while (f->form == index) 64 { 65 if ((!is_long || f->form >= LONG) && (f->modifier == mod)) 66 { 67 insn->form = f; 68 break; 69 } 70 f++; 71 } 72 if (insn->form) 73 break; 74 } 75 while ((index = op->format[i++]) != 0); 76 if (insn->form) 77 break; 78 op++; 79 i = 0; 80 } 81 if (insn->form == NULL) 82 return 0; 83 84 insn->op = op; 85 insn->ecc = (num >> 28) & 0x7; 86 if (op->format[1]) 87 return 2; 88 else 89 return 1; 90} 91 92static int 93extract_value (long long num, struct d30v_operand *oper, int is_long) 94{ 95 int val; 96 int shift = 12 - oper->position; 97 int mask = (0xFFFFFFFF >> (32 - oper->bits)); 98 99 if (is_long) 100 { 101 if (oper->bits == 32) 102 /* Piece together 32-bit constant. */ 103 val = ((num & 0x3FFFF) 104 | ((num & 0xFF00000) >> 2) 105 | ((num & 0x3F00000000LL) >> 6)); 106 else 107 val = (num >> (32 + shift)) & mask; 108 } 109 else 110 val = (num >> shift) & mask; 111 112 if (oper->flags & OPERAND_SHIFT) 113 val <<= 3; 114 115 return val; 116} 117 118static void 119print_insn (struct disassemble_info *info, 120 bfd_vma memaddr, 121 long long num, 122 struct d30v_insn *insn, 123 int is_long, 124 int show_ext) 125{ 126 int val, opnum, need_comma = 0; 127 struct d30v_operand *oper; 128 int i, match, opind = 0, need_paren = 0, found_control = 0; 129 130 (*info->fprintf_func) (info->stream, "%s", insn->op->name); 131 132 /* Check for CMP or CMPU. */ 133 if (d30v_operand_table[insn->form->operands[0]].flags & OPERAND_NAME) 134 { 135 opind++; 136 val = 137 extract_value (num, 138 (struct d30v_operand *) &d30v_operand_table[insn->form->operands[0]], 139 is_long); 140 (*info->fprintf_func) (info->stream, "%s", d30v_cc_names[val]); 141 } 142 143 /* Add in ".s" or ".l". */ 144 if (show_ext == 2) 145 { 146 if (is_long) 147 (*info->fprintf_func) (info->stream, ".l"); 148 else 149 (*info->fprintf_func) (info->stream, ".s"); 150 } 151 152 if (insn->ecc) 153 (*info->fprintf_func) (info->stream, "/%s", d30v_ecc_names[insn->ecc]); 154 155 (*info->fprintf_func) (info->stream, "\t"); 156 157 while ((opnum = insn->form->operands[opind++]) != 0) 158 { 159 int bits; 160 161 oper = (struct d30v_operand *) &d30v_operand_table[opnum]; 162 bits = oper->bits; 163 if (oper->flags & OPERAND_SHIFT) 164 bits += 3; 165 166 if (need_comma 167 && oper->flags != OPERAND_PLUS 168 && oper->flags != OPERAND_MINUS) 169 { 170 need_comma = 0; 171 (*info->fprintf_func) (info->stream, ", "); 172 } 173 174 if (oper->flags == OPERAND_ATMINUS) 175 { 176 (*info->fprintf_func) (info->stream, "@-"); 177 continue; 178 } 179 if (oper->flags == OPERAND_MINUS) 180 { 181 (*info->fprintf_func) (info->stream, "-"); 182 continue; 183 } 184 if (oper->flags == OPERAND_PLUS) 185 { 186 (*info->fprintf_func) (info->stream, "+"); 187 continue; 188 } 189 if (oper->flags == OPERAND_ATSIGN) 190 { 191 (*info->fprintf_func) (info->stream, "@"); 192 continue; 193 } 194 if (oper->flags == OPERAND_ATPAR) 195 { 196 (*info->fprintf_func) (info->stream, "@("); 197 need_paren = 1; 198 continue; 199 } 200 201 if (oper->flags == OPERAND_SPECIAL) 202 continue; 203 204 val = extract_value (num, oper, is_long); 205 206 if (oper->flags & OPERAND_REG) 207 { 208 match = 0; 209 if (oper->flags & OPERAND_CONTROL) 210 { 211 struct d30v_operand *oper3 = 212 (struct d30v_operand *) &d30v_operand_table[insn->form->operands[2]]; 213 int id = extract_value (num, oper3, is_long); 214 215 found_control = 1; 216 switch (id) 217 { 218 case 0: 219 val |= OPERAND_CONTROL; 220 break; 221 case 1: 222 case 2: 223 val = OPERAND_CONTROL + MAX_CONTROL_REG + id; 224 break; 225 case 3: 226 val |= OPERAND_FLAG; 227 break; 228 default: 229 fprintf (stderr, "illegal id (%d)\n", id); 230 } 231 } 232 else if (oper->flags & OPERAND_ACC) 233 val |= OPERAND_ACC; 234 else if (oper->flags & OPERAND_FLAG) 235 val |= OPERAND_FLAG; 236 for (i = 0; i < reg_name_cnt (); i++) 237 { 238 if (val == pre_defined_registers[i].value) 239 { 240 if (pre_defined_registers[i].pname) 241 (*info->fprintf_func) 242 (info->stream, "%s", pre_defined_registers[i].pname); 243 else 244 (*info->fprintf_func) 245 (info->stream, "%s", pre_defined_registers[i].name); 246 match = 1; 247 break; 248 } 249 } 250 if (match == 0) 251 { 252 /* This would only get executed if a register was not in 253 the register table. */ 254 (*info->fprintf_func) 255 (info->stream, _("<unknown register %d>"), val & 0x3F); 256 } 257 } 258 /* repeati has a relocation, but its first argument is a plain 259 immediate. OTOH instructions like djsri have a pc-relative 260 delay target, but an absolute jump target. Therefore, a test 261 of insn->op->reloc_flag is not specific enough; we must test 262 if the actual operand we are handling now is pc-relative. */ 263 else if (oper->flags & OPERAND_PCREL) 264 { 265 int neg = 0; 266 267 /* IMM6S3 is unsigned. */ 268 if (oper->flags & OPERAND_SIGNED || bits == 32) 269 { 270 long max; 271 max = (1 << (bits - 1)); 272 if (val & max) 273 { 274 if (bits == 32) 275 val = -val; 276 else 277 val = -val & ((1 << bits) - 1); 278 neg = 1; 279 } 280 } 281 if (neg) 282 { 283 (*info->fprintf_func) (info->stream, "-%x\t(", val); 284 (*info->print_address_func) ((memaddr - val) & PC_MASK, info); 285 (*info->fprintf_func) (info->stream, ")"); 286 } 287 else 288 { 289 (*info->fprintf_func) (info->stream, "%x\t(", val); 290 (*info->print_address_func) ((memaddr + val) & PC_MASK, info); 291 (*info->fprintf_func) (info->stream, ")"); 292 } 293 } 294 else if (insn->op->reloc_flag == RELOC_ABS) 295 { 296 (*info->print_address_func) (val, info); 297 } 298 else 299 { 300 if (oper->flags & OPERAND_SIGNED) 301 { 302 int max = (1 << (bits - 1)); 303 304 if (val & max) 305 { 306 val = -val; 307 if (bits < 32) 308 val &= ((1 << bits) - 1); 309 (*info->fprintf_func) (info->stream, "-"); 310 } 311 } 312 (*info->fprintf_func) (info->stream, "0x%x", val); 313 } 314 /* If there is another operand, then write a comma and space. */ 315 if (insn->form->operands[opind] && !(found_control && opind == 2)) 316 need_comma = 1; 317 } 318 if (need_paren) 319 (*info->fprintf_func) (info->stream, ")"); 320} 321 322int 323print_insn_d30v (bfd_vma memaddr, struct disassemble_info *info) 324{ 325 int status, result; 326 bfd_byte buffer[12]; 327 unsigned long in1, in2; 328 struct d30v_insn insn; 329 long long num; 330 331 insn.form = NULL; 332 333 info->bytes_per_line = 8; 334 info->bytes_per_chunk = 4; 335 info->display_endian = BFD_ENDIAN_BIG; 336 337 status = (*info->read_memory_func) (memaddr, buffer, 4, info); 338 if (status != 0) 339 { 340 (*info->memory_error_func) (status, memaddr, info); 341 return -1; 342 } 343 in1 = bfd_getb32 (buffer); 344 345 status = (*info->read_memory_func) (memaddr + 4, buffer, 4, info); 346 if (status != 0) 347 { 348 info->bytes_per_line = 8; 349 if (!(result = lookup_opcode (&insn, in1, 0))) 350 (*info->fprintf_func) (info->stream, ".long\t0x%lx", in1); 351 else 352 print_insn (info, memaddr, (long long) in1, &insn, 0, result); 353 return 4; 354 } 355 in2 = bfd_getb32 (buffer); 356 357 if (in1 & in2 & FM01) 358 { 359 /* LONG instruction. */ 360 if (!(result = lookup_opcode (&insn, in1, 1))) 361 { 362 (*info->fprintf_func) (info->stream, ".long\t0x%lx,0x%lx", in1, in2); 363 return 8; 364 } 365 num = (long long) in1 << 32 | in2; 366 print_insn (info, memaddr, num, &insn, 1, result); 367 } 368 else 369 { 370 num = in1; 371 if (!(result = lookup_opcode (&insn, in1, 0))) 372 (*info->fprintf_func) (info->stream, ".long\t0x%lx", in1); 373 else 374 print_insn (info, memaddr, num, &insn, 0, result); 375 376 switch (((in1 >> 31) << 1) | (in2 >> 31)) 377 { 378 case 0: 379 (*info->fprintf_func) (info->stream, "\t||\t"); 380 break; 381 case 1: 382 (*info->fprintf_func) (info->stream, "\t->\t"); 383 break; 384 case 2: 385 (*info->fprintf_func) (info->stream, "\t<-\t"); 386 default: 387 break; 388 } 389 390 insn.form = NULL; 391 num = in2; 392 if (!(result = lookup_opcode (&insn, in2, 0))) 393 (*info->fprintf_func) (info->stream, ".long\t0x%lx", in2); 394 else 395 print_insn (info, memaddr, num, &insn, 0, result); 396 } 397 return 8; 398} 399