/* tc-h8300.c -- Assemble code for the Hitachi H8/300
   Copyright (C) 1991, 1992 Free Software Foundation.

   This file is part of GAS, the GNU Assembler.

   GAS is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   GAS is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GAS; see the file COPYING.  If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */


/*
  Written By Steve Chamberlain
  sac@cygnus.com
  */

#include <stdio.h>
#include "as.h"
#include "bfd.h"
#include "opcode/h8300.h"
#include <ctype.h>

const char comment_chars[] =
{';', 0};
const char line_separator_chars[] =
{'$', 0};
const char line_comment_chars[] = "";

/* This table describes all the machine specific pseudo-ops the assembler
   has to support.  The fields are:
   pseudo-op name without dot
   function to call to execute this pseudo-op
   Integer arg to pass to the function
   */

void cons ();

const pseudo_typeS md_pseudo_table[] =
{
  {"int", cons, 2},
  {"data.b", cons, 1},
  {"data.w", cons, 2},
  {"data.l", cons, 4},
  {"form", listing_psize, 0},
  {"heading", listing_title, 0},
  {"import", s_ignore, 0},
  {"page", listing_eject, 0},
  {"program", s_ignore, 0},
  {0, 0, 0}
};

int md_reloc_size;

const char EXP_CHARS[] = "eE";

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


const relax_typeS md_relax_table[1];


static struct hash_control *opcode_hash_control;	/* Opcode mnemonics */

/*
  This function is called once, at assembler startup time.  This should
  set up all the tables, etc that the MD part of the assembler needs
  */
#if 0
/* encode the size and number into the number field
   xxnnnn
   00    8 bit
   01    16 bit
   10    ccr
   nnnnreg number
   */
#define WORD_REG 0x10
#define BYTE_REG 0x00
#define CCR_REG  0x20
struct reg_entry
{
  char *name;
  char number;
};

struct reg_entry reg_list[] =
{
  "r0", WORD_REG + 0,
  "r1", WORD_REG + 1,
  "r2", WORD_REG + 2,
  "r3", WORD_REG + 3,
  "r4", WORD_REG + 4,
  "r5", WORD_REG + 5,
  "r6", WORD_REG + 6,
  "r7", WORD_REG + 7,
  "fp", WORD_REG + 6,
  "sp", WORD_REG + 7,
  "r0h", BYTE_REG + 0,
  "r0l", BYTE_REG + 1,
  "r1h", BYTE_REG + 2,
  "r1l", BYTE_REG + 3,
  "r2h", BYTE_REG + 4,
  "r2l", BYTE_REG + 5,
  "r3h", BYTE_REG + 6,
  "r3l", BYTE_REG + 7,
  "r4h", BYTE_REG + 8,
  "r4l", BYTE_REG + 9,
  "r5h", BYTE_REG + 10,
  "r5l", BYTE_REG + 11,
  "r6h", BYTE_REG + 12,
  "r6l", BYTE_REG + 13,
  "r7h", BYTE_REG + 14,
  "r7l", BYTE_REG + 15,
  "ccr", CCR_REG,
  0, 0
}
 ;

#endif


void
md_begin ()
{
  struct h8_opcode *opcode;
  const struct reg_entry *reg;
  char prev_buffer[100];
  int idx = 0;

  opcode_hash_control = hash_new ();
  prev_buffer[0] = 0;

  for (opcode = h8_opcodes; opcode->name; opcode++)
    {
      /* Strip off any . part when inserting the opcode and only enter
         unique codes into the hash table
         */
      char *src = opcode->name;
      unsigned int len = strlen (src);
      char *dst = malloc (len + 1);
      char *buffer = dst;

      opcode->size = 0;
      while (*src)
	{
	  if (*src == '.')
	    {
	      *dst++ = 0;
	      src++;
	      opcode->size = *src;
	      break;
	    }
	  *dst++ = *src++;
	}
      if (strcmp (buffer, prev_buffer))
	{
	  hash_insert (opcode_hash_control, buffer, (char *) opcode);
	  strcpy (prev_buffer, buffer);
	  idx++;
	}
      opcode->idx = idx;


      /* Find the number of operands */
      opcode->noperands = 0;
      while (opcode->args.nib[opcode->noperands] != E)
	opcode->noperands++;
      /* Find the length of the opcode in bytes */
      opcode->length = 0;
      while (opcode->data.nib[opcode->length * 2] != E)
	opcode->length++;
    }

}


