/* tc-maxq.c -- assembler code for a MAXQ chip.

   Copyright 2004, 2005 Free Software Foundation, Inc.

   Contributed by HCL Technologies Pvt. Ltd.

   Author: Vineet Sharma(vineets@noida.hcltech.com) Inderpreet
   S.(inderpreetb@noida.hcltech.com)

   This file is part of GAS.

   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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include "as.h"
#include "safe-ctype.h"
#include "subsegs.h"
#include "dwarf2dbg.h"
#include "tc-maxq.h"
#include "opcode/maxq.h"
#include "ctype.h"

#ifndef MAXQ10S
#define MAXQ10S 1
#endif

#ifndef _STRING_H
#include "string.h"
#endif

#ifndef DEFAULT_ARCH
#define DEFAULT_ARCH "MAXQ20"
#endif

#ifndef MAX_OPERANDS
#define MAX_OPERANDS 2
#endif

#ifndef MAX_MNEM_SIZE
#define MAX_MNEM_SIZE 8
#endif

#ifndef END_OF_INSN
#define END_OF_INSN '\0'
#endif

#ifndef IMMEDIATE_PREFIX
#define IMMEDIATE_PREFIX '#'
#endif

#ifndef MAX_REG_NAME_SIZE
#define MAX_REG_NAME_SIZE 4
#endif

#ifndef MAX_MEM_NAME_SIZE
#define MAX_MEM_NAME_SIZE 9
#endif

/* opcode for PFX[0].  */
#define PFX0 0x0b

/* Set default to MAXQ20.  */
unsigned int max_version = bfd_mach_maxq20;

const char *default_arch = DEFAULT_ARCH;

/* Type of the operand: Register,Immediate,Memory access,flag or bit.  */

union _maxq20_op
{
  const reg_entry *  reg;
  char               imms; /* This is to store the immediate value operand.  */
  expressionS *      disps;
  symbolS *          data;
  const mem_access * mem;
  int                flag;
  const reg_bit *    r_bit;
};

typedef union _maxq20_op maxq20_opcode;

/* For handling optional L/S in Maxq20.  */
#ifdef BFD_ASSEMBLER

/* Exposed For Linker - maps indirectly to the liker relocations.  */
#define LONG_PREFIX  		MAXQ_LONGJUMP	/* BFD_RELOC_16 */
#define SHORT_PREFIX 		MAXQ_SHORTJUMP	/* BFD_RELOC_16_PCREL_S2 */
#define ABSOLUTE_ADDR_FOR_DATA 	MAXQ_INTERSEGMENT

#define NO_PREFIX    		0
#define EXPLICT_LONG_PREFIX     14

#else

#define EXPLICT_LONG_PREFIX 14
#define LONG_PREFIX  5
#define SHORT_PREFIX 1
#define ABSOLUTE_ADDR_FOR_DATA 0
#define NO_PREFIX    0

#endif

/* The main instruction structure containing fields to describe instrn */
typedef struct _maxq20_insn
{
  /* The opcode information for the MAXQ20 */
  MAXQ20_OPCODE_INFO op;

  /* The number of operands */
  unsigned int operands;

  /* Number of different types of operands - Comments can be removed if reqd. 
   */
  unsigned int reg_operands, mem_operands, disp_operands, data_operands;
  unsigned int imm_operands, imm_bit_operands, bit_operands, flag_operands;

  /* Types of the individual operands */
  UNKNOWN_OP types[MAX_OPERANDS];

  /* Relocation type for operand : to be investigated into */
  int reloc[MAX_OPERANDS];

  /* Complete information of the Operands */
  maxq20_opcode maxq20_op[MAX_OPERANDS];

  /* Choice of prefix register whenever needed */
  int prefix;

  /* Optional Prefix for Instructions like LJUMP, SJUMP etc */
  unsigned char Instr_Prefix;

  /* 16 bit Instruction word */
  unsigned char instr[2];
}
maxq20_insn;

/* Definitions of all possible characters that can start an operand.  */
const char *extra_symbol_chars = "@(#";

/* Special Character that would start a comment.  */
const char comment_chars[] = ";";

/* Starts a comment when it appears at the start of a line.  */
const char line_comment_chars[] = ";#";

const char line_separator_chars[] = "";	/* originally may b by sudeep "\n".  */

/*  The following are used for option processing.  */

/* This is added to the mach independent string passed to getopt.  */
const char *md_shortopts = "q";

/* Characters for exponent and floating point.  */
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "";

/* This is for the machine dependent option handling.  */
#define OPTION_EB               (OPTION_MD_BASE + 0)
#define OPTION_EL               (OPTION_MD_BASE + 1)
#define MAXQ_10                 (OPTION_MD_BASE + 2)
#define MAXQ_20			(OPTION_MD_BASE + 3)

struct option md_longopts[] =
{
  {"MAXQ10", no_argument, NULL, MAXQ_10},
  {"MAXQ20", no_argument, NULL, MAXQ_20},
  {NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);

/* md_undefined_symbol We have no need for this function.  */

symbolS *
md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
{
  return NULL;
}

static void
maxq_target (int target)
{
  max_version = target;
  bfd_set_arch_mach (stdoutput, bfd_arch_maxq, max_version);
}

int
md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
{
  /* Any options support will be added onto this switch case.  */
  switch (c)
    {
    case MAXQ_10:
      max_version = bfd_mach_maxq10;
      break;
    case MAXQ_20:
      max_version = bfd_mach_maxq20;
      break;

    default:
      return 0;
    }

  return 1;
}

/* When a usage message is printed, this function is called and
   it prints a description of the machine specific options.  */

void
md_show_usage (FILE * stream)
{
  /* Over here we will fill the description of the machine specific options.  */

  fprintf (stream, _(" MAXQ-specific assembler options:\n"));

  fprintf (stream, _("\
	-MAXQ20		       generate obj for MAXQ20(default)\n\
	-MAXQ10		       generate obj for MAXQ10\n\
	"));
}

#ifdef BFD_ASSEMBLER
unsigned long
maxq20_mach (void)
{
  if (!(strcmp (default_arch, "MAXQ20")))
    return 0;

  as_fatal (_("Unknown architecture"));
  return 1;
}

arelent *
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
{
  arelent *rel;
  bfd_reloc_code_real_type code;

  switch (fixp->fx_r_type)
    {
    case MAXQ_INTERSEGMENT:
    case MAXQ_LONGJUMP:
    case BFD_RELOC_16_PCREL_S2:
      code = fixp->fx_r_type;
      break;

    case 0:
    default:
      switch (fixp->fx_size)
	{
	default:
	  as_bad_where (fixp->fx_file, fixp->fx_line,
			_("can not do %d byte relocation"), fixp->fx_size);
	  code = BFD_RELOC_32;
	  break;

	case 1:
	  code = BFD_RELOC_8;
	  break;
	case 2:
	  code = BFD_RELOC_16;
	  break;
	case 4:
	  code = BFD_RELOC_32;
	  break;
	}
    }

  rel = xmalloc (sizeof (arelent));
  rel->sym_ptr_ptr  = xmalloc (sizeof (asymbol *));
  *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);

  rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
  rel->addend  = fixp->fx_addnumber;
  rel->howto   = bfd_reloc_type_lookup (stdoutput, code);

  if (rel->howto == NULL)
    {
      as_bad_where (fixp->fx_file, fixp->fx_line,
		    _("cannot represent relocation type %s"),
		    bfd_get_reloc_code_name (code));

      /* Set howto to a garbage value so that we can keep going.  */
      rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
      assert (rel->howto != NULL);
    }

  return rel;
}

#endif

/* md_estimate_size_before_relax()

   Called just before relax() for rs_machine_dependent frags.  The MAXQ
   assembler uses these frags to handle 16 bit absolute jumps which require a 
   prefix instruction to be inserted. Any symbol that is now undefined will
   not become defined. Return the correct fr_subtype in the frag. Return the
   initial "guess for variable size of frag"(This will be eiter 2 or 0) to
   caller. The guess is actually the growth beyond the fixed part.  Whatever
   we do to grow the fixed or variable part contributes to our returned
   value.  */

int
md_estimate_size_before_relax (fragS *fragP, segT segment)
{
  /* Check whether the symbol has been resolved or not.
     Otherwise we will have to generate a fixup.  */
  if ((S_GET_SEGMENT (fragP->fr_symbol) != segment)
      || fragP->fr_subtype == EXPLICT_LONG_PREFIX)
    {
      RELOC_ENUM reloc_type;
      unsigned char *opcode;
      int old_fr_fix;

      /* Now this symbol has not been defined in this file.
	 Hence we will have to create a fixup.  */
      int size = 2;

      /* This is for the prefix instruction.  */

      if (fragP->fr_subtype == EXPLICT_LONG_PREFIX)
	fragP->fr_subtype = LONG_PREFIX;

      if (S_GET_SEGMENT (fragP->fr_symbol) != segment
	  && ((!(fragP->fr_subtype) == EXPLICT_LONG_PREFIX)))
	fragP->fr_subtype = ABSOLUTE_ADDR_FOR_DATA;

      reloc_type =
	(fragP->fr_subtype ? fragP->fr_subtype : ABSOLUTE_ADDR_FOR_DATA);

      fragP->fr_subtype = reloc_type;

      if (reloc_type == SHORT_PREFIX)
	size = 0;
      old_fr_fix = fragP->fr_fix;
      opcode = (unsigned char *) fragP->fr_opcode;

      fragP->fr_fix += (size);

      fix_new (fragP, old_fr_fix - 2, size + 2,
	       fragP->fr_symbol, fragP->fr_offset, 0, reloc_type);
      frag_wane (fragP);
      return fragP->fr_fix - old_fr_fix;
    }

  if (fragP->fr_subtype == SHORT_PREFIX)
    {
      fragP->fr_subtype = SHORT_PREFIX;
      return 0;
    }

  if (fragP->fr_subtype == NO_PREFIX || fragP->fr_subtype == LONG_PREFIX)
    {
      unsigned long instr;
      unsigned long call_addr;
      long diff;
      fragS *f;
      diff = diff ^ diff;;
      call_addr = call_addr ^ call_addr;
      instr = 0;
      f = NULL;

      /* segment_info_type *seginfo = seg_info (segment);  */
      instr = fragP->fr_address + fragP->fr_fix - 2;

      /* This is the offset if it is a PC relative jump.  */
      call_addr = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset;

      /* PC stores the value of the next instruction.  */
      diff = (call_addr - instr) - 1;

      if (diff >= (-128 * 2) && diff <= (2 * 127))
	{
	  /* Now as offset is an 8 bit value, we will pass
	     that to the jump instruction directly.  */
	  fragP->fr_subtype = NO_PREFIX;
	  return 0;
	}

      fragP->fr_subtype = LONG_PREFIX;
      return 2;
    }

  as_fatal (_("Illegal Reloc type in md_estimate_size_before_relax for line : %d"),
	    frag_now->fr_line);
  return 0;
}

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

/* 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.  */

char *
md_atof (int type, char * litP, int * sizeP)
{
  int prec;
  LITTLENUM_TYPE words[4];
  char *t;
  int i;

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

    case 'd':
      prec = 2;
      /* The size of Double has been changed to 2 words ie 32 bits.  */
      /* prec = 4; */
      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 * 2;

  for (i = prec - 1; i >= 0; i--)
    {
      md_number_to_chars (litP, (valueT) words[i], 2);
      litP += 2;
    }

  return NULL;
}

