OpenCores
URL https://opencores.org/ocsvn/openrisc_2011-10-31/openrisc_2011-10-31/trunk

Subversion Repositories openrisc_2011-10-31

[/] [openrisc/] [trunk/] [gnu-src/] [binutils-2.18.50/] [gas/] [config/] [tc-z80.c] - Diff between revs 38 and 156

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 38 Rev 156
/* tc-z80.c -- Assemble code for the Zilog Z80 and ASCII R800
/* tc-z80.c -- Assemble code for the Zilog Z80 and ASCII R800
   Copyright 2005, 2006, 2007 Free Software Foundation, Inc.
   Copyright 2005, 2006, 2007 Free Software Foundation, Inc.
   Contributed by Arnold Metselaar <arnold_m@operamail.com>
   Contributed by Arnold Metselaar <arnold_m@operamail.com>
 
 
   This file is part of GAS, the GNU Assembler.
   This file is part of GAS, the GNU Assembler.
 
 
   GAS is free software; you can redistribute it and/or modify
   GAS is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   the Free Software Foundation; either version 3, or (at your option)
   any later version.
   any later version.
 
 
   GAS is distributed in the hope that it will be useful,
   GAS is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   GNU General Public License for more details.
 
 
   You should have received a copy of the GNU General Public License
   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
   along with GAS; see the file COPYING.  If not, write to the Free
   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
   02110-1301, USA.  */
   02110-1301, USA.  */
 
 
#include "as.h"
#include "as.h"
#include "safe-ctype.h"
#include "safe-ctype.h"
#include "subsegs.h"
#include "subsegs.h"
 
 
/* Exported constants.  */
/* Exported constants.  */
const char comment_chars[] = ";\0";
const char comment_chars[] = ";\0";
const char line_comment_chars[] = "#;\0";
const char line_comment_chars[] = "#;\0";
const char line_separator_chars[] = "\0";
const char line_separator_chars[] = "\0";
const char EXP_CHARS[] = "eE\0";
const char EXP_CHARS[] = "eE\0";
const char FLT_CHARS[] = "RrFf\0";
const char FLT_CHARS[] = "RrFf\0";
 
 
/* For machine specific options.  */
/* For machine specific options.  */
const char * md_shortopts = ""; /* None yet.  */
const char * md_shortopts = ""; /* None yet.  */
 
 
enum options
enum options
{
{
  OPTION_MACH_Z80 = OPTION_MD_BASE,
  OPTION_MACH_Z80 = OPTION_MD_BASE,
  OPTION_MACH_R800,
  OPTION_MACH_R800,
  OPTION_MACH_IUD,
  OPTION_MACH_IUD,
  OPTION_MACH_WUD,
  OPTION_MACH_WUD,
  OPTION_MACH_FUD,
  OPTION_MACH_FUD,
  OPTION_MACH_IUP,
  OPTION_MACH_IUP,
  OPTION_MACH_WUP,
  OPTION_MACH_WUP,
  OPTION_MACH_FUP
  OPTION_MACH_FUP
};
};
 
 
#define INS_Z80    1
#define INS_Z80    1
#define INS_UNDOC  2
#define INS_UNDOC  2
#define INS_UNPORT 4
#define INS_UNPORT 4
#define INS_R800   8
#define INS_R800   8
 
 
struct option md_longopts[] =
struct option md_longopts[] =
{
{
  { "z80",       no_argument, NULL, OPTION_MACH_Z80},
  { "z80",       no_argument, NULL, OPTION_MACH_Z80},
  { "r800",      no_argument, NULL, OPTION_MACH_R800},
  { "r800",      no_argument, NULL, OPTION_MACH_R800},
  { "ignore-undocumented-instructions", no_argument, NULL, OPTION_MACH_IUD },
  { "ignore-undocumented-instructions", no_argument, NULL, OPTION_MACH_IUD },
  { "Wnud",  no_argument, NULL, OPTION_MACH_IUD },
  { "Wnud",  no_argument, NULL, OPTION_MACH_IUD },
  { "warn-undocumented-instructions",  no_argument, NULL, OPTION_MACH_WUD },
  { "warn-undocumented-instructions",  no_argument, NULL, OPTION_MACH_WUD },
  { "Wud",  no_argument, NULL, OPTION_MACH_WUD },
  { "Wud",  no_argument, NULL, OPTION_MACH_WUD },
  { "forbid-undocumented-instructions", no_argument, NULL, OPTION_MACH_FUD },
  { "forbid-undocumented-instructions", no_argument, NULL, OPTION_MACH_FUD },
  { "Fud",  no_argument, NULL, OPTION_MACH_FUD },
  { "Fud",  no_argument, NULL, OPTION_MACH_FUD },
  { "ignore-unportable-instructions", no_argument, NULL, OPTION_MACH_IUP },
  { "ignore-unportable-instructions", no_argument, NULL, OPTION_MACH_IUP },
  { "Wnup",  no_argument, NULL, OPTION_MACH_IUP },
  { "Wnup",  no_argument, NULL, OPTION_MACH_IUP },
  { "warn-unportable-instructions",  no_argument, NULL, OPTION_MACH_WUP },
  { "warn-unportable-instructions",  no_argument, NULL, OPTION_MACH_WUP },
  { "Wup",  no_argument, NULL, OPTION_MACH_WUP },
  { "Wup",  no_argument, NULL, OPTION_MACH_WUP },
  { "forbid-unportable-instructions", no_argument, NULL, OPTION_MACH_FUP },
  { "forbid-unportable-instructions", no_argument, NULL, OPTION_MACH_FUP },
  { "Fup",  no_argument, NULL, OPTION_MACH_FUP },
  { "Fup",  no_argument, NULL, OPTION_MACH_FUP },
 
 
  { NULL, no_argument, NULL, 0 }
  { NULL, no_argument, NULL, 0 }
} ;
} ;
 
 
size_t md_longopts_size = sizeof (md_longopts);
size_t md_longopts_size = sizeof (md_longopts);
 
 
extern int coff_flags;
extern int coff_flags;
/* Instruction classes that silently assembled.  */
/* Instruction classes that silently assembled.  */
static int ins_ok = INS_Z80 | INS_UNDOC;
static int ins_ok = INS_Z80 | INS_UNDOC;
/* Instruction classes that generate errors.  */
/* Instruction classes that generate errors.  */
static int ins_err = INS_R800;
static int ins_err = INS_R800;
/* Instruction classes actually used, determines machine type.  */
/* Instruction classes actually used, determines machine type.  */
static int ins_used = INS_Z80;
static int ins_used = INS_Z80;
 
 
int
int
md_parse_option (int c, char* arg ATTRIBUTE_UNUSED)
md_parse_option (int c, char* arg ATTRIBUTE_UNUSED)
{
{
  switch (c)
  switch (c)
    {
    {
    default:
    default:
      return 0;
      return 0;
    case OPTION_MACH_Z80:
    case OPTION_MACH_Z80:
      ins_ok &= ~INS_R800;
      ins_ok &= ~INS_R800;
      ins_err |= INS_R800;
      ins_err |= INS_R800;
      break;
      break;
    case OPTION_MACH_R800:
    case OPTION_MACH_R800:
      ins_ok = INS_Z80 | INS_UNDOC | INS_R800;
      ins_ok = INS_Z80 | INS_UNDOC | INS_R800;
      ins_err = INS_UNPORT;
      ins_err = INS_UNPORT;
      break;
      break;
    case OPTION_MACH_IUD:
    case OPTION_MACH_IUD:
      ins_ok |= INS_UNDOC;
      ins_ok |= INS_UNDOC;
      ins_err &= ~INS_UNDOC;
      ins_err &= ~INS_UNDOC;
      break;
      break;
    case OPTION_MACH_IUP:
    case OPTION_MACH_IUP:
      ins_ok |= INS_UNDOC | INS_UNPORT;
      ins_ok |= INS_UNDOC | INS_UNPORT;
      ins_err &= ~(INS_UNDOC | INS_UNPORT);
      ins_err &= ~(INS_UNDOC | INS_UNPORT);
      break;
      break;
    case OPTION_MACH_WUD:
    case OPTION_MACH_WUD:
      if ((ins_ok & INS_R800) == 0)
      if ((ins_ok & INS_R800) == 0)
        {
        {
          ins_ok &= ~(INS_UNDOC|INS_UNPORT);
          ins_ok &= ~(INS_UNDOC|INS_UNPORT);
          ins_err &= ~INS_UNDOC;
          ins_err &= ~INS_UNDOC;
        }
        }
      break;
      break;
    case OPTION_MACH_WUP:
    case OPTION_MACH_WUP:
      ins_ok &= ~INS_UNPORT;
      ins_ok &= ~INS_UNPORT;
      ins_err &= ~(INS_UNDOC|INS_UNPORT);
      ins_err &= ~(INS_UNDOC|INS_UNPORT);
      break;
      break;
    case OPTION_MACH_FUD:
    case OPTION_MACH_FUD:
      if ((ins_ok & INS_R800) == 0)
      if ((ins_ok & INS_R800) == 0)
        {
        {
          ins_ok &= (INS_UNDOC | INS_UNPORT);
          ins_ok &= (INS_UNDOC | INS_UNPORT);
          ins_err |= INS_UNDOC | INS_UNPORT;
          ins_err |= INS_UNDOC | INS_UNPORT;
        }
        }
      break;
      break;
    case OPTION_MACH_FUP:
    case OPTION_MACH_FUP:
      ins_ok &= ~INS_UNPORT;
      ins_ok &= ~INS_UNPORT;
      ins_err |= INS_UNPORT;
      ins_err |= INS_UNPORT;
      break;
      break;
    }
    }
 
 
  return 1;
  return 1;
}
}
 
 
void
void
md_show_usage (FILE * f)
md_show_usage (FILE * f)
{
{
  fprintf (f, "\n\
  fprintf (f, "\n\
CPU model/instruction set options:\n\
CPU model/instruction set options:\n\
\n\
\n\
  -z80\t\t  assemble for Z80\n\
  -z80\t\t  assemble for Z80\n\
  -ignore-undocumented-instructions\n\
  -ignore-undocumented-instructions\n\
  -Wnud\n\
  -Wnud\n\
\tsilently assemble undocumented Z80-instructions that work on R800\n\
\tsilently assemble undocumented Z80-instructions that work on R800\n\
  -ignore-unportable-instructions\n\
  -ignore-unportable-instructions\n\
  -Wnup\n\
  -Wnup\n\
\tsilently assemble all undocumented Z80-instructions\n\
\tsilently assemble all undocumented Z80-instructions\n\
  -warn-undocumented-instructions\n\
  -warn-undocumented-instructions\n\
  -Wud\n\
  -Wud\n\
\tissue warnings for undocumented Z80-instructions that work on R800\n\
\tissue warnings for undocumented Z80-instructions that work on R800\n\
  -warn-unportable-instructions\n\
  -warn-unportable-instructions\n\
  -Wup\n\
  -Wup\n\
\tissue warnings for other undocumented Z80-instructions\n\
\tissue warnings for other undocumented Z80-instructions\n\
  -forbid-undocumented-instructions\n\
  -forbid-undocumented-instructions\n\
  -Fud\n\
  -Fud\n\
\ttreat all undocumented z80-instructions as errors\n\
\ttreat all undocumented z80-instructions as errors\n\
  -forbid-unportable-instructions\n\
  -forbid-unportable-instructions\n\
  -Fup\n\
  -Fup\n\
\ttreat undocumented z80-instructions that do not work on R800 as errors\n\
\ttreat undocumented z80-instructions that do not work on R800 as errors\n\
  -r800\t  assemble for R800\n\n\
  -r800\t  assemble for R800\n\n\
Default: -z80 -ignore-undocument-instructions -warn-unportable-instructions.\n");
Default: -z80 -ignore-undocument-instructions -warn-unportable-instructions.\n");
}
}
 
 
static symbolS * zero;
static symbolS * zero;
 
 
void
void
md_begin (void)
md_begin (void)
{
{
  expressionS nul;
  expressionS nul;
  char * p;
  char * p;
 
 
  p = input_line_pointer;
  p = input_line_pointer;
  input_line_pointer = "0";
  input_line_pointer = "0";
  nul.X_md=0;
  nul.X_md=0;
  expression (& nul);
  expression (& nul);
  input_line_pointer = p;
  input_line_pointer = p;
  zero = make_expr_symbol (& nul);
  zero = make_expr_symbol (& nul);
  /* We do not use relaxation (yet).  */
  /* We do not use relaxation (yet).  */
  linkrelax = 0;
  linkrelax = 0;
}
}
 
 
void
void
z80_md_end (void)
z80_md_end (void)
{
{
  int mach_type;
  int mach_type;
 
 
  if (ins_used & (INS_UNPORT | INS_R800))
  if (ins_used & (INS_UNPORT | INS_R800))
    ins_used |= INS_UNDOC;
    ins_used |= INS_UNDOC;
 
 
  switch (ins_used)
  switch (ins_used)
    {
    {
    case INS_Z80:
    case INS_Z80:
      mach_type = bfd_mach_z80strict;
      mach_type = bfd_mach_z80strict;
      break;
      break;
    case INS_Z80|INS_UNDOC:
    case INS_Z80|INS_UNDOC:
      mach_type = bfd_mach_z80;
      mach_type = bfd_mach_z80;
      break;
      break;
    case INS_Z80|INS_UNDOC|INS_UNPORT:
    case INS_Z80|INS_UNDOC|INS_UNPORT:
      mach_type = bfd_mach_z80full;
      mach_type = bfd_mach_z80full;
      break;
      break;
    case INS_Z80|INS_UNDOC|INS_R800:
    case INS_Z80|INS_UNDOC|INS_R800:
      mach_type = bfd_mach_r800;
      mach_type = bfd_mach_r800;
      break;
      break;
    default:
    default:
      mach_type = 0;
      mach_type = 0;
    }
    }
 
 
  bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach_type);
  bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach_type);
}
}
 
 
static const char *
static const char *
skip_space (const char *s)
skip_space (const char *s)
{
{
  while (*s == ' ' || *s == '\t')
  while (*s == ' ' || *s == '\t')
    ++s;
    ++s;
  return s;
  return s;
}
}
 
 
/* A non-zero return-value causes a continue in the
/* A non-zero return-value causes a continue in the
   function read_a_source_file () in ../read.c.  */
   function read_a_source_file () in ../read.c.  */
