OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [binutils-2.18.50/] [gas/] [macro.c] - Diff between revs 156 and 816

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

Rev 156 Rev 816
/* macro.c - macro support for gas
/* macro.c - macro support for gas
   Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
   Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
   2004, 2005, 2006, 2007 Free Software Foundation, Inc.
   2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
 
   Written by Steve and Judy Chamberlain of Cygnus Support,
   Written by Steve and Judy Chamberlain of Cygnus Support,
      sac@cygnus.com
      sac@cygnus.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 "sb.h"
#include "sb.h"
#include "macro.h"
#include "macro.h"
 
 
/* The routines in this file handle macro definition and expansion.
/* The routines in this file handle macro definition and expansion.
   They are called by gas.  */
   They are called by gas.  */
 
 
/* Internal functions.  */
/* Internal functions.  */
 
 
static int get_token (int, sb *, sb *);
static int get_token (int, sb *, sb *);
static int getstring (int, sb *, sb *);
static int getstring (int, sb *, sb *);
static int get_any_string (int, sb *, sb *);
static int get_any_string (int, sb *, sb *);
static formal_entry *new_formal (void);
static formal_entry *new_formal (void);
static void del_formal (formal_entry *);
static void del_formal (formal_entry *);
static int do_formals (macro_entry *, int, sb *);
static int do_formals (macro_entry *, int, sb *);
static int get_apost_token (int, sb *, sb *, int);
static int get_apost_token (int, sb *, sb *, int);
static int sub_actual (int, sb *, sb *, struct hash_control *, int, sb *, int);
static int sub_actual (int, sb *, sb *, struct hash_control *, int, sb *, int);
static const char *macro_expand_body
static const char *macro_expand_body
  (sb *, sb *, formal_entry *, struct hash_control *, const macro_entry *);
  (sb *, sb *, formal_entry *, struct hash_control *, const macro_entry *);
static const char *macro_expand (int, sb *, macro_entry *, sb *);
static const char *macro_expand (int, sb *, macro_entry *, sb *);
static void free_macro(macro_entry *);
static void free_macro(macro_entry *);
 
 
#define ISWHITE(x) ((x) == ' ' || (x) == '\t')
#define ISWHITE(x) ((x) == ' ' || (x) == '\t')
 
 
#define ISSEP(x) \
#define ISSEP(x) \
 ((x) == ' ' || (x) == '\t' || (x) == ',' || (x) == '"' || (x) == ';' \
 ((x) == ' ' || (x) == '\t' || (x) == ',' || (x) == '"' || (x) == ';' \
  || (x) == ')' || (x) == '(' \
  || (x) == ')' || (x) == '(' \
  || ((macro_alternate || macro_mri) && ((x) == '<' || (x) == '>')))
  || ((macro_alternate || macro_mri) && ((x) == '<' || (x) == '>')))
 
 
#define ISBASE(x) \
#define ISBASE(x) \
  ((x) == 'b' || (x) == 'B' \
  ((x) == 'b' || (x) == 'B' \
   || (x) == 'q' || (x) == 'Q' \
   || (x) == 'q' || (x) == 'Q' \
   || (x) == 'h' || (x) == 'H' \
   || (x) == 'h' || (x) == 'H' \
   || (x) == 'd' || (x) == 'D')
   || (x) == 'd' || (x) == 'D')
 
 
/* The macro hash table.  */
/* The macro hash table.  */
 
 
struct hash_control *macro_hash;
struct hash_control *macro_hash;
 
 
/* Whether any macros have been defined.  */
/* Whether any macros have been defined.  */
 
 
int macro_defined;
int macro_defined;
 
 
/* Whether we are in alternate syntax mode.  */
/* Whether we are in alternate syntax mode.  */
 
 
static int macro_alternate;
static int macro_alternate;
 
 
/* Whether we are in MRI mode.  */
/* Whether we are in MRI mode.  */
 
 
static int macro_mri;
static int macro_mri;
 
 
/* Whether we should strip '@' characters.  */
/* Whether we should strip '@' characters.  */
 
 
static int macro_strip_at;
static int macro_strip_at;
 
 
/* Function to use to parse an expression.  */
/* Function to use to parse an expression.  */
 
 
static int (*macro_expr) (const char *, int, sb *, int *);
static int (*macro_expr) (const char *, int, sb *, int *);
 
 
/* Number of macro expansions that have been done.  */
/* Number of macro expansions that have been done.  */
 
 
static int macro_number;
static int macro_number;
 
 
/* Initialize macro processing.  */
/* Initialize macro processing.  */
 
 
void
void
macro_init (int alternate, int mri, int strip_at,
macro_init (int alternate, int mri, int strip_at,
            int (*expr) (const char *, int, sb *, int *))
            int (*expr) (const char *, int, sb *, int *))
{
{
  macro_hash = hash_new ();
  macro_hash = hash_new ();
  macro_defined = 0;
  macro_defined = 0;
  macro_alternate = alternate;
  macro_alternate = alternate;
  macro_mri = mri;
  macro_mri = mri;
  macro_strip_at = strip_at;
  macro_strip_at = strip_at;
  macro_expr = expr;
  macro_expr = expr;
}
}
 
 
/* Switch in and out of alternate mode on the fly.  */
/* Switch in and out of alternate mode on the fly.  */
 
 
void
void
macro_set_alternate (int alternate)
macro_set_alternate (int alternate)
{
{
  macro_alternate = alternate;
  macro_alternate = alternate;
}
}
 
 
/* Switch in and out of MRI mode on the fly.  */
/* Switch in and out of MRI mode on the fly.  */
 
 
void
void
macro_mri_mode (int mri)
macro_mri_mode (int mri)
{
{
  macro_mri = mri;
  macro_mri = mri;
}
}
 
 
/* Read input lines till we get to a TO string.
/* Read input lines till we get to a TO string.
   Increase nesting depth if we get a FROM string.
   Increase nesting depth if we get a FROM string.
   Put the results into sb at PTR.
   Put the results into sb at PTR.
   FROM may be NULL (or will be ignored) if TO is "ENDR".
   FROM may be NULL (or will be ignored) if TO is "ENDR".
   Add a new input line to an sb using GET_LINE.
   Add a new input line to an sb using GET_LINE.
   Return 1 on success, 0 on unexpected EOF.  */
   Return 1 on success, 0 on unexpected EOF.  */
 
 
