/* tc-sparc.c -- Assemble for the SPARC
   Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.

   This file is part of GAS, the GNU Assembler.

   GAS 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, or (at your option)
   any later version.

   GAS 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 GAS; see the file COPYING.  If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */

/* relocation type for internal assembler use only */
#define SPARC_RELOC_13 (127)
#define SPARC_RELOC_22 (126)
#define SPARC_RELOC_NONE (125)

#define cypress 1234

#undef DEBUGINSN

#include <stdio.h>
#include <ctype.h>
#include "as.h"
#include "libc.h"
#include "md.h"
#include "messages.h"
#include "symbols.h"
#include "sections.h"

/* careful, this file includes data *declarations* */
#include "sparc-opcode.h"
#include "stuff/target_arch.h"
#include <mach-o/sparc/reloc.h>

/* From GNU ansidecl.h */
#define PARAMS(paramlist)		paramlist

typedef long	offsetT;

/*
 * These are the default cputype and cpusubtype for the Sparc architecture.
 */
const cpu_type_t md_cputype = CPU_TYPE_SPARC;
cpu_subtype_t md_cpusubtype = CPU_SUBTYPE_SPARC_ALL;

/* This is the byte sex for the Sparc architecture */
const enum byte_sex md_target_byte_sex = BIG_ENDIAN_BYTE_SEX;

/* These characters start a comment anywhere on the line */
const char md_comment_chars[] = ";!";

/* These characters only start a comment at the beginning of a line */
const char md_line_comment_chars[] = "#";

/*
 * These characters can be used to separate mantissa decimal digits from 
 * exponent decimal digits in floating point numbers.
 */
const char md_EXP_CHARS[] = "eE";

/*
 * The characters after a leading 0 that means this number is a floating point
 * constant as in 0f123.456 or 0d1.234E-12 (see md_EXP_CHARS above).
 */
const char md_FLT_CHARS[] = "dDfF";

static void sparc_ip PARAMS ((char *));

static enum sparc_architecture current_architecture = v6;
static int architecture_requested;
static int warn_on_bump;

const relax_typeS md_relax_table[1];

/* handle of the OPCODE hash table */
static struct hash_control *op_hash = NULL;

#ifdef	NeXT_MOD
static void s_proc PARAMS ((int));
static void s_ignore PARAMS ((int));
extern void s_seg PARAMS ((int));
#else	/* NeXT_MOD */
static void s_data1 PARAMS ((void));
static void s_seg PARAMS ((int));
static void s_proc PARAMS ((int));
static void s_reserve PARAMS ((int));
static void s_common PARAMS ((int));
#endif	/* NeXT_MOD */

const pseudo_typeS md_pseudo_table[] =
{
#ifdef	NeXT_MOD
  {"global", s_globl, 0},	/* Maybe we should fix compiler to use globl */
  {"proc", s_proc, 0},		/* nop??? */
  /* These are to handle SUN assembler files and point to existing handlers */
  {"empty", s_ignore, 0},
  {"ident", s_ignore, 0},
  {"optim", s_ignore, 0},
  {"skip", s_space, 0},
  {"type", s_ignore, 0},
  {"word", cons, 4},
  {"half", cons, 2},
  /* these are custom handlers for SUN SPARC assembler only */

#else	/* NeXT_MOD */
  {"seg", s_seg, 0},
  {"align", s_align_bytes, 0},	/* Defaulting is invalid (0) */
  {"common", s_common, 0},
  {"global", s_globl, 0},
  {"half", cons, 2},
  {"optim", s_ignore, 0},
  {"proc", s_proc, 0},
  {"reserve", s_reserve, 0},
  {"seg", s_seg, 0},
  {"skip", s_space, 0},
  {"word", cons, 4},
#endif	/* NeXT_MOD */
  {NULL, 0, 0},
};

const int md_short_jump_size = 4;
const int md_long_jump_size = 4;
const int md_reloc_size = 12;	/* Size of relocation record */

/* This array holds the chars that always start a comment.  If the
   pre-processor is disabled, these aren't very useful */
const char comment_chars[] = "!";	/* JF removed '|' from comment_chars */

/* This array holds the chars that only start a comment at the beginning of
   a line.  If the line seems to have the form '# 123 filename'
   .line and .file directives will appear in the pre-processed output */
/* Note that input_file.c hand checks for '#' at the beginning of the
   first line of the input file.  This is because the compiler outputs
   #NO_APP at the beginning of its output. */
/* Also note that comments started like this one will always
   work if '/' isn't otherwise defined. */
const char line_comment_chars[] = "#";

const char line_separator_chars[] = "";