int
int
z80_start_line_hook (void)
z80_start_line_hook (void)
{
{
  char *p, quote;
  char *p, quote;
  char buf[4];
  char buf[4];
 
 
  /* Convert one character constants.  */
  /* Convert one character constants.  */
  for (p = input_line_pointer; *p && *p != '\n'; ++p)
  for (p = input_line_pointer; *p && *p != '\n'; ++p)
    {
    {
      switch (*p)
      switch (*p)
        {
        {
        case '\'':
        case '\'':
          if (p[1] != 0 && p[1] != '\'' && p[2] == '\'')
          if (p[1] != 0 && p[1] != '\'' && p[2] == '\'')
            {
            {
              snprintf (buf, 4, "%3d", (unsigned char)p[1]);
              snprintf (buf, 4, "%3d", (unsigned char)p[1]);
              *p++ = buf[0];
              *p++ = buf[0];
              *p++ = buf[1];
              *p++ = buf[1];
              *p++ = buf[2];
              *p++ = buf[2];
              break;
              break;
            }
            }
        case '"':
        case '"':
          for (quote = *p++; quote != *p && '\n' != *p; ++p)
          for (quote = *p++; quote != *p && '\n' != *p; ++p)
            /* No escapes.  */ ;
            /* No escapes.  */ ;
          if (quote != *p)
          if (quote != *p)
            {
            {
              as_bad (_("-- unterminated string"));
              as_bad (_("-- unterminated string"));
              ignore_rest_of_line ();
              ignore_rest_of_line ();
              return 1;
              return 1;
            }
            }
          break;
          break;
        }
        }
    }
    }
  /* Check for <label>[:] [.](EQU|DEFL) <value>.  */
  /* Check for <label>[:] [.](EQU|DEFL) <value>.  */
  if (is_name_beginner (*input_line_pointer))
  if (is_name_beginner (*input_line_pointer))
    {
    {
      char c, *rest, *line_start;
      char c, *rest, *line_start;
      int len;
      int len;
      symbolS * symbolP;
      symbolS * symbolP;
 
 
      line_start = input_line_pointer;
      line_start = input_line_pointer;
      LISTING_NEWLINE ();
      LISTING_NEWLINE ();
      if (ignore_input ())
      if (ignore_input ())
        return 0;
        return 0;
 
 
      c = get_symbol_end ();
      c = get_symbol_end ();
      rest = input_line_pointer + 1;
      rest = input_line_pointer + 1;
 
 
      if (*rest == ':')
      if (*rest == ':')
        ++rest;
        ++rest;
      if (*rest == ' ' || *rest == '\t')
      if (*rest == ' ' || *rest == '\t')
        ++rest;
        ++rest;
      if (*rest == '.')
      if (*rest == '.')
        ++rest;
        ++rest;
      if (strncasecmp (rest, "EQU", 3) == 0)
      if (strncasecmp (rest, "EQU", 3) == 0)
        len = 3;
        len = 3;
      else if (strncasecmp (rest, "DEFL", 4) == 0)
      else if (strncasecmp (rest, "DEFL", 4) == 0)
        len = 4;
        len = 4;
      else
      else
        len = 0;
        len = 0;
      if (len && (rest[len] == ' ' || rest[len] == '\t'))
      if (len && (rest[len] == ' ' || rest[len] == '\t'))
        {
        {
          /* Handle assignment here.  */
          /* Handle assignment here.  */
          input_line_pointer = rest + len;
          input_line_pointer = rest + len;
          if (line_start[-1] == '\n')
          if (line_start[-1] == '\n')
            bump_line_counters ();
            bump_line_counters ();
          /* Most Z80 assemblers require the first definition of a
          /* Most Z80 assemblers require the first definition of a
             label to use "EQU" and redefinitions to have "DEFL".  */
             label to use "EQU" and redefinitions to have "DEFL".  */
          if (len == 3 && (symbolP = symbol_find (line_start)) != NULL)
          if (len == 3 && (symbolP = symbol_find (line_start)) != NULL)
            {
            {
              if (S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP))
              if (S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP))
                as_bad (_("symbol `%s' is already defined"), line_start);
                as_bad (_("symbol `%s' is already defined"), line_start);
            }
            }
          equals (line_start, 1);
          equals (line_start, 1);
          return 1;
          return 1;
        }
        }
      else
      else
        {
        {
          /* Restore line and pointer.  */
          /* Restore line and pointer.  */
          *input_line_pointer = c;
          *input_line_pointer = c;
          input_line_pointer = line_start;
          input_line_pointer = line_start;
        }
        }
    }
    }
  return 0;
  return 0;
}
}
 
 
symbolS *
symbolS *
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
{
{
  return NULL;
  return NULL;
}
}
 
 
char *
char *
md_atof (int type ATTRIBUTE_UNUSED, char *litP ATTRIBUTE_UNUSED,
md_atof (int type ATTRIBUTE_UNUSED, char *litP ATTRIBUTE_UNUSED,
         int *sizeP ATTRIBUTE_UNUSED)
         int *sizeP ATTRIBUTE_UNUSED)
{
{
  return _("floating point numbers are not implemented");
  return _("floating point numbers are not implemented");
}
}
 
 
valueT
valueT
md_section_align (segT seg ATTRIBUTE_UNUSED, valueT size)
md_section_align (segT seg ATTRIBUTE_UNUSED, valueT size)
{
{
  return size;
  return size;
}
}
 
 
long
long
md_pcrel_from (fixS * fixp)
md_pcrel_from (fixS * fixp)
{
{
  return fixp->fx_where +
  return fixp->fx_where +
    fixp->fx_frag->fr_address + 1;
    fixp->fx_frag->fr_address + 1;
}
}
 
 
typedef const char * (asfunc)(char, char, const char*);
typedef const char * (asfunc)(char, char, const char*);
 
 
typedef struct _table_t
typedef struct _table_t
{
{
  char* name;
  char* name;
  char prefix;
  char prefix;
  char opcode;
  char opcode;
  asfunc * fp;
  asfunc * fp;
} table_t;
} table_t;
 
 
/* Compares the key for structs that start with a char * to the key.  */
/* Compares the key for structs that start with a char * to the key.  */
static int
static int
key_cmp (const void * a, const void * b)
key_cmp (const void * a, const void * b)
{
{
  const char *str_a, *str_b;
  const char *str_a, *str_b;
 
 
  str_a = *((const char**)a);
  str_a = *((const char**)a);
  str_b = *((const char**)b);
  str_b = *((const char**)b);
  return strcmp (str_a, str_b);
  return strcmp (str_a, str_b);
}
}
 
 
#define BUFLEN 8 /* Large enough for any keyword.  */
#define BUFLEN 8 /* Large enough for any keyword.  */
 
 
char buf[BUFLEN];
char buf[BUFLEN];
const char *key = buf;
const char *key = buf;
 
 
#define R_STACKABLE (0x80)
#define R_STACKABLE (0x80)
#define R_ARITH     (0x40)
#define R_ARITH     (0x40)
#define R_IX        (0x20)
#define R_IX        (0x20)
#define R_IY        (0x10)
#define R_IY        (0x10)
#define R_INDEX     (R_IX | R_IY)
#define R_INDEX     (R_IX | R_IY)
 
 
#define REG_A (7)
#define REG_A (7)
#define REG_B (0)
#define REG_B (0)
#define REG_C (1)
#define REG_C (1)
#define REG_D (2)
#define REG_D (2)
#define REG_E (3)
#define REG_E (3)
#define REG_H (4)
#define REG_H (4)
#define REG_L (5)
#define REG_L (5)
#define REG_F (6 | 8)
#define REG_F (6 | 8)
#define REG_I (9)
#define REG_I (9)
#define REG_R (10)
#define REG_R (10)
 
 
#define REG_AF (3 | R_STACKABLE)
#define REG_AF (3 | R_STACKABLE)
#define REG_BC (0 | R_STACKABLE | R_ARITH)
#define REG_BC (0 | R_STACKABLE | R_ARITH)
#define REG_DE (1 | R_STACKABLE | R_ARITH)
#define REG_DE (1 | R_STACKABLE | R_ARITH)
#define REG_HL (2 | R_STACKABLE | R_ARITH)
#define REG_HL (2 | R_STACKABLE | R_ARITH)
#define REG_SP (3 | R_ARITH)
#define REG_SP (3 | R_ARITH)
 
 
static const struct reg_entry
static const struct reg_entry
{
{
  char* name;
  char* name;
  int number;
  int number;
} regtable[] =
} regtable[] =
{
{
  {"a",  REG_A },
  {"a",  REG_A },
  {"af", REG_AF },
  {"af", REG_AF },
  {"b",  REG_B },
  {"b",  REG_B },
  {"bc", REG_BC },
  {"bc", REG_BC },
  {"c",  REG_C },
  {"c",  REG_C },
  {"d",  REG_D },
  {"d",  REG_D },
  {"de", REG_DE },
  {"de", REG_DE },
  {"e",  REG_E },
  {"e",  REG_E },
  {"f",  REG_F },
  {"f",  REG_F },
  {"h",  REG_H },
  {"h",  REG_H },
  {"hl", REG_HL },
  {"hl", REG_HL },
  {"i",  REG_I },
  {"i",  REG_I },
  {"ix", REG_HL | R_IX },
  {"ix", REG_HL | R_IX },
  {"ixh",REG_H | R_IX },
  {"ixh",REG_H | R_IX },
  {"ixl",REG_L | R_IX },
  {"ixl",REG_L | R_IX },
  {"iy", REG_HL | R_IY },
  {"iy", REG_HL | R_IY },
  {"iyh",REG_H | R_IY },
  {"iyh",REG_H | R_IY },
  {"iyl",REG_L | R_IY },
  {"iyl",REG_L | R_IY },
  {"l",  REG_L },
  {"l",  REG_L },
  {"r",  REG_R },
  {"r",  REG_R },
  {"sp", REG_SP },
  {"sp", REG_SP },
} ;
} ;
 
 
/* Prevent an error on a line from also generating
/* Prevent an error on a line from also generating
   a "junk at end of line" error message.  */
   a "junk at end of line" error message.  */
