/* tc-z8k.c -- Assemble code for the Zilog Z800N
   Copyright (C) 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>
#define DEFINE_TABLE
#include "../opcodes/z8k-opc.h"

#include "as.h"
#include "bfd.h"
#include <ctype.h>

const char comment_chars[] =
{'!', 0};
const char line_separator_chars[] =
{';', 0};
const char line_comment_chars[] = { '#', 0};

extern int machine;
extern int coff_flags;
int segmented_mode;
int md_reloc_size;

/* 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 ();

void
s_segm ()
{
  segmented_mode = 1;
  machine = bfd_mach_z8001;
  coff_flags = F_Z8001;
}

void
s_unseg ()
{
  segmented_mode = 0;
  machine = bfd_mach_z8002;
  coff_flags = F_Z8002;
}

static 
void even()
{
  frag_align (1, 0);
  record_alignment(now_seg,1);
}
void obj_coff_section();

int tohex(c)
int c;
{
if (isdigit(c)) return c - '0';
if (islower(c)) return c - 'a' + 10;
return c - 'A' + 10;
}
void sval()
{

  SKIP_WHITESPACE();
  if (*input_line_pointer == '\'') {
    int c;
    input_line_pointer++;
    c = *input_line_pointer++;
    while (c != '\'') {
      if (c== '%') {
	c = (tohex(input_line_pointer[0])  << 4)
	 | tohex(input_line_pointer[1]);
	input_line_pointer+=2;
      }
      FRAG_APPEND_1_CHAR(c);
      c = *input_line_pointer++;
    }
    demand_empty_rest_of_line();
  }

}
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},
 {"z8001", s_segm, 0},
 {"z8002", s_unseg, 0},


 {"segm", s_segm, 0},
 {"unsegm", s_unseg, 0},
 {"name", s_app_file, 0},
 {"global",s_globl,0},
 {"wval",cons,2},
 {"lval",cons,4},
 {"bval",cons,1},
 {"sval",sval,0},
 {"rsect",obj_coff_section,0},
 {"sect",obj_coff_section,0},
 {"block",s_space,0},
 {"even",even,0},
 {0, 0, 0}
};

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

void
md_begin ()
{
  opcode_entry_type *opcode;
  char *prev_name = "";
  int idx = 0;

  opcode_hash_control = hash_new ();

  for (opcode = z8k_table; opcode->name; opcode++)
    {
      /* Only enter unique codes into the table */
      char *src = opcode->name;

      if (strcmp (opcode->name, prev_name))
	{
	  hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
	  idx++;
	}
      opcode->idx = idx;
      prev_name = opcode->name;
    }

  /* default to z8002 */
  s_unseg ();

  /* insert the pseudo ops too */
  for (idx = 0; md_pseudo_table[idx].poc_name; idx++) 
  {
    opcode_entry_type *fake_opcode;
    fake_opcode = (opcode_entry_type*)malloc(sizeof(opcode_entry_type));
    fake_opcode->name = md_pseudo_table[idx].poc_name,
    fake_opcode->func = (void *)(md_pseudo_table+idx);
    fake_opcode->opcode = 250;

    hash_insert(opcode_hash_control,fake_opcode->name,fake_opcode);
    
  }
}

struct z8k_exp
{
  char *e_beg;
  char *e_end;
  expressionS e_exp;
};
typedef struct z8k_op
{
  char regsize;			/* 'b','w','r','q' */
  unsigned int reg;		/* 0..15 */

  int mode;

  unsigned int x_reg;		/* any other register associated with the mode */
  expressionS exp;		/* any expression */
}

op_type;

static expressionS *da_operand;
static expressionS *imm_operand;

int reg[16];
int the_cc;

char *
DEFUN (whatreg, (reg, src),
       int *reg AND
       char *src)
{
  if (isdigit (src[1]))
    {
      *reg = (src[0] - '0') * 10 + src[1] - '0';
      return src + 2;
    }
  else
    {
      *reg = (src[0] - '0');
      return src + 1;
    }
}

/*
  parse operands

  rh0-rh7, rl0-rl7
  r0-r15
  rr0-rr14
  rq0--rq12
  WREG r0,r1,r2,r3,r4,r5,r6,r7,fp,sp
  r0l,r0h,..r7l,r7h
  @WREG
  @WREG+
  @-WREG
  #const

  */