/* Chars that can be used to separate mant from exp in floating point nums */
const char EXP_CHARS[] = "eE";

/* Chars that mean this number is a floating point constant */
/* As in 0f12.456 */
/* or    0d1.2345e12 */
const char FLT_CHARS[] = "rRsSfFdDxXpP";

/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
   changed in read.c.  Ideally it shouldn't have to know about it at all,
   but nothing is ideal around here.  */

static unsigned char octal[256];
#define isoctal(c)  octal[(unsigned char) (c)]
static unsigned char toHex[256];

struct sparc_it
  {
    char *error;
    unsigned long opcode;
    nlist_t *nlistp;
    expressionS exp;
    int pcrel;
    char pcrel_reloc;	/* do relocation? */
    enum reloc_type_sparc reloc;
  };

struct sparc_it the_insn;

#ifdef DEBUGINSN
static void print_insn PARAMS ((struct sparc_it *insn));
#endif
static int getExpression PARAMS ((char *str));

static char *expr_end;


/*
 * Indicates a 'set' instruction which may require a either
 * of the following instructions depending on the size of the 
 * value argument:
 *
 * sethi %hi(value),reg
 *
 * or    %g0,value,reg
 *
 * sethi %hi(value),reg
 * or    reg,%lo(value),reg
 *
 */
static int special_case_set = 0;

/* s_proc and s_ignore are included for rudimentary 
   compatibility with the Sun assembler only */

static void
s_proc (ignore)
     int ignore;
{
  totally_ignore_line();
}

/* we simply ignore the rest of this statement */
static void
s_ignore (ignore)
     int ignore;
{
  totally_ignore_line();
}

/* This function is called once, at assembler startup time.  It should
   set up all the tables, etc. that the MD part of the assembler will need. */
void
md_begin ()
{
  register const char *retval = NULL;
  int lose = 0;
  register int i = 0;

  op_hash = hash_new ();

  while (i < NUMOPCODES)
    {
      const char *name = sparc_opcodes[i].name;
      retval = hash_insert (op_hash, (char *)name, (char *)&sparc_opcodes[i]);

      if(retval != NULL && *retval != '\0') {
	fprintf (stderr, "internal error: can't hash `%s': %s\n",
		 sparc_opcodes[i].name, retval);
	lose = 1;
      } do {
	if (sparc_opcodes[i].match & sparc_opcodes[i].lose)
	  {
	    fprintf (stderr, "internal error: losing opcode: `%s' \"%s\"\n",
		     sparc_opcodes[i].name, sparc_opcodes[i].args);
	    lose = 1;
	  }
	++i;
      }
      while (i < NUMOPCODES
	     && !strcmp (sparc_opcodes[i].name, name));
    }

  if (lose)
    as_fatal ("Broken assembler.  No assembly attempted.");

  for (i = '0'; i < '8'; ++i)
    octal[i] = 1;
  for (i = '0'; i <= '9'; ++i)
    toHex[i] = i - '0';
  for (i = 'a'; i <= 'f'; ++i)
    toHex[i] = i + 10 - 'a';
  for (i = 'A'; i <= 'F'; ++i)
    toHex[i] = i + 10 - 'A';
}

void
md_end(
void)
{
	return;
}

