Annotation of src/external/gpl3/binutils.old/dist/opcodes/microblaze-dis.c, Revision 1.1
1.1 ! christos 1: /* Disassemble Xilinx microblaze instructions.
! 2:
! 3: Copyright 2009, 2012 Free Software Foundation, Inc.
! 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
! 19: Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
! 20: MA 02110-1301, USA. */
! 21:
! 22:
! 23: #include "sysdep.h"
! 24: #define STATIC_TABLE
! 25: #define DEFINE_TABLE
! 26:
! 27: #include "dis-asm.h"
! 28: #include <strings.h>
! 29: #include "microblaze-opc.h"
! 30: #include "microblaze-dis.h"
! 31:
! 32: #define get_field_rd(instr) get_field (instr, RD_MASK, RD_LOW)
! 33: #define get_field_r1(instr) get_field (instr, RA_MASK, RA_LOW)
! 34: #define get_field_r2(instr) get_field (instr, RB_MASK, RB_LOW)
! 35: #define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW)
! 36: #define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW)
! 37:
! 38:
! 39:
! 40: static char *
! 41: get_field (long instr, long mask, unsigned short low)
! 42: {
! 43: char tmpstr[25];
! 44:
! 45: sprintf (tmpstr, "%s%d", register_prefix, (int)((instr & mask) >> low));
! 46: return (strdup (tmpstr));
! 47: }
! 48:
! 49: static char *
! 50: get_field_imm (long instr)
! 51: {
! 52: char tmpstr[25];
! 53:
! 54: sprintf (tmpstr, "%d", (short)((instr & IMM_MASK) >> IMM_LOW));
! 55: return (strdup (tmpstr));
! 56: }
! 57:
! 58: static char *
! 59: get_field_imm5 (long instr)
! 60: {
! 61: char tmpstr[25];
! 62:
! 63: sprintf (tmpstr, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW));
! 64: return (strdup (tmpstr));
! 65: }
! 66:
! 67: static char *
! 68: get_field_rfsl (long instr)
! 69: {
! 70: char tmpstr[25];
! 71:
! 72: sprintf (tmpstr, "%s%d", fsl_register_prefix,
! 73: (short)((instr & RFSL_MASK) >> IMM_LOW));
! 74: return (strdup (tmpstr));
! 75: }
! 76:
! 77: static char *
! 78: get_field_imm15 (long instr)
! 79: {
! 80: char tmpstr[25];
! 81:
! 82: sprintf (tmpstr, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW));
! 83: return (strdup (tmpstr));
! 84: }
! 85:
! 86: static char *
! 87: get_field_special (long instr, struct op_code_struct * op)
! 88: {
! 89: char tmpstr[25];
! 90: char spr[6];
! 91:
! 92: switch ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask))
! 93: {
! 94: case REG_MSR_MASK :
! 95: strcpy (spr, "msr");
! 96: break;
! 97: case REG_PC_MASK :
! 98: strcpy (spr, "pc");
! 99: break;
! 100: case REG_EAR_MASK :
! 101: strcpy (spr, "ear");
! 102: break;
! 103: case REG_ESR_MASK :
! 104: strcpy (spr, "esr");
! 105: break;
! 106: case REG_FSR_MASK :
! 107: strcpy (spr, "fsr");
! 108: break;
! 109: case REG_BTR_MASK :
! 110: strcpy (spr, "btr");
! 111: break;
! 112: case REG_EDR_MASK :
! 113: strcpy (spr, "edr");
! 114: break;
! 115: case REG_PID_MASK :
! 116: strcpy (spr, "pid");
! 117: break;
! 118: case REG_ZPR_MASK :
! 119: strcpy (spr, "zpr");
! 120: break;
! 121: case REG_TLBX_MASK :
! 122: strcpy (spr, "tlbx");
! 123: break;
! 124: case REG_TLBLO_MASK :
! 125: strcpy (spr, "tlblo");
! 126: break;
! 127: case REG_TLBHI_MASK :
! 128: strcpy (spr, "tlbhi");
! 129: break;
! 130: case REG_TLBSX_MASK :
! 131: strcpy (spr, "tlbsx");
! 132: break;
! 133: default :
! 134: if (((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000)
! 135: == REG_PVR_MASK)
! 136: {
! 137: sprintf (tmpstr, "%spvr%d", register_prefix,
! 138: (unsigned short)(((instr & IMM_MASK) >> IMM_LOW)
! 139: ^ op->immval_mask) ^ REG_PVR_MASK);
! 140: return (strdup (tmpstr));
! 141: }
! 142: else
! 143: strcpy (spr, "pc");
! 144: break;
! 145: }
! 146:
! 147: sprintf (tmpstr, "%s%s", register_prefix, spr);
! 148: return (strdup (tmpstr));
! 149: }
! 150:
! 151: static unsigned long
! 152: read_insn_microblaze (bfd_vma memaddr,
! 153: struct disassemble_info *info,
! 154: struct op_code_struct **opr)
! 155: {
! 156: unsigned char ibytes[4];
! 157: int status;
! 158: struct op_code_struct * op;
! 159: unsigned long inst;
! 160:
! 161: status = info->read_memory_func (memaddr, ibytes, 4, info);
! 162:
! 163: if (status != 0)
! 164: {
! 165: info->memory_error_func (status, memaddr, info);
! 166: return 0;
! 167: }
! 168:
! 169: if (info->endian == BFD_ENDIAN_BIG)
! 170: inst = (ibytes[0] << 24) | (ibytes[1] << 16) | (ibytes[2] << 8) | ibytes[3];
! 171: else if (info->endian == BFD_ENDIAN_LITTLE)
! 172: inst = (ibytes[3] << 24) | (ibytes[2] << 16) | (ibytes[1] << 8) | ibytes[0];
! 173: else
! 174: abort ();
! 175:
! 176: /* Just a linear search of the table. */
! 177: for (op = opcodes; op->name != 0; op ++)
! 178: if (op->bit_sequence == (inst & op->opcode_mask))
! 179: break;
! 180:
! 181: *opr = op;
! 182: return inst;
! 183: }
! 184:
! 185:
! 186: int
! 187: print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info)
! 188: {
! 189: fprintf_ftype print_func = info->fprintf_func;
! 190: void * stream = info->stream;
! 191: unsigned long inst, prev_inst;
! 192: struct op_code_struct * op, *pop;
! 193: int immval = 0;
! 194: bfd_boolean immfound = FALSE;
! 195: static bfd_vma prev_insn_addr = -1; /* Init the prev insn addr. */
! 196: static int prev_insn_vma = -1; /* Init the prev insn vma. */
! 197: int curr_insn_vma = info->buffer_vma;
! 198:
! 199: info->bytes_per_chunk = 4;
! 200:
! 201: inst = read_insn_microblaze (memaddr, info, &op);
! 202: if (inst == 0)
! 203: return -1;
! 204:
! 205: if (prev_insn_vma == curr_insn_vma)
! 206: {
! 207: if (memaddr-(info->bytes_per_chunk) == prev_insn_addr)
! 208: {
! 209: prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop);
! 210: if (prev_inst == 0)
! 211: return -1;
! 212: if (pop->instr == imm)
! 213: {
! 214: immval = (get_int_field_imm (prev_inst) << 16) & 0xffff0000;
! 215: immfound = TRUE;
! 216: }
! 217: else
! 218: {
! 219: immval = 0;
! 220: immfound = FALSE;
! 221: }
! 222: }
! 223: }
! 224:
! 225: /* Make curr insn as prev insn. */
! 226: prev_insn_addr = memaddr;
! 227: prev_insn_vma = curr_insn_vma;
! 228:
! 229: if (op->name == NULL)
! 230: print_func (stream, ".short 0x%04x", (unsigned int) inst);
! 231: else
! 232: {
! 233: print_func (stream, "%s", op->name);
! 234:
! 235: switch (op->inst_type)
! 236: {
! 237: case INST_TYPE_RD_R1_R2:
! 238: print_func (stream, "\t%s, %s, %s", get_field_rd (inst),
! 239: get_field_r1(inst), get_field_r2 (inst));
! 240: break;
! 241: case INST_TYPE_RD_R1_IMM:
! 242: print_func (stream, "\t%s, %s, %s", get_field_rd (inst),
! 243: get_field_r1(inst), get_field_imm (inst));
! 244: if (info->print_address_func && get_int_field_r1 (inst) == 0
! 245: && info->symbol_at_address_func)
! 246: {
! 247: if (immfound)
! 248: immval |= (get_int_field_imm (inst) & 0x0000ffff);
! 249: else
! 250: {
! 251: immval = get_int_field_imm (inst);
! 252: if (immval & 0x8000)
! 253: immval |= 0xFFFF0000;
! 254: }
! 255: if (immval > 0 && info->symbol_at_address_func (immval, info))
! 256: {
! 257: print_func (stream, "\t// ");
! 258: info->print_address_func (immval, info);
! 259: }
! 260: }
! 261: break;
! 262: case INST_TYPE_RD_R1_IMM5:
! 263: print_func (stream, "\t%s, %s, %s", get_field_rd (inst),
! 264: get_field_r1(inst), get_field_imm5 (inst));
! 265: break;
! 266: case INST_TYPE_RD_RFSL:
! 267: print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_rfsl (inst));
! 268: break;
! 269: case INST_TYPE_R1_RFSL:
! 270: print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_rfsl (inst));
! 271: break;
! 272: case INST_TYPE_RD_SPECIAL:
! 273: print_func (stream, "\t%s, %s", get_field_rd (inst),
! 274: get_field_special (inst, op));
! 275: break;
! 276: case INST_TYPE_SPECIAL_R1:
! 277: print_func (stream, "\t%s, %s", get_field_special (inst, op),
! 278: get_field_r1(inst));
! 279: break;
! 280: case INST_TYPE_RD_R1:
! 281: print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_r1 (inst));
! 282: break;
! 283: case INST_TYPE_R1_R2:
! 284: print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_r2 (inst));
! 285: break;
! 286: case INST_TYPE_R1_IMM:
! 287: print_func (stream, "\t%s, %s", get_field_r1 (inst), get_field_imm (inst));
! 288: /* The non-pc relative instructions are returns, which shouldn't
! 289: have a label printed. */
! 290: if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET
! 291: && info->symbol_at_address_func)
! 292: {
! 293: if (immfound)
! 294: immval |= (get_int_field_imm (inst) & 0x0000ffff);
! 295: else
! 296: {
! 297: immval = get_int_field_imm (inst);
! 298: if (immval & 0x8000)
! 299: immval |= 0xFFFF0000;
! 300: }
! 301: immval += memaddr;
! 302: if (immval > 0 && info->symbol_at_address_func (immval, info))
! 303: {
! 304: print_func (stream, "\t// ");
! 305: info->print_address_func (immval, info);
! 306: }
! 307: else
! 308: {
! 309: print_func (stream, "\t\t// ");
! 310: print_func (stream, "%x", immval);
! 311: }
! 312: }
! 313: break;
! 314: case INST_TYPE_RD_IMM:
! 315: print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_imm (inst));
! 316: if (info->print_address_func && info->symbol_at_address_func)
! 317: {
! 318: if (immfound)
! 319: immval |= (get_int_field_imm (inst) & 0x0000ffff);
! 320: else
! 321: {
! 322: immval = get_int_field_imm (inst);
! 323: if (immval & 0x8000)
! 324: immval |= 0xFFFF0000;
! 325: }
! 326: if (op->inst_offset_type == INST_PC_OFFSET)
! 327: immval += (int) memaddr;
! 328: if (info->symbol_at_address_func (immval, info))
! 329: {
! 330: print_func (stream, "\t// ");
! 331: info->print_address_func (immval, info);
! 332: }
! 333: }
! 334: break;
! 335: case INST_TYPE_IMM:
! 336: print_func (stream, "\t%s", get_field_imm (inst));
! 337: if (info->print_address_func && info->symbol_at_address_func
! 338: && op->instr != imm)
! 339: {
! 340: if (immfound)
! 341: immval |= (get_int_field_imm (inst) & 0x0000ffff);
! 342: else
! 343: {
! 344: immval = get_int_field_imm (inst);
! 345: if (immval & 0x8000)
! 346: immval |= 0xFFFF0000;
! 347: }
! 348: if (op->inst_offset_type == INST_PC_OFFSET)
! 349: immval += (int) memaddr;
! 350: if (immval > 0 && info->symbol_at_address_func (immval, info))
! 351: {
! 352: print_func (stream, "\t// ");
! 353: info->print_address_func (immval, info);
! 354: }
! 355: else if (op->inst_offset_type == INST_PC_OFFSET)
! 356: {
! 357: print_func (stream, "\t\t// ");
! 358: print_func (stream, "%x", immval);
! 359: }
! 360: }
! 361: break;
! 362: case INST_TYPE_RD_R2:
! 363: print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_r2 (inst));
! 364: break;
! 365: case INST_TYPE_R2:
! 366: print_func (stream, "\t%s", get_field_r2 (inst));
! 367: break;
! 368: case INST_TYPE_R1:
! 369: print_func (stream, "\t%s", get_field_r1 (inst));
! 370: break;
! 371: case INST_TYPE_RD_R1_SPECIAL:
! 372: print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_r2 (inst));
! 373: break;
! 374: case INST_TYPE_RD_IMM15:
! 375: print_func (stream, "\t%s, %s", get_field_rd (inst), get_field_imm15 (inst));
! 376: break;
! 377: /* For tuqula instruction */
! 378: case INST_TYPE_RD:
! 379: print_func (stream, "\t%s", get_field_rd (inst));
! 380: break;
! 381: case INST_TYPE_RFSL:
! 382: print_func (stream, "\t%s", get_field_rfsl (inst));
! 383: break;
! 384: default:
! 385: /* If the disassembler lags the instruction set. */
! 386: print_func (stream, "\tundecoded operands, inst is 0x%04x", (unsigned int) inst);
! 387: break;
! 388: }
! 389: }
! 390:
! 391: /* Say how many bytes we consumed. */
! 392: return 4;
! 393: }
! 394:
! 395: enum microblaze_instr
! 396: get_insn_microblaze (long inst,
! 397: bfd_boolean *isunsignedimm,
! 398: enum microblaze_instr_type *insn_type,
! 399: short *delay_slots)
! 400: {
! 401: struct op_code_struct * op;
! 402: *isunsignedimm = FALSE;
! 403:
! 404: /* Just a linear search of the table. */
! 405: for (op = opcodes; op->name != 0; op ++)
! 406: if (op->bit_sequence == (inst & op->opcode_mask))
! 407: break;
! 408:
! 409: if (op->name == 0)
! 410: return invalid_inst;
! 411: else
! 412: {
! 413: *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM);
! 414: *insn_type = op->instr_type;
! 415: *delay_slots = op->delay_slots;
! 416: return op->instr;
! 417: }
! 418: }
! 419:
! 420: enum microblaze_instr
! 421: microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *immed)
! 422: {
! 423: enum microblaze_instr op;
! 424: bfd_boolean t1;
! 425: enum microblaze_instr_type t2;
! 426: short t3;
! 427:
! 428: op = get_insn_microblaze (insn, &t1, &t2, &t3);
! 429: *rd = (insn & RD_MASK) >> RD_LOW;
! 430: *ra = (insn & RA_MASK) >> RA_LOW;
! 431: *rb = (insn & RB_MASK) >> RB_LOW;
! 432: t3 = (insn & IMM_MASK) >> IMM_LOW;
! 433: *immed = (int) t3;
! 434: return (op);
! 435: }
! 436:
! 437: unsigned long
! 438: microblaze_get_target_address (long inst, bfd_boolean immfound, int immval,
! 439: long pcval, long r1val, long r2val,
! 440: bfd_boolean *targetvalid,
! 441: bfd_boolean *unconditionalbranch)
! 442: {
! 443: struct op_code_struct * op;
! 444: long targetaddr = 0;
! 445:
! 446: *unconditionalbranch = FALSE;
! 447: /* Just a linear search of the table. */
! 448: for (op = opcodes; op->name != 0; op ++)
! 449: if (op->bit_sequence == (inst & op->opcode_mask))
! 450: break;
! 451:
! 452: if (op->name == 0)
! 453: {
! 454: *targetvalid = FALSE;
! 455: }
! 456: else if (op->instr_type == branch_inst)
! 457: {
! 458: switch (op->inst_type)
! 459: {
! 460: case INST_TYPE_R2:
! 461: *unconditionalbranch = TRUE;
! 462: /* Fall through. */
! 463: case INST_TYPE_RD_R2:
! 464: case INST_TYPE_R1_R2:
! 465: targetaddr = r2val;
! 466: *targetvalid = TRUE;
! 467: if (op->inst_offset_type == INST_PC_OFFSET)
! 468: targetaddr += pcval;
! 469: break;
! 470: case INST_TYPE_IMM:
! 471: *unconditionalbranch = TRUE;
! 472: /* Fall through. */
! 473: case INST_TYPE_RD_IMM:
! 474: case INST_TYPE_R1_IMM:
! 475: if (immfound)
! 476: {
! 477: targetaddr = (immval << 16) & 0xffff0000;
! 478: targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
! 479: }
! 480: else
! 481: {
! 482: targetaddr = get_int_field_imm (inst);
! 483: if (targetaddr & 0x8000)
! 484: targetaddr |= 0xFFFF0000;
! 485: }
! 486: if (op->inst_offset_type == INST_PC_OFFSET)
! 487: targetaddr += pcval;
! 488: *targetvalid = TRUE;
! 489: break;
! 490: default:
! 491: *targetvalid = FALSE;
! 492: break;
! 493: }
! 494: }
! 495: else if (op->instr_type == return_inst)
! 496: {
! 497: if (immfound)
! 498: {
! 499: targetaddr = (immval << 16) & 0xffff0000;
! 500: targetaddr |= (get_int_field_imm (inst) & 0x0000ffff);
! 501: }
! 502: else
! 503: {
! 504: targetaddr = get_int_field_imm (inst);
! 505: if (targetaddr & 0x8000)
! 506: targetaddr |= 0xFFFF0000;
! 507: }
! 508: targetaddr += r1val;
! 509: *targetvalid = TRUE;
! 510: }
! 511: else
! 512: *targetvalid = FALSE;
! 513: return targetaddr;
! 514: }
CVSweb <webmaster@jp.NetBSD.org>