1 /* mmix-dis.c -- Disassemble MMIX instructions. 2 Copyright (C) 2000-2020 Free Software Foundation, Inc. 3 Written by Hans-Peter Nilsson (hp@bitrange.com) 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 file; see the file COPYING. If not, write to the Free 19 Software Foundation, 51 Franklin Street - Fifth Floor, Boston, 20 MA 02110-1301, USA. */ 21 22 #include "sysdep.h" 23 #include <stdio.h> 24 #include "opcode/mmix.h" 25 #include "disassemble.h" 26 #include "libiberty.h" 27 #include "bfd.h" 28 #include "opintl.h" 29 30 #define BAD_CASE(x) \ 31 do \ 32 { \ 33 opcodes_error_handler (_("bad case %d (%s) in %s:%d"), \ 34 x, #x, __FILE__, __LINE__); \ 35 abort (); \ 36 } \ 37 while (0) 38 39 #define FATAL_DEBUG \ 40 do \ 41 { \ 42 opcodes_error_handler (_("internal: non-debugged code " \ 43 "(test-case missing): %s:%d"), \ 44 __FILE__, __LINE__); \ 45 abort (); \ 46 } \ 47 while (0) 48 49 #define ROUND_MODE(n) \ 50 ((n) == 1 ? "ROUND_OFF" : (n) == 2 ? "ROUND_UP" : \ 51 (n) == 3 ? "ROUND_DOWN" : (n) == 4 ? "ROUND_NEAR" : \ 52 _("(unknown)")) 53 54 #define INSN_IMMEDIATE_BIT (IMM_OFFSET_BIT << 24) 55 #define INSN_BACKWARD_OFFSET_BIT (1 << 24) 56 57 #define MAX_REG_NAME_LEN 256 58 #define MAX_SPEC_REG_NAME_LEN 32 59 struct mmix_dis_info 60 { 61 const char *reg_name[MAX_REG_NAME_LEN]; 62 const char *spec_reg_name[MAX_SPEC_REG_NAME_LEN]; 63 64 /* Waste a little memory so we don't have to allocate each separately. 65 We could have an array with static contents for these, but on the 66 other hand, we don't have to. */ 67 char basic_reg_name[MAX_REG_NAME_LEN][sizeof ("$255")]; 68 }; 69 70 /* Initialize a target-specific array in INFO. */ 71 72 static bfd_boolean 73 initialize_mmix_dis_info (struct disassemble_info *info) 74 { 75 struct mmix_dis_info *minfop = malloc (sizeof (struct mmix_dis_info)); 76 long i; 77 78 if (minfop == NULL) 79 return FALSE; 80 81 memset (minfop, 0, sizeof (*minfop)); 82 83 /* Initialize register names from register symbols. If there's no 84 register section, then there are no register symbols. */ 85 if ((info->section != NULL && info->section->owner != NULL) 86 || (info->symbols != NULL 87 && info->symbols[0] != NULL 88 && bfd_asymbol_bfd (info->symbols[0]) != NULL)) 89 { 90 bfd *abfd = info->section && info->section->owner != NULL 91 ? info->section->owner 92 : bfd_asymbol_bfd (info->symbols[0]); 93 asection *reg_section = bfd_get_section_by_name (abfd, "*REG*"); 94 95 if (reg_section != NULL) 96 { 97 /* The returned symcount *does* include the ending NULL. */ 98 long symsize = bfd_get_symtab_upper_bound (abfd); 99 asymbol **syms = malloc (symsize); 100 long nsyms; 101 102 if (syms == NULL) 103 { 104 FATAL_DEBUG; 105 free (minfop); 106 return FALSE; 107 } 108 nsyms = bfd_canonicalize_symtab (abfd, syms); 109 110 /* We use the first name for a register. If this is MMO, then 111 it's the name with the first sequence number, presumably the 112 first in the source. */ 113 for (i = 0; i < nsyms && syms[i] != NULL; i++) 114 { 115 if (syms[i]->section == reg_section 116 && syms[i]->value < MAX_REG_NAME_LEN 117 && minfop->reg_name[syms[i]->value] == NULL) 118 minfop->reg_name[syms[i]->value] = syms[i]->name; 119 } 120 } 121 } 122 123 /* Fill in the rest with the canonical names. */ 124 for (i = 0; i < MAX_REG_NAME_LEN; i++) 125 if (minfop->reg_name[i] == NULL) 126 { 127 sprintf (minfop->basic_reg_name[i], "$%ld", i); 128 minfop->reg_name[i] = minfop->basic_reg_name[i]; 129 } 130 131 /* We assume it's actually a one-to-one mapping of number-to-name. */ 132 for (i = 0; mmix_spec_regs[i].name != NULL; i++) 133 minfop->spec_reg_name[mmix_spec_regs[i].number] = mmix_spec_regs[i].name; 134 135 info->private_data = (void *) minfop; 136 return TRUE; 137 } 138 139 /* A table indexed by the first byte is constructed as we disassemble each 140 tetrabyte. The contents is a pointer into mmix_insns reflecting the 141 first found entry with matching match-bits and lose-bits. Further 142 entries are considered one after one until the operand constraints 143 match or the match-bits and lose-bits do not match. Normally a 144 "further entry" will just show that there was no other match. */ 145 146 static const struct mmix_opcode * 147 get_opcode (unsigned long insn) 148 { 149 static const struct mmix_opcode **opcodes = NULL; 150 const struct mmix_opcode *opcodep = mmix_opcodes; 151 unsigned int opcode_part = (insn >> 24) & 255; 152 153 if (opcodes == NULL) 154 opcodes = xcalloc (256, sizeof (struct mmix_opcode *)); 155 156 opcodep = opcodes[opcode_part]; 157 if (opcodep == NULL 158 || (opcodep->match & insn) != opcodep->match 159 || (opcodep->lose & insn) != 0) 160 { 161 /* Search through the table. */ 162 for (opcodep = mmix_opcodes; opcodep->name != NULL; opcodep++) 163 { 164 /* FIXME: Break out this into an initialization function. */ 165 if ((opcodep->match & (opcode_part << 24)) == opcode_part 166 && (opcodep->lose & (opcode_part << 24)) == 0) 167 opcodes[opcode_part] = opcodep; 168 169 if ((opcodep->match & insn) == opcodep->match 170 && (opcodep->lose & insn) == 0) 171 break; 172 } 173 } 174 175 if (opcodep->name == NULL) 176 return NULL; 177 178 /* Check constraints. If they don't match, loop through the next opcode 179 entries. */ 180 do 181 { 182 switch (opcodep->operands) 183 { 184 /* These have no restraint on what can be in the lower three 185 bytes. */ 186 case mmix_operands_regs: 187 case mmix_operands_reg_yz: 188 case mmix_operands_regs_z_opt: 189 case mmix_operands_regs_z: 190 case mmix_operands_jmp: 191 case mmix_operands_pushgo: 192 case mmix_operands_pop: 193 case mmix_operands_sync: 194 case mmix_operands_x_regs_z: 195 case mmix_operands_neg: 196 case mmix_operands_pushj: 197 case mmix_operands_regaddr: 198 case mmix_operands_get: 199 case mmix_operands_set: 200 case mmix_operands_save: 201 case mmix_operands_unsave: 202 case mmix_operands_xyz_opt: 203 return opcodep; 204 205 /* For a ROUND_MODE, the middle byte must be 0..4. */ 206 case mmix_operands_roundregs_z: 207 case mmix_operands_roundregs: 208 { 209 int midbyte = (insn >> 8) & 255; 210 211 if (midbyte <= 4) 212 return opcodep; 213 } 214 break; 215 216 case mmix_operands_put: 217 /* A "PUT". If it is "immediate", then no restrictions, 218 otherwise we have to make sure the register number is < 32. */ 219 if ((insn & INSN_IMMEDIATE_BIT) 220 || ((insn >> 16) & 255) < 32) 221 return opcodep; 222 break; 223 224 case mmix_operands_resume: 225 /* Middle bytes must be zero. */ 226 if ((insn & 0x00ffff00) == 0) 227 return opcodep; 228 break; 229 230 default: 231 BAD_CASE (opcodep->operands); 232 } 233 234 opcodep++; 235 } 236 while ((opcodep->match & insn) == opcodep->match 237 && (opcodep->lose & insn) == 0); 238 239 /* If we got here, we had no match. */ 240 return NULL; 241 } 242 243 static inline const char * 244 get_reg_name (const struct mmix_dis_info * minfop, unsigned int x) 245 { 246 if (x >= MAX_REG_NAME_LEN) 247 return _("*illegal*"); 248 return minfop->reg_name[x]; 249 } 250 251 static inline const char * 252 get_spec_reg_name (const struct mmix_dis_info * minfop, unsigned int x) 253 { 254 if (x >= MAX_SPEC_REG_NAME_LEN) 255 return _("*illegal*"); 256 return minfop->spec_reg_name[x]; 257 } 258 259 /* The main disassembly function. */ 260 261 int 262 print_insn_mmix (bfd_vma memaddr, struct disassemble_info *info) 263 { 264 unsigned char buffer[4]; 265 unsigned long insn; 266 unsigned int x, y, z; 267 const struct mmix_opcode *opcodep; 268 int status = (*info->read_memory_func) (memaddr, buffer, 4, info); 269 struct mmix_dis_info *minfop; 270 271 if (status != 0) 272 { 273 (*info->memory_error_func) (status, memaddr, info); 274 return -1; 275 } 276 277 /* FIXME: Is -1 suitable? */ 278 if (info->private_data == NULL 279 && ! initialize_mmix_dis_info (info)) 280 return -1; 281 282 minfop = (struct mmix_dis_info *) info->private_data; 283 x = buffer[1]; 284 y = buffer[2]; 285 z = buffer[3]; 286 287 insn = bfd_getb32 (buffer); 288 289 opcodep = get_opcode (insn); 290 291 if (opcodep == NULL) 292 { 293 (*info->fprintf_func) (info->stream, _("*unknown*")); 294 return 4; 295 } 296 297 (*info->fprintf_func) (info->stream, "%s ", opcodep->name); 298 299 /* Present bytes in the order they are laid out in memory. */ 300 info->display_endian = BFD_ENDIAN_BIG; 301 302 info->insn_info_valid = 1; 303 info->bytes_per_chunk = 4; 304 info->branch_delay_insns = 0; 305 info->target = 0; 306 switch (opcodep->type) 307 { 308 case mmix_type_normal: 309 case mmix_type_memaccess_block: 310 info->insn_type = dis_nonbranch; 311 break; 312 313 case mmix_type_branch: 314 info->insn_type = dis_branch; 315 break; 316 317 case mmix_type_condbranch: 318 info->insn_type = dis_condbranch; 319 break; 320 321 case mmix_type_memaccess_octa: 322 info->insn_type = dis_dref; 323 info->data_size = 8; 324 break; 325 326 case mmix_type_memaccess_tetra: 327 info->insn_type = dis_dref; 328 info->data_size = 4; 329 break; 330 331 case mmix_type_memaccess_wyde: 332 info->insn_type = dis_dref; 333 info->data_size = 2; 334 break; 335 336 case mmix_type_memaccess_byte: 337 info->insn_type = dis_dref; 338 info->data_size = 1; 339 break; 340 341 case mmix_type_jsr: 342 info->insn_type = dis_jsr; 343 break; 344 345 default: 346 BAD_CASE(opcodep->type); 347 } 348 349 switch (opcodep->operands) 350 { 351 case mmix_operands_regs: 352 /* All registers: "$X,$Y,$Z". */ 353 (*info->fprintf_func) (info->stream, "%s,%s,%s", 354 get_reg_name (minfop, x), 355 get_reg_name (minfop, y), 356 get_reg_name (minfop, z)); 357 break; 358 359 case mmix_operands_reg_yz: 360 /* Like SETH - "$X,YZ". */ 361 (*info->fprintf_func) (info->stream, "%s,0x%x", 362 get_reg_name (minfop, x), y * 256 + z); 363 break; 364 365 case mmix_operands_regs_z_opt: 366 case mmix_operands_regs_z: 367 case mmix_operands_pushgo: 368 /* The regular "$X,$Y,$Z|Z". */ 369 if (insn & INSN_IMMEDIATE_BIT) 370 (*info->fprintf_func) (info->stream, "%s,%s,%d", 371 get_reg_name (minfop, x), 372 get_reg_name (minfop, y), z); 373 else 374 (*info->fprintf_func) (info->stream, "%s,%s,%s", 375 get_reg_name (minfop, x), 376 get_reg_name (minfop, y), 377 get_reg_name (minfop, z)); 378 break; 379 380 case mmix_operands_jmp: 381 /* Address; only JMP. */ 382 { 383 bfd_signed_vma offset = (x * 65536 + y * 256 + z) * 4; 384 385 if (insn & INSN_BACKWARD_OFFSET_BIT) 386 offset -= (256 * 65536) * 4; 387 388 info->target = memaddr + offset; 389 (*info->print_address_func) (memaddr + offset, info); 390 } 391 break; 392 393 case mmix_operands_roundregs_z: 394 /* Two registers, like FLOT, possibly with rounding: "$X,$Z|Z" 395 "$X,ROUND_MODE,$Z|Z". */ 396 if (y != 0) 397 { 398 if (insn & INSN_IMMEDIATE_BIT) 399 (*info->fprintf_func) (info->stream, "%s,%s,%d", 400 get_reg_name (minfop, x), 401 ROUND_MODE (y), z); 402 else 403 (*info->fprintf_func) (info->stream, "%s,%s,%s", 404 get_reg_name (minfop, x), 405 ROUND_MODE (y), 406 get_reg_name (minfop, z)); 407 } 408 else 409 { 410 if (insn & INSN_IMMEDIATE_BIT) 411 (*info->fprintf_func) (info->stream, "%s,%d", 412 get_reg_name (minfop, x), z); 413 else 414 (*info->fprintf_func) (info->stream, "%s,%s", 415 get_reg_name (minfop, x), 416 get_reg_name (minfop, z)); 417 } 418 break; 419 420 case mmix_operands_pop: 421 /* Like POP - "X,YZ". */ 422 (*info->fprintf_func) (info->stream, "%d,%d", x, y*256 + z); 423 break; 424 425 case mmix_operands_roundregs: 426 /* Two registers, possibly with rounding: "$X,$Z" or 427 "$X,ROUND_MODE,$Z". */ 428 if (y != 0) 429 (*info->fprintf_func) (info->stream, "%s,%s,%s", 430 get_reg_name (minfop, x), 431 ROUND_MODE (y), 432 get_reg_name (minfop, z)); 433 else 434 (*info->fprintf_func) (info->stream, "%s,%s", 435 get_reg_name (minfop, x), 436 get_reg_name (minfop, z)); 437 break; 438 439 case mmix_operands_sync: 440 /* Like SYNC - "XYZ". */ 441 (*info->fprintf_func) (info->stream, "%u", 442 x * 65536 + y * 256 + z); 443 break; 444 445 case mmix_operands_x_regs_z: 446 /* Like SYNCD - "X,$Y,$Z|Z". */ 447 if (insn & INSN_IMMEDIATE_BIT) 448 (*info->fprintf_func) (info->stream, "%d,%s,%d", 449 x, get_reg_name (minfop, y), z); 450 else 451 (*info->fprintf_func) (info->stream, "%d,%s,%s", 452 x, get_reg_name (minfop, y), 453 get_reg_name (minfop, z)); 454 break; 455 456 case mmix_operands_neg: 457 /* Like NEG and NEGU - "$X,Y,$Z|Z". */ 458 if (insn & INSN_IMMEDIATE_BIT) 459 (*info->fprintf_func) (info->stream, "%s,%d,%d", 460 get_reg_name (minfop, x), y, z); 461 else 462 (*info->fprintf_func) (info->stream, "%s,%d,%s", 463 get_reg_name (minfop, x), y, 464 get_reg_name (minfop, z)); 465 break; 466 467 case mmix_operands_pushj: 468 case mmix_operands_regaddr: 469 /* Like GETA or branches - "$X,Address". */ 470 { 471 bfd_signed_vma offset = (y * 256 + z) * 4; 472 473 if (insn & INSN_BACKWARD_OFFSET_BIT) 474 offset -= 65536 * 4; 475 476 info->target = memaddr + offset; 477 478 (*info->fprintf_func) (info->stream, "%s,", get_reg_name (minfop, x)); 479 (*info->print_address_func) (memaddr + offset, info); 480 } 481 break; 482 483 case mmix_operands_get: 484 /* GET - "X,spec_reg". */ 485 (*info->fprintf_func) (info->stream, "%s,%s", 486 get_reg_name (minfop, x), 487 get_spec_reg_name (minfop, z)); 488 break; 489 490 case mmix_operands_put: 491 /* PUT - "spec_reg,$Z|Z". */ 492 if (insn & INSN_IMMEDIATE_BIT) 493 (*info->fprintf_func) (info->stream, "%s,%d", 494 get_spec_reg_name (minfop, x), z); 495 else 496 (*info->fprintf_func) (info->stream, "%s,%s", 497 get_spec_reg_name (minfop, x), 498 get_reg_name (minfop, z)); 499 break; 500 501 case mmix_operands_set: 502 /* Two registers, "$X,$Y". */ 503 (*info->fprintf_func) (info->stream, "%s,%s", 504 get_reg_name (minfop, x), 505 get_reg_name (minfop, y)); 506 break; 507 508 case mmix_operands_save: 509 /* SAVE - "$X,0". */ 510 (*info->fprintf_func) (info->stream, "%s,0", minfop->reg_name[x]); 511 break; 512 513 case mmix_operands_unsave: 514 /* UNSAVE - "0,$Z". */ 515 (*info->fprintf_func) (info->stream, "0,%s", minfop->reg_name[z]); 516 break; 517 518 case mmix_operands_xyz_opt: 519 /* Like SWYM or TRAP - "X,Y,Z". */ 520 (*info->fprintf_func) (info->stream, "%d,%d,%d", x, y, z); 521 break; 522 523 case mmix_operands_resume: 524 /* Just "Z", like RESUME. */ 525 (*info->fprintf_func) (info->stream, "%d", z); 526 break; 527 528 default: 529 (*info->fprintf_func) (info->stream, _("*unknown operands type: %d*"), 530 opcodep->operands); 531 break; 532 } 533 534 return 4; 535 } 536