static char err_flag;
static char err_flag;
 
 
static void
static void
error (const char * message)
error (const char * message)
{
{
  as_bad (message);
  as_bad (message);
  err_flag = 1;
  err_flag = 1;
}
}
 
 
static void
static void
ill_op (void)
ill_op (void)
{
{
  error (_("illegal operand"));
  error (_("illegal operand"));
}
}
 
 
static void
static void
wrong_mach (int ins_type)
wrong_mach (int ins_type)
{
{
  const char *p;
  const char *p;
 
 
  switch (ins_type)
  switch (ins_type)
    {
    {
    case INS_UNDOC:
    case INS_UNDOC:
      p = "undocumented instruction";
      p = "undocumented instruction";
      break;
      break;
    case INS_UNPORT:
    case INS_UNPORT:
      p = "instruction does not work on R800";
      p = "instruction does not work on R800";
      break;
      break;
    case INS_R800:
    case INS_R800:
      p = "instruction only works R800";
      p = "instruction only works R800";
      break;
      break;
    default:
    default:
      p = 0; /* Not reachable.  */
      p = 0; /* Not reachable.  */
    }
    }
 
 
  if (ins_type & ins_err)
  if (ins_type & ins_err)
    error (_(p));
    error (_(p));
  else
  else
    as_warn (_(p));
    as_warn (_(p));
}
}
 
 
static void
static void
check_mach (int ins_type)
check_mach (int ins_type)
{
{
  if ((ins_type & ins_ok) == 0)
  if ((ins_type & ins_ok) == 0)
    wrong_mach (ins_type);
    wrong_mach (ins_type);
  ins_used |= ins_type;
  ins_used |= ins_type;
}
}
 
 
/* Check whether an expression is indirect.  */
/* Check whether an expression is indirect.  */
static int
static int
is_indir (const char *s)
is_indir (const char *s)
{
{
  char quote;
  char quote;
  const char *p;
  const char *p;
  int indir, depth;
  int indir, depth;
 
 
  /* Indirection is indicated with parentheses.  */
  /* Indirection is indicated with parentheses.  */
  indir = (*s == '(');
  indir = (*s == '(');
 
 
  for (p = s, depth = 0; *p && *p != ','; ++p)
  for (p = s, depth = 0; *p && *p != ','; ++p)
    {
    {
      switch (*p)
      switch (*p)
        {
        {
        case '"':
        case '"':
        case '\'':
        case '\'':
          for (quote = *p++; quote != *p && *p != '\n'; ++p)
          for (quote = *p++; quote != *p && *p != '\n'; ++p)
            if (*p == '\\' && p[1])
            if (*p == '\\' && p[1])
              ++p;
              ++p;
          break;
          break;
        case '(':
        case '(':
          ++ depth;
          ++ depth;
          break;
          break;
        case ')':
        case ')':
          -- depth;
          -- depth;
          if (depth == 0)
          if (depth == 0)
            {
            {
              p = skip_space (p + 1);
              p = skip_space (p + 1);
              if (*p && *p != ',')
              if (*p && *p != ',')
                indir = 0;
                indir = 0;
              --p;
              --p;
            }
            }
          if (depth < 0)
          if (depth < 0)
            error (_("mismatched parentheses"));
            error (_("mismatched parentheses"));
          break;
          break;
        }
        }
    }
    }
 
 
  if (depth != 0)
  if (depth != 0)
    error (_("mismatched parentheses"));
    error (_("mismatched parentheses"));
 
 
  return indir;
  return indir;
}
}
 
 
/* Parse general expression.  */
/* Parse general expression.  */
static const char *
static const char *
parse_exp2 (const char *s, expressionS *op, segT *pseg)
parse_exp2 (const char *s, expressionS *op, segT *pseg)
{
{
  const char *p;
  const char *p;
  int indir;
  int indir;
  int i;
  int i;
  const struct reg_entry * regp;
  const struct reg_entry * regp;
  expressionS offset;
  expressionS offset;
 
 
  p = skip_space (s);
  p = skip_space (s);
  op->X_md = indir = is_indir (p);
  op->X_md = indir = is_indir (p);
  if (indir)
  if (indir)
    p = skip_space (p + 1);
    p = skip_space (p + 1);
 
 
  for (i = 0; i < BUFLEN; ++i)
  for (i = 0; i < BUFLEN; ++i)
    {
    {
      if (!ISALPHA (p[i])) /* Register names consist of letters only.  */
      if (!ISALPHA (p[i])) /* Register names consist of letters only.  */
        break;
        break;
      buf[i] = TOLOWER (p[i]);
      buf[i] = TOLOWER (p[i]);
    }
    }
 
 
  if ((i < BUFLEN) && ((p[i] == 0) || (strchr (")+-, \t", p[i]))))
  if ((i < BUFLEN) && ((p[i] == 0) || (strchr (")+-, \t", p[i]))))
    {
    {
      buf[i] = 0;
      buf[i] = 0;
      regp = bsearch (& key, regtable, ARRAY_SIZE (regtable),
      regp = bsearch (& key, regtable, ARRAY_SIZE (regtable),
                      sizeof (regtable[0]), key_cmp);
                      sizeof (regtable[0]), key_cmp);
      if (regp)
      if (regp)
        {
        {
          *pseg = reg_section;
          *pseg = reg_section;
          op->X_add_symbol = op->X_op_symbol = 0;
          op->X_add_symbol = op->X_op_symbol = 0;
          op->X_add_number = regp->number;
          op->X_add_number = regp->number;
          op->X_op = O_register;
          op->X_op = O_register;
          p += strlen (regp->name);
          p += strlen (regp->name);
          p = skip_space (p);
          p = skip_space (p);
          if (indir)
          if (indir)
            {
            {
              if (*p == ')')
              if (*p == ')')
                ++p;
                ++p;
              if ((regp->number & R_INDEX) && (regp->number & R_ARITH))
              if ((regp->number & R_INDEX) && (regp->number & R_ARITH))
                {
                {
                  op->X_op = O_md1;
                  op->X_op = O_md1;
 
 
                  if  ((*p == '+') || (*p == '-'))
                  if  ((*p == '+') || (*p == '-'))
                    {
                    {
                      input_line_pointer = (char*) p;
                      input_line_pointer = (char*) p;
                      expression (& offset);
                      expression (& offset);
                      p = skip_space (input_line_pointer);
                      p = skip_space (input_line_pointer);
                      if (*p != ')')
                      if (*p != ')')
                        error (_("bad offset expression syntax"));
                        error (_("bad offset expression syntax"));
                      else
                      else
                        ++ p;
                        ++ p;
                      op->X_add_symbol = make_expr_symbol (& offset);
                      op->X_add_symbol = make_expr_symbol (& offset);
                      return p;
                      return p;
                    }
                    }
 
 
                  /* We treat (i[xy]) as (i[xy]+0), which is how it will
                  /* We treat (i[xy]) as (i[xy]+0), which is how it will
                     end up anyway, unless we're processing jp (i[xy]).  */
                     end up anyway, unless we're processing jp (i[xy]).  */
                  op->X_add_symbol = zero;
                  op->X_add_symbol = zero;
                }
                }
            }
            }
          p = skip_space (p);
          p = skip_space (p);
 
 
          if ((*p == 0) || (*p == ','))
          if ((*p == 0) || (*p == ','))
            return p;
            return p;
        }
        }
    }
    }
  /* Not an argument involving a register; use the generic parser.  */
  /* Not an argument involving a register; use the generic parser.  */
  input_line_pointer = (char*) s ;
  input_line_pointer = (char*) s ;
  *pseg = expression (op);
  *pseg = expression (op);
  if (op->X_op == O_absent)
  if (op->X_op == O_absent)
    error (_("missing operand"));
    error (_("missing operand"));
  if (op->X_op == O_illegal)
  if (op->X_op == O_illegal)
    error (_("bad expression syntax"));
    error (_("bad expression syntax"));
  return input_line_pointer;
  return input_line_pointer;
}
}
 
 
static const char *
static const char *
parse_exp (const char *s, expressionS *op)
parse_exp (const char *s, expressionS *op)
{
{
  segT dummy;
  segT dummy;
  return parse_exp2 (s, op, & dummy);
  return parse_exp2 (s, op, & dummy);
}
}
 
 
/* Condition codes, including some synonyms provided by HiTech zas.  */
/* Condition codes, including some synonyms provided by HiTech zas.  */
static const struct reg_entry cc_tab[] =
static const struct reg_entry cc_tab[] =
{
{
  { "age", 6 << 3 },
  { "age", 6 << 3 },
  { "alt", 7 << 3 },
  { "alt", 7 << 3 },
  { "c",   3 << 3 },
  { "c",   3 << 3 },
  { "di",  4 << 3 },
  { "di",  4 << 3 },
  { "ei",  5 << 3 },
  { "ei",  5 << 3 },
  { "lge", 2 << 3 },
  { "lge", 2 << 3 },
  { "llt", 3 << 3 },
  { "llt", 3 << 3 },
  { "m",   7 << 3 },
  { "m",   7 << 3 },
  { "nc",  2 << 3 },
  { "nc",  2 << 3 },
  { "nz",  0 << 3 },
  { "nz",  0 << 3 },
  { "p",   6 << 3 },
  { "p",   6 << 3 },
  { "pe",  5 << 3 },
  { "pe",  5 << 3 },
  { "po",  4 << 3 },
  { "po",  4 << 3 },
  { "z",   1 << 3 },
  { "z",   1 << 3 },
} ;
} ;
 
 
/* Parse condition code.  */
/* Parse condition code.  */
static const char *
static const char *
parse_cc (const char *s, char * op)
parse_cc (const char *s, char * op)
{
{
  const char *p;
  const char *p;
  int i;
  int i;
  struct reg_entry * cc_p;
  struct reg_entry * cc_p;
 
 
  for (i = 0; i < BUFLEN; ++i)
  for (i = 0; i < BUFLEN; ++i)
    {
    {
      if (!ISALPHA (s[i])) /* Condition codes consist of letters only.  */
      if (!ISALPHA (s[i])) /* Condition codes consist of letters only.  */
        break;
        break;
      buf[i] = TOLOWER (s[i]);
      buf[i] = TOLOWER (s[i]);
    }
    }
 
 
  if ((i < BUFLEN)
  if ((i < BUFLEN)
      && ((s[i] == 0) || (s[i] == ',')))
      && ((s[i] == 0) || (s[i] == ',')))
    {
    {
      buf[i] = 0;
      buf[i] = 0;
      cc_p = bsearch (&key, cc_tab, ARRAY_SIZE (cc_tab),
      cc_p = bsearch (&key, cc_tab, ARRAY_SIZE (cc_tab),
                      sizeof (cc_tab[0]), key_cmp);
                      sizeof (cc_tab[0]), key_cmp);
    }
    }
  else
  else
    cc_p = NULL;
    cc_p = NULL;
 
 
  if (cc_p)
  if (cc_p)
    {
    {
      *op = cc_p->number;
      *op = cc_p->number;
      p = s + i;
      p = s + i;
    }
    }
  else
  else
    p = NULL;
    p = NULL;
 
 
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_insn (char prefix, char opcode, const char * args)
emit_insn (char prefix, char opcode, const char * args)
{
{
  char *p;
  char *p;
 
 
  if (prefix)
  if (prefix)
    {
    {
      p = frag_more (2);
      p = frag_more (2);
      *p++ = prefix;
      *p++ = prefix;
    }
    }
  else
  else
    p = frag_more (1);
    p = frag_more (1);
  *p = opcode;
  *p = opcode;
  return args;
  return args;
}
}
 
 
void z80_cons_fix_new (fragS *frag_p, int offset, int nbytes, expressionS *exp)
void z80_cons_fix_new (fragS *frag_p, int offset, int nbytes, expressionS *exp)
{
{
  bfd_reloc_code_real_type r[4] =
  bfd_reloc_code_real_type r[4] =
    {
    {
      BFD_RELOC_8,
      BFD_RELOC_8,
      BFD_RELOC_16,
      BFD_RELOC_16,
      BFD_RELOC_24,
      BFD_RELOC_24,
      BFD_RELOC_32
      BFD_RELOC_32
    };
    };
 
 
  if (nbytes < 1 || nbytes > 4)
  if (nbytes < 1 || nbytes > 4)
    {
    {
      as_bad (_("unsupported BFD relocation size %u"), nbytes);
      as_bad (_("unsupported BFD relocation size %u"), nbytes);
    }
    }
  else
  else
    {
    {
      fix_new_exp (frag_p, offset, nbytes, exp, 0, r[nbytes-1]);
      fix_new_exp (frag_p, offset, nbytes, exp, 0, r[nbytes-1]);
    }
    }
}
}
 
 
static void
static void
emit_byte (expressionS * val, bfd_reloc_code_real_type r_type)
emit_byte (expressionS * val, bfd_reloc_code_real_type r_type)
{
{
  char *p;
  char *p;
  int lo, hi;
  int lo, hi;
  fixS * fixp;
  fixS * fixp;
 
 
  p = frag_more (1);
  p = frag_more (1);
  *p = val->X_add_number;
  *p = val->X_add_number;
  if ((r_type == BFD_RELOC_8_PCREL) && (val->X_op == O_constant))
  if ((r_type == BFD_RELOC_8_PCREL) && (val->X_op == O_constant))
    {
    {
      as_bad(_("cannot make a relative jump to an absolute location"));
      as_bad(_("cannot make a relative jump to an absolute location"));
    }
    }
  else if (val->X_op == O_constant)
  else if (val->X_op == O_constant)
    {
    {
      lo = -128;
      lo = -128;
      hi = (BFD_RELOC_8 == r_type) ? 255 : 127;
      hi = (BFD_RELOC_8 == r_type) ? 255 : 127;
 
 
      if ((val->X_add_number < lo) || (val->X_add_number > hi))
      if ((val->X_add_number < lo) || (val->X_add_number > hi))
        {
        {
          if (r_type == BFD_RELOC_Z80_DISP8)
          if (r_type == BFD_RELOC_Z80_DISP8)
            as_bad (_("offset too large"));
            as_bad (_("offset too large"));
          else
          else
            as_warn (_("overflow"));
            as_warn (_("overflow"));
        }
        }
    }
    }
  else
  else
    {
    {
      fixp = fix_new_exp (frag_now, p - frag_now->fr_literal, 1, val,
      fixp = fix_new_exp (frag_now, p - frag_now->fr_literal, 1, val,
                          (r_type == BFD_RELOC_8_PCREL) ? TRUE : FALSE, r_type);
                          (r_type == BFD_RELOC_8_PCREL) ? TRUE : FALSE, r_type);
      /* FIXME : Process constant offsets immediately.  */
      /* FIXME : Process constant offsets immediately.  */
    }
    }
}
}
 
 
static void
static void
emit_word (expressionS * val)
emit_word (expressionS * val)
{
{
  char *p;
  char *p;
 
 
  p = frag_more (2);
  p = frag_more (2);
  if (   (val->X_op == O_register)
  if (   (val->X_op == O_register)
      || (val->X_op == O_md1))
      || (val->X_op == O_md1))
    ill_op ();
    ill_op ();
  else
  else
    {
    {
      *p = val->X_add_number;
      *p = val->X_add_number;
      p[1] = (val->X_add_number>>8);
      p[1] = (val->X_add_number>>8);
      if (val->X_op != O_constant)
      if (val->X_op != O_constant)
        fix_new_exp (frag_now, p - frag_now->fr_literal, 2,
        fix_new_exp (frag_now, p - frag_now->fr_literal, 2,
                     val, FALSE, BFD_RELOC_16);
                     val, FALSE, BFD_RELOC_16);
    }
    }
}
}
 
 
static void
static void
emit_mx (char prefix, char opcode, int shift, expressionS * arg)
emit_mx (char prefix, char opcode, int shift, expressionS * arg)
     /* The operand m may be r, (hl), (ix+d), (iy+d),
     /* The operand m may be r, (hl), (ix+d), (iy+d),
        if 0 == prefix m may also be ixl, ixh, iyl, iyh.  */
        if 0 == prefix m may also be ixl, ixh, iyl, iyh.  */
{
{
  char *q;
  char *q;
  int rnum;
  int rnum;
 
 
  rnum = arg->X_add_number;
  rnum = arg->X_add_number;
  switch (arg->X_op)
  switch (arg->X_op)
    {
    {
    case O_register:
    case O_register:
      if (arg->X_md)
      if (arg->X_md)
        {
        {
          if (rnum != REG_HL)
          if (rnum != REG_HL)
            {
            {
              ill_op ();
              ill_op ();
              break;
              break;
            }
            }
          else
          else
            rnum = 6;
            rnum = 6;
        }
        }
      else
      else
        {
        {
          if ((prefix == 0) && (rnum & R_INDEX))
          if ((prefix == 0) && (rnum & R_INDEX))
            {
            {
              prefix = (rnum & R_IX) ? 0xDD : 0xFD;
              prefix = (rnum & R_IX) ? 0xDD : 0xFD;
              check_mach (INS_UNDOC);
              check_mach (INS_UNDOC);
              rnum &= ~R_INDEX;
              rnum &= ~R_INDEX;
            }
            }
          if (rnum > 7)
          if (rnum > 7)
            {
            {
              ill_op ();
              ill_op ();
              break;
              break;
            }
            }
        }
        }
      q = frag_more (prefix ? 2 : 1);
      q = frag_more (prefix ? 2 : 1);
      if (prefix)
      if (prefix)
        * q ++ = prefix;
        * q ++ = prefix;
      * q ++ = opcode + (rnum << shift);
      * q ++ = opcode + (rnum << shift);
      break;
      break;
    case O_md1:
    case O_md1:
      q = frag_more (2);
      q = frag_more (2);
      *q++ = (rnum & R_IX) ? 0xDD : 0xFD;
      *q++ = (rnum & R_IX) ? 0xDD : 0xFD;
      *q = (prefix) ? prefix : (opcode + (6 << shift));
      *q = (prefix) ? prefix : (opcode + (6 << shift));
      emit_byte (symbol_get_value_expression (arg->X_add_symbol),
      emit_byte (symbol_get_value_expression (arg->X_add_symbol),
                 BFD_RELOC_Z80_DISP8);
                 BFD_RELOC_Z80_DISP8);
      if (prefix)
      if (prefix)
        {
        {
          q = frag_more (1);
          q = frag_more (1);
          *q = opcode+(6<<shift);
          *q = opcode+(6<<shift);
        }
        }
      break;
      break;
    default:
    default:
      abort ();
      abort ();
    }
    }
}
}
 
 
/* The operand m may be r, (hl), (ix+d), (iy+d),
/* The operand m may be r, (hl), (ix+d), (iy+d),
   if 0 = prefix m may also be ixl, ixh, iyl, iyh.  */
   if 0 = prefix m may also be ixl, ixh, iyl, iyh.  */
static const char *
static const char *
emit_m (char prefix, char opcode, const char *args)
emit_m (char prefix, char opcode, const char *args)
{
{
  expressionS arg_m;
  expressionS arg_m;
  const char *p;
  const char *p;
 
 
  p = parse_exp (args, &arg_m);
  p = parse_exp (args, &arg_m);
  switch (arg_m.X_op)
  switch (arg_m.X_op)
    {
    {
    case O_md1:
    case O_md1:
    case O_register:
    case O_register:
      emit_mx (prefix, opcode, 0, &arg_m);
      emit_mx (prefix, opcode, 0, &arg_m);
      break;
      break;
    default:
    default:
      ill_op ();
      ill_op ();
    }
    }
  return p;
  return p;
}
}
 
 
/* The operand m may be as above or one of the undocumented
/* The operand m may be as above or one of the undocumented
   combinations (ix+d),r and (iy+d),r (if unportable instructions
   combinations (ix+d),r and (iy+d),r (if unportable instructions
   are allowed).  */
   are allowed).  */
static const char *
static const char *
emit_mr (char prefix, char opcode, const char *args)
emit_mr (char prefix, char opcode, const char *args)
{
{
  expressionS arg_m, arg_r;
  expressionS arg_m, arg_r;
  const char *p;
  const char *p;
 
 
  p = parse_exp (args, & arg_m);
  p = parse_exp (args, & arg_m);
 
 
  switch (arg_m.X_op)
  switch (arg_m.X_op)
    {
    {
    case O_md1:
    case O_md1:
      if (*p == ',')
      if (*p == ',')
        {
        {
          p = parse_exp (p + 1, & arg_r);
          p = parse_exp (p + 1, & arg_r);
 
 
          if ((arg_r.X_md == 0)
          if ((arg_r.X_md == 0)
              && (arg_r.X_op == O_register)
              && (arg_r.X_op == O_register)
              && (arg_r.X_add_number < 8))
              && (arg_r.X_add_number < 8))
            opcode += arg_r.X_add_number-6; /* Emit_mx () will add 6.  */
            opcode += arg_r.X_add_number-6; /* Emit_mx () will add 6.  */
          else
          else
            {
            {
              ill_op ();
              ill_op ();
              break;
              break;
            }
            }
          check_mach (INS_UNPORT);
          check_mach (INS_UNPORT);
        }
        }
    case O_register:
    case O_register:
      emit_mx (prefix, opcode, 0, & arg_m);
      emit_mx (prefix, opcode, 0, & arg_m);
      break;
      break;
    default:
    default:
      ill_op ();
      ill_op ();
    }
    }
  return p;
  return p;
}
}
 
 
static void
static void
emit_sx (char prefix, char opcode, expressionS * arg_p)
emit_sx (char prefix, char opcode, expressionS * arg_p)
{
{
  char *q;
  char *q;
 
 
  switch (arg_p->X_op)
  switch (arg_p->X_op)
    {
    {
    case O_register:
    case O_register:
    case O_md1:
    case O_md1:
      emit_mx (prefix, opcode, 0, arg_p);
      emit_mx (prefix, opcode, 0, arg_p);
      break;
      break;
    default:
    default:
      if (arg_p->X_md)
      if (arg_p->X_md)
        ill_op ();
        ill_op ();
      else
      else
        {
        {
          q = frag_more (prefix ? 2 : 1);
          q = frag_more (prefix ? 2 : 1);
          if (prefix)
          if (prefix)
            *q++ = prefix;
            *q++ = prefix;
          *q = opcode ^ 0x46;
          *q = opcode ^ 0x46;
          emit_byte (arg_p, BFD_RELOC_8);
          emit_byte (arg_p, BFD_RELOC_8);
        }
        }
    }
    }
}
}
 
 
/* The operand s may be r, (hl), (ix+d), (iy+d), n.  */
/* The operand s may be r, (hl), (ix+d), (iy+d), n.  */
static const char *
static const char *
emit_s (char prefix, char opcode, const char *args)
emit_s (char prefix, char opcode, const char *args)
{
{
  expressionS arg_s;
  expressionS arg_s;
  const char *p;
  const char *p;
 
 
  p = parse_exp (args, & arg_s);
  p = parse_exp (args, & arg_s);
  emit_sx (prefix, opcode, & arg_s);
  emit_sx (prefix, opcode, & arg_s);
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_call (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
emit_call (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
{
{
  expressionS addr;
  expressionS addr;
  const char *p;  char *q;
  const char *p;  char *q;
 
 
  p = parse_exp (args, &addr);
  p = parse_exp (args, &addr);
  if (addr.X_md)
  if (addr.X_md)
    ill_op ();
    ill_op ();
  else
  else
    {
    {
      q = frag_more (1);
      q = frag_more (1);
      *q = opcode;
      *q = opcode;
      emit_word (& addr);
      emit_word (& addr);
    }
    }
  return p;
  return p;
}
}
 
 
/* Operand may be rr, r, (hl), (ix+d), (iy+d).  */
/* Operand may be rr, r, (hl), (ix+d), (iy+d).  */
static const char *
static const char *
emit_incdec (char prefix, char opcode, const char * args)
emit_incdec (char prefix, char opcode, const char * args)
{
{
  expressionS operand;
  expressionS operand;
  int rnum;
  int rnum;
  const char *p;  char *q;
  const char *p;  char *q;
 
 
  p = parse_exp (args, &operand);
  p = parse_exp (args, &operand);
  rnum = operand.X_add_number;
  rnum = operand.X_add_number;
  if ((! operand.X_md)
  if ((! operand.X_md)
      && (operand.X_op == O_register)
      && (operand.X_op == O_register)
      && (R_ARITH&rnum))
      && (R_ARITH&rnum))
    {
    {
      q = frag_more ((rnum & R_INDEX) ? 2 : 1);
      q = frag_more ((rnum & R_INDEX) ? 2 : 1);
      if (rnum & R_INDEX)
      if (rnum & R_INDEX)
        *q++ = (rnum & R_IX) ? 0xDD : 0xFD;
        *q++ = (rnum & R_IX) ? 0xDD : 0xFD;
      *q = prefix + ((rnum & 3) << 4);
      *q = prefix + ((rnum & 3) << 4);
    }
    }
  else
  else
    {
    {
      if ((operand.X_op == O_md1) || (operand.X_op == O_register))
      if ((operand.X_op == O_md1) || (operand.X_op == O_register))
        emit_mx (0, opcode, 3, & operand);
        emit_mx (0, opcode, 3, & operand);
      else
      else
        ill_op ();
        ill_op ();
    }
    }
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_jr (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
emit_jr (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
{
{
  expressionS addr;
  expressionS addr;
  const char *p;
  const char *p;
  char *q;
  char *q;
 
 
  p = parse_exp (args, &addr);
  p = parse_exp (args, &addr);
  if (addr.X_md)
  if (addr.X_md)
    ill_op ();
    ill_op ();
  else
  else
    {
    {
      q = frag_more (1);
      q = frag_more (1);
      *q = opcode;
      *q = opcode;
      emit_byte (&addr, BFD_RELOC_8_PCREL);
      emit_byte (&addr, BFD_RELOC_8_PCREL);
    }
    }
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_jp (char prefix, char opcode, const char * args)
emit_jp (char prefix, char opcode, const char * args)
{
{
  expressionS addr;
  expressionS addr;
  const char *p;
  const char *p;
  char *q;
  char *q;
  int rnum;
  int rnum;
 
 
  p = parse_exp (args, & addr);
  p = parse_exp (args, & addr);
  if (addr.X_md)
  if (addr.X_md)
    {
    {
      rnum = addr.X_add_number;
      rnum = addr.X_add_number;
      if ((addr.X_op == O_register && (rnum & ~R_INDEX) == REG_HL)
      if ((addr.X_op == O_register && (rnum & ~R_INDEX) == REG_HL)
         /* An operand (i[xy]) would have been rewritten to (i[xy]+0)
         /* An operand (i[xy]) would have been rewritten to (i[xy]+0)
            in parse_exp ().  */
            in parse_exp ().  */
          || (addr.X_op == O_md1 && addr.X_add_symbol == zero))
          || (addr.X_op == O_md1 && addr.X_add_symbol == zero))
        {
        {
          q = frag_more ((rnum & R_INDEX) ? 2 : 1);
          q = frag_more ((rnum & R_INDEX) ? 2 : 1);
          if (rnum & R_INDEX)
          if (rnum & R_INDEX)
            *q++ = (rnum & R_IX) ? 0xDD : 0xFD;
            *q++ = (rnum & R_IX) ? 0xDD : 0xFD;
          *q = prefix;
          *q = prefix;
        }
        }
      else
      else
        ill_op ();
        ill_op ();
    }
    }
  else
  else
    {
    {
      q = frag_more (1);
      q = frag_more (1);
      *q = opcode;
      *q = opcode;
      emit_word (& addr);
      emit_word (& addr);
    }
    }
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_im (char prefix, char opcode, const char * args)
emit_im (char prefix, char opcode, const char * args)
{
{
  expressionS mode;
  expressionS mode;
  const char *p;
  const char *p;
  char *q;
  char *q;
 
 
  p = parse_exp (args, & mode);
  p = parse_exp (args, & mode);
  if (mode.X_md || (mode.X_op != O_constant))
  if (mode.X_md || (mode.X_op != O_constant))
    ill_op ();
    ill_op ();
  else
  else
    switch (mode.X_add_number)
    switch (mode.X_add_number)
      {
      {
      case 1:
      case 1:
      case 2:
      case 2:
        ++mode.X_add_number;
        ++mode.X_add_number;
        /* Fall through.  */
        /* Fall through.  */
      case 0:
      case 0:
        q = frag_more (2);
        q = frag_more (2);
        *q++ = prefix;
        *q++ = prefix;
        *q = opcode + 8*mode.X_add_number;
        *q = opcode + 8*mode.X_add_number;
        break;
        break;
      default:
      default:
        ill_op ();
        ill_op ();
      }
      }
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_pop (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
emit_pop (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
{
{
  expressionS regp;
  expressionS regp;
  const char *p;
  const char *p;
  char *q;
  char *q;
 
 
  p = parse_exp (args, & regp);
  p = parse_exp (args, & regp);
  if ((!regp.X_md)
  if ((!regp.X_md)
      && (regp.X_op == O_register)
      && (regp.X_op == O_register)
      && (regp.X_add_number & R_STACKABLE))
      && (regp.X_add_number & R_STACKABLE))
    {
    {
      int rnum;
      int rnum;
 
 
      rnum = regp.X_add_number;
      rnum = regp.X_add_number;
      if (rnum&R_INDEX)
      if (rnum&R_INDEX)
        {
        {
          q = frag_more (2);
          q = frag_more (2);
          *q++ = (rnum&R_IX)?0xDD:0xFD;
          *q++ = (rnum&R_IX)?0xDD:0xFD;
        }
        }
      else
      else
        q = frag_more (1);
        q = frag_more (1);
      *q = opcode + ((rnum & 3) << 4);
      *q = opcode + ((rnum & 3) << 4);
    }
    }
  else
  else
    ill_op ();
    ill_op ();
 
 
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_retcc (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
emit_retcc (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
{
{
  char cc, *q;
  char cc, *q;
  const char *p;
  const char *p;
 
 
  p = parse_cc (args, &cc);
  p = parse_cc (args, &cc);
  q = frag_more (1);
  q = frag_more (1);
  if (p)
  if (p)
    *q = opcode + cc;
    *q = opcode + cc;
  else
  else
    *q = prefix;
    *q = prefix;
  return p ? p : args;
  return p ? p : args;
}
}
 
 
static const char *
static const char *
emit_adc (char prefix, char opcode, const char * args)
emit_adc (char prefix, char opcode, const char * args)
{
{
  expressionS term;
  expressionS term;
  int rnum;
  int rnum;
  const char *p;
  const char *p;
  char *q;
  char *q;
 
 
  p = parse_exp (args, &term);
  p = parse_exp (args, &term);
  if (*p++ != ',')
  if (*p++ != ',')
    {
    {
      error (_("bad intruction syntax"));
      error (_("bad intruction syntax"));
      return p;
      return p;
    }
    }
 
 
  if ((term.X_md) || (term.X_op != O_register))
  if ((term.X_md) || (term.X_op != O_register))
    ill_op ();
    ill_op ();
  else
  else
    switch (term.X_add_number)
    switch (term.X_add_number)
      {
      {
      case REG_A:
      case REG_A:
        p = emit_s (0, prefix, p);
        p = emit_s (0, prefix, p);
        break;
        break;
      case REG_HL:
      case REG_HL:
        p = parse_exp (p, &term);
        p = parse_exp (p, &term);
        if ((!term.X_md) && (term.X_op == O_register))
        if ((!term.X_md) && (term.X_op == O_register))
          {
          {
            rnum = term.X_add_number;
            rnum = term.X_add_number;
            if (R_ARITH == (rnum & (R_ARITH | R_INDEX)))
            if (R_ARITH == (rnum & (R_ARITH | R_INDEX)))
              {
              {
                q = frag_more (2);
                q = frag_more (2);
                *q++ = 0xED;
                *q++ = 0xED;
                *q = opcode + ((rnum & 3) << 4);
                *q = opcode + ((rnum & 3) << 4);
                break;
                break;
              }
              }
          }
          }
        /* Fall through.  */
        /* Fall through.  */
      default:
      default:
        ill_op ();
        ill_op ();
      }
      }
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_add (char prefix, char opcode, const char * args)
emit_add (char prefix, char opcode, const char * args)
{
{
  expressionS term;
  expressionS term;
  int lhs, rhs;
  int lhs, rhs;
  const char *p;
  const char *p;
  char *q;
  char *q;
 
 
  p = parse_exp (args, &term);
  p = parse_exp (args, &term);
  if (*p++ != ',')
  if (*p++ != ',')
    {
    {
      error (_("bad intruction syntax"));
      error (_("bad intruction syntax"));
      return p;
      return p;
    }
    }
 
 
  if ((term.X_md) || (term.X_op != O_register))
  if ((term.X_md) || (term.X_op != O_register))
    ill_op ();
    ill_op ();
  else
  else
    switch (term.X_add_number & ~R_INDEX)
    switch (term.X_add_number & ~R_INDEX)
      {
      {
      case REG_A:
      case REG_A:
        p = emit_s (0, prefix, p);
        p = emit_s (0, prefix, p);
        break;
        break;
      case REG_HL:
      case REG_HL:
        lhs = term.X_add_number;
        lhs = term.X_add_number;
        p = parse_exp (p, &term);
        p = parse_exp (p, &term);
        if ((!term.X_md) && (term.X_op == O_register))
        if ((!term.X_md) && (term.X_op == O_register))
          {
          {
            rhs = term.X_add_number;
            rhs = term.X_add_number;
            if ((rhs & R_ARITH)
            if ((rhs & R_ARITH)
                && ((rhs == lhs) || ((rhs & ~R_INDEX) != REG_HL)))
                && ((rhs == lhs) || ((rhs & ~R_INDEX) != REG_HL)))
              {
              {
                q = frag_more ((lhs & R_INDEX) ? 2 : 1);
                q = frag_more ((lhs & R_INDEX) ? 2 : 1);
                if (lhs & R_INDEX)
                if (lhs & R_INDEX)
                  *q++ = (lhs & R_IX) ? 0xDD : 0xFD;
                  *q++ = (lhs & R_IX) ? 0xDD : 0xFD;
                *q = opcode + ((rhs & 3) << 4);
                *q = opcode + ((rhs & 3) << 4);
                break;
                break;
              }
              }
          }
          }
        /* Fall through.  */
        /* Fall through.  */
      default:
      default:
        ill_op ();
        ill_op ();
      }
      }
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_bit (char prefix, char opcode, const char * args)
emit_bit (char prefix, char opcode, const char * args)
{
{
  expressionS b;
  expressionS b;
  int bn;
  int bn;
  const char *p;
  const char *p;
 
 
  p = parse_exp (args, &b);
  p = parse_exp (args, &b);
  if (*p++ != ',')
  if (*p++ != ',')
    error (_("bad intruction syntax"));
    error (_("bad intruction syntax"));
 
 
  bn = b.X_add_number;
  bn = b.X_add_number;
  if ((!b.X_md)
  if ((!b.X_md)
      && (b.X_op == O_constant)
      && (b.X_op == O_constant)
      && (0 <= bn)
      && (0 <= bn)
      && (bn < 8))
      && (bn < 8))
    {
    {
      if (opcode == 0x40)
      if (opcode == 0x40)
        /* Bit : no optional third operand.  */
        /* Bit : no optional third operand.  */
        p = emit_m (prefix, opcode + (bn << 3), p);
        p = emit_m (prefix, opcode + (bn << 3), p);
      else
      else
        /* Set, res : resulting byte can be copied to register.  */
        /* Set, res : resulting byte can be copied to register.  */
        p = emit_mr (prefix, opcode + (bn << 3), p);
        p = emit_mr (prefix, opcode + (bn << 3), p);
    }
    }
  else
  else
    ill_op ();
    ill_op ();
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_jpcc (char prefix, char opcode, const char * args)
emit_jpcc (char prefix, char opcode, const char * args)
{
{
  char cc;
  char cc;
  const char *p;
  const char *p;
 
 
  p = parse_cc (args, & cc);
  p = parse_cc (args, & cc);
  if (p && *p++ == ',')
  if (p && *p++ == ',')
    p = emit_call (0, opcode + cc, p);
    p = emit_call (0, opcode + cc, p);
  else
  else
    p = (prefix == (char)0xC3)
    p = (prefix == (char)0xC3)
      ? emit_jp (0xE9, prefix, args)
      ? emit_jp (0xE9, prefix, args)
      : emit_call (0, prefix, args);
      : emit_call (0, prefix, args);
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_jrcc (char prefix, char opcode, const char * args)
emit_jrcc (char prefix, char opcode, const char * args)
{
{
  char cc;
  char cc;
  const char *p;
  const char *p;
 
 
  p = parse_cc (args, &cc);
  p = parse_cc (args, &cc);
  if (p && *p++ == ',')
  if (p && *p++ == ',')
    {
    {
      if (cc > (3 << 3))
      if (cc > (3 << 3))
        error (_("condition code invalid for jr"));
        error (_("condition code invalid for jr"));
      else
      else
        p = emit_jr (0, opcode + cc, p);
        p = emit_jr (0, opcode + cc, p);
    }
    }
  else
  else
    p = emit_jr (0, prefix, args);
    p = emit_jr (0, prefix, args);
 
 
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_ex (char prefix_in ATTRIBUTE_UNUSED,
emit_ex (char prefix_in ATTRIBUTE_UNUSED,
         char opcode_in ATTRIBUTE_UNUSED, const char * args)
         char opcode_in ATTRIBUTE_UNUSED, const char * args)
{
{
  expressionS op;
  expressionS op;
  const char * p;
  const char * p;
  char prefix, opcode;
  char prefix, opcode;
 
 
  p = parse_exp (args, &op);
  p = parse_exp (args, &op);
  p = skip_space (p);
  p = skip_space (p);
  if (*p++ != ',')
  if (*p++ != ',')
    {
    {
      error (_("bad instruction syntax"));
      error (_("bad instruction syntax"));
      return p;
      return p;
    }
    }
 
 
  prefix = opcode = 0;
  prefix = opcode = 0;
  if (op.X_op == O_register)
  if (op.X_op == O_register)
    switch (op.X_add_number | (op.X_md ? 0x8000 : 0))
    switch (op.X_add_number | (op.X_md ? 0x8000 : 0))
      {
      {
      case REG_AF:
      case REG_AF:
        if (TOLOWER (*p++) == 'a' && TOLOWER (*p++) == 'f')
        if (TOLOWER (*p++) == 'a' && TOLOWER (*p++) == 'f')
          {
          {
            /* The scrubber changes '\'' to '`' in this context.  */
            /* The scrubber changes '\'' to '`' in this context.  */
            if (*p == '`')
            if (*p == '`')
              ++p;
              ++p;
            opcode = 0x08;
            opcode = 0x08;
          }
          }
        break;
        break;
      case REG_DE:
      case REG_DE:
        if (TOLOWER (*p++) == 'h' && TOLOWER (*p++) == 'l')
        if (TOLOWER (*p++) == 'h' && TOLOWER (*p++) == 'l')
          opcode = 0xEB;
          opcode = 0xEB;
        break;
        break;
      case REG_SP|0x8000:
      case REG_SP|0x8000:
        p = parse_exp (p, & op);
        p = parse_exp (p, & op);
        if (op.X_op == O_register
        if (op.X_op == O_register
            && op.X_md == 0
            && op.X_md == 0
            && (op.X_add_number & ~R_INDEX) == REG_HL)
            && (op.X_add_number & ~R_INDEX) == REG_HL)
          {
          {
            opcode = 0xE3;
            opcode = 0xE3;
            if (R_INDEX & op.X_add_number)
            if (R_INDEX & op.X_add_number)
              prefix = (R_IX & op.X_add_number) ? 0xDD : 0xFD;
              prefix = (R_IX & op.X_add_number) ? 0xDD : 0xFD;
          }
          }
        break;
        break;
      }
      }
  if (opcode)
  if (opcode)
    emit_insn (prefix, opcode, p);
    emit_insn (prefix, opcode, p);
  else
  else
    ill_op ();
    ill_op ();
 
 
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_in (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
emit_in (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
        const char * args)
        const char * args)
{
{
  expressionS reg, port;
  expressionS reg, port;
  const char *p;
  const char *p;
  char *q;
  char *q;
 
 
  p = parse_exp (args, &reg);
  p = parse_exp (args, &reg);
  if (*p++ != ',')
  if (*p++ != ',')
    {
    {
      error (_("bad intruction syntax"));
      error (_("bad intruction syntax"));
      return p;
      return p;
    }
    }
 
 
  p = parse_exp (p, &port);
  p = parse_exp (p, &port);
  if (reg.X_md == 0
  if (reg.X_md == 0
      && reg.X_op == O_register
      && reg.X_op == O_register
      && (reg.X_add_number <= 7 || reg.X_add_number == REG_F)
      && (reg.X_add_number <= 7 || reg.X_add_number == REG_F)
      && (port.X_md))
      && (port.X_md))
    {
    {
      if (port.X_op != O_md1 && port.X_op != O_register)
      if (port.X_op != O_md1 && port.X_op != O_register)
        {
        {
          if (REG_A == reg.X_add_number)
          if (REG_A == reg.X_add_number)
            {
            {
              q = frag_more (1);
              q = frag_more (1);
              *q = 0xDB;
              *q = 0xDB;
              emit_byte (&port, BFD_RELOC_8);
              emit_byte (&port, BFD_RELOC_8);
            }
            }
          else
          else
            ill_op ();
            ill_op ();
        }
        }
      else
      else
        {
        {
          if (port.X_add_number == REG_C)
          if (port.X_add_number == REG_C)
            {
            {
              if (reg.X_add_number == REG_F)
              if (reg.X_add_number == REG_F)
                check_mach (INS_UNDOC);
                check_mach (INS_UNDOC);
              else
              else
                {
                {
                  q = frag_more (2);
                  q = frag_more (2);
                  *q++ = 0xED;
                  *q++ = 0xED;
                  *q = 0x40|((reg.X_add_number&7)<<3);
                  *q = 0x40|((reg.X_add_number&7)<<3);
                }
                }
            }
            }
          else
          else
            ill_op ();
            ill_op ();
        }
        }
    }
    }
  else
  else
    ill_op ();
    ill_op ();
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_out (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
emit_out (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
         const char * args)
         const char * args)
{
{
  expressionS reg, port;
  expressionS reg, port;
  const char *p;
  const char *p;
  char *q;
  char *q;
 
 
  p = parse_exp (args, & port);
  p = parse_exp (args, & port);
  if (*p++ != ',')
  if (*p++ != ',')
    {
    {
      error (_("bad intruction syntax"));
      error (_("bad intruction syntax"));
      return p;
      return p;
    }
    }
  p = parse_exp (p, &reg);
  p = parse_exp (p, &reg);
  if (!port.X_md)
  if (!port.X_md)
    { ill_op (); return p; }
    { ill_op (); return p; }
  /* Allow "out (c), 0" as unportable instruction.  */
  /* Allow "out (c), 0" as unportable instruction.  */
  if (reg.X_op == O_constant && reg.X_add_number == 0)
  if (reg.X_op == O_constant && reg.X_add_number == 0)
    {
    {
      check_mach (INS_UNPORT);
      check_mach (INS_UNPORT);
      reg.X_op = O_register;
      reg.X_op = O_register;
      reg.X_add_number = 6;
      reg.X_add_number = 6;
    }
    }
  if (reg.X_md
  if (reg.X_md
      || reg.X_op != O_register
      || reg.X_op != O_register
      || reg.X_add_number > 7)
      || reg.X_add_number > 7)
    ill_op ();
    ill_op ();
  else
  else
    if (port.X_op != O_register && port.X_op != O_md1)
    if (port.X_op != O_register && port.X_op != O_md1)
      {
      {
        if (REG_A == reg.X_add_number)
        if (REG_A == reg.X_add_number)
          {
          {
            q = frag_more (1);
            q = frag_more (1);
            *q = 0xD3;
            *q = 0xD3;
            emit_byte (&port, BFD_RELOC_8);
            emit_byte (&port, BFD_RELOC_8);
          }
          }
        else
        else
          ill_op ();
          ill_op ();
      }
      }
    else
    else
      {
      {
        if (REG_C == port.X_add_number)
        if (REG_C == port.X_add_number)
          {
          {
            q = frag_more (2);
            q = frag_more (2);
            *q++ = 0xED;
            *q++ = 0xED;
            *q = 0x41 | (reg.X_add_number << 3);
            *q = 0x41 | (reg.X_add_number << 3);
          }
          }
        else
        else
          ill_op ();
          ill_op ();
      }
      }
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_rst (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
emit_rst (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
{
{
  expressionS addr;
  expressionS addr;
  const char *p;
  const char *p;
  char *q;
  char *q;
 
 
  p = parse_exp (args, &addr);
  p = parse_exp (args, &addr);
  if (addr.X_op != O_constant)
  if (addr.X_op != O_constant)
    {
    {
      error ("rst needs constant address");
      error ("rst needs constant address");
      return p;
      return p;
    }
    }
 
 
  if (addr.X_add_number & ~(7 << 3))
  if (addr.X_add_number & ~(7 << 3))
    ill_op ();
    ill_op ();
  else
  else
    {
    {
      q = frag_more (1);
      q = frag_more (1);
      *q = opcode + (addr.X_add_number & (7 << 3));
      *q = opcode + (addr.X_add_number & (7 << 3));
    }
    }
  return p;
  return p;
}
}
 
 
static void
static void
emit_ldxhl (char prefix, char opcode, expressionS *src, expressionS *d)
emit_ldxhl (char prefix, char opcode, expressionS *src, expressionS *d)
{
{
  char *q;
  char *q;
 
 
  if (src->X_md)
  if (src->X_md)
    ill_op ();
    ill_op ();
  else
  else
    {
    {
      if (src->X_op == O_register)
      if (src->X_op == O_register)
        {
        {
          if (src->X_add_number>7)
          if (src->X_add_number>7)
            ill_op ();
            ill_op ();
          if (prefix)
          if (prefix)
            {
            {
              q = frag_more (2);
              q = frag_more (2);
              *q++ = prefix;
              *q++ = prefix;
            }
            }
          else
          else
        q = frag_more (1);
        q = frag_more (1);
          *q = opcode + src->X_add_number;
          *q = opcode + src->X_add_number;
          if (d)
          if (d)
            emit_byte (d, BFD_RELOC_Z80_DISP8);
            emit_byte (d, BFD_RELOC_Z80_DISP8);
        }
        }
      else
      else
        {
        {
          if (prefix)
          if (prefix)
            {
            {
              q = frag_more (2);
              q = frag_more (2);
              *q++ = prefix;
              *q++ = prefix;
            }
            }
          else
          else
            q = frag_more (1);
            q = frag_more (1);
          *q = opcode^0x46;
          *q = opcode^0x46;
          if (d)
          if (d)
            emit_byte (d, BFD_RELOC_Z80_DISP8);
            emit_byte (d, BFD_RELOC_Z80_DISP8);
          emit_byte (src, BFD_RELOC_8);
          emit_byte (src, BFD_RELOC_8);
        }
        }
    }
    }
}
}
 
 
static void
static void
emit_ldreg (int dest, expressionS * src)
emit_ldreg (int dest, expressionS * src)
{
{
  char *q;
  char *q;
  int rnum;
  int rnum;
 
 
  switch (dest)
  switch (dest)
    {
    {
      /* 8 Bit ld group:  */
      /* 8 Bit ld group:  */
    case REG_I:
    case REG_I:
    case REG_R:
    case REG_R:
      if (src->X_md == 0 && src->X_op == O_register && src->X_add_number == REG_A)
      if (src->X_md == 0 && src->X_op == O_register && src->X_add_number == REG_A)
        {
        {
          q = frag_more (2);
          q = frag_more (2);
          *q++ = 0xED;
          *q++ = 0xED;
          *q = (dest == REG_I) ? 0x47 : 0x4F;
          *q = (dest == REG_I) ? 0x47 : 0x4F;
        }
        }
      else
      else
        ill_op ();
        ill_op ();
      break;
      break;
 
 
    case REG_A:
    case REG_A:
      if ((src->X_md) && src->X_op != O_register && src->X_op != O_md1)
      if ((src->X_md) && src->X_op != O_register && src->X_op != O_md1)
        {
        {
          q = frag_more (1);
          q = frag_more (1);
          *q = 0x3A;
          *q = 0x3A;
          emit_word (src);
          emit_word (src);
          break;
          break;
        }
        }
 
 
      if ((src->X_md)
      if ((src->X_md)
          && src->X_op == O_register
          && src->X_op == O_register
          && (src->X_add_number == REG_BC || src->X_add_number == REG_DE))
          && (src->X_add_number == REG_BC || src->X_add_number == REG_DE))
        {
        {
          q = frag_more (1);
          q = frag_more (1);
          *q = 0x0A + ((dest & 1) << 4);
          *q = 0x0A + ((dest & 1) << 4);
          break;
          break;
        }
        }
 
 
      if ((!src->X_md)
      if ((!src->X_md)
          && src->X_op == O_register
          && src->X_op == O_register
          && (src->X_add_number == REG_R || src->X_add_number == REG_I))
          && (src->X_add_number == REG_R || src->X_add_number == REG_I))
        {
        {
          q = frag_more (2);
          q = frag_more (2);
          *q++ = 0xED;
          *q++ = 0xED;
          *q = (src->X_add_number == REG_I) ? 0x57 : 0x5F;
          *q = (src->X_add_number == REG_I) ? 0x57 : 0x5F;
          break;
          break;
        }
        }
      /* Fall through.  */
      /* Fall through.  */
    case REG_B:
    case REG_B:
    case REG_C:
    case REG_C:
    case REG_D:
    case REG_D:
    case REG_E:
    case REG_E:
      emit_sx (0, 0x40 + (dest << 3), src);
      emit_sx (0, 0x40 + (dest << 3), src);
      break;
      break;
 
 
    case REG_H:
    case REG_H:
    case REG_L:
    case REG_L:
      if ((src->X_md == 0)
      if ((src->X_md == 0)
          && (src->X_op == O_register)
          && (src->X_op == O_register)
          && (src->X_add_number & R_INDEX))
          && (src->X_add_number & R_INDEX))
        ill_op ();
        ill_op ();
      else
      else
        emit_sx (0, 0x40 + (dest << 3), src);
        emit_sx (0, 0x40 + (dest << 3), src);
      break;
      break;
 
 
    case R_IX | REG_H:
    case R_IX | REG_H:
    case R_IX | REG_L:
    case R_IX | REG_L:
    case R_IY | REG_H:
    case R_IY | REG_H:
    case R_IY | REG_L:
    case R_IY | REG_L:
      if (src->X_md)
      if (src->X_md)
        {
        {
          ill_op ();
          ill_op ();
          break;
          break;
        }
        }
      check_mach (INS_UNDOC);
      check_mach (INS_UNDOC);
      if (src-> X_op == O_register)
      if (src-> X_op == O_register)
        {
        {
          rnum = src->X_add_number;
          rnum = src->X_add_number;
          if ((rnum & ~R_INDEX) < 8
          if ((rnum & ~R_INDEX) < 8
              && ((rnum & R_INDEX) == (dest & R_INDEX)
              && ((rnum & R_INDEX) == (dest & R_INDEX)
                   || (   (rnum & ~R_INDEX) != REG_H
                   || (   (rnum & ~R_INDEX) != REG_H
                       && (rnum & ~R_INDEX) != REG_L)))
                       && (rnum & ~R_INDEX) != REG_L)))
            {
            {
              q = frag_more (2);
              q = frag_more (2);
              *q++ = (dest & R_IX) ? 0xDD : 0xFD;
              *q++ = (dest & R_IX) ? 0xDD : 0xFD;
              *q = 0x40 + ((dest & 0x07) << 3) + (rnum & 7);
              *q = 0x40 + ((dest & 0x07) << 3) + (rnum & 7);
            }
            }
          else
          else
            ill_op ();
            ill_op ();
        }
        }
      else
      else
        {
        {
          q = frag_more (2);
          q = frag_more (2);
          *q++ = (dest & R_IX) ? 0xDD : 0xFD;
          *q++ = (dest & R_IX) ? 0xDD : 0xFD;
          *q = 0x06 + ((dest & 0x07) << 3);
          *q = 0x06 + ((dest & 0x07) << 3);
          emit_byte (src, BFD_RELOC_8);
          emit_byte (src, BFD_RELOC_8);
        }
        }
      break;
      break;
 
 
      /* 16 Bit ld group:  */
      /* 16 Bit ld group:  */
    case REG_SP:
    case REG_SP:
      if (src->X_md == 0
      if (src->X_md == 0
          && src->X_op == O_register
          && src->X_op == O_register
          && REG_HL == (src->X_add_number &~ R_INDEX))
          && REG_HL == (src->X_add_number &~ R_INDEX))
        {
        {
          q = frag_more ((src->X_add_number & R_INDEX) ? 2 : 1);
          q = frag_more ((src->X_add_number & R_INDEX) ? 2 : 1);
          if (src->X_add_number & R_INDEX)
          if (src->X_add_number & R_INDEX)
            *q++ = (src->X_add_number & R_IX) ? 0xDD : 0xFD;
            *q++ = (src->X_add_number & R_IX) ? 0xDD : 0xFD;
          *q = 0xF9;
          *q = 0xF9;
          break;
          break;
        }
        }
      /* Fall through.  */
      /* Fall through.  */
    case REG_BC:
    case REG_BC:
    case REG_DE:
    case REG_DE:
      if (src->X_op == O_register || src->X_op == O_md1)
      if (src->X_op == O_register || src->X_op == O_md1)
        ill_op ();
        ill_op ();
      q = frag_more (src->X_md ? 2 : 1);
      q = frag_more (src->X_md ? 2 : 1);
      if (src->X_md)
      if (src->X_md)
        {
        {
          *q++ = 0xED;
          *q++ = 0xED;
          *q = 0x4B + ((dest & 3) << 4);
          *q = 0x4B + ((dest & 3) << 4);
        }
        }
      else
      else
        *q = 0x01 + ((dest & 3) << 4);
        *q = 0x01 + ((dest & 3) << 4);
      emit_word (src);
      emit_word (src);
      break;
      break;
 
 
    case REG_HL:
    case REG_HL:
    case REG_HL | R_IX:
    case REG_HL | R_IX:
    case REG_HL | R_IY:
    case REG_HL | R_IY:
      if (src->X_op == O_register || src->X_op == O_md1)
      if (src->X_op == O_register || src->X_op == O_md1)
        ill_op ();
        ill_op ();
      q = frag_more ((dest & R_INDEX) ? 2 : 1);
      q = frag_more ((dest & R_INDEX) ? 2 : 1);
      if (dest & R_INDEX)
      if (dest & R_INDEX)
        * q ++ = (dest & R_IX) ? 0xDD : 0xFD;
        * q ++ = (dest & R_IX) ? 0xDD : 0xFD;
      *q = (src->X_md) ? 0x2A : 0x21;
      *q = (src->X_md) ? 0x2A : 0x21;
      emit_word (src);
      emit_word (src);
      break;
      break;
 
 
    case REG_AF:
    case REG_AF:
    case REG_F:
    case REG_F:
      ill_op ();
      ill_op ();
      break;
      break;
 
 
    default:
    default:
      abort ();
      abort ();
    }
    }
}
}
 
 
static const char *
static const char *
emit_ld (char prefix_in ATTRIBUTE_UNUSED, char opcode_in ATTRIBUTE_UNUSED,
emit_ld (char prefix_in ATTRIBUTE_UNUSED, char opcode_in ATTRIBUTE_UNUSED,
        const char * args)
        const char * args)
{
{
  expressionS dst, src;
  expressionS dst, src;
  const char *p;
  const char *p;
  char *q;
  char *q;
  char prefix, opcode;
  char prefix, opcode;
 
 
  p = parse_exp (args, &dst);
  p = parse_exp (args, &dst);
  if (*p++ != ',')
  if (*p++ != ',')
    error (_("bad intruction syntax"));
    error (_("bad intruction syntax"));
  p = parse_exp (p, &src);
  p = parse_exp (p, &src);
 
 
  switch (dst.X_op)
  switch (dst.X_op)
    {
    {
    case O_md1:
    case O_md1:
      emit_ldxhl ((dst.X_add_number & R_IX) ? 0xDD : 0xFD, 0x70,
      emit_ldxhl ((dst.X_add_number & R_IX) ? 0xDD : 0xFD, 0x70,
                  &src, symbol_get_value_expression (dst.X_add_symbol));
                  &src, symbol_get_value_expression (dst.X_add_symbol));
      break;
      break;
 
 
    case O_register:
    case O_register:
      if (dst.X_md)
      if (dst.X_md)
        {
        {
          switch (dst.X_add_number)
          switch (dst.X_add_number)
            {
            {
            case REG_BC:
            case REG_BC:
            case REG_DE:
            case REG_DE:
              if (src.X_md == 0 && src.X_op == O_register && src.X_add_number == REG_A)
              if (src.X_md == 0 && src.X_op == O_register && src.X_add_number == REG_A)
                {
                {
                  q = frag_more (1);
                  q = frag_more (1);
                  *q = 0x02 + ( (dst.X_add_number & 1) << 4);
                  *q = 0x02 + ( (dst.X_add_number & 1) << 4);
                }
                }
              else
              else
                ill_op ();
                ill_op ();
              break;
              break;
            case REG_HL:
            case REG_HL:
              emit_ldxhl (0, 0x70, &src, NULL);
              emit_ldxhl (0, 0x70, &src, NULL);
              break;
              break;
            default:
            default:
              ill_op ();
              ill_op ();
            }
            }
        }
        }
      else
      else
        emit_ldreg (dst.X_add_number, &src);
        emit_ldreg (dst.X_add_number, &src);
      break;
      break;
 
 
    default:
    default:
      if (src.X_md != 0 || src.X_op != O_register)
      if (src.X_md != 0 || src.X_op != O_register)
        ill_op ();
        ill_op ();
      prefix = opcode = 0;
      prefix = opcode = 0;
      switch (src.X_add_number)
      switch (src.X_add_number)
        {
        {
        case REG_A:
        case REG_A:
          opcode = 0x32; break;
          opcode = 0x32; break;
        case REG_BC: case REG_DE: case REG_SP:
        case REG_BC: case REG_DE: case REG_SP:
          prefix = 0xED; opcode = 0x43 + ((src.X_add_number&3)<<4); break;
          prefix = 0xED; opcode = 0x43 + ((src.X_add_number&3)<<4); break;
        case REG_HL:
        case REG_HL:
          opcode = 0x22; break;
          opcode = 0x22; break;
        case REG_HL|R_IX:
        case REG_HL|R_IX:
          prefix = 0xDD; opcode = 0x22; break;
          prefix = 0xDD; opcode = 0x22; break;
        case REG_HL|R_IY:
        case REG_HL|R_IY:
          prefix = 0xFD; opcode = 0x22; break;
          prefix = 0xFD; opcode = 0x22; break;
        }
        }
      if (opcode)
      if (opcode)
        {
        {
          q = frag_more (prefix?2:1);
          q = frag_more (prefix?2:1);
          if (prefix)
          if (prefix)
            *q++ = prefix;
            *q++ = prefix;
          *q = opcode;
          *q = opcode;
          emit_word (&dst);
          emit_word (&dst);
        }
        }
      else
      else
        ill_op ();
        ill_op ();
    }
    }
  return p;
  return p;
}
}
 
 
static void
static void
emit_data (int size ATTRIBUTE_UNUSED)
emit_data (int size ATTRIBUTE_UNUSED)
{
{
  const char *p, *q;
  const char *p, *q;
  char *u, quote;
  char *u, quote;
  int cnt;
  int cnt;
  expressionS exp;
  expressionS exp;
 
 
  if (is_it_end_of_statement ())
  if (is_it_end_of_statement ())
    {
    {
      demand_empty_rest_of_line ();
      demand_empty_rest_of_line ();
      return;
      return;
    }
    }
  p = skip_space (input_line_pointer);
  p = skip_space (input_line_pointer);
 
 
  do
  do
    {
    {
      if (*p == '\"' || *p == '\'')
      if (*p == '\"' || *p == '\'')
        {
        {
            for (quote = *p, q = ++p, cnt = 0; *p && quote != *p; ++p, ++cnt)
            for (quote = *p, q = ++p, cnt = 0; *p && quote != *p; ++p, ++cnt)
              ;
              ;
            u = frag_more (cnt);
            u = frag_more (cnt);
            memcpy (u, q, cnt);
            memcpy (u, q, cnt);
            if (!*p)
            if (!*p)
              as_warn (_("unterminated string"));
              as_warn (_("unterminated string"));
            else
            else
              p = skip_space (p+1);
              p = skip_space (p+1);
        }
        }
      else
      else
        {
        {
          p = parse_exp (p, &exp);
          p = parse_exp (p, &exp);
          if (exp.X_op == O_md1 || exp.X_op == O_register)
          if (exp.X_op == O_md1 || exp.X_op == O_register)
            {
            {
              ill_op ();
              ill_op ();
              break;
              break;
            }
            }
          if (exp.X_md)
          if (exp.X_md)
            as_warn (_("parentheses ignored"));
            as_warn (_("parentheses ignored"));
          emit_byte (&exp, BFD_RELOC_8);
          emit_byte (&exp, BFD_RELOC_8);
          p = skip_space (p);
          p = skip_space (p);
        }
        }
    }
    }
  while (*p++ == ',') ;
  while (*p++ == ',') ;
  input_line_pointer = (char *)(p-1);
  input_line_pointer = (char *)(p-1);
}
}
 
 
static const char *
static const char *
emit_mulub (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
emit_mulub (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
{
{
  const char *p;
  const char *p;
 
 
  p = skip_space (args);
  p = skip_space (args);
  if (TOLOWER (*p++) != 'a' || *p++ != ',')
  if (TOLOWER (*p++) != 'a' || *p++ != ',')
    ill_op ();
    ill_op ();
  else
  else
    {
    {
      char *q, reg;
      char *q, reg;
 
 
      reg = TOLOWER (*p++);
      reg = TOLOWER (*p++);
      switch (reg)
      switch (reg)
        {
        {
        case 'b':
        case 'b':
        case 'c':
        case 'c':
        case 'd':
        case 'd':
        case 'e':
        case 'e':
          check_mach (INS_R800);
          check_mach (INS_R800);
          if (!*skip_space (p))
          if (!*skip_space (p))
            {
            {
              q = frag_more (2);
              q = frag_more (2);
              *q++ = prefix;
              *q++ = prefix;
              *q = opcode + ((reg - 'b') << 3);
              *q = opcode + ((reg - 'b') << 3);
              break;
              break;
            }
            }
        default:
        default:
          ill_op ();
          ill_op ();
        }
        }
    }
    }
  return p;
  return p;
}
}
 
 
static const char *
static const char *
emit_muluw (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
emit_muluw (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
{
{
  const char *p;
  const char *p;
 
 
  p = skip_space (args);
  p = skip_space (args);
  if (TOLOWER (*p++) != 'h' || TOLOWER (*p++) != 'l' || *p++ != ',')
  if (TOLOWER (*p++) != 'h' || TOLOWER (*p++) != 'l' || *p++ != ',')
    ill_op ();
    ill_op ();
  else
  else
    {
    {
      expressionS reg;
      expressionS reg;
      char *q;
      char *q;
 
 
      p = parse_exp (p, & reg);
      p = parse_exp (p, & reg);
 
 
      if ((!reg.X_md) && reg.X_op == O_register)
      if ((!reg.X_md) && reg.X_op == O_register)
        switch (reg.X_add_number)
        switch (reg.X_add_number)
          {
          {
          case REG_BC:
          case REG_BC:
          case REG_SP:
          case REG_SP:
            check_mach (INS_R800);
            check_mach (INS_R800);
            q = frag_more (2);
            q = frag_more (2);
            *q++ = prefix;
            *q++ = prefix;
            *q = opcode + ((reg.X_add_number & 3) << 4);
            *q = opcode + ((reg.X_add_number & 3) << 4);
            break;
            break;
          default:
          default:
            ill_op ();
            ill_op ();
          }
          }
    }
    }
  return p;
  return p;
}
}
 
 
/* Port specific pseudo ops.  */
/* Port specific pseudo ops.  */
const pseudo_typeS md_pseudo_table[] =
const pseudo_typeS md_pseudo_table[] =
{
{
  { "db" , emit_data, 1},
  { "db" , emit_data, 1},
  { "d24", cons, 3},
  { "d24", cons, 3},
  { "d32", cons, 4},
  { "d32", cons, 4},
  { "def24", cons, 3},
  { "def24", cons, 3},
  { "def32", cons, 4},
  { "def32", cons, 4},
  { "defb", emit_data, 1},
  { "defb", emit_data, 1},
  { "defs", s_space, 1}, /* Synonym for ds on some assemblers.  */
  { "defs", s_space, 1}, /* Synonym for ds on some assemblers.  */
  { "defw", cons, 2},
  { "defw", cons, 2},
  { "ds",   s_space, 1}, /* Fill with bytes rather than words.  */
  { "ds",   s_space, 1}, /* Fill with bytes rather than words.  */
  { "dw", cons, 2},
  { "dw", cons, 2},
  { "psect", obj_coff_section, 0}, /* TODO: Translate attributes.  */
  { "psect", obj_coff_section, 0}, /* TODO: Translate attributes.  */
  { "set", 0, 0},                 /* Real instruction on z80.  */
  { "set", 0, 0},                 /* Real instruction on z80.  */
  { NULL, 0, 0 }
  { NULL, 0, 0 }
} ;
} ;
 
 
static table_t instab[] =
static table_t instab[] =
{
{
  { "adc",  0x88, 0x4A, emit_adc },
  { "adc",  0x88, 0x4A, emit_adc },
  { "add",  0x80, 0x09, emit_add },
  { "add",  0x80, 0x09, emit_add },
  { "and",  0x00, 0xA0, emit_s },
  { "and",  0x00, 0xA0, emit_s },
  { "bit",  0xCB, 0x40, emit_bit },
  { "bit",  0xCB, 0x40, emit_bit },
  { "call", 0xCD, 0xC4, emit_jpcc },
  { "call", 0xCD, 0xC4, emit_jpcc },
  { "ccf",  0x00, 0x3F, emit_insn },
  { "ccf",  0x00, 0x3F, emit_insn },
  { "cp",   0x00, 0xB8, emit_s },
  { "cp",   0x00, 0xB8, emit_s },
  { "cpd",  0xED, 0xA9, emit_insn },
  { "cpd",  0xED, 0xA9, emit_insn },
  { "cpdr", 0xED, 0xB9, emit_insn },
  { "cpdr", 0xED, 0xB9, emit_insn },
  { "cpi",  0xED, 0xA1, emit_insn },
  { "cpi",  0xED, 0xA1, emit_insn },
  { "cpir", 0xED, 0xB1, emit_insn },
  { "cpir", 0xED, 0xB1, emit_insn },
  { "cpl",  0x00, 0x2F, emit_insn },
  { "cpl",  0x00, 0x2F, emit_insn },
  { "daa",  0x00, 0x27, emit_insn },
  { "daa",  0x00, 0x27, emit_insn },
  { "dec",  0x0B, 0x05, emit_incdec },
  { "dec",  0x0B, 0x05, emit_incdec },
  { "di",   0x00, 0xF3, emit_insn },
  { "di",   0x00, 0xF3, emit_insn },
  { "djnz", 0x00, 0x10, emit_jr },
  { "djnz", 0x00, 0x10, emit_jr },
  { "ei",   0x00, 0xFB, emit_insn },
  { "ei",   0x00, 0xFB, emit_insn },
  { "ex",   0x00, 0x00, emit_ex},
  { "ex",   0x00, 0x00, emit_ex},
  { "exx",  0x00, 0xD9, emit_insn },
  { "exx",  0x00, 0xD9, emit_insn },
  { "halt", 0x00, 0x76, emit_insn },
  { "halt", 0x00, 0x76, emit_insn },
  { "im",   0xED, 0x46, emit_im },
  { "im",   0xED, 0x46, emit_im },
  { "in",   0x00, 0x00, emit_in },
  { "in",   0x00, 0x00, emit_in },
  { "inc",  0x03, 0x04, emit_incdec },
  { "inc",  0x03, 0x04, emit_incdec },
  { "ind",  0xED, 0xAA, emit_insn },
  { "ind",  0xED, 0xAA, emit_insn },
  { "indr", 0xED, 0xBA, emit_insn },
  { "indr", 0xED, 0xBA, emit_insn },
  { "ini",  0xED, 0xA2, emit_insn },
  { "ini",  0xED, 0xA2, emit_insn },
  { "inir", 0xED, 0xB2, emit_insn },
  { "inir", 0xED, 0xB2, emit_insn },
  { "jp",   0xC3, 0xC2, emit_jpcc },
  { "jp",   0xC3, 0xC2, emit_jpcc },
  { "jr",   0x18, 0x20, emit_jrcc },
  { "jr",   0x18, 0x20, emit_jrcc },
  { "ld",   0x00, 0x00, emit_ld },
  { "ld",   0x00, 0x00, emit_ld },
  { "ldd",  0xED, 0xA8, emit_insn },
  { "ldd",  0xED, 0xA8, emit_insn },
  { "lddr", 0xED, 0xB8, emit_insn },
  { "lddr", 0xED, 0xB8, emit_insn },
  { "ldi",  0xED, 0xA0, emit_insn },
  { "ldi",  0xED, 0xA0, emit_insn },
  { "ldir", 0xED, 0xB0, emit_insn },
  { "ldir", 0xED, 0xB0, emit_insn },
  { "mulub", 0xED, 0xC5, emit_mulub }, /* R800 only.  */
  { "mulub", 0xED, 0xC5, emit_mulub }, /* R800 only.  */
  { "muluw", 0xED, 0xC3, emit_muluw }, /* R800 only.  */
  { "muluw", 0xED, 0xC3, emit_muluw }, /* R800 only.  */
  { "neg",  0xed, 0x44, emit_insn },
  { "neg",  0xed, 0x44, emit_insn },
  { "nop",  0x00, 0x00, emit_insn },
  { "nop",  0x00, 0x00, emit_insn },
  { "or",   0x00, 0xB0, emit_s },
  { "or",   0x00, 0xB0, emit_s },
  { "otdr", 0xED, 0xBB, emit_insn },
  { "otdr", 0xED, 0xBB, emit_insn },
  { "otir", 0xED, 0xB3, emit_insn },
  { "otir", 0xED, 0xB3, emit_insn },
  { "out",  0x00, 0x00, emit_out },
  { "out",  0x00, 0x00, emit_out },
  { "outd", 0xED, 0xAB, emit_insn },
  { "outd", 0xED, 0xAB, emit_insn },
  { "outi", 0xED, 0xA3, emit_insn },
  { "outi", 0xED, 0xA3, emit_insn },
  { "pop",  0x00, 0xC1, emit_pop },
  { "pop",  0x00, 0xC1, emit_pop },
  { "push", 0x00, 0xC5, emit_pop },
  { "push", 0x00, 0xC5, emit_pop },
  { "res",  0xCB, 0x80, emit_bit },
  { "res",  0xCB, 0x80, emit_bit },
  { "ret",  0xC9, 0xC0, emit_retcc },
  { "ret",  0xC9, 0xC0, emit_retcc },
  { "reti", 0xED, 0x4D, emit_insn },
  { "reti", 0xED, 0x4D, emit_insn },
  { "retn", 0xED, 0x45, emit_insn },
  { "retn", 0xED, 0x45, emit_insn },
  { "rl",   0xCB, 0x10, emit_mr },
  { "rl",   0xCB, 0x10, emit_mr },
  { "rla",  0x00, 0x17, emit_insn },
  { "rla",  0x00, 0x17, emit_insn },
  { "rlc",  0xCB, 0x00, emit_mr },
  { "rlc",  0xCB, 0x00, emit_mr },
  { "rlca", 0x00, 0x07, emit_insn },
  { "rlca", 0x00, 0x07, emit_insn },
  { "rld",  0xED, 0x6F, emit_insn },
  { "rld",  0xED, 0x6F, emit_insn },
  { "rr",   0xCB, 0x18, emit_mr },
  { "rr",   0xCB, 0x18, emit_mr },
  { "rra",  0x00, 0x1F, emit_insn },
  { "rra",  0x00, 0x1F, emit_insn },
  { "rrc",  0xCB, 0x08, emit_mr },
  { "rrc",  0xCB, 0x08, emit_mr },
  { "rrca", 0x00, 0x0F, emit_insn },
  { "rrca", 0x00, 0x0F, emit_insn },
  { "rrd",  0xED, 0x67, emit_insn },
  { "rrd",  0xED, 0x67, emit_insn },
  { "rst",  0x00, 0xC7, emit_rst},
  { "rst",  0x00, 0xC7, emit_rst},
  { "sbc",  0x98, 0x42, emit_adc },
  { "sbc",  0x98, 0x42, emit_adc },
  { "scf",  0x00, 0x37, emit_insn },
  { "scf",  0x00, 0x37, emit_insn },
  { "set",  0xCB, 0xC0, emit_bit },
  { "set",  0xCB, 0xC0, emit_bit },
  { "sla",  0xCB, 0x20, emit_mr },
  { "sla",  0xCB, 0x20, emit_mr },
  { "sli",  0xCB, 0x30, emit_mr },
  { "sli",  0xCB, 0x30, emit_mr },
  { "sll",  0xCB, 0x30, emit_mr },
  { "sll",  0xCB, 0x30, emit_mr },
  { "sra",  0xCB, 0x28, emit_mr },
  { "sra",  0xCB, 0x28, emit_mr },
  { "srl",  0xCB, 0x38, emit_mr },
  { "srl",  0xCB, 0x38, emit_mr },
  { "sub",  0x00, 0x90, emit_s },
  { "sub",  0x00, 0x90, emit_s },
  { "xor",  0x00, 0xA8, emit_s },
  { "xor",  0x00, 0xA8, emit_s },
} ;
} ;
 
 
void
void
md_assemble (char* str)
md_assemble (char* str)
{
{
  const char *p;
  const char *p;
  char * old_ptr;
  char * old_ptr;
  int i;
  int i;
  table_t *insp;
  table_t *insp;
 
 
  err_flag = 0;
  err_flag = 0;
  old_ptr = input_line_pointer;
  old_ptr = input_line_pointer;
  p = skip_space (str);
  p = skip_space (str);
  for (i = 0; (i < BUFLEN) && (ISALPHA (*p));)
  for (i = 0; (i < BUFLEN) && (ISALPHA (*p));)
    buf[i++] = TOLOWER (*p++);
    buf[i++] = TOLOWER (*p++);
 
 
  if (i == BUFLEN)
  if (i == BUFLEN)
    {
    {
      buf[BUFLEN-3] = buf[BUFLEN-2] = '.'; /* Mark opcode as abbreviated.  */
      buf[BUFLEN-3] = buf[BUFLEN-2] = '.'; /* Mark opcode as abbreviated.  */
      buf[BUFLEN-1] = 0;
      buf[BUFLEN-1] = 0;
      as_bad (_("Unknown instruction '%s'"), buf);
      as_bad (_("Unknown instruction '%s'"), buf);
    }
    }
  else if ((*p) && (!ISSPACE (*p)))
  else if ((*p) && (!ISSPACE (*p)))
    as_bad (_("syntax error"));
    as_bad (_("syntax error"));
  else
  else
    {
    {
      buf[i] = 0;
      buf[i] = 0;
      p = skip_space (p);
      p = skip_space (p);
      key = buf;
      key = buf;
 
 
      insp = bsearch (&key, instab, ARRAY_SIZE (instab),
      insp = bsearch (&key, instab, ARRAY_SIZE (instab),
                    sizeof (instab[0]), key_cmp);
                    sizeof (instab[0]), key_cmp);
      if (!insp)
      if (!insp)
        as_bad (_("Unknown instruction '%s'"), buf);
        as_bad (_("Unknown instruction '%s'"), buf);
      else
      else
        {
        {
          p = insp->fp (insp->prefix, insp->opcode, p);
          p = insp->fp (insp->prefix, insp->opcode, p);
          p = skip_space (p);
          p = skip_space (p);
        if ((!err_flag) && *p)
        if ((!err_flag) && *p)
          as_bad (_("junk at end of line, first unrecognized character is `%c'"),
          as_bad (_("junk at end of line, first unrecognized character is `%c'"),
                  *p);
                  *p);
        }
        }
    }
    }
  input_line_pointer = old_ptr;
  input_line_pointer = old_ptr;
}
}
 
 
void
void
md_apply_fix (fixS * fixP, valueT* valP, segT seg ATTRIBUTE_UNUSED)
md_apply_fix (fixS * fixP, valueT* valP, segT seg ATTRIBUTE_UNUSED)
{
{
  long val = * (long *) valP;
  long val = * (long *) valP;
  char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
  char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
 
 
  switch (fixP->fx_r_type)
  switch (fixP->fx_r_type)
    {
    {
    case BFD_RELOC_8_PCREL:
    case BFD_RELOC_8_PCREL:
      if (fixP->fx_addsy)
      if (fixP->fx_addsy)
        {
        {
          fixP->fx_no_overflow = 1;
          fixP->fx_no_overflow = 1;
          fixP->fx_done = 0;
          fixP->fx_done = 0;
        }
        }
      else
      else
        {
        {
          fixP->fx_no_overflow = (-128 <= val && val < 128);
          fixP->fx_no_overflow = (-128 <= val && val < 128);
          if (!fixP->fx_no_overflow)
          if (!fixP->fx_no_overflow)
            as_bad_where (fixP->fx_file, fixP->fx_line,
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("relative jump out of range"));
                          _("relative jump out of range"));
          *buf++ = val;
          *buf++ = val;
          fixP->fx_done = 1;
          fixP->fx_done = 1;
        }
        }
      break;
      break;
 
 
    case BFD_RELOC_Z80_DISP8:
    case BFD_RELOC_Z80_DISP8:
      if (fixP->fx_addsy)
      if (fixP->fx_addsy)
        {
        {
          fixP->fx_no_overflow = 1;
          fixP->fx_no_overflow = 1;
          fixP->fx_done = 0;
          fixP->fx_done = 0;
        }
        }
      else
      else
        {
        {
          fixP->fx_no_overflow = (-128 <= val && val < 128);
          fixP->fx_no_overflow = (-128 <= val && val < 128);
          if (!fixP->fx_no_overflow)
          if (!fixP->fx_no_overflow)
            as_bad_where (fixP->fx_file, fixP->fx_line,
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("index offset  out of range"));
                          _("index offset  out of range"));
          *buf++ = val;
          *buf++ = val;
          fixP->fx_done = 1;
          fixP->fx_done = 1;
        }
        }
      break;
      break;
 
 
    case BFD_RELOC_8:
    case BFD_RELOC_8:
      if (val > 255 || val < -128)
      if (val > 255 || val < -128)
        as_warn_where (fixP->fx_file, fixP->fx_line, _("overflow"));
        as_warn_where (fixP->fx_file, fixP->fx_line, _("overflow"));
      *buf++ = val;
      *buf++ = val;
      fixP->fx_no_overflow = 1;
      fixP->fx_no_overflow = 1;
      if (fixP->fx_addsy == NULL)
      if (fixP->fx_addsy == NULL)
        fixP->fx_done = 1;
        fixP->fx_done = 1;
      break;
      break;
 
 
    case BFD_RELOC_16:
    case BFD_RELOC_16:
      *buf++ = val;
      *buf++ = val;
      *buf++ = (val >> 8);
      *buf++ = (val >> 8);
      fixP->fx_no_overflow = 1;
      fixP->fx_no_overflow = 1;
      if (fixP->fx_addsy == NULL)
      if (fixP->fx_addsy == NULL)
        fixP->fx_done = 1;
        fixP->fx_done = 1;
      break;
      break;
 
 
    case BFD_RELOC_24: /* Def24 may produce this.  */
    case BFD_RELOC_24: /* Def24 may produce this.  */
      *buf++ = val;
      *buf++ = val;
      *buf++ = (val >> 8);
      *buf++ = (val >> 8);
      *buf++ = (val >> 16);
      *buf++ = (val >> 16);
      fixP->fx_no_overflow = 1;
      fixP->fx_no_overflow = 1;
      if (fixP->fx_addsy == NULL)
      if (fixP->fx_addsy == NULL)
        fixP->fx_done = 1;
        fixP->fx_done = 1;
      break;
      break;
 
 
    case BFD_RELOC_32: /* Def32 and .long may produce this.  */
    case BFD_RELOC_32: /* Def32 and .long may produce this.  */
      *buf++ = val;
      *buf++ = val;
      *buf++ = (val >> 8);
      *buf++ = (val >> 8);
      *buf++ = (val >> 16);
      *buf++ = (val >> 16);
      *buf++ = (val >> 24);
      *buf++ = (val >> 24);
      if (fixP->fx_addsy == NULL)
      if (fixP->fx_addsy == NULL)
        fixP->fx_done = 1;
        fixP->fx_done = 1;
      break;
      break;
 
 
    default:
    default:
      printf (_("md_apply_fix: unknown r_type 0x%x\n"), fixP->fx_r_type);
      printf (_("md_apply_fix: unknown r_type 0x%x\n"), fixP->fx_r_type);
      abort ();
      abort ();
    }
    }
}
}
 
 
/* GAS will call this to generate a reloc.  GAS will pass the
/* GAS will call this to generate a reloc.  GAS will pass the
   resulting reloc to `bfd_install_relocation'.  This currently works
   resulting reloc to `bfd_install_relocation'.  This currently works
   poorly, as `bfd_install_relocation' often does the wrong thing, and
   poorly, as `bfd_install_relocation' often does the wrong thing, and
   instances of `tc_gen_reloc' have been written to work around the
   instances of `tc_gen_reloc' have been written to work around the
   problems, which in turns makes it difficult to fix
   problems, which in turns makes it difficult to fix
   `bfd_install_relocation'.  */
   `bfd_install_relocation'.  */
 
 
/* If while processing a fixup, a reloc really
/* If while processing a fixup, a reloc really
   needs to be created then it is done here.  */
   needs to be created then it is done here.  */
 
 
arelent *
arelent *
tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED , fixS *fixp)
tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED , fixS *fixp)
{
{
  arelent *reloc;
  arelent *reloc;
 
 
  if (! bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type))
  if (! bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type))
    {
    {
      as_bad_where (fixp->fx_file, fixp->fx_line,
      as_bad_where (fixp->fx_file, fixp->fx_line,
                    _("reloc %d not supported by object file format"),
                    _("reloc %d not supported by object file format"),
                    (int) fixp->fx_r_type);
                    (int) fixp->fx_r_type);
      return NULL;
      return NULL;
    }
    }
 
 
  reloc               = xmalloc (sizeof (arelent));
  reloc               = xmalloc (sizeof (arelent));
  reloc->sym_ptr_ptr  = xmalloc (sizeof (asymbol *));
  reloc->sym_ptr_ptr  = xmalloc (sizeof (asymbol *));
  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
  reloc->address      = fixp->fx_frag->fr_address + fixp->fx_where;
  reloc->address      = fixp->fx_frag->fr_address + fixp->fx_where;
  reloc->howto        = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
  reloc->howto        = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
  reloc->addend       = fixp->fx_offset;
  reloc->addend       = fixp->fx_offset;
 
 
  return reloc;
  return reloc;
}
}
 
 
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.