void
maxq20_cons_fix_new (fragS * frag, unsigned int off, unsigned int len,
		     expressionS * exp)
{
  int r = 0;

  switch (len)
    {
    case 2:
      r = MAXQ_WORDDATA;	/* Word+n */
      break;
    case 4:
      r = MAXQ_LONGDATA;	/* Long+n */
      break;
    }

  fix_new_exp (frag, off, len, exp, 0, r);
  return;
}

short
tc_coff_fix2rtype (fixS * fixP)
{
  return fixP->fx_r_type;
}

int
tc_coff_sizemachdep (fragS *fragP)
{
  if (fragP->fr_next)
    return (fragP->fr_next->fr_address - fragP->fr_address);

  return 0;
}

/* GAS will call this for every rs_machine_dependent fragment. The
   instruction is compleated using the data from the relaxation pass. It may
   also create any necessary relocations.  */
#ifdef BFD_ASSEMBLER
void
md_convert_frag (bfd *   headers ATTRIBUTE_UNUSED,
		 segT    seg ATTRIBUTE_UNUSED,
		 fragS * fragP)
#else
void
md_convert_frag (object_headers * headers ATTRIBUTE_UNUSED,
		 segT             sec ATTRIBUTE_UNUSED,
		 fragS *          fragP)
#endif
{
  char *opcode;
  offsetT target_address;
  offsetT opcode_address;
  offsetT displacement_from_opcode_start;
  int address;

  opcode = fragP->fr_opcode;
  address = 0;
  target_address = opcode_address = displacement_from_opcode_start = 0;

  target_address =
    (S_GET_VALUE (fragP->fr_symbol) / MAXQ_OCTETS_PER_BYTE) +
    (fragP->fr_offset / MAXQ_OCTETS_PER_BYTE);

  opcode_address =
    (fragP->fr_address / MAXQ_OCTETS_PER_BYTE) +
    ((fragP->fr_fix - 2) / MAXQ_OCTETS_PER_BYTE);

  /* PC points to the next Instruction.  */
  displacement_from_opcode_start = ((target_address - opcode_address)  - 1);

  if ((displacement_from_opcode_start >= -128
       && displacement_from_opcode_start <= 127)
      && (fragP->fr_subtype == SHORT_PREFIX
	  || fragP->fr_subtype == NO_PREFIX))
    {
      /* Its a displacement.  */
      *opcode = (char) displacement_from_opcode_start;
    }
  else
    {
      /* Its an absolute 16 bit jump. Now we have to
	 load the prefix operator with the upper 8 bits.  */
      if (fragP->fr_subtype == SHORT_PREFIX)
	{
	  as_bad (_("Cant make long jump/call into short jump/call : %d"),
		  fragP->fr_line);
	  return;
	}

      /* Check whether the symbol has been resolved or not.
	 Otherwise we will have to generate a fixup.  */

      if (fragP->fr_subtype != SHORT_PREFIX)
	{
	  RELOC_ENUM reloc_type;
	  int old_fr_fix;
	  int size = 2;

	  /* Now this is a basolute jump/call.
	     Hence we will have to create a fixup.  */
	  if (fragP->fr_subtype == NO_PREFIX)
	    fragP->fr_subtype = LONG_PREFIX;

	  reloc_type =
	    (fragP->fr_subtype ? fragP->fr_subtype : LONG_PREFIX);

	  if (reloc_type == 1)
	    size = 0;
	  old_fr_fix = fragP->fr_fix;

	  fragP->fr_fix += (size);

	  fix_new (fragP, old_fr_fix - 2, size + 2,
		   fragP->fr_symbol, fragP->fr_offset, 0, reloc_type);
	  frag_wane (fragP);
	}
    }
}

long
md_pcrel_from (fixS *fixP)
{
  return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
}

/* Writes the val to the buf, where n is the nuumber of bytes to write.  */

void
maxq_number_to_chars (char *buf, valueT val, int n)
{
  if (target_big_endian)
    number_to_chars_bigendian (buf, val, n);
  else
    number_to_chars_littleendian (buf, val, n);
}

/* GAS will call this for each fixup. It's main objective is to store the
   correct value in the object file. 'fixup_segment' performs the generic
   overflow check on the 'valueT *val' argument after md_apply_fix3 returns.
   If the overflow check is relevant for the target machine, then
   'md_apply_fix3' should modify 'valueT *val', typically to the value stored 
   in the object file (not to be done in MAXQ).  */

void
md_apply_fix3 (fixS *fixP, valueT *valT, segT seg ATTRIBUTE_UNUSED)
{
  char *p = fixP->fx_frag->fr_literal + fixP->fx_where;
  char *frag_to_fix_at =
    fixP->fx_frag->fr_literal + fixP->fx_frag->fr_fix - 2;

  if (fixP)
    {
      if (fixP->fx_frag && valT)
	{
	  /* If the relaxation substate is not defined we make it equal
	     to the kind of relocation the fixup is generated for.  */
	  if (!fixP->fx_frag->fr_subtype)
	    fixP->fx_frag->fr_subtype = fixP->fx_r_type;

	  /* For any instruction in which either we have specified an
	     absolute address or it is a long jump we need to add a PFX0
	     instruction to it. In this case as the instruction has already
	     being written at 'fx_where' in the frag we copy it at the end of 
	     the frag(which is where the relocation was generated) as when
	     the relocation is generated the frag is grown by 2 type, this is 
	     where we copy the contents of fx_where and add a pfx0 at
	     fx_where.  */
	  if ((fixP->fx_frag->fr_subtype == ABSOLUTE_ADDR_FOR_DATA)
	      || (fixP->fx_frag->fr_subtype == LONG_PREFIX))
	    {
	      *(frag_to_fix_at + 1) = *(p + 1);
	      maxq_number_to_chars (p + 1, PFX0, 1);
	    }

#ifdef BFD_ASSEMBLER
	  /* Remember value for tc_gen_reloc.  */
	  fixP->fx_addnumber = *valT;
#endif
	}

      /* This prob can be fixed by defining tc_fix_adjustable.  */
#ifndef BFD_ASSEMBLER
      if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy))
	segment_info[S_GET_SEGMENT (fixP->fx_addsy)].dot = NULL;
#endif

      /* Some fixups generated by GAS which gets resovled before this this
         func. is called need to be wriiten to the frag as here we are going
         to go away with the relocations fx_done=1.  */
      if (fixP->fx_addsy == NULL)
	{
	  maxq_number_to_chars (p, *valT, fixP->fx_size);
	  fixP->fx_addnumber = *valT;
	  fixP->fx_done = 1;
	}
    }
}

/* Tables for lexical analysis.  */
static char mnemonic_chars[256];
static char register_chars[256];
static char operand_chars[256];
static char identifier_chars[256];
static char digit_chars[256];

/* Lexical Macros.  */
#define is_mnemonic_char(x)   (mnemonic_chars[(unsigned char)(x)])
#define is_register_char(x)   (register_chars[(unsigned char)(x)])
#define is_operand_char(x)    (operand_chars[(unsigned char)(x)])
#define is_space_char(x)      (x==' ')
#define is_identifier_char(x) (identifier_chars[(unsigned char)(x)])
#define is_digit_char(x)      (identifier_chars[(unsigned char)(x)])

/* Special characters for operands.  */
static char operand_special_chars[] = "[]@.-+";

/* md_assemble() will always leave the instruction passed to it unaltered.
   To do this we store the instruction in a special stack.  */
static char save_stack[32];
static char *save_stack_p;

#define END_STRING_AND_SAVE(s)	\
  do				\
    {				\
      *save_stack_p++ = *(s);	\
      *s = '\0';		\
    }				\
  while (0)

#define RESTORE_END_STRING(s)	\
  do				\
    {				\
      *(s) = *(--save_stack_p);	\
    }				\
  while (0)

/* The instruction we are assembling.  */
static maxq20_insn i;

/* The current template.  */
static MAXQ20_OPCODES *current_templates;

/* The displacement operand if any.  */
static expressionS disp_expressions;

/* Current Operand we are working on (0:1st operand,1:2nd operand).  */
static int this_operand;

/* The prefix instruction if used.  */
static char PFX_INSN[2];
static char INSERT_BUFFER[2];

/* For interface with expression() ????? */
extern char *input_line_pointer;

/* The HASH Tables:  */

/* Operand Hash Table.  */
static struct hash_control *op_hash;

/* Register Hash Table.  */
static struct hash_control *reg_hash;

/* Memory reference Hash Table.  */
static struct hash_control *mem_hash;

/* Bit hash table.  */
static struct hash_control *bit_hash;

/* Memory Access syntax table.  */
static struct hash_control *mem_syntax_hash;

/* This is a mapping from pseudo-op names to functions.  */

const pseudo_typeS md_pseudo_table[] =
{
  {"int", cons, 2},		/* size of 'int' has been changed to 1 word
				   (i.e) 16 bits.  */
  {"maxq10", maxq_target, bfd_mach_maxq10},
  {"maxq20", maxq_target, bfd_mach_maxq20},
  {NULL, 0, 0},
};

#if defined(BFD_HEADERS)
#ifdef RELSZ
const int md_reloc_size = RELSZ;	/* Coff headers.  */
#else
const int md_reloc_size = 12;		/* Something else headers.  */
#endif
#else
const int md_reloc_size = 12;		/* Not bfdized.  */
#endif

#define SET_PFX_ARG(x) (PFX_INSN[1] = x)


/* This function sets the PFX value coresponding to the specs. Source
   Destination Index Selection ---------------------------------- Write To|
   SourceRegRange | Dest Addr Range
   ------------------------------------------------------ PFX[0] | 0h-Fh |
   0h-7h PFX[1] | 10h-1Fh | 0h-7h PFX[2] | 0h-Fh | 8h-Fh PFX[3] | 10h-1Fh |
   8h-Fh PFX[4] | 0h-Fh | 10h-17h PFX[5] | 10h-1Fh | 10h-17h PFX[6] | 0h-Fh | 
   18h-1Fh PFX[7] | 0h-Fh | 18h-1Fh */

static void
set_prefix (void)
{
  short int src_index = 0, dst_index = 0;

  if (i.operands == 0)
    return;
  if (i.operands == 1)		/* Only SRC is Present */
    {
      if (i.types[0] == REG)
	{
	  if (!strcmp (i.op.name, "POP") || !strcmp (i.op.name, "POPI"))
	    {
	      dst_index = i.maxq20_op[0].reg[0].Mod_index;
	      src_index = 0x00;
	    }
	  else
	    {
	      src_index = i.maxq20_op[0].reg[0].Mod_index;
	      dst_index = 0x00;
	    }
	}
    }

  if (i.operands == 2)
    {
      if (i.types[0] == REG && i.types[1] == REG)
	{
	  dst_index = i.maxq20_op[0].reg[0].Mod_index;
	  src_index = i.maxq20_op[1].reg[0].Mod_index;
	}
      else if (i.types[0] != REG && i.types[1] == REG)	/* DST is Absent */
	{
	  src_index = i.maxq20_op[1].reg[0].Mod_index;
	  dst_index = 0x00;
	}
      else if (i.types[0] == REG && i.types[1] != REG)	/* Id SRC is Absent */
	{
	  dst_index = i.maxq20_op[0].reg[0].Mod_index;
	  src_index = 0x00;
	}
      else if (i.types[0] == BIT && i.maxq20_op[0].r_bit)
	{
	  dst_index = i.maxq20_op[0].r_bit->reg->Mod_index;
	  src_index = 0x00;
	}

      else if (i.types[1] == BIT && i.maxq20_op[1].r_bit)
	{
	  dst_index = 0x00;
	  src_index = i.maxq20_op[1].r_bit->reg->Mod_index;
	}
    }

  if (src_index >= 0x00 && src_index <= 0xF)
    {
      if (dst_index >= 0x00 && dst_index <= 0x07)
	/* Set PFX[0] */
	i.prefix = 0;

      else if (dst_index >= 0x08 && dst_index <= 0x0F)
	/* Set PFX[2] */
	i.prefix = 2;

      else if (dst_index >= 0x10 && dst_index <= 0x17)
	/* Set PFX[4] */
	i.prefix = 4;

      else if (dst_index >= 0x18 && dst_index <= 0x1F)
	/* Set PFX[6] */
	i.prefix = 6;
    }
  else if (src_index >= 0x10 && src_index <= 0x1F)
    {
      if (dst_index >= 0x00 && dst_index <= 0x07)
	/* Set PFX[1] */
	i.prefix = 1;

      else if (dst_index >= 0x08 && dst_index <= 0x0F)
	/* Set PFX[3] */
	i.prefix = 3;

      else if (dst_index >= 0x10 && dst_index <= 0x17)
	/* Set PFX[5] */
	i.prefix = 5;

      else if (dst_index >= 0x18 && dst_index <= 0x1F)
	/* Set PFX[7] */
	i.prefix = 7;
    }
}