/* try and parse a reg name, returns number of chars consumed */
char *
DEFUN (parse_reg, (src, mode, reg),
       char *src AND
       int *mode AND
       unsigned int *reg)
{
  char *res = 0;

  if (src[0] == 's' && src[1]=='p') {
    if (segmented_mode) {
      *mode = CLASS_REG_LONG;
      *reg = 14;
    }
    else 
    {
      *mode = CLASS_REG_WORD;
      *reg = 15;
    }
    return src+2;
  }
  if (src[0] == 'r')
  {
    if (src[1] == 'r')
    {
      *mode = CLASS_REG_LONG;
      res = whatreg (reg, src + 2);
    }
    else if (src[1] == 'h' )
    {
      *mode = CLASS_REG_BYTE;
      res = whatreg (reg, src + 2) ;
    }
    else if (src[1] == 'l'  )
    {
      *mode = CLASS_REG_BYTE;
      res = whatreg (reg, src + 2);
      *reg += 8;
    }
    else if (src[1] == 'q')
    {
      *mode = CLASS_REG_QUAD;
      res = whatreg (reg, src + 2);
    }
    else
    {
      *mode = CLASS_REG_WORD;
      res = whatreg (reg, src + 1);
    }
  }
  return res;

}

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;
    }
}

/* The many forms of operand:

   <rb>
   <r>
   <rr>
   <rq>
   @r
   #exp
   exp
   exp(r)
   r(#exp)
   r(r)



   */

static
char *
DEFUN (checkfor, (ptr, what),
       char *ptr AND
       char what)
{
  if (*ptr == what)
    ptr++;
  else
    {
      as_bad ("expected %c", what);
    }
  return ptr;
}

/* Make sure the mode supplied is the size of a word */
static void
DEFUN (regword, (mode, string),
       int mode AND
       char *string)
{
  int ok;

  ok = CLASS_REG_WORD;
  if (ok != mode)
    {
      as_bad ("register is wrong size for a word %s", string);
    }
}

/* Make sure the mode supplied is the size of an address */
static void
DEFUN (regaddr, (mode, string),
       int mode AND
       char *string)
{
  int ok;

  ok = segmented_mode ? CLASS_REG_LONG : CLASS_REG_WORD;
  if (ok != mode)
    {
      as_bad ("register is wrong size for address %s", string);
    }
}

struct cc_names
{
  int value;
  char *name;

};

struct cc_names table[] =
{
  0x0, "f",
  0x1, "lt",
  0x2, "le",
  0x3, "ule",
  0x4, "ov",
  0x4, "pe",
  0x5, "mi",
  0x6, "eq",
  0x6, "z",
  0x7, "c",
  0x7, "ult",
  0x8, "t",
  0x9, "ge",
  0xa, "gt",
  0xb, "ugt",
  0xc, "nov",
  0xc, "po",
  0xd, "pl",
  0xe, "ne",
  0xe, "nz",
  0xf, "nc",
  0xf, "uge",
  0, 0
};