void
md_assemble (str)
     char *str;
{
  char *toP;
  int rsd;

  know (str);
  sparc_ip (str);

#ifdef DEBUGINSN
  print_insn(&the_insn);
#endif

  /* See if "set" operand is absolute and small; skip sethi if so. */
  if (special_case_set && the_insn.exp.X_seg == SEG_ABSOLUTE)
    {
      if (the_insn.exp.X_add_number >= -(1 << 12)
	  && the_insn.exp.X_add_number < (1 << 12))
	{
	  the_insn.opcode = 0x80102000	/* or %g0,imm,... */
	    | (the_insn.opcode & 0x3E000000)	/* dest reg */
	    | (the_insn.exp.X_add_number & 0x1FFF);	/* imm */
	  special_case_set = 0;	/* No longer special */
	  the_insn.reloc = SPARC_RELOC_NONE;	/* No longer relocated */
	}
    }

#ifdef NeXT_MOD	/* mark sections containing instructions */
  /*
   * We are putting a machine instruction in this section so mark it as
   * containg some machine instructions.
   */
  frchain_now->frch_section.flags |= S_ATTR_SOME_INSTRUCTIONS;
#endif /* NeXT_MOD */

  toP = frag_more (4);
  /* put out the opcode */
  md_number_to_chars (toP, (valueT) the_insn.opcode, 4);

  /* put out the symbol-dependent stuff */
  if (the_insn.reloc != SPARC_RELOC_NONE)
    {
	fix_new(frag_now,
		(toP - frag_now->fr_literal),
		4,
		the_insn.exp.X_add_symbol,
		the_insn.exp.X_subtract_symbol,
		the_insn.exp.X_add_number,
		the_insn.pcrel,
		the_insn.pcrel_reloc,	/* 1 for local labels due to scatter loading */
		the_insn.reloc);
    }

  if (special_case_set) {
    special_case_set = 0;
    assert (the_insn.reloc == SPARC_RELOC_HI22);
    /* See if "set" operand has no low-order bits; skip OR if so. */
    if ((the_insn.exp.X_seg == SEG_ABSOLUTE) && 
	((the_insn.exp.X_add_number & 0x3FF) == 0))
      return;

    toP = frag_more (4);
    rsd = (the_insn.opcode >> 25) & 0x1f;
    the_insn.opcode = 0x80102000 | (rsd << 25) | (rsd << 14);
    md_number_to_chars (toP, (valueT) the_insn.opcode, 4);
    the_insn.pcrel_reloc = 0;

    fix_new(frag_now,
	(toP - frag_now->fr_literal),
	4,
	the_insn.exp.X_add_symbol,
	the_insn.exp.X_subtract_symbol,
	the_insn.exp.X_add_number,
	the_insn.pcrel,
	the_insn.pcrel_reloc,
	SPARC_RELOC_LO10);
    return;
  }
}