static unsigned char
is_a_LSinstr (const char *ln_pointer)
{
  int i = 0;

  for (i = 0; LSInstr[i] != NULL; i++)
    if (!strcmp (LSInstr[i], ln_pointer))
      return 1;

  return 0;
}

static void
LS_processing (const char *line)
{
  if (is_a_LSinstr (line))
    {
      if ((line[0] == 'L') || (line[0] == 'l'))
	{
	  i.prefix = 0;
	  INSERT_BUFFER[0] = PFX0;
	  i.Instr_Prefix = LONG_PREFIX;
	}
      else if ((line[0] == 'S') || (line[0] == 's'))
	i.Instr_Prefix = SHORT_PREFIX;
      else
	i.Instr_Prefix = NO_PREFIX;
    }
  else
    i.Instr_Prefix = LONG_PREFIX;
}

/* Separate mnemonics and the operands.  */

static char *
parse_insn (char *line, char *mnemonic)
{
  char *l = line;
  char *token_start = l;
  char *mnem_p;
  char temp[MAX_MNEM_SIZE];
  int ii = 0;

  memset (temp, END_OF_INSN, MAX_MNEM_SIZE);
  mnem_p = mnemonic;

  while ((*mnem_p = mnemonic_chars[(unsigned char) *l]) != 0)
    {
      ii++;
      mnem_p++;
      if (mnem_p >= mnemonic + MAX_MNEM_SIZE)
	{
	  as_bad (_("no such instruction: `%s'"), token_start);
	  return NULL;
	}
      l++;
    }

  if (!is_space_char (*l) && *l != END_OF_INSN)
    {
      as_bad (_("invalid character %s in mnemonic"), l);
      return NULL;
    }

  while (ii)
    {
      temp[ii - 1] = toupper ((char) mnemonic[ii - 1]);
      ii--;
    }

  LS_processing (temp);

  if (i.Instr_Prefix != 0 && is_a_LSinstr (temp))
    /* Skip the optional L-S.  */
    memcpy (temp, temp + 1, MAX_MNEM_SIZE);

  /* Look up instruction (or prefix) via hash table.  */
  current_templates = (MAXQ20_OPCODES *) hash_find (op_hash, temp);

  if (current_templates != NULL)
    return l;

  as_bad (_("no such instruction: `%s'"), token_start);
  return NULL;
}

/* Function to calculate x to the power of y.
   Just to avoid including the math libraries.  */

static int
pwr (int x, int y)
{
  int k, ans = 1;

  for (k = 0; k < y; k++)
    ans *= x;

  return ans;
}

static reg_entry *
parse_reg_by_index (char *imm_start)
{
  int k = 0, mid = 0, rid = 0, val = 0, j = 0;
  char temp[4] = { 0 };
  reg_entry *reg = NULL;

  do
    {
      if (isdigit (imm_start[k]))
	temp[k] = imm_start[k] - '0';

      else if (isalpha (imm_start[k])
	       && (imm_start[k] = tolower (imm_start[k])) < 'g')
	temp[k] = 10 + (int) (imm_start[k] - 'a');

      else if (imm_start[k] == 'h')
	break;

      else if (imm_start[k] == END_OF_INSN)
	{
	  imm_start[k] = 'd';
	  break;
	}

      else
	return NULL;		/* not a hex digit */

      k++;
    }
  while (imm_start[k] != '\n');

  switch (imm_start[k])
    {
    case 'h':
      for (j = 0; j < k; j++)
	val += temp[j] * pwr (16, k - j - 1);
      break;

    case 'd':
      for (j = 0; j < k; j++)
	{
	  if (temp[j] > 9)
	    return NULL;	/* not a number */

	  val += temp[j] * pwr (10, k - j - 1);
	  break;
	}
    }

  /* Get the module and register id's.  */
  mid = val & 0x0f;
  rid = (val >> 4) & 0x0f;

  if (mid < 6)
    {
      /* Search the pheripheral reg table.  */
      for (j = 0; j < num_of_reg; j++)
	{
	  if (new_reg_table[j].opcode == val)
	    {
	      reg = (reg_entry *) & new_reg_table[j];
	      break;
	    }
	}
    }

  else
    {
      /* Search the system register table.  */
      j = 0;

      while (system_reg_table[j].reg_name != NULL)
	{
	  if (system_reg_table[j].opcode == val)
	    {
	      reg = (reg_entry *) & system_reg_table[j];
	      break;
	    }
	  j++;
	}
    }

  if (reg == NULL)
    {
      as_bad (_("Invalid register value %s"), imm_start);
      return reg;
    }

#if CHANGE_PFX
  if (this_operand == 0 && reg != NULL)
    {
      if (reg->Mod_index > 7)
	i.prefix = 2;
      else
	i.prefix = 0;
    }
#endif
  return (reg_entry *) reg;
}

/* REG_STRING starts *before* REGISTER_PREFIX.  */

static reg_entry *
parse_register (char *reg_string, char **end_op)
{
  char *s = reg_string;
  char *p = NULL;
  char reg_name_given[MAX_REG_NAME_SIZE + 1];
  reg_entry *r = NULL;

  r = NULL;
  p = NULL;

  /* Skip possible REGISTER_PREFIX and possible whitespace.  */
  if (is_space_char (*s))
    ++s;

  p = reg_name_given;
  while ((*p++ = register_chars[(unsigned char) *s]) != '\0')
    {
      if (p >= reg_name_given + MAX_REG_NAME_SIZE)
	return (reg_entry *) NULL;
      s++;
    }

  *end_op = s;

  r = (reg_entry *) hash_find (reg_hash, reg_name_given);

#if CHANGE_PFX
  if (this_operand == 0 && r != NULL)
    {
      if (r->Mod_index > 7)
	i.prefix = 2;
      else
	i.prefix = 0;
    }
#endif
  return r;
}

static reg_bit *
parse_register_bit (char *reg_string, char **end_op)
{
  const char *s = reg_string;
  short k = 0;
  char diff = 0;
  reg_bit *rb = NULL;
  reg_entry *r = NULL;
  bit_name *b = NULL;
  char temp_bitname[MAX_REG_NAME_SIZE + 2];
  char temp[MAX_REG_NAME_SIZE + 1];

  memset (&temp, '\0', (MAX_REG_NAME_SIZE + 1));
  memset (&temp_bitname, '\0', (MAX_REG_NAME_SIZE + 2));

  diff = 0;
  r = NULL;
  rb = NULL;
  rb = xmalloc (sizeof (reg_bit));
  rb->reg = xmalloc (sizeof (reg_entry));
  k = 0;

  /* For supporting bit names.  */
  b = (bit_name *) hash_find (bit_hash, reg_string);

  if (b != NULL)
    {
      *end_op = reg_string + strlen (reg_string);
      strcpy (temp_bitname, b->reg_bit);
      s = temp_bitname;
    }

  if (strchr (s, '.'))
    {
      while (*s != '.')
	{
	  if (*s == '\0')
	    return NULL;
	  temp[k] = *s++;

	  k++;
	}
      temp[k] = '\0';
    }

  if ((r = parse_register (temp, end_op)) == NULL)
    return NULL;

  rb->reg = r;

  /* Skip the "."  */
  s++;

  if (isdigit ((char) *s))
    rb->bit = atoi (s);
  else if (isalpha ((char) *s))
    {
      rb->bit = (char) *s - 'a';
      rb->bit += 10;
      if (rb->bit > 15)
	{
	  as_bad (_("Invalid bit number : '%c'"), (char) *s);
	  return NULL;
	}
    }

  if (b != NULL)
    diff = strlen (temp_bitname) - strlen (temp) - 1;
  else
    diff = strlen (reg_string) - strlen (temp) - 1;

  if (*(s + diff) != '\0')
    {
      as_bad (_("Illegal character after operand '%s'"), reg_string);
      return NULL;
    }

  return rb;
}

static void
pfx_for_imm_val (int arg)
{
  if (i.prefix == -1)
    return;

  if (i.prefix == 0 && arg == 0 && PFX_INSN[1] == 0 && !(i.data_operands))
    return;

  if (!(i.prefix < 0) && !(i.prefix > 7))
    PFX_INSN[0] = (i.prefix << 4) | PFX0;

  if (!PFX_INSN[1])
    PFX_INSN[1] = arg;

}