struct h8_exp
{
  char *e_beg;
  char *e_end;
  expressionS e_exp;
};
struct h8_op
{
  unsigned int dispreg;
  op_type mode;
  unsigned reg;
  expressionS exp;
};

/*
  parse operands
  WREG r0,r1,r2,r3,r4,r5,r6,r7,fp,sp
  r0l,r0h,..r7l,r7h
  @WREG
  @WREG+
  @-WREG
  #const

  */

op_type r8_sord[] =
{RS8, RD8};
op_type r16_sord[] =
{RS16, RD16};
op_type rind_sord[] =
{RSIND, RDIND};
op_type abs_sord[2] =
{ABS16SRC, ABS16DST};
op_type disp_sord[] =
{DISPSRC, DISPDST};

/* try and parse a reg name, returns number of chars consumed */
int
DEFUN (parse_reg, (src, mode, reg, dst),
       char *src AND
       op_type * mode AND
       unsigned int *reg AND
       int dst)
{
  if (src[0] == 's' && src[1] == 'p')
    {
      *mode = r16_sord[dst];
      *reg = 7;
      return 2;
    }
  if (src[0] == 'c' && src[1] == 'c' && src[2] == 'r')
    {
      *mode = CCR;
      *reg = 0;
      return 3;
    }
  if (src[0] == 'f' && src[1] == 'p')
    {
      *mode = r16_sord[dst];
      *reg = 6;
      return 2;
    }
  if (src[0] == 'r')
    {
      if (src[1] >= '0' && src[1] <= '7')
	{
	  if (src[2] == 'l')
	    {
	      *mode = r8_sord[dst];
	      *reg = (src[1] - '0') + 8;
	      return 3;
	    }
	  if (src[2] == 'h')
	    {
	      *mode = r8_sord[dst];
	      *reg = (src[1] - '0');
	      return 3;
	    }
	  *mode = r16_sord[dst];
	  *reg = (src[1] - '0');
	  return 2;
	}
    }
  return 0;
}

char *
DEFUN (parse_exp, (s, op),
       char *s AND
       expressionS * op)
{
  char *save = input_line_pointer;
  char *new;
  segT seg;

  input_line_pointer = s;
  seg = expr (0, op);
  new = input_line_pointer;
  input_line_pointer = save;
  if (SEG_NORMAL (seg))
    return new;
  switch (seg)
    {
    case SEG_ABSOLUTE:
    case SEG_UNKNOWN:
    case SEG_DIFFERENCE:
    case SEG_BIG:
    case SEG_REGISTER:
      return new;
    case SEG_ABSENT:
      as_bad ("Missing operand");
      return new;
    default:
      as_bad ("Don't understand operand of type %s", segment_name (seg));
      return new;
    }
}

static char *
DEFUN (skip_colonthing, (ptr, exp),
       char *ptr AND
       expressionS * exp)
{
  if (*ptr == ':')
    {
      ptr++;
      if (*ptr == '8')
	{
	  ptr++;
	  /* ff fill any 8 bit quantity */
	  exp->X_add_number |= 0xff00;
	}
      else
	{
	  while (isdigit (*ptr))
	    ptr++;
	}
    }
  return ptr;
}

