/***************************************************************************/
/*                                                                         */
/* Project   :       as1750 -- Mil-Std-1750 Assembler and Linker           */
/*                                                                         */
/* Component :          as1750.c -- 1750 instruction assembly              */
/*                                                                         */
/* Copyright :        (C) Daimler-Benz Aerospace AG, 1994-1997             */
/*                                                                         */
/* Author    :       Oliver M. Kellogg, Dornier Satellite Systems,         */
/*                       Dept. RST13, D-81663 Munich, Germany.             */
/* Contact   :             oliver.kellogg@space.otn.dasa.de                */
/*                                                                         */
/* Disclaimer:                                                             */
/*                                                                         */
/*  This program is free software; you can redistribute it and/or modify   */
/*  it under the terms of the GNU General Public License as published by   */
/*  the Free Software Foundation; either version 2 of the License, or      */
/*  (at your option) any later version.                                    */
/*                                                                         */
/*  This program is distributed in the hope that it will be useful,        */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
/*  GNU General Public License for more details.                           */
/*                                                                         */
/*  You should have received a copy of the GNU General Public License      */
/*  along with this program; if not, write to the Free Software            */
/*  Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.   */
/*                                                                         */
/***************************************************************************/

#ifdef AS1750
#include "common.h"
#include "utils.h"
#else /* ASL */
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
#include "datatypes.h"
#include "strutil.h"
#include "asmdef.h"
#include "asmsub.h"
#include "asmpars.h"
#include "as1750.h"
#define bool      char
#define ushort    unsigned short
#define ulong     unsigned long
#define status    unsigned
#define dtoi(ascii_char) (ascii_char - '0')
#endif


#ifndef AS1750
static
#endif
  status			/* Output an error text (printf style). */
error (char *layout,...)	/*  Return the ERROR status code. */
{
  va_list vargu;
  char output_line[132];

  va_start (vargu, layout);
  vsprintf (output_line, layout, vargu);
#ifdef AS1750
  fprintf (stderr, "%s line%5d: %s\n", nopath (file[curr_file].name),
	   file[curr_file].line_number, output_line);
#else /* ASL */
  WrErrorString (output_line, "\0", 0, 0);
#endif
  va_end (vargu);
  return ERROR;
}


/* Get a number. Return pointer to first position in s after the end of
 * the number, or NULL for error.
 * Will also read character constants of the form: 'x', and two-character
 * packed strings of the form "xy" (where x=>highbyte,y=>lowbyte.)
 */

#ifdef AS1750

char *
get_num (char *s, int *outnum)
{
  bool is_neg = FALSE, intel = FALSE, c_lang = FALSE, tld = FALSE;
  char *start;

  *outnum = 0;
  if (*s == '-')
    {
      is_neg = TRUE;
      ++s;
    }
  else if (*s == '+')
    ++s;
  /* determine if Intel format */
  if (isdigit (*s))
    {
      char *p = s;
      while (isxdigit (*++p))
	;
      if (upcase (*p) == 'H')
	intel = TRUE;
    }
  if (intel
      || (c_lang = (*s == '0' && upcase (*(s + 1)) == 'X'))
      || (tld = strncmp (s, "16#", 3) == 0))
    {
      s += c_lang ? 2 : tld ? 3 : 0;
      start = s;
      while (isxdigit (*s))
	{
	  *outnum = (*outnum << 4) | xtoi (*s);
	  ++s;
	}
      if (s - start > 4)
	{
	  error ("get_num -- number at '%s' too large", start);
	  return NULL;
	}
      if (intel)
	s++;
      else if (tld)
	{
	  if (*s != '#')
	    {
	      error ("get_num -- expecting '#' at end of number");
	      return NULL;
	    }
	  s++;
	}
    }
  else if (*s == '0' || (tld = (*s == '8' && *(s + 1) == '#')))
    {
      s += tld ? 2 : 1;
      start = s;
      while (*s >= '0' && *s <= '7')
	{
	  *outnum = (*outnum << 3) | (*s - '0');
	  ++s;
	}
      if (s - start > 6)
	{
	  error ("get_num -- number at '%s' too large", start);
	  return NULL;
	}
      if (tld)
	{
	  if (*s != '#')
	    {
	      error ("get_num -- expecting '#' at end of number");
	      return NULL;
	    }
	  ++s;
	}
    }
  else if (*s == '@' || (tld = (*s == '2' && *(s + 1) == '#')))
    {
      s += (tld ? 2 : 1);
      start = s;
      while (*s == '0' || *s == '1')
	{
	  *outnum = (*outnum << 1) | (*s - '0');
	  ++s;
	}
      if (s - start > 16)
	{
	  error ("get_num -- number at '%s' too large", start);
	  return NULL;
	}
      if (tld)
	{
	  if (*s != '#')
	    {
	      error ("get_num -- expecting '#' at end of number");
	      return NULL;
	    }
	  ++s;
	}
    }
  else if (isdigit (*s))
    {
      start = s;
      while (isdigit (*s))
	{
	  *outnum = (*outnum * 10) + dtoi (*s);
	  ++s;
	}
      if (s - start > 5)
	{
	  error ("get_num -- number at '%s' too large", start);
	  return NULL;
	}
    }
  else if (*s == '\'')
    {
      start = s;
      if (*++s == '\\')
	{
	  switch (*++s)
	    {
	    case 't':
	      *outnum = 9;
	      break;
	    case 'n':
	      *outnum = 10;
	      break;
	    case 'r':
	      *outnum = 13;
	      break;
	    default:
	      error ("get_num -- unknown escape '\\%c'", *s);
	      return NULL;
	    }
	}
      else
        *outnum = (int) *s & 0xFF;
      if (*++s != '\'')
	{
	  error ("get_num -- character constant incorrectly terminated", start);
	  return NULL;
	}
      ++s;
    }
  else if (*s == '"')
    {
      start = s;
      *outnum = ((int) *++s & 0xFF) << 8;
      *outnum |= (int) *++s & 0xFF;
      if (*++s != '"')
	{
	  error ("get_num -- character tuple incorrectly terminated", start);
	  return NULL;
	}
      ++s;
    }
  else
    return NULL;
  if (is_neg)
    *outnum = -*outnum;
  return s;
}