static void
DEFUN (get_cc_operand, (ptr, mode, dst),
       char **ptr AND
       struct z8k_op *mode AND
       unsigned int dst)
{
  char *src = *ptr;
  int r;
  int i;

  while (*src == ' ')
    src++;

  mode->mode = CLASS_CC;
  for (i = 0; table[i].name; i++)
    {
      int j;

      for (j = 0; table[i].name[j]; j++)
	{
	  if (table[i].name[j] != src[j])
	    goto fail;
	}
      the_cc = table[i].value;
      *ptr = src + j;
      return;
    fail:;
    }
  the_cc = 0x8;
  return;
}

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

  mode->mode = 0;

  while (*src == ' ')
    src++;
  if (*src == '#')
    {
      mode->mode = CLASS_IMM;
      imm_operand = &(mode->exp);
      src = parse_exp (src + 1, &(mode->exp));
    }
  else if (*src == '@')
    {
      int d;

      mode->mode = CLASS_IR;
      src = parse_reg (src + 1, &d, &mode->reg);
    }
  else
    {
      int regn;

      end = parse_reg (src, &mode->mode, &regn);

      if (end)
	{
	  int nw, nr;

	  src = end;
	  if (*src == '(')
	    {
	      src++;
	      end = parse_reg (src, &nw, &nr);
	      if (end)
		{
		  /* Got Ra(Rb) */
		  src = end;

		  if (*src != ')')
		    {
		      as_bad ("Missing ) in ra(rb)");
		    }
		  else
		    {
		      src++;
		    }

		  regaddr (mode->mode, "ra(rb) ra");
		  regword (mode->mode, "ra(rb) rb");
		  mode->mode = CLASS_BX;
		  mode->reg = regn;
		  mode->x_reg = nr;
		  reg[ARG_RX] = nr;
		}
	      else
		{
		  /* Got Ra(disp) */
		  if (*src == '#')
		    src++;
		  src = parse_exp (src, &(mode->exp));
		  src = checkfor (src, ')');
		  mode->mode = CLASS_BA;
		  mode->reg = regn;
		  mode->x_reg = 0;
		  imm_operand = &(mode->exp);
		}
	    }
	  else
	    {
	      mode->reg = regn;
	      mode->x_reg = 0;
	    }
	}
      else
	{
	  /* No initial reg */
	  src = parse_exp (src, &(mode->exp));
	  if (*src == '(')
	    {
	      src++;
	      end = parse_reg (src, &(mode->mode), &regn);
	      regword (mode->mode, "addr(Ra) ra");
	      mode->mode = CLASS_X;
	      mode->reg = regn;
	      mode->x_reg = 0;
	      da_operand = &(mode->exp);
	      src = checkfor (end, ')');
	    }
	  else
	    {
	      /* Just an address */
	      mode->mode = CLASS_DA;
	      mode->reg = 0;
	      mode->x_reg = 0;
	      da_operand = &(mode->exp);
	    }
	}
    }
  *ptr = src;
}

static
char *
DEFUN (get_operands, (opcode, op_end, operand),
       opcode_entry_type * opcode AND
       char *op_end AND
       op_type * operand)
{
  char *ptr = op_end;

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

    case 1:
      ptr++;
      if (opcode->arg_info[0] == CLASS_CC)
	{
	  get_cc_operand (&ptr, operand + 0, 0);
	}
      else
	{

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

    case 2:
      ptr++;
      if (opcode->arg_info[0] == CLASS_CC)
	{
	  get_cc_operand (&ptr, operand + 0, 0);
	}
      else
	{

	  get_operand (&ptr, operand + 0, 0);
	}
      if(ptr == 0) 
       return;
      if (*ptr == ',')
	ptr++;
      get_operand (&ptr, operand + 1, 1);
      break;

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

    case 4:
      ptr++;
      get_operand (&ptr, operand + 0, 0);
      if (*ptr == ',')
	ptr++;
      get_operand (&ptr, operand + 1, 1);
      if (*ptr == ',')
	ptr++;
      get_operand (&ptr, operand + 2, 2);
      if (*ptr == ',')
	ptr++;
      get_cc_operand (&ptr, operand + 3, 3);
      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
opcode_entry_type *
DEFUN (get_specific, (opcode, operands),
       opcode_entry_type * opcode AND
       op_type * operands)

{
  opcode_entry_type *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++)
	{
	  int mode = operands[i].mode;

	  if ((mode & CLASS_MASK) != (this_try->arg_info[i] & CLASS_MASK))
	    {
	      /* it could be an pc rel operand, if this is a da mode and
	   we like disps, then insert it */

	      if (mode == CLASS_DA && this_try->arg_info[i] == CLASS_DISP)
		{
		  /* This is the case */
		  operands[i].mode = CLASS_DISP;
		}
	      else if (mode == CLASS_BA && this_try->arg_info[i])
		{
		  /* Can't think of a way to turn what we've been given into
	     something that's ok */
		  goto fail;
		}
	      else
		goto fail;
	    }
	  switch (mode & CLASS_MASK)
	    {
	    default:
	      break;
	    case CLASS_X:
	    case CLASS_IR:
	    case CLASS_BA:
	    case CLASS_BX:
	    case CLASS_DISP:
	    case CLASS_REG:
	    case CLASS_REG_WORD:
	    case CLASS_REG_BYTE:
	    case CLASS_REG_QUAD:
	    case CLASS_REG_LONG:
	    case CLASS_REGN0:
	      reg[this_try->arg_info[i] & ARG_MASK] = operands[i].reg;
	      break;
	    }
	}

      found = 1;
    fail:;
    }
  if (found)
    return this_try;
  else
    return 0;
}

