1 /* Disassemble D30V instructions. 2 Copyright (C) 1997, 1998, 2000 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 "sysdep.h" 19 #include <stdio.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 } 109 return 8; 110 } 111 112 113 /* returns 0 if lookup fails */ 114 /* 1 if found and only one form */ 115 /* 2 if found and there are short and long forms */ 116 static int 117 lookup_opcode (insn, num, is_long) 118 struct d30v_insn *insn; 119 long num; 120 int is_long; 121 { 122 int i=0, index; 123 struct d30v_format *f; 124 struct d30v_opcode *op = (struct d30v_opcode *)d30v_opcode_table; 125 int op1 = (num >> 25) & 0x7; 126 int op2 = (num >> 20) & 0x1f; 127 int mod = (num >> 18) & 0x3; 128 129 /* find the opcode */ 130 do { 131 if ((op->op1 == op1) && (op->op2 == op2)) 132 break; 133 op++; 134 } while (op->name); 135 136 if (!op || !op->name) 137 return 0; 138 139 while (op->op1 == op1 && op->op2 == op2) 140 { 141 /* scan through all the formats for the opcode */ 142 index = op->format[i++]; 143 do 144 { 145 f = (struct d30v_format *)&d30v_format_table[index]; 146 while (f->form == index) 147 { 148 if ((!is_long || f->form >= LONG) && (f->modifier == mod)) 149 { 150 insn->form = f; 151 break; 152 } 153 f++; 154 } 155 if (insn->form) 156 break; 157 } while ((index = op->format[i++]) != 0); 158 if (insn->form) 159 break; 160 op++; 161 i=0; 162 } 163 if (insn->form == NULL) 164 return 0; 165 166 insn->op = op; 167 insn->ecc = (num >> 28) & 0x7; 168 if (op->format[1]) 169 return 2; 170 else 171 return 1; 172 } 173 174 175 static void 176 print_insn ( info, memaddr, num, insn, is_long, show_ext ) 177 struct disassemble_info *info; 178 bfd_vma memaddr; 179 long long num; 180 struct d30v_insn *insn; 181 int is_long; 182 int show_ext; 183 { 184 int val, opnum, need_comma=0; 185 struct d30v_operand *oper; 186 int i, match, opind=0, need_paren=0, found_control=0; 187 188 (*info->fprintf_func) (info->stream, "%s",insn->op->name); 189 190 /* check for CMP or CMPU */ 191 if (d30v_operand_table[insn->form->operands[0]].flags & OPERAND_NAME) 192 { 193 opind++; 194 val = extract_value(num,(struct d30v_operand *)&d30v_operand_table[insn->form->operands[0]],is_long); 195 (*info->fprintf_func) (info->stream, "%s",d30v_cc_names[val]); 196 } 197 198 /* add in ".s" or ".l" */ 199 if (show_ext == 2) 200 { 201 if (is_long) 202 (*info->fprintf_func) (info->stream, ".l"); 203 else 204 (*info->fprintf_func) (info->stream, ".s"); 205 } 206 207 if (insn->ecc) 208 (*info->fprintf_func) (info->stream, "/%s",d30v_ecc_names[insn->ecc]); 209 210 (*info->fprintf_func) (info->stream, "\t"); 211 212 while ((opnum = insn->form->operands[opind++]) != 0) 213 { 214 int bits; 215 oper = (struct d30v_operand *)&d30v_operand_table[opnum]; 216 bits = oper->bits; 217 if (oper->flags & OPERAND_SHIFT) 218 bits += 3; 219 220 if (need_comma && oper->flags != OPERAND_PLUS && oper->flags != OPERAND_MINUS) 221 { 222 need_comma=0; 223 (*info->fprintf_func) (info->stream, ", "); 224 } 225 226 if (oper->flags == OPERAND_ATMINUS) 227 { 228 (*info->fprintf_func) (info->stream, "@-"); 229 continue; 230 } 231 if (oper->flags == OPERAND_MINUS) 232 { 233 (*info->fprintf_func) (info->stream, "-"); 234 continue; 235 } 236 if (oper->flags == OPERAND_PLUS) 237 { 238 (*info->fprintf_func) (info->stream, "+"); 239 continue; 240 } 241 if (oper->flags == OPERAND_ATSIGN) 242 { 243 (*info->fprintf_func) (info->stream, "@"); 244 continue; 245 } 246 if (oper->flags == OPERAND_ATPAR) 247 { 248 (*info->fprintf_func) (info->stream, "@("); 249 need_paren = 1; 250 continue; 251 } 252 253 if (oper->flags == OPERAND_SPECIAL) 254 continue; 255 256 val = extract_value(num, oper, is_long); 257 258 if (oper->flags & OPERAND_REG) 259 { 260 match = 0; 261 if (oper->flags & OPERAND_CONTROL) 262 { 263 struct d30v_operand *oper3 = 264 (struct d30v_operand *)&d30v_operand_table[insn->form->operands[2]]; 265 int id = extract_value (num, oper3, is_long ); 266 found_control = 1; 267 switch ( id ) 268 { 269 case 0: 270 val |= OPERAND_CONTROL; 271 break; 272 case 1: 273 case 2: 274 val = OPERAND_CONTROL + MAX_CONTROL_REG + id; 275 break; 276 case 3: 277 val |= OPERAND_FLAG; 278 break; 279 default: 280 fprintf(stderr,"illegal id (%d)\n",id); 281 } 282 } 283 else if (oper->flags & OPERAND_ACC) 284 val |= OPERAND_ACC; 285 else if (oper->flags & OPERAND_FLAG) 286 val |= OPERAND_FLAG; 287 for (i=0;i<reg_name_cnt();i++) 288 { 289 if (val == pre_defined_registers[i].value) 290 { 291 if (pre_defined_registers[i].pname) 292 (*info->fprintf_func) 293 (info->stream, "%s",pre_defined_registers[i].pname); 294 else 295 (*info->fprintf_func) 296 (info->stream, "%s",pre_defined_registers[i].name); 297 match=1; 298 break; 299 } 300 } 301 if (match==0) 302 { 303 /* this would only get executed if a register was not in the 304 register table */ 305 (*info->fprintf_func) 306 (info->stream, _("<unknown register %d>"), val & 0x3F); 307 } 308 } 309 /* repeati has a relocation, but its first argument is a plain 310 immediate. OTOH instructions like djsri have a pc-relative 311 delay target, but a absolute jump target. Therefore, a test 312 of insn->op->reloc_flag is not specific enough; we must test 313 if the actual operand we are handling now is pc-relative. */ 314 else if (oper->flags & OPERAND_PCREL) 315 { 316 int neg = 0; 317 318 /* IMM6S3 is unsigned. */ 319 if (oper->flags & OPERAND_SIGNED || bits == 32) 320 { 321 long max; 322 max = (1 << (bits - 1)); 323 if (val & max) 324 { 325 if (bits == 32) 326 val = -val; 327 else 328 val = -val & ((1 << bits)-1); 329 neg = 1; 330 } 331 } 332 if (neg) 333 { 334 (*info->fprintf_func) (info->stream, "-%x\t(",val); 335 (*info->print_address_func) ((memaddr - val) & PC_MASK, info); 336 (*info->fprintf_func) (info->stream, ")"); 337 } 338 else 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 } 345 else if (insn->op->reloc_flag == RELOC_ABS) 346 { 347 (*info->print_address_func) (val, info); 348 } 349 else 350 { 351 if (oper->flags & OPERAND_SIGNED) 352 { 353 int max = (1 << (bits - 1)); 354 if (val & max) 355 { 356 val = -val; 357 if (bits < 32) 358 val &= ((1 << bits) - 1); 359 (*info->fprintf_func) (info->stream, "-"); 360 } 361 } 362 (*info->fprintf_func) (info->stream, "0x%x",val); 363 } 364 /* if there is another operand, then write a comma and space */ 365 if (insn->form->operands[opind] && !(found_control && opind == 2)) 366 need_comma = 1; 367 } 368 if (need_paren) 369 (*info->fprintf_func) (info->stream, ")"); 370 } 371 372 373 374 static int 375 extract_value (num, oper, is_long) 376 long long num; 377 struct d30v_operand *oper; 378 int is_long; 379 { 380 int val; 381 int shift = 12 - oper->position; 382 int mask = (0xFFFFFFFF >> (32 - oper->bits)); 383 384 if (is_long) 385 { 386 if (oper->bits == 32) 387 { 388 /* piece together 32-bit constant */ 389 val = ((num & 0x3FFFF) 390 | ((num & 0xFF00000) >> 2) 391 | ((num & 0x3F00000000LL) >> 6)); 392 } 393 else 394 val = (num >> (32 + shift)) & mask; 395 } 396 else 397 val = (num >> shift) & mask; 398 399 if (oper->flags & OPERAND_SHIFT) 400 val <<= 3; 401 402 return val; 403 } 404