/* tc-hppa.c -- Assemble for the PA
   Copyright (C) 1989 Free Software Foundation, Inc.

This file is part of GAS, the GNU Assembler.

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

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

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


/*
   HP PA-RISC support was contributed by the Center for Software Science
   at the University of Utah.
 */

#include <stdio.h>
#include <ctype.h>

#include "as.h"
#include "subsegs.h"

/* #include "../bfd/libhppa.h" */
/*
 * Unwind table and descriptor.
 */

struct unwind_desc {
  unsigned int cannot_unwind         :  1;
  unsigned int millicode             :  1;
  unsigned int millicode_save_rest   :  1;
  unsigned int region_desc           :  2;
  unsigned int save_sr               :  2;
  unsigned int entry_fr              :  4;        /* number saved */
  unsigned int entry_gr              :  5;        /* number saved */
  unsigned int args_stored           :  1;
  unsigned int call_fr               :  5;
  unsigned int call_gr               :  5;
  unsigned int save_sp               :  1;
  unsigned int save_rp               :  1;
  unsigned int save_rp_in_frame      :  1;
  unsigned int extn_ptr_defined      :  1;
  unsigned int cleanup_defined       :  1;

  unsigned int hpe_interrupt_marker  :  1;
  unsigned int hpux_interrupt_marker :  1;
  unsigned int reserved              :  3;
  unsigned int frame_size            : 27;
};

typedef struct unwind_desc unwind_descS;

struct unwind_table {
  unsigned int start_offset;  /* starting offset (from SR4) of applicable region */
  unsigned int end_offset;    /* ending offset (from SR4) of applicable region */
  unwind_descS descriptor;
};

typedef struct unwind_table unwind_tableS;

/*
 * This structure is used by the .callinfo, .enter, .leave pseudo-ops to
 * control the entry and exit code they generate. It is also used in
 * creation of the correct stack unwind descriptors.
 *
 * The fields in structure roughly correspond to the arguments available on the
 * .callinfo pseudo-op.
 */

struct call_info {
  int frame;
  int entry_sr;
  int makes_calls;
  int hpux_int;
  unwind_tableS ci_unwind;	/* the unwind descriptor we are building */
  symbolS *start_symbol;	/* name of function (used in relocation info) */
  symbolS *end_symbol;		/* temporary symbol used to mark the */
				/* end of the function (used in */
				/* relocation info) */
  fragS *start_frag;		/* frag associated w/ start of this function */
  fragS *end_frag;		/* frag associated w/ end of this function */
  fragS *start_offset_frag;	/* frag for start offset of this descriptor */
  int   start_frag_where;	/* where in start_offset_frag is start_offset */
  fixS *start_fix;		/* fixup for the start_offset */
  fragS *end_offset_frag;	/* frag for start offset of this descriptor */
  int   end_frag_where;		/* where in end_offset_frag is end_offset */
  fixS *end_fix;		/* fixup for the end_offset */
  struct call_info *ci_next;	/* the next call_info structure */
};

typedef struct call_info call_infoS;

call_infoS *last_call_info;
call_infoS *call_info_root;
call_descS last_call_desc;

/* A structure used during assembly of individual instructions */

struct pa_it {
    char    *error;
    unsigned long opcode;
    /* symbol_dictS *nlistp; *//*** used to be:    struct nlist *nlistp; */
    asymbol * nlistp;
    expressionS exp;
    int pcrel;
    FP_Operand_Format fpof1;   /* Floating Point Operand Format, operand 1 */
    FP_Operand_Format fpof2;   /* Floating Point Operand Format, operand 2 */
                               /* (used only for class 1 instructions --   */
			       /* the conversion instructions)             */
#ifdef OBJ_SOM
    long field_selector;
    unsigned int reloc;
    int code;
    long arg_reloc;
    unwind_descS unwind;
#endif
#ifdef OBJ_ELF
    elf32_hppa_reloc_type reloc;
    long field_selector;
    int format;
    long arg_reloc;
    unwind_descS unwind;
#endif
};

extern struct pa_it the_insn;

/* careful, this file includes data *declarations* */
#include "opcode/hppa.h"

void md_begin();
void md_end();
void md_number_to_chars();
void md_assemble();
char *md_atof();
void md_convert_frag();
void md_create_short_jump();
void md_create_long_jump();
int  md_estimate_size_before_relax();
void md_number_to_imm();
void md_number_to_disp();
void md_number_to_field();
void md_ri_to_chars();
void emit_relocations();
static void pa_ip();


/* SKV 10/18/92.  Added the following to assist in the determination of the
   relocation to be applied to an instruction. */

#ifdef OBJ_ELF

#define ERROR_INDICATOR -1
#define ILLEGAL_RELOC ERROR_INDICATOR

/* The following tables are indexed by [reloc_table_index(operand_format)] [field_selector]. */

int HPPA_ADDR_RELOC_TABLE [N_HPPA_FIELD_SELECTORS] [N_HPPA_OPERAND_FORMATS] = {
/*      11               12            14              17             21        */
  { R_HPPA_11,     ILLEGAL_RELOC, R_HPPA_14,     R_HPPA_17,     ILLEGAL_RELOC}, /* F% */
  { ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, R_HPPA_L21},    /* L% */
  { R_HPPA_R11,    ILLEGAL_RELOC, R_HPPA_R14,    R_HPPA_R17,    ILLEGAL_RELOC}, /* R% */
  { ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, R_HPPA_LS21},   /* LS% */
  { R_HPPA_RS11,   ILLEGAL_RELOC, R_HPPA_RS14,   R_HPPA_RS17,   ILLEGAL_RELOC}, /* RS% */
  { ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, R_HPPA_LD21},   /* LD% */
  { R_HPPA_RD11,   ILLEGAL_RELOC, R_HPPA_RD14,   R_HPPA_RD17    ILLEGAL_RELOC}, /* RD% */
  { ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, R_HPPA_LR21},   /* LR% */
  { ILLEGAL_RELOC, ILLEGAL_RELOC, R_HPPA_RR14,   R_HPPA_RR17,   ILLEGAL_RELOC}, /* RR% */
  { ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC}, /* T% */
  { ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC}, /* LT% */
  { ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC}, /* RT% */
  { ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC}, /* P% */
  { ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC}, /* LP% */
  { ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC, ILLEGAL_RELOC}  /* RD */
};

int HPPA_GOTOFF_RELOC_TABLE [N_HPPA_FIELD_SELECTORS] [N_HPPA_OPERAND_FORMATS] = {
/*        11                   12                 14                 17                  21        */
  { R_HPPA_GOTOFF_11,   ILLEGAL_RELOC,      R_HPPA_GOTOFF_14,   ILLEGAL_RELOC,      ILLEGAL_RELOC},      /* F% */
  { ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      R_HPPA_GOTOFF_L21},  /* L% */
  { R_HPPA_GOTOFF_R11,  ILLEGAL_RELOC,      R_HPPA_GOTOFF_R14,  ILLEGAL_RELOC,      ILLEGAL_RELOC},      /* R% */
  { ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      R_HPPA_GOTOFF_LS21}, /* LS% */
  { R_HPPA_GOTOFF_RS11, ILLEGAL_RELOC,      R_HPPA_GOTOFF_RS14, ILLEGAL_RELOC,      ILLEGAL_RELOC},      /* RS% */
  { ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      R_HPPA_GOTOFF_LD21}, /* LD% */
  { R_HPPA_GOTOFF_RD11, ILLEGAL_RELOC,      R_HPPA_GOTOFF_RD14,  ILLEGAL_RELOC,      ILLEGAL_RELOC},      /* RD% */
  { ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      R_HPPA_GOTOFF_LR21}, /* LR% */
  { ILLEGAL_RELOC,       ILLEGAL_RELOC,     R_HPPA_GOTOFF_RR14, ILLEGAL_RELOC,      ILLEGAL_RELOC},      /* RR% */
  { ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC},      /* T% */
  { ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC},      /* LT% */
  { ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC},      /* RT% */
  { ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC},      /* P% */
  { ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC},      /* LP% */
  { ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC,      ILLEGAL_RELOC}       /* RP% */
};

int HPPA_ABS_CALL_RELOC_TABLE [N_HPPA_FIELD_SELECTORS] [N_HPPA_OPERAND_FORMATS] = {
/*         11                   12                      14                    17                  21        */
  { R_HPPA_ABS_CALL_11,   ILLEGAL_RELOC,        R_HPPA_ABS_CALL_14,   R_HPPA_ABS_CALL_17,   ILLEGAL_RELOC},        /* F% */
  { ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        R_HPPA_ABS_CALL_L21},  /* L% */
  { R_HPPA_ABS_CALL_R11,  ILLEGAL_RELOC,        R_HPPA_ABS_CALL_R14,  R_HPPA_ABS_CALL_R17,  ILLEGAL_RELOC},        /* R% */
  { ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        R_HPPA_ABS_CALL_LS21}, /* LS% */
  { R_HPPA_ABS_CALL_RS11, ILLEGAL_RELOC,        R_HPPA_ABS_CALL_RS14, R_HPPA_ABS_CALL_RS17, ILLEGAL_RELOC},        /* RS% */
  { ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        R_HPPA_ABS_CALL_LD21}, /* LD% */
  { R_HPPA_ABS_CALL_RD11, ILLEGAL_RELOC,        R_HPPA_ABS_CALL_RD14, R_HPPA_ABS_CALL_RD17, ILLEGAL_RELOC},        /* RD% */
  { ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        R_HPPA_ABS_CALL_LR21}, /* LR% */
  { ILLEGAL_RELOC,        ILLEGAL_RELOC,        R_HPPA_ABS_CALL_RR14, R_HPPA_ABS_CALL_RR17, ILLEGAL_RELOC},        /* RR% */
  { ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC},        /* T% */
  { ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC},        /* LT% */
  { ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC},        /* RT% */
  { ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC},        /* P% */
  { ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC},        /* LP% */
  { ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC,        ILLEGAL_RELOC}         /* RP% */
};

int HPPA_PCREL_CALL_RELOC_TABLE [N_HPPA_FIELD_SELECTORS] [N_HPPA_OPERAND_FORMATS] = {
/*          11                      12                      14                      17                      21  */
                                                                                                                   /* F% */
  { R_HPPA_PCREL_CALL_11,   ILLEGAL_RELOC,	    R_HPPA_PCREL_CALL_14,   R_HPPA_PCREL_CALL_17,   ILLEGAL_RELOC},
                                                                                                                   /* L% */
  { ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          R_HPPA_PCREL_CALL_L21},
                                                                                                                   /* R% */
  { R_HPPA_PCREL_CALL_R11,  ILLEGAL_RELOC,          R_HPPA_PCREL_CALL_R14,  R_HPPA_PCREL_CALL_R17,  ILLEGAL_RELOC},
                                                                                                                   /* LS% */
  { ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          R_HPPA_PCREL_CALL_LS21},
                                                                                                                   /* RS% */
  { R_HPPA_PCREL_CALL_RS11, ILLEGAL_RELOC,          R_HPPA_PCREL_CALL_RS14, R_HPPA_PCREL_CALL_RS17, ILLEGAL_RELOC},
                                                                                                                   /* LD% */
  { ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          R_HPPA_PCREL_CALL_LD21},
                                                                                                                   /* RD% */
  { R_HPPA_PCREL_CALL_RD11, ILLEGAL_RELOC,          R_HPPA_PCREL_CALL_RD14, R_HPPA_PCREL_CALL_RD17, ILLEGAL_RELOC},
                                                                                                                   /* LR% */
  { ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          R_HPPA_PCREL_CALL_LR21},
                                                                                                                   /* RR% */
  { ILLEGAL_RELOC,          ILLEGAL_RELOC,          R_HPPA_PCREL_CALL_RR14, R_HPPA_PCREL_CALL_RR17, ILLEGAL_RELOC},
                                                                                                                   /* T% */
  { ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC},
                                                                                                                   /* LT% */
  { ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC},
                                                                                                                   /* RT% */
  { ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC},
                                                                                                                   /* P% */
  { ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC},
                                                                                                                   /* LP% */
  { ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC},
                                                                                                                   /* RP% */
  { ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC,          ILLEGAL_RELOC}
};

int HPPA_PLABEL_RELOC_TABLE [N_HPPA_FIELD_SELECTORS] [N_HPPA_OPERAND_FORMATS] = {
/*         11                 12                 14                 17                 21  */
  { ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC},     /* F% */
  { ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC}, /* L% */
  { ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC},     /* R% */
  { ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC},     /* LS% */
  { ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC},     /* RS% */
  { ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC},     /* LD% */
  { ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC},     /* RD% */
  { ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC},     /* LR% */
  { ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC},     /* RR% */
  { ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC},     /* T% */
  { ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC},     /* LT% */
  { ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC},     /* RT% */
  { R_HPPA_PLABEL_11,  ILLEGAL_RELOC,     R_HPPA_PLABEL_14,  ILLEGAL_RELOC,  	ILLEGAL_RELOC},     /* P% */
  { ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     ILLEGAL_RELOC,     R_HPPA_PLABEL_L21}, /* LP% */
  { R_HPPA_PLABEL_R11, ILLEGAL_RELOC,     R_HPPA_PLABEL_R14, ILLEGAL_RELOC,	ILLEGAL_RELOC}      /* RP% */
};


static int reloc_table_idx();

#endif /* OBJ_ELF */

const relax_typeS md_relax_table[] = { 0 };
 /* handle of the OPCODE hash table */
static struct hash_control *op_hash = NULL;

int md_short_jump_size = 4;
int md_long_jump_size = 4;

/* This array holds the chars that always start a comment.  If the
    pre-processor is disabled, these aren't very useful */
const char comment_chars[] = ";";	/* JF removed '|' from comment_chars */

const pseudo_typeS
md_pseudo_table[] = {
  { "align",	s_align_bytes,	0 }, /* .align 4 means .align to 4-byte boundary */
  { "ALIGN",	s_align_bytes,	0 }, /* .align 4 means .align to 4-byte boundary */
  { "block",	pa_block,	1 },
  { "BLOCK",	pa_block,	1 },
  { "blockz",	pa_block,	0 },
  { "BLOCKZ",	pa_block,	0 },
  { "call",	pa_call,	0 },
  { "CALL",	pa_call,	0 },
  { "callinfo",	pa_callinfo,	0 },
  { "CALLINFO",	pa_callinfo,	0 },
  { "code",	pa_code,	0 },
  { "CODE",	pa_code,	0 },
  { "comm",	pa_comm,	0 },
  { "COMM",	pa_comm,	0 },
  { "copyright",pa_copyright,	0 },
  { "COPYRIGHT",pa_copyright,	0 },
  { "end",	pa_end,		0 },
  { "END",	pa_end,		0 },
  { "enter",	pa_enter,	0 },
  { "ENTER",	pa_enter,	0 },
  { "entry",	pa_entry,	0 },
  { "ENTRY",	pa_entry,	0 },
  { "equ",	pa_equ,		0 },
  { "EQU",	pa_equ,		0 },
  { "exit",	pa_exit,	0 },
  { "EXIT",	pa_exit,	0 },
  { "export",	pa_export,	0 },
  { "EXPORT",	pa_export,	0 },
  { "half",	cons,		2 },
  { "HALF",	cons,		2 },
  { "import",	pa_import,	0 },
  { "IMPORT",	pa_import,	0 },
  { "label",	pa_label,	0 },
  { "LABEL",	pa_label,	0 },
  { "leave",	pa_leave,	0 },
  { "LEAVE",	pa_leave,	0 },
  { "origin",	pa_origin,	0 },
  { "ORIGIN",	pa_origin,	0 },
  { "proc",	pa_proc,	0 },
  { "PROC",	pa_proc,	0 },
  { "procend",	pa_procend,	0 },
  { "PROCEND",	pa_procend,	0 },
  { "reg",	pa_equ,		1 },      /* very similar to .equ */
  { "REG",	pa_equ,		1 },      /* very similar to .equ */
  { "space",	pa_space,	0 },
  { "SPACE",	pa_space,	0 },
  { "spnum",	pa_spnum,	0 },
  { "SPNUM",	pa_spnum,	0 },
  { "string",	stringer,	0 },
  { "STRING",	stringer,	0 },
  { "stringz",	stringer,	1 },
  { "STRINGZ",	stringer,	1 },
  { "subspa",	pa_subspace,	0 },
  { "SUBSPA",	pa_subspace,	0 },
  { "word",	cons,		4 },
  { "WORD",	cons,		4 },
  { NULL,	0,		0 }
};

/* This array holds the chars that only start a comment at the beginning of
   a line.  If the line seems to have the form '# 123 filename'
   .line and .file directives will appear in the pre-processed output */
/* Note that input_file.c hand checks for '#' at the beginning of the
   first line of the input file.  This is because the compiler outputs
   #NO_APP at the beginning of its output. */
/* Also note that '/*' will always start a comment */
const char line_comment_chars[] = "#";

const char line_separator_chars[] = "!";

/* Chars that can be used to separate mant from exp in floating point nums */
const char EXP_CHARS[] = "eE";

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

/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
   changed in read.c .  Ideally it shouldn't have to know about it at all,
   but nothing is ideal around here.
 */

static unsigned char octal[256];
#ifndef isoctal
#define isoctal(c)  octal[c]
#endif
static unsigned char toHex[256];

struct pa_it set_insn;   /* this structure is defined above */

/* SKV 12/22/92.  Added prev_insn, prev_fix, and initialized the_insn
   so that we can recognize instruction sequences such as (ldil, ble)
   and generate the appropriate fixups. */

struct pa_it the_insn = {
    NULL,         /* error */
    0,            /* opcode */
    NULL,         /* nlistp */
    {
      NULL,       /* exp.X_add_symbol */
      NULL,       /* exp.X_subtract_symbol */
      0,          /* exp.X_add_number */
      NULL        /* exp.asection */
    },
    0,            /* pcrel */
    0,            /* fpof1 */
    0,            /* fpof2 */
    0,            /* reloc */
    0,            /* field_selector */
    0,            /* code */
    0,            /* arg_reloc */
    {             /* unwind */
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    }
  };

#ifdef OBJ_ELF

struct pa_it prev_insn;
char prev_str[10] = "";
fixS *prev_fix = NULL;
fixS *curr_fix = NULL;

#endif /* OBJ_ELF */

#ifdef __STDC__
void print_insn(struct pa_it *insn);
#else
void print_insn();
#endif
char *expr_end;

symbolS *label_symbolP;       /* the last label symbol encountered */
                              /* saved here in case a .equ is encountered */
int label_symbol_defined;

int callinfo_found;           /* T if a .callinfo appeared within the current */
                              /*  procedure definition and F otherwise        */

int within_entry_exit;        /* T if the assembler is currently within a     */
                              /* .entry/.exit pair and F otherwise            */

int exit_processing_complete; /* T is the assembler has completed exit */
                              /* processing for the current procedure  */
                              /* and F otherwise                       */

int within_procedure;         /* T if the assembler is currently within a  */
                              /* a procedure definition and F otherwise    */

void ignore_rest_of_line();   /* a useful function in read.c */

/* default space and subspace dictionaries */

#define GDB_SYMBOLS          GDB_SYMBOLS_SUBSPACE_NAME
#define GDB_STRINGS          GDB_STRINGS_SUBSPACE_NAME

#if defined(OBJ_ELF)
struct default_subspace_dict pa_def_subspaces[] =
{
  { "$CODE$",  0, 1, 0, 0, 0, 0, 24, 0x2c, 0, 8, 0, 0, SUBSEG_CODE   },
  { "$DATA$",  0, 1, 0, 0, 0, 0, 24, 0x1f, 0, 8, 1, 1, SUBSEG_DATA   },
  { "$LIT$",   0, 1, 0, 0, 0, 0, 16, 0x2c, 0, 8, 0, 0, SUBSEG_LIT    },
  { "$BSS$",   0, 1, 0, 0, 0, 1, 80, 0x1f, 0, 8, 1, 1, SUBSEG_BSS    },
  { "$UNWIND$",0, 1, 0, 0, 0, 0, 64, 0x2c, 0, 4, 0, 0, SUBSEG_UNWIND },
  { GDB_STRINGS,0,0, 0, 0, 0, 0,254, 0x1f, 0, 4, 0, 2, SUBSEG_GDB_STRINGS },
  { GDB_SYMBOLS,0,0, 0, 0, 0, 0,255, 0x1f, 0, 4, 0, 2, SUBSEG_GDB_SYMBOLS },
  { NULL,      0, 1, 0, 0, 0, 0,255, 0x1f, 0, 4, 0, 0, 0 }
};

struct default_space_dict pa_def_spaces[] =
{
  { "$TEXT$",                   0, 1, 0, 0,   8, ASEC_NULL,	".text" },
  { "$PRIVATE$",                0, 1, 0, 0,  16, ASEC_NULL,	".data"	},
  { GDB_DEBUG_SPACE_NAME,       0, 0, 0, 0, 255, ASEC_NULL,	NULL /* ".debug" */ },
  { NULL,                       0, 0, 0, 0,   0, ASEC_NULL,	NULL	}
};
#else
struct default_subspace_dict pa_def_subspaces[] =
{
  { "$CODE$",  0, 1, 0, 0, 0, 0, 24, 0x2c, 0, 8, 0, SEG_TEXT, SUBSEG_CODE   },
  { "$DATA$",  0, 1, 0, 0, 0, 0, 24, 0x1f, 0, 8, 1, SEG_DATA, SUBSEG_DATA   },
  { "$LIT$",   0, 1, 0, 0, 0, 0, 16, 0x2c, 0, 8, 0, SEG_TEXT, SUBSEG_LIT    },
  { "$BSS$",   0, 1, 0, 0, 0, 1, 80, 0x1f, 0, 8, 1, SEG_DATA, SUBSEG_BSS    },
  { "$UNWIND$",0, 1, 0, 0, 0, 0, 64, 0x2c, 0, 4, 0, SEG_TEXT, SUBSEG_UNWIND },
  { GDB_STRINGS,0,0, 0, 0, 0, 0,254, 0x1f, 0, 4, 0, SEG_GDB,  SUBSEG_GDB_STRINGS
 },
  { GDB_SYMBOLS,0,0, 0, 0, 0, 0,255, 0x1f, 0, 4, 0, SEG_GDB,  SUBSEG_GDB_SYMBOLS
 },
  { NULL,0,1,0,0,0,0,255,0x1f,0,4,0,SEG_GOOF,0 }
};