int
int
buffer_and_nest (const char *from, const char *to, sb *ptr,
buffer_and_nest (const char *from, const char *to, sb *ptr,
                 int (*get_line) (sb *))
                 int (*get_line) (sb *))
{
{
  int from_len;
  int from_len;
  int to_len = strlen (to);
  int to_len = strlen (to);
  int depth = 1;
  int depth = 1;
  int line_start = ptr->len;
  int line_start = ptr->len;
 
 
  int more = get_line (ptr);
  int more = get_line (ptr);
 
 
  if (to_len == 4 && strcasecmp(to, "ENDR") == 0)
  if (to_len == 4 && strcasecmp(to, "ENDR") == 0)
    {
    {
      from = NULL;
      from = NULL;
      from_len = 0;
      from_len = 0;
    }
    }
  else
  else
    from_len = strlen (from);
    from_len = strlen (from);
 
 
  while (more)
  while (more)
    {
    {
      /* Try to find the first pseudo op on the line.  */
      /* Try to find the first pseudo op on the line.  */
      int i = line_start;
      int i = line_start;
 
 
      /* With normal syntax we can suck what we want till we get
      /* With normal syntax we can suck what we want till we get
         to the dot.  With the alternate, labels have to start in
         to the dot.  With the alternate, labels have to start in
         the first column, since we can't tell what's a label and
         the first column, since we can't tell what's a label and
         what's a pseudoop.  */
         what's a pseudoop.  */
 
 
      if (! LABELS_WITHOUT_COLONS)
      if (! LABELS_WITHOUT_COLONS)
        {
        {
          /* Skip leading whitespace.  */
          /* Skip leading whitespace.  */
          while (i < ptr->len && ISWHITE (ptr->ptr[i]))
          while (i < ptr->len && ISWHITE (ptr->ptr[i]))
            i++;
            i++;
        }
        }
 
 
      for (;;)
      for (;;)
        {
        {
          /* Skip over a label, if any.  */
          /* Skip over a label, if any.  */
          if (i >= ptr->len || ! is_name_beginner (ptr->ptr[i]))
          if (i >= ptr->len || ! is_name_beginner (ptr->ptr[i]))
            break;
            break;
          i++;
          i++;
          while (i < ptr->len && is_part_of_name (ptr->ptr[i]))
          while (i < ptr->len && is_part_of_name (ptr->ptr[i]))
            i++;
            i++;
          if (i < ptr->len && is_name_ender (ptr->ptr[i]))
          if (i < ptr->len && is_name_ender (ptr->ptr[i]))
            i++;
            i++;
          if (LABELS_WITHOUT_COLONS)
          if (LABELS_WITHOUT_COLONS)
            break;
            break;
          /* Skip whitespace.  */
          /* Skip whitespace.  */
          while (i < ptr->len && ISWHITE (ptr->ptr[i]))
          while (i < ptr->len && ISWHITE (ptr->ptr[i]))
            i++;
            i++;
          /* Check for the colon.  */
          /* Check for the colon.  */
          if (i >= ptr->len || ptr->ptr[i] != ':')
          if (i >= ptr->len || ptr->ptr[i] != ':')
            {
            {
              i = line_start;
              i = line_start;
              break;
              break;
            }
            }
          i++;
          i++;
          line_start = i;
          line_start = i;
        }
        }
 
 
      /* Skip trailing whitespace.  */
      /* Skip trailing whitespace.  */
      while (i < ptr->len && ISWHITE (ptr->ptr[i]))
      while (i < ptr->len && ISWHITE (ptr->ptr[i]))
        i++;
        i++;
 
 
      if (i < ptr->len && (ptr->ptr[i] == '.'
      if (i < ptr->len && (ptr->ptr[i] == '.'
                           || NO_PSEUDO_DOT
                           || NO_PSEUDO_DOT
                           || macro_mri))
                           || macro_mri))
        {
        {
          if (! flag_m68k_mri && ptr->ptr[i] == '.')
          if (! flag_m68k_mri && ptr->ptr[i] == '.')
            i++;
            i++;
          if (from == NULL
          if (from == NULL
             && strncasecmp (ptr->ptr + i, "IRPC", from_len = 4) != 0
             && strncasecmp (ptr->ptr + i, "IRPC", from_len = 4) != 0
             && strncasecmp (ptr->ptr + i, "IRP", from_len = 3) != 0
             && strncasecmp (ptr->ptr + i, "IRP", from_len = 3) != 0
             && strncasecmp (ptr->ptr + i, "IREPC", from_len = 5) != 0
             && strncasecmp (ptr->ptr + i, "IREPC", from_len = 5) != 0
             && strncasecmp (ptr->ptr + i, "IREP", from_len = 4) != 0
             && strncasecmp (ptr->ptr + i, "IREP", from_len = 4) != 0
             && strncasecmp (ptr->ptr + i, "REPT", from_len = 4) != 0
             && strncasecmp (ptr->ptr + i, "REPT", from_len = 4) != 0
             && strncasecmp (ptr->ptr + i, "REP", from_len = 3) != 0)
             && strncasecmp (ptr->ptr + i, "REP", from_len = 3) != 0)
            from_len = 0;
            from_len = 0;
          if ((from != NULL
          if ((from != NULL
               ? strncasecmp (ptr->ptr + i, from, from_len) == 0
               ? strncasecmp (ptr->ptr + i, from, from_len) == 0
               : from_len > 0)
               : from_len > 0)
              && (ptr->len == (i + from_len)
              && (ptr->len == (i + from_len)
                  || ! (is_part_of_name (ptr->ptr[i + from_len])
                  || ! (is_part_of_name (ptr->ptr[i + from_len])
                        || is_name_ender (ptr->ptr[i + from_len]))))
                        || is_name_ender (ptr->ptr[i + from_len]))))
            depth++;
            depth++;
          if (strncasecmp (ptr->ptr + i, to, to_len) == 0
          if (strncasecmp (ptr->ptr + i, to, to_len) == 0
              && (ptr->len == (i + to_len)
              && (ptr->len == (i + to_len)
                  || ! (is_part_of_name (ptr->ptr[i + to_len])
                  || ! (is_part_of_name (ptr->ptr[i + to_len])
                        || is_name_ender (ptr->ptr[i + to_len]))))
                        || is_name_ender (ptr->ptr[i + to_len]))))
            {
            {
              depth--;
              depth--;
              if (depth == 0)
              if (depth == 0)
                {
                {
                  /* Reset the string to not include the ending rune.  */
                  /* Reset the string to not include the ending rune.  */
                  ptr->len = line_start;
                  ptr->len = line_start;
                  break;
                  break;
                }
                }
            }
            }
        }
        }
 
 
      /* Add the original end-of-line char to the end and keep running.  */
      /* Add the original end-of-line char to the end and keep running.  */
      sb_add_char (ptr, more);
      sb_add_char (ptr, more);
      line_start = ptr->len;
      line_start = ptr->len;
      more = get_line (ptr);
      more = get_line (ptr);
    }
    }
 
 
  /* Return 1 on success, 0 on unexpected EOF.  */
  /* Return 1 on success, 0 on unexpected EOF.  */
  return depth == 0;
  return depth == 0;
}
}
 
 
/* Pick up a token.  */
/* Pick up a token.  */
 
 
static int
static int
get_token (int idx, sb *in, sb *name)
get_token (int idx, sb *in, sb *name)
{
{
  if (idx < in->len
  if (idx < in->len
      && is_name_beginner (in->ptr[idx]))
      && is_name_beginner (in->ptr[idx]))
    {
    {
      sb_add_char (name, in->ptr[idx++]);
      sb_add_char (name, in->ptr[idx++]);
      while (idx < in->len
      while (idx < in->len
             && is_part_of_name (in->ptr[idx]))
             && is_part_of_name (in->ptr[idx]))
        {
        {
          sb_add_char (name, in->ptr[idx++]);
          sb_add_char (name, in->ptr[idx++]);
        }
        }
      if (idx < in->len
      if (idx < in->len
             && is_name_ender (in->ptr[idx]))
             && is_name_ender (in->ptr[idx]))
        {
        {
          sb_add_char (name, in->ptr[idx++]);
          sb_add_char (name, in->ptr[idx++]);
        }
        }
    }
    }
  /* Ignore trailing &.  */
  /* Ignore trailing &.  */
  if (macro_alternate && idx < in->len && in->ptr[idx] == '&')
  if (macro_alternate && idx < in->len && in->ptr[idx] == '&')
    idx++;
    idx++;
  return idx;
  return idx;
}
}
 
 
/* Pick up a string.  */
/* Pick up a string.  */
 
 
static int
static int
getstring (int idx, sb *in, sb *acc)
getstring (int idx, sb *in, sb *acc)
{
{
  while (idx < in->len
  while (idx < in->len
         && (in->ptr[idx] == '"'
         && (in->ptr[idx] == '"'
             || (in->ptr[idx] == '<' && (macro_alternate || macro_mri))
             || (in->ptr[idx] == '<' && (macro_alternate || macro_mri))
             || (in->ptr[idx] == '\'' && macro_alternate)))
             || (in->ptr[idx] == '\'' && macro_alternate)))
    {
    {
      if (in->ptr[idx] == '<')
      if (in->ptr[idx] == '<')
        {
        {
          int nest = 0;
          int nest = 0;
          idx++;
          idx++;
          while ((in->ptr[idx] != '>' || nest)
          while ((in->ptr[idx] != '>' || nest)
                 && idx < in->len)
                 && idx < in->len)
            {
            {
              if (in->ptr[idx] == '!')
              if (in->ptr[idx] == '!')
                {
                {
                  idx++;
                  idx++;
                  sb_add_char (acc, in->ptr[idx++]);
                  sb_add_char (acc, in->ptr[idx++]);
                }
                }
              else
              else
                {
                {
                  if (in->ptr[idx] == '>')
                  if (in->ptr[idx] == '>')
                    nest--;
                    nest--;
                  if (in->ptr[idx] == '<')
                  if (in->ptr[idx] == '<')
                    nest++;
                    nest++;
                  sb_add_char (acc, in->ptr[idx++]);
                  sb_add_char (acc, in->ptr[idx++]);
                }
                }
            }
            }
          idx++;
          idx++;
        }
        }
      else if (in->ptr[idx] == '"' || in->ptr[idx] == '\'')
      else if (in->ptr[idx] == '"' || in->ptr[idx] == '\'')
        {
        {
          char tchar = in->ptr[idx];
          char tchar = in->ptr[idx];
          int escaped = 0;
          int escaped = 0;
 
 
          idx++;
          idx++;
 
 
          while (idx < in->len)
          while (idx < in->len)
            {
            {
              if (in->ptr[idx - 1] == '\\')
              if (in->ptr[idx - 1] == '\\')
                escaped ^= 1;
                escaped ^= 1;
              else
              else
                escaped = 0;
                escaped = 0;
 
 
              if (macro_alternate && in->ptr[idx] == '!')
              if (macro_alternate && in->ptr[idx] == '!')
                {
                {
                  idx ++;
                  idx ++;
 
 
                  sb_add_char (acc, in->ptr[idx]);
                  sb_add_char (acc, in->ptr[idx]);
 
 
                  idx ++;
                  idx ++;
                }
                }
              else if (escaped && in->ptr[idx] == tchar)
              else if (escaped && in->ptr[idx] == tchar)
                {
                {
                  sb_add_char (acc, tchar);
                  sb_add_char (acc, tchar);
                  idx ++;
                  idx ++;
                }
                }
              else
              else
                {
                {
                  if (in->ptr[idx] == tchar)
                  if (in->ptr[idx] == tchar)
                    {
                    {
                      idx ++;
                      idx ++;
 
 
                      if (idx >= in->len || in->ptr[idx] != tchar)
                      if (idx >= in->len || in->ptr[idx] != tchar)
                        break;
                        break;
                    }
                    }
 
 
                  sb_add_char (acc, in->ptr[idx]);
                  sb_add_char (acc, in->ptr[idx]);
                  idx ++;
                  idx ++;
                }
                }
            }
            }
        }
        }
    }
    }
 
 
  return idx;
  return idx;
}
}
 
 
/* Fetch string from the input stream,
/* Fetch string from the input stream,
   rules:
   rules:
    'Bxyx<whitespace>   -> return 'Bxyza
    'Bxyx<whitespace>   -> return 'Bxyza
    %<expr>             -> return string of decimal value of <expr>
    %<expr>             -> return string of decimal value of <expr>
    "string"            -> return string
    "string"            -> return string
    (string)            -> return (string-including-whitespaces)
    (string)            -> return (string-including-whitespaces)
    xyx<whitespace>     -> return xyz.  */
    xyx<whitespace>     -> return xyz.  */
 
 