/* Get a constant symbol (previously defined by EQU) or a number.
 * Return pointer to first character after the symbol or number consumed,
 * or NULL if reading the symbol or number was unsuccessful.
 */
char *
get_sym_num (char *s, int *outnum)
{
  char *p, c;
  symbol_t sym;

  if ((p = get_num (s, outnum)) != NULL)
    return p;

  /* Didn't find a raw number; try symbol. */
  if (!issymstart (*s))
    {
      error ("expecting symbol at '%s'", s);
      return NULL;
    }
  p = s;
  while (issymchar (*p))
    p++;
  c = *p;
  *p = '\0';
  if ((sym = find_symbol (s)) == SNULL)
    {
      error ("unidentified symbol at '%s'", s);
      return NULL;
    }
  *p = c;
  if (!sym->is_constant)
    {
      error ("symbol must be constant in this context");
      return NULL;
    }
  *outnum = (int) sym->value;
  return p;
}


extern int curr_frag;		/* imported from main.c */

/* Enter a 16-bit word into the object space */
void
add_word (ushort word)
{
  struct objblock *obj = &objblk[curr_frag];

  if (obj->data == (ushort *) 0)
    {
      obj->n_allocated = 256;
      obj->data = (ushort *) malloc (obj->n_allocated * sizeof (ushort));
      obj->line = (struct linelist **) malloc
	(obj->n_allocated * sizeof (struct linelist *));
    }
  else if (obj->n_used == obj->n_allocated)
    {
      obj->n_allocated *= 2;
      obj->data = (ushort *) realloc (obj->data,
				      obj->n_allocated * sizeof (ushort));
      obj->line = (struct linelist **) realloc (obj->line,
			     obj->n_allocated * sizeof (struct linelist *));
    }
  if (obj->data == (ushort *) 0 || obj->line == (struct linelist **) 0)
    problem ("request for object space refused by OS");
  obj->data[obj->n_used] = word;
  obj->line[obj->n_used] = line;
  obj->n_used++;
}

void
add_reloc (symbol_t sym)	/* auxiliary to parse_addr() */
{
  struct reloc *rel;

  if (relblk.data == (struct reloc *) 0)
    {
      relblk.n_allocated = 256;
      relblk.data = (struct reloc *) malloc
	(relblk.n_allocated * sizeof (struct reloc));
    }
  else if (relblk.n_used == relblk.n_allocated)
    {
      relblk.n_allocated *= 2;
      relblk.data = (struct reloc *) realloc (relblk.data,
				relblk.n_allocated * sizeof (struct reloc));
    }
  if (relblk.data == (struct reloc *) 0)
    problem ("request for relocation space refused by OS");
  rel = &relblk.data[relblk.n_used];
  rel->done = FALSE;		/* default initializations */
  rel->reltype = Word_Reloc;
  rel->frag_index = curr_frag;
  rel->obj_index = objblk[curr_frag].n_used;
  rel->sym = sym;
  relblk.n_used++;
}


/* Parse the address expression at s into object space.
   Returns OKAY or ERROR. */