static void
sparc_ip (str)
     char *str;
{
  char *error_message = "";
  char *s;
  const char *args;
  char c;
  struct sparc_opcode *insn;
  char *argsStart;
  unsigned long opcode;
  unsigned int mask = 0;
  int match = 0;
  int comma = 0;
  long immediate_max = 0;

  for (s = str; islower (*s) || (*s >= '0' && *s <= '3'); ++s)
    ;
  switch (*s)
    {

    case '\0':
      break;

    case ',':
      comma = 1;

      /*FALLTHROUGH */

    case ' ':
      *s++ = '\0';
      break;

    default:
      as_bad ("Unknown opcode: `%s'", str);
      exit (1);
    }
  if ((insn = (struct sparc_opcode *) hash_find (op_hash, str)) == NULL)
    {
      as_bad ("Unknown opcode: `%s'", str);
      return;
    }
  if (comma)
    {
      *--s = ',';
    }
  argsStart = s;
  for (;;)
    {
      opcode = insn->match;
      memset (&the_insn, '\0', sizeof (the_insn));
      the_insn.reloc = SPARC_RELOC_NONE;
      the_insn.pcrel_reloc = 1; /* default, reloc, for scatter loading */

      /*
       * Build the opcode, checking as we go to make
       * sure that the operands match
       */
      for (args = insn->args;; ++args)
	{
	  switch (*args)
	    {
	    case 'M':
	    case 'm':
	      if (strncmp (s, "%asr", 4) == 0)
		{
		  s += 4;

		  if (isdigit (*s))
		    {
		      long num = 0;

		      while (isdigit (*s))
			{
			  num = num * 10 + *s - '0';
			  ++s;
			}

		      if (num < 16 || 31 < num)
			{
			  error_message = ": asr number must be between 15 and 31";
			  goto error;
			}	/* out of range */

		      opcode |= (*args == 'M' ? RS1 (num) : RD (num));
		      continue;
		    }
		  else
		    {
		      error_message = ": expecting %asrN";
		      goto error;
		    }		/* if %asr followed by a number. */

		}		/* if %asr */
	      break;


	    case '\0':		/* end of args */
	      if (*s == '\0')
		{
		  match = 1;
		}
	      break;

	    case '+':
	      if (*s == '+')
		{
		  ++s;
		  continue;
		}
	      if (*s == '-')
		{
		  continue;
		}
	      break;

	    case '[':		/* these must match exactly */
	    case ']':
	    case ',':
	    case ' ':
	      if (*s++ == *args)
		continue;
	      break;

	    case '#':		/* must be at least one digit */
	      if (isdigit (*s++))
		{
		  while (isdigit (*s))
		    {
		      ++s;
		    }
		  continue;
		}
	      break;

	    case 'C':		/* coprocessor state register */
	      if (strncmp (s, "%csr", 4) == 0)
		{
		  s += 4;
		  continue;
		}
	      break;

	    case 'b':		/* next operand is a coprocessor register */
	    case 'c':
	    case 'D':
	      if (*s++ == '%' && *s++ == 'c' && isdigit (*s))
		{
		  mask = *s++;
		  if (isdigit (*s))
		    {
		      mask = 10 * (mask - '0') + (*s++ - '0');
		      if (mask >= 32)
			{
			  break;
			}
		    }
		  else
		    {
		      mask -= '0';
		    }
		  switch (*args)
		    {

		    case 'b':
		      opcode |= mask << 14;
		      continue;

		    case 'c':
		      opcode |= mask;
		      continue;

		    case 'D':
		      opcode |= mask << 25;
		      continue;
		    }
		}
	      break;

	    case 'r':		/* next operand must be a register */
	    case 'u':
	    case '1':
	    case '2':
	    case 'd':
	      if (*s++ == '%')
		{
		  switch (c = *s++)
		    {

		    case 'f':	/* frame pointer */
		      if (*s++ == 'p')
			{
			  mask = 0x1e;
			  break;
			}
		      error_message = ": register not fp";
		      goto error;

		    case 'g':	/* global register */
		      if (isoctal (c = *s++))
			{
			  mask = c - '0';
			  break;
			}
		      error_message = ": invalid global register";
		      goto error;

		    case 'i':	/* in register */
		      if (isoctal (c = *s++))
			{
			  mask = c - '0' + 24;
			  break;
			}
		      error_message = ": invalid in register";
		      goto error;

		    case 'l':	/* local register */
		      if (isoctal (c = *s++))
			{
			  mask = (c - '0' + 16);
			  break;
			}
		      error_message = ": invalid local register";
		      goto error;

		    case 'o':	/* out register */
		      if (isoctal (c = *s++))
			{
			  mask = (c - '0' + 8);
			  break;
			}
		      error_message = ": invalid out register";
		      goto error;

		    case 's':	/* stack pointer */
		      if (*s++ == 'p')
			{
			  mask = 0xe;
			  break;
			}
		      error_message = ": register is not sp";
		      goto error;

		    case 'r':	/* any register */
		      if (!isdigit (c = *s++))
			{
			  error_message = ": invalid register";
			  goto error;
			}
		      /* FALLTHROUGH */
		    case '0':
		    case '1':
		    case '2':
		    case '3':
		    case '4':
		    case '5':
		    case '6':
		    case '7':
		    case '8':
		    case '9':
		      if (isdigit (*s))
			{
			  if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32)
			    {
			      error_message = ": register # out of range";
			      goto error;
			    }
			}
		      else
			{
			  c -= '0';
			}
		      mask = c;
		      break;

		    default:
		      error_message = ": invalid resgiter #";
		      goto error;
		    }
		 /*
		 * Got the register, now figure out where
		 * it goes in the opcode.
		 */
		  switch (*args)
		    {

		    case '1':
		      opcode |= mask << 14;
		      continue;

		    case '2':
		      opcode |= mask;
		      continue;

		    case 'd':
		      opcode |= mask << 25;
		      continue;

		    case 'r':
		      opcode |= (mask << 25) | (mask << 14);
		      continue;

		    case 'u':
		      opcode |= (mask << 25) | mask;
		      continue;
		    }
		}
	      break;

	    case 'e':		/* next operand is a floating point register */
	    case 'v':
	    case 'V':

	    case 'f':
	    case 'B':
	    case 'R':

	    case 'g':
	    case 'H':
	    case 'J':
	      {
		char format;

		if (*s++ == '%'
		    && ((format = *s) == 'f')
		    && isdigit (*++s))
		  {
		    for (mask = 0; isdigit (*s); ++s)
		      {
			mask = 10 * mask + (*s - '0');
		      }		/* read the number */

		    if ((*args == 'v'
			 || *args == 'B'
			 || *args == 'H')
			&& (mask & 1))
		      {
			break;
		      }		/* register must be even numbered */

		    if ((*args == 'V'
			 || *args == 'R'
			 || *args == 'J')
			&& (mask & 3))
		      {
			break;
		      }		/* register must be multiple of 4 */

		    if (mask >= 32)
		      {
			error_message = ": There are only 32 f registers; [0-31]";
			goto error;
		      }	/* on error */
		  }
		else
		  {
		    break;
		  }	/* if not an 'f' register. */

		switch (*args)
		  {

		  case 'v':
		  case 'V':
		  case 'e':
		    opcode |= RS1 (mask);
		    continue;


		  case 'f':
		  case 'B':
		  case 'R':
		    opcode |= RS2 (mask);
		    continue;

		  case 'g':
		  case 'H':
		  case 'J':
		    opcode |= RD (mask);
		    continue;
		  }		/* pack it in. */

		know (0);
		break;
	      }			/* float arg */

	    case 'F':
	      if (strncmp (s, "%fsr", 4) == 0)
		{
		  s += 4;
		  continue;
		}
	      break;

	    case 'h':		/* high 22 bits */
	      the_insn.reloc = SPARC_RELOC_HI22;
	      goto immediate;

	    case 'l':		/* 22 bit PC relative immediate */
	      the_insn.reloc = SPARC_RELOC_WDISP22;
	      the_insn.pcrel = 1;
	      goto immediate;

	    case 'L':		/* 30 bit immediate for call insn */
	      the_insn.reloc = SPARC_RELOC_WDISP30;
	      the_insn.pcrel = 1;
	      goto immediate;

	    case 'n':		/* 22 bit immediate */
	      the_insn.reloc = SPARC_RELOC_22;
	      goto immediate;

	    case 'i':		/* 13 bit immediate */
	      /* What's the difference between base13 and 13?  
	         13-bit immediate and 13-bit immediate+register */
	      the_insn.reloc = SPARC_RELOC_13;
	      immediate_max = 0x1FFF;

	      /*FALLTHROUGH */

	    immediate:
	      if (*s == ' ')
		s++;
	      if (*s == '%')
		{
		  if ((c = s[1]) == 'h' && s[2] == 'i')
		    {
		      the_insn.reloc = SPARC_RELOC_HI22;
		      s += 3;
		    }
		  else if (c == 'l' && s[2] == 'o')
		    {
		      the_insn.reloc = SPARC_RELOC_LO10;
		      s += 3;
		    }
		  else
		    break;
		}
	      /* Note that if the getExpression() fails, we will still
		 have created U entries in the symbol table for the
		 'symbols' in the input string.  Try not to create U
		 symbols for registers, etc.  */
	      {
		/* This stuff checks to see if the expression ends in
		   +%reg.  If it does, it removes the register from
		   the expression, and re-sets 's' to point to the
		   right place.  */

		char *s1;

		for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++);

		if (s1 != s && isdigit (s1[-1]))
		  {
		    if (s1[-2] == '%' && s1[-3] == '+')
		      {
			s1 -= 3;
			*s1 = '\0';
			(void) getExpression (s);
			*s1 = '+';
			s = s1;
			continue;
		      }
		    else if (strchr ("goli0123456789", 
				     s1[-2]) && s1[-3] == '%' && s1[-4] == '+')
		      {
			s1 -= 4;
			*s1 = '\0';
			(void) getExpression (s);
			*s1 = '+';
			s = s1;
			continue;
		      }
		  }
	      }
	      (void) getExpression (s);
	      s = expr_end;
	/* The Next linker has the ability to scatter blocks of sections between
	 * labels.  This requires that branches to labels that survive to the
	 * link phase be relocatable.  These labels are those that are not L*
	 */
	     if (the_insn.exp.X_add_symbol != NULL && !flagseen['L']
		&& the_insn.exp.X_add_symbol->sy_nlist.n_un.n_name[0] == 'L') {
		/* local symbol which will be thrown away.  Don't bother
		 * to reloc it.
		 */
		the_insn.pcrel_reloc = 0;
	     }

	      if ((the_insn.exp.X_seg == SEG_ABSOLUTE || 
		   the_insn.exp.X_seg == SEG_BIG)
		  && the_insn.exp.X_add_symbol == 0)
		{

		  /* Check for invalid constant values.  Don't warn if
		     constant was inside %hi or %lo, since these
		     truncate the constant to fit.  */
		  if (immediate_max != 0
		      && the_insn.reloc != SPARC_RELOC_LO10
		      && the_insn.reloc != SPARC_RELOC_HI22
		      && (the_insn.exp.X_add_number > immediate_max
			  || the_insn.exp.X_add_number < ~immediate_max))
		    as_bad ("constant value must be between %ld and %ld",
			    ~immediate_max, immediate_max);

		  if ((the_insn.reloc == SPARC_RELOC_WDISP22 ||
		      the_insn.reloc == SPARC_RELOC_WDISP30) &&
		      the_insn.exp.X_add_number & 3) 
		    as_bad ("displacement is not long aligned");

 		  /* plug absolutes directly into opcode */

		  switch(the_insn.reloc) {
		  case SPARC_RELOC_13:
		    if (the_insn.exp.X_seg == SEG_BIG)
		      opcode |= (*(int *) generic_bignum) & 0x1fff;
		    else
		      opcode |= the_insn.exp.X_add_number & 0x1fff;
		    the_insn.reloc = SPARC_RELOC_NONE;
		    break;
		  case SPARC_RELOC_22:
		    if (the_insn.exp.X_seg == SEG_BIG)
		      opcode |= (*(int *) generic_bignum) & 0x3fffff;
		    else
		      opcode |= the_insn.exp.X_add_number & 0x3fffff;
		    the_insn.reloc = SPARC_RELOC_NONE;
		    break;
		  case SPARC_RELOC_HI22:
		    /* extract upper 22 bits from constant */
		    opcode |= (the_insn.exp.X_add_number >> 10) & 0x3fffff;
		    the_insn.reloc = SPARC_RELOC_NONE;
		    break;
		  case SPARC_RELOC_LO10:
		    opcode |= the_insn.exp.X_add_number & 0x3ff;
		    break;
	      
		    /* the PC relative displacements are plugged in
		       if the argument is absolute, but retain
		       relocatability */
		  case SPARC_RELOC_WDISP22:
		    opcode |= (the_insn.exp.X_add_number >> 2) & 0x3fffff;
		    break;
		  case SPARC_RELOC_WDISP30:
		    opcode |= (the_insn.exp.X_add_number >> 2) & 0x3fffffff;
		    break;
		  default:
		    printf("Unknown reloc entry\n");
		  }
		}

	      /* Reset to prevent extraneous range check.  */
	      immediate_max = 0;

	      continue;

	    case 'a':
	      if (*s++ == 'a')
		{
		  opcode |= ANNUL;
		  continue;
		}
	      break;

	    case 'A':
	      {
		char *push = input_line_pointer;
		expressionS e;

		input_line_pointer = s;

		expression (&e);

		if (e.X_seg == SEG_ABSOLUTE)
		  {
		    opcode |= e.X_add_number << 5;
		    s = input_line_pointer;
		    input_line_pointer = push;
		    continue;
		  }		/* if absolute */

		break;
	      }			/* alternate space */

	    case 'p':
	      if (strncmp (s, "%psr", 4) == 0)
		{
		  s += 4;
		  continue;
		}
	      break;

	    case 'q':		/* floating point queue */
	      if (strncmp (s, "%fq", 3) == 0)
		{
		  s += 3;
		  continue;
		}
	      break;

	    case 'Q':		/* coprocessor queue */
	      if (strncmp (s, "%cq", 3) == 0)
		{
		  s += 3;
		  continue;
		}
	      break;

	    case 'S':
	      if (strcmp (str, "set") == 0)
		{
		  special_case_set = 1;
		  continue;
		}
	      break;


	    case 't':
	      if (strncmp (s, "%tbr", 4) != 0)
		break;
	      s += 4;
	      continue;

	    case 'w':
	      if (strncmp (s, "%wim", 4) != 0)
		break;
	      s += 4;
	      continue;

	    case 'y':
	      if (strncmp (s, "%y", 2) != 0)
		break;
	      s += 2;
	      continue;

	    default:
	      as_fatal ("failed sanity check.");
	    }			/* switch on arg code */
	  break;
	}			/* for each arg that we expect */
    error:
      if (match == 0)
	{
	  /* Args don't match. */
	  if (((int) (&insn[1] - sparc_opcodes)) < NUMOPCODES
	      && !strcmp (insn->name, insn[1].name))
	    {
	      ++insn;
	      s = argsStart;
	      continue;
	    }
	  else
	    {
	      as_bad ("Illegal operands%s", error_message);
	      return;
	    }
	}
      else
	{
	  if (insn->architecture > current_architecture)
	    {
	      if ((!architecture_requested || warn_on_bump)
		  &&
		  1
		)
		{
		  if (warn_on_bump)
		    {
		      as_warn ("architecture bumped from \"%s\" to \"%s\" on \"%s\"",
			       architecture_pname[current_architecture],
			       architecture_pname[insn->architecture],
			       str);
		    }		/* if warning */

		  current_architecture = insn->architecture;
		}
	      else
		{
		  as_bad ("architecture mismatch on \"%s\" (\"%s\").  current architecture is \"%s\"",
			  str,
			  architecture_pname[insn->architecture],
			  architecture_pname[current_architecture]);
		  return;
		}		/* if bump ok else error */
	    }			/* if architecture higher */
	}			/* if no match */

      break;
    }				/* forever looking for a match */

  the_insn.opcode = opcode;
}