/* The many forms of operand:

   Rn			Register direct
   @Rn			Register indirect
   @(exp[:16], Rn)	Register indirect with displacement
   @Rn+
   @-Rn
   @aa:8			absolute 8 bit
   @aa:16			absolute 16 bit
   @aa			absolute 16 bit

   #xx[:size]		immediate data
   @(exp:[8], pc)		pc rel
   @@aa[:8]		memory indirect

   */

static void
DEFUN (get_operand, (ptr, op, dst),
       char **ptr AND
       struct h8_op *op AND
       unsigned int dst)
{
  char *src = *ptr;
  op_type mode;
  unsigned int num;
  unsigned int len;
  unsigned int size;

  op->mode = E;

  len = parse_reg (src, &op->mode, &op->reg, dst);
  if (len)
    {
      *ptr = src + len;
      return;
    }

  if (*src == '@')
    {
      src++;
      if (*src == '@')
	{
	  src++;
	  src = parse_exp (src, &op->exp);
	  src = skip_colonthing (src, &op->exp);

	  *ptr = src;

	  op->mode = MEMIND;
	  return;

	}


      if (*src == '-')
	{
	  src++;
	  len = parse_reg (src, &mode, &num, dst);
	  if (len == 0)
	    {
	      /* Oops, not a reg after all, must be ordinary exp */
	      src--;
	      /* must be a symbol */
	      op->mode = abs_sord[dst];
	      *ptr = skip_colonthing (parse_exp (src, &op->exp), &op->exp);

	      return;


	    }

	  if (mode != r16_sord[dst])
	    {
	      as_bad ("@- needs word register");
	    }
	  op->mode = RDDEC;
	  op->reg = num;
	  *ptr = src + len;
	  return;
	}
      if (*src == '(' && ')')
	{
	  /* Disp */
	  src++;
	  src = parse_exp (src, &op->exp);

	  if (*src == ')')
	    {
	      src++;
	      op->mode = abs_sord[dst];
	      *ptr = src;
	      return;
	    }
	  src = skip_colonthing (src, &op->exp);

	  if (*src != ',')
	    {
	      as_bad ("expected @(exp, reg16)");
	      return;

	    }
	  src++;
	  len = parse_reg (src, &mode, &op->reg, dst);
	  if (len == 0 || mode != r16_sord[dst])
	    {
	      as_bad ("expected @(exp, reg16)");
	      return;
	    }
	  op->mode = disp_sord[dst];
	  src += len;
	  src = skip_colonthing (src, &op->exp);

	  if (*src != ')' && '(')
	    {
	      as_bad ("expected @(exp, reg16)");
	      return;
	    }
	  *ptr = src + 1;

	  return;
	}
      len = parse_reg (src, &mode, &num, dst);

      if (len)
	{
	  src += len;
	  if (*src == '+')
	    {
	      src++;
	      if (mode != RS16)
		{
		  as_bad ("@Rn+ needs src word register");
		  return;
		}
	      op->mode = RSINC;
	      op->reg = num;
	      *ptr = src;
	      return;
	    }
	  if (mode != r16_sord[dst])
	    {
	      as_bad ("@Rn needs word register");
	      return;

	    }
	  op->mode = rind_sord[dst];
	  op->reg = num;
	  *ptr = src;

	  return;
	}
      else
	{
	  /* must be a symbol */
	  op->mode = abs_sord[dst];
	  *ptr = skip_colonthing (parse_exp (src, &op->exp), &op->exp);

	  return;
	}
    }


  if (*src == '#')
    {
      src++;
      op->mode = IMM16;
      src = parse_exp (src, &op->exp);
      *ptr = skip_colonthing (src, &op->exp);

      return;
    }
  else
    {
      *ptr = parse_exp (src, &op->exp);
      op->mode = DISP8;
    }
}


static
char *
DEFUN (get_operands, (noperands, op_end, operand),
       unsigned int noperands AND
       char *op_end AND
       struct h8_op *operand)
{
  char *ptr = op_end;