static int
static int
get_any_string (int idx, sb *in, sb *out)
get_any_string (int idx, sb *in, sb *out)
{
{
  sb_reset (out);
  sb_reset (out);
  idx = sb_skip_white (idx, in);
  idx = sb_skip_white (idx, in);
 
 
  if (idx < in->len)
  if (idx < in->len)
    {
    {
      if (in->len > idx + 2 && in->ptr[idx + 1] == '\'' && ISBASE (in->ptr[idx]))
      if (in->len > idx + 2 && in->ptr[idx + 1] == '\'' && ISBASE (in->ptr[idx]))
        {
        {
          while (!ISSEP (in->ptr[idx]))
          while (!ISSEP (in->ptr[idx]))
            sb_add_char (out, in->ptr[idx++]);
            sb_add_char (out, in->ptr[idx++]);
        }
        }
      else if (in->ptr[idx] == '%' && macro_alternate)
      else if (in->ptr[idx] == '%' && macro_alternate)
        {
        {
          int val;
          int val;
          char buf[20];
          char buf[20];
 
 
          /* Turns the next expression into a string.  */
          /* Turns the next expression into a string.  */
          /* xgettext: no-c-format */
          /* xgettext: no-c-format */
          idx = (*macro_expr) (_("% operator needs absolute expression"),
          idx = (*macro_expr) (_("% operator needs absolute expression"),
                               idx + 1,
                               idx + 1,
                               in,
                               in,
                               &val);
                               &val);
          sprintf (buf, "%d", val);
          sprintf (buf, "%d", val);
          sb_add_string (out, buf);
          sb_add_string (out, buf);
        }
        }
      else if (in->ptr[idx] == '"'
      else if (in->ptr[idx] == '"'
               || (in->ptr[idx] == '<' && (macro_alternate || macro_mri))
               || (in->ptr[idx] == '<' && (macro_alternate || macro_mri))
               || (macro_alternate && in->ptr[idx] == '\''))
               || (macro_alternate && in->ptr[idx] == '\''))
        {
        {
          if (macro_alternate && ! macro_strip_at && in->ptr[idx] != '<')
          if (macro_alternate && ! macro_strip_at && in->ptr[idx] != '<')
            {
            {
              /* Keep the quotes.  */
              /* Keep the quotes.  */
              sb_add_char (out, '"');
              sb_add_char (out, '"');
              idx = getstring (idx, in, out);
              idx = getstring (idx, in, out);
              sb_add_char (out, '"');
              sb_add_char (out, '"');
            }
            }
          else
          else
            {
            {
              idx = getstring (idx, in, out);
              idx = getstring (idx, in, out);
            }
            }
        }
        }
      else
      else
        {
        {
          char *br_buf = xmalloc(1);
          char *br_buf = xmalloc(1);
          char *in_br = br_buf;
          char *in_br = br_buf;
 
 
          *in_br = '\0';
          *in_br = '\0';
          while (idx < in->len
          while (idx < in->len
                 && (*in_br
                 && (*in_br
                     || (in->ptr[idx] != ' '
                     || (in->ptr[idx] != ' '
                         && in->ptr[idx] != '\t'))
                         && in->ptr[idx] != '\t'))
                 && in->ptr[idx] != ','
                 && in->ptr[idx] != ','
                 && (in->ptr[idx] != '<'
                 && (in->ptr[idx] != '<'
                     || (! macro_alternate && ! macro_mri)))
                     || (! macro_alternate && ! macro_mri)))
            {
            {
              char tchar = in->ptr[idx];
              char tchar = in->ptr[idx];
 
 
              switch (tchar)
              switch (tchar)
                {
                {
                case '"':
                case '"':
                case '\'':
                case '\'':
                  sb_add_char (out, in->ptr[idx++]);
                  sb_add_char (out, in->ptr[idx++]);
                  while (idx < in->len
                  while (idx < in->len
                         && in->ptr[idx] != tchar)
                         && in->ptr[idx] != tchar)
                    sb_add_char (out, in->ptr[idx++]);
                    sb_add_char (out, in->ptr[idx++]);
                  if (idx == in->len)
                  if (idx == in->len)
                    return idx;
                    return idx;
                  break;
                  break;
                case '(':
                case '(':
                case '[':
                case '[':
                  if (in_br > br_buf)
                  if (in_br > br_buf)
                    --in_br;
                    --in_br;
                  else
                  else
                    {
                    {
                      br_buf = xmalloc(strlen(in_br) + 2);
                      br_buf = xmalloc(strlen(in_br) + 2);
                      strcpy(br_buf + 1, in_br);
                      strcpy(br_buf + 1, in_br);
                      free(in_br);
                      free(in_br);
                      in_br = br_buf;
                      in_br = br_buf;
                    }
                    }
                  *in_br = tchar;
                  *in_br = tchar;
                  break;
                  break;
                case ')':
                case ')':
                  if (*in_br == '(')
                  if (*in_br == '(')
                    ++in_br;
                    ++in_br;
                  break;
                  break;
                case ']':
                case ']':
                  if (*in_br == '[')
                  if (*in_br == '[')
                    ++in_br;
                    ++in_br;
                  break;
                  break;
                }
                }
              sb_add_char (out, tchar);
              sb_add_char (out, tchar);
              ++idx;
              ++idx;
            }
            }
          free(br_buf);
          free(br_buf);
        }
        }
    }
    }
 
 
  return idx;
  return idx;
}
}
 
 
/* Allocate a new formal.  */
/* Allocate a new formal.  */
 
 
static formal_entry *
static formal_entry *
new_formal (void)
new_formal (void)
{
{
  formal_entry *formal;
  formal_entry *formal;
 
 
  formal = xmalloc (sizeof (formal_entry));
  formal = xmalloc (sizeof (formal_entry));
 
 
  sb_new (&formal->name);
  sb_new (&formal->name);
  sb_new (&formal->def);
  sb_new (&formal->def);
  sb_new (&formal->actual);
  sb_new (&formal->actual);
  formal->next = NULL;
  formal->next = NULL;
  formal->type = FORMAL_OPTIONAL;
  formal->type = FORMAL_OPTIONAL;
  return formal;
  return formal;
}
}
 
 
/* Free a formal.  */
/* Free a formal.  */
 
 
static void
static void
del_formal (formal_entry *formal)
del_formal (formal_entry *formal)
{
{
  sb_kill (&formal->actual);
  sb_kill (&formal->actual);
  sb_kill (&formal->def);
  sb_kill (&formal->def);
  sb_kill (&formal->name);
  sb_kill (&formal->name);
  free (formal);
  free (formal);
}
}
 
 
/* Pick up the formal parameters of a macro definition.  */
/* Pick up the formal parameters of a macro definition.  */
 
 
static int
static int
do_formals (macro_entry *macro, int idx, sb *in)
do_formals (macro_entry *macro, int idx, sb *in)
{
{
  formal_entry **p = &macro->formals;
  formal_entry **p = &macro->formals;
  const char *name;
  const char *name;
 
 
  idx = sb_skip_white (idx, in);
  idx = sb_skip_white (idx, in);
  while (idx < in->len)
  while (idx < in->len)
    {
    {
      formal_entry *formal = new_formal ();
      formal_entry *formal = new_formal ();
      int cidx;
      int cidx;
 
 
      idx = get_token (idx, in, &formal->name);
      idx = get_token (idx, in, &formal->name);
      if (formal->name.len == 0)
      if (formal->name.len == 0)
        {
        {
          if (macro->formal_count)
          if (macro->formal_count)
            --idx;
            --idx;
          break;
          break;
        }
        }
      idx = sb_skip_white (idx, in);
      idx = sb_skip_white (idx, in);
      /* This is a formal.  */
      /* This is a formal.  */
      name = sb_terminate (&formal->name);
      name = sb_terminate (&formal->name);
      if (! macro_mri
      if (! macro_mri
          && idx < in->len
          && idx < in->len
          && in->ptr[idx] == ':'
          && in->ptr[idx] == ':'
          && (! is_name_beginner (':')
          && (! is_name_beginner (':')
              || idx + 1 >= in->len
              || idx + 1 >= in->len
              || ! is_part_of_name (in->ptr[idx + 1])))
              || ! is_part_of_name (in->ptr[idx + 1])))
        {
        {
          /* Got a qualifier.  */
          /* Got a qualifier.  */
          sb qual;
          sb qual;
 
 
          sb_new (&qual);
          sb_new (&qual);
          idx = get_token (sb_skip_white (idx + 1, in), in, &qual);
          idx = get_token (sb_skip_white (idx + 1, in), in, &qual);
          sb_terminate (&qual);
          sb_terminate (&qual);
          if (qual.len == 0)
          if (qual.len == 0)
            as_bad_where (macro->file,
            as_bad_where (macro->file,
                          macro->line,
                          macro->line,
                          _("Missing parameter qualifier for `%s' in macro `%s'"),
                          _("Missing parameter qualifier for `%s' in macro `%s'"),
                          name,
                          name,
                          macro->name);
                          macro->name);
          else if (strcmp (qual.ptr, "req") == 0)
          else if (strcmp (qual.ptr, "req") == 0)
            formal->type = FORMAL_REQUIRED;
            formal->type = FORMAL_REQUIRED;
          else if (strcmp (qual.ptr, "vararg") == 0)
          else if (strcmp (qual.ptr, "vararg") == 0)
            formal->type = FORMAL_VARARG;
            formal->type = FORMAL_VARARG;
          else
          else
            as_bad_where (macro->file,
            as_bad_where (macro->file,
                          macro->line,
                          macro->line,
                          _("`%s' is not a valid parameter qualifier for `%s' in macro `%s'"),
                          _("`%s' is not a valid parameter qualifier for `%s' in macro `%s'"),
                          qual.ptr,
                          qual.ptr,
                          name,
                          name,
                          macro->name);
                          macro->name);
          sb_kill (&qual);
          sb_kill (&qual);
          idx = sb_skip_white (idx, in);
          idx = sb_skip_white (idx, in);
        }
        }
      if (idx < in->len && in->ptr[idx] == '=')
      if (idx < in->len && in->ptr[idx] == '=')
        {
        {
          /* Got a default.  */
          /* Got a default.  */
          idx = get_any_string (idx + 1, in, &formal->def);
          idx = get_any_string (idx + 1, in, &formal->def);
          idx = sb_skip_white (idx, in);
          idx = sb_skip_white (idx, in);
          if (formal->type == FORMAL_REQUIRED)
          if (formal->type == FORMAL_REQUIRED)
            {
            {
              sb_reset (&formal->def);
              sb_reset (&formal->def);
              as_warn_where (macro->file,
              as_warn_where (macro->file,
                            macro->line,
                            macro->line,
                            _("Pointless default value for required parameter `%s' in macro `%s'"),
                            _("Pointless default value for required parameter `%s' in macro `%s'"),
                            name,
                            name,
                            macro->name);
                            macro->name);
            }
            }
        }
        }
 
 
      /* Add to macro's hash table.  */
      /* Add to macro's hash table.  */
      if (! hash_find (macro->formal_hash, name))
      if (! hash_find (macro->formal_hash, name))
        hash_jam (macro->formal_hash, name, formal);
        hash_jam (macro->formal_hash, name, formal);
      else
      else
        as_bad_where (macro->file,
        as_bad_where (macro->file,
                      macro->line,
                      macro->line,
                      _("A parameter named `%s' already exists for macro `%s'"),
                      _("A parameter named `%s' already exists for macro `%s'"),
                      name,
                      name,
                      macro->name);
                      macro->name);
 
 
      formal->index = macro->formal_count++;
      formal->index = macro->formal_count++;
      *p = formal;
      *p = formal;
      p = &formal->next;
      p = &formal->next;
      if (formal->type == FORMAL_VARARG)
      if (formal->type == FORMAL_VARARG)
        break;
        break;
      cidx = idx;
      cidx = idx;
      idx = sb_skip_comma (idx, in);
      idx = sb_skip_comma (idx, in);
      if (idx != cidx && idx >= in->len)
      if (idx != cidx && idx >= in->len)
        {
        {
          idx = cidx;
          idx = cidx;
          break;
          break;
        }
        }
    }
    }
 
 
  if (macro_mri)
  if (macro_mri)
    {
    {
      formal_entry *formal = new_formal ();
      formal_entry *formal = new_formal ();
 
 
      /* Add a special NARG formal, which macro_expand will set to the
      /* Add a special NARG formal, which macro_expand will set to the
         number of arguments.  */
         number of arguments.  */
      /* The same MRI assemblers which treat '@' characters also use
      /* The same MRI assemblers which treat '@' characters also use
         the name $NARG.  At least until we find an exception.  */
         the name $NARG.  At least until we find an exception.  */
      if (macro_strip_at)
      if (macro_strip_at)
        name = "$NARG";
        name = "$NARG";
      else
      else
        name = "NARG";
        name = "NARG";
 
 
      sb_add_string (&formal->name, name);
      sb_add_string (&formal->name, name);
 
 
      /* Add to macro's hash table.  */
      /* Add to macro's hash table.  */
      if (hash_find (macro->formal_hash, name))
      if (hash_find (macro->formal_hash, name))
        as_bad_where (macro->file,
        as_bad_where (macro->file,
                      macro->line,
                      macro->line,
                      _("Reserved word `%s' used as parameter in macro `%s'"),
                      _("Reserved word `%s' used as parameter in macro `%s'"),
                      name,
                      name,
                      macro->name);
                      macro->name);
      hash_jam (macro->formal_hash, name, formal);
      hash_jam (macro->formal_hash, name, formal);
 
 
      formal->index = NARG_INDEX;
      formal->index = NARG_INDEX;
      *p = formal;
      *p = formal;
    }
    }
 
 
  return idx;
  return idx;
}
}
 
 
/* Define a new macro.  Returns NULL on success, otherwise returns an
/* Define a new macro.  Returns NULL on success, otherwise returns an
   error message.  If NAMEP is not NULL, *NAMEP is set to the name of
   error message.  If NAMEP is not NULL, *NAMEP is set to the name of
   the macro which was defined.  */
   the macro which was defined.  */
 
 