static int
getExpression (str)
     char *str;
{
  char *save_in;
  segT seg;

  save_in = input_line_pointer;
  input_line_pointer = str;
  seg = expression (&the_insn.exp);

  if (seg != SEG_ABSOLUTE
      && seg != SEG_SECT
      && seg != SEG_DIFFSECT
      && seg != SEG_UNKNOWN
      && seg != SEG_NONE
      && seg != SEG_BIG) {
    the_insn.error = "bad segment";
    expr_end = input_line_pointer;
    input_line_pointer = save_in;
    return 1;
  }
  expr_end = input_line_pointer;
  input_line_pointer = save_in;
  return 0;
}				/* getExpression() */


/*
  This is identical to the md_atof in m68k.c.  I think this is right,
  but I'm not sure.

  Turn a string in input_line_pointer into a floating point constant of type
  type, and store the appropriate bytes in *litP.  The number of LITTLENUMS
  emitted is stored in *sizeP .  An error message is returned, or NULL on OK.
  */

/* Equal to MAX_PRECISION in atof-ieee.c */
#define MAX_LITTLENUMS 6

char *
md_atof (type, litP, sizeP)
     char type;
     char *litP;
     int *sizeP;
{
  int prec;
  LITTLENUM_TYPE words[MAX_LITTLENUMS];
  LITTLENUM_TYPE *wordP;
  char *t;
  char *atof_ieee ();

  switch (type)
    {
    case 'f':
    case 'F':
    case 's':
    case 'S':
      prec = 2;
      break;

    case 'd':
    case 'D':
    case 'r':
    case 'R':
      prec = 4;
      break;

    case 'x':
    case 'X':
      prec = 6;
      break;

    case 'p':
    case 'P':
      prec = 6;
      break;

    default:
      *sizeP = 0;
      return "Bad call to MD_ATOF()";
    }
  t = atof_ieee (input_line_pointer, type, words);
  if (t)
    input_line_pointer = t;
  *sizeP = prec * sizeof (LITTLENUM_TYPE);
  for (wordP = words; prec--;)
    {
      md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
      litP += sizeof (LITTLENUM_TYPE);
    }
  return "";
}