  switch (noperands)
    {
    case 0:
      operand[0].mode = 0;
      operand[1].mode = 0;
      break;

    case 1:
      ptr++;
      get_operand (&ptr, operand + 0, 0);
      operand[1].mode = 0;
      break;

    case 2:
      ptr++;
      get_operand (&ptr, operand + 0, 0);
      if (*ptr == ',')
	ptr++;
      get_operand (&ptr, operand + 1, 1);
      break;

    default:
      abort ();
    }


  return ptr;
}

/* Passed a pointer to a list of opcodes which use different
   addressing modes, return the opcode which matches the opcodes
   provided
   */
static
struct h8_opcode *
DEFUN (get_specific, (opcode, operands),
       struct h8_opcode *opcode AND
       struct h8_op *operands)

{
  struct h8_opcode *this_try = opcode;
  int found = 0;
  unsigned int noperands = opcode->noperands;

  unsigned int dispreg;
  unsigned int this_index = opcode->idx;

  while (this_index == opcode->idx && !found)
    {
      unsigned int i;

      this_try = opcode++;
      for (i = 0; i < noperands; i++)
	{
	  op_type op = (this_try->args.nib[i]) & ~(B30 | B31);

	  switch (op)
	    {
	    case Hex0:
	    case Hex1:
	    case Hex2:
	    case Hex3:
	    case Hex4:
	    case Hex5:
	    case Hex6:
	    case Hex7:
	    case Hex8:
	    case Hex9:
	    case HexA:
	    case HexB:
	    case HexC:
	    case HexD:
	    case HexE:
	    case HexF:
	      break;
	    case DISPSRC:
	    case DISPDST:
	      operands[0].dispreg = operands[i].reg;
	    case RD8:
	    case RS8:
	    case RDIND:
	    case RSIND:
	    case RD16:
	    case RS16:
	    case CCR:
	    case RSINC:
	    case RDDEC:
	      if (operands[i].mode != op)
		goto fail;
	      break;
	    case KBIT:
	    case IMM16:
	    case IMM3:
	    case IMM8:
	      if (operands[i].mode != IMM16)
		goto fail;
	      break;
	    case MEMIND:
	      if (operands[i].mode != MEMIND)
		goto fail;
	      break;
	    case ABS16SRC:
	    case ABS8SRC:
	    case ABS16OR8SRC:
	    case ABS16ORREL8SRC:
	      if (operands[i].mode == DISP8)
		{
		  /* For compatibility with other assemblers, treat jsr 0xfff
		     the same as jsr @0xfff */


		}
	      else
		{
		  if (operands[i].mode != ABS16SRC)
		    goto fail;
		}
	      break;
	    case ABS16OR8DST:
	    case ABS16DST:
	    case ABS8DST:
	      if (operands[i].mode != ABS16DST)
		goto fail;
	      break;
	    }
	}
      found = 1;
    fail:;
    }
  if (found)
    return this_try;
  else
    return 0;
}

static void
DEFUN (check_operand, (operand, width, string),
       struct h8_op *operand AND
       unsigned int width AND
       char *string)
{
  if (operand->exp.X_add_symbol == 0
      && operand->exp.X_subtract_symbol == 0)
    {

      /* No symbol involved, let's look at offset, it's dangerous if any of
         the high bits are not 0 or ff's, find out by oring or anding with
         the width and seeing if the answer is 0 or all fs*/
      if ((operand->exp.X_add_number & ~width) != 0 &&
	  (operand->exp.X_add_number | width) != (~0))
	{
	  as_warn ("operand %s0x%x out of range.", string, operand->exp.X_add_number);
	}
    }

}

/* Now we know what sort of opcodes it is, lets build the bytes -
 */
static void
DEFUN (build_bytes, (this_try, operand),
       struct h8_opcode *this_try AND
       struct h8_op *operand)

{
  unsigned int i;