static void
DEFUN (check_operand, (operand, width, string),
       struct z8k_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);
	}
    }

}

static char buffer[20];

static void
DEFUN (newfix, (ptr, type, operand),
       int ptr AND
       int type AND
       expressionS * operand)
{
  if (operand->X_add_symbol
      || operand->X_subtract_symbol
      || operand->X_add_number)
    {
      fix_new (frag_now,
	       ptr,
	       1,
	       operand->X_add_symbol,
	       operand->X_subtract_symbol,
	       operand->X_add_number,
	       0,
	       type);
    }
}

static char *
DEFUN (apply_fix, (ptr, type, operand, size),
       char *ptr AND
       int type AND
       expressionS * operand AND
       int size)
{
  int n = operand->X_add_number;

  operand->X_add_number = n;
  newfix ((ptr - buffer) / 2, type, operand);
#if 1
  switch (size)
    {
    case 8:			/* 8 nibbles == 32 bits */
      *ptr++ = n >> 28;
      *ptr++ = n >> 24;
      *ptr++ = n >> 20;
      *ptr++ = n >> 16;
    case 4:			/* 4 niblles == 16 bits */
      *ptr++ = n >> 12;
      *ptr++ = n >> 8;
    case 2:
      *ptr++ = n >> 4;
    case 1:
      *ptr++ = n >> 0;
      break;
    }
#endif
  return ptr;

}

/* Now we know what sort of opcodes it is, lets build the bytes -
 */
#define INSERT(x,y) *x++ = y>>24; *x++ = y>> 16; *x++=y>>8; *x++ =y;
static void
DEFUN (build_bytes, (this_try, operand),
       opcode_entry_type * this_try AND
       struct z8k_op *operand)
{
  unsigned int i;

  int length;
  char *output;
  char *output_ptr = buffer;
  char part;
  int c;
  char high;
  int nib;
  int nibble;
  unsigned short *class_ptr;

  frag_wane (frag_now);
  frag_new (0);

  memset (buffer, 20, 0);
  class_ptr = this_try->byte_info;
top:;

  for (nibble = 0; c = *class_ptr++; nibble++)
    {

      switch (c & CLASS_MASK)
	{
	default:

	  abort ();
	case CLASS_ADDRESS:
	  /* Direct address, we don't cope with the SS mode right now */
	  if (segmented_mode)
	    {
	      da_operand->X_add_number |= 0x80000000;
	      output_ptr = apply_fix (output_ptr, R_IMM32, da_operand, 8);
	    }
	  else
	    {
	      output_ptr = apply_fix (output_ptr, R_IMM16, da_operand, 4);
	    }
	  da_operand = 0;
	  break;
	case CLASS_DISP8:
	  /* pc rel 8 bit */
	  output_ptr = apply_fix (output_ptr, R_JR, da_operand, 2);
	  da_operand = 0;

	  break;
	case CLASS_BIT_1OR2:
	  *output_ptr = c & 0xf;
	  if (imm_operand)  
	  {
	    if (imm_operand->X_add_number==2)
	    {
	      *output_ptr |= 2;
	    }
	    else if (imm_operand->X_add_number != 1)
	    {
	      as_bad("immediate must be 1 or 2");
	    }
	  }
	  else 
	  {
	    as_bad("immediate 1 or 2 expected");
	  }
	  output_ptr++;
	  break;
	case CLASS_CC:
	  *output_ptr++ = the_cc;
	  break;
	case CLASS_BIT:
	  *output_ptr++ = c & 0xf;
	  break;
	case CLASS_REGN0:
	  if (reg[c & 0xf] == 0)
	    {
	      as_bad ("can't use R0 here");
	    }
	case CLASS_REG:
	case CLASS_REG_BYTE:
	case CLASS_REG_WORD:
	case CLASS_REG_LONG:
	case CLASS_REG_QUAD:
	  /* Insert bit mattern of
	 right reg */
	  *output_ptr++ = reg[c & 0xf];
	  break;
	case CLASS_DISP:
	  output_ptr = apply_fix (output_ptr, R_IMM16, da_operand, 4);
	  da_operand = 0;
	  break;
	  
	case CLASS_IMM:
	  {
	    nib = 0;
	    switch (c & ARG_MASK)
	      {
	      case ARG_IMM4:
		output_ptr = apply_fix (output_ptr, R_IMM4L, imm_operand, 1);
		break;
	      case ARG_IMM4M1:
		imm_operand->X_add_number--;
		output_ptr = apply_fix (output_ptr, R_IMM4L, imm_operand, 1);
		break;
	      case ARG_IMMNMINUS1:
		imm_operand->X_add_number--;
		output_ptr = apply_fix (output_ptr, R_IMM4L, imm_operand, 1);
		break;
	      case ARG_NIM8:
		imm_operand->X_add_number = -imm_operand->X_add_number;
	      case ARG_IMM8:
		output_ptr = apply_fix (output_ptr, R_IMM8, imm_operand, 2);
		break;
	      case ARG_IMM16:
		output_ptr = apply_fix (output_ptr, R_IMM16, imm_operand, 4);
		break;

	      case ARG_IMM32:
		output_ptr = apply_fix (output_ptr, R_IMM32, imm_operand, 8);
		break;

	      default:
		abort ();
	      }
	  }
	}
    }