/*
 * Write out big-endian.
 */
void
md_number_to_chars (buf, val, n)
     char *buf;
     signed_target_addr_t val;
     int n;
{
  // sigh, all architectures do this..,
  switch(n) {
    
  case 4:
    *buf++ = val >> 24;
    *buf++ = val >> 16;
  case 2:
    *buf++ = val >> 8;
  case 1:
    *buf = val;
    break;
    
  default:
    abort();
  }
}

/* Apply a fixS to the frags, now that we know the value it ought to
   hold. */


void
md_number_to_imm(unsigned char *buf, signed_target_addr_t val, int size, fixS *fixP, int nsect)
{

  /* handle the most common case quickly */
  if ((fixP->fx_r_type == NO_RELOC) ||
      (fixP->fx_r_type == SPARC_RELOC_NONE) ||
      (fixP->fx_r_type == SPARC_RELOC_VANILLA)) {
    switch(size){
    case 4:
      *buf++ = val >> 24;
      *buf++ = val >> 16;
    case 2:
      *buf++ = val >> 8;
    case 1:
      *buf = val;
      break;
    default:
      abort();
    }
    return;
  }

  switch (fixP->fx_r_type) {
  case SPARC_RELOC_WDISP30:
    val = (val >> 2) + 1;	/* adjust for word displacement */
    buf[0] |= (val >> 24) & 0x3f;
    buf[1] = (val >> 16);
    buf[2] = val >> 8;
    buf[3] = val;
    break;
  case SPARC_RELOC_WDISP22:
    val = (val >> 2) + 1;
    buf[1] |= (val >> 16) & 0x3f;
    buf[2] = val >> 8;
    buf[3] = val;
    break;
  case SPARC_RELOC_HI22:
    buf[1] |= (val >> 26) & 0x3f;
    buf[2] = val >> 18;
    buf[3] = val >> 10;
    break;
  case SPARC_RELOC_LO10:
    buf[2] |= (val >> 8) & 0x03;
    buf[3] = val;
    break;

  /* special cases that need to be handled internally by the as */
  case SPARC_RELOC_22:
    if (!fixP->fx_addsy) {
      if (val & ~0x003fffff) {
	as_bad ("relocation overflow");
      }			/* on overflow */
      buf[1] |= (val >> 16) & 0x3f;
      buf[2] = val >> 8;
      buf[3] = val & 0xff;
    } else
      as_bad ("Undefined symbolic 22-bit immediate reference: %s", 
	      fixP->fx_addsy->sy_name);
    break;
  case SPARC_RELOC_13:
    if (!fixP->fx_addsy) {
      if (((val > 0) && (val & ~(offsetT)0x00001fff))
	  || ((val < 0) && (~(val - 1) & ~(offsetT)0x00001fff))) {
	as_bad ("relocation overflow");
      }
      buf[2] |= (val >> 8) & 0x1f;
      buf[3] = val;
    } else
      as_bad ("Undefined symbolic 13-bit immediate reference: %s", 
	      fixP->fx_addsy->sy_name);
    break;
  case SPARC_RELOC_NONE:
  default:
    as_bad ("bad or unhandled relocation type: 0x%02x", fixP->fx_r_type);
    break;
  }
}