  char *output = frag_more (this_try->length);
  char *output_ptr = output;
  op_type *nibble_ptr = this_try->data.nib;
  char part;
  op_type c;
  char high;
  int nib;

top:;
  while (*nibble_ptr != E)
    {
      int nibble;

      for (nibble = 0; nibble < 2; nibble++)
	{
	  c = *nibble_ptr & ~(B30 | B31);
	  switch (c)
	    {
	    default:
	      abort ();
	    case KBIT:
	      switch (operand[0].exp.X_add_number)
		{
		case 1:
		  nib = 0;
		  break;
		case 2:
		  nib = 8;
		  break;
		default:
		  as_bad ("Need #1 or #2 here");
		  break;
		}
	      /* stop it making a fix */
	      operand[0].mode = 0;
	      break;
	    case 0:
	    case 1:
	    case 2:
	    case 3:
	    case 4:
	    case 5:
	    case 6:
	    case 7:
	    case 8:
	    case 9:
	    case 10:
	    case 11:
	    case 12:
	    case 13:
	    case 14:
	    case 15:
	      nib = c;
	      break;
	    case DISPREG:
	      nib = operand[0].dispreg;
	      break;
	    case IMM8:
	      operand[0].mode = IMM8;
	      nib = 0;
	      break;

	    case DISPDST:
	      nib = 0;
	      break;
	    case IMM3:
	      if (operand[0].exp.X_add_symbol == 0)
		{
		  operand[0].mode = 0;	/* stop it making a fix */
		  nib = (operand[0].exp.X_add_number);
		}
	      else
		as_bad ("can't have symbol for bit number");
	      if (nib < 0 || nib > 7)
		{
		  as_bad ("Bit number out of range %d", nib);
		}

	      break;

	    case ABS16DST:
	      nib = 0;
	      break;
	    case ABS8DST:
	      operand[1].mode = ABS8DST;
	      nib = 0;
	      break;
	    case ABS8SRC:
	      operand[0].mode = ABS8SRC;
	      nib = 0;
	      break;
	    case ABS16OR8DST:
	      operand[1].mode = c;

	      nib = 0;

	      break;

	    case ABS16ORREL8SRC:
	      operand[0].mode = c;
	      nib = 0;
	      break;

	    case ABS16OR8SRC:
	      operand[0].mode = ABS16OR8SRC;
	      nib = 0;
	      break;
	    case DISPSRC:
	      operand[0].mode = ABS16SRC;
	      nib = 0;
	      break;

	    case DISP8:
	      operand[0].mode = DISP8;
	      nib = 0;
	      break;

	    case ABS16SRC:
	    case IMM16:
	    case IGNORE:
	    case MEMIND:

	      nib = 0;
	      break;
	    case RS8:
	    case RS16:
	    case RSIND:
	    case RSINC:
	      nib = operand[0].reg;
	      break;

	    case RD8:
	    case RD16:
	    case RDDEC:
	    case RDIND:
	      nib = operand[1].reg;
	      break;

	    case E:
	      abort ();
	      break;
	    }
	  if (*nibble_ptr & B31)
	    {
	      nib |= 0x8;
	    }

	  if (nibble == 0)
	    {
	      *output_ptr = nib << 4;
	    }
	  else
	    {
	      *output_ptr |= nib;
	      output_ptr++;
	    }
	  nibble_ptr++;
	}

    }