const char *
const char *
define_macro (int idx, sb *in, sb *label,
define_macro (int idx, sb *in, sb *label,
              int (*get_line) (sb *),
              int (*get_line) (sb *),
              char *file, unsigned int line,
              char *file, unsigned int line,
              const char **namep)
              const char **namep)
{
{
  macro_entry *macro;
  macro_entry *macro;
  sb name;
  sb name;
  const char *error = NULL;
  const char *error = NULL;
 
 
  macro = (macro_entry *) xmalloc (sizeof (macro_entry));
  macro = (macro_entry *) xmalloc (sizeof (macro_entry));
  sb_new (&macro->sub);
  sb_new (&macro->sub);
  sb_new (&name);
  sb_new (&name);
  macro->file = file;
  macro->file = file;
  macro->line = line;
  macro->line = line;
 
 
  macro->formal_count = 0;
  macro->formal_count = 0;
  macro->formals = 0;
  macro->formals = 0;
  macro->formal_hash = hash_new ();
  macro->formal_hash = hash_new ();
 
 
  idx = sb_skip_white (idx, in);
  idx = sb_skip_white (idx, in);
  if (! buffer_and_nest ("MACRO", "ENDM", &macro->sub, get_line))
  if (! buffer_and_nest ("MACRO", "ENDM", &macro->sub, get_line))
    error = _("unexpected end of file in macro `%s' definition");
    error = _("unexpected end of file in macro `%s' definition");
  if (label != NULL && label->len != 0)
  if (label != NULL && label->len != 0)
    {
    {
      sb_add_sb (&name, label);
      sb_add_sb (&name, label);
      macro->name = sb_terminate (&name);
      macro->name = sb_terminate (&name);
      if (idx < in->len && in->ptr[idx] == '(')
      if (idx < in->len && in->ptr[idx] == '(')
        {
        {
          /* It's the label: MACRO (formals,...)  sort  */
          /* It's the label: MACRO (formals,...)  sort  */
          idx = do_formals (macro, idx + 1, in);
          idx = do_formals (macro, idx + 1, in);
          if (idx < in->len && in->ptr[idx] == ')')
          if (idx < in->len && in->ptr[idx] == ')')
            idx = sb_skip_white (idx + 1, in);
            idx = sb_skip_white (idx + 1, in);
          else if (!error)
          else if (!error)
            error = _("missing `)' after formals in macro definition `%s'");
            error = _("missing `)' after formals in macro definition `%s'");
        }
        }
      else
      else
        {
        {
          /* It's the label: MACRO formals,...  sort  */
          /* It's the label: MACRO formals,...  sort  */
          idx = do_formals (macro, idx, in);
          idx = do_formals (macro, idx, in);
        }
        }
    }
    }
  else
  else
    {
    {
      int cidx;
      int cidx;
 
 
      idx = get_token (idx, in, &name);
      idx = get_token (idx, in, &name);
      macro->name = sb_terminate (&name);
      macro->name = sb_terminate (&name);
      if (name.len == 0)
      if (name.len == 0)
        error = _("Missing macro name");
        error = _("Missing macro name");
      cidx = sb_skip_white (idx, in);
      cidx = sb_skip_white (idx, in);
      idx = sb_skip_comma (cidx, in);
      idx = sb_skip_comma (cidx, in);
      if (idx == cidx || idx < in->len)
      if (idx == cidx || idx < in->len)
        idx = do_formals (macro, idx, in);
        idx = do_formals (macro, idx, in);
      else
      else
        idx = cidx;
        idx = cidx;
    }
    }
  if (!error && idx < in->len)
  if (!error && idx < in->len)
    error = _("Bad parameter list for macro `%s'");
    error = _("Bad parameter list for macro `%s'");
 
 
  /* And stick it in the macro hash table.  */
  /* And stick it in the macro hash table.  */
  for (idx = 0; idx < name.len; idx++)
  for (idx = 0; idx < name.len; idx++)
    name.ptr[idx] = TOLOWER (name.ptr[idx]);
    name.ptr[idx] = TOLOWER (name.ptr[idx]);
  if (hash_find (macro_hash, macro->name))
  if (hash_find (macro_hash, macro->name))
    error = _("Macro `%s' was already defined");
    error = _("Macro `%s' was already defined");
  if (!error)
  if (!error)
    error = hash_jam (macro_hash, macro->name, (PTR) macro);
    error = hash_jam (macro_hash, macro->name, (PTR) macro);
 
 
  if (namep != NULL)
  if (namep != NULL)
    *namep = macro->name;
    *namep = macro->name;
 
 
  if (!error)
  if (!error)
    macro_defined = 1;
    macro_defined = 1;
  else
  else
    free_macro (macro);
    free_macro (macro);
 
 
  return error;
  return error;
}
}
 
 
/* Scan a token, and then skip KIND.  */
/* Scan a token, and then skip KIND.  */
 
 
static int
static int
get_apost_token (int idx, sb *in, sb *name, int kind)
get_apost_token (int idx, sb *in, sb *name, int kind)
{
{
  idx = get_token (idx, in, name);
  idx = get_token (idx, in, name);
  if (idx < in->len
  if (idx < in->len
      && in->ptr[idx] == kind
      && in->ptr[idx] == kind
      && (! macro_mri || macro_strip_at)
      && (! macro_mri || macro_strip_at)
      && (! macro_strip_at || kind == '@'))
      && (! macro_strip_at || kind == '@'))
    idx++;
    idx++;
  return idx;
  return idx;
}
}
 
 
/* Substitute the actual value for a formal parameter.  */
/* Substitute the actual value for a formal parameter.  */
 
 
static int
static int
sub_actual (int start, sb *in, sb *t, struct hash_control *formal_hash,
sub_actual (int start, sb *in, sb *t, struct hash_control *formal_hash,
            int kind, sb *out, int copyifnotthere)
            int kind, sb *out, int copyifnotthere)
{
{
  int src;
  int src;
  formal_entry *ptr;
  formal_entry *ptr;
 
 
  src = get_apost_token (start, in, t, kind);
  src = get_apost_token (start, in, t, kind);
  /* See if it's in the macro's hash table, unless this is
  /* See if it's in the macro's hash table, unless this is
     macro_strip_at and kind is '@' and the token did not end in '@'.  */
     macro_strip_at and kind is '@' and the token did not end in '@'.  */
  if (macro_strip_at
  if (macro_strip_at
      && kind == '@'
      && kind == '@'
      && (src == start || in->ptr[src - 1] != '@'))
      && (src == start || in->ptr[src - 1] != '@'))
    ptr = NULL;
    ptr = NULL;
  else
  else
    ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (t));
    ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (t));
  if (ptr)
  if (ptr)
    {
    {
      if (ptr->actual.len)
      if (ptr->actual.len)
        {
        {
          sb_add_sb (out, &ptr->actual);
          sb_add_sb (out, &ptr->actual);
        }
        }
      else
      else
        {
        {
          sb_add_sb (out, &ptr->def);
          sb_add_sb (out, &ptr->def);
        }
        }
    }
    }
  else if (kind == '&')
  else if (kind == '&')
    {
    {
      /* Doing this permits people to use & in macro bodies.  */
      /* Doing this permits people to use & in macro bodies.  */
      sb_add_char (out, '&');
      sb_add_char (out, '&');
      sb_add_sb (out, t);
      sb_add_sb (out, t);
    }
    }
  else if (copyifnotthere)
  else if (copyifnotthere)
    {
    {
      sb_add_sb (out, t);
      sb_add_sb (out, t);
    }
    }
  else
  else
    {
    {
      sb_add_char (out, '\\');
      sb_add_char (out, '\\');
      sb_add_sb (out, t);
      sb_add_sb (out, t);
    }
    }
  return src;
  return src;
}
}
 
 
/* Expand the body of a macro.  */
/* Expand the body of a macro.  */
 
 
static const char *
static const char *
macro_expand_body (sb *in, sb *out, formal_entry *formals,
macro_expand_body (sb *in, sb *out, formal_entry *formals,
                   struct hash_control *formal_hash, const macro_entry *macro)
                   struct hash_control *formal_hash, const macro_entry *macro)
{
{
  sb t;
  sb t;
  int src = 0, inquote = 0, macro_line = 0;
  int src = 0, inquote = 0, macro_line = 0;
  formal_entry *loclist = NULL;
  formal_entry *loclist = NULL;
  const char *err = NULL;
  const char *err = NULL;
 
 
  sb_new (&t);
  sb_new (&t);
 
 
  while (src < in->len && !err)
  while (src < in->len && !err)
    {
    {
      if (in->ptr[src] == '&')
      if (in->ptr[src] == '&')
        {
        {
          sb_reset (&t);
          sb_reset (&t);
          if (macro_mri)
          if (macro_mri)
            {
            {
              if (src + 1 < in->len && in->ptr[src + 1] == '&')
              if (src + 1 < in->len && in->ptr[src + 1] == '&')
                src = sub_actual (src + 2, in, &t, formal_hash, '\'', out, 1);
                src = sub_actual (src + 2, in, &t, formal_hash, '\'', out, 1);
              else
              else
                sb_add_char (out, in->ptr[src++]);
                sb_add_char (out, in->ptr[src++]);
            }
            }
          else
          else
            {
            {
              /* FIXME: Why do we do this?  */
              /* FIXME: Why do we do this?  */
              /* At least in alternate mode this seems correct; without this
              /* At least in alternate mode this seems correct; without this
                 one can't append a literal to a parameter.  */
                 one can't append a literal to a parameter.  */
              src = sub_actual (src + 1, in, &t, formal_hash, '&', out, 0);
              src = sub_actual (src + 1, in, &t, formal_hash, '&', out, 0);
            }
            }
        }
        }
      else if (in->ptr[src] == '\\')
      else if (in->ptr[src] == '\\')
        {
        {
          src++;
          src++;
          if (src < in->len && in->ptr[src] == '(')
          if (src < in->len && in->ptr[src] == '(')
            {
            {
              /* Sub in till the next ')' literally.  */
              /* Sub in till the next ')' literally.  */
              src++;
              src++;
              while (src < in->len && in->ptr[src] != ')')
              while (src < in->len && in->ptr[src] != ')')
                {
                {
                  sb_add_char (out, in->ptr[src++]);
                  sb_add_char (out, in->ptr[src++]);
                }
                }
              if (src < in->len)
              if (src < in->len)
                src++;
                src++;
              else if (!macro)
              else if (!macro)
                err = _("missing `)'");
                err = _("missing `)'");
              else
              else
                as_bad_where (macro->file, macro->line + macro_line, _("missing `)'"));
                as_bad_where (macro->file, macro->line + macro_line, _("missing `)'"));
            }
            }
          else if (src < in->len && in->ptr[src] == '@')
          else if (src < in->len && in->ptr[src] == '@')
            {
            {
              /* Sub in the macro invocation number.  */
              /* Sub in the macro invocation number.  */
 
 
              char buffer[10];
              char buffer[10];
              src++;
              src++;
              sprintf (buffer, "%d", macro_number);
              sprintf (buffer, "%d", macro_number);
              sb_add_string (out, buffer);
              sb_add_string (out, buffer);
            }
            }
          else if (src < in->len && in->ptr[src] == '&')
          else if (src < in->len && in->ptr[src] == '&')
            {
            {
              /* This is a preprocessor variable name, we don't do them
              /* This is a preprocessor variable name, we don't do them
                 here.  */
                 here.  */
              sb_add_char (out, '\\');
              sb_add_char (out, '\\');
              sb_add_char (out, '&');
              sb_add_char (out, '&');
              src++;
              src++;
            }
            }
          else if (macro_mri && src < in->len && ISALNUM (in->ptr[src]))
          else if (macro_mri && src < in->len && ISALNUM (in->ptr[src]))
            {
            {
              int ind;
              int ind;
              formal_entry *f;
              formal_entry *f;
 
 
              if (ISDIGIT (in->ptr[src]))
              if (ISDIGIT (in->ptr[src]))
                ind = in->ptr[src] - '0';
                ind = in->ptr[src] - '0';
              else if (ISUPPER (in->ptr[src]))
              else if (ISUPPER (in->ptr[src]))
                ind = in->ptr[src] - 'A' + 10;
                ind = in->ptr[src] - 'A' + 10;
              else
              else
                ind = in->ptr[src] - 'a' + 10;
                ind = in->ptr[src] - 'a' + 10;
              ++src;
              ++src;
              for (f = formals; f != NULL; f = f->next)
              for (f = formals; f != NULL; f = f->next)
                {
                {
                  if (f->index == ind - 1)
                  if (f->index == ind - 1)
                    {
                    {
                      if (f->actual.len != 0)
                      if (f->actual.len != 0)
                        sb_add_sb (out, &f->actual);
                        sb_add_sb (out, &f->actual);
                      else
                      else
                        sb_add_sb (out, &f->def);
                        sb_add_sb (out, &f->def);
                      break;
                      break;
                    }
                    }
                }
                }
            }
            }
          else
          else
            {
            {
              sb_reset (&t);
              sb_reset (&t);
              src = sub_actual (src, in, &t, formal_hash, '\'', out, 0);
              src = sub_actual (src, in, &t, formal_hash, '\'', out, 0);
            }
            }
        }
        }
      else if ((macro_alternate || macro_mri)
      else if ((macro_alternate || macro_mri)
               && is_name_beginner (in->ptr[src])
               && is_name_beginner (in->ptr[src])
               && (! inquote
               && (! inquote
                   || ! macro_strip_at
                   || ! macro_strip_at
                   || (src > 0 && in->ptr[src - 1] == '@')))
                   || (src > 0 && in->ptr[src - 1] == '@')))
        {
        {
          if (! macro
          if (! macro
              || src + 5 >= in->len
              || src + 5 >= in->len
              || strncasecmp (in->ptr + src, "LOCAL", 5) != 0
              || strncasecmp (in->ptr + src, "LOCAL", 5) != 0
              || ! ISWHITE (in->ptr[src + 5]))
              || ! ISWHITE (in->ptr[src + 5]))
            {
            {
              sb_reset (&t);
              sb_reset (&t);
              src = sub_actual (src, in, &t, formal_hash,
              src = sub_actual (src, in, &t, formal_hash,
                                (macro_strip_at && inquote) ? '@' : '\'',
                                (macro_strip_at && inquote) ? '@' : '\'',
                                out, 1);
                                out, 1);
            }
            }
          else
          else
            {
            {
              src = sb_skip_white (src + 5, in);
              src = sb_skip_white (src + 5, in);
              while (in->ptr[src] != '\n')
              while (in->ptr[src] != '\n')
                {
                {
                  const char *name;
                  const char *name;
                  formal_entry *f = new_formal ();
                  formal_entry *f = new_formal ();
 
 
                  src = get_token (src, in, &f->name);
                  src = get_token (src, in, &f->name);
                  name = sb_terminate (&f->name);
                  name = sb_terminate (&f->name);
                  if (! hash_find (formal_hash, name))
                  if (! hash_find (formal_hash, name))
                    {
                    {
                      static int loccnt;
                      static int loccnt;
                      char buf[20];
                      char buf[20];
 
 
                      f->index = LOCAL_INDEX;
                      f->index = LOCAL_INDEX;
                      f->next = loclist;
                      f->next = loclist;
                      loclist = f;
                      loclist = f;
 
 
                      sprintf (buf, IS_ELF ? ".LL%04x" : "LL%04x", ++loccnt);
                      sprintf (buf, IS_ELF ? ".LL%04x" : "LL%04x", ++loccnt);
                      sb_add_string (&f->actual, buf);
                      sb_add_string (&f->actual, buf);
 
 
                      err = hash_jam (formal_hash, name, f);
                      err = hash_jam (formal_hash, name, f);
                      if (err != NULL)
                      if (err != NULL)
                        break;
                        break;
                    }
                    }
                  else
                  else
                    {
                    {
                      as_bad_where (macro->file,
                      as_bad_where (macro->file,
                                    macro->line + macro_line,
                                    macro->line + macro_line,
                                    _("`%s' was already used as parameter (or another local) name"),
                                    _("`%s' was already used as parameter (or another local) name"),
                                    name);
                                    name);
                      del_formal (f);
                      del_formal (f);
                    }
                    }
 
 
                  src = sb_skip_comma (src, in);
                  src = sb_skip_comma (src, in);
                }
                }
            }
            }
        }
        }
      else if (in->ptr[src] == '"'
      else if (in->ptr[src] == '"'
               || (macro_mri && in->ptr[src] == '\''))
               || (macro_mri && in->ptr[src] == '\''))
        {
        {
          inquote = !inquote;
          inquote = !inquote;
          sb_add_char (out, in->ptr[src++]);
          sb_add_char (out, in->ptr[src++]);
        }
        }
      else if (in->ptr[src] == '@' && macro_strip_at)
      else if (in->ptr[src] == '@' && macro_strip_at)
        {
        {
          ++src;
          ++src;
          if (src < in->len
          if (src < in->len
              && in->ptr[src] == '@')
              && in->ptr[src] == '@')
            {
            {
              sb_add_char (out, '@');
              sb_add_char (out, '@');
              ++src;
              ++src;
            }
            }
        }
        }
      else if (macro_mri
      else if (macro_mri
               && in->ptr[src] == '='
               && in->ptr[src] == '='
               && src + 1 < in->len
               && src + 1 < in->len
               && in->ptr[src + 1] == '=')
               && in->ptr[src + 1] == '=')
        {
        {
          formal_entry *ptr;
          formal_entry *ptr;
 
 
          sb_reset (&t);
          sb_reset (&t);
          src = get_token (src + 2, in, &t);
          src = get_token (src + 2, in, &t);
          ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (&t));
          ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (&t));
          if (ptr == NULL)
          if (ptr == NULL)
            {
            {
              /* FIXME: We should really return a warning string here,
              /* FIXME: We should really return a warning string here,
                 but we can't, because the == might be in the MRI
                 but we can't, because the == might be in the MRI
                 comment field, and, since the nature of the MRI
                 comment field, and, since the nature of the MRI
                 comment field depends upon the exact instruction
                 comment field depends upon the exact instruction
                 being used, we don't have enough information here to
                 being used, we don't have enough information here to
                 figure out whether it is or not.  Instead, we leave
                 figure out whether it is or not.  Instead, we leave
                 the == in place, which should cause a syntax error if
                 the == in place, which should cause a syntax error if
                 it is not in a comment.  */
                 it is not in a comment.  */
              sb_add_char (out, '=');
              sb_add_char (out, '=');
              sb_add_char (out, '=');
              sb_add_char (out, '=');
              sb_add_sb (out, &t);
              sb_add_sb (out, &t);
            }
            }
          else
          else
            {
            {
              if (ptr->actual.len)
              if (ptr->actual.len)
                {
                {
                  sb_add_string (out, "-1");
                  sb_add_string (out, "-1");
                }
                }
              else
              else
                {
                {
                  sb_add_char (out, '0');
                  sb_add_char (out, '0');
                }
                }
            }
            }
        }
        }
      else
      else
        {
        {
          if (in->ptr[src] == '\n')
          if (in->ptr[src] == '\n')
            ++macro_line;
            ++macro_line;
          sb_add_char (out, in->ptr[src++]);
          sb_add_char (out, in->ptr[src++]);
        }
        }
    }
    }
 
 
  sb_kill (&t);
  sb_kill (&t);
 
 
  while (loclist != NULL)
  while (loclist != NULL)
    {
    {
      formal_entry *f;
      formal_entry *f;
 
 
      f = loclist->next;
      f = loclist->next;
      /* Setting the value to NULL effectively deletes the entry.  We
      /* Setting the value to NULL effectively deletes the entry.  We
         avoid calling hash_delete because it doesn't reclaim memory.  */
         avoid calling hash_delete because it doesn't reclaim memory.  */
      hash_jam (formal_hash, sb_terminate (&loclist->name), NULL);
      hash_jam (formal_hash, sb_terminate (&loclist->name), NULL);
      del_formal (loclist);
      del_formal (loclist);
      loclist = f;
      loclist = f;
    }
    }
 
 
  return err;
  return err;
}
}
 
 
/* Assign values to the formal parameters of a macro, and expand the
/* Assign values to the formal parameters of a macro, and expand the
   body.  */
   body.  */
 
 