status
parse_addr (char *s)
{
  int num;
  char *p, c, c1;
  symbol_t sym, sym1;

  if (issymstart (*s))
    {
      p = skip_symbol (s);
      c = *p;			/* prepare for symbol lookup */
      *p = '\0';
      if ((sym = find_or_enter (s)) == SNULL)
	return ERROR;
      sym->is_referenced = TRUE;
      *p = c;
      s = p++;
      if (c == '\0')
	{
	  if (sym->is_constant)
	    add_word ((ushort) sym->value);
	  else
	    {
	      add_reloc (sym);
	      add_word (0);
	    }
	}
      else if (c != '+' && c != '-')
	return error ("error after symbolname at: %s", s);
      else if (issymstart (*p))
	{
	  s = skip_symbol (p);
	  if (*s != '\0' && *s != ',')
	    return error ("address expression too complex");
	  c1 = *s;		/* prepare for symbol lookup */
	  *s = '\0';
	  if ((sym1 = find_or_enter (p)) == SNULL)
	    return ERROR;
	  sym1->is_referenced = TRUE;
	  *s = c1;
	  if (c == '+')
	    {
	      if (sym->is_constant)
		{
		  if (sym1->is_constant)
		    {
		      long sum = sym->value + sym1->value;
		      if (sum < -0x8000L)
			return error ("negative overflow in symbol addition");
		      else if (sum > 0xFFFFL)
			return error ("positive overflow in symbol addition");
		      add_word ((ushort) sum);
		    }
		  else
		    {
		      add_reloc (sym1);
		      add_word ((ushort) sym->value);
		    }
		}
	      else
		{
		  if (sym1->is_constant)
		    {
		      add_reloc (sym);
		      add_word ((ushort) sym1->value);
		    }
		  else
		    return error ("cannot add relocatable symbols");
		}
	    }
	  else
	    /* subtraction */
	    {
	      if (sym->is_constant)
		{
		  if (sym1->is_constant)
		    {
		      long dif = sym->value - sym1->value;
		      if (dif < -0x8000L)
			return error ("negative overflow in symbol subtraction");
		      else if (dif > 0xFFFFL)
			return error ("positive overflow symbol subtraction");
		      add_word ((ushort) dif);
		    }
		  else
		    error ("cannot subtract relocatable symbol from constant symbol");
		}
	      else
		{
		  if (sym1->is_constant)
		    {
		      add_reloc (sym);
		      add_word ((ushort) - sym1->value);
		    }
		  else
		    {
		      if (objblk[sym->frag_index].section !=
			  objblk[sym1->frag_index].section)
			return error
			  ("cannot subtract relocatable symbols from different fragments");
		      if (sym->value < sym1->value)
			error ("warning: strange subtraction of relocatable symbols");
		      add_word ((ushort) (sym->value - sym1->value));
		    }
		}
	    }
	}
      else
	{
	  if (get_num (s, &num) == NULL)
	    return ERROR;
	  if (sym->is_constant)
	    {
	      long sum = sym->value + (long) num;
	      if (sum < -32768L)
		return error ("neg. overflow in symbolic expression");
	      else if (sum > 65535L)
		return error ("overflow in symbolic expression");
	      add_word ((ushort) sum);
	    }
	  else
	    {
	      add_reloc (sym);
	      add_word ((ushort) num);
	    }
	}
    }
  else if ((s = get_num (s, &num)) == NULL)
    return ERROR;
  else
    {
      if (*s == '\0')
	add_word ((ushort) num);
      else if (*s != '+')
	return error ("expected '+' in address expression at: %s", s);
      else
	{
	  ++s;
	  if (!issymstart (*s))
	    return error ("expected symbolname in address expression at: %s", s);
	  p = skip_symbol (s);
	  if (*p != '\0' && *p != ',')
	    return error ("illegal characters after symbolname at: %s", s);
	  c = *p;		/* prepare for symbol lookup */
	  *p = '\0';
	  if ((sym = find_or_enter (s)) == SNULL)
	    return ERROR;
	  sym->is_referenced = TRUE;
	  *p = c;
	  s = p;
	  if (sym->is_constant)
	    add_word ((ushort) (sym->value + (long) num));
	  else
	    {
	      add_reloc (sym);
	      add_word ((ushort) num);
	    }
	}
    }
  return OKAY;
}

#else /* ASL */

static char *
get_sym_num (char *s, int *outnum)
{
  Boolean okay;
  *outnum = (int) EvalIntExpression (s, Int16, &okay);
  if (!okay)
    return NULL;
  return (s + strlen (s));	/* Any non-NULL value will do here. */
}


#define add_word(word)  WAsmCode[CodeLen++]=word

static status
parse_addr (char *s)
{
  int value;
  Boolean okay;
  value = (int) EvalIntExpression (s, Int16, &okay);
  if (!okay)
    return ERROR;
  add_word ((ushort) value);
  return OKAY;
}

#endif /* from #else of #ifdef AS1750 */

/* From here on, everything is identical between as1750 and ASL. */

static ushort
get_num_bounded (char *s, int lowlim, int highlim)
{
  int num;
  if (get_sym_num (s, &num) == NULL)
    return ERROR;
  if (num < lowlim || num > highlim)
    return error ("number not in range %d..%d", lowlim, highlim);
  return (ushort) num;
}

static ushort
get_regnum (char *s)
{
  ushort regnum;
  *s = toupper (*s);
  if (*s != 'R')
    return error ("expecting register at '%s'", s);
  ++s;
  if (!isdigit (*s))
    return error ("expecting register at '%s'", s);
  regnum = dtoi (*s);
  ++s;
  if (isdigit (*s))
    {
      regnum = regnum * 10 + dtoi (*s);
      if (regnum > 15)
	return error ("register number out of range");
      ++s;
    }
  return regnum;
}


/**********************************************************************/
/* Functions to process opcode arguments according to addressing mode */

static int n_args;
static char *arg[4];