struct default_space_dict pa_def_spaces[] =
{
  { "$TEXT$",                   0, 1, 0, 0,   8, SEG_TEXT},
  { "$PRIVATE$",                0, 1, 0, 0,  16, SEG_DATA},
  { GDB_DEBUG_SPACE_NAME,       0, 0, 0, 0, 255, SEG_GDB},
  { NULL,                       0, 0, 0, 0,   0, SEG_GOOF}
};
#endif

#ifdef USG
#define bcmp(b1,b2,n) memcmp((b1),(b2),(n))
#define index strchr
#endif

#ifndef FALSE
#define FALSE   (0)
#define TRUE    (!FALSE)
#endif /* no FALSE yet */

/*
	Support for keeping track of the most recent label in each
	space.
 */

/*
	PA_PSEUDO_OP_MOVES_PC

	A predicate that returns true if the pseudo-operation or
	assembly directive results in a movement in the current
	location.  All instructions cause movement in the current
	location.
 */

static const char * movers[] = {
  	/* these entries from 'static pseudo_typeS potable[]' in pa-read.c */
	"ascii",	"asciz",
	"byte",
	"comm",
	"data",		"desc",		"double",
	"fill",		"float",
	"globl",
	"half",
	"int",
	"lcomm",	"long",		"lsym",
	"octa",		"org",
	"quad",
	"short",	"single",
	"text",
	"word",
  /* these entries from 'pseudo_typeS md_pseudo_table[]' in pa-aux.c */
	"block",	"blockz",
	"code",		"copyright",
	"equ",
	"origin",
	"reg",      /* very similar to .equ */
	"string",	"stringz",
	"version",
	NULL	/* end sentinel */
};

int
pa_pseudo_op_moves_pc(name)
char * name;
{
	int i = 0;
	while ( movers[i] ) {
		if ( strcmp(name,movers[i++]) == 0 )
			return 1;
	}

	return 0;
}

/*
	Support for keeping track of the most recent label in each
	space.
 */

/* XXX:  NOTE:  label_symbolS is defined in pa.h */

label_symbolS *label_symbols_rootP = NULL;

/*
	PA_GET_LABEL

	Returns a pointer to the label_symbolS for the current space.
 */

label_symbolS *
pa_get_label()
{
	label_symbolS *lssP;
	space_dict_chainS *now_sdcP = pa_segment_to_space(now_seg);

	for ( lssP = label_symbols_rootP; lssP; lssP = lssP->lss_next ) {
		if ( now_sdcP == lssP->lss_space && lssP->lss_label )
			return lssP;
	}

	return (label_symbolS *)NULL;
}

/*
	PA_LABEL_IS_DEFINED

	A predicate to determine whether a useable label is defined in
	the current space.
 */

int
pa_label_is_defined()
{
	return (int)pa_get_label();
}

/*
	PA_DEFINE_LABEL

	Defines a label for the current space.  If one is already defined,
	this function will replace it with the new label.
 */

void
pa_define_label(symbolP)
symbolS * symbolP;
{
	label_symbolS *lssP		= pa_get_label();
	space_dict_chainS *now_sdcP	= pa_segment_to_space(now_seg);

	if ( lssP ) {
		lssP->lss_label = symbolP;
	}
	else {
		lssP = (label_symbolS *)xmalloc(sizeof(label_symbolS));
		lssP->lss_label	= symbolP;
		lssP->lss_space	= now_sdcP;
		lssP->lss_next	= (label_symbolS *)NULL;

		if ( label_symbols_rootP ) {
			lssP->lss_next	= label_symbols_rootP;
		}
		label_symbols_rootP	= lssP;
	}
}

/*
	PA_UNDEFINE_LABEL

	Removes a label definition for the current space.
	If there is no label_symbolS entry, then no action is taken.
 */

void
pa_undefine_label()
{
	label_symbolS *lssP;
	label_symbolS *prevP = (label_symbolS *)NULL;
	space_dict_chainS *now_sdcP = pa_segment_to_space(now_seg);

	for ( lssP = label_symbols_rootP; lssP; lssP = lssP->lss_next ) {
		if ( now_sdcP == lssP->lss_space && lssP->lss_label ) {
			if ( prevP )
				prevP->lss_next = lssP->lss_next;
			else
				label_symbols_rootP = lssP->lss_next;

			free(lssP);
			break;
		}
		prevP = lssP;
	}
}

/* end of label symbol support. */


/* An HPPA-specific version of fix_new.  This is required because the HPPA	*/
/* code needs to keep track of some extra stuff.  Each call to fix_new_hppa	*/
/* results in the creation of an instance of an hppa_fixS.  An hppa_fixS	*/
/* stores the extra information along with a pointer to the original fixS.	*/

typedef struct hppa_fix_struct {
	fixS   *fx_fixP;
	int	fx_r_field;
	int	fx_r_type;
	int	fx_r_format;
	long	fx_arg_reloc;
	call_infoS *fx_call_infop;
	char	fx_unwind[8];
	struct hppa_fix_struct *fx_next;
} hppa_fixS;

hppa_fixS * hppa_fix_root = NULL;

void fix_new_hppa(frag, where, size, add_symbol, sub_symbol, offset, pcrel,
		  r_type, r_field, r_format, arg_reloc, unwind_desc)
     fragS	*frag;		/* Which frag? */
     int	where;		/* Where in that frag? */
     short int	size;		/* 1, 2  or 4 usually. */
     symbolS	*add_symbol;	/* X_add_symbol. */
     symbolS	*sub_symbol;	/* X_subtract_symbol. */
     long	offset;		/* X_add_number. */
     int	pcrel;		/* TRUE if PC-relative relocation. */
     enum reloc_type r_type;	/* Relocation type */
     long       r_field;        /* F, R, L, etc */
     int        r_format;	/* 11,12,14,17,21,32, etc */
     long       arg_reloc;
     char       *unwind_desc;
{
	fixS *new_fix = fix_new(frag, where, size,
				add_symbol, sub_symbol,
				offset, pcrel, r_type);

	hppa_fixS *hppa_fix = (hppa_fixS *)obstack_alloc(&notes,sizeof(hppa_fixS));

	hppa_fix->fx_fixP	= new_fix;
	hppa_fix->fx_r_field	= r_field;
	hppa_fix->fx_r_format	= r_format;
	hppa_fix->fx_arg_reloc	= arg_reloc;
	hppa_fix->fx_next	= (hppa_fixS *)0;
	hppa_fix->fx_call_infop	= last_call_info;
	if (unwind_desc)
		bcopy(unwind_desc, hppa_fix->fx_unwind, 8);

	if ( hppa_fix_root )
		hppa_fix->fx_next = hppa_fix_root;

	hppa_fix_root = hppa_fix;

/* SKV 12/22/92.  Added prev_insn, prev_fix, and initialized the_insn
   so that we can recognize instruction sequences such as (ldil, ble)
   and generate the appropriate fixups. */

#ifdef OBJ_ELF

	curr_fix = new_fix;

#endif /* OBJ_ELF */
}

/* Given a FixS, find the hppa_fixS associated with it. */
hppa_fixS *
	hppa_find_hppa_fix(fix)
fixS *fix;
{
	hppa_fixS *hfP;

	for ( hfP = hppa_fix_root; hfP; hfP = hfP->fx_next ) {
		if ( hfP->fx_fixP == fix )
			return hfP;
	}

	return (hppa_fixS *)0;
}

/* This function is called once, at assembler startup time.  It should
   set up all the tables, etc. that the MD part of the assembler will need.  */
void
md_begin()
{
  register char *retval = NULL;
  int lose = 0;
  register unsigned int i = 0;

  last_call_info = NULL;
  call_info_root = NULL;

  pa_spaces_begin();

  op_hash = hash_new();
  if (op_hash == NULL)
    as_fatal("Virtual memory exhausted");

  while (i < NUMOPCODES)
    {
      const char *name = pa_opcodes[i].name;
      retval = hash_insert(op_hash, name, &pa_opcodes[i]);
      if(retval != NULL && *retval != '\0')
	{
	  as_fatal("Internal error: can't hash `%s': %s\n",
		   pa_opcodes[i].name, retval);
	  lose = 1;
	}
      do
	{
	  if ( (pa_opcodes[i].match & pa_opcodes[i].mask) != pa_opcodes[i].match )
	    {
	      fprintf(stderr, "internal error: losing opcode: `%s' \"%s\"\n",
		       pa_opcodes[i].name, pa_opcodes[i].args);
	      lose = 1;
	    }
	  ++i;
	} while (i < NUMOPCODES
		 && !strcmp(pa_opcodes[i].name, name));
    }

  if (lose)
    as_fatal ("Broken assembler.  No assembly attempted.");

  for (i = '0'; i < '8'; ++i)
    octal[i] = 1;
  for (i = '0'; i <= '9'; ++i)
    toHex[i] = i - '0';
  for (i = 'a'; i <= 'f'; ++i)
    toHex[i] = i + 10 - 'a';
  for (i = 'A'; i <= 'F'; ++i)
    toHex[i] = i + 10 - 'A';

}

void
md_end()
{
    return;
}