static const char *
static const char *
macro_expand (int idx, sb *in, macro_entry *m, sb *out)
macro_expand (int idx, sb *in, macro_entry *m, sb *out)
{
{
  sb t;
  sb t;
  formal_entry *ptr;
  formal_entry *ptr;
  formal_entry *f;
  formal_entry *f;
  int is_keyword = 0;
  int is_keyword = 0;
  int narg = 0;
  int narg = 0;
  const char *err = NULL;
  const char *err = NULL;
 
 
  sb_new (&t);
  sb_new (&t);
 
 
  /* Reset any old value the actuals may have.  */
  /* Reset any old value the actuals may have.  */
  for (f = m->formals; f; f = f->next)
  for (f = m->formals; f; f = f->next)
    sb_reset (&f->actual);
    sb_reset (&f->actual);
  f = m->formals;
  f = m->formals;
  while (f != NULL && f->index < 0)
  while (f != NULL && f->index < 0)
    f = f->next;
    f = f->next;
 
 
  if (macro_mri)
  if (macro_mri)
    {
    {
      /* The macro may be called with an optional qualifier, which may
      /* The macro may be called with an optional qualifier, which may
         be referred to in the macro body as \0.  */
         be referred to in the macro body as \0.  */
      if (idx < in->len && in->ptr[idx] == '.')
      if (idx < in->len && in->ptr[idx] == '.')
        {
        {
          /* The Microtec assembler ignores this if followed by a white space.
          /* The Microtec assembler ignores this if followed by a white space.
             (Macro invocation with empty extension) */
             (Macro invocation with empty extension) */
          idx++;
          idx++;
          if (    idx < in->len
          if (    idx < in->len
                  && in->ptr[idx] != ' '
                  && in->ptr[idx] != ' '
                  && in->ptr[idx] != '\t')
                  && in->ptr[idx] != '\t')
            {
            {
              formal_entry *n = new_formal ();
              formal_entry *n = new_formal ();
 
 
              n->index = QUAL_INDEX;
              n->index = QUAL_INDEX;
 
 
              n->next = m->formals;
              n->next = m->formals;
              m->formals = n;
              m->formals = n;
 
 
              idx = get_any_string (idx, in, &n->actual);
              idx = get_any_string (idx, in, &n->actual);
            }
            }
        }
        }
    }
    }
 
 
  /* Peel off the actuals and store them away in the hash tables' actuals.  */
  /* Peel off the actuals and store them away in the hash tables' actuals.  */
  idx = sb_skip_white (idx, in);
  idx = sb_skip_white (idx, in);
  while (idx < in->len)
  while (idx < in->len)
    {
    {
      int scan;
      int scan;
 
 
      /* Look and see if it's a positional or keyword arg.  */
      /* Look and see if it's a positional or keyword arg.  */
      scan = idx;
      scan = idx;
      while (scan < in->len
      while (scan < in->len
             && !ISSEP (in->ptr[scan])
             && !ISSEP (in->ptr[scan])
             && !(macro_mri && in->ptr[scan] == '\'')
             && !(macro_mri && in->ptr[scan] == '\'')
             && (!macro_alternate && in->ptr[scan] != '='))
             && (!macro_alternate && in->ptr[scan] != '='))
        scan++;
        scan++;
      if (scan < in->len && !macro_alternate && in->ptr[scan] == '=')
      if (scan < in->len && !macro_alternate && in->ptr[scan] == '=')
        {
        {
          is_keyword = 1;
          is_keyword = 1;
 
 
          /* It's OK to go from positional to keyword.  */
          /* It's OK to go from positional to keyword.  */
 
 
          /* This is a keyword arg, fetch the formal name and
          /* This is a keyword arg, fetch the formal name and
             then the actual stuff.  */
             then the actual stuff.  */
          sb_reset (&t);
          sb_reset (&t);
          idx = get_token (idx, in, &t);
          idx = get_token (idx, in, &t);
          if (in->ptr[idx] != '=')
          if (in->ptr[idx] != '=')
            {
            {
              err = _("confusion in formal parameters");
              err = _("confusion in formal parameters");
              break;
              break;
            }
            }
 
 
          /* Lookup the formal in the macro's list.  */
          /* Lookup the formal in the macro's list.  */
          ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t));
          ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t));
          if (!ptr)
          if (!ptr)
            as_bad (_("Parameter named `%s' does not exist for macro `%s'"),
            as_bad (_("Parameter named `%s' does not exist for macro `%s'"),
                    t.ptr,
                    t.ptr,
                    m->name);
                    m->name);
          else
          else
            {
            {
              /* Insert this value into the right place.  */
              /* Insert this value into the right place.  */
              if (ptr->actual.len)
              if (ptr->actual.len)
                {
                {
                  as_warn (_("Value for parameter `%s' of macro `%s' was already specified"),
                  as_warn (_("Value for parameter `%s' of macro `%s' was already specified"),
                           ptr->name.ptr,
                           ptr->name.ptr,
                           m->name);
                           m->name);
                  sb_reset (&ptr->actual);
                  sb_reset (&ptr->actual);
                }
                }
              idx = get_any_string (idx + 1, in, &ptr->actual);
              idx = get_any_string (idx + 1, in, &ptr->actual);
              if (ptr->actual.len > 0)
              if (ptr->actual.len > 0)
                ++narg;
                ++narg;
            }
            }
        }
        }
      else
      else
        {
        {
          if (is_keyword)
          if (is_keyword)
            {
            {
              err = _("can't mix positional and keyword arguments");
              err = _("can't mix positional and keyword arguments");
              break;
              break;
            }
            }
 
 
          if (!f)
          if (!f)
            {
            {
              formal_entry **pf;
              formal_entry **pf;
              int c;
              int c;
 
 
              if (!macro_mri)
              if (!macro_mri)
                {
                {
                  err = _("too many positional arguments");
                  err = _("too many positional arguments");
                  break;
                  break;
                }
                }
 
 
              f = new_formal ();
              f = new_formal ();
 
 
              c = -1;
              c = -1;
              for (pf = &m->formals; *pf != NULL; pf = &(*pf)->next)
              for (pf = &m->formals; *pf != NULL; pf = &(*pf)->next)
                if ((*pf)->index >= c)
                if ((*pf)->index >= c)
                  c = (*pf)->index + 1;
                  c = (*pf)->index + 1;
              if (c == -1)
              if (c == -1)
                c = 0;
                c = 0;
              *pf = f;
              *pf = f;
              f->index = c;
              f->index = c;
            }
            }
 
 
          if (f->type != FORMAL_VARARG)
          if (f->type != FORMAL_VARARG)
            idx = get_any_string (idx, in, &f->actual);
            idx = get_any_string (idx, in, &f->actual);
          else
          else
            {
            {
              sb_add_buffer (&f->actual, in->ptr + idx, in->len - idx);
              sb_add_buffer (&f->actual, in->ptr + idx, in->len - idx);
              idx = in->len;
              idx = in->len;
            }
            }
          if (f->actual.len > 0)
          if (f->actual.len > 0)
            ++narg;
            ++narg;
          do
          do
            {
            {
              f = f->next;
              f = f->next;
            }
            }
          while (f != NULL && f->index < 0);
          while (f != NULL && f->index < 0);
        }
        }
 
 
      if (! macro_mri)
      if (! macro_mri)
        idx = sb_skip_comma (idx, in);
        idx = sb_skip_comma (idx, in);
      else
      else
        {
        {
          if (in->ptr[idx] == ',')
          if (in->ptr[idx] == ',')
            ++idx;
            ++idx;
          if (ISWHITE (in->ptr[idx]))
          if (ISWHITE (in->ptr[idx]))
            break;
            break;
        }
        }
    }
    }
 
 
  if (! err)
  if (! err)
    {
    {
      for (ptr = m->formals; ptr; ptr = ptr->next)
      for (ptr = m->formals; ptr; ptr = ptr->next)
        {
        {
          if (ptr->type == FORMAL_REQUIRED && ptr->actual.len == 0)
          if (ptr->type == FORMAL_REQUIRED && ptr->actual.len == 0)
            as_bad (_("Missing value for required parameter `%s' of macro `%s'"),
            as_bad (_("Missing value for required parameter `%s' of macro `%s'"),
                    ptr->name.ptr,
                    ptr->name.ptr,
                    m->name);
                    m->name);
        }
        }
 
 
      if (macro_mri)
      if (macro_mri)
        {
        {
          char buffer[20];
          char buffer[20];
 
 
          sb_reset (&t);
          sb_reset (&t);
          sb_add_string (&t, macro_strip_at ? "$NARG" : "NARG");
          sb_add_string (&t, macro_strip_at ? "$NARG" : "NARG");
          ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t));
          ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t));
          sprintf (buffer, "%d", narg);
          sprintf (buffer, "%d", narg);
          sb_add_string (&ptr->actual, buffer);
          sb_add_string (&ptr->actual, buffer);
        }
        }
 
 
      err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, m);
      err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, m);
    }
    }
 
 
  /* Discard any unnamed formal arguments.  */
  /* Discard any unnamed formal arguments.  */
  if (macro_mri)
  if (macro_mri)
    {
    {
      formal_entry **pf;
      formal_entry **pf;
 
 
      pf = &m->formals;
      pf = &m->formals;
      while (*pf != NULL)
      while (*pf != NULL)
        {
        {
          if ((*pf)->name.len != 0)
          if ((*pf)->name.len != 0)
            pf = &(*pf)->next;
            pf = &(*pf)->next;
          else
          else
            {
            {
              f = (*pf)->next;
              f = (*pf)->next;
              del_formal (*pf);
              del_formal (*pf);
              *pf = f;
              *pf = f;
            }
            }
        }
        }
    }
    }
 
 
  sb_kill (&t);
  sb_kill (&t);
  if (!err)
  if (!err)
    macro_number++;
    macro_number++;
 
 
  return err;
  return err;
}
}
 
 
/* Check for a macro.  If one is found, put the expansion into
/* Check for a macro.  If one is found, put the expansion into
   *EXPAND.  Return 1 if a macro is found, 0 otherwise.  */
   *EXPAND.  Return 1 if a macro is found, 0 otherwise.  */
 
 
