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