ftp.nice.ch/Attic/openStep/developer/bundles/GDBbundle.1.0.s.tgz#/GDBbundle-1.0.s/debug/gdb/gdb/sparc/sparc-disasm.c

This is sparc-disasm.c in view mode; [Download] [Up]

#include <stdio.h>
#include <string.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <mach-o/reloc.h>
#include <mach-o/sparc/reloc.h>
#include "bytesex.h"
#include "otool.h"
#include "defs.h"
#include "symtab.h"
#include "sparc-opcode.h"
#include "dis-asm.h"

#define DEBUG


/* Sign-extend a value which is N bits long.  */
#define	SEX(value, bits) \
	((((int)(value)) << ((8 * sizeof (int)) - bits))	\
			 >> ((8 * sizeof (int)) - bits) )

#define	freg_names	(&reg_names[4 * 8])

union sparc_insn
  {
    unsigned long int code;
    struct
      {
#ifdef __BIG_ENDIAN__
	unsigned int anop:2;
	unsigned int anrd:5;
	unsigned int op3:6;
	unsigned int anrs1:5;
	unsigned int i:1;
	unsigned int anasi:8;
	unsigned int anrs2:5;
#endif
#ifdef __LITTLE_ENDIAN__
	unsigned int anrs2:5;
	unsigned int anasi:8;
	unsigned int i:1;
	unsigned int anrs1:5;
	unsigned int op3:6;
	unsigned int anrd:5;
	unsigned int anop:2;
#endif
      } ldst;
    struct
      {
#ifdef __BIG_ENDIAN__
	unsigned int anop:2; 
        unsigned int anrd:5;
        unsigned int op3:6;
        unsigned int anrs1:5;
        unsigned int i:1;
	unsigned int IMM13:13;
#endif
#ifdef __LITTLE_ENDIAN__
	unsigned int IMM13:13;
        unsigned int i:1;
        unsigned int anrs1:5;
        unsigned int op3:6;
        unsigned int anrd:5;
	unsigned int anop:2; 
#endif
      } IMM13;
    struct
      {
#ifdef __BIG_ENDIAN__
	unsigned int anop:2;
	unsigned int a:1;
	unsigned int cond:4;
	unsigned int op2:3;
	unsigned int DISP22:22;
#endif
#ifdef __LITTLE_ENDIAN__
	unsigned int DISP22:22;
	unsigned int op2:3;
	unsigned int cond:4;
	unsigned int a:1;
	unsigned int anop:2;
#endif
      } branch;
    struct
      {
#ifdef __BIG_ENDIAN__
	unsigned int anop:2;
	unsigned int adisp30:30;
#endif
#ifdef __LITTLE_ENDIAN__
	unsigned int adisp30:30;
	unsigned int anop:2;
#endif
      } call;
  };

#define	op	ldst.anop
#define	rd	ldst.anrd
#define	rs1	ldst.anrs1
#define	asi	ldst.anasi
#define	rs2	ldst.anrs2
#define	shcnt	rs2
#define	imm13	IMM13.IMM13
#define	disp22	branch.DISP22
#define	imm22	disp22
#define	disp30	call.adisp30


