1271493Sdelphij/* Disassemble D30V instructions. 2271493Sdelphij Copyright (C) 1997-2024 Free Software Foundation, Inc. 3271493Sdelphij 4271493Sdelphij This file is part of the GNU opcodes library. 5271493Sdelphij 6271493Sdelphij This library is free software; you can redistribute it and/or modify 7271493Sdelphij it under the terms of the GNU General Public License as published by 8271493Sdelphij the Free Software Foundation; either version 3, or (at your option) 9271493Sdelphij any later version. 10271493Sdelphij 11271493Sdelphij It is distributed in the hope that it will be useful, but WITHOUT 12271493Sdelphij ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13271493Sdelphij or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14271493Sdelphij License for more details. 15271493Sdelphij 16271493Sdelphij You should have received a copy of the GNU General Public License 17271493Sdelphij along with this program; if not, write to the Free Software 18271493Sdelphij Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 19271493Sdelphij MA 02110-1301, USA. */ 20271493Sdelphij 21271493Sdelphij#include "sysdep.h" 22271493Sdelphij#include <stdio.h> 23271493Sdelphij#include "opcode/d30v.h" 24271493Sdelphij#include "disassemble.h" 25271493Sdelphij#include "opintl.h" 26271493Sdelphij#include "libiberty.h" 27271493Sdelphij 28271493Sdelphij#define PC_MASK 0xFFFFFFFF 29271493Sdelphij 30271493Sdelphij/* Return 0 if lookup fails, 31271493Sdelphij 1 if found and only one form, 32271493Sdelphij 2 if found and there are short and long forms. */ 33271493Sdelphij 34271493Sdelphijstatic int 35271493Sdelphijlookup_opcode (struct d30v_insn *insn, long num, int is_long) 36271493Sdelphij{ 37271493Sdelphij int i = 0, op_index; 38271493Sdelphij struct d30v_format *f; 39271493Sdelphij struct d30v_opcode *op = (struct d30v_opcode *) d30v_opcode_table; 40271493Sdelphij int op1 = (num >> 25) & 0x7; 41271493Sdelphij int op2 = (num >> 20) & 0x1f; 42271493Sdelphij int mod = (num >> 18) & 0x3; 43271493Sdelphij 44271493Sdelphij /* Find the opcode. */ 45271493Sdelphij do 46271493Sdelphij { 47271493Sdelphij if ((op->op1 == op1) && (op->op2 == op2)) 48271493Sdelphij break; 49271493Sdelphij op++; 50271493Sdelphij } 51271493Sdelphij while (op->name); 52271493Sdelphij 53271493Sdelphij if (!op || !op->name) 54271493Sdelphij return 0; 55271493Sdelphij 56271493Sdelphij while (op->op1 == op1 && op->op2 == op2) 57271493Sdelphij { 58271493Sdelphij /* Scan through all the formats for the opcode. */ 59271493Sdelphij op_index = op->format[i++]; 60271493Sdelphij do 61271493Sdelphij { 62271493Sdelphij f = (struct d30v_format *) &d30v_format_table[op_index]; 63271493Sdelphij while (f->form == op_index) 64271493Sdelphij { 65271493Sdelphij if ((!is_long || f->form >= LONG) && (f->modifier == mod)) 66271493Sdelphij { 67271493Sdelphij insn->form = f; 68271493Sdelphij break; 69271493Sdelphij } 70271493Sdelphij f++; 71271493Sdelphij } 72271493Sdelphij if (insn->form) 73271493Sdelphij break; 74 } 75 while ((op_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 (uint64_t num, const struct d30v_operand *oper, int is_long) 94{ 95 unsigned int val; 96 int shift = 12 - oper->position; 97 unsigned 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 uint64_t num, 122 struct d30v_insn *insn, 123 int is_long, 124 int show_ext) 125{ 126 unsigned int val, opnum; 127 const struct d30v_operand *oper; 128 int i, match, need_comma = 0, need_paren = 0, found_control = 0; 129 unsigned int opind = 0; 130 131 (*info->fprintf_func) (info->stream, "%s", insn->op->name); 132 133 /* Check for CMP or CMPU. */ 134 if (d30v_operand_table[insn->form->operands[0]].flags & OPERAND_NAME) 135 { 136 opind++; 137 val = 138 extract_value (num, 139 &d30v_operand_table[insn->form->operands[0]], 140 is_long); 141 (*info->fprintf_func) (info->stream, "%s", d30v_cc_names[val]); 142 } 143 144 /* Add in ".s" or ".l". */ 145 if (show_ext == 2) 146 { 147 if (is_long) 148 (*info->fprintf_func) (info->stream, ".l"); 149 else 150 (*info->fprintf_func) (info->stream, ".s"); 151 } 152 153 if (insn->ecc) 154 (*info->fprintf_func) (info->stream, "/%s", d30v_ecc_names[insn->ecc]); 155 156 (*info->fprintf_func) (info->stream, "\t"); 157 158 while (opind < ARRAY_SIZE (insn->form->operands) 159 && (opnum = insn->form->operands[opind++]) != 0) 160 { 161 int bits; 162 163 oper = &d30v_operand_table[opnum]; 164 bits = oper->bits; 165 if (oper->flags & OPERAND_SHIFT) 166 bits += 3; 167 168 if (need_comma 169 && oper->flags != OPERAND_PLUS 170 && oper->flags != OPERAND_MINUS) 171 { 172 need_comma = 0; 173 (*info->fprintf_func) (info->stream, ", "); 174 } 175 176 if (oper->flags == OPERAND_ATMINUS) 177 { 178 (*info->fprintf_func) (info->stream, "@-"); 179 continue; 180 } 181 if (oper->flags == OPERAND_MINUS) 182 { 183 (*info->fprintf_func) (info->stream, "-"); 184 continue; 185 } 186 if (oper->flags == OPERAND_PLUS) 187 { 188 (*info->fprintf_func) (info->stream, "+"); 189 continue; 190 } 191 if (oper->flags == OPERAND_ATSIGN) 192 { 193 (*info->fprintf_func) (info->stream, "@"); 194 continue; 195 } 196 if (oper->flags == OPERAND_ATPAR) 197 { 198 (*info->fprintf_func) (info->stream, "@("); 199 need_paren = 1; 200 continue; 201 } 202 203 if (oper->flags == OPERAND_SPECIAL) 204 continue; 205 206 val = extract_value (num, oper, is_long); 207 208 if (oper->flags & OPERAND_REG) 209 { 210 match = 0; 211 if (oper->flags & OPERAND_CONTROL) 212 { 213 const struct d30v_operand *oper3 214 = &d30v_operand_table[insn->form->operands[2]]; 215 int id = extract_value (num, oper3, is_long); 216 217 found_control = 1; 218 switch (id) 219 { 220 case 0: 221 val |= OPERAND_CONTROL; 222 break; 223 case 1: 224 case 2: 225 val = OPERAND_CONTROL + MAX_CONTROL_REG + id; 226 break; 227 case 3: 228 val |= OPERAND_FLAG; 229 break; 230 default: 231 /* xgettext: c-format */ 232 opcodes_error_handler (_("illegal id (%d)"), id); 233 abort (); 234 } 235 } 236 else if (oper->flags & OPERAND_ACC) 237 val |= OPERAND_ACC; 238 else if (oper->flags & OPERAND_FLAG) 239 val |= OPERAND_FLAG; 240 for (i = 0; i < reg_name_cnt (); i++) 241 { 242 if (val == pre_defined_registers[i].value) 243 { 244 if (pre_defined_registers[i].pname) 245 (*info->fprintf_func) 246 (info->stream, "%s", pre_defined_registers[i].pname); 247 else 248 (*info->fprintf_func) 249 (info->stream, "%s", pre_defined_registers[i].name); 250 match = 1; 251 break; 252 } 253 } 254 if (match == 0) 255 { 256 /* This would only get executed if a register was not in 257 the register table. */ 258 (*info->fprintf_func) 259 (info->stream, _("<unknown register %d>"), val & 0x3F); 260 } 261 } 262 /* repeati has a relocation, but its first argument is a plain 263 immediate. OTOH instructions like djsri have a pc-relative 264 delay target, but an absolute jump target. Therefore, a test 265 of insn->op->reloc_flag is not specific enough; we must test 266 if the actual operand we are handling now is pc-relative. */ 267 else if (oper->flags & OPERAND_PCREL) 268 { 269 int neg = 0; 270 271 /* IMM6S3 is unsigned. */ 272 if (oper->flags & OPERAND_SIGNED || bits == 32) 273 { 274 unsigned int sign = 1u << (bits - 1); 275 if (val & sign) 276 { 277 val = -val & (sign + sign - 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 unsigned int sign = 1u << (bits - 1); 303 304 if (val & sign) 305 { 306 val = -val & (sign + sign - 1); 307 (*info->fprintf_func) (info->stream, "-"); 308 } 309 } 310 (*info->fprintf_func) (info->stream, "0x%x", val); 311 } 312 /* If there is another operand, then write a comma and space. */ 313 if (opind < ARRAY_SIZE (insn->form->operands) 314 && insn->form->operands[opind] 315 && !(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 uint32_t in1, in2; 328 struct d30v_insn insn; 329 uint64_t 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%x", in1); 351 else 352 print_insn (info, memaddr, (uint64_t) 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%x,0x%x", in1, in2); 363 return 8; 364 } 365 num = (uint64_t) 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%x", 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%x", in2); 394 else 395 print_insn (info, memaddr, num, &insn, 0, result); 396 } 397 return 8; 398} 399