static int
maxq20_immediate (char *imm_start)
{
  int val = 0, val_pfx = 0;
  char sign_val = 0;
  int k = 0, j;
  int temp[4] = { 0 };

  imm_start++;

  if (imm_start[1] == '\0' && (imm_start[0] == '0' || imm_start[0] == '1')
      && (this_operand == 1 && ((i.types[0] == BIT || i.types[0] == FLAG))))
    {
      val = imm_start[0] - '0';
      i.imm_bit_operands++;
      i.types[this_operand] = IMMBIT;
      i.maxq20_op[this_operand].imms = (char) val;
#if CHANGE_PFX
      if (i.prefix == 2)
	pfx_for_imm_val (0);
#endif
      return 1;
    }

  /* Check For Sign Charcater.  */
  sign_val = 0;

  do
    {
      if (imm_start[k] == '-' && k == 0)
	sign_val = -1;

      else if (imm_start[k] == '+' && k == 0)
	sign_val = 1;

      else if (isdigit (imm_start[k]))
	temp[k] = imm_start[k] - '0';

      else if (isalpha (imm_start[k])
	       && (imm_start[k] = tolower (imm_start[k])) < 'g')
	temp[k] = 10 + (int) (imm_start[k] - 'a');

      else if (imm_start[k] == 'h')
	break;

      else if (imm_start[k] == '\0')
	{
	  imm_start[k] = 'd';
	  break;
	}
      else
	{
	  as_bad (_("Invalid Character in immediate Value : %c"),
		  imm_start[k]);
	  return 0;
	}
      k++;
    }
  while (imm_start[k] != '\n');

  switch (imm_start[k])
    {
    case 'h':
      for (j = (sign_val ? 1 : 0); j < k; j++)
	val += temp[j] * pwr (16, k - j - 1);
      break;

    case 'd':
      for (j = (sign_val ? 1 : 0); j < k; j++)
	{
	  if (temp[j] > 9)
	    {
	      as_bad (_("Invalid Character in immediate value : %c"),
		      imm_start[j]);
	      return 0;
	    }
	  val += temp[j] * pwr (10, k - j - 1);
	}
    }

  if (!sign_val)
    sign_val = 1;

  /* Now over here the value val stores the 8 bit/16 bit value. We will put a 
     check if we are moving a 16 bit immediate value into an 8 bit register. 
     In that case we will generate a warning and move only the lower 8 bits */
  if (val > 65535)
    {
      as_bad (_("Immediate value greater than 16 bits"));
      return 0;
    }

  val = val * sign_val;

  /* If it is a stack pointer and the value is greater than the maximum
     permissible size */
  if (this_operand == 1)
    {
      if ((val * sign_val) > MAX_STACK && i.types[0] == REG
	  && !strcmp (i.maxq20_op[0].reg->reg_name, "SP"))
	{
	  as_warn (_
		   ("Attempt to move a value in the stack pointer greater than the size of the stack"));
	  val = val & MAX_STACK;
	}

      /* Check the range for 8 bit registers.  */
      else if (((val * sign_val) > 0xFF) && (i.types[0] == REG)
	       && (i.maxq20_op[0].reg->rtype == Reg_8W))
	{
	  as_warn (_
		   ("Attempt to move 16 bit value into an 8 bit register.Truncating..\n"));
	  val = val & 0xfe;
	}

      else if (((sign_val == -1) || (val > 0xFF)) && (i.types[0] == REG)
	       && (i.maxq20_op[0].reg->rtype == Reg_8W))
	{
	  val_pfx = val >> 8;
	  val = ((val) & 0x00ff);
	  SET_PFX_ARG (val_pfx);
	  i.maxq20_op[this_operand].imms = (char) val;
	}

      else if ((val <= 0xff) && (i.types[0] == REG)
	       && (i.maxq20_op[0].reg->rtype == Reg_8W))
	i.maxq20_op[this_operand].imms = (char) val;


      /* Check for 16 bit registers.  */
      else if (((sign_val == -1) || val > 0xFE) && i.types[0] == REG
	       && i.maxq20_op[0].reg->rtype == Reg_16W)
	{
	  /* Add PFX for any negative value -> 16bit register.  */
	  val_pfx = val >> 8;
	  val = ((val) & 0x00ff);
	  SET_PFX_ARG (val_pfx);
	  i.maxq20_op[this_operand].imms = (char) val;
	}

      else if (val < 0xFF && i.types[0] == REG
	       && i.maxq20_op[0].reg->rtype == Reg_16W)
	{
	  i.maxq20_op[this_operand].imms = (char) val;
	}

      /* All the immediate memory access - no PFX.  */
      else if (i.types[0] == MEM)
	{
	  if ((sign_val == -1) || val > 0xFE)
	    {
	      val_pfx = val >> 8;
	      val = ((val) & 0x00ff);
	      SET_PFX_ARG (val_pfx);
	      i.maxq20_op[this_operand].imms = (char) val;
	    }
	  else
	    i.maxq20_op[this_operand].imms = (char) val;
	}

      /* Special handling for immediate jumps like jump nz, #03h etc.  */
      else if (val < 0xFF && i.types[0] == FLAG)
	i.maxq20_op[this_operand].imms = (char) val;

      else if ((((sign_val == -1) || val > 0xFE)) && i.types[0] == FLAG)
	{
	  val_pfx = val >> 8;
	  val = ((val) & 0x00ff);
	  SET_PFX_ARG (val_pfx);
	  i.maxq20_op[this_operand].imms = (char) val;
	}
      else
	{
	  as_bad (_("Invalid immediate move operation"));
	  return 0;
	}
    }
  else
    {
      /* All the instruction with operation on ACC: like ADD src, etc.  */
      if ((sign_val == -1) || val > 0xFE)
	{
	  val_pfx = val >> 8;
	  val = ((val) & 0x00ff);
	  SET_PFX_ARG (val_pfx);
	  i.maxq20_op[this_operand].imms = (char) val;
	}
      else
	i.maxq20_op[this_operand].imms = (char) val;
    }

  i.imm_operands++;
  return 1;
}

static int
extract_int_val (const char *imm_start)
{
  int k, j, val;
  char sign_val;
  int temp[4];

  k = 0;
  j = 0;
  val = 0;
  sign_val = 0;
  do
    {
      if (imm_start[k] == '-' && k == 0)
	sign_val = -1;

      else if (imm_start[k] == '+' && k == 0)
	sign_val = 1;

      else if (isdigit (imm_start[k]))
	temp[k] = imm_start[k] - '0';

      else if (isalpha (imm_start[k]) && (tolower (imm_start[k])) < 'g')
	temp[k] = 10 + (int) (tolower (imm_start[k]) - 'a');

      else if (tolower (imm_start[k]) == 'h')
	break;

      else if ((imm_start[k] == '\0') || (imm_start[k] == ']'))
	/* imm_start[k]='d'; */
	break;

      else
	{
	  as_bad (_("Invalid Character in immediate Value : %c"),
		  imm_start[k]);
	  return 0;
	}
      k++;
    }
  while (imm_start[k] != '\n');

  switch (imm_start[k])
    {
    case 'h':
      for (j = (sign_val ? 1 : 0); j < k; j++)
	val += temp[j] * pwr (16, k - j - 1);
      break;

    default:
      for (j = (sign_val ? 1 : 0); j < k; j++)
	{
	  if (temp[j] > 9)
	    {
	      as_bad (_("Invalid Character in immediate value : %c"),
		      imm_start[j]);
	      return 0;
	    }
	  val += temp[j] * pwr (10, k - j - 1);
	}
    }

  if (!sign_val)
    sign_val = 1;

  return val * sign_val;
}

static char
check_for_parse (const char *line)
{
  int val;

  if (*(line + 1) == '[')
    {
      do
	{
	  line++;
	  if ((*line == '-') || (*line == '+'))
	    break;
	}
      while (!is_space_char (*line));

      if ((*line == '-') || (*line == '+'))
	val = extract_int_val (line);
      else
	val = extract_int_val (line + 1);

      INSERT_BUFFER[0] = 0x3E;
      INSERT_BUFFER[1] = val;

      return 1;
    }

  return 0;
}

static mem_access *
maxq20_mem_access (char *mem_string, char **end_op)
{
  char *s = mem_string;
  char *p;
  char mem_name_given[MAX_MEM_NAME_SIZE + 1];
  mem_access *m;

  m = NULL;

  /* Skip possible whitespace.  */
  if (is_space_char (*s))
    ++s;

  p = mem_name_given;
  while ((*p++ = register_chars[(unsigned char) *s]) != '\0')
    {
      if (p >= mem_name_given + MAX_MEM_NAME_SIZE)
	return (mem_access *) NULL;
      s++;
    }

  *end_op = s;

  m = (mem_access *) hash_find (mem_hash, mem_name_given);

  return m;
}

/* This function checks whether the operand is a variable in the data segment 
   and if so, it returns its symbol entry from the symbol table.  */

static symbolS *
maxq20_data (char *op_string)
{
  symbolS *symbolP;
  symbolP = symbol_find (op_string);

  if (symbolP != NULL
      && S_GET_SEGMENT (symbolP) != now_seg
      && S_GET_SEGMENT (symbolP) !=
#ifdef BFD_ASSEMBLER
      bfd_und_section_ptr
#else
      SEG_UNKNOWN
#endif
      )
    {
      int val_pfx;

#ifdef BFD_ASSEMBLER
      val_pfx =	0;
#else
      val_pfx = (symbolP->sy_value.X_add_number) >> 8;
#endif

      /* In case we do not want to always include the prefix instruction and
         let the loader handle the job or in case of a 8 bit addressing mode, 
         we will just check for val_pfx to be equal to zero and then load the 
         prefix instruction. Otherwise no prefix instruction needs to be
         loaded.  */
      /* The prefix register will have to be loaded automatically as we have 
	 a 16 bit addressing field.  */
      pfx_for_imm_val (val_pfx);
      return symbolP;
    }

  return NULL;
}

static int
maxq20_displacement (char *disp_start, char *disp_end)
{
  expressionS *exp;
  segT exp_seg = 0;
  char *save_input_line_pointer;
#ifndef LEX_AT
  char *gotfree_input_line;
#endif

  gotfree_input_line = NULL;
  exp = &disp_expressions;
  i.maxq20_op[this_operand].disps = exp;
  i.disp_operands++;
  save_input_line_pointer = input_line_pointer;
  input_line_pointer = disp_start;

  END_STRING_AND_SAVE (disp_end);

#ifndef LEX_AT
  /* gotfree_input_line = lex_got (&i.reloc[this_operand], NULL); if
     (gotfree_input_line) input_line_pointer = gotfree_input_line; */
#endif
  exp_seg = expression (exp);

  SKIP_WHITESPACE ();
  if (*input_line_pointer)
    as_bad (_("junk `%s' after expression"), input_line_pointer);
#if GCC_ASM_O_HACK
  RESTORE_END_STRING (disp_end + 1);
#endif
  RESTORE_END_STRING (disp_end);
  input_line_pointer = save_input_line_pointer;
#ifndef LEX_AT
  if (gotfree_input_line)
    free (gotfree_input_line);
#endif
  if (exp->X_op == O_absent || exp->X_op == O_big)
    {
      /* Missing or bad expr becomes absolute 0.  */
      as_bad (_("missing or invalid displacement expression `%s' taken as 0"),
	      disp_start);
      exp->X_op = O_constant;
      exp->X_add_number = 0;
      exp->X_add_symbol = (symbolS *) 0;
      exp->X_op_symbol = (symbolS *) 0;
    }
#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT))

  if (exp->X_op != O_constant
#ifdef BFD_ASSEMBLER
      && OUTPUT_FLAVOR == bfd_target_aout_flavour
#endif
      && exp_seg != absolute_section
      && exp_seg != text_section
      && exp_seg != data_section
      && exp_seg != bss_section && exp_seg != undefined_section
#ifdef BFD_ASSEMBLER
      && !bfd_is_com_section (exp_seg)
#endif
    )
    {
#ifdef BFD_ASSEMBLER
      as_bad (_("unimplemented segment %s in operand"), exp_seg->name);
#else
      as_bad (_("unimplemented segment type %d in operand"), exp_seg);
#endif
      return 0;
    }
#endif
  i.maxq20_op[this_operand].disps = exp;
  return 1;
}

/* Parse OPERAND_STRING into the maxq20_insn structure I.
   Returns non-zero on error.  */

