1 /* mmix-dis.c -- Disassemble MMIX instructions. 2 Copyright (C) 2000-2024 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 bool 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 free (syms); 121 } 122 } 123 124 /* Fill in the rest with the canonical names. */ 125 for (i = 0; i < MAX_REG_NAME_LEN; i++) 126 if (minfop->reg_name[i] == NULL) 127 { 128 sprintf (minfop->basic_reg_name[i], "$%ld", i); 129 minfop->reg_name[i] = minfop->basic_reg_name[i]; 130 } 131 132 /* We assume it's actually a one-to-one mapping of number-to-name. */ 133 for (i = 0; mmix_spec_regs[i].name != NULL; i++) 134 minfop->spec_reg_name[mmix_spec_regs[i].number] = mmix_spec_regs[i].name; 135 136 info->private_data = (void *) minfop; 137 return true; 138 } 139 140 /* A table indexed by the first byte is constructed as we disassemble each 141 tetrabyte. The contents is a pointer into mmix_insns reflecting the 142 first found entry with matching match-bits and lose-bits. Further 143 entries are considered one after one until the operand constraints 144 match or the match-bits and lose-bits do not match. Normally a 145 "further entry" will just show that there was no other match. */ 146 147 static const struct mmix_opcode * 148 get_opcode (unsigned long insn) 149 { 150 static const struct mmix_opcode **opcodes = NULL; 151 const struct mmix_opcode *opcodep = mmix_opcodes; 152 unsigned int opcode_part = (insn >> 24) & 255; 153 154 if (opcodes == NULL) 155 opcodes = xcalloc (256, sizeof (struct mmix_opcode *)); 156 157 opcodep = opcodes[opcode_part]; 158 if (opcodep == NULL 159 || (opcodep->match & insn) != opcodep->match 160 || (opcodep->lose & insn) != 0) 161 { 162 /* Search through the table. */ 163 for (opcodep = mmix_opcodes; opcodep->name != NULL; opcodep++) 164 { 165 /* FIXME: Break out this into an initialization function. */ 166 if ((opcodep->match & (opcode_part << 24)) == opcode_part 167 && (opcodep->lose & (opcode_part << 24)) == 0) 168 opcodes[opcode_part] = opcodep; 169 170 if ((opcodep->match & insn) == opcodep->match 171 && (opcodep->lose & insn) == 0) 172 break; 173 } 174 } 175 176 if (opcodep->name == NULL) 177 return NULL; 178 179 /* Check constraints. If they don't match, loop through the next opcode 180 entries. */ 181 do 182 { 183 switch (opcodep->operands) 184 { 185 /* These have no restraint on what can be in the lower three 186 bytes. */ 187 case mmix_operands_regs: 188 case mmix_operands_reg_yz: 189 case mmix_operands_regs_z_opt: 190 case mmix_operands_regs_z: 191 case mmix_operands_jmp: 192 case mmix_operands_pushgo: 193 case mmix_operands_pop: 194 case mmix_operands_sync: 195 case mmix_operands_x_regs_z: 196 case mmix_operands_neg: 197 case mmix_operands_pushj: 198 case mmix_operands_regaddr: 199 case mmix_operands_get: 200 case mmix_operands_set: 201 case mmix_operands_save: 202 case mmix_operands_unsave: 203 case mmix_operands_xyz_opt: 204 return opcodep; 205 206 /* For a ROUND_MODE, the middle byte must be 0..4. */ 207 case mmix_operands_roundregs_z: 208 case mmix_operands_roundregs: 209 { 210 int midbyte = (insn >> 8) & 255; 211 212 if (midbyte <= 4) 213 return opcodep; 214 } 215 break; 216 217 case mmix_operands_put: 218 /* A "PUT". If it is "immediate", then no restrictions, 219 otherwise we have to make sure the register number is < 32. */ 220 if ((insn & INSN_IMMEDIATE_BIT) 221 || ((insn >> 16) & 255) < 32) 222 return opcodep; 223 break; 224 225 case mmix_operands_resume: 226 /* Middle bytes must be zero. */ 227 if ((insn & 0x00ffff00) == 0) 228 return opcodep; 229 break; 230 231 default: 232 BAD_CASE (opcodep->operands); 233 } 234 235 opcodep++; 236 } 237 while ((opcodep->match & insn) == opcodep->match 238 && (opcodep->lose & insn) == 0); 239 240 /* If we got here, we had no match. */ 241 return NULL; 242 } 243 244 static inline const char * 245 get_reg_name (const struct mmix_dis_info * minfop, unsigned int x) 246 { 247 if (x >= MAX_REG_NAME_LEN) 248 return _("*illegal*"); 249 return minfop->reg_name[x]; 250 } 251 252 static inline const char * 253 get_spec_reg_name (const struct mmix_dis_info * minfop, unsigned int x) 254 { 255 if (x >= MAX_SPEC_REG_NAME_LEN) 256 return _("*illegal*"); 257 return minfop->spec_reg_name[x]; 258 } 259 260 /* The main disassembly function. */ 261 262 int 263 print_insn_mmix (bfd_vma memaddr, struct disassemble_info *info) 264 { 265 unsigned char buffer[4]; 266 unsigned long insn; 267 unsigned int x, y, z; 268 const struct mmix_opcode *opcodep; 269 int status = (*info->read_memory_func) (memaddr, buffer, 4, info); 270 struct mmix_dis_info *minfop; 271 272 if (status != 0) 273 { 274 (*info->memory_error_func) (status, memaddr, info); 275 return -1; 276 } 277 278 /* FIXME: Is -1 suitable? */ 279 if (info->private_data == NULL 280 && ! initialize_mmix_dis_info (info)) 281 return -1; 282 283 minfop = (struct mmix_dis_info *) info->private_data; 284 x = buffer[1]; 285 y = buffer[2]; 286 z = buffer[3]; 287 288 insn = bfd_getb32 (buffer); 289 290 opcodep = get_opcode (insn); 291 292 if (opcodep == NULL) 293 { 294 (*info->fprintf_func) (info->stream, _("*unknown*")); 295 return 4; 296 } 297 298 (*info->fprintf_func) (info->stream, "%s ", opcodep->name); 299 300 /* Present bytes in the order they are laid out in memory. */ 301 info->display_endian = BFD_ENDIAN_BIG; 302 303 info->insn_info_valid = 1; 304 info->bytes_per_chunk = 4; 305 info->branch_delay_insns = 0; 306 info->target = 0; 307 switch (opcodep->type) 308 { 309 case mmix_type_normal: 310 case mmix_type_memaccess_block: 311 info->insn_type = dis_nonbranch; 312 break; 313 314 case mmix_type_branch: 315 info->insn_type = dis_branch; 316 break; 317 318 case mmix_type_condbranch: 319 info->insn_type = dis_condbranch; 320 break; 321 322 case mmix_type_memaccess_octa: 323 info->insn_type = dis_dref; 324 info->data_size = 8; 325 break; 326 327 case mmix_type_memaccess_tetra: 328 info->insn_type = dis_dref; 329 info->data_size = 4; 330 break; 331 332 case mmix_type_memaccess_wyde: 333 info->insn_type = dis_dref; 334 info->data_size = 2; 335 break; 336 337 case mmix_type_memaccess_byte: 338 info->insn_type = dis_dref; 339 info->data_size = 1; 340 break; 341 342 case mmix_type_jsr: 343 info->insn_type = dis_jsr; 344 break; 345 346 default: 347 BAD_CASE(opcodep->type); 348 } 349 350 switch (opcodep->operands) 351 { 352 case mmix_operands_regs: 353 /* All registers: "$X,$Y,$Z". */ 354 (*info->fprintf_func) (info->stream, "%s,%s,%s", 355 get_reg_name (minfop, x), 356 get_reg_name (minfop, y), 357 get_reg_name (minfop, z)); 358 break; 359 360 case mmix_operands_reg_yz: 361 /* Like SETH - "$X,YZ". */ 362 (*info->fprintf_func) (info->stream, "%s,0x%x", 363 get_reg_name (minfop, x), y * 256 + z); 364 break; 365 366 case mmix_operands_regs_z_opt: 367 case mmix_operands_regs_z: 368 case mmix_operands_pushgo: 369 /* The regular "$X,$Y,$Z|Z". */ 370 if (insn & INSN_IMMEDIATE_BIT) 371 (*info->fprintf_func) (info->stream, "%s,%s,%d", 372 get_reg_name (minfop, x), 373 get_reg_name (minfop, y), z); 374 else 375 (*info->fprintf_func) (info->stream, "%s,%s,%s", 376 get_reg_name (minfop, x), 377 get_reg_name (minfop, y), 378 get_reg_name (minfop, z)); 379 break; 380 381 case mmix_operands_jmp: 382 /* Address; only JMP. */ 383 { 384 bfd_signed_vma offset = (x * 65536 + y * 256 + z) * 4; 385 386 if (insn & INSN_BACKWARD_OFFSET_BIT) 387 offset -= (256 * 65536) * 4; 388 389 info->target = memaddr + offset; 390 (*info->print_address_func) (memaddr + offset, info); 391 } 392 break; 393 394 case mmix_operands_roundregs_z: 395 /* Two registers, like FLOT, possibly with rounding: "$X,$Z|Z" 396 "$X,ROUND_MODE,$Z|Z". */ 397 if (y != 0) 398 { 399 if (insn & INSN_IMMEDIATE_BIT) 400 (*info->fprintf_func) (info->stream, "%s,%s,%d", 401 get_reg_name (minfop, x), 402 ROUND_MODE (y), z); 403 else 404 (*info->fprintf_func) (info->stream, "%s,%s,%s", 405 get_reg_name (minfop, x), 406 ROUND_MODE (y), 407 get_reg_name (minfop, z)); 408 } 409 else 410 { 411 if (insn & INSN_IMMEDIATE_BIT) 412 (*info->fprintf_func) (info->stream, "%s,%d", 413 get_reg_name (minfop, x), z); 414 else 415 (*info->fprintf_func) (info->stream, "%s,%s", 416 get_reg_name (minfop, x), 417 get_reg_name (minfop, z)); 418 } 419 break; 420 421 case mmix_operands_pop: 422 /* Like POP - "X,YZ". */ 423 (*info->fprintf_func) (info->stream, "%d,%d", x, y*256 + z); 424 break; 425 426 case mmix_operands_roundregs: 427 /* Two registers, possibly with rounding: "$X,$Z" or 428 "$X,ROUND_MODE,$Z". */ 429 if (y != 0) 430 (*info->fprintf_func) (info->stream, "%s,%s,%s", 431 get_reg_name (minfop, x), 432 ROUND_MODE (y), 433 get_reg_name (minfop, z)); 434 else 435 (*info->fprintf_func) (info->stream, "%s,%s", 436 get_reg_name (minfop, x), 437 get_reg_name (minfop, z)); 438 break; 439 440 case mmix_operands_sync: 441 /* Like SYNC - "XYZ". */ 442 (*info->fprintf_func) (info->stream, "%u", 443 x * 65536 + y * 256 + z); 444 break; 445 446 case mmix_operands_x_regs_z: 447 /* Like SYNCD - "X,$Y,$Z|Z". */ 448 if (insn & INSN_IMMEDIATE_BIT) 449 (*info->fprintf_func) (info->stream, "%d,%s,%d", 450 x, get_reg_name (minfop, y), z); 451 else 452 (*info->fprintf_func) (info->stream, "%d,%s,%s", 453 x, get_reg_name (minfop, y), 454 get_reg_name (minfop, z)); 455 break; 456 457 case mmix_operands_neg: 458 /* Like NEG and NEGU - "$X,Y,$Z|Z". */ 459 if (insn & INSN_IMMEDIATE_BIT) 460 (*info->fprintf_func) (info->stream, "%s,%d,%d", 461 get_reg_name (minfop, x), y, z); 462 else 463 (*info->fprintf_func) (info->stream, "%s,%d,%s", 464 get_reg_name (minfop, x), y, 465 get_reg_name (minfop, z)); 466 break; 467 468 case mmix_operands_pushj: 469 case mmix_operands_regaddr: 470 /* Like GETA or branches - "$X,Address". */ 471 { 472 bfd_signed_vma offset = (y * 256 + z) * 4; 473 474 if (insn & INSN_BACKWARD_OFFSET_BIT) 475 offset -= 65536 * 4; 476 477 info->target = memaddr + offset; 478 479 (*info->fprintf_func) (info->stream, "%s,", get_reg_name (minfop, x)); 480 (*info->print_address_func) (memaddr + offset, info); 481 } 482 break; 483 484 case mmix_operands_get: 485 /* GET - "X,spec_reg". */ 486 (*info->fprintf_func) (info->stream, "%s,%s", 487 get_reg_name (minfop, x), 488 get_spec_reg_name (minfop, z)); 489 break; 490 491 case mmix_operands_put: 492 /* PUT - "spec_reg,$Z|Z". */ 493 if (insn & INSN_IMMEDIATE_BIT) 494 (*info->fprintf_func) (info->stream, "%s,%d", 495 get_spec_reg_name (minfop, x), z); 496 else 497 (*info->fprintf_func) (info->stream, "%s,%s", 498 get_spec_reg_name (minfop, x), 499 get_reg_name (minfop, z)); 500 break; 501 502 case mmix_operands_set: 503 /* Two registers, "$X,$Y". */ 504 (*info->fprintf_func) (info->stream, "%s,%s", 505 get_reg_name (minfop, x), 506 get_reg_name (minfop, y)); 507 break; 508 509 case mmix_operands_save: 510 /* SAVE - "$X,0". */ 511 (*info->fprintf_func) (info->stream, "%s,0", minfop->reg_name[x]); 512 break; 513 514 case mmix_operands_unsave: 515 /* UNSAVE - "0,$Z". */ 516 (*info->fprintf_func) (info->stream, "0,%s", minfop->reg_name[z]); 517 break; 518 519 case mmix_operands_xyz_opt: 520 /* Like SWYM or TRAP - "X,Y,Z". */ 521 (*info->fprintf_func) (info->stream, "%d,%d,%d", x, y, z); 522 break; 523 524 case mmix_operands_resume: 525 /* Just "Z", like RESUME. */ 526 (*info->fprintf_func) (info->stream, "%d", z); 527 break; 528 529 default: 530 (*info->fprintf_func) (info->stream, _("*unknown operands type: %d*"), 531 opcodep->operands); 532 break; 533 } 534 535 return 4; 536 } 537