int
int
check_macro (const char *line, sb *expand,
check_macro (const char *line, sb *expand,
             const char **error, macro_entry **info)
             const char **error, macro_entry **info)
{
{
  const char *s;
  const char *s;
  char *copy, *cs;
  char *copy, *cs;
  macro_entry *macro;
  macro_entry *macro;
  sb line_sb;
  sb line_sb;
 
 
  if (! is_name_beginner (*line)
  if (! is_name_beginner (*line)
      && (! macro_mri || *line != '.'))
      && (! macro_mri || *line != '.'))
    return 0;
    return 0;
 
 
  s = line + 1;
  s = line + 1;
  while (is_part_of_name (*s))
  while (is_part_of_name (*s))
    ++s;
    ++s;
  if (is_name_ender (*s))
  if (is_name_ender (*s))
    ++s;
    ++s;
 
 
  copy = (char *) alloca (s - line + 1);
  copy = (char *) alloca (s - line + 1);
  memcpy (copy, line, s - line);
  memcpy (copy, line, s - line);
  copy[s - line] = '\0';
  copy[s - line] = '\0';
  for (cs = copy; *cs != '\0'; cs++)
  for (cs = copy; *cs != '\0'; cs++)
    *cs = TOLOWER (*cs);
    *cs = TOLOWER (*cs);
 
 
  macro = (macro_entry *) hash_find (macro_hash, copy);
  macro = (macro_entry *) hash_find (macro_hash, copy);
 
 
  if (macro == NULL)
  if (macro == NULL)
    return 0;
    return 0;
 
 
  /* Wrap the line up in an sb.  */
  /* Wrap the line up in an sb.  */
  sb_new (&line_sb);
  sb_new (&line_sb);
  while (*s != '\0' && *s != '\n' && *s != '\r')
  while (*s != '\0' && *s != '\n' && *s != '\r')
    sb_add_char (&line_sb, *s++);
    sb_add_char (&line_sb, *s++);
 
 
  sb_new (expand);
  sb_new (expand);
  *error = macro_expand (0, &line_sb, macro, expand);
  *error = macro_expand (0, &line_sb, macro, expand);
 
 
  sb_kill (&line_sb);
  sb_kill (&line_sb);
 
 
  /* Export the macro information if requested.  */
  /* Export the macro information if requested.  */
  if (info)
  if (info)
    *info = macro;
    *info = macro;
 
 
  return 1;
  return 1;
}
}
 
 
/* Free the memory allocated to a macro.  */
/* Free the memory allocated to a macro.  */
 
 
static void
static void
free_macro(macro_entry *macro)
free_macro(macro_entry *macro)
{
{
  formal_entry *formal;
  formal_entry *formal;
 
 
  for (formal = macro->formals; formal; )
  for (formal = macro->formals; formal; )
    {
    {
      formal_entry *f;
      formal_entry *f;
 
 
      f = formal;
      f = formal;
      formal = formal->next;
      formal = formal->next;
      del_formal (f);
      del_formal (f);
    }
    }
  hash_die (macro->formal_hash);
  hash_die (macro->formal_hash);
  sb_kill (&macro->sub);
  sb_kill (&macro->sub);
  free (macro);
  free (macro);
}
}
 
 
/* Delete a macro.  */
/* Delete a macro.  */
 
 
void
void
delete_macro (const char *name)
delete_macro (const char *name)
{
{
  char *copy;
  char *copy;
  size_t i, len;
  size_t i, len;
  macro_entry *macro;
  macro_entry *macro;
 
 
  len = strlen (name);
  len = strlen (name);
  copy = (char *) alloca (len + 1);
  copy = (char *) alloca (len + 1);
  for (i = 0; i < len; ++i)
  for (i = 0; i < len; ++i)
    copy[i] = TOLOWER (name[i]);
    copy[i] = TOLOWER (name[i]);
  copy[i] = '\0';
  copy[i] = '\0';
 
 
  /* Since hash_delete doesn't free memory, just clear out the entry.  */
  /* Since hash_delete doesn't free memory, just clear out the entry.  */
  if ((macro = hash_find (macro_hash, copy)) != NULL)
  if ((macro = hash_find (macro_hash, copy)) != NULL)
    {
    {
      hash_jam (macro_hash, copy, NULL);
      hash_jam (macro_hash, copy, NULL);
      free_macro (macro);
      free_macro (macro);
    }
    }
  else
  else
    as_warn (_("Attempt to purge non-existant macro `%s'"), copy);
    as_warn (_("Attempt to purge non-existant macro `%s'"), copy);
}
}
 
 
/* Handle the MRI IRP and IRPC pseudo-ops.  These are handled as a
/* Handle the MRI IRP and IRPC pseudo-ops.  These are handled as a
   combined macro definition and execution.  This returns NULL on
   combined macro definition and execution.  This returns NULL on
   success, or an error message otherwise.  */
   success, or an error message otherwise.  */
 
 