static status
check_indexreg (void)
{
  if (n_args > 2)
    {
      ushort rx;

      if ((rx = get_regnum (arg[2])) == ERROR)
	return ERROR;
      if (rx == 0)
	return error ("R0 not an index register");
#ifdef AS1750
      objblk[curr_frag].data[objblk[curr_frag].n_used - 2] |= rx;
#else /* ASL */
      WAsmCode[0] |= rx;
#endif
    }
  return OKAY;
}


static ushort
as_r (ushort oc)
{
  ushort ra, rb;
  if (n_args != 2)
    return error ("incorrect number of operands");
  if ((ra = get_regnum (arg[0])) == ERROR)
    return ERROR;
  if ((rb = get_regnum (arg[1])) == ERROR)
    return ERROR;

  add_word (oc | (ra << 4) | rb);
  return 1;
}

static ushort
as_mem (ushort oc)
{
  ushort ra;
  if (n_args < 2)
    return error ("insufficient number of operands");
  if ((ra = get_regnum (arg[0])) == ERROR)
    return ERROR;
  add_word (oc | (ra << 4));
  if (parse_addr (arg[1]))
    return ERROR;
  if (check_indexreg ())
    return ERROR;
  return 2;
}

static ushort
as_addr (ushort oc)		/* LST and LSTI */
{
  if (n_args < 1)
    return error ("insufficient number of operands");
  add_word (oc);
  if (parse_addr (arg[0]))
    return ERROR;
  n_args++;			/* cheat check_indexreg() */
  arg[2] = arg[1];
  if (check_indexreg ())
    return ERROR;
  return 2;
}

static ushort
as_xmem (ushort oc)		/* MIL-STD-1750B extended mem. addr. */
{
  ushort ra;

  if (n_args < 2)
    return error ("insufficient number of operands");
  if ((ra = get_regnum (arg[0])) == ERROR)
    return ERROR;
  add_word (oc | (ra << 4));
  if (parse_addr (arg[1]))
    return ERROR;
  if (n_args > 2)
    {
      ushort rx;
      if ((rx = get_regnum (arg[2])) == ERROR)
	return ERROR;
#ifdef AS1750
      objblk[curr_frag].data[objblk[curr_frag].n_used - 2] |= rx;
#else /* ASL */
      WAsmCode[0] |= rx;
#endif
    }
  return 2;
}

static ushort
as_im_0_15 (ushort oc)		/* for STC, LM, TB,SB,RB,TSB */
{
  ushort ra;
  if (n_args < 2)
    return error ("insufficient number of operands");
  if ((ra = get_num_bounded (arg[0], 0, 15)) == ERROR)
    return ERROR;
  add_word (oc | (ra << 4));
  if (parse_addr (arg[1]))
    return ERROR;
  if (check_indexreg ())
    return ERROR;
  return 2;
}

static ushort
as_im_1_16 (ushort oc)
{
  ushort ra;
  if (n_args < 2)
    return error ("insufficient number of operands");
  if ((ra = get_num_bounded (arg[0], 1, 16)) == ERROR)
    return ERROR;
  add_word (oc | ((ra - 1) << 4));
  if (parse_addr (arg[1]))
    return ERROR;
  if (check_indexreg ())
    return ERROR;
  return 2;
}

static ushort
as_im_ocx (ushort oc)
{
  ushort ra;
  if (n_args != 2)
    return error ("incorrect number of operands");
  if ((ra = get_regnum (arg[0])) == ERROR)
    return ERROR;
  add_word (oc | (ra << 4));	/* oc has OCX in LSnibble */
  if (parse_addr (arg[1]))
    return ERROR;
  return 2;
}

static ushort
as_is (ushort oc)
{
  ushort ra, i;
  if (n_args != 2)
    return error ("incorrect number of operands");
  if ((ra = get_regnum (arg[0])) == ERROR)
    return ERROR;
  if ((i = get_num_bounded (arg[1], 1, 16)) == ERROR)
    return ERROR;
  add_word (oc | (ra << 4) | (i - 1));
  return 1;
}

static ushort
as_icr (ushort oc)
{
#ifdef AS1750
  struct objblock *obj = &objblk[curr_frag];
  int last = obj->n_used;
#endif
  if (n_args != 1)
    return error ("incorrect number of operands");
  if (parse_addr (arg[0]))
    return ERROR;
#ifdef AS1750
  /* If symbol relocation, then set the relocation type to Byte_Reloc */
  if (relblk.n_used > 0)
    {
      struct reloc *rel = &relblk.data[relblk.n_used - 1];
      if (rel->frag_index == curr_frag && rel->obj_index == last)
	rel->reltype = Byte_Reloc;
    }
  obj->data[last] &= 0x00FF;
  obj->data[last] |= oc;
#else /* ASL */
  {
    const short target = (short) WAsmCode[0];
    const long curr_pc = (long) EProgCounter () & 0xFFFFL;
    const long diff = (long) target - curr_pc;
    if (diff < -128L || diff > 127L)
      return error
	("address distance too large in Instruction Counter Relative operation");
    WAsmCode[0] = oc | (ushort) (diff & 0xFFL);
  }
#endif
  return 1;
}