  /* Copy from the nibble buffer into the frag */

  {
    int length = (output_ptr - buffer) / 2;
    char *src = buffer;
    char *fragp = frag_more (length);

    while (src < output_ptr)
      {
	*fragp = (src[0] << 4) | src[1];
	src += 2;
	fragp++;
      }

  }

}

/* 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 z8k_op operand[3];
  opcode_entry_type *opcode;
  opcode_entry_type *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 == op_start)
  {
    as_bad ("can't find opcode ");
  }
  c = *op_end;

  *op_end = 0;

  opcode = (opcode_entry_type *) hash_find (opcode_hash_control,
					    op_start);


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

  if (opcode->opcode == 250) 
  {
    /* was really a pseudo op */

    pseudo_typeS *p ;
    char oc;

    char *old = input_line_pointer;
    *op_end = c;

     
    input_line_pointer = op_end;

    oc = *old;
    *old = '\n';
    while (*input_line_pointer == ' ')
     input_line_pointer++;
    p = (pseudo_typeS *)(opcode->func);

    (p->poc_handler)(p->poc_val);
    input_line_pointer = old;
    *old = oc;
  }
  else {
    input_line_pointer = get_operands (opcode, 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;

      as_bad ("Can't find opcode to match operands");
      return;
    }

    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;

{
  if (!strcmp(*argP,"z8001")) {
    s_segm();
  }
  else if (!strcmp(*argP,"z8002")) {
    s_unseg();
  }
  else return 0;
  **argP = 0;
  return 1;
}

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_r_type)
    {
    case R_IMM4L:
      buf[0] = (buf[0] & 0xf0) | ((buf[0] + val) & 0xf);
      break;

    case R_JR:

      *buf++ = val;
      /*    if (val != 0) abort();*/
      break;

    case R_IMM8:
      buf[0] += val;
      break;
     case R_IMM16:
      *buf++ = (val >> 8);
      *buf++ = val;
      break;
    case R_IMM32:
      *buf++ = (val >> 24);
      *buf++ = (val >> 16);
      *buf++ = (val >> 8);
      *buf++ = val;
      break;
#if 0
    case R_DA | R_SEG:
      *buf++ = (val >> 16);
      *buf++ = 0x00;
      *buf++ = (val >> 8);
      *buf++ = val;
      break;
#endif

    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 == 0)
  {
    /* cons likes to create reloc32's whatever the size of the reloc..
     */
    switch (fix_ptr->fx_size)
    {

     case 2:
      intr->r_type = R_IMM16;
      break;
     case 1:
      intr->r_type = R_IMM8;
      break;
     case 4:
      intr->r_type = R_IMM32;
      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;

}