  /* output any fixes */
  for (i = 0; i < 2; i++)
    {
      switch (operand[i].mode)
	{
	case 0:
	  break;

	case DISP8:
	  check_operand (operand + i, 0x7f, "@");

	  if (operand[i].exp.X_add_number & 1)
	    {
	      as_warn ("branch operand has odd offset (%x)\n",
		       operand->exp.X_add_number);
	    }

	  fix_new (frag_now,
		   output - frag_now->fr_literal + 1,
		   1,
		   operand[i].exp.X_add_symbol,
		   operand[i].exp.X_subtract_symbol,
		   (char) (operand[i].exp.X_add_number - 1),
		   1,
		   R_PCRBYTE);
	  break;
	case IMM8:
	  check_operand (operand + i, 0xff, "#");
	  /* If there is nothing else going on we can safely
             reloc in place */
	  if (operand[i].exp.X_add_symbol == 0)
	    {
	      output[1] = operand[i].exp.X_add_number;
	    }
	  else
	    {
	      fix_new (frag_now,
		       output - frag_now->fr_literal + 1,
		       1,
		       operand[i].exp.X_add_symbol,
		       operand[i].exp.X_subtract_symbol,
		       (short) (operand[i].exp.X_add_number),
		       0,
		       R_RELBYTE);
	    }

	  break;
	case MEMIND:
	  check_operand (operand + i, 0xff, "@@");
	  fix_new (frag_now,
		   output - frag_now->fr_literal + 1,
		   1,
		   operand[i].exp.X_add_symbol,
		   operand[i].exp.X_subtract_symbol,
		   operand[i].exp.X_add_number,
		   0,
		   R_RELBYTE);
	  break;
	case ABS8DST:
	case ABS8SRC:
#if 0
	  /* Unwanted*/
	  check_operand (operand + i, 0xff, "@");
#endif
	  fix_new (frag_now,
		   output - frag_now->fr_literal + 1,
		   1,
		   operand[i].exp.X_add_symbol,
		   operand[i].exp.X_subtract_symbol,
		   (char) (operand[i].exp.X_add_number),
		   0,
		   R_RELBYTE);
	  break;

	case ABS16OR8SRC:
	case ABS16OR8DST:

	  check_operand (operand + i, 0xffff, "@");

	  fix_new (frag_now,
		   output - frag_now->fr_literal + 2,
		   2,
		   operand[i].exp.X_add_symbol,
		   operand[i].exp.X_subtract_symbol,
		   (short) (operand[i].exp.X_add_number),
		   0,
		   R_MOVB1);
	  break;

	case ABS16ORREL8SRC:
	  check_operand (operand + i, 0xffff, "@");
	  if (operand[i].exp.X_add_number & 1)
	    {
	      as_warn ("branch operand has odd offset (%x)\n",
		       operand->exp.X_add_number);
	    }
	  fix_new (frag_now,
		   output - frag_now->fr_literal + 2,
		   2,
		   operand[i].exp.X_add_symbol,
		   operand[i].exp.X_subtract_symbol,
		   (short) (operand[i].exp.X_add_number),
		   0,
		   R_JMP1);
	  break;


	case ABS16SRC:
	case ABS16DST:
	case IMM16:
	case DISPSRC:
	case DISPDST:
	  check_operand (operand + i, 0xffff, "@");
	  if (operand[i].exp.X_add_symbol == 0)
	    {
	      /* This should be done with bfd */
	      output[3] = operand[i].exp.X_add_number & 0xff;
	      output[2] = operand[i].exp.X_add_number >> 8;

	    }
	  else
	    {

	      fix_new (frag_now,
		       output - frag_now->fr_literal + 2,
		       2,
		       operand[i].exp.X_add_symbol,
		       operand[i].exp.X_subtract_symbol,
		       (short) (operand[i].exp.X_add_number),
		       0,
		       R_RELWORD);
	    }

	  break;
	case RS8:
	case RD8:
	case RS16:
	case RD16:
	case RDDEC:
	case KBIT:
	case RSINC:
	case RDIND:
	case RSIND:
	case CCR:

	  break;
	default:
	  abort ();
	}
    }

}

/*
  try and give an intelligent error message for common and simple to
  detect errors
  */

static void
DEFUN (clever_message, (opcode, operand),
       struct h8_opcode *opcode AND
       struct h8_op *operand)
{
  struct h8_opcode *scan = opcode;

  /* Find out if there was more than one possible opccode */