/*
 * md_parse_option
 *	Invocation line includes a switch not recognized by the base assembler.
 *	See if it's a processor-specific option.  These are:
 *
 *	-bump
 *		Warn on architecture bumps.  See also -A.
 *
 *	-Av6, -Av7, -Av8, -Asparclite
 *		Select the architecture.  Instructions or features not
 *		supported by the selected architecture cause fatal errors.
 *
 *		The default is to start at v6, and bump the architecture up
 *		whenever an instruction is seen at a higher level.
 *
 *		If -bump is specified, a warning is printing when bumping to
 *		higher levels.
 *
 *		If an architecture is specified, all instructions must match
 *		that architecture.  Any higher level instructions are flagged
 *		as errors.
 *
 *		if both an architecture and -bump are specified, the
 *		architecture starts at the specified level, but bumps are
 *		warnings.
 *
 */

int 
md_parse_option (argP, cntP, vecP)
     char **argP;
     int *cntP;
     char ***vecP;
{
  char *p;
  const char **arch;

  if (!strcmp (*argP, "bump"))
    {
      warn_on_bump = 1;
    }
  else if (**argP == 'A')
    {
      p = (*argP) + 1;

      for (arch = architecture_pname; *arch != NULL; ++arch)
	{
	  if (strcmp (p, *arch) == 0)
	    {
	      break;
	    }			/* found a match */
	}			/* walk the pname table */

      if (*arch == NULL)
	{
	  as_bad ("unknown architecture: %s", p);
	}
      else
	{
	  current_architecture = (enum sparc_architecture) (arch - architecture_pname);
	  architecture_requested = 1;
	}
    }
#ifndef NeXT_MOD
#ifdef OBJ_ELF
  else if (**argP == 'V')
    {
      print_version_id ();
    }
  else if (**argP == 'Q')
    {
      /* Qy - do emit .comment
	 Qn - do not emit .comment */
    }
  else if (**argP == 's')
    {
      /* use .stab instead of .stab.excl */
    }
#endif
  else if (strcmp (*argP, "sparc") == 0)
    {
      /* Ignore -sparc, used by SunOS make default .s.o rule.  */
    }
#endif /* NeXT_MOD */
  else
    {
      /* Unknown option */
      (*argP)++;
      return 0;
    }
  **argP = '\0';		/* Done parsing this switch */
  return 1;
}				/* md_parse_option() */