static ushort
as_b (ushort oc)
{
  char r;
  ushort br, lobyte;
  if (n_args != 2)
    return error ("incorrect number of operands");
  r = toupper (*arg[0]);
  if (r != 'B' && r != 'R')
    return error ("expecting base register");
  if ((br = get_num_bounded (arg[0] + 1, 12, 15)) == ERROR)
    return ERROR;
  if ((lobyte = get_num_bounded (arg[1], 0, 255)) == ERROR)
    return ERROR;
  add_word (oc | ((br - 12) << 8) | lobyte);
  return 1;
}

static ushort
as_bx (ushort oc)
{
  char r;
  ushort br, rx = 0;
  if (n_args != 2)
    return error ("incorrect number of operands");
  r = toupper (*arg[0]);
  if ((r != 'B' && r != 'R')
      || (br = get_num_bounded (arg[0] + 1, 12, 15)) == ERROR)
    return error ("expecting base register");
  if ((rx = get_regnum (arg[1])) == ERROR)
    return ERROR;
  if (rx == 0)
    return error ("R0 not an index register");
  add_word (oc | ((br - 12) << 8) | rx);
  return 1;
}

static ushort
as_r_imm (ushort oc)		/* for shifts with immediate shiftcount */
{
  ushort rb, n;
  if (n_args != 2)
    return error ("incorrect number of operands");
  if ((rb = get_regnum (arg[0])) == ERROR)
    return ERROR;
  if ((n = get_num_bounded (arg[1], 1, 16)) == ERROR)
    return ERROR;
  add_word (oc | ((n - 1) << 4) | rb);
  return 1;
}

static ushort
as_imm_r (ushort oc)		/* for test/set/reset-bit in reg. */
{
  ushort n, rb;
  if (n_args != 2)
    return error ("incorrect number of operands");
  if ((n = get_num_bounded (arg[0], 0, 15)) == ERROR)
    return ERROR;
  if ((rb = get_regnum (arg[1])) == ERROR)
    return ERROR;
  add_word (oc | (n << 4) | rb);
  return 1;
}

static ushort
as_jump (ushort oc)
{
  ushort condcode = 0xFFFF;
  static const struct
    {
      char *name;
      ushort value;
    }
  cond[] =
  {				/* CPZN */
    { "LT",  0x1 },		/* 0001 */
    { "LZ",  0x1 },		/* 0001 */
    { "EQ",  0x2 },		/* 0010 */
    { "EZ",  0x2 },		/* 0010 */
    { "LE",  0x3 },		/* 0011 */
    { "LEZ", 0x3 },		/* 0011 */
    { "GT",  0x4 },		/* 0100 */
    { "GTZ", 0x4 },		/* 0100 */
    { "NE",  0x5 },		/* 0101 */
    { "NZ",  0x5 },		/* 0101 */
    { "GE",  0x6 },		/* 0110 */
    { "GEZ", 0x6 },		/* 0110 */
    { "ALL", 0x7 },		/* 0111 */
    { "CY",  0x8 },		/* 1000 */
    { "CLT", 0x9 },		/* 1001 */
    { "CEQ", 0xA },		/* 1010 */
    { "CEZ", 0xA },		/* 1010 */
    { "CLE", 0xB },		/* 1011 */
    { "CGT", 0xC },		/* 1100 */
    { "CNZ", 0xD },		/* 1101 */
    { "CGE", 0xE },		/* 1110 */
    { "UC",  0xF }		/* 1111 */
  };
  if (n_args < 2)
    return error ("insufficient number of operands");
  if (isalpha (*arg[0]))
    {
      int i;
      for (i = 0; i < 22; i++)
	if (!strcasecmp (arg[0], cond[i].name))
	  {
	    condcode = cond[i].value;
	    break;
	  }
    }
  if (condcode == 0xFFFF)
    if ((condcode = get_num_bounded (arg[0], 0, 15)) == ERROR)
      return ERROR;
  add_word (oc | (condcode << 4));
  if (parse_addr (arg[1]))
    return ERROR;
  if (check_indexreg ())
    return ERROR;
  return 2;
}

static ushort
as_s (ushort oc)		/* For the moment, BEX only. */
{
  ushort lsnibble;
  if (n_args != 1)
    return error ("incorrect number of operands");
  if ((lsnibble = get_num_bounded (arg[0], 0, 15)) == ERROR)
    return ERROR;
  add_word (oc | lsnibble);
  return 1;
}

static ushort
as_sr (ushort oc)		/* XBR and URS. */
{
  ushort hlp;
  if (n_args != 1)
    return error ("incorrect number of operands");
  if ((hlp = get_regnum (arg[0])) == ERROR)
    return ERROR;
  add_word (oc | (hlp << 4));
  return 1;
}