const char *
const char *
expand_irp (int irpc, int idx, sb *in, sb *out, int (*get_line) (sb *))
expand_irp (int irpc, int idx, sb *in, sb *out, int (*get_line) (sb *))
{
{
  sb sub;
  sb sub;
  formal_entry f;
  formal_entry f;
  struct hash_control *h;
  struct hash_control *h;
  const char *err;
  const char *err;
 
 
  idx = sb_skip_white (idx, in);
  idx = sb_skip_white (idx, in);
 
 
  sb_new (&sub);
  sb_new (&sub);
  if (! buffer_and_nest (NULL, "ENDR", &sub, get_line))
  if (! buffer_and_nest (NULL, "ENDR", &sub, get_line))
    return _("unexpected end of file in irp or irpc");
    return _("unexpected end of file in irp or irpc");
 
 
  sb_new (&f.name);
  sb_new (&f.name);
  sb_new (&f.def);
  sb_new (&f.def);
  sb_new (&f.actual);
  sb_new (&f.actual);
 
 
  idx = get_token (idx, in, &f.name);
  idx = get_token (idx, in, &f.name);
  if (f.name.len == 0)
  if (f.name.len == 0)
    return _("missing model parameter");
    return _("missing model parameter");
 
 
  h = hash_new ();
  h = hash_new ();
  err = hash_jam (h, sb_terminate (&f.name), &f);
  err = hash_jam (h, sb_terminate (&f.name), &f);
  if (err != NULL)
  if (err != NULL)
    return err;
    return err;
 
 
  f.index = 1;
  f.index = 1;
  f.next = NULL;
  f.next = NULL;
  f.type = FORMAL_OPTIONAL;
  f.type = FORMAL_OPTIONAL;
 
 
  sb_reset (out);
  sb_reset (out);
 
 
  idx = sb_skip_comma (idx, in);
  idx = sb_skip_comma (idx, in);
  if (idx >= in->len)
  if (idx >= in->len)
    {
    {
      /* Expand once with a null string.  */
      /* Expand once with a null string.  */
      err = macro_expand_body (&sub, out, &f, h, 0);
      err = macro_expand_body (&sub, out, &f, h, 0);
    }
    }
  else
  else
    {
    {
      bfd_boolean in_quotes = FALSE;
      bfd_boolean in_quotes = FALSE;
 
 
      if (irpc && in->ptr[idx] == '"')
      if (irpc && in->ptr[idx] == '"')
        {
        {
          in_quotes = TRUE;
          in_quotes = TRUE;
          ++idx;
          ++idx;
        }
        }
 
 
      while (idx < in->len)
      while (idx < in->len)
        {
        {
          if (!irpc)
          if (!irpc)
            idx = get_any_string (idx, in, &f.actual);
            idx = get_any_string (idx, in, &f.actual);
          else
          else
            {
            {
              if (in->ptr[idx] == '"')
              if (in->ptr[idx] == '"')
                {
                {
                  int nxt;
                  int nxt;
 
 
                  if (irpc)
                  if (irpc)
                    in_quotes = ! in_quotes;
                    in_quotes = ! in_quotes;
 
 
                  nxt = sb_skip_white (idx + 1, in);
                  nxt = sb_skip_white (idx + 1, in);
                  if (nxt >= in->len)
                  if (nxt >= in->len)
                    {
                    {
                      idx = nxt;
                      idx = nxt;
                      break;
                      break;
                    }
                    }
                }
                }
              sb_reset (&f.actual);
              sb_reset (&f.actual);
              sb_add_char (&f.actual, in->ptr[idx]);
              sb_add_char (&f.actual, in->ptr[idx]);
              ++idx;
              ++idx;
            }
            }
 
 
          err = macro_expand_body (&sub, out, &f, h, 0);
          err = macro_expand_body (&sub, out, &f, h, 0);
          if (err != NULL)
          if (err != NULL)
            break;
            break;
          if (!irpc)
          if (!irpc)
            idx = sb_skip_comma (idx, in);
            idx = sb_skip_comma (idx, in);
          else if (! in_quotes)
          else if (! in_quotes)
            idx = sb_skip_white (idx, in);
            idx = sb_skip_white (idx, in);
        }
        }
    }
    }
 
 
  hash_die (h);
  hash_die (h);
  sb_kill (&f.actual);
  sb_kill (&f.actual);
  sb_kill (&f.def);
  sb_kill (&f.def);
  sb_kill (&f.name);
  sb_kill (&f.name);
  sb_kill (&sub);
  sb_kill (&sub);
 
 
  return err;
  return err;
}
}
 
 

powered by: WebSVN 2.1.0

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