static int
maxq20_operand (char *operand_string)
{
  reg_entry *r = NULL;
  reg_bit *rb = NULL;
  mem_access *m = NULL;
  char *end_op = NULL;
  symbolS *sym = NULL;
  char *base_string = NULL;
  int ii = 0;
  /* Start and end of displacement string expression (if found).  */
  char *displacement_string_start = NULL;
  char *displacement_string_end = NULL;
  /* This maintains the  case sentivness.  */
  char case_str_op_string[MAX_OPERAND_SIZE + 1];
  char str_op_string[MAX_OPERAND_SIZE + 1];
  char *org_case_op_string = case_str_op_string;
  char *op_string = str_op_string;

  
  memset (op_string, END_OF_INSN, (MAX_OPERAND_SIZE + 1));
  memset (org_case_op_string, END_OF_INSN, (MAX_OPERAND_SIZE + 1));

  memcpy (op_string, operand_string, strlen (operand_string) + 1);
  memcpy (org_case_op_string, operand_string, strlen (operand_string) + 1);

  ii = strlen (operand_string) + 1;

  if (ii > MAX_OPERAND_SIZE)
    {
      as_bad (_("Size of Operand '%s' greater than %d"), op_string,
	      MAX_OPERAND_SIZE);
      return 0;
    }

  while (ii)
    {
      op_string[ii - 1] = toupper ((char) op_string[ii - 1]);
      ii--;
    }

  if (is_space_char (*op_string))
    ++op_string;

  if (isxdigit (operand_string[0]))
    {
      /* Now the operands can start with an Integer.  */
      r = parse_reg_by_index (op_string);
      if (r != NULL)
	{
	  if (is_space_char (*op_string))
	    ++op_string;
	  i.types[this_operand] = REG;	/* Set the type.  */
	  i.maxq20_op[this_operand].reg = r;	/* Set the Register value.  */
	  i.reg_operands++;
	  return 1;
	}

      /* Get the origanal string.  */
      memcpy (op_string, operand_string, strlen (operand_string) + 1);
      ii = strlen (operand_string) + 1;

      while (ii)
	{
	  op_string[ii - 1] = toupper ((char) op_string[ii - 1]);
	  ii--;
	}
    }

  /* Check for flags.  */
  if (!strcmp (op_string, "Z"))
    {
      if (is_space_char (*op_string))
	++op_string;

      i.types[this_operand] = FLAG;		/* Set the type.  */
      i.maxq20_op[this_operand].flag = FLAG_Z;	/* Set the Register value.  */

      i.flag_operands++;

      return 1;
    }

  else if (!strcmp (op_string, "NZ"))
    {
      if (is_space_char (*op_string))
	++op_string;

      i.types[this_operand] = FLAG;		/* Set the type.  */
      i.maxq20_op[this_operand].flag = FLAG_NZ;	/* Set the Register value.  */
      i.flag_operands++;
      return 1;
    }

  else if (!strcmp (op_string, "NC"))
    {
      if (is_space_char (*op_string))
	++op_string;

      i.types[this_operand] = FLAG;		/* Set the type.  */
      i.maxq20_op[this_operand].flag = FLAG_NC;	/* Set the Register value.  */
      i.flag_operands++;
      return 1;
    }

  else if (!strcmp (op_string, "E"))
    {
      if (is_space_char (*op_string))
	++op_string;

      i.types[this_operand] = FLAG;		/* Set the type.  */
      i.maxq20_op[this_operand].flag = FLAG_E;	/* Set the Register value.  */

      i.flag_operands++;

      return 1;
    }

  else if (!strcmp (op_string, "S"))
    {
      if (is_space_char (*op_string))
	++op_string;

      i.types[this_operand] = FLAG;	/* Set the type.  */
      i.maxq20_op[this_operand].flag = FLAG_S;	/* Set the Register value.  */

      i.flag_operands++;

      return 1;
    }

  else if (!strcmp (op_string, "C"))
    {
      if (is_space_char (*op_string))
	++op_string;

      i.types[this_operand] = FLAG;	/* Set the type.  */
      i.maxq20_op[this_operand].flag = FLAG_C;	/* Set the Register value.  */

      i.flag_operands++;

      return 1;
    }

  else if (!strcmp (op_string, "NE"))
    {

      if (is_space_char (*op_string))
	++op_string;

      i.types[this_operand] = FLAG;	/* Set the type.  */

      i.maxq20_op[this_operand].flag = FLAG_NE;	/* Set the Register value.  */

      i.flag_operands++;

      return 1;
    }

  /* CHECK FOR REGISTER BIT */
  else if ((rb = parse_register_bit (op_string, &end_op)) != NULL)
    {
      op_string = end_op;

      if (is_space_char (*op_string))
	++op_string;

      i.types[this_operand] = BIT;

      i.maxq20_op[this_operand].r_bit = rb;

      i.bit_operands++;

      return 1;
    }

  else if (*op_string == IMMEDIATE_PREFIX)	/* FOR IMMEDITE.  */
    {
      if (is_space_char (*op_string))
	++op_string;

      i.types[this_operand] = IMM;

      if (!maxq20_immediate (op_string))
	{
	  as_bad (_("illegal immediate operand '%s'"), op_string);
	  return 0;
	}
      return 1;
    }

  else if (*op_string == ABSOLUTE_PREFIX || !strcmp (op_string, "NUL"))
    {
     if (is_space_char (*op_string))
	++op_string;

      /* For new requiremnt of copiler of for, @(BP,cons).  */
      if (check_for_parse (op_string))
	{
	  memset (op_string, '\0', strlen (op_string) + 1);
	  memcpy (op_string, "@BP[OFFS]\0", 11);
	}

      i.types[this_operand] = MEM;

      if ((m = maxq20_mem_access (op_string, &end_op)) == NULL)
	{
	  as_bad (_("Invalid operand for memory access '%s'"), op_string);
	  return 0;
	}
      i.maxq20_op[this_operand].mem = m;

      i.mem_operands++;

      return 1;
    }

  else if ((r = parse_register (op_string, &end_op)) != NULL)	/* Check for register.  */
    {
      op_string = end_op;

      if (is_space_char (*op_string))
	++op_string;

      i.types[this_operand] = REG;	/* Set the type.  */
      i.maxq20_op[this_operand].reg = r;	/* Set the Register value.  */
      i.reg_operands++;
      return 1;
    }

  if (this_operand == 1)
    {
      /* Changed for orginal case of data refrence on 30 Nov 2003.  */
      /* The operand can either be a data reference or a symbol reference.  */
      if ((sym = maxq20_data (org_case_op_string)) != NULL)	/* Check for data memory.  */
	{
	  while (is_space_char (*op_string))
	    ++op_string;

	  /* Set the type of the operand.  */
	  i.types[this_operand] = DATA;

	  /* Set the value of the data.  */
	  i.maxq20_op[this_operand].data = sym;
	  i.data_operands++;

	  return 1;
	}

      else if (is_digit_char (*op_string) || is_identifier_char (*op_string))
	{
	  /* This is a memory reference of some sort. char *base_string;
	     Start and end of displacement string expression (if found). char 
	     *displacement_string_start; char *displacement_string_end.  */
	  base_string = org_case_op_string + strlen (org_case_op_string);

	  --base_string;
	  if (is_space_char (*base_string))
	    --base_string;

	  /* If we only have a displacement, set-up for it to be parsed
	     later.  */
	  displacement_string_start = org_case_op_string;
	  displacement_string_end = base_string + 1;
	  if (displacement_string_start != displacement_string_end)
	    {
	      if (!maxq20_displacement (displacement_string_start,
					displacement_string_end))
		{
		  as_bad (_("illegal displacement operand "));
		  return 0;
		}
	      /* A displacement operand found.  */
	      i.types[this_operand] = DISP;	/* Set the type.  */
	      return 1;
	    }
	}
    }
  
  /* Check for displacement.  */
  else if (is_digit_char (*op_string) || is_identifier_char (*op_string))
    {
      /* This is a memory reference of some sort. char *base_string;
         Start and end of displacement string expression (if found). char
         *displacement_string_start; char *displacement_string_end;  */
      base_string = org_case_op_string + strlen (org_case_op_string);

      --base_string;
      if (is_space_char (*base_string))
	--base_string;

      /* If we only have a displacement, set-up for it to be parsed later.  */
      displacement_string_start = org_case_op_string;
      displacement_string_end = base_string + 1;
      if (displacement_string_start != displacement_string_end)
	{
	  if (!maxq20_displacement (displacement_string_start,
				    displacement_string_end))
	    return 0;
	  /* A displacement operand found.  */
	  i.types[this_operand] = DISP;	/* Set the type.  */
	}
    }
  return 1;
}

/* Parse_operand takes as input instruction and operands and Parse operands
   and makes entry in the template.  */

static char *
parse_operands (char *l, const char *mnemonic)
{
  char *token_start;

  /* 1 if operand is pending after ','.  */
  short int expecting_operand = 0;

  /* Non-zero if operand parens not balanced.  */
  short int paren_not_balanced;

  int operand_ok;

  /* For Overcoming Warning of unused variable.  */
  if (mnemonic)
    operand_ok = 0;

  while (*l != END_OF_INSN)
    {
      /* Skip optional white space before operand.  */
      if (is_space_char (*l))
	++l;

      if (!is_operand_char (*l) && *l != END_OF_INSN)
	{
	  as_bad (_("invalid character %c before operand %d"),
		  (char) (*l), i.operands + 1);
	  return NULL;
	}
      token_start = l;

      paren_not_balanced = 0;
      while (paren_not_balanced || *l != ',')
	{
	  if (*l == END_OF_INSN)
	    {
	      if (paren_not_balanced)
		{
		  as_bad (_("unbalanced brackets in operand %d."),
			  i.operands + 1);
		  return NULL;
		}

	      break;
	    }
	  else if (!is_operand_char (*l) && !is_space_char (*l))
	    {
	      as_bad (_("invalid character %c in operand %d"),
		      (char) (*l), i.operands + 1);
	      return NULL;
	    }
	  if (*l == '[')
	    ++paren_not_balanced;
	  if (*l == ']')
	    --paren_not_balanced;
	  l++;
	}

      if (l != token_start)
	{
	  /* Yes, we've read in another operand.  */
	  this_operand = i.operands++;
	  if (i.operands > MAX_OPERANDS)
	    {
	      as_bad (_("spurious operands; (%d operands/instruction max)"),
		      MAX_OPERANDS);
	      return NULL;
	    }

	  /* Now parse operand adding info to 'i' as we go along.  */
	  END_STRING_AND_SAVE (l);

	  operand_ok = maxq20_operand (token_start);

	  RESTORE_END_STRING (l);

	  if (!operand_ok)
	    return NULL;
	}
      else
	{
	  if (expecting_operand)
	    {
	    expecting_operand_after_comma:
	      as_bad (_("expecting operand after ','; got nothing"));
	      return NULL;
	    }
	}

      if (*l == ',')
	{
	  if (*(++l) == END_OF_INSN)
	    /* Just skip it, if it's \n complain.  */
	    goto expecting_operand_after_comma;

	  expecting_operand = 1;
	}
    }

  return l;
}

static int
match_operands (int type, MAX_ARG_TYPE flag_type, MAX_ARG_TYPE arg_type,
		int op_num)
{
  switch (type)
    {
    case REG:
      if ((arg_type & A_REG) == A_REG)
	return 1;
      break;
    case IMM:
      if ((arg_type & A_IMM) == A_IMM)
	return 1;
      break;
    case IMMBIT:
      if ((arg_type & A_BIT_0) == A_BIT_0 && (i.maxq20_op[op_num].imms == 0))
	return 1;
      else if ((arg_type & A_BIT_1) == A_BIT_1
	       && (i.maxq20_op[op_num].imms == 1))
	return 1;
      break;
    case MEM:
      if ((arg_type & A_MEM) == A_MEM)
	return 1;
      break;

    case FLAG:
      if ((arg_type & flag_type) == flag_type)
	return 1;

      break;

    case BIT:
      if ((arg_type & ACC_BIT) == ACC_BIT && !strcmp (i.maxq20_op[op_num].r_bit->reg->reg_name, "ACC"))
	return 1;
      else if ((arg_type & SRC_BIT) == SRC_BIT && (op_num == 1))
	return 1;
      else if ((op_num == 0) && (arg_type & DST_BIT) == DST_BIT)
	return 1;
      break;
    case DISP:
      if ((arg_type & A_DISP) == A_DISP)
	return 1;
    case DATA:
      if ((arg_type & A_DATA) == A_DATA)
	return 1;
    case BIT_BUCKET:
      if ((arg_type & A_BIT_BUCKET) == A_BIT_BUCKET)
	return 1;
    }
  return 0;
}