static ushort
as_xio (ushort oc)
{
  ushort ra;
  int cmdfld = -1;
  static const struct
    {
      char *mnem;
      ushort cmd;
    }
  xio[] =
  {
    { "SMK",  0x2000 },
    { "CLIR", 0x2001 },
    { "ENBL", 0x2002 },
    { "DSBL", 0x2003 },
    { "RPI",  0x2004 },
    { "SPI",  0x2005 },
    { "OD",   0x2008 },
    { "RNS",  0x200A },
    { "WSW",  0x200E },
    { "CO",   0x4000 },
    { "CLC",  0x4001 },
    { "MPEN", 0x4003 },
    { "ESUR", 0x4004 },
    { "DSUR", 0x4005 },
    { "DMAE", 0x4006 },
    { "DMAD", 0x4007 },
    { "TAS",  0x4008 },
    { "TAH",  0x4009 },
    { "OTA",  0x400A },
    { "GO",   0x400B },
    { "TBS",  0x400C },
    { "TBH",  0x400D },
    { "OTB",  0x400E },
    { "LMP",  0x5000 },
    { "WIPR", 0x5100 },
    { "WOPR", 0x5200 },
    { "RMP",  0xD000 },
    { "RIPR", 0xD100 },
    { "ROPR", 0xD200 },
    { "RMK",  0xA000 },
    { "RIC1", 0xA001 },
    { "RIC2", 0xA002 },
    { "RPIR", 0xA004 },
    { "RDOR", 0xA008 },
    { "RDI",  0xA009 },
    { "TPIO", 0xA00B },
    { "RMFS", 0xA00D },
    { "RSW",  0xA00E },
    { "RCFR", 0xA00F },
    { "CI",   0xC000 },
    { "RCS",  0xC001 },
    { "ITA",  0xC00A },
    { "ITB",  0xC00E },
    { "", 0xFFFF }
  };

  if (n_args < 2)
    return error ("incorrect number of operands");
  if ((ra = get_regnum (arg[0])) == ERROR)
    return ERROR;
  add_word (oc | (ra << 4));
  /* Get the XIO command field. */
  if (isalpha (*arg[1]))
    {
      int i;
      for (i = 0; xio[i].cmd != 0xFFFF; i++)
	if (!strcasecmp (arg[1], xio[i].mnem))
	  break;
      if (xio[i].cmd != 0xFFFF)
	cmdfld = xio[i].cmd;
    }
  if (cmdfld == -1)
    if (get_sym_num (arg[1], &cmdfld) == NULL)
      return ERROR;
  add_word ((ushort) cmdfld);
  if (check_indexreg ())
    return ERROR;
  return 2;
}

static ushort
as_none (ushort oc)
{
  add_word (oc);
  return 1;
}

/* end of argument assembly functions */

/***********************************************************************/