  if ((opcode + 1)->idx != opcode->idx)
    {
      unsigned int argn;

      /* Only one opcode of this flavour, try and guess which operand
         didn't match */
      for (argn = 0; argn < opcode->noperands; argn++)
	{
	  switch (opcode->args.nib[argn])
	    {
	    case RD16:
	      if (operand[argn].mode != RD16)
		{
		  as_bad ("destination operand must be 16 bit register");
		  return;

		}
	      break;

	    case RS8:

	      if (operand[argn].mode != RS8)
		{
		  as_bad ("source operand must be 8 bit register");
		  return;
		}
	      break;

	    case ABS16DST:
	      if (operand[argn].mode != ABS16DST)
		{
		  as_bad ("destination operand must be 16bit absolute address");
		  return;
		}
	      break;
	    case RD8:
	      if (operand[argn].mode != RD8)
		{
		  as_bad ("destination operand must be 8 bit register");
		  return;
		}
	      break;


	    case ABS16SRC:
	      if (operand[argn].mode != ABS16SRC)
		{
		  as_bad ("source operand must be 16bit absolute address");
		  return;
		}
	      break;

	    }
	}
    }
  as_bad ("invalid operands");
}

/* This is the guts of the machine-dependent assembler.  STR points to a
   machine dependent instruction.  This funciton is supposed to emit
   the frags/bytes it assembles to.
   */



void
DEFUN (md_assemble, (str),
       char *str)
{
  char *op_start;
  char *op_end;
  unsigned int i;
  struct h8_op operand[2];
  struct h8_opcode *opcode;
  struct h8_opcode *prev_opcode;

  char *dot = 0;
  char c;

  /* Drop leading whitespace */
  while (*str == ' ')
    str++;

  /* find the op code end */
  for (op_start = op_end = str;
       *op_end != 0 && *op_end != ' ';
       op_end++)
    {
      if (*op_end == '.')
	{
	  dot = op_end + 1;
	  *op_end = 0;
	  op_end += 2;
	  break;
	}
    }

  ;

  if (op_end == op_start)
    {
      as_bad ("can't find opcode ");
    }
  c = *op_end;

  *op_end = 0;

  opcode = (struct h8_opcode *) hash_find (opcode_hash_control,
					   op_start);

  if (opcode == NULL)
    {
      as_bad ("unknown opcode");
      return;
    }


  input_line_pointer = get_operands (opcode->noperands, op_end,
				     operand);
  *op_end = c;
  prev_opcode = opcode;

  opcode = get_specific (opcode, operand);

  if (opcode == 0)
    {
      /* Couldn't find an opcode which matched the operands */
      char *where = frag_more (2);

      where[0] = 0x0;
      where[1] = 0x0;
      clever_message (prev_opcode, operand);

      return;
    }
  if (opcode->size && dot)
    {
      if (opcode->size != *dot)
	{
	  as_warn ("mismatch between opcode size and operand size");
	}
    }

  build_bytes (opcode, operand);

}

void
DEFUN (tc_crawl_symbol_chain, (headers),
       object_headers * headers)
{
  printf ("call to tc_crawl_symbol_chain \n");
}

symbolS *
DEFUN (md_undefined_symbol, (name),
       char *name)
{
  return 0;
}

void
DEFUN (tc_headers_hook, (headers),
       object_headers * headers)
{
  printf ("call to tc_headers_hook \n");
}

void
DEFUN_VOID (md_end)
{
}

/* Various routines to kill one day */
/* 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 (type, litP, sizeP)
     char type;
     char *litP;
     int *sizeP;
{
  int prec;
  LITTLENUM_TYPE words[MAX_LITTLENUMS];
  LITTLENUM_TYPE *wordP;
  char *t;
  char *atof_ieee ();

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

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

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

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

    default:
      *sizeP = 0;
      return "Bad call to MD_ATOF()";
    }
  t = atof_ieee (input_line_pointer, type, words);
  if (t)
    input_line_pointer = t;

  *sizeP = prec * sizeof (LITTLENUM_TYPE);
  for (wordP = words; prec--;)
    {
      md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE));
      litP += sizeof (LITTLENUM_TYPE);
    }
  return "";			/* Someone should teach Dean about null pointers */
}