static int opcodes_sorted = 0;
//extern void qsort();
static int compare_opcodes ();

      
void sparc_disassemble(buffer, addr, stream)
     char *buffer;
     unsigned long addr;
     FILE *stream;
{
  int i;
  union sparc_insn	insn;
  struct sparc_opcode	*opcode;
  const char *s;
  
  
  if (!opcodes_sorted) {
    qsort ((char *) sparc_opcodes, NUMOPCODES,
	   sizeof (sparc_opcodes[0]), compare_opcodes);
    opcodes_sorted = 1;
  }
  
  memcpy(&insn, buffer, sizeof(unsigned long));
  SWAP_TARGET_AND_HOST(&insn.code,sizeof(insn.code));
  
  /* search through the opcode table */
  for (i=0; i<NUMOPCODES; ++i) {
    opcode = &sparc_opcodes[i];
    if ((opcode->match & insn.code) == opcode->match
	&& (opcode->lose & insn.code) == 0) {
      /* Can't do simple format if source and dest are different.  */
      if (insn.rs1 != insn.rd
	  && strchr (opcode->args, 'r') != 0) {
	continue;
      }
      else {
	break;
      }
    }
  }
  
  if (i >= NUMOPCODES) {
    fprintf_filtered(stream,".long 0x%08x\n", (unsigned int)insn.code);
    return;
  }
  
  fprintf_filtered(stream,"%s", opcode->name);
  
  if (opcode->args[0] != ',')
    fprintf_filtered(stream,"\t ");
  for (s = opcode->args; *s != '\0'; ++s) {
    while (*s == ',') {
      fprintf_filtered(stream,",");
      ++s;
      switch (*s) {
      case 'a':
	fprintf_filtered(stream,"a\t");
	++s;
	continue;
      default:
	break;
      }	/* switch on arg */
    } /* while there are comma started args */
	
    switch (*s) {
    default:
      fprintf_filtered(stream,"%c", *s);
      break;
    case '#':
      fprintf_filtered(stream,"0");
      break;

#define	reg(n)	fprintf_filtered(stream,"%%%s", reg_names[n])
    case '1':
    case 'r':
      reg (insn.rs1);
      break;

    case '2':
      reg (insn.rs2);
      break;

    case 'd':
      reg (insn.rd);
      break;
#undef	reg

#define	freg(n)		fprintf_filtered(stream,"%%%s", freg_names[n])
#define	fregx(n)	fprintf_filtered(stream,"%%%s", freg_names[((n) & ~1) | (((n) & 1) << 5)])
    case 'e':
      freg (insn.rs1);
      break;
    case 'v':			/* double/even */
    case 'V':			/* quad/multiple of 4 */
      fregx (insn.rs1);
      break;

    case 'f':
      freg (insn.rs2);
      break;
    case 'B':			/* double/even */
    case 'R':			/* quad/multiple of 4 */
      fregx (insn.rs2);
      break;

    case 'g':
      freg (insn.rd);
      break;
    case 'H':			/* double/even */
    case 'J':			/* quad/multiple of 4 */
      fregx (insn.rd);
      break;
#undef	freg
#undef	fregx

#define	creg(n)	fprintf_filtered(stream,"%%c%u", (unsigned int) (n))
    case 'b':
      creg (insn.rs1);
      break;

    case 'c':
      creg (insn.rs2);
      break;

    case 'D':
      creg (insn.rd);
      break;
#undef	creg
    case 'h':
      print_address((int) insn.imm22<<10,stream);
      break;
    case 'i':
      {
	/* We cannot trust the compiler to sign-extend
	   when extracting the bitfield, hence the shifts.  */
	int imm = SEX (insn.imm13, 13);
        fprintf_filtered(stream,"%#x", imm);
      }
      break;
    case 'M':
      fprintf_filtered(stream,"%%asr%d", insn.rs1);
      break;
    case 'm':
      fprintf_filtered(stream,"%%asr%d", insn.rd);
      break;
    case 'L':
      print_address(4 * SEX(insn.disp30,30) + addr, stream);
      break;
    case 'n':
      fprintf_filtered(stream,"%#x", (SEX (insn.disp22, 22)));
      break;
    case 'l':
      print_address(4 * SEX(insn.disp22,22) +addr, stream);
      break;
    case 'A':
      fprintf_filtered(stream,"(%d)", (int) insn.asi);
      break;
    case 'C':
      fprintf_filtered(stream,"%%csr");
      break;
    case 'F':
      fprintf_filtered(stream,"%%fsr");
      break;
    case 'p':
      fprintf_filtered(stream,"%%psr");
      break;
    case 'q':
      fprintf_filtered(stream,"%%fq");
      break;
    case 'Q':
      fprintf_filtered(stream,"%%cq");
      break;
    case 't':
      fprintf_filtered(stream,"%%tbr");
      break;
    case 'w':
      fprintf_filtered(stream,"%%wim");
      break;
    case 'y':
      fprintf_filtered(stream,"%%y");
      break;
    }
  }
}

/* Compare opcodes A and B.  */

static int
compare_opcodes (a, b)
     char *a, *b;
{
  struct sparc_opcode *op0 = (struct sparc_opcode *) a;
  struct sparc_opcode *op1 = (struct sparc_opcode *) b;
  unsigned long int match0 = op0->match, match1 = op1->match;
  unsigned long int lose0 = op0->lose, lose1 = op1->lose;
  register unsigned int i;

  /* If a bit is set in both match and lose, there is something
     wrong with the opcode table.  */
  if (match0 & lose0)
    {
      fprintf (stderr, "Internal error:  bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n",
	       op0->name, match0, lose0);
      op0->lose &= ~op0->match;
      lose0 = op0->lose;
    }

  if (match1 & lose1)
    {
      fprintf (stderr, "Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n",
	       op1->name, match1, lose1);
      op1->lose &= ~op1->match;
      lose1 = op1->lose;
    }

  /* Because the bits that are variable in one opcode are constant in
     another, it is important to order the opcodes in the right order.  */
  for (i = 0; i < 32; ++i)
    {
      unsigned long int x = 1 << i;
      int x0 = (match0 & x) != 0;
      int x1 = (match1 & x) != 0;

      if (x0 != x1)
	return x1 - x0;
    }

  for (i = 0; i < 32; ++i)
    {
      unsigned long int x = 1 << i;
      int x0 = (lose0 & x) != 0;
      int x1 = (lose1 & x) != 0;

      if (x0 != x1)
	return x1 - x0;
    }

  /* They are functionally equal.  So as long as the opcode table is
     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);
    if (length_diff != 0)
      /* Put the one with fewer arguments first.  */
      return length_diff;
  }

  /* Put 1+i before i+1.  */
  {
    char *p0 = (char *) strchr(op0->args, '+');
    char *p1 = (char *) strchr(op1->args, '+');

    if (p0 && p1)
      {
	/* There is a plus in both operands.  Note that a plus
	   sign cannot be the first character in args,
	   so the following [-1]'s are valid.  */
	if (p0[-1] == 'i' && p1[1] == 'i')
	  /* op0 is i+1 and op1 is 1+i, so op1 goes first.  */
	  return 1;
	if (p0[1] == 'i' && p1[-1] == 'i')
	  /* op0 is 1+i and op1 is i+1, so op0 goes first.  */
	  return -1;
      }
  }

  /* They are, as far as we can tell, identical.
     Since qsort may have rearranged the table partially, there is
     no way to tell which one was first in the opcode table as
     written, so just say there are equal.  */
  return 0;
}

#define INST_LEN (sizeof(long))

print_insn_sparc (memaddr, info)
     CORE_ADDR memaddr;
     disassemble_info *info;
{
  unsigned char buffer[INST_LEN];
  FILE *stream = info->stream;

  read_memory (memaddr, buffer, INST_LEN);
  sparc_disassemble(buffer, (unsigned) memaddr, stream);
  return INST_LEN;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.