static struct
  {
    char *mnemon;
    ushort opcode;
    ushort (*as_args) (ushort);
  } optab[] =
 {
  { "AISP", 0xA200, as_is },		/* Sorted by beginning letter. */
  { "AIM",  0x4A01, as_im_ocx },	/* Within each beginning letter, */
  { "AR",   0xA100, as_r },		/* sorted by approximate */
  { "A",    0xA000, as_mem },		/* instruction frequency. */
  { "ANDM", 0x4A07, as_im_ocx },
  { "ANDR", 0xE300, as_r },
  { "AND",  0xE200, as_mem },
  { "ABS",  0xA400, as_r },
  { "AB",   0x1000, as_b },
  { "ANDB", 0x3400, as_b },
  { "ABX",  0x4040, as_bx },
  { "ANDX", 0x40E0, as_bx },
  { "BEZ",  0x7500, as_icr },
  { "BNZ",  0x7A00, as_icr },
  { "BGT",  0x7900, as_icr },
  { "BLE",  0x7800, as_icr },
  { "BGE",  0x7B00, as_icr },
  { "BLT",  0x7600, as_icr },
  { "BR",   0x7400, as_icr },
  { "BEX",  0x7700, as_s },
  { "BPT",  0xFFFF, as_none },
  { "BIF",  0x4F00, as_s },
  { "CISP", 0xF200, as_is },
  { "CIM",  0x4A0A, as_im_ocx },
  { "CR",   0xF100, as_r },
  { "C",    0xF000, as_mem },
  { "CISN", 0xF300, as_is },
  { "CB",   0x3800, as_b },
  { "CBL",  0xF400, as_mem },
  { "CBX",  0x40C0, as_bx },
  { "DLR",  0x8700, as_r },
  { "DL",   0x8600, as_mem },
  { "DST",  0x9600, as_mem },
  { "DSLL", 0x6500, as_r_imm },
  { "DSRL", 0x6600, as_r_imm },
  { "DSRA", 0x6700, as_r_imm },
  { "DSLC", 0x6800, as_r_imm },
  { "DSLR", 0x6D00, as_r },
  { "DSAR", 0x6E00, as_r },
  { "DSCR", 0x6F00, as_r },
  { "DECM", 0xB300, as_im_1_16 },
  { "DAR",  0xA700, as_r },
  { "DA",   0xA600, as_mem },
  { "DSR",  0xB700, as_r },
  { "DS",   0xB600, as_mem },
  { "DMR",  0xC700, as_r },
  { "DM",   0xC600, as_mem },
  { "DDR",  0xD700, as_r },
  { "DD",   0xD600, as_mem },
  { "DCR",  0xF700, as_r },
  { "DC",   0xF600, as_mem },
  { "DLB",  0x0400, as_b },
  { "DSTB", 0x0C00, as_b },
  { "DNEG", 0xB500, as_r },
  { "DABS", 0xA500, as_r },
  { "DR",   0xD500, as_r },
  { "D",    0xD400, as_mem },
  { "DISP", 0xD200, as_is },
  { "DIM",  0x4A05, as_im_ocx },
  { "DISN", 0xD300, as_is },
  { "DVIM", 0x4A06, as_im_ocx },
  { "DVR",  0xD100, as_r },
  { "DV",   0xD000, as_mem },
  { "DLI",  0x8800, as_mem },
  { "DSTI", 0x9800, as_mem },
  { "DB",   0x1C00, as_b },
  { "DBX",  0x4070, as_bx },
  { "DLBX", 0x4010, as_bx },
  { "DSTX", 0x4030, as_bx },
  { "DLE",  0xDF00, as_xmem },
  { "DSTE", 0xDD00, as_xmem },
  { "EFL",  0x8A00, as_mem },
  { "EFST", 0x9A00, as_mem },
  { "EFCR", 0xFB00, as_r },
  { "EFC",  0xFA00, as_mem },
  { "EFAR", 0xAB00, as_r },
  { "EFA",  0xAA00, as_mem },
  { "EFSR", 0xBB00, as_r },
  { "EFS",  0xBA00, as_mem },
  { "EFMR", 0xCB00, as_r },
  { "EFM",  0xCA00, as_mem },
  { "EFDR", 0xDB00, as_r },
  { "EFD",  0xDA00, as_mem },
  { "EFLT", 0xEB00, as_r },
  { "EFIX", 0xEA00, as_r },
  { "FAR",  0xA900, as_r },
  { "FA",   0xA800, as_mem },
  { "FSR",  0xB900, as_r },
  { "FS",   0xB800, as_mem },
  { "FMR",  0xC900, as_r },
  { "FM",   0xC800, as_mem },
  { "FDR",  0xD900, as_r },
  { "FD",   0xD800, as_mem },
  { "FCR",  0xF900, as_r },
  { "FC",   0xF800, as_mem },
  { "FABS", 0xAC00, as_r },
  { "FIX",  0xE800, as_r },
  { "FLT",  0xE900, as_r },
  { "FNEG", 0xBC00, as_r },
  { "FAB",  0x2000, as_b },
  { "FABX", 0x4080, as_bx },
  { "FSB",  0x2400, as_b },
  { "FSBX", 0x4090, as_bx },
  { "FMB",  0x2800, as_b },
  { "FMBX", 0x40A0, as_bx },
  { "FDB",  0x2C00, as_b },
  { "FDBX", 0x40B0, as_bx },
  { "FCB",  0x3C00, as_b },
  { "FCBX", 0x40D0, as_bx },
  { "INCM", 0xA300, as_im_1_16 },
  { "JC",   0x7000, as_jump },
  { "J",    0x7400, as_icr },	/* TBD (GAS) */
  { "JEZ",  0x7500, as_icr },	/* TBD (GAS) */
  { "JLE",  0x7800, as_icr },	/* TBD (GAS) */
  { "JGT",  0x7900, as_icr },	/* TBD (GAS) */
  { "JNZ",  0x7A00, as_icr },	/* TBD (GAS) */
  { "JGE",  0x7B00, as_icr },	/* TBD (GAS) */
  { "JLT",  0x7600, as_icr },	/* TBD (GAS) */
  { "JCI",  0x7100, as_jump },
  { "JS",   0x7200, as_mem },
  { "LISP", 0x8200, as_is },
  { "LIM",  0x8500, as_mem },
  { "LR",   0x8100, as_r },
  { "L",    0x8000, as_mem },
  { "LISN", 0x8300, as_is },
  { "LB",   0x0000, as_b },
  { "LBX",  0x4000, as_bx },
  { "LSTI", 0x7C00, as_addr },
  { "LST",  0x7D00, as_addr },
  { "LI",   0x8400, as_mem },
  { "LM",   0x8900, as_im_0_15 },
  { "LUB",  0x8B00, as_mem },
  { "LLB",  0x8C00, as_mem },
  { "LUBI", 0x8D00, as_mem },
  { "LLBI", 0x8E00, as_mem },
  { "LE",   0xDE00, as_xmem },
  { "MISP", 0xC200, as_is },
  { "MSIM", 0x4A04, as_im_ocx },
  { "MSR",  0xC100, as_r },
  { "MS",   0xC000, as_mem },
  { "MISN", 0xC300, as_is },
  { "MIM",  0x4A03, as_im_ocx },
  { "MR",   0xC500, as_r },
  { "M",    0xC400, as_mem },
  { "MOV",  0x9300, as_r },
  { "MB",   0x1800, as_b },
  { "MBX",  0x4060, as_bx },
  { "NEG",  0xB400, as_r },
  { "NOP",  0xFF00, as_none },
  { "NIM",  0x4A0B, as_im_ocx },
  { "NR",   0xE700, as_r },
  { "N",    0xE600, as_mem },
  { "ORIM", 0x4A08, as_im_ocx },
  { "ORR",  0xE100, as_r },
  { "OR",   0xE000, as_mem },
  { "ORB",  0x3000, as_b },
  { "ORBX", 0x40F0, as_bx },
  { "PSHM", 0x9F00, as_r },
  { "POPM", 0x8F00, as_r },
  { "RBR",  0x5400, as_imm_r },
  { "RVBR", 0x5C00, as_r },
  { "RB",   0x5300, as_im_0_15 },
  { "RBI",  0x5500, as_im_0_15 },
  { "ST",   0x9000, as_mem },
  { "STC",  0x9100, as_im_0_15 },
  { "SISP", 0xB200, as_is },
  { "SIM",  0x4A02, as_im_ocx },
  { "SR",   0xB100, as_r },
  { "S",    0xB000, as_mem },
  { "SLL",  0x6000, as_r_imm },
  { "SRL",  0x6100, as_r_imm },
  { "SRA",  0x6200, as_r_imm },
  { "SLC",  0x6300, as_r_imm },
  { "SLR",  0x6A00, as_r },
  { "SAR",  0x6B00, as_r },
  { "SCR",  0x6C00, as_r },
  { "SJS",  0x7E00, as_mem },
  { "STB",  0x0800, as_b },
  { "SBR",  0x5100, as_imm_r },
  { "SB",   0x5000, as_im_0_15 },
  { "SVBR", 0x5A00, as_r },
  { "SOJ",  0x7300, as_mem },
  { "SBB",  0x1400, as_b },
  { "STBX", 0x4020, as_bx },
  { "SBBX", 0x4050, as_bx },
  { "SBI",  0x5200, as_im_0_15 },
  { "STZ",  0x9100, as_addr },
  { "STCI", 0x9200, as_im_0_15 },
  { "STI",  0x9400, as_mem },
  { "SFBS", 0x9500, as_r },
  { "SRM",  0x9700, as_mem },
  { "STM",  0x9900, as_im_0_15 },
  { "STUB", 0x9B00, as_mem },
  { "STLB", 0x9C00, as_mem },
  { "SUBI", 0x9D00, as_mem },
  { "SLBI", 0x9E00, as_mem },
  { "STE",  0xDC00, as_xmem },
  { "TBR",  0x5700, as_imm_r },
  { "TB",   0x5600, as_im_0_15 },
  { "TBI",  0x5800, as_im_0_15 },
  { "TSB",  0x5900, as_im_0_15 },
  { "TVBR", 0x5E00, as_r },
  { "URS",  0x7F00, as_sr },
  { "UAR",  0xAD00, as_r },
  { "UA",   0xAE00, as_mem },
  { "USR",  0xBD00, as_r },
  { "US",   0xBE00, as_mem },
  { "UCIM", 0xF500, as_im_ocx },
  { "UCR",  0xFC00, as_r },
  { "UC",   0xFD00, as_mem },
  { "VIO",  0x4900, as_mem },
  { "XORR", 0xE500, as_r },
  { "XORM", 0x4A09, as_im_ocx },
  { "XOR",  0xE400, as_mem },
  { "XWR",  0xED00, as_r },
  { "XBR",  0xEC00, as_sr },
  { "XIO",  0x4800, as_xio },
  { "", 0, as_none }			/* end-of-array marker */
};