int
md_estimate_size_before_relax(
fragS *fragP,
int segment_type)
{
	as_fatal("internal error: Relaxation should never occur");
	return(0);
}

void
md_convert_frag(
fragS *fragP)
{
	as_fatal("internal error: Relaxation should never occur");
}


#ifdef DEBUGINSN

char *
S_GET_NAME(sym)
     symbolS *sym;
{
  return (sym->sy_nlist.n_un.n_name);
}

/* for debugging only */
static void
print_insn (insn)
     struct sparc_it *insn;
{
  const char *const Reloc[] = {
    "VANILLA",
    "PAIR",
    "HI22",
    "LO10",
    "DISP22",
    "PCREL",
    "22",
    "13",
    "SECTDIFF",
    "HI22_SECTDIFF",
    "LO10_SECTDIFF",
    "NONE",
    "UNUSED"
  };

  const char *const InternalReloc[] = {
    "13",
    "22"
  };

  if (insn->error)
    fprintf (stderr, "ERROR: %s\n", insn->error);
  fprintf (stderr, "opcode=0x%08x\n", (unsigned int)insn->opcode);
  if (insn->reloc >= 127)
    fprintf (stderr, "internal reloc = %s\n", InternalReloc[insn->reloc-127]);
  else
    fprintf (stderr, "reloc = %s\n", Reloc[insn->reloc]);
  fprintf (stderr, "X_add_number = 0x%x\n",
	   insn->exp.X_add_number);

  if (insn->exp.X_add_symbol != NULL)
    fprintf(stderr, "Add symbol: %s\n", S_GET_NAME(insn->exp.X_add_symbol));
  if (insn->exp.X_subtract_symbol != NULL)
    fprintf(stderr, "Subtract symbol: %s\n", S_GET_NAME(insn->exp.X_subtract_symbol));
}
#endif