void
md_assemble(str)
    char *str;
{
    char *toP;

    assert(str);
    pa_ip(str);
    toP = frag_more(4);
    /* put out the opcode */
    md_number_to_chars(toP, the_insn.opcode, 4);

    /* put out the symbol-dependent stuff */
#if defined ( OBJ_SOM )
    if (the_insn.reloc != R_NO_RELOCATION) {
#elif defined ( OBJ_ELF )
    if (the_insn.reloc != R_HPPA_NONE) {	    
#endif

#if defined(OBJ_ELF)
	fix_new_hppa(frag_now,                           /* which frag */
		     (toP - frag_now->fr_literal), /* where */
		     4,                                  /* size */
		     the_insn.exp.X_add_symbol,
		     the_insn.exp.X_subtract_symbol,
		     the_insn.exp.X_add_number,
		     the_insn.pcrel,
		     the_insn.reloc,
		     the_insn.field_selector,
		     the_insn.format,
		     the_insn.arg_reloc,
		     (char *) 0 );
#endif
#ifdef OBJ_SOM
	fix_new(frag_now,                           /* which frag */
		(toP - frag_now->fr_literal), /* where */
		4,                                  /* size */
		the_insn.exp.X_add_symbol,
		the_insn.exp.X_subtract_symbol,
		the_insn.exp.X_add_number,
		the_insn.pcrel,
		the_insn.reloc,
		the_insn.field_selector,
		the_insn.code,
		the_insn.arg_reloc,
		(char *) 0 );
#endif
    }

/* SKV 12/22/92.  Added prev_insn, prev_fix, and initialized the_insn
   so that we can recognize instruction sequences such as (ldil, ble)
   and generate the appropriate fixups. */

#ifdef OBJ_ELF

    prev_insn = the_insn;
    strncpy(prev_str, str, 10);
    if (prev_insn.reloc = R_HPPA_NONE)
      {
         prev_fix = NULL;
      }
    else
      {
         prev_fix = curr_fix;
      }

#endif /* OBJ_ELF */
}

static void
pa_ip(str)
    char *str;
{
	char *error_message = "";
	char *s;
	const char *args;
	char c;
	unsigned long i;
	struct pa_opcode *insn;
	char *argsStart;
	unsigned long   opcode;
	unsigned int mask;
	int match = FALSE;
	int comma = 0;

	int reg,reg1,reg2,s2,s3;
	unsigned int im21,im14,im11,im5;
	int m,a,uu,f;
	int cmpltr,nullif, flag;
	int sfu, cond;
	char *name;
	char *p, *save_s;

#ifdef PA_DEBUG
    fprintf(stderr,"STATEMENT: \"%s\"\n",str);
#endif
    for (s = str; isupper(*s) || islower(*s) || (*s >= '0' && *s <= '3'); ++s)
	;
    switch (*s) {

    case '\0':
	break;

    case ',':
	comma = 1;

	/*FALLTHROUGH*/

    case ' ':
	*s++ = '\0';
	break;

    default:
	    as_bad("Unknown opcode: `%s'", str);
	    exit(1);
    }

    save_s = str;

    while ( *save_s ) {
      if ( isupper(*save_s) )
	*save_s = tolower(*save_s);
      save_s++;
    }

    if ((insn = (struct pa_opcode *) hash_find(op_hash, str)) == NULL) {
	as_bad("Unknown opcode: `%s'", str);
	return;
    }
    if (comma) {
	*--s = ',';
    }
    argsStart = s;
    for (;;) {
	opcode = insn->match;
	bzero(&the_insn, sizeof(the_insn));
#if defined( OBJ_SOM )
	the_insn.reloc = R_NO_RELOCATION;
#else
#if defined ( OBJ_ELF )
	the_insn.reloc = R_HPPA_NONE;
#endif
#endif
	/*
	 * Build the opcode, checking as we go to make
	 * sure that the operands match
	 */
	for (args = insn->args; ; ++args) {

	    switch (*args) {

	    case '\0':  /* end of args */
	      if (*s == '\0') {
		match = TRUE;
	      }
	      break;

	    case '+':
	      if (*s == '+') {
		++s;
		continue;
	      }
	      if (*s == '-') {
		continue;
	      }
	      break;

	    case '(':   /* these must match exactly */
	    case ')':
	    case ',':
	    case ' ':
	      if (*s++ == *args)
		continue;
	      break;

	    case 'b':   /* 5 bit register field at 10 */
	    case '^':   /* 5 bit control register field at 10 */
	      reg = pa_parse_number(&s);
	      if ( reg < 32 && reg >= 0 ) {
		opcode |= reg << 21;
		continue;
	      }
	      break;
	    case 'x':   /* 5 bit register field at 15 */
	      reg = pa_parse_number(&s);
	      if ( reg < 32 && reg >= 0 ) {
		opcode |= reg << 16;
		continue;
	      }
	      break;
	    case 't':   /* 5 bit register field at 31 */
	      reg = pa_parse_number(&s);
	      if ( reg < 32 && reg >= 0 ) {
		opcode |= reg;
		continue;
	      }
	      break;
	    case 'T':   /* 5 bit field length at 31 (encoded as 32-T) */
	      /*
		reg = pa_parse_number(&s);
	       */
	      getAbsoluteExpression(s);
	      if ( the_insn.exp.X_seg == &bfd_abs_section ) {
		reg = the_insn.exp.X_add_number;
		if ( reg <= 32 && reg > 0 ) {
		  opcode |= 32 - reg;
		  s = expr_end;
		  continue;
		}
	      }
	      break;
	    case '5':   /* 5 bit immediate at 15 */
	      getAbsoluteExpression(s);
	      /** PJH: The following 2 calls to as_bad() might eventually **/
	      /**      want to end up as as_warn().  **/
	      if (   the_insn.exp.X_add_number > 15 ) {
		as_bad("5 bit immediate > 15. Set to 15",
		       the_insn.exp.X_add_number);
		the_insn.exp.X_add_number = 15;
	      }
	      else if ( the_insn.exp.X_add_number < -16 ) {
		as_bad("5 bit immediate < -16. Set to -16",
		       the_insn.exp.X_add_number);
		the_insn.exp.X_add_number = -16;
	      }

	      low_sign_unext(evaluateAbsolute(the_insn.exp,the_insn.field_selector),
			     5,&im5);
	      opcode |= ( im5 << 16 );
	      s = expr_end;
	      continue;

	    case 's':   /* 2 bit space identifier at 17 */
	      s2 = pa_parse_number(&s);
	      if ( s2 < 4 && s2 >= 0 ) {
		opcode |= s2 << 14;
		continue;
	      }
	      break;
	    case 'S':   /* 3 bit space identifier at 18 */
	      s3 = pa_parse_number(&s);
	      if ( s3 < 8 && s3 >= 0 ) {
		dis_assemble_3(s3,&s3);
		opcode |= s3 << 13;
		continue;
	      }
	      break;
	    case 'c':   /* indexed load completer. */
	      uu = 0;
	      m = 0;
	      i = 0;
	      while ( *s == ',' && i < 2 ) {
		s++;
		if ( strncasecmp(s,"sm",2) == 0 ) {
		  uu = 1;
		  m = 1;
		  s++;
		  i++;
		}
		else if ( strncasecmp(s,"m",1) == 0 )
		  m = 1;
		else if ( strncasecmp(s,"s",1) == 0 )
		  uu = 1;
		else
		  as_bad("Unrecognized Indexed Load Completer...assuming 0");
		s++;
		i++;
	      }
	      if ( i > 2 )
		as_bad("Illegal Indexed Load Completer Syntax...extras ignored");
	      /* pa_skip(&s); */
	      while ( *s == ' ' || *s == '\t' )
		s++;
	      
	      opcode |= m << 5;
	      opcode |= uu << 13;
	      continue;
	    case 'C':   /* short load and store completer */
	      a = 0;
	      m = 0;
	      if ( *s == ',' ) {
		s++;
		if ( strncasecmp(s,"ma",2) == 0 ) {
		  a = 0;
		  m = 1;
		}
		else if ( strncasecmp(s,"mb",2) == 0 ) {
		  a = 1;
		  m = 1;
		}
		else
		  as_bad("Unrecognized Indexed Load Completer...assuming 0");
		s += 2;
	      }
	      /* pa_skip(&s); */
	      while ( *s == ' ' || *s == '\t' )
		s++;
	      opcode |= m << 5;
	      opcode |= a << 13;
	      continue;
	    case 'Y':   /* Store Bytes Short completer */
	      a = 0;
	      m = 0;
	      i = 0;
	      while ( *s == ',' && i < 2 ) {
		s++;
		if ( strncasecmp(s,"m",1) == 0 )
		  m = 1;
		else if ( strncasecmp(s,"b",1) == 0 )
		  a = 0;
		else if ( strncasecmp(s,"e",1) == 0 )
		  a = 1;
		else
		  as_bad("Unrecognized Store Bytes Short Completer...assuming 0");
		s++;
		i++;
	      }
	      /**		if ( i >= 2 ) **/
	      if ( i > 2 )
		as_bad("Illegal Store Bytes Short Completer...extras ignored");
	      while ( *s == ' ' || *s == '\t' ) /* skip to next operand */
		s++;
	      opcode |= m << 5;
	      opcode |= a << 13;
	      continue;
	    case '<':   /* non-negated compare/subtract conditions. */
	      cmpltr = pa_parse_nonneg_cmpsub_cmpltr(&s);
	      if ( cmpltr < 0 ) {
		as_bad("Unrecognized Compare/Subtract Condition: %c",*s);
		cmpltr = 0;
	      }
	      opcode |= cmpltr << 13;
	      continue;
	    case '?':   /* negated or non-negated cmp/sub conditions. */
	      /* used only by ``comb'' and ``comib'' pseudo-ops */
	      save_s = s;
	      cmpltr = pa_parse_nonneg_cmpsub_cmpltr(&s);
	      if ( cmpltr < 0 ) {
		s = save_s;
		cmpltr = pa_parse_neg_cmpsub_cmpltr(&s);
		if ( cmpltr < 0 ) {
		  as_bad("Unrecognized Compare/Subtract Condition: %c",*s);
		  cmpltr = 0;
		}
		else {
		  opcode |= 1 << 27; /* required opcode change to make
		                        COMIBT into a COMIBF or a
		                        COMBT into a COMBF or a
			                ADDBT into a ADDBF or a
			                ADDIBT into a ADDIBF */
		}
	      }
	      opcode |= cmpltr << 13;
	      continue;
	    case '!':   /* negated or non-negated add conditions. */
	      /* used only by ``addb'' and ``addib'' pseudo-ops */
	      save_s = s;
	      cmpltr = pa_parse_nonneg_add_cmpltr(&s);
	      if ( cmpltr < 0 ) {
		s = save_s;
		cmpltr = pa_parse_neg_add_cmpltr(&s);
		if ( cmpltr < 0 ) {
		  as_bad("Unrecognized Compare/Subtract Condition: %c",*s);
		  cmpltr = 0;
		}
		else {
		  opcode |= 1 << 27; /* required opcode change to make
		    COMIBT into a COMIBF or a
		      COMBT into a COMBF or a
			ADDBT into a ADDBF or a
			  ADDIBT into a ADDIBF */
		}
	      }
	      opcode |= cmpltr << 13;
	      continue;
	    case 'a':   /* compare/subtract conditions */
	      cmpltr = 0;
	      f = 0;
	      save_s = s;
	      if ( *s == ',' ) {
		cmpltr = pa_parse_nonneg_cmpsub_cmpltr(&s);
		if ( cmpltr < 0 ) {
		  f = 1;
		  s = save_s;
		  cmpltr = pa_parse_neg_cmpsub_cmpltr(&s);
		  if ( cmpltr < 0 ) {
		    as_bad("Unrecognized Compare/Subtract Condition");
		  }
		}
	      }
	      opcode |= cmpltr << 13;
	      opcode |= f << 12;
	      continue;
	    case 'd':   /* non-negated add conditions */
	      cmpltr = 0;
	      nullif = 0;
	      flag   = 0;
	      if ( *s == ',' ) {
		s++;
		name = s;
		while ( *s != ',' && *s != ' ' && *s != '\t' )
		  s += 1;
		c = *s;
		*s = 0x00;
		if ( strcmp(name,"=") == 0 ) {
		  cmpltr = 1;
		}
		else if ( strcmp(name,"<") == 0 ) {
		  cmpltr = 2;
		}
		else if ( strcmp(name,"<=") == 0 ) {
		  cmpltr = 3;
		}
		else if ( strcasecmp(name,"nuv") == 0 ) {
		  cmpltr = 4;
		}
		else if ( strcasecmp(name,"znv") == 0 ) {
		  cmpltr = 5;
		}
		else if ( strcasecmp(name,"sv") == 0 ) {
		  cmpltr = 6;
		}
		else if ( strcasecmp(name,"od") == 0 ) {
		  cmpltr = 7;
		}
		else if ( strcasecmp(name,"n") == 0 ) {
		  nullif = 1;
		}
		else if ( strcasecmp(name,"tr") == 0 ) {
		  cmpltr = 0;
		  flag   = 1;
		}
		else if ( strcasecmp(name,"<>") == 0 ) {
		  cmpltr = 1;
		  flag   = 1;
		}
		else if ( strcasecmp(name,">=") == 0 ) {
		  cmpltr = 2;
		  flag   = 1;
		}
		else if ( strcasecmp(name,">") == 0 ) {
		  cmpltr = 3;
		  flag   = 1;
		}
		else if ( strcasecmp(name,"uv") == 0 ) {
		  cmpltr = 4;
		  flag   = 1;
		}
		else if ( strcasecmp(name,"vnz") == 0 ) {
		  cmpltr = 5;
		  flag   = 1;
		}
		else if ( strcasecmp(name,"nsv") == 0 ) {
		  cmpltr = 6;
		  flag   = 1;
		}
		else if ( strcasecmp(name,"ev") == 0 ) {
		  cmpltr = 7;
		  flag   = 1;
		}
		else
		  as_bad("Unrecognized Add Condition: %s",name);
		*s = c;
	      }
	      nullif = pa_parse_nullif(&s);
	      opcode |= nullif << 1;
	      opcode |= cmpltr << 13;
	      opcode |= flag << 12;
	      continue;		
	    case '&':   /* logical instruction conditions */
	      cmpltr = 0;
	      f = 0;
	      if ( *s == ',' ) {
		s++;
		name = s;
		while ( *s != ',' && *s != ' ' && *s != '\t' )
		  s += 1;
		c = *s;
		*s = 0x00;
		if ( strcmp(name,"=") == 0 ) {
		  cmpltr = 1;
		}
		else if ( strcmp(name,"<") == 0 ) {
		  cmpltr = 2;
		}
		else if ( strcmp(name,"<=") == 0 ) {
		  cmpltr = 3;
		}
		else if ( strcasecmp(name,"od") == 0 ) {
		  cmpltr = 7;
		}
		else if ( strcasecmp(name,"tr") == 0 ) {
		  cmpltr = 0;
		  f = 1;
		}
		else if ( strcmp(name,"<>") == 0 ) {
		  cmpltr = 1;
		  f = 1;
		}
		else if ( strcmp(name,">=") == 0 ) {
		  cmpltr = 2;
		  f = 1;
		}
		else if ( strcmp(name,">") == 0 ) {
		  cmpltr = 3;
		  f = 1;
		}
		else if ( strcasecmp(name,"ev") == 0 ) {
		  cmpltr = 7;
		  f = 1;
		}
		else
		  as_bad("Unrecognized Logical Instruction Condition: %s",name);
		*s = c;
	      }
	      opcode |= cmpltr << 13;
	      opcode |= f << 12;		
	      continue;
	    case 'U':   /* unit instruction conditions */
	      cmpltr = 0;
	      f = 0;
	      if ( *s == ',' ) {
		s++;
		if ( strncasecmp(s,"sbz",3) == 0 ) {
		  cmpltr = 2;
		  s += 3;
		}
		else if ( strncasecmp(s,"shz",3) == 0 ) {
		  cmpltr = 3;
		  s += 3;
		}
		else if ( strncasecmp(s,"sdc",3) == 0 ) {
		  cmpltr = 4;
		  s += 3;
		}
		else if ( strncasecmp(s,"sbc",3) == 0 ) {
		  cmpltr = 6;
		  s += 3;
		}
		else if ( strncasecmp(s,"shc",3) == 0 ) {
		  cmpltr = 7;
		  s += 3;
		}
		else if ( strncasecmp(s,"tr",2) == 0 ) {
		  cmpltr = 0;
		  f = 1;
		  s += 2;
		}
		else if ( strncasecmp(s,"nbz",3) == 0 ) {
		  cmpltr = 2;
		  f = 1;
		  s += 3;
		}
		else if ( strncasecmp(s,"nhz",3) == 0 ) {
		  cmpltr = 3;
		  f = 1;
		  s += 3;
		}
		else if ( strncasecmp(s,"ndc",3) == 0 ) {
		  cmpltr = 4;
		  f = 1;
		  s += 3;
		}
		else if ( strncasecmp(s,"nbc",3) == 0 ) {
		  cmpltr = 6;
		  f = 1;
		  s += 3;
		}
		else if ( strncasecmp(s,"nhc",3) == 0 ) {
		  cmpltr = 7;
		  f = 1;
		  s += 3;
		}
		else
		  as_bad("Unrecognized Logical Instruction Condition: %c",*s);
	      }
	      opcode |= cmpltr << 13;
	      opcode |= f << 12;		
	      continue;
	    case '>':   /* shift/extract/deposit conditions. */
	      cmpltr = 0;
	      if ( *s == ',' ) {
		s++;
		name = s;
		while ( *s != ',' && *s != ' ' && *s != '\t' )
		  s += 1;
		c = *s;
		*s = 0x00;
		if ( strcmp(name,"=") == 0 ) {
		  cmpltr = 1;
		}
		else if ( strcmp(name,"<") == 0 ) {
		  cmpltr = 2;
		}
		else if ( strcasecmp(name,"od") == 0 ) {
		  cmpltr = 3;
		}
		else if ( strcasecmp(name,"tr") == 0 ) {
		  cmpltr = 4;
		}
		else if ( strcmp(name,"<>") == 0 ) {
		  cmpltr = 5;
		}
		else if ( strcmp(name,">=") == 0 ) {
		  cmpltr = 6;
		}
		else if ( strcasecmp(name,"ev") == 0 ) {
		  cmpltr = 7;
		}
		else
		  as_bad("Unrecognized Shift/Extract/Deposit Condition: %s",name);
		*s = c;
	      }
	      opcode |= cmpltr << 13;
	      continue;
	    case '~':   /* bvb,bb conditions */
	      cmpltr = 0;
	      if ( *s == ',' ) {
		s++;
		if ( strncmp(s,"<",1) == 0 ) {
		  cmpltr = 2;
		  s++;
		}
		else if ( strncmp(s,">=",2) == 0 ) {
		  cmpltr = 6;
		  s += 2;
		}
		else
		  as_bad("Unrecognized Bit Branch Condition: %c",*s);
	      }
	      opcode |= cmpltr << 13;
	      continue;
	    case 'V':   /* 5  bit immediate at 31 */
	      getExpression(s);
	      low_sign_unext(evaluateAbsolute(the_insn.exp,the_insn.field_selector),
			     5,&im5);
	      opcode |= im5;
	      s = expr_end;
	      continue;
	    case 'r':   /* 5  bit immediate at 31 */
	      /* (unsigned value for the break instruction) */
	      getExpression(s);
	      im5 = evaluateAbsolute(the_insn.exp,the_insn.field_selector);
	      if ( im5 > 31 || im5 < 0 ) {
		as_bad("Operand out of range. Was: %d. Should be [0..31]. Assuming %d.\n",im5,im5&0x1f);
		im5 = im5 & 0x1f;
	      }
	      opcode |= im5;
	      s = expr_end;
	      continue;
	    case 'R':   /* 5  bit immediate at 15 */
	      /* (unsigned value for the ssm and rsm instruction) */
	      getExpression(s);
	      im5 = evaluateAbsolute(the_insn.exp,the_insn.field_selector);
	      if ( im5 > 31 || im5 < 0 ) {
		as_bad("Operand out of range. Was: %d. Should be [0..31]. Assuming %d.\n",im5,im5&0x1f);
		im5 = im5 & 0x1f;
	      }
	      opcode |= im5 << 16;
	      s = expr_end;
	      continue;
	    case 'i':   /* 11 bit immediate at 31 */
#ifdef OBJ_SOM
	      getExpression(s);
	      if ( the_insn.exp.X_seg == &bfd_abs_section ) {
		low_sign_unext(evaluateAbsolute(the_insn.exp,the_insn.field_selector),
			       11,&im11);
		opcode |= im11;
	      }
	      else {
		the_insn.reloc = R_CODE_ONE_SYMBOL;
		the_insn.code = 'i';
		the_insn.field_selector = the_insn.exp.field_selector;
	      }
	      s = expr_end;
	      continue;
#else
	      the_insn.field_selector = pa_chk_field_selector(&s);
	      getExpression(s);
	      if ( the_insn.exp.X_seg == &bfd_abs_section ) {
		low_sign_unext(evaluateAbsolute(the_insn.exp,the_insn.field_selector),
			       11,&im11);
		opcode |= im11;
	      }
	      else {
		      if ( is_DP_relative(the_insn.exp) )
			      the_insn.reloc = R_HPPA_GOTOFF;
		      else
			      the_insn.reloc = R_HPPA;
		      the_insn.format = 11;
	      }
	      s = expr_end;
	      continue;
#endif
	    case 'j':   /* 14 bit immediate at 31 */
#ifdef OBJ_SOM
	      getExpression(s);
	      if ( the_insn.exp.X_seg == &bfd_abs_section ) {
		low_sign_unext(evaluateAbsolute(the_insn.exp,field_selector),
			       14,&im14);
		if ( the_insn.exp.field_selector == e_rsel )
		  opcode |= (im14 & 0xfff);
		else
		  opcode |= im14;
	      }
	      else {
		the_insn.reloc = R_CODE_ONE_SYMBOL;
		the_insn.code = 'j';
		the_insn.field_selector = the_insn.exp.field_selector;
	      }
	      s = expr_end;
	      continue;
#else
	      the_insn.field_selector = pa_chk_field_selector(&s);
	      getExpression(s);
	      if ( the_insn.exp.X_seg == &bfd_abs_section ) {
		low_sign_unext(evaluateAbsolute(the_insn.exp,the_insn.field_selector),
			       14,&im14);
		if ( the_insn.field_selector == e_rsel )
		  opcode |= (im14 & 0xfff);
		else
		  opcode |= im14;
	      }
	      else {
		      if ( is_DP_relative(the_insn.exp) )
			      the_insn.reloc = R_HPPA_GOTOFF;
		      else
			      the_insn.reloc = R_HPPA;
		      the_insn.format = 14;
	      }
	      s = expr_end;
	      continue;
#endif
	      
	    case 'k':   /* 21 bit immediate at 31 */
#ifdef OBJ_SOM
	      getExpression(s);
	      if ( the_insn.exp.X_seg == &bfd_abs_section ) {
		dis_assemble_21(evaluateAbsolute(the_insn.exp,the_insn.field_selector),
				&im21);
		opcode |= im21;
	      }
	      else {
		the_insn.reloc = R_CODE_ONE_SYMBOL;
		the_insn.code = 'k';
		the_insn.field_selector = the_insn.exp.field_selector;
	      }
	      s = expr_end;
	      continue;
#else
	      the_insn.field_selector = pa_chk_field_selector(&s);
	      getExpression(s);
	      if ( the_insn.exp.X_seg == &bfd_abs_section ) {
		dis_assemble_21(evaluateAbsolute(the_insn.exp,the_insn.field_selector),
				&im21);
		opcode |= im21;
	      }
	      else {
		      if ( is_DP_relative(the_insn.exp) )
			      the_insn.reloc = R_HPPA_GOTOFF;
		      else
			      the_insn.reloc = R_HPPA;
		      the_insn.format = 21;
	      }
	      s = expr_end;
	      continue;
#endif
	      
	    case 'n':   /* nullification for branch instructions */
	      nullif = pa_parse_nullif(&s);
	      opcode |= nullif << 1;
	      continue;		
	    case 'w':   /* 12 bit branch displacement */
#ifdef OBJ_SOM
	      getExpression(s);
	      the_insn.pcrel = 1;
	      if ( strcmp(the_insn.exp.X_add_symbol->sy_nlist.n_un.n_name,"L0\001") == 0 ) {
		unsigned int w1,w,result;
		
		sign_unext( (the_insn.exp.X_add_number - 8) >> 2,12,&result);
		dis_assemble_12(result,&w1,&w);
		opcode |= ( ( w1 << 2 ) | w );
		the_insn.exp.X_add_symbol->sy_ref = FALSE;
	      }
	      else {
		/* this has to be wrong -- dont know what is right! */
		the_insn.reloc = R_PCREL_CALL;
		the_insn.code = 'w';
		the_insn.field_selector = the_insn.exp.field_selector;
		the_insn.arg_reloc = last_call_desc.arg_reloc;
		bzero(&last_call_desc,sizeof(call_descS));
	      }
	      s = expr_end;
	      continue;
#else
	      the_insn.field_selector = pa_chk_field_selector(&s);
	      getExpression(s);
	      the_insn.pcrel = 1;
	      if ( strcmp(S_GET_NAME(the_insn.exp.X_add_symbol),"L0\001") == 0 ) {
		unsigned int w1,w,result;
		
		sign_unext( (the_insn.exp.X_add_number - 8) >> 2,12,&result);
		dis_assemble_12(result,&w1,&w);
		opcode |= ( ( w1 << 2 ) | w );
		/* the_insn.exp.X_add_symbol->sy_ref = FALSE; */ /* XXX: not sure how to do this in BFD */
	      }
	      else {
		      /* this has to be wrong -- dont know what is right! */
		      the_insn.reloc = R_HPPA_PCREL_CALL;
		the_insn.format = 12;
		the_insn.arg_reloc = last_call_desc.arg_reloc;
		bzero(&last_call_desc,sizeof(call_descS));
	      }
	      s = expr_end;
	      continue;
#endif
	    case 'W':   /* 17 bit branch displacement */
#if defined(OBJ_ELF)
	      the_insn.field_selector = pa_chk_field_selector(&s);
#endif
	      getExpression(s);
	      the_insn.pcrel = 1;
#ifdef OBJ_SOM
	      if ( strcmp(the_insn.exp.X_add_symbol->sy_nlist.n_un.n_name,"L0\001") == 0 ) {
		unsigned int w2,w1,w,result;

		sign_unext( (the_insn.exp.X_add_number - 8) >> 2,17,&result);
		dis_assemble_17(result,&w1,&w2,&w);
		opcode |= ( ( w2 << 2 ) | ( w1 << 16 ) | w );
		the_insn.exp.X_add_symbol->sy_ref = FALSE;
	      }
	      else {
		/* this has to be wrong -- dont know what is right! */
		the_insn.reloc = R_PCREL_CALL;
		the_insn.code = 'W';
		the_insn.field_selector = the_insn.exp.field_selector;
		the_insn.arg_reloc = last_call_desc.arg_reloc;
		bzero(&last_call_desc,sizeof(call_descS));
	      }
#else
	      if ( the_insn.exp.X_add_symbol ) {
		      if ( strcmp(S_GET_NAME(the_insn.exp.X_add_symbol),"L0\001") == 0 ) {
			      unsigned int w2,w1,w,result;

			      sign_unext( (the_insn.exp.X_add_number - 8) >> 2,17,&result);
			      dis_assemble_17(result,&w1,&w2,&w);
			      opcode |= ( ( w2 << 2 ) | ( w1 << 16 ) | w );
		      }
		      else {
			      /* this has to be wrong -- dont know what is right! */
			      the_insn.reloc = R_HPPA_PCREL_CALL;
			      the_insn.format = 17;
			      the_insn.arg_reloc = last_call_desc.arg_reloc;
			      bzero(&last_call_desc,sizeof(call_descS));
		      }
	      }
	      else {
		      unsigned int w2,w1,w,result;

		      sign_unext( the_insn.exp.X_add_number >> 2,17,&result);
		      dis_assemble_17(result,&w1,&w2,&w);
		      opcode |= ( ( w2 << 2 ) | ( w1 << 16 ) | w );
	      }		      
#endif
	      s = expr_end;
	      continue;
	    case 'p':   /* 5 bit shift count at 26 (to support SHD instr.) */
	      /* value is encoded in instr. as 31-p where p is   */
	      /* the value scanned here */
	      getExpression(s);
	      if ( the_insn.exp.X_seg == &bfd_abs_section ) {
		opcode |= ( ( (31 - the_insn.exp.X_add_number) & 0x1f ) << 5 );
	      }
	      s = expr_end;
	      continue;
	    case 'P':   /* 5-bit bit position at 26 */
	      getExpression(s);
	      if ( the_insn.exp.X_seg == &bfd_abs_section ) {
		opcode |= ( the_insn.exp.X_add_number & 0x1f ) << 5;
	      }
	      s = expr_end;
	      continue;
	    case 'Q':   /* 5  bit immediate at 10 */
	      /* (unsigned bit position value for the bb instruction) */
	      getExpression(s);
	      im5 = evaluateAbsolute(the_insn.exp,the_insn.field_selector);
	      if ( im5 > 31 || im5 < 0 ) {
		as_bad("Operand out of range. Was: %d. Should be [0..31]. Assuming %d.\n",im5,im5&0x1f);
		im5 = im5 & 0x1f;
	      }
	      opcode |= im5 << 21;
	      s = expr_end;
	      continue;
	    case 'A':   /* 13 bit immediate at 18 (to support BREAK instr.) */
	      getAbsoluteExpression(s);
	      if ( the_insn.exp.X_seg == &bfd_abs_section )
		opcode |= (the_insn.exp.X_add_number & 0x1fff) << 13;
	      s = expr_end;
	      continue;
	    case 'Z':   /* System Control Completer(for LDA, LHA, etc.) */
	      if ( *s == ',' && ( *(s+1) == 'm' || *(s+1) == 'M' ) ) {
		m = 1;
		s += 2;
	      }
	      else
		m = 0;
	      
	      opcode |= m << 5;
	      while ( *s == ' ' || *s == '\t' ) /* skip to next operand */
		s++;
	      
	      continue;
	    case 'D':   /* 26 bit immediate at 31 (to support DIAG instr.) */
	      /* the action (and interpretation of this operand is
			     implementation dependent) */
#if defined(OBJ_ELF)
	      the_insn.field_selector = pa_chk_field_selector(&s);
#endif
	      getExpression(s);
	      if ( the_insn.exp.X_seg == &bfd_abs_section ) {
		opcode |= ( (evaluateAbsolute(the_insn.exp,the_insn.field_selector) & 0x1ffffff) << 1 );
#ifdef NEW_SOM /* XXX what replaces this? */
              /* PJH: VERY unsure about the following */
		the_insn.field_selector = the_insn.exp.field_selector;
#endif
	      }
	      else
		as_bad("Illegal DIAG operand");
	      s = expr_end;
	      continue;
	    case 'f':   /* 3 bit Special Function Unit (SFU) identifier at 25 */
	      sfu = pa_parse_number(&s);
	      if ( (sfu > 7) || (sfu < 0) )
		as_bad("Illegal SFU identifier: %02x", sfu);
	      opcode |= (sfu & 7) << 6;
	      continue;
	    case 'O':   /* 20 bit SFU op. split between 15 bits at 20
	                   and 5 bits at 31 */
	      getExpression(s);
	      s = expr_end;
	      continue;
	    case 'o':   /* 15 bit Special Function Unit operation at 20 */
	      getExpression(s);
	      s = expr_end;
	      continue;
	    case '2':   /* 22 bit SFU op. split between 17 bits at 20
	                   and 5 bits at 31 */
	      getExpression(s);
	      s = expr_end;
	      continue;
	    case '1':   /* 15 bit SFU op. split between 10 bits at 20
	                   and 5 bits at 31 */
	      getExpression(s);
	      s = expr_end;
	      continue;
	    case '0':   /* 10 bit SFU op. split between 5 bits at 20
	                   and 5 bits at 31 */
	      getExpression(s);
	      s = expr_end;
	      continue;
	    case 'u':   /* 3 bit coprocessor unit identifier at 25 */
	      getExpression(s);
	      s = expr_end;
	      continue;
	    case 'F':   /* Source FP Operand Format Completer (2 bits at 20) */
	      f = pa_parse_fp_format(&s);
	      opcode |= (int)f << 11;
	      the_insn.fpof1 = f;
	      continue;
	    case 'G':   /* Destination FP Operand Format Completer (2 bits at 18) */
	      s--;      /* need to pass the previous comma to pa_parse_fp_format */
	      f = pa_parse_fp_format(&s);
	      opcode |= (int)f << 13;
	      the_insn.fpof2 = f;
	      continue;
	    case 'M':   /* FP Compare Conditions (encoded as 5 bits at 31) */
	      cond = pa_parse_fp_cmp_cond(&s);
	      opcode |= cond;
	      continue;

            case 'v':   /* a 't' type extended to handle L/R register halves. */
	      {
		struct pa_89_fp_reg_struct result;
		int status;

		pa_89_parse_number(&s,&result);
		if ( result.number_part < 32 && result.number_part >= 0 ) {
		  opcode |= (result.number_part & 0x1f);

		  /* 0x30 opcodes are FP arithmetic operation opcodes */
		  /* load/store FP opcodes do not get converted to 0x38 */
                  /* opcodes like the 0x30 opcodes do */
		  if ( need_89_opcode(&the_insn,&result) ) {
		    if ( (opcode & 0xfc000000) == 0x30000000 ) {
		      opcode |= (result.L_R_select & 1) << 6;
		      opcode |= 1 << 27;
		    }
		    else {
		      opcode |= (result.L_R_select & 1) << 6;
		    }
		  }
		  continue;
		}
	      }
	      break;
	    case 'E':   /* a 'b' type extended to handle L/R register halves. */
	      {
		struct pa_89_fp_reg_struct result;
		int status;

		pa_89_parse_number(&s,&result);
		if ( result.number_part < 32 && result.number_part >= 0 ) {
		  opcode |= (result.number_part & 0x1f) << 21;
		  if ( need_89_opcode(&the_insn,&result) ) {
		    opcode |= (result.L_R_select & 1) << 7;
		    opcode |= 1 << 27;
		  }
		  continue;
		}
	      }
	      break;

	    case 'X':   /* an 'x' type extended to handle L/R register halves. */
	      {
		struct pa_89_fp_reg_struct result;
		int status;


		pa_89_parse_number(&s,&result);
		if ( result.number_part < 32 && result.number_part >= 0 ) {
		  opcode |= (result.number_part & 0x1f) << 16;
		  if ( need_89_opcode(&the_insn,&result) ) {
		    opcode |= (result.L_R_select & 1) << 12;
		    opcode |= 1 << 27;
		  }
		  continue;
		}
	      }
	      break;

	    case '4':   /* 5 bit register field at 10
				 (used in 'fmpyadd' and 'fmpysub') */
	      {
		struct pa_89_fp_reg_struct result;
		int status;

		status = pa_89_parse_number(&s,&result);
		if ( result.number_part < 32 && result.number_part >= 0 ) {
		  if ( the_insn.fpof1 == SGL ) {
		    result.number_part &= 0xF;
		    result.number_part |= (result.L_R_select & 1) << 4;
		  }
		  opcode |= result.number_part << 21;
		  continue;
		}
	      }
	      break;

	    case '6':   /* 5 bit register field at 15
				 (used in 'fmpyadd' and 'fmpysub') */
	      {
		struct pa_89_fp_reg_struct result;
		int status;

		status = pa_89_parse_number(&s,&result);
		if ( result.number_part < 32 && result.number_part >= 0 ) {
		  if ( the_insn.fpof1 == SGL ) {
		    result.number_part &= 0xF;
		    result.number_part |= (result.L_R_select & 1) << 4;
		  }
		  opcode |= result.number_part << 16;
		  continue;
		}
	      }
	      break;

	    case '7':   /* 5 bit register field at 31
				 (used in 'fmpyadd' and 'fmpysub') */
	      {
		struct pa_89_fp_reg_struct result;
		int status;

		status = pa_89_parse_number(&s,&result);
		if ( result.number_part < 32 && result.number_part >= 0 ) {
		  if ( the_insn.fpof1 == SGL ) {
		    result.number_part &= 0xF;
		    result.number_part |= (result.L_R_select & 1) << 4;
		  }
		  opcode |= result.number_part;
		  continue;
		}
	      }
	      break;

	    case '8':   /* 5 bit register field at 20
				 (used in 'fmpyadd' and 'fmpysub') */
	      {
		struct pa_89_fp_reg_struct result;
		int status;

		status = pa_89_parse_number(&s,&result);
		if ( result.number_part < 32 && result.number_part >= 0 ) {
		  if ( the_insn.fpof1 == SGL ) {
		    result.number_part &= 0xF;
		    result.number_part |= (result.L_R_select & 1) << 4;
		  }
		  opcode |= result.number_part << 11;
		  continue;
		}
	      }
	      break;

	    case '9':   /* 5 bit register field at 25
				 (used in 'fmpyadd' and 'fmpysub') */
	      {
		struct pa_89_fp_reg_struct result;
		int status;

		status = pa_89_parse_number(&s,&result);
		if ( result.number_part < 32 && result.number_part >= 0 ) {
		  if ( the_insn.fpof1 == SGL ) {
		    result.number_part &= 0xF;
		    result.number_part |= (result.L_R_select & 1) << 4;
		  }
		  opcode |= result.number_part << 6;
		  continue;
		}
	      }
	      break;

	    case 'H':  /* Floating Point Operand Format at 26 for       */
	               /* 'fmpyadd' and 'fmpysub' (very similar to 'F') */
	               /* bits are switched from other FP Operand       */
	               /* formats. 1=SGL, 1=<none>, 0=DBL               */
	      f = pa_parse_fp_format(&s);
	      switch (f) {
	      case SGL:
		opcode |= 0x20;
	      case DBL:
		the_insn.fpof1 = f;
		continue;

	      case QUAD:
	      case ILLEGAL_FMT:
	      default:
		as_bad("Illegal Floating Point Operand Format for this instruction: '%s'",*s);
	      }
	      break;

	    default:
		abort();
	    }
	    break;
	  }
	error:
	if (match == FALSE)
	  {
	    /* Args don't match.  */
	    if (&insn[1] - pa_opcodes < NUMOPCODES
		&& !strcmp(insn->name, insn[1].name))
	      {
		++insn;
		s = argsStart;
		continue;
	      }
	    else
              {
		as_bad("Illegal operands %s",error_message );
		return;
              }
	  }
	break;
    }

    the_insn.opcode = opcode;

#ifdef PA_DEBUG
    if ( the_insn.exp.X_add_symbol && the_insn.exp.X_subtract_symbol )
       print_insn_short(&the_insn);
    fprintf(stderr,"*********** END OF STATEMENT\n");
#endif

    return;
}

/*
    This is identical to the md_atof in m68k.c.  I think this is right,
    but I'm not sure.

   Turn a string in input_line_pointer into a floating point constant of type
   type, and store the appropriate bytes in *litP.  The number of LITTLENUMS
   emitted is stored in *sizeP .  An error message is returned, or NULL on OK.
 */

/* Equal to MAX_PRECISION in atof-ieee.c */
#define MAX_LITTLENUMS 6

char *
md_atof(type,litP,sizeP)
    char type;
    char *litP;
    int *sizeP;
{
    int	prec;
    LITTLENUM_TYPE words[MAX_LITTLENUMS];
    LITTLENUM_TYPE *wordP;
    char	*t;
    char	*atof_ieee();

    switch(type) {

    case 'f':
    case 'F':
    case 's':
    case 'S':
	prec = 2;
	break;

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

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

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

    default:
	*sizeP=0;
	return "Bad call to MD_ATOF()";
    }
    t=atof_ieee(input_line_pointer,type,words);
    if(t)
	input_line_pointer=t;
    *sizeP=prec * sizeof(LITTLENUM_TYPE);
    for(wordP=words;prec--;) {
	md_number_to_chars(litP,(long)(*wordP++),sizeof(LITTLENUM_TYPE));
	litP+=sizeof(LITTLENUM_TYPE);
    }
    return "";	/* Someone should teach Dean about null pointers */
}

/*
 * Write out big-endian.
 */
void
md_number_to_chars(buf,val,n)
    char *buf;
    long val;
    int n;
{

    switch(n) {

    case 4:
	*buf++ = val >> 24;
	*buf++ = val >> 16;
    case 2:
	*buf++ = val >> 8;
    case 1:
	*buf = val;
	break;

    default:
	abort();
    }
    return;
}

void
md_create_short_jump(ptr, from_addr, to_addr, frag, to_symbol)
    char *ptr;
    long from_addr, to_addr;
    fragS *frag;
    symbolS *to_symbol;
{
    fprintf(stderr, "pa_create_short_jmp\n");
    abort();
}

void
md_number_to_disp(buf,val,n)
    char	*buf;
    long	val;
    int		n;
{
    fprintf(stderr, "md_number_to_disp\n");
    abort();
}

void
md_number_to_field(buf,val,fix)
    char *buf;
    long val;
    void *fix;
{
    fprintf(stderr, "pa_number_to_field\n");
    abort();
}

/* the bit-field entries in the relocation_info struct plays hell 
   with the byte-order problems of cross-assembly.  So as a hack,
   I added this mach. dependent ri twiddler.  Ugly, but it gets
   you there. -KWK */
/* on sparc: first 4 bytes are normal unsigned long address, next three
   bytes are index, most sig. byte first.  Byte 7 is broken up with
   bit 7 as external, bits 6 & 5 unused, and the lower
   five bits as relocation type.  Next 4 bytes are long int addend. */
/* Thanx and a tip of the hat to Michael Bloom, mb@ttidca.tti.com */

#ifdef OBJ_SOM
void
md_ri_to_chars(ri_p, ri)
     struct reloc_info_pa *ri_p, ri;
{
  unsigned char the_bytes[sizeof(*ri_p)];
#if defined(OBJ_SOM) | defined(OBJ_OSFROSE) | defined(OBJ_ELF)
  /* not sure what, if any, changes are required for new-style cross-assembly */
#else
  the_bytes[0] = ((ri.need_data_ref << 7) & 0x80) | ((ri.arg_reloc & 0x03f8) >> 3);
  the_bytes[1] = ((ri.arg_reloc & 0x07) << 5) | ri.expression_type;
  the_bytes[2] = ((ri.exec_level << 6) & 0xc0) | ri.fixup_format;
  the_bytes[3] = ri.fixup_field & 0xff;
  md_number_to_chars(&the_bytes[4], ri.subspace_offset, sizeof(ri.subspace_offset));
  md_number_to_chars(&the_bytes[8], ri.symbol_index_one, sizeof(ri.symbol_index_one));
  md_number_to_chars(&the_bytes[12], ri.symbol_index_two, sizeof(ri.symbol_index_two));
  md_number_to_chars(&the_bytes[16], ri.fixup_constant, sizeof(ri.fixup_constant));

  /* now put it back where you found it, Junior... */
  bcopy (the_bytes, (char *)ri_p, sizeof(*ri_p));
#endif

}
#endif


/* Translate internal representation of relocation info to BFD target
   format.  */

arelent * tc_gen_reloc (section, fixp)
asection * section;
fixS * fixp;
{
	arelent *reloc;
	hppa_fixS *hppa_fixp = hppa_find_hppa_fix(fixp);
	bfd_reloc_code_real_type code;
	static int unwind_reloc_fixp_cnt = 0;
	static arelent *unwind_reloc_entryP = NULL;

	if ( fixp->fx_addsy == 0 )
		return 0;
	assert (hppa_fixp != 0);
	assert (section != 0);

	/* unwind section relocations are handled in a special way. */
	/* The relocations for the .unwind section are originally */
	/* built in the usual way.  That is, for each unwind table */
	/* entry there are two relocations:  one for the beginning of */
	/* the function and one for the end.	*/

	/* The first time we enter this function we create a */
	/* relocation of the type R_HPPA_UNWIND_ENTRIES.  The addend */
	/* of the relocation is initialized to 0.  Each additional */
	/* pair of times this function is called for the unwind */
	/* section represents an additional unwind table entry.  Thus, */
	/* the addend of the relocation should end up to be the number */
	/* of unwind table entries. */
	if ( strcmp(UNWIND_SECTION_NAME,section->name) == 0 ) {
		if ( unwind_reloc_entryP == NULL ) {
			reloc = (arelent *) bfd_alloc_by_size_t (stdoutput, sizeof (arelent));
			assert (reloc != 0);
			unwind_reloc_entryP = reloc;
			unwind_reloc_fixp_cnt++;
			unwind_reloc_entryP->address = fixp->fx_frag->fr_address + fixp->fx_where;
			/* a pointer any function will do.  We only */
			/* need one to tell us what section the unwind */
			/* relocations are for. */
			unwind_reloc_entryP->sym_ptr_ptr = &fixp->fx_addsy->bsym;
			code = R_HPPA_UNWIND_ENTRIES;
			unwind_reloc_entryP->howto = bfd_reloc_type_lookup (stdoutput, code);
			unwind_reloc_entryP->addend = unwind_reloc_fixp_cnt / 2;

			return unwind_reloc_entryP;
		}
		unwind_reloc_fixp_cnt++;
		unwind_reloc_entryP->addend = unwind_reloc_fixp_cnt / 2;

		return NULL;		
	}

	reloc = (arelent *) bfd_alloc_by_size_t (stdoutput, sizeof (arelent));
	assert (reloc != 0);

	reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
	/* XXX: might need additional processing here	*/
	/* hppa_elf_gen_reloc_type() is defined in the	*/
	/* ELF/PA BFD back-end				*/
	code = hppa_elf_gen_reloc_type(fixp->fx_r_type,
				       hppa_fixp->fx_r_format,
				       hppa_fixp->fx_r_field);
	reloc->howto = bfd_reloc_type_lookup (stdoutput, code);

	assert(code == reloc->howto->type);

	reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
	reloc->addend = 0;	/* default */

	/* Now, do any processing that is dependent on the relocation */
	/* type. */
	switch ( code ) {
	    case R_HPPA_PLABEL_32:
	    case R_HPPA_PLABEL_11:
	    case R_HPPA_PLABEL_14:
	    case R_HPPA_PLABEL_L21:
	    case R_HPPA_PLABEL_R11:
	    case R_HPPA_PLABEL_R14:
		/* For plabel relocations, the addend of the */
		/* relocation should be either 0 (no static link) or 2 */
		/* (static link required).	*/
		/* XXX: assume that fx_addnumber contains this */
		/* information */
		reloc->addend = fixp->fx_addnumber;
		break;

	    case R_HPPA_ABS_CALL_11:
	    case R_HPPA_ABS_CALL_14:
	    case R_HPPA_ABS_CALL_17:
	    case R_HPPA_ABS_CALL_L21:
	    case R_HPPA_ABS_CALL_R11:
	    case R_HPPA_ABS_CALL_R14:
	    case R_HPPA_ABS_CALL_R17:
	    case R_HPPA_ABS_CALL_LS21:
	    case R_HPPA_ABS_CALL_RS11:
	    case R_HPPA_ABS_CALL_RS14:
	    case R_HPPA_ABS_CALL_RS17:
	    case R_HPPA_ABS_CALL_LD21:
	    case R_HPPA_ABS_CALL_RD11:
	    case R_HPPA_ABS_CALL_RD14:
	    case R_HPPA_ABS_CALL_RD17:
	    case R_HPPA_ABS_CALL_LR21:
	    case R_HPPA_ABS_CALL_RR14:
	    case R_HPPA_ABS_CALL_RR17:
		
	    case R_HPPA_PCREL_CALL_11:
	    case R_HPPA_PCREL_CALL_14:
	    case R_HPPA_PCREL_CALL_17:
	    case R_HPPA_PCREL_CALL_L21:
	    case R_HPPA_PCREL_CALL_R11:
	    case R_HPPA_PCREL_CALL_R14:
	    case R_HPPA_PCREL_CALL_R17:
	    case R_HPPA_PCREL_CALL_LS21:
	    case R_HPPA_PCREL_CALL_RS11:
	    case R_HPPA_PCREL_CALL_RS14:
	    case R_HPPA_PCREL_CALL_RS17:
	    case R_HPPA_PCREL_CALL_LD21:
	    case R_HPPA_PCREL_CALL_RD11:
	    case R_HPPA_PCREL_CALL_RD14:
	    case R_HPPA_PCREL_CALL_RD17:
	    case R_HPPA_PCREL_CALL_LR21:
	    case R_HPPA_PCREL_CALL_RR14:
	    case R_HPPA_PCREL_CALL_RR17:
		/* constant is stored in the instruction */
		reloc->addend = ELF32_HPPA_R_ADDEND(hppa_fixp->fx_arg_reloc,0);
		break;
	    default:
		reloc->addend = fixp->fx_addnumber;
		break;
	}

	return reloc;
}

void
md_convert_frag(abfd, sec, fragP)
    register bfd *abfd;
    register asection *sec;
    register fragS *fragP;
{
  unsigned int address;

  if ( fragP -> fr_type == rs_machine_dependent ) {
    switch ( (int) fragP -> fr_subtype ) {
    case 0:
      fragP -> fr_type = rs_fill;
      know( fragP -> fr_var == 1 );
      know( fragP -> fr_next );
      address = fragP -> fr_address + fragP -> fr_fix;
      if ( address % fragP -> fr_offset ) {
	fragP -> fr_offset =
	  fragP -> fr_next -> fr_address
	    -   fragP -> fr_address
	      - fragP -> fr_fix;
      }
      else
	fragP -> fr_offset = 0;
      break;
    }
  }
}

/* Round up a section size to the appropriate boundary. */
long
md_section_align (segment, size)
     asection *segment;
     long size;
{
  return (size + 7) & ~7;	/* Round all sects to multiple of 8 */
} /* md_section_align() */

void
md_create_long_jump(ptr, from_addr, to_addr, frag, to_symbol)
    char	*ptr;
    long	from_addr,
	        to_addr;
    fragS	*frag;
    symbolS	*to_symbol;
{
    fprintf(stderr, "pa_create_long_jump\n");
    abort();
}

int
/* md_estimate_size_before_relax(fragP, segtype) */
md_estimate_size_before_relax(fragP, segment)
    register fragS *fragP;
    asection *segment;
{
  int size;

  size = 0;

  while ( (fragP->fr_fix + size) % fragP->fr_offset )
    size++;

  return size;
}

int
md_parse_option(argP,cntP,vecP)
    char **argP;
    int *cntP;
    char ***vecP;
{
    return 1;
}

/* We have no need to default values of symbols. */

/* ARGSUSED */
symbolS *md_undefined_symbol(name)
char *name;
{
        return 0;
} /*md_undefined_symbol() */

/* Parse an operand that is machine-specific.
   We just return without modifying the expression if we have nothing
   to do.  */

/* ARGSUSED */
void
md_operand (expressionP)
     expressionS *expressionP;
{
}

/* Apply a fixS to the frags, now that we know the value it ought to
   hold. */

int apply_field_selector(value,constant,field_selector)
long value;
long constant;
int field_selector;
{
	/* hppa_field_adjust() is defined in the HPPA target */
	return hppa_field_adjust(value,constant,field_selector);
}

void md_apply_fix(fixP, val)
fixS *fixP;
long val;
{
	char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
	hppa_fixS *hppa_fixP = hppa_find_hppa_fix(fixP);
	long new_val;
	long result;
	unsigned int w1,w2,w;
	/* The following routine is defined in the ELF/PA back-end	*/
	extern unsigned char hppa_elf_insn2fmt();

	if ( hppa_fixP ) {
		unsigned char fmt = hppa_elf_insn2fmt(fixP->fx_r_type,
						      *(unsigned long *)buf);

		assert(fixP->fx_r_type < R_HPPA_UNIMPLEMENTED);
		assert(fixP->fx_r_type >= R_HPPA_NONE);

		fixP->fx_addnumber = val;	/* Remember value for emit_reloc */

		/* Check if this is an undefined symbol.  No relocation can	*/
		/* possibly be performed in this case.				*/

		if ( (fixP->fx_addsy && fixP->fx_addsy->bsym->section == &bfd_und_section)
		    || (fixP->fx_subsy && fixP->fx_subsy->bsym->section == &bfd_und_section) )
			return;

		/* Perform some processing particular to unwind */
		/* relocations */

		if ( hppa_fixP->fx_call_infop
		    && ( ( (fixP == hppa_fixP->fx_call_infop->start_fix)
			  && (fixP->fx_addsy ==
			      hppa_fixP->fx_call_infop->start_symbol) )
			|| ( (fixP == hppa_fixP->fx_call_infop->end_fix)
			    && (fixP->fx_addsy ==
			      hppa_fixP->fx_call_infop->end_symbol) )
			) )
			val += fixP->fx_addsy->sy_frag->fr_address;

		switch (fmt) {
			
		    case 14:	/* all the opcodes with the 'j' operand type */
			new_val = apply_field_selector(val,0,hppa_fixP->fx_r_field);
			/* need to check for overflow here */
			
			/* mask off 14 bits to be changed */
			*(long *)buf = *(long *)buf & 0xffffc000;
			low_sign_unext(new_val,14,&result);
			break;
			
		    case 21:	/* all the opcodes with the 'k' operand type */
			new_val = apply_field_selector(val,0,hppa_fixP->fx_r_field);
			/* need to check for overflow here */
			
			/* mask off 21 bits to be changed */
			*(long *)buf = *(long *)buf & 0xffe00000;
			dis_assemble_21(new_val,&result);
			break;
			
		    case 11:	/* all the opcodes with the 'i' operand type */
			new_val = apply_field_selector(val,0,hppa_fixP->fx_r_field);
			/* need to check for overflow here */
			
			/* mask off 11 bits to be changed */
			*(long *)buf = *(long *)buf & 0xffff800;
			low_sign_unext(new_val,11,&result);
			break;
			
		    case 12:	/* all the opcodes with the 'w' operand type */
			new_val = apply_field_selector(val,0,hppa_fixP->fx_r_field);
			
			/* mask off 11 bits to be changed */
			sign_unext( (new_val - 8) >> 2,12,&result);
			*(long *)buf = *(long *)buf & 0xffffe002;
			
			dis_assemble_12(result,&w1,&w);
			result = ( ( w1 << 2 ) | w );
			break;
			
		    case 17:	/* some of the opcodes with the 'W' operand type */
			new_val = apply_field_selector(val,0,hppa_fixP->fx_r_field);
			/* need to check for overflow here */
			
			/* mask off 17 bits to be changed */
			*(long *)buf = *(long *)buf & 0xffe0e002;
			sign_unext( (new_val - 8) >> 2,17,&result);
			dis_assemble_17(result,&w1,&w2,&w);
			result = ( ( w2 << 2 ) | ( w1 << 16 ) | w );
			break;
			
		    case 32:
			new_val = apply_field_selector(val,0,hppa_fixP->fx_r_field);
			result = new_val;	/* no transformation on result */
			*(long *)buf = 0;	/* clear out everything */
			break;
			
		    case 0:
			return;
			
		    default:
			as_bad("bad relocation type/fmt: 0x%02x/0x%02x",
			       fixP->fx_r_type, fmt);
			return;
		}
		buf[0] |= (result & 0xff000000) >> 24;
		buf[1] |= (result & 0x00ff0000) >> 16;
		buf[2] |= (result & 0x0000ff00) >> 8;
		buf[3] |= result & 0x000000ff;
		/* We've now adjusted for fx_addnumber, we can */
		/* forget it now. */
		fixP->fx_addnumber = 0;
	}
	else {
		printf("no hppa_fixup entry for this fixup (fixP = 0x%x, type = 0x%x)\n",
		       fixP, fixP->fx_r_type);
	}
} /* md_apply_fix() */

/* Exactly what point is a PC-relative offset relative TO?
   On the PA, they're relative to the address of the offset.
   (??? Is this right?  FIXME-SOON) */
long md_pcrel_from(fixP)
fixS *fixP;
{
  return fixP->fx_where + fixP->fx_frag->fr_address;
} /* md_pcrel_from() */

int is_end_of_statement()
{
  return (   (*input_line_pointer == '\n')
	  || (*input_line_pointer == ';')
	  || (*input_line_pointer == '!') );
}

/* pa-aux.c -- Assembler for the PA - PA-RISC specific support routines */

struct aux_hdr_list *aux_hdr_root = NULL;

int print_errors = 1;

void pa_skip(s)
     char **s;
{
  while ( **s == ' ' || **s == '\t' )
    *s = *s + 1;
}

int pa_parse_number(s)
  char **s;
{
  int num;
  char *name;
  char c;
  symbolS *sym;
  int status;
  char * p = *s;

  while ( *p == ' ' || *p == '\t' )
    p = p + 1;
  num=-1; /* assume invalid number to begin with */
  if (isdigit(*p)) {
    num = 0; /* now we know it is a number */

    if ( *p == '0' && ( *(p+1) == 'x' || *(p+1) == 'X' ) ) { /* hex input */
      p = p + 2;
      while ( isdigit(*p) || ( (*p >= 'a') && (*p <= 'f') )
	                   || ( (*p >= 'A') && (*p <= 'F') ) ){
	if ( isdigit(*p) )
	  num = num*16 + *p-'0';
	else if ( *p >= 'a' && *p <= 'f' )
	  num = num*16 + *p-'a' + 10; 
	else
	  num = num*16 + *p-'A' + 10; 
	++p;
      }
    }
    else {
      while (isdigit(*p)) {
	num= num*10 + *p-'0';
	++p;
      }
    }
  }
  else if ( *p == '%' ) {   /* could be a pre-defined register */
    num = 0;
    name = p;
    p++;
    c = *p;
    /* tege hack: Special case for general registers
       as the general code makes a binary search with case translation,
       and is VERY slow.  */
    if (c == 'r') {
            p++;
	    if ( *p == 'e' && *(p+1) == 't' && (*(p+2) == '0' || *(p+2) == '1') ) {
		    p += 2;
		    num = *p - '0' + 28;	/* r28 is ret0 */
		    p++;
	    }
            else if (!isdigit(*p))
                    as_bad("Undefined register: '%s'. ASSUMING 0",name);
            else {
                    do
                            num= num*10 + *p++ - '0';
                    while (isdigit(*p));
            }
    }
    else {
	    while ( is_part_of_name(c) ) {
		    p = p + 1;
		    c = *p;
	    }
	    *p = 0;
	    status = reg_name_search(name);
	    if ( status >= 0 )
		    num = status;
	    else {
		    if ( print_errors )
			    as_bad("Undefined register: '%s'. ASSUMING 0",name);
		    else
			    num = -1;
	    }
	    *p = c;
    }
  }
  else {
    num = 0;
    name = p;
    c = *p;
    while ( is_part_of_name(c) ) {
      p = p + 1;
      c = *p;
    }
    *p = 0;
    if ( (sym = symbol_find(name)) != NULL ) {
#ifdef OBJ_SOM
      if ( sym->pa_sy_type == ST_ABSOLUTE ) {
	num = sym->pa_sy_value;
#else
      if ( S_GET_SEGMENT(sym) == &bfd_abs_section ) {
	num = S_GET_VALUE(sym);
#endif
      }
      else {
	if ( print_errors )
	  as_bad("Non-absolute constant: '%s'. ASSUMING 0",name);
	else
	  num = -1;
      }
    }
    else {
      if ( print_errors )
	as_bad("Undefined absolute constant: '%s'. ASSUMING 0",name);
      else
	num = -1;
    }
    *p = c;
  }

  *s = p;
  return num;
}

struct pd_reg {
	char	*name;
	int	value;
};

/*	List of registers that are pre-defined:

	General Registers:

	Name	Value		Name	Value
	%r0	0		%r16	16
	%r1	1		%r17	17
	%r2	2		%r18	18
	%r3	3		%r19	19
	%r4	4		%r20	20
	%r5	5		%r21	21
	%r6	6		%r22	22
	%r7	7		%r23	23
	%r8	8		%r24	24
	%r9	9		%r25	25
	%r10	10		%r26	26
	%r11	11		%r27	27
	%r12	12		%r28	28
	%r13	13		%r29	29
	%r14	14		%r30	30
	%r15	15		%r31	31

	Floating-point Registers:
	[NOTE:  Also includes L and R versions of these (e.g. %fr19L, %fr19R)]

	Name	Value		Name	Value
	%fr0	0		%fr16	16
	%fr1	1		%fr17	17
	%fr2	2		%fr18	18
	%fr3	3		%fr19	19
	%fr4	4		%fr20	20
	%fr5	5		%fr21	21
	%fr6	6		%fr22	22
	%fr7	7		%fr23	23
        %fr8	8		%fr24	24
	%fr9	9		%fr25	25
	%fr10	10		%fr26	26
	%fr11	11		%fr27	27
	%fr12	12		%fr28	28
	%fr13	13		%fr29	29
	%fr14	14		%fr30	30
	%fr15	15		%fr31	31

	Space Registers:

	Name	Value		Name	Value
	%sr0	0		%sr4	4
	%sr1	1		%sr5	5
	%sr2	2		%sr6	6
	%sr3	3		%sr7	7

	Control registers and their synonyms:

	Names			Value
	%cr0	%rctr		0
	%cr8	%pidr1		8
	%cr9	%pidr2		9
	%cr10	%ccr		10
	%cr11	%sar		11
	%cr12	%pidr3		12
	%cr13	%pidr4		13
	%cr14	%iva		14
	%cr15	%eiem		15
	%cr16	%itmr		16
	%cr17	%pcsq		17
	%cr18	%pcoq		18
	%cr19	%iir		19
	%cr20	%isr		20
	%cr21	%ior		21
	%cr22	%ipsw		22
	%cr23	%eirr		23
	%cr24	%tr0 %ppda	24
	%cr25	%tr1 %hta	25
	%cr26	%tr2		26
	%cr27	%tr3		27
	%cr28	%tr4		28
	%cr29	%tr5		29
	%cr30	%tr6		30
	%cr31	%tr7		31

	Miscellaneous registers and their synonyms:

	Names			Value
	%arg0			26
	%arg1			25
	%arg2			24
	%arg3			23
	%sp			30
	%ret0			28
	%ret1			29
*/

/* This table is sorted. Suitable for searching by a binary search. */

static struct pd_reg pre_defined_registers[] = {
	{	"%arg0",	26	},
	{	"%arg1",	25	},
	{	"%arg2",	24	},
	{	"%arg3",	23	},
	{	"%cr0",		0	},
	{	"%cr10",	10	},
	{	"%cr11",	11	},
	{	"%cr12",	12	},
	{	"%cr13",	13	},
	{	"%cr14",	14	},
	{	"%cr15",	15	},
	{	"%cr16",	16	},
	{	"%cr17",	17	},
	{	"%cr18",	18	},
	{	"%cr19",	19	},
	{	"%cr20",	20	},
	{	"%cr21",	21	},
	{	"%cr22",	22	},
	{	"%cr23",	23	},
	{	"%cr24",	24	},
	{	"%cr25",	25	},
	{	"%cr26",	26	},
	{	"%cr27",	27	},
	{	"%cr28",	28	},
	{	"%cr29",	29	},
	{	"%cr30",	30	},
	{	"%cr31",	31	},
	{	"%cr8",		8	},
	{	"%cr9",		9	},
	{	"%eiem",	15	},
	{	"%eirr",	23	},
	{	"%fr0",		0	},
	{	"%fr0L",	0	},
	{	"%fr0R",	0	},
	{	"%fr1",		1	},
	{	"%fr10",	10	},
	{	"%fr10L",	10	},
	{	"%fr10R",	10	},
	{	"%fr11",	11	},
	{	"%fr11L",	11	},
	{	"%fr11R",	11	},
	{	"%fr12",	12	},
	{	"%fr12L",	12	},
	{	"%fr12R",	12	},
	{	"%fr13",	13	},
	{	"%fr13L",	13	},
	{	"%fr13R",	13	},
	{	"%fr14",	14	},
	{	"%fr14L",	14	},
	{	"%fr14R",	14	},
	{	"%fr15",	15	},
	{	"%fr15L",	15	},
	{	"%fr15R",	15	},
	{	"%fr16",	16	},
	{	"%fr16L",	16	},
	{	"%fr16R",	16	},
	{	"%fr17",	17	},
	{	"%fr17L",	17	},
	{	"%fr17R",	17	},
	{	"%fr18",	18	},
	{	"%fr18L",	18	},
	{	"%fr18R",	18	},
	{	"%fr19",	19	},
	{	"%fr19L",	19	},
	{	"%fr19R",	19	},
	{	"%fr1L",	1	},
	{	"%fr1R",	1	},
	{	"%fr2",		2	},
	{	"%fr20",	20	},
	{	"%fr20L",	20	},
	{	"%fr20R",	20	},
	{	"%fr21",	21	},
	{	"%fr21L",	21	},
	{	"%fr21R",	21	},
	{	"%fr22",	22	},
	{	"%fr22L",	22	},
	{	"%fr22R",	22	},
	{	"%fr23",	23	},
	{	"%fr23L",	23	},
	{	"%fr23R",	23	},
	{	"%fr24",	24	},
	{	"%fr24L",	24	},
	{	"%fr24R",	24	},
	{	"%fr25",	25	},
	{	"%fr25L",	25	},
	{	"%fr25R",	25	},
	{	"%fr26",	26	},
	{	"%fr26L",	26	},
	{	"%fr26R",	26	},
	{	"%fr27",	27	},
	{	"%fr27L",	27	},
	{	"%fr27R",	27	},
	{	"%fr28",	28	},
	{	"%fr28L",	28	},
	{	"%fr28R",	28	},
	{	"%fr29",	29	},
	{	"%fr29L",	29	},
	{	"%fr29R",	29	},
	{	"%fr2L",	2	},
	{	"%fr2R",	2	},
	{	"%fr3",		3	},
	{	"%fr30",	30	},
	{	"%fr30L",	30	},
	{	"%fr30R",	30	},
	{	"%fr31",	31	},
	{	"%fr31L",	31	},
	{	"%fr31R",	31	},
	{	"%fr3L",	3	},
	{	"%fr3R",	3	},
	{	"%fr4",		4	},
	{	"%fr4L",	4	},
	{	"%fr4R",	4	},
	{	"%fr5",		5	},
	{	"%fr5L",	5	},
	{	"%fr5R",	5	},
	{	"%fr6",		6	},
	{	"%fr6L",	6	},
	{	"%fr6R",	6	},
	{	"%fr7",		7	},
	{	"%fr7L",	7	},
	{	"%fr7R",	7	},
	{	"%fr8",		8	},
	{	"%fr8L",	8	},
	{	"%fr8R",	8	},
	{	"%fr9",		9	},
	{	"%fr9L",	9	},
	{	"%fr9R",	9	},
	{	"%hta",		25	},
	{	"%iir",		19	},
	{	"%ior",		21	},
	{	"%ipsw",	22	},
	{	"%isr",		20	},
	{	"%itmr",	16	},
	{	"%iva",		14	},
	{	"%pcoq",	18	},
	{	"%pcsq",	17	},
	{	"%pidr1",	8	},
	{	"%pidr2",	9	},
	{	"%pidr3",	12	},
	{	"%pidr4",	13	},
	{	"%ppda",	24	},
	{	"%r0",	0	},
	{	"%r1",	1	},
	{	"%r10",	10	},
	{	"%r11",	11	},
	{	"%r12",	12	},
	{	"%r13",	13	},
	{	"%r14",	14	},
	{	"%r15",	15	},
	{	"%r16",	16	},
	{	"%r17",	17	},
	{	"%r18",	18	},
	{	"%r19",	19	},
	{	"%r2",	2	},
	{	"%r20",	20	},
	{	"%r21",	21	},
	{	"%r22",	22	},
	{	"%r23",	23	},
	{	"%r24",	24	},
	{	"%r25",	25	},
	{	"%r26",	26	},
	{	"%r27",	27	},
	{	"%r28",	28	},
	{	"%r29",	29	},
	{	"%r3",	3	},
	{	"%r30",	30	},
	{	"%r31",	31	},
	{	"%r4",	4	},
	{	"%r4L",	4	},
	{	"%r4R",	4	},
	{	"%r5",	5	},
	{	"%r5L",	5	},
	{	"%r5R",	5	},
	{	"%r6",	6	},
	{	"%r6L",	6	},
	{	"%r6R",	6	},
	{	"%r7",	7	},
	{	"%r7L",	7	},
	{	"%r7R",	7	},
	{	"%r8",	8	},
	{	"%r8L",	8	},
	{	"%r8R",	8	},
	{	"%r9",	9	},
	{	"%r9L",	9	},
	{	"%r9R",	9	},
	{	"%rctr",0	},
	{	"%ret0",28	},
	{	"%ret1",29	},
	{	"%sar",	11	},
	{	"%sp",	30	},
	{	"%sr0",	0	},
	{	"%sr1",	1	},
	{	"%sr2",	2	},
	{	"%sr3",	3	},
	{	"%sr4",	4	},
	{	"%sr5",	5	},
	{	"%sr6",	6	},
	{	"%sr7",	7	},
	{	"%tr0",	24	},
	{	"%tr1",	25	},
	{	"%tr2",	26	},
	{	"%tr3",	27	},
	{	"%tr4",	28	},
	{	"%tr5",	29	},
	{	"%tr6",	30	},
	{	"%tr7",	31	}
};

#define REG_NAME_CNT	(sizeof(pre_defined_registers) / sizeof(struct pd_reg))

int reg_name_search(name)
	char	*name;
{
	int x,l,r;

	l = 0;
	r = REG_NAME_CNT - 1;

	do {
		x = (l + r) / 2;
		if (strcasecmp(name,pre_defined_registers[x].name) < 0)
			r = x - 1;
		else
			l = x + 1;
	} while ( !( (strcasecmp(name,pre_defined_registers[x].name) == 0) ||
		     (l > r) ) );

	if ( strcasecmp(name,pre_defined_registers[x].name) == 0 )
		return(pre_defined_registers[x].value);
	else
		return(-1);

}

int is_pre_defined_register(s)
     char *s;
{
	if ( reg_name_search(s) >= 0 )
		return(TRUE);
	else
		return(FALSE);
}
    
int is_R_select(s)
  char *s;
{

  if ( *s == 'R' || *s == 'r' )
    return(TRUE);
  else
    return(FALSE);	
}

int is_L_select(s)
  char *s;
{

  if ( *s == 'L' || *s == 'l' )
    return(TRUE);
  else
    return(FALSE);	
}

int need_89_opcode(insn,result)
     struct pa_it *insn;
     struct pa_89_fp_reg_struct *result;
{
  /* if ( result->L_R_select == 1 || insn->fpof1 == DBL || insn->fpof2 == DBL ) */
  /* if (result->L_R_select == 1 && !(insn->fpof1 == DBL || insn->fpof2 == DBL) ) */
  if ( result->L_R_select == 1 && !(insn->fpof1 == DBL && insn->fpof2 == DBL) )
  /* if ( insn->fpof1 == DBL || insn->fpof2 == DBL ) */
    return TRUE;
  else
    return FALSE;
}

int
pa_89_parse_number(s,result)
     char **s;
     struct pa_89_fp_reg_struct *result;
{
  int num;
  char *name;
  char c;
  symbolS *sym;
  int status;
  char * p = *s;

  while ( *p == ' ' || *p == '\t' )
    p = p + 1;
  num=-1; /* assume invalid number to begin with */
  result->number_part = -1;
  result->L_R_select  = -1;

  if (isdigit(*p)) {
    num = 0; /* now we know it is a number */

    if ( *p == '0' && ( *(p+1) == 'x' || *(p+1) == 'X' ) ) { /* hex input */
      p = p + 2;
      while ( isdigit(*p) || ( (*p >= 'a') && (*p <= 'f') )
	                   || ( (*p >= 'A') && (*p <= 'F') ) ){
	if ( isdigit(*p) )
	  num = num*16 + *p-'0';
	else if ( *p >= 'a' && *p <= 'f' )
	  num = num*16 + *p-'a' + 10; 
	else
	  num = num*16 + *p-'A' + 10; 
	++p;
      }
    }
    else {
      while (isdigit(*p)) {
	num= num*10 + *p-'0';
	++p;
      }
    }

    result->number_part = num;

    if ( is_R_select(p) ) {
      result->L_R_select = 1;
      ++p;
    }
    else if ( is_L_select(p) ) {
      result->L_R_select = 0;
      ++p;
    }
    else
      result->L_R_select = 0;

  }
  else if ( *p == '%' ) {          /* could be a pre-defined register */
    num = 0;
    name = p;
    p++;
    c = *p;
    /* tege hack: Special case for general registers
       as the general code makes a binary search with case translation,
       and is VERY slow.  */
    if (c == 'r') {
      p++;
      if ( *p == 'e' && *(p+1) == 't' && (*(p+2) == '0' || *(p+2) == '1') ) {
	      p += 2;
	      num = *p - '0' + 28;	/* r28 is ret0 */
	      p++;
      }
      else if (!isdigit(*p))
        as_bad("Undefined register: '%s'. ASSUMING 0",name);
      else {
          do
            num= num*10 + *p++ - '0';
          while (isdigit(*p));
        }
      }
    else {
	    while ( is_part_of_name(c) ) {
		    p = p + 1;
		    c = *p;
	    }
	    *p = 0;
	    status = reg_name_search(name);
	    if ( status >= 0 )
		    num = status;
	    else {
		    if ( print_errors )
			    as_bad("Undefined register: '%s'. ASSUMING 0",name);
		    else
			    num = -1;
	    }
	    *p = c;
    }

    result->number_part = num;

    if ( is_R_select(p-1) )
      result->L_R_select = 1;
    else if ( is_L_select(p-1) )
      result->L_R_select = 0;
    else
      result->L_R_select = 0;

  }
  else {
    num = 0;
    name = p;
    c = *p;
    while ( is_part_of_name(c) ) {
      p = p + 1;
      c = *p;
    }
    *p = 0;
    if ( (sym = symbol_find(name)) != NULL ) {
#ifdef OBJ_SOM
      if ( sym->pa_sy_type == ST_ABSOLUTE ) {
	num = sym->pa_sy_value;
#else
      if ( S_GET_SEGMENT(sym) == &bfd_abs_section ) {
	num = S_GET_VALUE(sym);
#endif
      }
      else {
	if ( print_errors )
	  as_bad("Non-absolute constant: '%s'. ASSUMING 0",name);
	else
	  num = -1;
      }
    }
    else {
      if ( print_errors )
	as_bad("Undefined absolute constant: '%s'. ASSUMING 0",name);
      else
	num = -1;
    }
    *p = c;
    result->number_part = num;

    if ( is_R_select(p-1) )
	result->L_R_select = 1;
    else if ( is_L_select(p-1) )
	result->L_R_select = 0;
    else
	result->L_R_select = 0;

  }

  *s = p;
  return num;

}

int pa_parse_fp_cmp_cond(s)
  char **s;
{
  int cond,i;
  struct possibleS {
    char *string;
    int cond;
  };

  /* 
     This table is sorted by order of the length of the string. This is so we
     check for <> before we check for <. If we had a <> and checked for < first,
     we would get a false match.
   */
  static struct possibleS poss[] =
    {
      { "false?", 0 },
      { "false",  1 },
      { "true?",  30 },
      { "true",   31 },
      { "!<=>",   3 },
      { "!?>=",   8 },
      { "!?<=",   16 },
      { "!<>",    7 },
      { "!>=",    11 },
      { "!?>",    12 },
      { "?<=",    14 },
      { "!<=",    19 },
      { "!?<",    20 },
      { "?>=",    22 },
      { "!?=",    24 },
      { "!=t",    27 },
      { "<=>",    29 },
      { "=t",     5 },
      { "?=",     6 },
      { "?<",     10 },
      { "<=",     13 },
      { "!>",     15 },
      { "?>",     18 },
      { ">=",     21 },
      { "!<",     23 },
      { "<>",     25 },
      { "!=",     26 },
      { "!?",     28 },
      { "?",      2 },
      { "=",      4 },
      { "<",      9 },
      { ">",      17 }
    };

  cond=0;

  for ( i = 0; i < 32; i++ ) {
    if ( strncasecmp(*s,poss[i].string,strlen(poss[i].string)) == 0 ) {
      cond = poss[i].cond;
      *s += strlen(poss[i].string);
      while ( **s == ' ' || **s == '\t' )
	*s = *s + 1;
      return cond;
    }
  }

  as_bad("Illegal FP Compare Condition: %c",**s);
  return 0;
}

FP_Operand_Format pa_parse_fp_format(s)
     char **s;
{
  int f;

  f = SGL;
  if ( **s == ',' ) {
    *s += 1;
    if ( strncasecmp(*s,"sgl",3) == 0 ) {
      f = SGL;
      *s += 4;
    }
    else if ( strncasecmp(*s,"dbl",3) == 0 ) {
      f = DBL;
      *s += 4;
    }
    else if ( strncasecmp(*s,"quad",4) == 0 ) {
      f = QUAD;
      *s += 5;
    }
    else {
      f = ILLEGAL_FMT;
      as_bad("Unrecognized FP Operand Format: %3s",*s);
    }
  }
  while ( **s == ' ' || **s == '\t' || **s == 0 )
    *s = *s + 1;

  return f;
}

#if defined(OBJ_ELF)
int pa_chk_field_selector(str)
char **str;
{
  int selector;
  struct selector_entry {
    char *prefix;
    int field_selector;
  };
  static struct selector_entry selector_table[] = {
    { "F'",  e_fsel  },
    { "F%",  e_fsel  },
    { "LS'", e_lssel },
    { "LS%", e_lssel },
    { "RS'", e_rssel },
    { "RS%", e_rssel },
    { "L'",  e_lsel  },
    { "L%",  e_lsel  },
    { "R'",  e_rsel  },
    { "R%",  e_rsel  },
    { "LD'", e_ldsel },
    { "LD%", e_ldsel },
    { "RD'", e_rdsel },
    { "RD%", e_rdsel },
    { "LR'", e_lrsel },
    { "LR%", e_lrsel },
    { "RR'", e_rrsel },
    { "RR%", e_rrsel },
    { "P'",  e_psel },
    { "P%",  e_psel },
    { "RP'", e_rpsel },
    { "RP%", e_rpsel },
    { "LP'", e_lpsel },
    { "LP%", e_lpsel },
    { "T'",  e_tsel },
    { "T%",  e_tsel },
    { "RT'", e_rtsel },
    { "RT%", e_rtsel },
    { "LT'", e_ltsel },
    { "LT%", e_ltsel },
    { NULL,  e_fsel  }
  };
  struct selector_entry *tableP;

  selector = e_fsel;

  while ( **str == ' '  || **str == '\t' || **str == '\n' || **str == '\f' )
      {
	      *str = *str + 1;
      }
  for ( tableP = selector_table; tableP->prefix; tableP++ )
      {
	      if ( strncasecmp(tableP->prefix,*str,strlen(tableP->prefix)) == 0 )
		  {
			  *str += strlen(tableP->prefix);
			  selector = tableP->field_selector;
			  break;
		  }
      }
  return selector;
}

int
getExpression(str)
    char *str;
{
    char *save_in;
    asection * seg;

    save_in = input_line_pointer;
    input_line_pointer = str;
    seg = expression(&the_insn.exp);

  if (! (seg == &bfd_abs_section
	 || seg == &bfd_und_section
	 || seg == text_section
	 || seg == data_section
	 || seg == bss_section
	 || seg == diff_section
	 || seg == big_section
	 || seg == absent_section))
    {
	the_insn.error = "bad segment";
	expr_end = input_line_pointer;
	input_line_pointer=save_in;
	return 1;
    }
    expr_end = input_line_pointer;
    input_line_pointer = save_in;
    return 0;
}

int
getAbsoluteExpression(str)
    char *str;
{
    char *save_in;
    asection * seg;

    save_in = input_line_pointer;
    input_line_pointer = str;
    seg = expression(&the_insn.exp);

    if ( seg != &bfd_abs_section ) {
	the_insn.error = "segment should be ABSOLUTE";
	expr_end = input_line_pointer;
	input_line_pointer=save_in;
	return 1;
    }
    expr_end = input_line_pointer;
    input_line_pointer = save_in;
    return 0;
}

#else
int
getExpression(str)
    char *str;
{
    char *save_in;
    segT seg;

    save_in = input_line_pointer;
    input_line_pointer = str;
    switch (seg = expression(&the_insn.exp)) {

    case SEG_ABSOLUTE:
    case SEG_TEXT:
    case SEG_DATA:
    case SEG_BSS:
    case SEG_UNKNOWN:
    case SEG_DIFFERENCE:
    case SEG_BIG:
    case SEG_GDB:
    case SEG_MILLICODE:
    case SEG_NONE:
	break;

    default:
	the_insn.error = "illegal segment";
	expr_end = input_line_pointer;
	input_line_pointer=save_in;
	return 1;
    }
    expr_end = input_line_pointer;
    input_line_pointer = save_in;
    return 0;
}

int
getAbsoluteExpression(str)
    char *str;
{
    char *save_in;
    segT seg;

    save_in = input_line_pointer;
    input_line_pointer = str;
    switch (seg = expression(&the_insn.exp)) {

    case SEG_ABSOLUTE:
	break;

    default:
	the_insn.error = "segment should be ABSOLUTE";
	expr_end = input_line_pointer;
	input_line_pointer=save_in;
	return 1;
    }
    expr_end = input_line_pointer;
    input_line_pointer = save_in;
    return 0;
}

#endif

int evaluateAbsolute(exp,field_selector)
     expressionS exp;
     int field_selector;
{
  int value;

  value = exp.X_add_number;

  if ( exp.X_add_symbol ) {
    value += S_GET_VALUE(exp.X_add_symbol);
  }
  if ( exp.X_subtract_symbol ) {
    value -= S_GET_VALUE(exp.X_subtract_symbol);
  }

  switch (field_selector) {
  case e_fsel:        /* F  : no change                      */
    break;

  case e_lssel:       /* LS : if (bit 21) then add 0x800
                              arithmetic shift right 11 bits */
    if ( value & 0x00000400 )
      value += 0x800;
    value = (value & 0xfffff800) >> 11;
    break;

  case e_rssel:       /* RS : Sign extend from bit 21        */
    if ( value & 0x00000400 )
	    value |= 0xfffff800;
    else
	    value &= 0x7ff;
    break;

  case e_lsel:        /* L  : Arithmetic shift right 11 bits */
    value = (value & 0xfffff800) >> 11;
    break;

  case e_rsel:        /* R  : Set bits 0-20 to zero          */
    value = value & 0x7ff;
    break;

  case e_ldsel:       /* LD : Add 0x800, arithmetic shift
                              right 11 bits                  */
    value += 0x800;
    value = (value & 0xfffff800) >> 11;    
    break;

  case e_rdsel:       /* RD : Set bits 0-20 to one           */
    value |= 0xfffff800;
    break;

  case e_lrsel:       /* LR : L with "rounded" constant      */
    /* XXX: this isn't right.  Need to add a "rounded" constant */
    /* XXX: (presumably from X_add_number)                      */
    value = (value & 0xfffff800) >> 11;
    break;

  case e_rrsel:       /* RR : R with "rounded" constant      */
    /* XXX: this isn't right.  Need to add a "rounded" constant */
    /* XXX: (presumably from X_add_number)                      */
    value = value & 0x7ff;
    break;

  default:
    BAD_CASE(field_selector);
    break;
  }
  return value;
}

int pa_build_arg_reloc(type_name)
     char *type_name;
{

  if ( strncasecmp(type_name,"no",2) == 0 ) {
    return 0;
  }
  if ( strncasecmp(type_name,"gr",2) == 0 ) {
    return 1;
  }
  else if ( strncasecmp(type_name,"fr",2) == 0 ) {
    return 2;
  }
  else if ( strncasecmp(type_name,"fu",2) == 0 ) {
    return 3;
  }
  else
    as_bad("Unrecognized argument location: %s\n",type_name);

  return 0;
}

unsigned int pa_align_arg_reloc(reg,arg_reloc)
     unsigned int reg;
     unsigned int arg_reloc;
{
  unsigned int new_reloc;

  new_reloc = arg_reloc;
  switch(reg) {
  case 0:
    new_reloc <<= 8;
    break;
  case 1:
    new_reloc <<= 6;
    break;
  case 2:
    new_reloc <<= 4;
    break;
  case 3:
    new_reloc <<= 2;
    break;
  default:
    as_bad("Illegal argument description: %d",reg);
  }

  return new_reloc;
}

int pa_parse_nullif(s)
     char **s;
{
  int nullif;

  nullif = 0;
  if ( **s == ',' ) {
    *s = *s + 1;
    if ( strncasecmp(*s,"n",1) == 0 )
      nullif = 1;
    else {
      as_bad("Unrecognized Nullification: (%c)",**s);
      nullif = 0;
    }
    *s = *s + 1;
  }
  while ( **s == ' ' || **s == '\t' )
    *s = *s + 1;

  return nullif;
}

#if 0
int pa_parse_nonneg_cmpsub_cmpltr(s)
     char **s;
{
  int cmpltr;
  char *name;
  char c;

  cmpltr = -1;
  /** cmpltr = 0; **/
  if ( **s == ',' ) {
    *s+=1;
    name = *s;
    while ( **s != ',' && **s != ' ' && **s != '\t' )
      *s += 1;
    c = **s;
    **s = 0x00;
    if ( strcmp(name,"=") == 0 ) {
      cmpltr = 1;
    }
    else if ( strcmp(name,"<") == 0 ) {
      cmpltr = 2;
    }
    else if ( strcmp(name,"<=") == 0 ) {
      cmpltr = 3;
    }
    else if ( strcmp(name,"<<") == 0 ) {
      cmpltr = 4;
    }
    else if ( strcmp(name,"<<=") == 0 ) {
      cmpltr = 5;
    }
    else if ( strcasecmp(name,"sv") == 0 ) {
      cmpltr = 6;
    }
    else if ( strcasecmp(name,"od") == 0 ) {
      cmpltr = 7;
    }
    /**
    else
      cmpltr = -1;
    **/
    **s = c;
  }
  if ( cmpltr >= 0 ) {
    while ( **s == ' ' || **s == '\t' )
      *s = *s + 1;
  }

  return cmpltr;
}
#endif
int pa_parse_nonneg_cmpsub_cmpltr(s)
     char **s;
{
  int cmpltr;
  char *name;
  char c;

  cmpltr = 0;
  if ( **s == ',' ) {
    *s+=1;
    name = *s;
    while ( **s != ',' && **s != ' ' && **s != '\t' )
      *s += 1;
    c = **s;
    **s = 0x00;
    if ( strcmp(name,"=") == 0 ) {
      cmpltr = 1;
    }
    else if ( strcmp(name,"<") == 0 ) {
      cmpltr = 2;
    }
    else if ( strcmp(name,"<=") == 0 ) {
      cmpltr = 3;
    }
    else if ( strcmp(name,"<<") == 0 ) {
      cmpltr = 4;
    }
    else if ( strcmp(name,"<<=") == 0 ) {
      cmpltr = 5;
    }
    else if ( strcasecmp(name,"sv") == 0 ) {
      cmpltr = 6;
    }
    else if ( strcasecmp(name,"od") == 0 ) {
      cmpltr = 7;
    }
    else
      cmpltr = -1;
    **s = c;
  }
  if ( cmpltr >= 0 ) {
    while ( **s == ' ' || **s == '\t' )
      *s = *s + 1;
  }

  return cmpltr;
}

int pa_parse_neg_cmpsub_cmpltr(s)
     char **s;
{
  int cmpltr;
  char *name;
  char c;

  cmpltr = -1;
  if ( **s == ',' ) {
    *s+=1;
    name = *s;
    while ( **s != ',' && **s != ' ' && **s != '\t' )
      *s += 1;
    c = **s;
    **s = 0x00;
    if ( strcasecmp(name,"tr") == 0 ) {
      cmpltr = 0;
    }
    else if ( strcmp(name,"<>") == 0 ) {
      cmpltr = 1;
    }
    else if ( strcmp(name,">=") == 0 ) {
      cmpltr = 2;
    }
    else if ( strcmp(name,">") == 0 ) {
      cmpltr = 3;
    }
    else if ( strcmp(name,">>=") == 0 ) {
      cmpltr = 4;
    }
    else if ( strcmp(name,">>") == 0 ) {
      cmpltr = 5;
    }
    else if ( strcasecmp(name,"nsv") == 0 ) {
      cmpltr = 6;
    }
    else if ( strcasecmp(name,"ev") == 0 ) {
      cmpltr = 7;
    }
    **s = c;
  }
  if ( cmpltr >= 0 ) {
    while ( **s == ' ' || **s == '\t' )
      *s = *s + 1;
  }

  return cmpltr;
}

int pa_parse_nonneg_add_cmpltr(s)
     char **s;
{
  int cmpltr;
  char *name;
  char c;

  cmpltr = -1;
  if ( **s == ',' ) {
    *s+=1;
    name = *s;
    while ( **s != ',' && **s != ' ' && **s != '\t' )
      *s += 1;
    c = **s;
    **s = 0x00;
    if ( strcmp(name,"=") == 0 ) {
      cmpltr = 1;
    }
    else if ( strcmp(name,"<") == 0 ) {
      cmpltr = 2;
    }
    else if ( strcmp(name,"<=") == 0 ) {
      cmpltr = 3;
    }
    else if ( strcasecmp(name,"nuv") == 0 ) {
      cmpltr = 4;
    }
    else if ( strcasecmp(name,"znv") == 0 ) {
      cmpltr = 5;
    }
    else if ( strcasecmp(name,"sv") == 0 ) {
      cmpltr = 6;
    }
    else if ( strcasecmp(name,"od") == 0 ) {
      cmpltr = 7;
    }
    **s = c;
  }
  if ( cmpltr >= 0 ) {
    while ( **s == ' ' || **s == '\t' )
      *s = *s + 1;
  }

  return cmpltr;
}

int pa_parse_neg_add_cmpltr(s)
     char **s;
{
  int cmpltr;
  char *name;
  char c;

  cmpltr = -1;
  if ( **s == ',' ) {
    *s+=1;
    name = *s;
    while ( **s != ',' && **s != ' ' && **s != '\t' )
      *s += 1;
    c = **s;
    **s = 0x00;
    if ( strcasecmp(name,"tr") == 0 ) {
      cmpltr = 0;
    }
    else if ( strcmp(name,"<>") == 0 ) {
      cmpltr = 1;
    }
    else if ( strcmp(name,">=") == 0 ) {
      cmpltr = 2;
    }
    else if ( strcmp(name,">") == 0 ) {
      cmpltr = 3;
    }
    else if ( strcmp(name,"uv") == 0 ) {
      cmpltr = 4;
    }
    else if ( strcmp(name,"vnz") == 0 ) {
      cmpltr = 5;
    }
    else if ( strcasecmp(name,"nsv") == 0 ) {
      cmpltr = 6;
    }
    else if ( strcasecmp(name,"ev") == 0 ) {
      cmpltr = 7;
    }
    **s = c;
  }
  if ( cmpltr >= 0 ) {
    while ( **s == ' ' || **s == '\t' )
      *s = *s + 1;
  }

  return cmpltr;
}

void s_seg()
{

    if (strncmp(input_line_pointer, "\"text\"", 6) == 0) {
	input_line_pointer += 6;
	s_text();
	return;
    }
    if (strncmp(input_line_pointer, "\"data\"", 6) == 0) {
	input_line_pointer += 6;
	s_data();
	return;
    }
    if (strncmp(input_line_pointer, "\"data1\"", 7) == 0) {
	input_line_pointer += 7;
	s_data1();
	return;
    }
    as_bad("Unknown segment type");
    demand_empty_rest_of_line();
    return;
}

void s_private()
{
	register int temp;

	temp = get_absolute_expression ();
#ifdef OBJ_SOM
	subseg_new (SEG_DATA, (subsegT)temp);
#else
	subseg_new ("$PRIVATE$", (subsegT)temp);
#endif
	demand_empty_rest_of_line();
}

void s_data1()
{
#ifdef OBJ_SOM
    subseg_new(SEG_DATA, 1);
#else
    subseg_new("$PRIVATE$", 1);
#endif
    demand_empty_rest_of_line();
    return;
}

void s_proc()
{
    extern char is_end_of_line[];

    while (!is_end_of_line[*input_line_pointer]) {
	++input_line_pointer;
    }
    ++input_line_pointer;
    return;
}

void pa_block(z)
     int z;
{
    register char *p;
    register long int temp_fill;
    register long int temp_size;
    register int i;

    temp_size = get_absolute_expression ();

    if (z) {                  /* fill with zeroes even if not requested to do so. */
      temp_fill = 0;          /* HP assembler does this too. */
    }
    else {
      temp_fill = 0;
    }

    if ( temp_size <= 0 ) {
      as_bad("size < 0, .block ignored");
      temp_size = 0;
    }
    p = frag_var(rs_fill,
		 (int)temp_size,
		 (int)temp_size,(relax_substateT)0,(symbolS *)0,1,(char *)0);
    bzero (p, (int)temp_size);

    /* convert 2 bytes at a time */

    for ( i = 0; i < temp_size; i+=2 ) {
      md_number_to_chars (p+i,
			  temp_fill,
			  (int)( (temp_size - i) > 2 ? 2 : (temp_size - i)));
    }

    demand_empty_rest_of_line();
    return;
}

void pa_call()
{

  pa_call_args(&last_call_desc);
  demand_empty_rest_of_line();
  return;
}

void pa_call_args(call_desc)
  register call_descS 	*call_desc;
{
  register char *name;
  register char c;
  register char *p;
  register int temp;
  register unsigned int arg_reloc;

  while ( !is_end_of_statement() ) {
    name = input_line_pointer;
    c = get_symbol_end();
    if ( (strncasecmp(name, "argw", 4) == 0) ) {
      temp = atoi(name+4);
      p = input_line_pointer;
      *p = c;
      input_line_pointer++;
      name = input_line_pointer;
      c = get_symbol_end();
      arg_reloc = pa_build_arg_reloc(name);
      call_desc->arg_reloc |= pa_align_arg_reloc(temp,arg_reloc);
    }
    else if ( (strncasecmp(name, "rtnval", 6) == 0) ) {
      p = input_line_pointer;
      *p = c;
      input_line_pointer++;
      name = input_line_pointer;
      c = get_symbol_end();
      arg_reloc = pa_build_arg_reloc(name);
      call_desc->arg_reloc |= ( arg_reloc & 0x3 );
    }
    else {
      as_bad("Unrecognized .CALL argument: %s",name);
    }
    p = input_line_pointer;
    *p = c;
    if ( !is_end_of_statement() )
      input_line_pointer++;
  }
}

static int
is_same_frag(frag1P,frag2P)
     fragS *frag1P;
     fragS *frag2P;
{

  if ( frag1P == NULL )
    return (FALSE);
  else if ( frag2P == NULL )
    return (FALSE);
  else if ( frag1P == frag2P )
    return (TRUE);
  else if ( frag2P->fr_type == rs_fill && frag2P->fr_fix == 0 )
    is_same_frag(frag1P,frag2P->fr_next);
  else
    return (FALSE);
}

#ifdef OBJ_ELF
static void
pa_build_unwind_subspace(call_info)
     call_infoS *call_info;
{
  char *unwindP;
  asection *seg;
  asection *save_seg;
  subsegT subseg,save_subseg;
  int i;
  char c;
  char *p;

  subseg = SUBSEG_UNWIND;
  seg = bfd_get_section_by_name(stdoutput,UNWIND_SECTION_NAME);
  if ( seg == ASEC_NULL ) {
	  seg = bfd_make_section_old_way(stdoutput,UNWIND_SECTION_NAME);
	  bfd_set_section_flags (stdoutput,
				 seg,
				 SEC_READONLY | SEC_ALLOC | SEC_LOAD | SEC_RELOC);
  }

  /* callinfo.frame is in bytes and unwind_desc is in 8 byte units */
  call_info->ci_unwind.descriptor.frame_size = call_info->frame / 8;

  /* Now, dump the unwind descriptor to the $UNWIND$ subspace. This
     creates a couple of relocations */

  save_seg = now_seg;
  save_subseg = now_subseg;
  subseg_new((char *)seg->name,subseg);
  unwindP = (char *)&call_info->ci_unwind;

  p = frag_more(4);
  call_info->start_offset_frag = frag_now;
  call_info->start_frag_where = p - frag_now->fr_literal;

  /* relocation info. for start offset of the function */

  fix_new_hppa (frag_now, p - frag_now -> fr_literal, 4,
		call_info->start_symbol, (symbolS *) NULL,
		0, 0, R_HPPA, e_fsel, 32, 0, (char *) 0);

  /** we need to search for the first relocation involving the start_symbol of **/
  /** this call_info descriptor **/

  {
    fixS *fixP;

    call_info->start_fix = seg_info(now_seg)->fix_root;   /* the default */
    for ( fixP = call_info->start_fix; fixP; fixP = fixP->fx_next ) {
	    if ( fixP->fx_addsy == call_info->start_symbol
		|| fixP->fx_subsy == call_info->start_symbol ) {
		    call_info->start_fix = fixP;
		    break;
	    }
    }
  }     

  p = frag_more(4);
  call_info->end_offset_frag = frag_now;
  call_info->end_frag_where = p - frag_now->fr_literal;

  /* relocation info. for end offset of the function */

    fix_new_hppa (frag_now, p - frag_now -> fr_literal, 4,
		  call_info->end_symbol, (symbolS *) NULL,
		  0, 0, R_HPPA, e_fsel, 32, 0, (char *) 0);

  /** we need to search for the first relocation involving the start_symbol of **/
  /** this call_info descriptor **/

  {
    fixS *fixP;

    call_info->end_fix = seg_info(now_seg)->fix_root;   /* the default */
    for ( fixP = call_info->end_fix; fixP; fixP = fixP->fx_next ) {
	    if ( fixP->fx_addsy == call_info->end_symbol
		|| fixP->fx_subsy == call_info->end_symbol ) {
		    call_info->end_fix = fixP;
		    break;
	    }
    }
  }

  for ( i = 8; i < sizeof(unwind_tableS); i++ ) {
    c = *(unwindP + i);
    {
      FRAG_APPEND_1_CHAR( c );
    }
  }

    subseg_new((char *)save_seg->name,save_subseg);
}
#else
#ifdef OBJ_SOM
static void
pa_build_unwind_subspace(call_info)
     call_infoS *call_info;
{
  space_dict_chainS *spaceP;
  subspace_dict_chainS *subspaceP;
  char *unwindP;
  char defined,loadable,code_only,common,dup_common,is_zero,sort;
  int access,space_index,alignment,quadrant;
  segT seg,save_seg;
  subsegT subseg,save_subseg;
  int i;
  char c;
  char *p;

  defined = 1;
  loadable = 1;
  code_only = 0;
  common = 0;
  dup_common = 0;
  is_zero = 0;
  sort = 0x40;
  access = 0x2c;
  space_index = 0;
  alignment = 8;
  quadrant = 0;
  subseg = SUBSEG_UNWIND;
  seg = SEG_TEXT;

  spaceP = pa_segment_to_space(seg);

  if ( (subspaceP = is_defined_subspace("$UNWIND$",SUBSEG_UNWIND) ) ) {
    update_subspace("$UNWIND$",defined,loadable,code_only,common,dup_common,
		    sort,is_zero,access,space_index,alignment,quadrant,
		    SUBSEG_UNWIND);
  }
  else {
    subspaceP = create_new_subspace(spaceP,"$UNWIND$",defined,loadable,code_only,
				    common,dup_common,is_zero,sort,access,
				    space_index,alignment,quadrant,seg);
  }


  /* callinfo.frame is in bytes and unwind_desc is in 8 byte units */
  call_info->ci_unwind.descriptor.frame_size = call_info->frame / 8;

  /* Now, dump the unwind descriptor to the $UNWIND$ subspace. This
     creates a couple of relocations */

  save_seg = now_seg;
  save_subseg = now_subseg;
  subseg_new(seg,subseg);
  unwindP = (char *)&call_info->ci_unwind;

  p = frag_more(4);
  call_info->start_offset_frag = frag_now;
  call_info->start_frag_where = p - frag_now->fr_literal;

  /* relocation info. for start offset of the function */

  fix_new (frag_now, p - frag_now -> fr_literal, 4,
	   call_info->start_symbol, (symbolS *) NULL,
	   0, 0, R_DATA_ONE_SYMBOL, e_fsel, 0, 0, (char *) 0);

  /** we need to search for the first relocation involving the start_symbol of **/
  /** this call_info descriptor **/

  {
    fixS *fixP;

    call_info->start_fix = seg_info(now_seg)->fix_root;   /* the default */
    for ( fixP = call_info->start_fix; fixP; fixP = fixP->fx_next ) {
      /*
      if ( ( fixP->fx_addsy == call_info->start_symbol ||
	     fixP->fx_subsy == call_info->start_symbol )
	  &&
	   ( fixP->fx_frag == call_info->start_symbol->sy_frag ) ) {
       */
      if ( ( fixP->fx_addsy == call_info->start_symbol ||
	     fixP->fx_subsy == call_info->start_symbol )
	  &&
	   ( is_same_frag(fixP->fx_frag,call_info->start_symbol->sy_frag) ) ) {
	call_info->start_fix = fixP;
	break;
      }
    }
  }     

  p = frag_more(4);
  call_info->end_offset_frag = frag_now;
  call_info->end_frag_where = p - frag_now->fr_literal;

  /* relocation info. for end offset of the function */

    fix_new (frag_now, p - frag_now -> fr_literal, 4,
	     call_info->start_symbol, (symbolS *) NULL,
	     0, 0, R_DATA_ONE_SYMBOL, e_fsel, 0, 0, (char *) 0);

  /** we need to search for the first relocation involving the start_symbol of **/
  /** this call_info descriptor **/

  {
    fixS *fixP;

    call_info->end_fix = seg_info(now_seg)->fix_root;   /* the default */
    for ( fixP = call_info->end_fix; fixP; fixP = fixP->fx_next ) {
      /*
      if ( ( fixP->fx_addsy == call_info->start_symbol ||
	     fixP->fx_subsy == call_info->start_symbol )
	  &&
	   ( fixP->fx_frag == call_info->start_symbol->sy_frag ) ) {
       */
      if ( ( fixP->fx_addsy == call_info->start_symbol ||
	     fixP->fx_subsy == call_info->start_symbol )
	  &&
	   ( is_same_frag(fixP->fx_frag,call_info->start_symbol->sy_frag) ) ) {
	call_info->end_fix = fixP;
	break;
      }
    }
  }

  for ( i = 8; i < sizeof(unwind_tableS); i++ ) {
    c = *(unwindP + i);
    {
      FRAG_APPEND_1_CHAR( c );
    }
  }

    subseg_new(save_seg,save_subseg);

}
#endif
#endif

void pa_callinfo()
{
    register char *name;
    register char c;
    register char *p;
    register int temp;
    register symbolS *	symbolP;

    if ( !within_procedure )
      as_bad(".callinfo is not within a procedure definition");

    callinfo_found = TRUE;

    while ( !is_end_of_statement() ) {
      name = input_line_pointer;
      c = get_symbol_end();
      if ( (strncasecmp(name, "frame", 5) == 0) ) {
	p = input_line_pointer;
	*p = c;
	input_line_pointer++;
	temp = get_absolute_expression ();
	if ( ( temp & 0x3 ) != 0 ) {
	  as_bad("FRAME parameter must be a multiple of 8: %d\n",temp);
	  temp = 0;
	}
	last_call_info->frame = temp;
      }
      else if ( (strncasecmp(name, "entry_gr", 8) == 0) ) {
	p = input_line_pointer;
	*p = c;
	input_line_pointer++;
	temp = get_absolute_expression ();
	last_call_info->ci_unwind.descriptor.entry_gr = temp;
      }
      else if ( (strncasecmp(name, "entry_fr", 8) == 0) ) {
	p = input_line_pointer;
	*p = c;
	input_line_pointer++;
	temp = get_absolute_expression ();
	last_call_info->ci_unwind.descriptor.entry_fr = temp;
      }
      else if ( (strncasecmp(name, "entry_sr", 8) == 0) ) {
	p = input_line_pointer;
	*p = c;
	input_line_pointer++;
	temp = get_absolute_expression ();
	last_call_info->entry_sr = temp;
      }
      else if ( (strncasecmp(name, "calls", 5) == 0) ||
	        (strncasecmp(name, "caller", 6) == 0) ) {
	p = input_line_pointer;
	*p = c;
	last_call_info->makes_calls = 1;
      }
      else if ( (strncasecmp(name, "no_calls", 8) == 0) ) {
	p = input_line_pointer;
	*p = c;
	last_call_info->makes_calls = 0;
      }
      else if ( (strncasecmp(name, "save_rp",7) == 0) ) {
	p = input_line_pointer;
	*p = c;
	last_call_info->ci_unwind.descriptor.save_rp = 1;
      }
      else if ( (strncasecmp(name, "save_sp",7) == 0) ) {
	p = input_line_pointer;
	*p = c;
	last_call_info->ci_unwind.descriptor.save_sp = 1;
      }
      else if ( (strncasecmp(name, "no_unwind",9) == 0) ) {
	p = input_line_pointer;
	*p = c;
	last_call_info->ci_unwind.descriptor.cannot_unwind = 1;
      }
      else if ( (strncasecmp(name, "hpux_int",7) == 0) ) {
	p = input_line_pointer;
	*p = c;
	last_call_info->hpux_int = 1;
      }
      else {
	as_bad("Unrecognized .CALLINFO argument: %s",name);
      }
      if (  !is_end_of_statement() )
	input_line_pointer++;
    }

    demand_empty_rest_of_line();
    return;
}

void pa_code()
{
  space_dict_chainS *sdchain;

  if ( (sdchain = is_defined_space("$TEXT$")) == NULL ) {
    sdchain = create_new_space(pa_def_spaces[0].name,pa_def_spaces[0].spnum,
			       pa_def_spaces[0].loadable,pa_def_spaces[0].defined,
			       pa_def_spaces[0].private,pa_def_spaces[0].sort,
			       1,pa_def_spaces[0].segment);
  }

  SPACE_DEFINED(sdchain) = 1;
#ifdef OBJ_SOM

  subseg_new (SEG_TEXT, SUBSEG_CODE);
#else

  subseg_new (".text",SUBSEG_CODE);
#endif
      
  demand_empty_rest_of_line();
  return;
}

/*
 * This is different than the standard GAS s_comm(). On HP9000/800 machines,
 * the .comm pseudo-op has the following symtax:
 *
 *     <label> .comm <length>
 *
 * where <label> is optional and is a symbol whose address will be the start of
 * a block of memory <length> bytes long. <length> must be an absolute expressio
n.
 * <length> bytes will be allocated in the current space and subspace.
 *
 */

void
pa_comm()
{
  register char *p;
  register int size,i;
  register symbolS *symbolP;
  register label_symbolS *label_symbolP = pa_get_label();

  if ( label_symbolP )
    symbolP = label_symbolP->lss_label;
  else
    symbolP = NULL;
  
  SKIP_WHITESPACE();
  if ( (size = get_absolute_expression ()) < 0 ) {
    as_warn(".COMMon length (%d.) <0! Ignored.", size);
    ignore_rest_of_line();
    return;
  }

  if ( symbolP ) {
#ifdef OBJ_SOM
	  if ( symbolP -> pa_sy_type == ST_STORAGE &&
	      symbolP -> pa_sy_scope == SS_UNSAT ) {
		  if (symbolP -> pa_sy_value != size) {
			  as_warn( "Length of .comm \"%s\" is already %d. Not changed to %d.",
				  symbolP -> pa_sy_name, symbolP -> pa_sy_value, size);
			  return;
		  }
	  }
	  else {
		  symbolP -> pa_sy_value = size;
		  symbolP -> pa_sy_scope = SS_UNSAT;
		  symbolP -> pa_sy_type  = ST_STORAGE;
		  symbolP -> sy_ref      = sym_def;
	  }
#endif
#ifdef OBJ_ELF
	  if ( S_IS_DEFINED(symbolP) && S_GET_SEGMENT(symbolP) == bss_section ) {
		as_bad("Ignoring attempt to re-define symbol");
		ignore_rest_of_line();
		return;
	  }
	  if ( S_GET_VALUE(symbolP) ) {
		  if ( S_GET_VALUE(symbolP) != size) {
			  as_warn( "Length of .comm \"%s\" is already %d. Not changed to %d.",
				  S_GET_NAME(symbolP), S_GET_VALUE(symbolP), size);
			  return;
		  }
	  }
	  else {
		  S_SET_VALUE(symbolP, size);
		  S_SET_SEGMENT(symbolP,bss_section);
		  S_SET_EXTERNAL(symbolP);
	  }
#endif
  }


  demand_empty_rest_of_line();
}

void pa_copyright()
{
    register char *name;
    register char c;
    register char *p;
    register int temp;
    register symbolS *	symbolP;

    SKIP_WHITESPACE();
    if (* input_line_pointer == '\"') {
      ++ input_line_pointer; /* -> 1st char of string. */
      name = input_line_pointer;
      while ( (c = next_char_of_string()) >= 0)
	;
      c = *input_line_pointer;
      *input_line_pointer = '\0';
      *(input_line_pointer-1) = '\0';
      {
#ifdef OBJ_SOM
#define PREFIX	"Copyright "
#define MIDFIX  ". All rights reserved. No part of this program may be photocopied, reproduced, or transmitted without prior written consent of "
#define SUFFIX "."

	struct aux_hdr_list *aux_hdr_entry;
	int len;
	char *company_name = name;
	char *date_part;

	date_part = (char *) index (name,',');
	if ( date_part ) {
	  *date_part = 0x00;
	  date_part++;
	}

	len =
	  strlen(PREFIX) +
	    strlen(MIDFIX) +
	      strlen(SUFFIX) +
		2*strlen(name);

	if ( date_part ) {
	  len += strlen(date_part) + strlen(",");
	}

	aux_hdr_entry =(struct aux_hdr_list *)malloc(sizeof(struct aux_hdr_list));
	if ( aux_hdr_root ) {
	  aux_hdr_entry->ahl_next = aux_hdr_root;
	  aux_hdr_root = aux_hdr_entry;
	}
	else {
	  aux_hdr_entry->ahl_next = NULL;
	  aux_hdr_root = aux_hdr_entry;
	}
	aux_hdr_entry->type = COPYRIGHT_AUX_ID;
	aux_hdr_entry->contents.cpy.header_id.append = 1;
	aux_hdr_entry->contents.cpy.header_id.type = COPYRIGHT_AUX_ID;
	aux_hdr_entry->contents.cpy.header_id.length = len + sizeof(unsigned int);
	while ( aux_hdr_entry->contents.usr_str.header_id.length % 4 )
	  aux_hdr_entry->contents.usr_str.header_id.length += 1;

	aux_hdr_entry->contents.cpy.string_length = len;
	aux_hdr_entry->contents.cpy.copyright = (char *)(malloc(len + 1));
	strcpy(aux_hdr_entry->contents.cpy.copyright,PREFIX);
	strcat(aux_hdr_entry->contents.cpy.copyright,name);
	if ( date_part ) {
	  strcat(aux_hdr_entry->contents.cpy.copyright,",");
	  strcat(aux_hdr_entry->contents.cpy.copyright,date_part);
	}
	strcat(aux_hdr_entry->contents.cpy.copyright,MIDFIX);
	strcat(aux_hdr_entry->contents.cpy.copyright,name);
	strcat(aux_hdr_entry->contents.cpy.copyright,SUFFIX);
	aux_hdr_entry->contents.cpy.copyright[len] = NULL;
#undef PREFIX
#undef MIDFIX
#undef SUFFIX
#endif /* OBJ_SOM */
      }
      *input_line_pointer = c;
    }
    else {
      as_bad( "Expected \"-ed string" );
    }
    demand_empty_rest_of_line();
}

void pa_end()
{

    demand_empty_rest_of_line();
    return;
}

void pa_enter()
{

  as_bad(".ENTER encountered. gas doesn't generate entry code sequences.");
  pa_entry();
  return;
}

void pa_entry()
{
  char *where;

  if ( !within_procedure )
    as_bad("Misplaced .entry. Ignored.");
  else {
    if ( !callinfo_found )
      as_bad("Missing .callinfo.");

    last_call_info->start_frag = frag_now;
  }
  demand_empty_rest_of_line();
  within_entry_exit = TRUE;
  where = frag_more(0);
#ifdef OBJ_SOM
  fix_new (frag_now, where - frag_now->fr_literal, 0,
	   last_call_info->start_symbol, (symbolS *) NULL, 0, 0,
	   R_ENTRY, e_fsel, 0, 0, (char *) &last_call_info->ci_unwind.descriptor);
#else
#ifdef OBJ_ELF
  /* XXX: no ENTRY relocation for PA ELF.  What do we do instead? */
#else
  fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
		last_call_info->start_symbol, (symbolS *) NULL, 0, 0,
		R_HPPA_ENTRY, 0, 0, 0,
		(char *) &last_call_info->ci_unwind.descriptor);
#endif
#endif
  return;
}

void pa_equ(reg)
     int reg;
{
    register label_symbolS *label_symbolP = pa_get_label();
    register symbolS *	symbolP;

    if ( label_symbolP ) {
	    symbolP = label_symbolP->lss_label;
#ifdef OBJ_SOM
	    symbolP->pa_sy_value = get_absolute_expression();
	    symbolP->pa_sy_type = ST_ABSOLUTE;
	    symbolP->sy_ref = sym_unref;
	    symbolP->sy_equ = 1;
#else
	    S_SET_VALUE(symbolP,get_absolute_expression());
	    S_SET_SEGMENT(symbolP,&bfd_abs_section);
#endif
    }
    else {
      if ( reg )
	as_bad(".REG must use a label");
      else
	as_bad(".EQU must use a label");
    }

    demand_empty_rest_of_line();
    return;
}

void process_exit()
{
  char *where;

  where = frag_more(0);
#ifdef OBJ_SOM
  fix_new (frag_now, where - frag_now->fr_literal, 0,
	   last_call_info->start_symbol, (symbolS *) NULL, 0,
	   0, R_EXIT, e_fsel, 0, 0, (char *) NULL);
#endif
#ifdef OBJ_ELF
  /* XXX: no EXIT relocation for PA ELF.  All we do is create a */
  /* temporary symbol marking the end of the function. */
  {
	  char *name = (char *)xmalloc(strlen("L\001end_") +
				       strlen(S_GET_NAME(last_call_info->start_symbol)) + 1);

	  if ( name ) {
		  symbolS *symbolP;

		  strcpy(name,"L\001end_");
		  strcat(name,S_GET_NAME(last_call_info->start_symbol));
		  
		  symbolP = symbol_find (name);
		  if ( symbolP )
			  as_warn("Symbol '%s' already defined.", name);
		  else {
			  /* symbol value should be the offset of the */
			  /* last instruction of the function */
			  symbolP = symbol_new(name,
					       now_seg, 
					       (valueT)(obstack_next_free(&frags)-frag_now->fr_literal-4),
					       frag_now);

			  assert(symbolP);

			  symbolP->bsym->flags = last_call_info->start_symbol->bsym->flags;
			  symbol_table_insert(symbolP);
		  }
		  if ( symbolP )
			  last_call_info->end_symbol = symbolP;
		  else
			  as_bad("Symbol '%s' could not be created.", name);
		 
	  }
	  else
		  as_bad("No memory for symbol name.");
  }
#else
  fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
		last_call_info->start_symbol, (symbolS *) NULL, 0,
		0, R_HPPA_EXIT, 0, 0, 0, (char *) NULL);
#endif
  last_call_info->end_frag = frag_now;

  pa_build_unwind_subspace(last_call_info);

  exit_processing_complete = TRUE;
}

void pa_exit()
{

  if ( !within_procedure )
    as_bad(".EXIT must appear within a procedure");
  else {
    if ( !callinfo_found )
      as_bad("Missing .callinfo");
    else {
      if ( !within_entry_exit )
	as_bad("No .ENTRY for this .EXIT");
      else {
	within_entry_exit = FALSE;
	process_exit();
      }
    }
  }
  demand_empty_rest_of_line();
  return;
}

void pa_export()
{
    register char *name;
    register char c;
    register char *p;
    register int temp;
    register int regno;
    register symbolS *	symbolP;

    name = input_line_pointer;
    c = get_symbol_end();
    /* just after name is now '\0' */

    if ( (symbolP = symbol_find_or_make(name)) == NULL ) {
      as_bad("Cannot define export symbol: %s\n",name);
      p = input_line_pointer;
      *p = c;
      input_line_pointer++;
    }
    else {
#ifdef OBJ_SOM
      symbolP->pa_sy_dict.symbol_scope = SS_UNIVERSAL;
      /* determination of the symbol_type field will have to wait until
	 we know the subspace index (within the object file) of the subspace
	 containing this symbol */
#else
      /* S_SET_SEGMENT(symbolP,&bfd_und_section); */
      S_SET_EXTERNAL(symbolP);
      /* symbolP->sy_frag = frag_now; */
#endif

      p = input_line_pointer;
      *p = c;
      if ( !is_end_of_statement() ) {
	input_line_pointer++;
	pa_export_args(symbolP);
      }
    }

    demand_empty_rest_of_line();
    return;
}

void pa_export_args(symbolP)
    register symbolS      *symbolP;
{
    register char *name;
    register char c;
    register char *p;
    register int temp;

    if (strncasecmp(input_line_pointer, "absolute", 8) == 0) {
      input_line_pointer += 8;
#ifdef OBJ_SOM
      symbolP->pa_sy_dict.symbol_type = ST_ABSOLUTE;
#else
      S_SET_SEGMENT(symbolP,&bfd_abs_section);
#endif
    }
    else if (strncasecmp(input_line_pointer, "code", 4) == 0) {
      input_line_pointer += 4;
#ifdef OBJ_SOM
      symbolP->pa_sy_dict.symbol_type = ST_CODE;
#else
      /* S_SET_SEGMENT(symbolP,text_section); */
#endif
    }
    else if (strncasecmp(input_line_pointer, "data", 4) == 0) {
      input_line_pointer += 4;
#ifdef OBJ_SOM
      symbolP->pa_sy_dict.symbol_type = ST_DATA;
#else
      /* S_SET_SEGMENT(symbolP,data_section); */
#endif
    }
    else if ( (strncasecmp(input_line_pointer, "entry", 5) == 0) ) {
      input_line_pointer += 5;
#ifdef OBJ_SOM
      symbolP->pa_sy_dict.symbol_type = ST_ENTRY;
#else
      symbolP->bsym->flags |= BSF_FUNCTION;
#endif
    }
    else if (strncasecmp(input_line_pointer, "millicode", 9) == 0) {
      input_line_pointer += 9;
#ifdef OBJ_SOM
      symbolP->pa_sy_dict.symbol_type = ST_MILLICODE;
#endif
    }
    else if (strncasecmp(input_line_pointer, "plabel", 6) == 0) {
      input_line_pointer += 6;
#ifdef OBJ_SOM
      symbolP->pa_sy_dict.symbol_type = ST_PLABEL;
#endif
    }
    else if (strncasecmp(input_line_pointer, "pri_prog", 8) == 0) {
      input_line_pointer += 8;
#ifdef OBJ_SOM
      symbolP->pa_sy_dict.symbol_type = ST_PRI_PROG;
#endif
    }
    else if (strncasecmp(input_line_pointer, "sec_prog", 8) == 0) {
      input_line_pointer += 8;
#ifdef OBJ_SOM
      symbolP->pa_sy_dict.symbol_type = ST_SEC_PROG;
#endif
    }

    while ( !is_end_of_statement() ) {
      if ( *input_line_pointer == ',' )
	      input_line_pointer++;
      name = input_line_pointer;
      c = get_symbol_end();
      if ( (strncasecmp(name, "argw", 4) == 0) ) {
	p = input_line_pointer;
	*p = c;
	input_line_pointer++;
	temp = atoi(name+4);
	name = input_line_pointer;
	c = get_symbol_end();
#ifdef OBJ_SOM
	symbolP->pa_sy_dict.arg_reloc |=
	             pa_align_arg_reloc(temp,pa_build_arg_reloc(name));
#endif
	*input_line_pointer = c;
      }
      else if (( strncasecmp(name, "rtnval",6)) == 0 ) {
	p = input_line_pointer;
	*p = c;
	input_line_pointer++;
	name = input_line_pointer;
	c = get_symbol_end();
#ifdef OBJ_SOM
	symbolP->pa_sy_dict.arg_reloc |= pa_build_arg_reloc(name);
#endif
	*input_line_pointer = c;
      }
      else if (( strncasecmp(name, "priv_lev",8)) == 0 ) {
	p = input_line_pointer;
	*p = c;
	input_line_pointer++;
	/*** temp = get_absolute_expression (); ***/
	temp = atoi(input_line_pointer);
	c = get_symbol_end();
	*input_line_pointer = c;
#ifdef OBJ_SOM
	symbolP->sy_priv_lev = temp & 3;  /* this is stored in symbol_value later */
#endif
      }
      else {
	as_bad("Undefined .EXPORT/.IMPORT argument (ignored): %s",name);
	p = input_line_pointer;
	*p = c;
      }
      if ( !is_end_of_statement() )
	input_line_pointer++;
    }
}

void pa_import()
{
    register char *name;
    register char c;
    register char *p;
    register symbolS *symbolP;
    register expressionS resultP; /* Deliver result here. */

    name = input_line_pointer;
    c = get_symbol_end();
    /* just after name is now '\0' */

    symbolP = symbol_find_or_make (name);
#if defined(OBJ_ELF)
    /* symbolP->bsym->flags |= BSF_IMPORT; */ /* XXX BSF_IMPORT is obsolete */
#else
    /* Check to see if this symbol has already been exported (this means its */
    /* defined locally and the import statement is redundant). */
    /* If it has not been exported, go ahead and mark this symbol as SS_UNSAT */
    /* (an unsatisfied external symbol) */

    /* But, if the symbol has already been referenced (sy_ref == TRUE),
       leave it alone. */

    if ( !symbolP->sy_ref ) {
      if ( symbolP->pa_sy_dict.symbol_scope != SS_UNIVERSAL ) {
	symbolP->pa_sy_dict.symbol_scope = SS_UNSAT;
	symbolP->sy_ref = FALSE;
      }
    }
#endif

    p = input_line_pointer;
    *p = c;

    if ( !is_end_of_statement() ) {
       input_line_pointer++;

       pa_export_args(symbolP);
#ifdef OBJ_ELF
       /* In ELF, since this is an import, leave the section undefined.	*/
       /* S_SET_SEGMENT(symbolP,&bfd_und_section); */
#endif
    }
    else {
#ifdef OBJ_SOM
	    /* no further arguments, assign a default type according
	       to the current subspace (CODE or DATA) */
      switch (now_seg) {
      case SEG_TEXT:		symbolP->pa_sy_dict.symbol_type = ST_CODE;break;
      case SEG_ABSOLUTE:	symbolP->pa_sy_dict.symbol_type = ST_ABSOLUTE;break;
      default:			symbolP->pa_sy_dict.symbol_type = ST_DATA;
      }
#else
      /* In ELF, if the section is undefined, then the symbol is undefined */
      /* Since this is an import, leave the section undefined.	*/
      S_SET_SEGMENT(symbolP,&bfd_und_section);
#endif
    }
      

    demand_empty_rest_of_line();
    return;
}

void pa_label()
{
    register char *name;
    register char c;
    register char *p;

    name = input_line_pointer;
    c = get_symbol_end();
    /* just after name is now '\0' */

    if ( strlen(name) > 0 ) {
      colon(name);
      p = input_line_pointer;
      *p = c;
    }
    else {
      as_warn("Missing label name on .LABEL");
    }

    if ( !is_end_of_statement() ) {
      as_warn("extra .LABEL arguments ignored.");
      ignore_rest_of_line();
    }
    demand_empty_rest_of_line();
    return;
}

void pa_leave()
{

  as_bad(".LEAVE encountered. gas doesn't generate exit code sequences.");
  pa_exit();
  return;
}

void pa_origin()
{
    s_org();     /* ORG actually allows another argument (the fill value)
                    but maybe this is OK? */
    return;
}

void pa_proc()
{
  call_infoS *call_info;

  if ( within_procedure )
    as_fatal("Nested procedures");

  callinfo_found = FALSE;
  within_procedure = TRUE;
  exit_processing_complete = FALSE;

  /* create another call_info structure */

  call_info = (call_infoS *)xmalloc(sizeof(call_infoS));

  if ( !call_info )
    as_fatal("Cannot allocate unwind descriptor\n");

  bzero(call_info,sizeof(call_infoS));

  call_info->ci_next = NULL;

  if ( call_info_root == NULL ) {
    call_info_root = call_info;
    last_call_info = call_info;
  }
  else {
    last_call_info->ci_next = call_info;
    last_call_info = call_info;
  }

  /* set up defaults on call_info structure */

  call_info->ci_unwind.descriptor.cannot_unwind = 1; /* PJH: Tim says gcc code
                                                        is unwindable */
  call_info->ci_unwind.descriptor.region_desc = 1;
  call_info->entry_sr = ~0;
  call_info->makes_calls = 1;
  call_info->hpux_int = 0;

  /* If we got a .PROC pseudo-op, we know that the function is defined
     locally. Make sure it gets into the symbol table */
  {
	  label_symbolS *label_symbolP = pa_get_label();

	  if ( label_symbolP ) {
		  if ( label_symbolP->lss_label ) {
#ifdef OBJ_SOM
			  label_symbolP->lss_label->sy_ref |= sym_def;
#endif
			  last_call_info->start_symbol = label_symbolP->lss_label;
			  label_symbolP->lss_label->bsym->flags |= BSF_FUNCTION;
		  }
		  else
			  as_bad("Missing function name for .PROC (corrupted label)");
	  }
	  else
		  as_bad("Missing function name for .PROC");
  }

  demand_empty_rest_of_line();
  return;
}

void pa_procend()
{

  if ( !within_procedure )
    as_bad("misplaced .procend");

  if ( !callinfo_found )
    as_bad("Missing .callinfo for this procedure");

  if ( within_entry_exit )
    as_bad("Missing .EXIT for a .ENTRY");

  if ( !exit_processing_complete )
    process_exit();

  within_procedure = FALSE;
  demand_empty_rest_of_line();
  return;
}

space_dict_chainS *pa_parse_space_stmt(space_name,create_flag)
     char *space_name;
     int create_flag;
{
  register char *name;
  register char c;
  register char *p;
  register int temp;

  char *ptemp;
  int spnum;
  char loadable;
  char defined;
  char private;
  char sort;
#ifdef OBJ_SOM
  segT seg;
#else
  asection * seg;
#endif
  space_dict_chainS *space;

  /* load default values */
  spnum = 0;
  loadable = TRUE;
  defined = TRUE;
  private = FALSE;
  if ( strcasecmp(space_name,"$TEXT$") == 0 ) {
#ifdef OBJ_SOM
    seg = SEG_TEXT;
#else
    seg = text_section;
#endif
    sort = 8;
  }
  else {
#ifdef OBJ_SOM
    seg = SEG_DATA;
#else
    seg = data_section;
#endif
    sort = 16;
  }

  if ( !is_end_of_statement() ) {
    print_errors = FALSE;
    ptemp = input_line_pointer + 1;
    if ( (temp = pa_parse_number(&ptemp)) >= 0 ) {
      spnum = temp;
      input_line_pointer = ptemp;
    }
    else {
      while ( !is_end_of_statement() ) {
	input_line_pointer++;
	name = input_line_pointer;
	c = get_symbol_end();
	if ( (strncasecmp(name, "SPNUM", 5) == 0) ) {
	  p = input_line_pointer;
	  *p = c;
	  input_line_pointer++;
	  temp = get_absolute_expression ();
	  spnum = temp;
	}
	else if ( (strncasecmp(name, "SORT",4) == 0) ) {
	  p = input_line_pointer;
	  *p = c;
	  input_line_pointer++;
	  temp = get_absolute_expression ();
	  sort = temp;
	}
	else if ( (strncasecmp(name, "UNLOADABLE",10) == 0) ) {
	  p = input_line_pointer;
	  *p = c;
	  loadable = FALSE;
	}
	else if ( (strncasecmp(name, "NOTDEFINED",10) == 0) ) {
	  p = input_line_pointer;
	  *p = c;
	  defined = FALSE;
	}
	else if ( (strncasecmp(name, "PRIVATE",7) == 0) ) {
	  p = input_line_pointer;
	  *p = c;
	  private = TRUE;
	}
	else {
	  as_bad("Unrecognized .SPACE argument");
	  p = input_line_pointer;
	  *p = c;
	}
      }
    }
    print_errors = TRUE;
  }
  if ( create_flag )
    space = create_new_space(space_name,spnum,loadable,defined,private,sort,1,seg);
  else {           /* if no creation of new space, this must be the first */
                   /* occurrence of a built-in space */
    space = is_defined_space(space_name);
    SPACE_SPNUM(space) = spnum;
    SPACE_LOADABLE(space) = loadable & 1;
    SPACE_DEFINED(space) = defined & 1;
    SPACE_PRIVATE(space) = private & 1;
    SPACE_SORT(space) = sort & 0xff;
    space->sd_defined = 1;
    space->sd_seg = seg;    
  }
  return space;
}

void pa_align_subseg(seg,subseg)
#ifdef OBJ_SOM
     segT seg;
#else
     asection * seg;
#endif
     subsegT subseg;
{
  subspace_dict_chainS * now_subspace;
  int alignment;
  int shift;

  now_subspace = pa_subsegment_to_subspace(seg,subseg);
  if ( SUBSPACE_ALIGN(now_subspace) == 0 )
    alignment = now_subspace->ssd_last_align;
  else if ( now_subspace->ssd_last_align > SUBSPACE_ALIGN(now_subspace) )
    alignment = now_subspace->ssd_last_align;
  else
    alignment = SUBSPACE_ALIGN(now_subspace);

  shift = 0;
  while ( (1 << shift) < alignment )
    shift++;

  frag_align(shift,0);
}

void pa_space()
{
    register char *name;
    register char c;
    register char *p;
    register int temp;
    register symbolS *	symbolP;
    register space_dict_chainS *sd_chain;
    char space_name[40];

    if ( within_procedure ) {
      as_bad("Can\'t change spaces within a procedure definition. Ignored");
      ignore_rest_of_line();
    }
    else {
      if (strncasecmp(input_line_pointer, "$text$", 6) == 0) {
	input_line_pointer += 6;
	sd_chain = is_defined_space("$TEXT$");
	if ( sd_chain == NULL )
	  sd_chain = pa_parse_space_stmt("$TEXT$",1);
        else if ( sd_chain->sd_defined == 0 )
	  sd_chain = pa_parse_space_stmt("$TEXT$",0);

	current_space = sd_chain;
	SPACE_DEFINED(current_space) = 1;
	current_space->sd_defined = 1;
#ifdef OBJ_SOM
	if ( now_seg != SEG_TEXT )   /* no need to align if we are already there */
#else
	if ( now_seg != text_section )/* no need to align if we are already there */
#endif
	  pa_align_subseg(now_seg,now_subseg);

#ifdef OBJ_SOM
	subseg_new (SEG_TEXT, sd_chain->sd_last_subseg);
	current_subspace = pa_subsegment_to_subspace(SEG_TEXT,
						     sd_chain->sd_last_subseg);
#else
	subseg_new ((char *)text_section->name, sd_chain->sd_last_subseg);
	current_subspace = pa_subsegment_to_subspace(text_section,
						     sd_chain->sd_last_subseg);
#endif
	demand_empty_rest_of_line();
	return;
      }
      if (strncasecmp(input_line_pointer, "$private$", 9) == 0) {
	input_line_pointer += 9;
	sd_chain = is_defined_space("$PRIVATE$");
	if ( sd_chain == NULL )
	  sd_chain = pa_parse_space_stmt("$PRIVATE$",1);
        else if ( sd_chain->sd_defined == 0 )
	  sd_chain = pa_parse_space_stmt("$PRIVATE$",0);

	current_space = sd_chain;
	SPACE_DEFINED(current_space) = 1;
	current_space->sd_defined = 1;
#ifdef OBJ_SOM
	if ( now_seg != SEG_DATA )   /* no need to align if we are already there */
#else
	if ( now_seg != data_section )/* no need to align if we are already there */
#endif
	  pa_align_subseg(now_seg,now_subseg);
#ifdef OBJ_SOM
	subseg_new (SEG_DATA, sd_chain->sd_last_subseg);
	current_subspace = pa_subsegment_to_subspace(SEG_DATA,
						     sd_chain->sd_last_subseg);
#else
	subseg_new ((char *)data_section->name, sd_chain->sd_last_subseg);
	current_subspace = pa_subsegment_to_subspace(data_section,
						     sd_chain->sd_last_subseg);
#endif
	demand_empty_rest_of_line();
	return;
      }
      if (strncasecmp(input_line_pointer,
		      GDB_DEBUG_SPACE_NAME,strlen(GDB_DEBUG_SPACE_NAME)) == 0) {
	input_line_pointer += strlen(GDB_DEBUG_SPACE_NAME);
        sd_chain = is_defined_space(GDB_DEBUG_SPACE_NAME);
	if ( sd_chain == NULL )
	  sd_chain = pa_parse_space_stmt(GDB_DEBUG_SPACE_NAME,1);
        else if ( sd_chain->sd_defined == 0 )
	  sd_chain = pa_parse_space_stmt(GDB_DEBUG_SPACE_NAME,0);

	current_space = sd_chain;
	SPACE_DEFINED(current_space) = 1;
	current_space->sd_defined = 1;
#ifdef OBJ_SOM
	if ( now_seg != SEG_GDB )   /* no need to align if we are already there */
	  pa_align_subseg(now_seg,now_subseg);
	subseg_new (SEG_GDB, sd_chain->sd_last_subseg);
	current_subspace = pa_subsegment_to_subspace(SEG_GDB,
						     sd_chain->sd_last_subseg);
#else
	if ( now_seg != gdb_section ) /* no need to align if we are already there */
	  pa_align_subseg(now_seg,now_subseg);
	subseg_new ((char *)gdb_section->name, sd_chain->sd_last_subseg);
	current_subspace = pa_subsegment_to_subspace(gdb_section,
						     sd_chain->sd_last_subseg);
#endif
	demand_empty_rest_of_line();
	return;
      }

      /* it could be a space specified by number */

      if ( (temp = pa_parse_number(&input_line_pointer)) >= 0 ) {
	if ( sd_chain = pa_find_space_by_number(temp) ) {
	  current_space = sd_chain;
	  SPACE_DEFINED(current_space) = 1;
	  current_space->sd_defined = 1;
	  if ( now_seg != sd_chain->sd_seg ) /* don't align if we're already there */
	    pa_align_subseg(now_seg,now_subseg);
#ifdef OBJ_SOM
	  subseg_new (sd_chain->sd_seg, sd_chain->sd_last_subseg);
#else
	  subseg_new ((char *)sd_chain->sd_seg->name, sd_chain->sd_last_subseg);
#endif
	  current_subspace = pa_subsegment_to_subspace(sd_chain->sd_seg,
						       sd_chain->sd_last_subseg);
	  demand_empty_rest_of_line();
	  return;
	}
      }

      /* not a number, attempt to create a new space */

      name = input_line_pointer;
      c = get_symbol_end();
      space_name[0] = 0x00;
      strcpy(space_name,name);
      *input_line_pointer = c;

      sd_chain = pa_parse_space_stmt(space_name,1);
      current_space = sd_chain;
      SPACE_DEFINED(current_space) = 1;
      current_space->sd_defined = 1;
      if ( now_seg != sd_chain->sd_seg ) /* don't align if we're already there */
	pa_align_subseg(now_seg,now_subseg);
#ifdef OBJ_SOM
      subseg_new (sd_chain->sd_seg, sd_chain->sd_last_subseg);
#else
      subseg_new ((char *)sd_chain->sd_seg->name, sd_chain->sd_last_subseg);
#endif
      current_subspace = pa_subsegment_to_subspace(sd_chain->sd_seg,
						   sd_chain->sd_last_subseg);
      demand_empty_rest_of_line();
    }
    return;
}

void pa_spnum()
{
    register char *name;
    register char c;
    register char *p;
    space_dict_chainS *space;

    name = input_line_pointer;
    c = get_symbol_end();
    space = is_defined_space(name);
    if ( space ) {
      p = frag_more (4);
      /* put bytes in right order. */
      md_number_to_chars (p,SPACE_SPNUM(space),4);
    }
    else
      as_warn("Undefined space: '%s' Assuming space number = 0.",name);

    *input_line_pointer = c;
    demand_empty_rest_of_line();
    return;
}

static
int is_power_of_2(value)
     int value;
{
  int shift;

  shift = 0;
  while ( ( 1 << shift ) != value && shift < 32 )
    shift++;

  if ( shift >= 32 )
    shift = 0;
  return shift;
}

void pa_subspace()
{
    register char *name;
    register char c;
    register char *p;
    register int temp;
    register symbolS *	symbolP;
    
    char loadable,code_only,common,dup_common,zero;
    char sort;
    int number;
    int i;
    int access;
    int space_index;
    int alignment;
    int quadrant;
    int subseg;
    space_dict_chainS *space;
    subspace_dict_chainS *ssd;
    subspace_dict_chainS *ssdCh;
    char *ss_name;
    int is_power_of_2();

    if ( within_procedure ) {
      as_bad("Can\'t change subspaces within a procedure definition. Ignored");
      ignore_rest_of_line();
    }
    else {
      name = input_line_pointer;
      c = get_symbol_end();
      space = pa_segment_to_space(now_seg);
      ssd = is_defined_subspace(name,space->sd_last_subseg);

      ss_name = xmalloc(strlen(name)+1);
      strcpy(ss_name,name);

      *input_line_pointer = c;

      /* load default values */
      sort = 0;
      access = 0x7f;
      loadable = 1;
      common = 0;
      dup_common = 0;
      code_only = 0;
      zero = 0;
      space_index = ~0;       /* filled in when the .o file is written */
      alignment = 0;    /* alignment=0 means no ALIGN= appeared on the .SUBSPA */
                        /* pseudo-op. The default will be the largest .ALIGN */
                        /* encountered (or 1 if no .ALIGN is encountered) */
      quadrant = 0;

      if ( ssd ) {
	if ( ssd->ssd_defined ) {
#ifdef OBJ_SOM
	  subseg_new(now_seg,ssd->ssd_subseg);
#else
	  /* subseg_new(now_seg->name,ssd->ssd_subseg); */
	  subseg_new((char *)ssd->ssd_seg->name,ssd->ssd_subseg);
#endif
	  if ( !is_end_of_statement() ) {
	    as_warn("Parameters of an existing subspace can\'t be modified");
	  }
	  demand_empty_rest_of_line();
	  return;
	}
	else {
	  ssd->ssd_defined = 1;
	}
      }
      else {
	/* a new subspace */
	/* load default values */
	i = 0;
	while ( pa_def_subspaces[i].name ) {
	  if ( strcasecmp(pa_def_subspaces[i].name,ss_name) == 0 ) {
	    loadable = pa_def_subspaces[i].loadable;
	    common = pa_def_subspaces[i].common;
	    dup_common = pa_def_subspaces[i].dup_common;
	    code_only = pa_def_subspaces[i].code_only;
	    zero = pa_def_subspaces[i].zero;
	    space_index = pa_def_subspaces[i].space_index;
	    /* alignment = pa_def_subspaces[i].alignment; */
	    alignment = 0;
	    quadrant = pa_def_subspaces[i].quadrant;
	    access = pa_def_subspaces[i].access;
	    sort = pa_def_subspaces[i].sort;
	    break;
	  }
	  i++;
	}
      }

      if ( !is_end_of_statement() ) {
	input_line_pointer++;
	while ( !is_end_of_statement() ) {
	  name = input_line_pointer;
	  c = get_symbol_end();
	  if ( (strncasecmp(name, "QUAD", 4) == 0) ) {
	    *input_line_pointer = c;
	    input_line_pointer++;
	    temp = get_absolute_expression ();
	    quadrant = temp;
	  }
	  else if ( (strncasecmp(name, "ALIGN", 5) == 0) ) {
	    *input_line_pointer = c;
	    input_line_pointer++;
	    temp = get_absolute_expression ();
	    alignment = temp;
	    if ( !is_power_of_2(alignment) ) {
	      as_bad("Alignment must be a power of 2");
	      alignment = 1;
	    }
	  }
	  else if ( (strncasecmp(name, "ACCESS",6) == 0) ) {
	    *input_line_pointer = c;
	    input_line_pointer++;
	    temp = get_absolute_expression ();
	    access = temp;
	  }
	  else if ( (strncasecmp(name, "SORT",4) == 0) ) {
	    *input_line_pointer = c;
	    input_line_pointer++;
	    temp = get_absolute_expression ();
	    sort = temp;
	  }
	  else if ( (strncasecmp(name, "CODE_ONLY",9) == 0) ) {
	    *input_line_pointer = c;
	    code_only = 1;
	  }
	  else if ( (strncasecmp(name, "UNLOADABLE",10) == 0) ) {
	    *input_line_pointer = c;
	    loadable = 0;
	  }
	  else if ( (strncasecmp(name, "COMMON",6) == 0) ) {
	    *input_line_pointer = c;
	    common = 1;
	  }
	  else if ( (strncasecmp(name, "DUP_COMM",8) == 0) ) {
	    *input_line_pointer = c;
	    dup_common = 1;
	  }
	  else if ( (strncasecmp(name, "ZERO",4) == 0) ) {
	    *input_line_pointer = c;
	    zero = 1;
	  }
	  else {
	    as_bad("Unrecognized .SUBSPACE argument");
	  }
	  if ( !is_end_of_statement() )
	    input_line_pointer++;
	}
      }
      space = pa_segment_to_space(now_seg);
      if ( ssd ) {
	current_subspace = update_subspace(ss_name,1,loadable,code_only,
					   common,dup_common,sort,zero,access,
					   space_index,alignment,quadrant,
					   ssd->ssd_subseg);
      }
      else {
	current_subspace = create_new_subspace(space,ss_name,1,loadable,code_only,
					       common,dup_common,zero,sort,
					       access,space_index,alignment,
					       quadrant,now_seg);
      }
      SUBSPACE_SUBSPACE_START(current_subspace) = pa_subspace_start(space,quadrant);

      demand_empty_rest_of_line();
#ifdef OBJ_SOM
      subseg_new (current_subspace->ssd_seg, current_subspace->ssd_subseg);
#else
      subseg_new ((char *)current_subspace->ssd_seg->name, current_subspace->ssd_subseg);
#endif
    }
    return;
}

/* For ELF, this function serves one purpose:  to setup the st_size	*/
/* field of STT_FUNC symbols.  To do this, we need to scan the 		*/
/* call_info structure list, determining st_size in one of two possible	*/
/* ways:								*/

/*	1. call_info->start_frag->fr_fix has the size of the fragment.	*/
/*	   This approach assumes that the function was built into a 	*/
/*	   single fragment.  This works for most cases, but might fail.	*/
/*	   For example, if there was a segment change in the middle of	*/
/*	   the function.						*/

/*	2. The st_size field is the difference in the addresses of the	*/
/*	   call_info->start_frag->fr_address field and the fr_address	*/
/*	   field of the next fragment with fr_type == rs_fill and	*/
/*	   fr_fix != 0.							*/

void tc_final_processing_hook()
{
	extern call_infoS *call_info_root;

	call_infoS *ciP;

	for ( ciP = call_info_root; ciP; ciP = ciP->ci_next ) {
		elf_symbol_type *esym = (elf_symbol_type *)ciP->start_symbol->bsym;
		esym->internal_elf_sym.st_size =
			ciP->end_symbol->bsym->value
				- ciP->start_symbol->bsym->value + 4;
	}
}
