/* 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 #include #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 (); 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}, {"byte", pa_cons, 1}, {"BYTE", pa_cons, 1}, {"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}, {"data", pa_data, 0}, {"DATA", pa_data, 0}, {"desc", pa_desc, 0}, {"DESC", pa_desc, 0}, {"double", pa_float_cons, 'd'}, {"DOUBLE", pa_float_cons, 'd'}, {"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}, {"fill", pa_fill, 0}, {"FILL", pa_fill, 0}, {"float", pa_float_cons, 'f'}, {"FLOAT", pa_float_cons, 'f'}, {"half", pa_cons, 2}, {"HALF", pa_cons, 2}, {"import", pa_import, 0}, {"IMPORT", pa_import, 0}, {"int", pa_cons, 4}, {"INT", pa_cons, 4}, {"label", pa_label, 0}, {"LABEL", pa_label, 0}, {"lcomm", pa_lcomm, 0}, {"LCOMM", pa_lcomm, 0}, {"leave", pa_leave, 0}, {"LEAVE", pa_leave, 0}, {"long", pa_cons, 4}, {"LONG", pa_cons, 4}, {"lsym", pa_lsym, 0}, {"LSYM", pa_lsym, 0}, {"octa", pa_big_cons, 16}, {"OCTA", pa_big_cons, 16}, {"org", pa_origin, 0}, {"ORG", pa_origin, 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}, {"quad", pa_big_cons, 8}, {"QUAD", pa_big_cons, 8}, {"reg", pa_equ, 1}, /* very similar to .equ */ {"REG", pa_equ, 1}, /* very similar to .equ */ {"short", pa_cons, 2}, {"SHORT", pa_cons, 2}, {"single", pa_float_cons, 'f'}, {"SINGLE", pa_float_cons, 'f'}, {"space", pa_space, 0}, {"SPACE", pa_space, 0}, {"spnum", pa_spnum, 0}, {"SPNUM", pa_spnum, 0}, {"string", pa_stringer, 0}, {"STRING", pa_stringer, 0}, {"stringz", pa_stringer, 1}, {"STRINGZ", pa_stringer, 1}, {"subspa", pa_subspace, 0}, {"SUBSPA", pa_subspace, 0}, {"text", pa_text, 0}, {"TEXT", pa_text, 0}, {"version", pa_version, 0}, {"VERSION", pa_version, 0}, {"word", pa_cons, 4}, {"WORD", pa_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, ".text", SUBSEG_CODE}, {"$DATA$", 0, 1, 0, 0, 0, 0, 24, 0x1f, 0, 8, 1, 1, ".data", SUBSEG_DATA}, {"$LIT$", 0, 1, 0, 0, 0, 0, 16, 0x2c, 0, 8, 0, 0, ".text", SUBSEG_LIT}, {"$BSS$", 0, 1, 0, 0, 0, 1, 80, 0x1f, 0, 8, 1, 1, ".bss", SUBSEG_BSS}, {"$UNWIND$", 0, 1, 0, 0, 0, 0, 64, 0x2c, 0, 4, 0, 0, ".hppa_unwind", SUBSEG_UNWIND}, {GDB_STRINGS, 0, 0, 0, 0, 0, 0, 254, 0x1f, 0, 4, 0, 2, ".stabstr", SUBSEG_GDB_STRINGS}, {GDB_SYMBOLS, 0, 0, 0, 0, 0, 0, 255, 0x1f, 0, 4, 0, 2, ".stab", 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, ".stab"}, {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. */ #ifdef BFD_ASSEMBLER bfd_reloc_code_real_type r_type; /* Relocation type */ #else int r_type; /* Relocation type */ #endif 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 (¬es, 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 */ } /* Parse a .byte, .word, .long expression for the HPPA. Called by cons via the TC_PARSE_CONS_EXPRESSION macro. */ static int hppa_field_selector; void parse_cons_expression_hppa (exp) expressionS *exp; { hppa_field_selector = pa_chk_field_selector (&input_line_pointer); expression (&exp); } /* This fix_new is called by cons via TC_CONS_FIX_NEW. hppa_field_selector is set by the parse_cons_expression_hppa. */ void cons_fix_new_hppa (frag, where, size, exp) fragS *frag; /* Which frag? */ int where; /* Where in that frag? */ int size; /* 1, 2 or 4 usually. */ expressionS *exp; /* Expression. */ { unsigned int reloc_type; if (is_DP_relative (*exp)) reloc_type = R_HPPA_GOTOFF; else if (is_complex (*exp)) reloc_type = R_HPPA_COMPLEX; else reloc_type = R_HPPA; if (hppa_field_selector != e_psel && hppa_field_selector != e_fsel) as_warn("Invalid field selector. Assuming F%%."); fix_new_hppa (frag, where, size exp->X_add_symbol, exp->X_subtract_symbol, exp->X_add_number, 0, reloc_type, hppa_field_selector, 32, 0, (char *) 0); } /* 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; void pa_spaces_begin (); /* forward declaration */ 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) { #else #if defined ( OBJ_ELF ) if (the_insn.reloc != R_HPPA_NONE) { #endif #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 if (is_PC_relative (the_insn.exp)) the_insn.reloc = R_HPPA_PCREL_CALL; else if (is_complex (the_insn.exp)) the_insn.reloc = R_HPPA_COMPLEX; 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 if (is_PC_relative (the_insn.exp)) the_insn.reloc = R_HPPA_PCREL_CALL; else if (is_complex (the_insn.exp)) the_insn.reloc = R_HPPA_COMPLEX; 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 if (is_PC_relative (the_insn.exp)) the_insn.reloc = R_HPPA_PCREL_CALL; else if (is_complex (the_insn.exp)) the_insn.reloc = R_HPPA_COMPLEX; 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 { if (is_complex (the_insn.exp)) the_insn.reloc = R_HPPA_COMPLEX_PCREL_CALL; else 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 { if (is_complex (the_insn.exp)) the_insn.reloc = R_HPPA_COMPLEX_PCREL_CALL; else 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 'z': /* 17 bit branch displacement (not pc-relative) */ #if defined(OBJ_ELF) the_insn.field_selector = pa_chk_field_selector (&s); #endif getExpression (s); the_insn.pcrel = 0; #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 { if (is_complex (the_insn.exp)) { the_insn.reloc = R_HPPA_COMPLEX_ABS_CALL; } else { the_insn.reloc = R_HPPA_ABS_CALL; } /* This could also be part of an instruction sequence of interest. If so, check to make sure that the previous instruction's fixup is appropriate. (ble, be instructions affect the reloc of immediately preceding ldil instructions.) */ if (strcasecmp (prev_str, "ldil") == 0 && prev_insn.exp.X_add_symbol == the_insn.exp.X_add_symbol && prev_insn.exp.X_subtract_symbol == the_insn.exp.X_subtract_symbol && prev_insn.exp.X_seg == the_insn.exp.X_seg && prev_insn.exp.X_add_number == the_insn.exp.X_add_number && prev_fix != NULL) prev_fix->fx_r_type = the_insn.reloc; 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=, 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; valueT 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; addressT 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. */ /* This may need additional work to make sure that SOM support is complete. */ #ifdef OBJ_ELF 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; static arelent *no_relocs = NULL; arelent **relocs; bfd_reloc_code_real_type **codes; int n_relocs; int i; if (fixp->fx_addsy == 0) return &no_relocs; 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; relocs = (arelent **) bfd_alloc_by_size_t (stdoutput, sizeof (arelent *) * 2); assert (relocs != 0); relocs[0] = unwind_reloc_entryP; relocs[1] = NULL; return relocs; } unwind_reloc_fixp_cnt++; unwind_reloc_entryP->addend = unwind_reloc_fixp_cnt / 2; return &no_relocs; } 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 */ codes = hppa_elf_gen_reloc_type (stdoutput, fixp->fx_r_type, hppa_fixp->fx_r_format, hppa_fixp->fx_r_field); for (n_relocs = 0; codes[n_relocs]; n_relocs++) ; relocs = (arelent **) bfd_alloc_by_size_t (stdoutput, sizeof (arelent *) * n_relocs + 1); assert (relocs != 0); reloc = (arelent *) bfd_alloc_by_size_t (stdoutput, sizeof (arelent) * n_relocs); if (n_relocs > 0) assert (reloc != 0); for (i = 0; i < n_relocs; i++) relocs[i] = &reloc[i]; relocs[n_relocs] = NULL; switch (fixp->fx_r_type) { case R_HPPA_COMPLEX: case R_HPPA_COMPLEX_PCREL_CALL: case R_HPPA_COMPLEX_ABS_CALL: assert (n_relocs == 5); for (i = 0; i < n_relocs; i++) { reloc[i].sym_ptr_ptr = NULL; reloc[i].address = 0; reloc[i].addend = 0; reloc[i].howto = bfd_reloc_type_lookup (stdoutput, *codes[i]); assert (reloc[i].howto && *codes[i] == reloc[i].howto->type); } reloc[0].sym_ptr_ptr = &fixp->fx_addsy->bsym; reloc[1].sym_ptr_ptr = &fixp->fx_subsy->bsym; reloc[4].address = fixp->fx_frag->fr_address + fixp->fx_where; if (fixp->fx_r_type == R_HPPA_COMPLEX) reloc[3].addend = fixp->fx_addnumber; else if (fixp->fx_r_type == R_HPPA_COMPLEX_PCREL_CALL || fixp->fx_r_type == R_HPPA_COMPLEX_ABS_CALL) reloc[1].addend = fixp->fx_addnumber; break; default: assert (n_relocs == 1); code = *codes[0]; reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym; reloc->howto = bfd_reloc_type_lookup (stdoutput, code); reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; reloc->addend = 0; /* default */ assert (reloc->howto && code == reloc->howto->type); /* 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; } break; } return relocs; } #else 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; if (fixp->fx_addsy == 0) return 0; assert (hppa_fixp != 0); assert (section != 0); 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 (stdoutput, 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. (Is there any?) */ switch (code) { default: reloc->addend = fixp->fx_addnumber; break; } return reloc; } #endif 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. */ valueT md_section_align (segment, size) asection *segment; valueT 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; addressT 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_1 (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 long buf_wd = bfd_get_32 (stdoutput, buf); unsigned char fmt = hppa_elf_insn2fmt (fixP->fx_r_type, buf_wd); 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; */ bfd_put_32 (stdoutput, bfd_get_32 (stdoutput, buf) & 0xffffc000, buf); 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; */ bfd_put_32 (stdoutput, bfd_get_32 (stdoutput, buf) & 0xffe00000, buf); 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; */ bfd_put_32 (stdoutput, bfd_get_32 (stdoutput, buf) & 0xffff800, buf); 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; */ bfd_put_32 (stdoutput, bfd_get_32 (stdoutput, buf) & 0xffffe002, buf); 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; */ bfd_put_32 (stdoutput, bfd_get_32 (stdoutput, buf) & 0xffe0e002, buf); 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 */ bfd_put_32 (stdoutput, 0, buf); /* 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_1() */ #ifdef BFD_ASSEMBLER int md_apply_fix (fixP, valp) fixS *fixP; long *valp; { md_apply_fix_1 (fixP, *valp); return 1; } #else void md_apply_fix (fixP, val) fixS *fixP; long val; { md_apply_fix_1 (fixP, val); } #endif /* 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 (".data", (subsegT) temp); #endif demand_empty_rest_of_line (); } void s_data1 () { #ifdef OBJ_SOM subseg_new (SEG_DATA, 1); #else subseg_new (".data", 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))); } pa_undefine_label (); 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: * *