/* Offset table with indexes into optab: indexed by starting letter
   (A => 0, B => 1, ..., Z => 25), returns the starting index
   and number of entries for that letter in optab[].
   start_index is -1 if no entries for that starting letter in optab[]. */

static struct
  {
    int start_index;
    int n_entries;
  }
ofstab[26];

/* Initialize ofstab[]. This must be called before as1750() can be used. */
void
init_as1750 ()
{
  int optab_ndx, ofstab_ndx = 1;
  char c;

  ofstab[0].start_index = 0;
  ofstab[0].n_entries = 1;
  for (optab_ndx = 1; (c = *optab[optab_ndx].mnemon) != '\0'; optab_ndx++)
    {
      if (c != *optab[optab_ndx - 1].mnemon)
	{
	  while (ofstab_ndx != c - 'A')
	    ofstab[ofstab_ndx++].start_index = -1;
	  ofstab[ofstab_ndx].start_index = optab_ndx;
	  ofstab[ofstab_ndx++].n_entries = 1;
	}
      else
	ofstab[ofstab_ndx - 1].n_entries++;
    }
}


/************************** HERE'S THE BEEF: ****************************/

ushort
as1750 (char *operation, int n_operands, char *operand[])
{
  int i, optab_ndx, firstletter;
  bool found = FALSE;

  if (n_operands < 0 || n_operands > 3 || *operation < 'A' || *operation > 'X')
    return NO_OPCODE;
  n_args = n_operands;
  for (i = 0; i < n_args; i++)
    arg[i] = operand[i];
  firstletter = (int) (*operation - 'A');
  if ((optab_ndx = ofstab[firstletter].start_index) < 0)
    return NO_OPCODE;
  for (i = 0; i < ofstab[firstletter].n_entries; i++, optab_ndx++)
    if (!strcmp (operation + 1, optab[optab_ndx].mnemon + 1))
      {
	found = TRUE;
	break;
      }
  if (!found)
    return NO_OPCODE;
  return (*optab[optab_ndx].as_args) (optab[optab_ndx].opcode);
}

