binutils-gdb/gdb/rs6000-pinsn.c
Per Bothner d453b386ac * rs6000-pinsn.c: New version from IBM (Metin).
* m2-exp.y:  Re-write string initializers ("<>" => {'<', '>'})
	to avoid warnings from some compilers.
1992-04-17 01:35:42 +00:00

381 lines
7.9 KiB
C

/* Print IBM RS/6000 instructions for GNU software.
Copyright 1991 Free Software Foundation, Inc.
Contributed by IBM Corporation.
This file is part of GDB and the GNU binutils.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "defs.h"
#include "opcode/rs6k.h"
/* Print the rs6k instruction at address MEMADDR in debugged memory,
on STREAM. Returns length of the instruction, in bytes. */
int
print_insn (memaddr, stream)
CORE_ADDR memaddr;
FILE *stream;
{
int pop, eop, probable_eop; /* primary and extended opcodes */
int min, max;
int best = -1; /* found best opcode index */
int oldbest = -1;
unsigned int the_insn;
read_memory (memaddr, &the_insn, sizeof (the_insn));
pop = (unsigned)(the_insn >> 26);
min = 0, max = NOPCODES-1;
while (min < max) {
best = (min + max) / 2;
/* see if we are running in loops */
if (best == oldbest)
goto not_found;
oldbest = best;
if (pop < rs6k_ops [best].p_opcode)
max = best;
else if (pop > rs6k_ops [best].p_opcode)
min = best;
else {
/* Opcode matched, check extended opcode. */
if (rs6k_ops [best].e_opcode == -1) {
/* there is no valid extended opcode, what we've got is
just fine. */
goto insn_found;
}
/* Largest possible value of extended opcode. */
probable_eop = ((the_insn) >> 1) & 0x3ff;
eop = probable_eop & eopMask [rs6k_ops [best].format];
if (eop < rs6k_ops [best].e_opcode) {
while (pop == rs6k_ops [best].p_opcode) {
if (eop == rs6k_ops [best].e_opcode) /* found it! */
goto insn_found;
--best;
eop = probable_eop & eopMask [rs6k_ops [best].format];
}
goto not_found;
}
else if (eop > rs6k_ops [best].e_opcode) {
while (pop == rs6k_ops [best].p_opcode) {
if (eop == rs6k_ops [best].e_opcode) /* found it! */
goto insn_found;
++best;
eop = probable_eop & eopMask [rs6k_ops [best].format];
}
goto not_found;
}
else /* eop == rs6k_ops [best].e_opcode */
goto insn_found;
}
}
best = min;
if (pop == rs6k_ops [best].p_opcode &&
(rs6k_ops [best].e_opcode == -1 || rs6k_ops [best].e_opcode == eop))
goto insn_found;
else
goto not_found;
insn_found:
print_operator (stream, memaddr, the_insn, best);
return 4;
not_found:
fprintf (stream, "0x%08x", the_insn);
return 4;
}
/* condition code names */
static char *cond_code [] = {
"lt", "gt", "eq", "so", "ge", "le", "ne", "ns", "nl", "ng", "z", "nz" };
print_operator (stream, memaddr, insn_word, insn_no)
FILE *stream;
long memaddr;
long insn_word;
int insn_no;
{
char buf [20];
char *qq = buf;
char *pp = rs6k_ops[insn_no].opr_ext;
int tmp;
int nocomma = 0; /* true if no comma needed */
if (pp) {
while (*pp) {
switch ( *pp ) {
case '.':
if (insn_word & 0x1)
*qq++ = '.';
break;
case 'l':
if (insn_word & 0x1)
*qq++ = 'l';
break;
case 't':
if ((insn_word & 0x03e00000) == 0x01800000)
*qq++ = 't';
break;
case 'f':
if ((insn_word & 0x03e00000) == 0x00800000)
*qq++ = 'f';
break;
case 'a':
if (insn_word & 0x2)
*qq++ = 'a';
break;
case 'o':
if (insn_word & 0x4000)
*qq++ = 'o';
break;
case '1': /* exception #1 for bb/bc ambiguity */
tmp = (insn_word >> 21) & 0x1f; /* extract BO */
if (tmp != 0xc && tmp != 0x4) {
/* you can't use `bb' now. switch to `bc' */
*(qq-1) = 'c';
++insn_no;
pp = rs6k_ops[insn_no].opr_ext;
continue;
}
break;
default:
abort ();
}
++pp;
}
}
*qq = '\0';
fprintf (stream, "%s%s\t", rs6k_ops[insn_no].operator, buf);
/* parse the operand now. */
pp = rs6k_ops[insn_no].oprnd_format;
while (1) {
switch (*pp) {
case TO :
fprintf (stream, "%d", (insn_word >> 21) & 0x1f);
break;
case RT :
case RS :
fprintf (stream, "r%d", (insn_word >> 21) & 0x1f);
break;
case LI :
tmp = (insn_word >> 16) & 0x1f;
if (tmp > 11) {
fprintf (stream, "{unknown cond code: 0x%x}", insn_word);
tmp = 0;
}
fprintf (stream, "%s", cond_code [tmp]);
break;
case A2 :
case TA14 :
tmp = (insn_word & 0xfffc);
if (tmp & 0x8000) /* fix sign extension */
tmp -= 0x10000;
if ((insn_word & 0x2) == 0) /* if AA not set */
tmp += memaddr;
print_address (tmp, stream);
break;
case TA24 :
tmp = insn_word & 0x03fffffc;
if (tmp & 0x2000000)
tmp -= 0x4000000;
if ((insn_word & 0x2) == 0) /* if no AA bit set */
tmp += memaddr;
print_address (tmp, stream);
break;
case LEV : /* for svc only */
if (insn_word & 0x2) { /* SA is set */
nocomma = 1;
}
else
fprintf (stream, "%d", (insn_word >> 5) & 0x7f);
break;
case FL1 : /* for svc only */
if (insn_word & 0x2) { /* SA is set */
nocomma = 1;
}
else
fprintf (stream, "%d", (insn_word >> 12) & 0xf);
break;
case FL2 : /* for svc only */
nocomma = 0;
if (insn_word & 0x2) /* SA is set */
fprintf (stream, "%d", (insn_word >> 2) & 0x3fff);
else
fprintf (stream, "%d", (insn_word >> 2) & 0x7);
break;
case RA :
if (nocomma) {
fprintf (stream, "r%d)", (insn_word >> 16) & 0x1f);
nocomma = 0;
}
else
fprintf (stream, "r%d", (insn_word >> 16) & 0x1f);
break;
case RB :
fprintf (stream, "r%d", (insn_word >> 11) & 0x1f);
break;
case SI :
tmp = insn_word & 0xffff;
if (tmp & 0x8000)
tmp -= 0x10000;
fprintf (stream, "%d", tmp);
break;
case UI :
fprintf (stream, "%d", insn_word & 0xffff);
break;
case BF :
fprintf (stream, "%d", (insn_word >> 23) & 0x7);
break;
case BFA :
fprintf (stream, "%d", (insn_word >> 18) & 0x7);
break;
case BT :
fprintf (stream, "%d", (insn_word >> 21) & 0x1f);
break;
case BA :
fprintf (stream, "%d", (insn_word >> 16) & 0x1f);
break;
case BB :
fprintf (stream, "%d", (insn_word >> 11) & 0x1f);
break;
case BO :
fprintf (stream, "%d", (insn_word >> 21) & 0x1f);
break;
case BI :
fprintf (stream, "%d", (insn_word >> 16) & 0x1f);
break;
case SH :
fprintf (stream, "%d", (insn_word >> 11) & 0x1f);
break;
case MB :
fprintf (stream, "0x%x", (insn_word >> 6) & 0x1f);
break;
case ME :
fprintf (stream, "0x%x", (insn_word >> 1) & 0x1f);
break;
case SPR :
fprintf (stream, "%d", (insn_word >> 16) & 0x1f);
break;
case DIS :
nocomma = 1;
tmp = insn_word & 0xffff;
if (tmp & 0x8000)
tmp -= 0x10000;
fprintf (stream, "%d(", tmp);
break;
case FXM :
fprintf (stream, "0x%x", (insn_word >> 12) & 0xff);
break;
case FRT :
case FRS :
fprintf (stream, "f%d", (insn_word >> 21) & 0x1f);
break;
case FRA :
fprintf (stream, "f%d", (insn_word >> 16) & 0x1f);
break;
case FRB :
fprintf (stream, "f%d", (insn_word >> 11) & 0x1f);
break;
case FRC :
fprintf (stream, "f%d", (insn_word >> 6) & 0x1f);
break;
case FLM :
fprintf (stream, "0x%x", (insn_word >> 17) & 0xff);
break;
case NB :
fprintf (stream, "%d", (insn_word >> 11) & 0x1f);
break;
case I :
fprintf (stream, "%d", (insn_word >> 12) & 0xf);
break;
default :
fprintf (stream,
"{Internal error: Unknown operand format identifier %d}",
*pp);
}
++pp;
if (*pp == '\0')
break;
else if (!nocomma)
fputc(',', stream);
}
}