static int
match_template (void)
{
  /* Points to template once we've found it.  */
  const MAXQ20_OPCODE_INFO *t;
  char inv_oper;
  inv_oper = 0;

  for (t = current_templates->start; t < current_templates->end; t++)
    {
      /* Must have right number of operands.  */
      if (i.operands != t->op_number)
	continue;
      else if (!t->op_number)
	break;

      switch (i.operands)
	{
	case 2:
	  if (!match_operands (i.types[1], i.maxq20_op[1].flag, t->arg[1], 1))
	    {
	      inv_oper = 1;
	      continue;
	    }
	case 1:
	  if (!match_operands (i.types[0], i.maxq20_op[0].flag, t->arg[0], 0))
	    {
	      inv_oper = 2;
	      continue;
	    }
	}
      break;
    }

  if (t == current_templates->end)
    {
      /* We found no match.  */
      as_bad (_("operand %d is invalid for `%s'"),
	      inv_oper, current_templates->start->name);
      return 0;
    }

  /* Copy the template we have found.  */
  i.op = *t;
  return 1;
}

/* This function filters out the various combinations of operands which are
   not allowed for a particular instruction.  */

static int
match_filters (void)
{
  /* Now we have at our disposal the instruction i. We will be using the
     following fields i.op.name : This is the mnemonic name. i.types[2] :
     These are the types of the operands (REG/IMM/DISP/MEM/BIT/FLAG/IMMBIT)
     i.maxq20_op[2] : This contains the specific info of the operands.  */

  /* Our first filter : NO ALU OPERATIONS CAN HAVE THE ACTIVE ACCUMULATOR AS
     SOURCE.  */
  if (!strcmp (i.op.name, "AND") || !strcmp (i.op.name, "OR")
      || !strcmp (i.op.name, "XOR") || !strcmp (i.op.name, "ADD")
      || !strcmp (i.op.name, "ADDC") || !strcmp (i.op.name, "SUB")
      || !strcmp (i.op.name, "SUBB"))
    {
      if (i.types[0] == REG)
	{
	  if (i.maxq20_op[0].reg->Mod_name == 0xa)
	    {
	      as_bad (_
		      ("The Accumulator cannot be used as a source in ALU instructions\n"));
	      return 0;
	    }
	}
    }

  if (!strcmp (i.op.name, "MOVE") && (i.types[0] == MEM || i.types[1] == MEM)
      && i.operands == 2)
    {
      mem_access_syntax *mem_op = NULL;

      if (i.types[0] == MEM)
	{
	  mem_op =
	    (mem_access_syntax *) hash_find (mem_syntax_hash,
					     i.maxq20_op[0].mem->name);
	  if ((mem_op->type == SRC) && mem_op)
	    {
	      as_bad (_("'%s' operand cant be used as destination in %s"),
		      mem_op->name, i.op.name);
	      return 0;
	    }
	  else if ((mem_op->invalid_op != NULL) && (i.types[1] == MEM)
		   && mem_op)
	    {
	      int k = 0;

	      for (k = 0; k < 5 || !mem_op->invalid_op[k]; k++)
		{
		  if (mem_op->invalid_op[k] != NULL)
		    if (!strcmp
			(mem_op->invalid_op[k], i.maxq20_op[1].mem->name))
		      {
			as_bad (_
				("Invalid Instruction '%s' operand cant be used with %s"),
				mem_op->name, i.maxq20_op[1].mem->name);
			return 0;
		      }
		}
	    }
	}

      if (i.types[1] == MEM)
	{
	  mem_op = NULL;
	  mem_op =
	    (mem_access_syntax *) hash_find (mem_syntax_hash,
					     i.maxq20_op[1].mem->name);
	  if (mem_op->type == DST && mem_op)
	    {
	      as_bad (_("'%s' operand cant be used as source in %s"),
		      mem_op->name, i.op.name);
	      return 0;
	    }
	  else if (mem_op->invalid_op != NULL && i.types[0] == MEM && mem_op)
	    {
	      int k = 0;

	      for (k = 0; k < 5 || !mem_op->invalid_op[k]; k++)
		{
		  if (mem_op->invalid_op[k] != NULL)
		    if (!strcmp
			(mem_op->invalid_op[k], i.maxq20_op[0].mem->name))
		      {
			as_bad (_
				("Invalid Instruction '%s' operand cant be used with %s"),
				mem_op->name, i.maxq20_op[0].mem->name);
			return 0;
		      }
		}
	    }
	  else if (i.types[0] == REG
		   && !strcmp (i.maxq20_op[0].reg->reg_name, "OFFS")
		   && mem_op)
	    {
	      if (!strcmp (mem_op->name, "@BP[OFFS--]")
		  || !strcmp (mem_op->name, "@BP[OFFS++]"))
		{
		  as_bad (_
			  ("Invalid Instruction '%s' operand cant be used with %s"),
			  mem_op->name, i.maxq20_op[0].mem->name);
		  return 0;
		}
	    }
	}
    }

  /* Added for SRC and DST in one operand instructioni i.e OR @--DP[1] added
     on 10-March-2004.  */
  if ((i.types[0] == MEM) && (i.operands == 1)
      && !(!strcmp (i.op.name, "POP") || !strcmp (i.op.name, "POPI")))
    {
      mem_access_syntax *mem_op = NULL;

      if (i.types[0] == MEM)
	{
	  mem_op =
	    (mem_access_syntax *) hash_find (mem_syntax_hash,
					     i.maxq20_op[0].mem->name);
	  if (mem_op->type == DST && mem_op)
	    {
	      as_bad (_("'%s' operand cant be used as source in %s"),
		      mem_op->name, i.op.name);
	      return 0;
	    }
	}
    }

  if (i.operands == 2 && i.types[0] == IMM)
    {
      as_bad (_("'%s' instruction cant have first operand as Immediate vale"),
	      i.op.name);
      return 0;
    }

  /* Our second filter : SP or @SP-- cannot be used with PUSH or POP */
  if (!strcmp (i.op.name, "PUSH") || !strcmp (i.op.name, "POP")
      || !strcmp (i.op.name, "POPI"))
    {
      if (i.types[0] == REG)
	{
	  if (!strcmp (i.maxq20_op[0].reg->reg_name, "SP"))
	    {
	      as_bad (_("SP cannot be used with %s\n"), i.op.name);
	      return 0;
	    }
	}
      else if (i.types[0] == MEM
	       && !strcmp (i.maxq20_op[0].mem->name, "@SP--"))
	{
	  as_bad (_("@SP-- cannot be used with PUSH\n"));
	  return 0;
	}
    }

  /* This filter checks that two memory references using DP's cannot be used
     together in an instruction */
  if (!strcmp (i.op.name, "MOVE") && i.mem_operands == 2)
    {
      if (strlen (i.maxq20_op[0].mem->name) != 6 ||
	  strcmp (i.maxq20_op[0].mem->name, i.maxq20_op[1].mem->name))
	{
	  if (!strncmp (i.maxq20_op[0].mem->name, "@DP", 3)
	      && !strncmp (i.maxq20_op[1].mem->name, "@DP", 3))
	    {
	      as_bad (_
		      ("Operands either contradictory or use the data bus in read/write state together"));
	      return 0;
	    }

	  if (!strncmp (i.maxq20_op[0].mem->name, "@SP", 3)
	      && !strncmp (i.maxq20_op[1].mem->name, "@SP", 3))
	    {
	      as_bad (_
		      ("Operands either contradictory or use the data bus in read/write state together"));
	      return 0;
	    }
	}
      if ((i.maxq20_op[1].mem != NULL)
	  && !strncmp (i.maxq20_op[1].mem->name, "NUL", 3))
	{
	  as_bad (_("MOVE Cant Use NUL as SRC"));
	  return 0;
	}
    }

  /* This filter checks that contradictory movement between DP register and
     Memory access using DP followed by increment or decrement.  */

  if (!strcmp (i.op.name, "MOVE") && i.mem_operands == 1
      && i.reg_operands == 1)
    {
      int memnum, regnum;

      memnum = (i.types[0] == MEM) ? 0 : 1;
      regnum = (memnum == 0) ? 1 : 0;
      if (!strncmp (i.maxq20_op[regnum].reg->reg_name, "DP", 2) &&
	  !strncmp ((i.maxq20_op[memnum].mem->name) + 1,
		    i.maxq20_op[regnum].reg->reg_name, 5)
	  && strcmp ((i.maxq20_op[memnum].mem->name) + 1,
		     i.maxq20_op[regnum].reg->reg_name))
	{
	  as_bad (_
		  ("Contradictory movement between DP register and memory access using DP"));
	  return 0;
	}
      else if (!strcmp (i.maxq20_op[regnum].reg->reg_name, "SP") &&
	       !strncmp ((i.maxq20_op[memnum].mem->name) + 1,
			 i.maxq20_op[regnum].reg->reg_name, 2))
	{
	  as_bad (_
		  ("SP and @SP-- cannot be used together in a move instruction"));
	  return 0;
	}
    }

  /* This filter restricts the instructions containing source and destination 
     bits to only CTRL module of the serial registers. Peripheral registers
     yet to be defined.  */

  if (i.bit_operands == 1 && i.operands == 2)
    {
      int bitnum = (i.types[0] == BIT) ? 0 : 1;

      if (strcmp (i.maxq20_op[bitnum].r_bit->reg->reg_name, "ACC"))
	{
	  if (i.maxq20_op[bitnum].r_bit->reg->Mod_name >= 0x7 &&
	      i.maxq20_op[bitnum].r_bit->reg->Mod_name != CTRL)
	    {
	      as_bad (_
		      ("Only Module 8 system registers allowed in this operation"));
	      return 0;
	    }
	}
    }

  /* This filter is for checking the register bits.  */
  if (i.bit_operands == 1 || i.operands == 2)
    {
      int bitnum = 0, size = 0;

      bitnum = (i.types[0] == BIT) ? 0 : 1;
      if (i.bit_operands == 1)
	{
	  switch (i.maxq20_op[bitnum].r_bit->reg->rtype)
	    {
	    case Reg_8W:
	      size = 7;		/* 8 bit register, both read and write.  */
	      break;
	    case Reg_16W:
	      size = 15;
	      break;
	    case Reg_8R:
	      size = 7;
	      if (bitnum == 0)
		{
		  as_fatal (_("Read only Register used as destination"));
		  return 0;
		}
	      break;

	    case Reg_16R:
	      size = 15;
	      if (bitnum == 0)
		{
		  as_fatal (_("Read only Register used as destination"));
		  return 0;
		}
	      break;
	    }

	  if (size < (i.maxq20_op[bitnum].r_bit)->bit)
	    {
	      as_bad (_("Bit No '%d'exceeds register size in this operation"),
		      (i.maxq20_op[bitnum].r_bit)->bit);
	      return 0;
	    }
	}

      if (i.bit_operands == 2)
	{
	  switch ((i.maxq20_op[0].r_bit)->reg->rtype)
	    {
	    case Reg_8W:
	      size = 7;		/* 8 bit register, both read and write.  */
	      break;
	    case Reg_16W:
	      size = 15;
	      break;
	    case Reg_8R:
	    case Reg_16R:
	      as_fatal (_("Read only Register used as destination"));
	      return 0;
	    }

	  if (size < (i.maxq20_op[0].r_bit)->bit)
	    {
	      as_bad (_
		      ("Bit No '%d' exceeds register size in this operation"),
		      (i.maxq20_op[0].r_bit)->bit);
	      return 0;
	    }

	  size = 0;
	  switch ((i.maxq20_op[1].r_bit)->reg->rtype)
	    {
	    case Reg_8R:
	    case Reg_8W:
	      size = 7;		/* 8 bit register, both read and write.  */
	      break;
	    case Reg_16R:
	    case Reg_16W:
	      size = 15;
	      break;
	    }

	  if (size < (i.maxq20_op[1].r_bit)->bit)
	    {
	      as_bad (_
		      ("Bit No '%d' exceeds register size in this operation"),
		      (i.maxq20_op[1].r_bit)->bit);
	      return 0;
	    }
	}
    }

  /* No branch operations should occur into the data memory. Hence any memory 
     references have to be filtered out when used with instructions like
     jump, djnz[] and call.  */

  if (!strcmp (i.op.name, "JUMP") || !strcmp (i.op.name, "CALL")
      || !strncmp (i.op.name, "DJNZ", 4))
    {
      if (i.mem_operands)
	as_warn (_
		 ("Memory References cannot be used with branching operations\n"));
    }

  if (!strcmp (i.op.name, "DJNZ"))
    {
      if (!
	  (strcmp (i.maxq20_op[0].reg->reg_name, "LC[0]")
	   || strcmp (i.maxq20_op[0].reg->reg_name, "LC[1]")))
	{
	  as_bad (_("DJNZ uses only LC[n] register \n"));
	  return 0;
	}
    }

  /* No destination register used should be read only!  */
  if ((i.operands == 2 && i.types[0] == REG) || !strcmp (i.op.name, "POP")
      || !strcmp (i.op.name, "POPI"))
    {				/* The destination is a register */
      int regnum = 0;

      if (!strcmp (i.op.name, "POP") || !strcmp (i.op.name, "POPI"))
	{
	  regnum = 0;

	  if (i.types[regnum] == MEM)
	    {
	      mem_access_syntax *mem_op = NULL;

	      mem_op =
		(mem_access_syntax *) hash_find (mem_syntax_hash,
						 i.maxq20_op[regnum].mem->
						 name);
	      if (mem_op->type == SRC && mem_op)
		{
		  as_bad (_
			  ("'%s' operand cant be used as destination  in %s"),
			  mem_op->name, i.op.name);
		  return 0;
		}
	    }
	}

      if (i.maxq20_op[regnum].reg->rtype == Reg_8R
	  || i.maxq20_op[regnum].reg->rtype == Reg_16R)
	{
	  as_bad (_("Read only register used for writing purposes '%s'"),
		  i.maxq20_op[regnum].reg->reg_name);
	  return 0;
	}
    }

  /* While moving the address of a data in the data section, the destination
     should be either data pointers only.  */
  if ((i.data_operands) && (i.operands == 2))
    {
      if ((i.types[0] != REG) && (i.types[0] != MEM))
	{
	  as_bad (_("Invalid destination for this kind of source."));
	  return 0;
	}

	if (i.types[0] == REG && i.maxq20_op[0].reg->rtype == Reg_8W)
	  {
	    as_bad (_
		    ("Invalid register as destination for this kind of source.Only data pointers can be used."));
	    return 0;
	  }
    }
  return 1;
}

