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