this is part of the binutils/gdb sparc disassembler merge

This commit is contained in:
Jim Kingdon 1993-04-01 16:58:41 +00:00
parent f7ed13c7d5
commit 3ac166b105

View file

@ -1,29 +1,23 @@
/* disassemble sparc instructions for objdump /* Print SPARC instructions.
Copyright (C) 1986, 1987, 1989, 1991 Free Software Foundation, Inc. Copyright 1989, 1991, 1992, 1993 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
This file is part of the binutils.
The binutils are free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option) the Free Software Foundation; either version 2 of the License, or
any later version. (at your option) any later version.
The binutils are distributed in the hope that they will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with the binutils; see the file COPYING. If not, write to along with this program; if not, write to the Free Software
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "bfd.h"
#include "sysdep.h"
#include <stdio.h>
#include "opcode/sparc.h" #include "opcode/sparc.h"
#include "objdump.h" #include "dis-asm.h"
extern int print_address(); #include <string.h>
static char *reg_names[] = static char *reg_names[] =
{ "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7", { "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
@ -38,34 +32,37 @@ static char *reg_names[] =
#define freg_names (&reg_names[4 * 8]) #define freg_names (&reg_names[4 * 8])
/* FIXME--need to deal with byte order (probably using masking and
shifting rather than bitfields is easiest). */
union sparc_insn union sparc_insn
{ {
unsigned long int code; unsigned long int code;
struct struct
{ {
unsigned int _OP:2; unsigned int anop:2;
#define op ldst._OP #define op ldst.anop
unsigned int _RD:5; unsigned int anrd:5;
#define rd ldst._RD #define rd ldst.anrd
unsigned int op3:6; unsigned int op3:6;
unsigned int _RS1:5; unsigned int anrs1:5;
#define rs1 ldst._RS1 #define rs1 ldst.anrs1
unsigned int i:1; unsigned int i:1;
unsigned int _ASI:8; unsigned int anasi:8;
#define asi ldst._ASI #define asi ldst.anasi
unsigned int _RS2:5; unsigned int anrs2:5;
#define rs2 ldst._RS2 #define rs2 ldst.anrs2
#define shcnt rs2 #define shcnt rs2
} ldst; } ldst;
struct struct
{ {
unsigned int _OP:2, _RD:5, op3:6, _RS1:5, i:1; unsigned int anop:2, anrd:5, op3:6, anrs1:5, i:1;
unsigned int IMM13:13; unsigned int IMM13:13;
#define imm13 IMM13.IMM13 #define imm13 IMM13.IMM13
} IMM13; } IMM13;
struct struct
{ {
unsigned int _OP:2; unsigned int anop:2;
unsigned int a:1; unsigned int a:1;
unsigned int cond:4; unsigned int cond:4;
unsigned int op2:3; unsigned int op2:3;
@ -94,9 +91,9 @@ union sparc_insn
#define imm22 disp22 #define imm22 disp22
struct struct
{ {
unsigned int _OP:2; unsigned int anop:2;
unsigned int _DISP30:30; unsigned int adisp30:30;
#define disp30 call._DISP30 #define disp30 call.adisp30
} call; } call;
}; };
@ -111,23 +108,28 @@ is_delayed_branch (insn)
{ {
const struct sparc_opcode *opcode = &sparc_opcodes[i]; const struct sparc_opcode *opcode = &sparc_opcodes[i];
if ((opcode->match & insn.code) == opcode->match if ((opcode->match & insn.code) == opcode->match
&& (opcode->lose & insn.code) == 0 && (opcode->lose & insn.code) == 0)
&& (opcode->flags&F_DELAYED)) return (opcode->flags & F_DELAYED);
return 1;
} }
return 0; return 0;
} }
static int opcodes_sorted = 0; static int opcodes_sorted = 0;
extern void qsort ();
/* Print one instruction from MEMADDR on STREAM. */ /* Print one instruction from MEMADDR on STREAM.
We suffix the instruction with a comment that gives the absolute
address involved, as well as its symbolic form, if the instruction
is preceded by a findable `sethi' and it either adds an immediate
displacement to that register, or it is an `add' or `or' instruction
on that register. */
int int
print_insn_sparc (memaddr, buffer, stream) print_insn_sparc (memaddr, info)
bfd_vma memaddr; bfd_vma memaddr;
bfd_byte *buffer; disassemble_info *info;
FILE *stream;
{ {
FILE *stream = info->stream;
union sparc_insn insn; union sparc_insn insn;
register unsigned int i; register unsigned int i;
@ -140,7 +142,15 @@ print_insn_sparc (memaddr, buffer, stream)
opcodes_sorted = 1; opcodes_sorted = 1;
} }
memcpy(&insn,buffer, sizeof (insn)); {
int status =
(*info->read_memory_func) (memaddr, (char *) &insn, sizeof (insn), info);
if (status != 0)
{
(*info->memory_error_func) (status, memaddr, info);
return -1;
}
}
for (i = 0; i < NUMOPCODES; ++i) for (i = 0; i < NUMOPCODES; ++i)
{ {
@ -156,48 +166,44 @@ memcpy(&insn,buffer, sizeof (insn));
field of the opcode table. */ field of the opcode table. */
int found_plus = 0; int found_plus = 0;
/* Do we have an 'or' instruction where rs1 is the same /* Do we have an `add' or `or' instruction where rs1 is the same
as rsd, and which has the i bit set? */ as rsd, and which has the i bit set? */
if (opcode->match == 0x80102000 if ((opcode->match == 0x80102000 || opcode->match == 0x80002000)
/* (or) (add) */
&& insn.rs1 == insn.rd) && insn.rs1 == insn.rd)
imm_added_to_rs1 = 1; imm_added_to_rs1 = 1;
if (strchr (opcode->args, 'S') != 0)
/* Reject the special case for `set'.
The real `sethi' will match. */
continue;
if (insn.rs1 != insn.rd if (insn.rs1 != insn.rd
&& strchr (opcode->args, 'r') != 0) && strchr (opcode->args, 'r') != 0)
/* Can't do simple format if source and dest are different. */ /* Can't do simple format if source and dest are different. */
continue; continue;
fputs (opcode->name, stream); (*info->fprintf_func) (stream, opcode->name);
{ {
register const char *s; register const char *s;
if (opcode->args[0] != ',') if (opcode->args[0] != ',')
fputs (" ", stream); (*info->fprintf_func) (stream, " ");
for (s = opcode->args; *s != '\0'; ++s) for (s = opcode->args; *s != '\0'; ++s)
{ {
while (*s == ',') while (*s == ',')
{ {
fputs (",", stream); (*info->fprintf_func) (stream, ",");
++s; ++s;
switch (*s) { switch (*s) {
case 'a': case 'a':
fputs ("a", stream); (*info->fprintf_func) (stream, "a");
++s; ++s;
continue; continue;
#ifndef NO_V9 #ifndef NO_V9
case 'N': case 'N':
fputs("pn", stream); (*info->fprintf_func) (stream, "pn");
++s; ++s;
continue; continue;
case 'T': case 'T':
fputs("pt", stream); (*info->fprintf_func) (stream, "pt");
++s; ++s;
continue; continue;
#endif /* NO_V9 */ #endif /* NO_V9 */
@ -207,7 +213,7 @@ memcpy(&insn,buffer, sizeof (insn));
} /* switch on arg */ } /* switch on arg */
} /* while there are comma started args */ } /* while there are comma started args */
fputs (" ", stream); (*info->fprintf_func) (stream, " ");
switch (*s) switch (*s)
{ {
@ -216,14 +222,14 @@ memcpy(&insn,buffer, sizeof (insn));
/* note fall-through */ /* note fall-through */
default: default:
fprintf (stream, "%c", *s); (*info->fprintf_func) (stream, "%c", *s);
break; break;
case '#': case '#':
fputs ("0", stream); (*info->fprintf_func) (stream, "0");
break; break;
#define reg(n) fprintf (stream, "%%%s", reg_names[n]) #define reg(n) (*info->fprintf_func) (stream, "%%%s", reg_names[n])
case '1': case '1':
case 'r': case 'r':
reg (insn.rs1); reg (insn.rs1);
@ -238,7 +244,7 @@ memcpy(&insn,buffer, sizeof (insn));
break; break;
#undef reg #undef reg
#define freg(n) fprintf (stream, "%%%s", freg_names[n]) #define freg(n) (*info->fprintf_func) (stream, "%%%s", freg_names[n])
case 'e': case 'e':
case 'v': /* double/even */ case 'v': /* double/even */
case 'V': /* quad/multiple of 4 */ case 'V': /* quad/multiple of 4 */
@ -258,7 +264,7 @@ memcpy(&insn,buffer, sizeof (insn));
break; break;
#undef freg #undef freg
#define creg(n) fprintf (stream, "%%c%u", (unsigned int) (n)) #define creg(n) (*info->fprintf_func) (stream, "%%c%u", (unsigned int) (n))
case 'b': case 'b':
creg (insn.rs1); creg (insn.rs1);
break; break;
@ -273,8 +279,8 @@ memcpy(&insn,buffer, sizeof (insn));
#undef creg #undef creg
case 'h': case 'h':
fprintf (stream, "%%hi(%#x)", (*info->fprintf_func) (stream, "%%hi(%#x)",
(unsigned int) insn.imm22 << 10); (int) insn.imm22 << 10);
break; break;
case 'i': case 'i':
@ -285,7 +291,7 @@ memcpy(&insn,buffer, sizeof (insn));
/* Check to see whether we have a 1+i, and take /* Check to see whether we have a 1+i, and take
note of that fact. note of that fact.
Note: because of the way we sort the table, Note: because of the way we sort the table,
we will be matching 1+i rather than i+1, we will be matching 1+i rather than i+1,
so it is OK to assume that i is after +, so it is OK to assume that i is after +,
@ -294,9 +300,9 @@ memcpy(&insn,buffer, sizeof (insn));
imm_added_to_rs1 = 1; imm_added_to_rs1 = 1;
if (imm <= 9) if (imm <= 9)
fprintf (stream, "%d", imm); (*info->fprintf_func) (stream, "%d", imm);
else else
fprintf (stream, "%#x", (unsigned) imm); (*info->fprintf_func) (stream, "%#x", imm);
} }
break; break;
@ -324,9 +330,9 @@ memcpy(&insn,buffer, sizeof (insn));
imm_added_to_rs1 = 1; imm_added_to_rs1 = 1;
if (imm <= 9) if (imm <= 9)
fprintf (stream, "%d", imm); (info->fprintf_func) (stream, "%d", imm);
else else
fprintf (stream, "%#x", (unsigned) imm); (info->fprintf_func) (stream, "%#x", (unsigned) imm);
} }
break; break;
@ -389,8 +395,9 @@ memcpy(&insn,buffer, sizeof (insn));
if ((insn.code >> 22) == 0) if ((insn.code >> 22) == 0)
/* Special case for `unimp'. Don't try to turn /* Special case for `unimp'. Don't try to turn
it's operand into a function offset. */ it's operand into a function offset. */
fprintf (stream, "%#x", (*info->fprintf_func)
(unsigned) (((int) insn.disp22 << 10) >> 10)); (stream, "%#x",
(int) (((int) insn.disp22 << 10) >> 10));
else else
/* We cannot trust the compiler to sign-extend /* We cannot trust the compiler to sign-extend
when extracting the bitfield, hence the shifts. */ when extracting the bitfield, hence the shifts. */
@ -401,39 +408,39 @@ memcpy(&insn,buffer, sizeof (insn));
break; break;
case 'A': case 'A':
fprintf (stream, "(%d)", (int) insn.asi); (*info->fprintf_func) (stream, "(%d)", (int) insn.asi);
break; break;
case 'C': case 'C':
fputs ("%csr", stream); (*info->fprintf_func) (stream, "%csr");
break; break;
case 'F': case 'F':
fputs ("%fsr", stream); (*info->fprintf_func) (stream, "%fsr");
break; break;
case 'p': case 'p':
fputs ("%psr", stream); (*info->fprintf_func) (stream, "%psr");
break; break;
case 'q': case 'q':
fputs ("%fq", stream); (*info->fprintf_func) (stream, "%fq");
break; break;
case 'Q': case 'Q':
fputs ("%cq", stream); (*info->fprintf_func) (stream, "%cq");
break; break;
case 't': case 't':
fputs ("%tbr", stream); (*info->fprintf_func) (stream, "%tbr");
break; break;
case 'w': case 'w':
fputs ("%wim", stream); (*info->fprintf_func) (stream, "%wim");
break; break;
case 'y': case 'y':
fputs ("%y", stream); (*info->fprintf_func) (stream, "%y");
break; break;
} }
} }
@ -448,9 +455,12 @@ memcpy(&insn,buffer, sizeof (insn));
if (imm_added_to_rs1) if (imm_added_to_rs1)
{ {
union sparc_insn prev_insn; union sparc_insn prev_insn;
int errcode = 0; int errcode;
memcpy(&prev_insn, buffer -4, sizeof (prev_insn)); errcode =
(*info->read_memory_func)
(memaddr - 4,
(char *)&prev_insn, sizeof (prev_insn));
if (errcode == 0) if (errcode == 0)
{ {
@ -464,8 +474,8 @@ memcpy(&insn,buffer, sizeof (insn));
*/ */
if (is_delayed_branch (prev_insn)) if (is_delayed_branch (prev_insn))
memcpy(&prev_insn, buffer - 8, sizeof(prev_insn)); errcode = (*info->read_memory_func)
(memaddr - 8, (char *)&prev_insn, sizeof (prev_insn));
} }
/* If there was a problem reading memory, then assume /* If there was a problem reading memory, then assume
@ -476,7 +486,7 @@ memcpy(&insn,buffer, sizeof (insn));
if ((prev_insn.code & 0xc1c00000) == 0x01000000 if ((prev_insn.code & 0xc1c00000) == 0x01000000
&& prev_insn.rd == insn.rs1) && prev_insn.rd == insn.rs1)
{ {
fprintf (stream, "\t! "); (*info->fprintf_func) (stream, "\t! ");
/* We cannot trust the compiler to sign-extend /* We cannot trust the compiler to sign-extend
when extracting the bitfield, hence the shifts. */ when extracting the bitfield, hence the shifts. */
print_address (((int) prev_insn.imm22 << 10) print_address (((int) prev_insn.imm22 << 10)
@ -489,11 +499,10 @@ memcpy(&insn,buffer, sizeof (insn));
} }
} }
fprintf (stream, "%#8x", insn.code); (*info->fprintf_func) (stream, "%#8x", insn.code);
return sizeof (insn); return sizeof (insn);
} }
/* Compare opcodes A and B. */ /* Compare opcodes A and B. */
static int static int
@ -548,6 +557,27 @@ compare_opcodes (a, b)
/* They are functionally equal. So as long as the opcode table is /* They are functionally equal. So as long as the opcode table is
valid, we can put whichever one first we want, on aesthetic grounds. */ valid, we can put whichever one first we want, on aesthetic grounds. */
/* Our first aesthetic ground is that aliases defer to real insns. */
{
int alias_diff = (op0->flags & F_ALIAS) - (op1->flags & F_ALIAS);
if (alias_diff != 0)
/* Put the one that isn't an alias first. */
return alias_diff;
}
/* Except for aliases, two "identical" instructions had
better have the same opcode. This is a sanity check on the table. */
i = strcmp (op0->name, op1->name);
if (i)
if (op0->flags & F_ALIAS) /* If they're both aliases, be arbitrary. */
return i;
else
fprintf (stderr,
"Internal error: bad sparc-opcode.h: \"%s\" == \"%s\"\n",
op0->name, op1->name);
/* Fewer arguments are preferred. */
{ {
int length_diff = strlen (op0->args) - strlen (op1->args); int length_diff = strlen (op0->args) - strlen (op1->args);
if (length_diff != 0) if (length_diff != 0)