static int
decode_insn (void)
{
  /* Check for the format Bit if defined.  */
  if (i.op.format == 0 || i.op.format == 1)
    i.instr[0] = i.op.format << 7;
  else
    {
      /* Format bit not defined. We will have to be find it out ourselves.  */
      if (i.imm_operands == 1 || i.data_operands == 1 || i.disp_operands == 1)
	i.op.format = 0;
      else
	i.op.format = 1;
      i.instr[0] = i.op.format << 7;
    }

  /* Now for the destination register.  */

  /* If destination register is already defined . The conditions are the
     following: (1) The second entry in the destination array should be 0 (2) 
     If there are two operands then the first entry should not be a register,
     memory or a register bit (3) If there are less than two operands and the
     it is not a pop operation (4) The second argument is the carry
     flag(applicable to move Acc.<b>,C.  */
  if (i.op.dst[1] == 0
      &&
      ((i.types[0] != REG && i.types[0] != MEM && i.types[0] != BIT
	&& i.operands == 2) || (i.operands < 2 && strcmp (i.op.name, "POP")
				&& strcmp (i.op.name, "POPI"))
       || (i.op.arg[1] == FLAG_C)))
    {
      i.op.dst[0] &= 0x7f;
      i.instr[0] |= i.op.dst[0];
    }
  else if (i.op.dst[1] == 0 && !strcmp (i.op.name, "DJNZ")
	   &&
	   (((i.types[0] == REG)
	     && (!strcmp (i.maxq20_op[0].reg->reg_name, "LC[0]")
		 || !strcmp (i.maxq20_op[0].reg->reg_name, "LC[1]")))))
    {
      i.op.dst[0] &= 0x7f;
      if (!strcmp (i.maxq20_op[0].reg->reg_name, "LC[0]"))
	i.instr[0] |= 0x4D;

      if (!strcmp (i.maxq20_op[0].reg->reg_name, "LC[1]"))
	i.instr[0] |= 0x5D;
    }
  else
    {
      unsigned char temp;

      /* Target register will have to be specified.  */
      if (i.types[0] == REG
	  && (i.op.dst[0] == REG || i.op.dst[0] == (REG | MEM)))
	{
	  temp = (i.maxq20_op[0].reg)->opcode;
	  temp &= 0x7f;
	  i.instr[0] |= temp;
	}
      else if (i.types[0] == MEM && (i.op.dst[0] == (REG | MEM)))
	{
	  temp = (i.maxq20_op[0].mem)->opcode;
	  temp &= 0x7f;
	  i.instr[0] |= temp;
	}
      else if (i.types[0] == BIT && (i.op.dst[0] == REG))
	{
	  temp = (i.maxq20_op[0].r_bit)->reg->opcode;
	  temp &= 0x7f;
	  i.instr[0] |= temp;
	}
      else if (i.types[1] == BIT && (i.op.dst[0] == BIT))
	{
	  temp = (i.maxq20_op[1].r_bit)->bit;
	  temp = temp << 4;
	  temp |= i.op.dst[1];
	  temp &= 0x7f;
	  i.instr[0] |= temp;
	}
      else
	{
	  as_bad (_("Invalid Instruction"));
	  return 0;
	}
    }

  /* Now for the source register.  */

  /* If Source register is already known. The following conditions are
     checked: (1) There are no operands (2) If there is only one operand and
     it is a flag (3) If the operation is MOVE C,#0/#1 (4) If it is a POP
     operation.  */

  if (i.operands == 0 || (i.operands == 1 && i.types[0] == FLAG)
      || (i.types[0] == FLAG && i.types[1] == IMMBIT)
      || !strcmp (i.op.name, "POP") || !strcmp (i.op.name, "POPI"))
    i.instr[1] = i.op.src[0];

  else if (i.imm_operands == 1 && ((i.op.src[0] & IMM) == IMM))
    i.instr[1] = i.maxq20_op[this_operand].imms;
  
  else if (i.types[this_operand] == REG && ((i.op.src[0] & REG) == REG))
    i.instr[1] = (char) ((i.maxq20_op[this_operand].reg)->opcode);

  else if (i.types[this_operand] == BIT && ((i.op.src[0] & REG) == REG))
    i.instr[1] = (char) (i.maxq20_op[this_operand].r_bit->reg->opcode);

  else if (i.types[this_operand] == MEM && ((i.op.src[0] & MEM) == MEM))
    i.instr[1] = (char) ((i.maxq20_op[this_operand].mem)->opcode);

  else if (i.types[this_operand] == DATA && ((i.op.src[0] & DATA) == DATA))
    /* This will copy only the lower order bytes into the instruction. The
       higher order bytes have already been copied into the prefix register.  */
    i.instr[1] = 0;

  /* Decoding the source in the case when the second array entry is not 0.
     This means that the source register has been divided into two nibbles.  */

  else if (i.op.src[1] != 0)
    {
      /* If the first operand is a accumulator bit then
	 the first 4 bits will be filled with the bit number.  */
      if (i.types[0] == BIT && ((i.op.src[0] & BIT) == BIT))
	{
	  unsigned char temp = (i.maxq20_op[0].r_bit)->bit;

	  temp = temp << 4;
	  temp |= i.op.src[1];
	  i.instr[1] = temp;
	}
      /* In case of MOVE dst.<b>,#1 The first nibble in the source register
         has to start with a zero. This is called a ZEROBIT */
      else if (i.types[0] == BIT && ((i.op.src[0] & ZEROBIT) == ZEROBIT))
	{
	  char temp = (i.maxq20_op[0].r_bit)->bit;

	  temp = temp << 4;
	  temp |= i.op.src[1];
	  temp &= 0x7f;
	  i.instr[1] = temp;
	}
      /* Similarly for a ONEBIT */
      else if (i.types[0] == BIT && ((i.op.src[0] & ONEBIT) == ONEBIT))
	{
	  char temp = (i.maxq20_op[0].r_bit)->bit;

	  temp = temp << 4;
	  temp |= i.op.src[1];
	  temp |= 0x80;
	  i.instr[1] = temp;
	}
      /* In case the second operand is a register bit (MOVE C,Acc.<b> or MOVE 
         C,src.<b> */
      else if (i.types[1] == BIT)
	{
	  if (i.op.src[1] == 0 && i.op.src[1] == REG)
	    i.instr[1] = (i.maxq20_op[1].r_bit)->reg->opcode;

	  else if (i.op.src[0] == BIT && i.op.src)
	    {
	      char temp = (i.maxq20_op[1].r_bit)->bit;

	      temp = temp << 4;
	      temp |= i.op.src[1];
	      i.instr[1] = temp;
	    }
	}
      else
	{
	  as_bad (_("Invalid Instruction"));
	  return 0;
	}
    }
  return 1;
}

/* This is a function for outputting displacement operands.  */

static void
output_disp (fragS *insn_start_frag, offsetT insn_start_off)
{
  char *p;
  relax_substateT subtype;
  symbolS *sym;
  offsetT off;
  int diff;

  diff = 0;
  insn_start_frag = frag_now;
  insn_start_off = frag_now_fix ();

  switch (i.Instr_Prefix)
    {
    case LONG_PREFIX:
      subtype = EXPLICT_LONG_PREFIX;
      break;
    case SHORT_PREFIX:
      subtype = SHORT_PREFIX;
      break;
    default:
      subtype = NO_PREFIX;
      break;
    }

  /* Its a symbol. Here we end the frag and start the relaxation. Now in our
     case there is no need for relaxation. But we do need support for a
     prefix operator. Hence we will check whethere is room for 4 bytes ( 2
     for prefix + 2 for the current instruction ) Hence if at a particular
     time we find out whether the prefix operator is reqd , we shift the
     current instruction two places ahead and insert the prefix instruction.  */
  frag_grow (2 + 2);
  p = frag_more (2);

  sym = i.maxq20_op[this_operand].disps->X_add_symbol;
  off = i.maxq20_op[this_operand].disps->X_add_number;

  if (i.maxq20_op[this_operand].disps->X_add_symbol != NULL && sym && frag_now
      && (subtype != EXPLICT_LONG_PREFIX))
    {
      /* If in the same frag.  */
      if (frag_now == symbol_get_frag (sym))
	{
	  diff =
	    ((((expressionS *) symbol_get_value_expression (sym))->
	      X_add_number) - insn_start_off);

	  /* PC points to the next instruction.  */
	  diff = (diff / MAXQ_OCTETS_PER_BYTE) - 1;

	  if (diff >= -128 && diff <= 127)
	    {
	      i.instr[1] = (char) diff;

	      /* This will be overwritten later when the symbol is resolved.  */
	      *p = i.instr[1];
	      *(p + 1) = i.instr[0];

	      /* No Need to create a FIXUP.  */
	      return;
	    }
	}
    }

  /* This will be overwritten later when the symbol is resolved.  */
  *p = i.instr[1];
  *(p + 1) = i.instr[0];

  if (i.maxq20_op[this_operand].disps->X_op != O_constant
      && i.maxq20_op[this_operand].disps->X_op != O_symbol)
    {
      /* Handle complex expressions.  */
      sym = make_expr_symbol (i.maxq20_op[this_operand].disps);
      off = 0;
    }

  /* Vineet : This has been added for md_estimate_size_before_relax to
     estimate the correct size.  */
  if (subtype != SHORT_PREFIX)
    i.reloc[this_operand] = LONG_PREFIX;

  frag_var (rs_machine_dependent, 2, i.reloc[this_operand], subtype, sym, off,  p);
}