int
md_parse_option (argP, cntP, vecP)
     char **argP;
     int *cntP;
     char ***vecP;

{
  return 0;

}

int md_short_jump_size;

void
tc_aout_fix_to_chars ()
{
  printf ("call to tc_aout_fix_to_chars \n");
  abort ();
}

void
md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol)
     char *ptr;
     long from_addr;
     long to_addr;
     fragS *frag;
     symbolS *to_symbol;
{
  as_fatal ("failed sanity check.");
}

void
md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
     char *ptr;
     long from_addr, to_addr;
     fragS *frag;
     symbolS *to_symbol;
{
  as_fatal ("failed sanity check.");
}

void
md_convert_frag (headers, fragP)
     object_headers *headers;
     fragS *fragP;

{
  printf ("call to md_convert_frag \n");
  abort ();
}

long
DEFUN (md_section_align, (seg, size),
       segT seg AND
       long size)
{
  return ((size + (1 << section_alignment[(int) seg]) - 1) & (-1 << section_alignment[(int) seg]));

}

void
md_apply_fix (fixP, val)
     fixS *fixP;
     long val;
{
  char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;

  switch (fixP->fx_size)
    {
    case 1:
      *buf++ = val;
      break;
    case 2:
      *buf++ = (val >> 8);
      *buf++ = val;
      break;
    case 4:
      *buf++ = (val >> 24);
      *buf++ = (val >> 16);
      *buf++ = (val >> 8);
      *buf++ = val;
      break;
    default:
      abort ();

    }
}

void
DEFUN (md_operand, (expressionP), expressionS * expressionP)
{
}

int md_long_jump_size;
int
md_estimate_size_before_relax (fragP, segment_type)
     register fragS *fragP;
     register segT segment_type;
{
  printf ("call tomd_estimate_size_before_relax \n");
  abort ();
}

/* Put number into target byte order */

void
DEFUN (md_number_to_chars, (ptr, use, nbytes),
       char *ptr AND
       long use AND
       int nbytes)
{
  switch (nbytes)
    {
    case 4:
      *ptr++ = (use >> 24) & 0xff;
    case 3:
      *ptr++ = (use >> 16) & 0xff;
    case 2:
      *ptr++ = (use >> 8) & 0xff;
    case 1:
      *ptr++ = (use >> 0) & 0xff;
      break;
    default:
      abort ();
    }
}
long
md_pcrel_from (fixP)
     fixS *fixP;
{
  abort ();
}

void
tc_coff_symbol_emit_hook ()
{
}


void
tc_reloc_mangle (fix_ptr, intr, base)
     fixS *fix_ptr;
     struct internal_reloc *intr;
     bfd_vma base;

{
  symbolS *symbol_ptr;

  symbol_ptr = fix_ptr->fx_addsy;

  /* If this relocation is attached to a symbol then it's ok
     to output it */
  if (fix_ptr->fx_r_type == RELOC_32)
    {
      /* cons likes to create reloc32's whatever the size of the reloc..
       */
      switch (fix_ptr->fx_size)
	{

	case 2:
	  intr->r_type = R_RELWORD;
	  break;
	case 1:
	  intr->r_type = R_RELBYTE;
	  break;
	default:
	  abort ();

	}

    }
  else
    {
      intr->r_type = fix_ptr->fx_r_type;
    }

  intr->r_vaddr = fix_ptr->fx_frag->fr_address + fix_ptr->fx_where + base;
  intr->r_offset = fix_ptr->fx_offset;

  if (symbol_ptr)
    intr->r_symndx = symbol_ptr->sy_number;
  else
    intr->r_symndx = -1;


}

/* end of tc-h8300.c */