/* This is a function for outputting displacement operands.  */

static void
output_data (fragS *insn_start_frag, offsetT insn_start_off)
{
  char *p;
  relax_substateT subtype;
  symbolS *sym;
  offsetT off;
  int diff;

  diff = 0;
  off = 0;
  insn_start_frag = frag_now;
  insn_start_off = frag_now_fix ();

  subtype = EXPLICT_LONG_PREFIX;

  frag_grow (2 + 2);
  p = frag_more (2);

  sym = i.maxq20_op[this_operand].data;
  off = 0;

  /* This will be overwritten later when the symbol is resolved.  */
  *p = i.instr[1];
  *(p + 1) = i.instr[0];

  if (i.maxq20_op[this_operand].disps->X_op != O_constant
      && i.maxq20_op[this_operand].disps->X_op != O_symbol)
    /* Handle complex expressions.  */
    /* Because data is already in terms of symbol so no
       need to convert it from expression to symbol.  */
    off = 0;

  frag_var (rs_machine_dependent, 2, i.reloc[this_operand], subtype, sym, off,  p);
}

static void
output_insn (void)
{
  fragS *insn_start_frag;
  offsetT insn_start_off;
  char *p;

  /* Tie dwarf2 debug info to the address at the start of the insn. We can't
     do this after the insn has been output as the current frag may have been 
     closed off.  eg. by frag_var.  */
  dwarf2_emit_insn (0);

  /* To ALign the text section on word.  */

  frag_align (1, 0, 1);

  /* We initialise the frags for this particular instruction.  */
  insn_start_frag = frag_now;
  insn_start_off = frag_now_fix ();

  /* If there are displacement operators(unresolved) present, then handle
     them separately.  */
  if (i.disp_operands)
    {
      output_disp (insn_start_frag, insn_start_off);
      return;
    }

  if (i.data_operands)
    {
      output_data (insn_start_frag, insn_start_off);
      return;
    }

  /* Check whether the INSERT_BUFFER has to be written.  */
  if (strcmp (INSERT_BUFFER, ""))
    {
      p = frag_more (2);

      *p++ = INSERT_BUFFER[1];
      *p = INSERT_BUFFER[0];
    }

  /* Check whether the prefix instruction has to be written.  */
  if (strcmp (PFX_INSN, ""))
    {
      p = frag_more (2);

      *p++ = PFX_INSN[1];
      *p = PFX_INSN[0];
    }

  p = frag_more (2);
  /* For Little endian.  */
  *p++ = i.instr[1];
  *p = i.instr[0];
}

static void
make_new_reg_table (void)
{
  unsigned long size_pm = sizeof (peripheral_reg_table);
  num_of_reg = ARRAY_SIZE (peripheral_reg_table);

  new_reg_table = xmalloc (size_pm);
  if (new_reg_table == NULL)
    as_bad (_("Cannot allocate memory"));

  memcpy (new_reg_table, peripheral_reg_table, size_pm);
}

/* pmmain performs the initilizations for the pheripheral modules. */

static void
pmmain (void)
{
  make_new_reg_table ();
  return;
}

void
md_begin (void)
{
  const char *hash_err = NULL;
  int c = 0;
  char *p;
  const MAXQ20_OPCODE_INFO *optab;
  MAXQ20_OPCODES *core_optab;	/* For opcodes of the same name. This will
				   be inserted into the hash table.  */
  struct reg *reg_tab;
  struct mem_access_syntax const *memsyntab;
  struct mem_access *memtab;
  struct bit_name *bittab;

  /* Initilize pherioipheral modules.  */
  pmmain ();

  /* Initialise the opcode hash table.  */
  op_hash = hash_new ();

  optab = op_table;		/* Initialise it to the first entry of the
				   maxq20 operand table.  */

  /* Setup for loop.  */
  core_optab = xmalloc (sizeof (MAXQ20_OPCODES));
  core_optab->start = optab;

  while (1)
    {
      ++optab;
      if (optab->name == NULL || strcmp (optab->name, (optab - 1)->name) != 0)
	{
	  /* different name --> ship out current template list; add to hash
	     table; & begin anew.  */

	  core_optab->end = optab;
#ifdef MAXQ10S
	  if (max_version == bfd_mach_maxq10)
	    {
	      if (((optab - 1)->arch == MAXQ10) || ((optab - 1)->arch == MAX))
		{
		  hash_err = hash_insert (op_hash,
					  (optab - 1)->name,
					  (PTR) core_optab);
		}
	    }
	  else if (max_version == bfd_mach_maxq20)
	    {
	      if (((optab - 1)->arch == MAXQ20) || ((optab - 1)->arch == MAX))
		{
#endif
		  hash_err = hash_insert (op_hash,
					  (optab - 1)->name,
					  (PTR) core_optab);
#if MAXQ10S
		}
	    }
	  else
	    as_fatal (_("Internal Error: Illegal Architecure specified"));
#endif
	  if (hash_err)
	    as_fatal (_("Internal Error:  Can't hash %s: %s"),
		      (optab - 1)->name, hash_err);

	  if (optab->name == NULL)
	    break;
	  core_optab = xmalloc (sizeof (MAXQ20_OPCODES));
	  core_optab->start = optab;
	}
    }

  /* Initialise a new register table.  */
  reg_hash = hash_new ();

  for (reg_tab = system_reg_table;
       reg_tab < (system_reg_table + ARRAY_SIZE (system_reg_table));
       reg_tab++)
    {
#if MAXQ10S
      switch (max_version)
	{
	case bfd_mach_maxq10:
	  if ((reg_tab->arch == MAXQ10) || (reg_tab->arch == MAX))
	    hash_err = hash_insert (reg_hash, reg_tab->reg_name, (PTR) reg_tab);
	  break;

	case bfd_mach_maxq20:
	  if ((reg_tab->arch == MAXQ20) || (reg_tab->arch == MAX))
	    {
#endif
	      hash_err =
		hash_insert (reg_hash, reg_tab->reg_name, (PTR) reg_tab);
#if MAXQ10S
	    }
	  break;
	default:
	  as_fatal (_("Invalid architecture type"));
	}
#endif

      if (hash_err)
	as_fatal (_("Internal Error : Can't Hash %s : %s"),
		  reg_tab->reg_name, hash_err);
    }

  /* Pheripheral Registers Entry.  */
  for (reg_tab = new_reg_table;
       reg_tab < (new_reg_table + num_of_reg - 1); reg_tab++)
    {
      hash_err = hash_insert (reg_hash, reg_tab->reg_name, (PTR) reg_tab);

      if (hash_err)
	as_fatal (_("Internal Error : Can't Hash %s : %s"),
		  reg_tab->reg_name, hash_err);
    }

  /* Initialise a new memory operand table.  */
  mem_hash = hash_new ();

  for (memtab = mem_table;
       memtab < mem_table + ARRAY_SIZE (mem_table);
       memtab++)
    {
      hash_err = hash_insert (mem_hash, memtab->name, (PTR) memtab);
      if (hash_err)
	as_fatal (_("Internal Error : Can't Hash %s : %s"),
		  memtab->name, hash_err);
    }

  bit_hash = hash_new ();

  for (bittab = bit_table;
       bittab < bit_table + ARRAY_SIZE (bit_table);
       bittab++)
    {
      hash_err = hash_insert (bit_hash, bittab->name, (PTR) bittab);
      if (hash_err)
	as_fatal (_("Internal Error : Can't Hash %s : %s"),
		  bittab->name, hash_err);
    }

  mem_syntax_hash = hash_new ();

  for (memsyntab = mem_access_syntax_table;
       memsyntab < mem_access_syntax_table + ARRAY_SIZE (mem_access_syntax_table);
       memsyntab++)
    {
      hash_err =
	hash_insert (mem_syntax_hash, memsyntab->name, (PTR) memsyntab);
      if (hash_err)
	as_fatal (_("Internal Error : Can't Hash %s : %s"),
		  memsyntab->name, hash_err);
    }

  /* Initialise the lexical tables,mnemonic chars,operand chars.  */
  for (c = 0; c < 256; c++)
    {
      if (ISDIGIT (c))
	{
	  digit_chars[c] = c;
	  mnemonic_chars[c] = c;
	  operand_chars[c] = c;
	  register_chars[c] = c;
	}
      else if (ISLOWER (c))
	{
	  mnemonic_chars[c] = c;
	  operand_chars[c] = c;
	  register_chars[c] = c;
	}
      else if (ISUPPER (c))
	{
	  mnemonic_chars[c] = TOLOWER (c);
	  register_chars[c] = c;
	  operand_chars[c] = c;
	}

      if (ISALPHA (c) || ISDIGIT (c))
	{
	  identifier_chars[c] = c;
	}
      else if (c > 128)
	{
	  identifier_chars[c] = c;
	  operand_chars[c] = c;
	}
    }

  /* All the special characters.  */
  register_chars['@'] = '@';
  register_chars['+'] = '+';
  register_chars['-'] = '-';
  digit_chars['-'] = '-';
  identifier_chars['_'] = '_';
  identifier_chars['.'] = '.';
  register_chars['['] = '[';
  register_chars[']'] = ']';
  operand_chars['_'] = '_';
  operand_chars['#'] = '#';
  mnemonic_chars['['] = '[';
  mnemonic_chars[']'] = ']';

  for (p = operand_special_chars; *p != '\0'; p++)
    operand_chars[(unsigned char) *p] = (unsigned char) *p;

  /* Set the maxq arch type.  */
  maxq_target (max_version);
}

/* md_assemble - Parse Instr - Seprate menmonics and operands - lookup the
   menmunonic in the operand table - Parse operands and populate the
   structure/template - Match the operand with opcode and its validity -
   Output Instr.  */

void
md_assemble (char *line)
{
  int j;

  char mnemonic[MAX_MNEM_SIZE];
  char temp4prev[256];
  static char prev_insn[256];

  /* Initialize globals.  */
  memset (&i, '\0', sizeof (i));
  for (j = 0; j < MAX_OPERANDS; j++)
    i.reloc[j] = NO_RELOC;

  i.prefix = -1;
  PFX_INSN[0] = 0;
  PFX_INSN[1] = 0;
  INSERT_BUFFER[0] = 0;
  INSERT_BUFFER[1] = 0;

  memcpy (temp4prev, line, strlen (line) + 1);

  save_stack_p = save_stack;

  line = (char *) parse_insn (line, mnemonic);
  if (line == NULL)
    return;

  line = (char *) parse_operands (line, mnemonic);
  if (line == NULL)
    return;

  /* Next, we find a template that matches the given insn, making sure the
     overlap of the given operands types is consistent with the template
     operand types.  */
  if (!match_template ())
    return;

  /* In the MAXQ20, there are certain register combinations, and other
     restrictions which are not allowed. We will try to resolve these right
     now.  */
  if (!match_filters ())
    return;

  /* Check for the approprate PFX register.  */
  set_prefix ();
  pfx_for_imm_val (0);

  if (!decode_insn ())		/* decode insn. */
    need_pass_2 = 1;

  /* Check for Exlipct PFX instruction.  */
  if (PFX_INSN[0] && (strstr (prev_insn, "PFX") || strstr (prev_insn, "pfx")))
    as_warn (_("Ineffective insntruction %s \n"), prev_insn);

  memcpy (prev_insn, temp4prev, strlen (temp4prev) + 1);

  /* We are ready to output the insn.  */
  output_insn ();
}
