/* This is the machine dependent code of the Visium Assembler. Copyright (C) 2005-2015 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 2, or (at your option) any later version. GAS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GAS; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "as.h" #include "safe-ctype.h" #include "subsegs.h" #include "obstack.h" #include "opcode/visium.h" #include "elf/visium.h" #include "dwarf2dbg.h" #include "dw2gencfi.h" /* Relocations and fixups: There are two different cases where an instruction or data directive operand requires relocation, or fixup. 1. Relative branch instructions, take an 16-bit signed word offset. The formula for computing the offset is this: offset = (destination - pc) / 4 Branch instructions never branch to a label not declared locally, so the actual offset can always be computed by the assembler. However, we provide a relocation type to support this. 2. Load literal instructions, such as MOVIU, which take a 16-bit literal operand. The literal may be the top or bottom half of a 32-bit value computed by the assembler, or by the linker. We provide two relocation types here. 3. Data items (long, word and byte) preset with a value computed by the linker. */ /* This string holds the chars that always start a comment. If the pre-processor is disabled, these aren't very useful. The macro tc_comment_chars points to this. */ const char *visium_comment_chars = "!;"; /* 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 comments like this one will always work. */ const char line_comment_chars[] = "#!;"; const char line_separator_chars[] = ""; /* Chars that can be used to separate mantissa from exponent in floating point numbers. */ const char EXP_CHARS[] = "eE"; /* Chars that mean this number is a floating point constant, as in "0f12.456" or "0d1.2345e12". ...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. */ const char FLT_CHARS[] = "rRsSfFdDxXeE"; /* The size of a relocation record. */ const int md_reloc_size = 8; /* The architecture for which we are assembling. */ enum visium_arch_val { VISIUM_ARCH_DEF, VISIUM_ARCH_MCM24, VISIUM_ARCH_MCM, VISIUM_ARCH_GR6 }; static enum visium_arch_val visium_arch = VISIUM_ARCH_DEF; /* The opcode architecture for which we are assembling. In contrast to the previous one, this only determines which instructions are supported. */ static enum visium_opcode_arch_val visium_opcode_arch = VISIUM_OPCODE_ARCH_DEF; /* Flags to set in the ELF header e_flags field. */ static flagword visium_flags = 0; /* More than this number of nops in an alignment op gets a branch instead. */ static unsigned int nop_limit = 5; /* Translate internal representation of relocation info to BFD target format. */ arelent * tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp) { arelent *reloc; bfd_reloc_code_real_type code; reloc = (arelent *) xmalloc (sizeof (arelent)); reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; switch (fixp->fx_r_type) { case BFD_RELOC_8: case BFD_RELOC_16: case BFD_RELOC_32: case BFD_RELOC_8_PCREL: case BFD_RELOC_16_PCREL: case BFD_RELOC_32_PCREL: case BFD_RELOC_VISIUM_HI16: case BFD_RELOC_VISIUM_LO16: case BFD_RELOC_VISIUM_IM16: case BFD_RELOC_VISIUM_REL16: case BFD_RELOC_VISIUM_HI16_PCREL: case BFD_RELOC_VISIUM_LO16_PCREL: case BFD_RELOC_VISIUM_IM16_PCREL: case BFD_RELOC_VTABLE_INHERIT: case BFD_RELOC_VTABLE_ENTRY: code = fixp->fx_r_type; break; default: as_bad_where (fixp->fx_file, fixp->fx_line, "internal error: unknown relocation type %d (`%s')", fixp->fx_r_type, bfd_get_reloc_code_name (fixp->fx_r_type)); return 0; } reloc->howto = bfd_reloc_type_lookup (stdoutput, code); if (reloc->howto == 0) { as_bad_where (fixp->fx_file, fixp->fx_line, "internal error: can't export reloc type %d (`%s')", fixp->fx_r_type, bfd_get_reloc_code_name (code)); return 0; } /* Write the addend. */ if (reloc->howto->pc_relative == 0) reloc->addend = fixp->fx_addnumber; else reloc->addend = fixp->fx_offset; return reloc; } extern char *input_line_pointer; static void s_bss (int); static void visium_rdata (int); static void visium_update_parity_bit (char *); static char *parse_exp (char *, expressionS *); /* These are the back-ends for the various machine dependent pseudo-ops. */ void demand_empty_rest_of_line (void); static void s_bss (int ignore ATTRIBUTE_UNUSED) { /* We don't support putting frags in the BSS segment, we fake it by marking in_bss, then looking at s_skip for clues. */ subseg_set (bss_section, 0); demand_empty_rest_of_line (); } /* This table describes all the machine specific pseudo-ops the assembler has to support. The fields are: 1: Pseudo-op name without dot. 2: Function to call to execute this pseudo-op. 3: Integer arg to pass to the function. */ const pseudo_typeS md_pseudo_table[] = { {"bss", s_bss, 0}, {"skip", s_space, 0}, {"align", s_align_bytes, 0}, {"noopt", s_ignore, 0}, {"optim", s_ignore, 0}, {"rdata", visium_rdata, 0}, {"rodata", visium_rdata, 0}, {0, 0, 0} }; static void visium_rdata (int xxx) { char *save_line = input_line_pointer; static char section[] = ".rodata\n"; /* Just pretend this is .section .rodata */ input_line_pointer = section; obj_elf_section (xxx); input_line_pointer = save_line; } /* Align a section. */ valueT md_section_align (asection *seg, valueT addr) { int align = bfd_get_section_alignment (stdoutput, seg); return ((addr + (1 << align) - 1) & (-1 << align)); } void md_number_to_chars (char *buf, valueT val, int n) { number_to_chars_bigendian (buf, val, n); } symbolS * md_undefined_symbol (char *name ATTRIBUTE_UNUSED) { return 0; } /* The parse options. */ const char *md_shortopts = "m:"; struct option md_longopts[] = { {NULL, no_argument, NULL, 0} }; size_t md_longopts_size = sizeof (md_longopts); struct visium_option_table { char *option; /* Option name to match. */ char *help; /* Help information. */ int *var; /* Variable to change. */ int value; /* To what to change it. */ char *deprecated; /* If non-null, print this message. */ }; static struct visium_option_table visium_opts[] = { {NULL, NULL, NULL, 0, NULL} }; struct visium_arch_option_table { char *name; enum visium_arch_val value; }; static struct visium_arch_option_table visium_archs[] = { {"mcm24", VISIUM_ARCH_MCM24}, {"mcm", VISIUM_ARCH_MCM}, {"gr5", VISIUM_ARCH_MCM}, {"gr6", VISIUM_ARCH_GR6}, {NULL, 0} }; struct visium_long_option_table { char *option; /* Substring to match. */ char *help; /* Help information. */ int (*func) (char *subopt); /* Function to decode sub-option. */ char *deprecated; /* If non-null, print this message. */ }; static int visium_parse_arch (char *str) { struct visium_arch_option_table *opt; if (strlen (str) == 0) { as_bad ("missing architecture name `%s'", str); return 0; } for (opt = visium_archs; opt->name != NULL; opt++) if (strcmp (opt->name, str) == 0) { visium_arch = opt->value; return 1; } as_bad ("unknown architecture `%s'\n", str); return 0; } static struct visium_long_option_table visium_long_opts[] = { {"mtune=", "\t assemble for architecture ", visium_parse_arch, NULL}, {NULL, NULL, NULL, NULL} }; int md_parse_option (int c, char *arg) { struct visium_option_table *opt; struct visium_long_option_table *lopt; switch (c) { case 'a': /* Listing option. Just ignore these, we don't support additional ones. */ return 0; default: for (opt = visium_opts; opt->option != NULL; opt++) { if (c == opt->option[0] && ((arg == NULL && opt->option[1] == 0) || strcmp (arg, opt->option + 1) == 0)) { /* If the option is deprecated, tell the user. */ if (opt->deprecated != NULL) as_tsktsk ("option `-%c%s' is deprecated: %s", c, arg ? arg : "", opt->deprecated); if (opt->var != NULL) *opt->var = opt->value; return 1; } } for (lopt = visium_long_opts; lopt->option != NULL; lopt++) { /* These options are expected to have an argument. */ if (c == lopt->option[0] && arg != NULL && strncmp (arg, lopt->option + 1, strlen (lopt->option + 1)) == 0) { /* If the option is deprecated, tell the user. */ if (lopt->deprecated != NULL) as_tsktsk ("option `-%c%s' is deprecated: %s", c, arg, lopt->deprecated); /* Call the sup-option parser. */ return lopt->func (arg + strlen (lopt->option) - 1); } } return 0; } return 1; } void md_show_usage (FILE * fp) { struct visium_option_table *opt; struct visium_long_option_table *lopt; fprintf (fp, " Visium-specific assembler options:\n"); for (opt = visium_opts; opt->option != NULL; opt++) if (opt->help != NULL) fprintf (fp, " -%-23s%s\n", opt->option, opt->help); for (lopt = visium_long_opts; lopt->option != NULL; lopt++) if (lopt->help != NULL) fprintf (fp, " -%s%s\n", lopt->option, lopt->help); } /* Interface to relax_segment. */ /* Return the estimate of the size of a machine dependent frag before any relaxing is done. It may also create any necessary relocations. */ int md_estimate_size_before_relax (fragS * fragP, segT segment ATTRIBUTE_UNUSED) { fragP->fr_var = 4; return 4; } /* Get the address of a symbol during relaxation. From tc-arm.c. */ static addressT relaxed_symbol_addr (fragS *fragp, long stretch) { fragS *sym_frag; addressT addr; symbolS *sym; sym = fragp->fr_symbol; sym_frag = symbol_get_frag (sym); know (S_GET_SEGMENT (sym) != absolute_section || sym_frag == &zero_address_frag); addr = S_GET_VALUE (sym) + fragp->fr_offset; /* If frag has yet to be reached on this pass, assume it will move by STRETCH just as we did. If this is not so, it will be because some frag between grows, and that will force another pass. */ if (stretch != 0 && sym_frag->relax_marker != fragp->relax_marker) { fragS *f; /* Adjust stretch for any alignment frag. Note that if have been expanding the earlier code, the symbol may be defined in what appears to be an earlier frag. FIXME: This doesn't handle the fr_subtype field, which specifies a maximum number of bytes to skip when doing an alignment. */ for (f = fragp; f != NULL && f != sym_frag; f = f->fr_next) { if (f->fr_type == rs_align || f->fr_type == rs_align_code) { if (stretch < 0) stretch = - ((- stretch) & ~ ((1 << (int) f->fr_offset) - 1)); else stretch &= ~ ((1 << (int) f->fr_offset) - 1); if (stretch == 0) break; } } if (f != NULL) addr += stretch; } return addr; } /* Relax a machine dependent frag. This returns the amount by which the current size of the frag should change. */ int visium_relax_frag (asection *sec, fragS *fragP, long stretch) { int old_size, new_size; addressT addr; /* We only handle relaxation for the BRR instruction. */ gas_assert (fragP->fr_subtype == mode_ci); if (!S_IS_DEFINED (fragP->fr_symbol) || sec != S_GET_SEGMENT (fragP->fr_symbol) || S_IS_WEAK (fragP->fr_symbol)) return 0; old_size = fragP->fr_var; addr = relaxed_symbol_addr (fragP, stretch); /* If the target is the address of the instruction, we'll insert a NOP. */ if (addr == fragP->fr_address + fragP->fr_fix) new_size = 8; else new_size = 4; fragP->fr_var = new_size; return new_size - old_size; } /* Convert a machine dependent frag. */ void md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED, fragS * fragP) { char *buf = fragP->fr_literal + fragP->fr_fix; expressionS exp; fixS *fixP; /* We only handle relaxation for the BRR instruction. */ gas_assert (fragP->fr_subtype == mode_ci); /* Insert the NOP if requested. */ if (fragP->fr_var == 8) { memcpy (buf + 4, buf, 4); memset (buf, 0, 4); fragP->fr_fix += 4; } exp.X_op = O_symbol; exp.X_add_symbol = fragP->fr_symbol; exp.X_add_number = fragP->fr_offset; /* Now we can create the relocation at the correct offset. */ fixP = fix_new_exp (fragP, fragP->fr_fix, 4, &exp, 1, BFD_RELOC_VISIUM_REL16); fixP->fx_file = fragP->fr_file; fixP->fx_line = fragP->fr_line; fragP->fr_fix += 4; fragP->fr_var = 0; } /* The location from which a PC relative jump should be calculated, given a PC relative jump reloc. */ long visium_pcrel_from_section (fixS *fixP, segT sec) { if (fixP->fx_addsy != (symbolS *) NULL && (!S_IS_DEFINED (fixP->fx_addsy) || S_GET_SEGMENT (fixP->fx_addsy) != sec)) { /* The symbol is undefined (or is defined but not in this section). Let the linker figure it out. */ return 0; } /* Return the address of the instruction. */ return fixP->fx_where + fixP->fx_frag->fr_address; } /* Indicate whether a fixup against a locally defined symbol should be adjusted to be against the section symbol. */ bfd_boolean visium_fix_adjustable (fixS *fix) { /* We need the symbol name for the VTABLE entries. */ return (fix->fx_r_type != BFD_RELOC_VTABLE_INHERIT && fix->fx_r_type != BFD_RELOC_VTABLE_ENTRY); } /* Update the parity bit of the 4-byte instruction in BUF. */ static void visium_update_parity_bit (char *buf) { int p1 = (buf[0] & 0x7f) ^ buf[1] ^ buf[2] ^ buf[3]; int p2 = 0; int i; for (i = 1; i <= 8; i++) { p2 ^= (p1 & 1); p1 >>= 1; } buf[0] = (buf[0] & 0x7f) | ((p2 << 7) & 0x80); } /* This is called from HANDLE_ALIGN in write.c. Fill in the contents of an rs_align_code fragment. */ void visium_handle_align (fragS *fragP) { valueT count = fragP->fr_next->fr_address - (fragP->fr_address + fragP->fr_fix); valueT fix = count & 3; char *p = fragP->fr_literal + fragP->fr_fix; if (fix) { memset (p, 0, fix); p += fix; count -= fix; fragP->fr_fix += fix; } if (count == 0) return; fragP->fr_var = 4; if (count > 4 * nop_limit && count <= 131068) { struct frag *rest; /* Make a branch, then follow with nops. Insert another frag to handle the nops. */ md_number_to_chars (p, 0x78000000 + (count >> 2), 4); visium_update_parity_bit (p); rest = xmalloc (SIZEOF_STRUCT_FRAG + 4); memcpy (rest, fragP, SIZEOF_STRUCT_FRAG); fragP->fr_next = rest; rest->fr_address += rest->fr_fix + 4; rest->fr_fix = 0; /* If we leave the next frag as rs_align_code we'll come here again, resulting in a bunch of branches rather than a branch followed by nops. */ rest->fr_type = rs_align; p = rest->fr_literal; } memset (p, 0, 4); } /* Apply a fixS to the frags, now that we know the value it ought to hold. */ void md_apply_fix (fixS * fixP, valueT * value, segT segment) { char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; offsetT val; long insn; val = *value; gas_assert (fixP->fx_r_type < BFD_RELOC_UNUSED); /* Remember value for tc_gen_reloc. */ fixP->fx_addnumber = val; /* Since DIFF_EXPR_OK is defined, .-foo gets turned into PC relative relocs. If this has happened, a non-PC relative reloc must be reinstalled with its PC relative version here. */ if (fixP->fx_pcrel) { switch (fixP->fx_r_type) { case BFD_RELOC_8: fixP->fx_r_type = BFD_RELOC_8_PCREL; break; case BFD_RELOC_16: fixP->fx_r_type = BFD_RELOC_16_PCREL; break; case BFD_RELOC_32: fixP->fx_r_type = BFD_RELOC_32_PCREL; break; case BFD_RELOC_VISIUM_HI16: fixP->fx_r_type = BFD_RELOC_VISIUM_HI16_PCREL; break; case BFD_RELOC_VISIUM_LO16: fixP->fx_r_type = BFD_RELOC_VISIUM_LO16_PCREL; break; case BFD_RELOC_VISIUM_IM16: fixP->fx_r_type = BFD_RELOC_VISIUM_IM16_PCREL; break; default: break; } } /* If this is a data relocation, just output VAL. */ switch (fixP->fx_r_type) { case BFD_RELOC_8: case BFD_RELOC_8_PCREL: md_number_to_chars (buf, val, 1); break; case BFD_RELOC_16: case BFD_RELOC_16_PCREL: md_number_to_chars (buf, val, 2); break; case BFD_RELOC_32: case BFD_RELOC_32_PCREL: md_number_to_chars (buf, val, 4); break; case BFD_RELOC_VTABLE_INHERIT: case BFD_RELOC_VTABLE_ENTRY: fixP->fx_done = 0; break; default: /* It's a relocation against an instruction. */ insn = bfd_getb32 ((unsigned char *) buf); switch (fixP->fx_r_type) { case BFD_RELOC_VISIUM_REL16: if (fixP->fx_addsy == NULL || (S_IS_DEFINED (fixP->fx_addsy) && S_GET_SEGMENT (fixP->fx_addsy) == segment)) { if (val > 0x1fffc || val < -0x20000) as_bad_where (fixP->fx_file, fixP->fx_line, "16-bit word displacement out of range: value = %d", (int) val); val = (val >> 2); insn = (insn & 0xffff0000) | (val & 0x0000ffff); } break; case BFD_RELOC_VISIUM_HI16: case BFD_RELOC_VISIUM_HI16_PCREL: if (fixP->fx_addsy == NULL) insn = (insn & 0xffff0000) | ((val >> 16) & 0x0000ffff); break; case BFD_RELOC_VISIUM_LO16: case BFD_RELOC_VISIUM_LO16_PCREL: if (fixP->fx_addsy == NULL) insn = (insn & 0xffff0000) | (val & 0x0000ffff); break; case BFD_RELOC_VISIUM_IM16: case BFD_RELOC_VISIUM_IM16_PCREL: if (fixP->fx_addsy == NULL) { if ((val & 0xffff0000) != 0) as_bad_where (fixP->fx_file, fixP->fx_line, "16-bit immediate out of range: value = %d", (int) val); insn = (insn & 0xffff0000) | val; } break; case BFD_RELOC_NONE: default: as_bad_where (fixP->fx_file, fixP->fx_line, "bad or unhandled relocation type: 0x%02x", fixP->fx_r_type); break; } bfd_putb32 (insn, (unsigned char *) buf); visium_update_parity_bit (buf); break; } /* Are we finished with this relocation now? */ if (fixP->fx_addsy == NULL) fixP->fx_done = 1; } char * parse_exp (char *s, expressionS * op) { char *save = input_line_pointer; char *new; if (!s) { return s; } input_line_pointer = s; expression (op); new = input_line_pointer; input_line_pointer = save; return new; } /* If the given string is a Visium opcode mnemonic return the code otherwise return -1. Use binary chop to find matching entry. */ static int get_opcode (int *code, enum addressing_mode *mode, char *flags, char *mnem) { int l = 0; int r = sizeof (opcode_table) / sizeof (struct opcode_entry) - 1; do { int mid = (l + r) / 2; int ans = strcmp (mnem, opcode_table[mid].mnem); if (ans < 0) r = mid - 1; else if (ans > 0) l = mid + 1; else { *code = opcode_table[mid].code; *mode = opcode_table[mid].mode; *flags = opcode_table[mid].flags; return 0; } } while (l <= r); return -1; } /* This function is called when the assembler starts up. It is called after the options have been parsed and the output file has been opened. */ void md_begin (void) { switch (visium_arch) { case VISIUM_ARCH_DEF: break; case VISIUM_ARCH_MCM24: visium_opcode_arch = VISIUM_OPCODE_ARCH_GR5; visium_flags |= EF_VISIUM_ARCH_MCM24; break; case VISIUM_ARCH_MCM: visium_opcode_arch = VISIUM_OPCODE_ARCH_GR5; visium_flags |= EF_VISIUM_ARCH_MCM; break; case VISIUM_ARCH_GR6: visium_opcode_arch = VISIUM_OPCODE_ARCH_GR6; visium_flags |= EF_VISIUM_ARCH_MCM | EF_VISIUM_ARCH_GR6; nop_limit = 2; break; default: gas_assert (0); } bfd_set_private_flags (stdoutput, visium_flags); } /* 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 (int type, char *litP, int *sizeP) { int i, prec; LITTLENUM_TYPE words[MAX_LITTLENUMS]; char *t; 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); if (target_big_endian) { for (i = 0; i < prec; i++) { md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE)); litP += sizeof (LITTLENUM_TYPE); } } else { for (i = prec - 1; i >= 0; i--) { md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE)); litP += sizeof (LITTLENUM_TYPE); } } return 0; } static inline char * skip_space (char *s) { while (*s == ' ' || *s == '\t') ++s; return s; } static int parse_gen_reg (char **sptr, int *rptr) { char *s = skip_space (*sptr); char buf[10]; int cnt; int l, r; cnt = 0; memset (buf, '\0', 10); while ((ISALNUM (*s)) && cnt < 10) buf[cnt++] = TOLOWER (*s++); l = 0; r = sizeof (gen_reg_table) / sizeof (struct reg_entry) - 1; do { int mid = (l + r) / 2; int ans = strcmp (buf, gen_reg_table[mid].name); if (ans < 0) r = mid - 1; else if (ans > 0) l = mid + 1; else { *rptr = gen_reg_table[mid].code; *sptr = s; return 0; } } while (l <= r); return -1; } static int parse_fp_reg (char **sptr, int *rptr) { char *s = skip_space (*sptr); char buf[10]; int cnt; int l, r; cnt = 0; memset (buf, '\0', 10); while ((ISALNUM (*s)) && cnt < 10) buf[cnt++] = TOLOWER (*s++); l = 0; r = sizeof (fp_reg_table) / sizeof (struct reg_entry) - 1; do { int mid = (l + r) / 2; int ans = strcmp (buf, fp_reg_table[mid].name); if (ans < 0) r = mid - 1; else if (ans > 0) l = mid + 1; else { *rptr = fp_reg_table[mid].code; *sptr = s; return 0; } } while (l <= r); return -1; } static int parse_cc (char **sptr, int *rptr) { char *s = skip_space (*sptr); char buf[10]; int cnt; int l, r; cnt = 0; memset (buf, '\0', 10); while ((ISALNUM (*s)) && cnt < 10) buf[cnt++] = TOLOWER (*s++); l = 0; r = sizeof (cc_table) / sizeof (struct cc_entry) - 1; do { int mid = (l + r) / 2; int ans = strcmp (buf, cc_table[mid].name); if (ans < 0) r = mid - 1; else if (ans > 0) l = mid + 1; else { *rptr = cc_table[mid].code; *sptr = s; return 0; } } while (l <= r); return -1; } /* Previous dest is the destination register number of the instruction before the current one. */ static int previous_dest = 0; static int previous_mode = 0; static int condition_code = 0; static int this_dest = 0; static int this_mode = 0; /* This is the main function in this file. It takes a line of assembly language source code and assembles it. Note, labels and pseudo ops have already been removed, so too has leading white space. */ void md_assemble (char *str0) { char *str = str0; int cnt; char mnem[10]; int opcode; enum addressing_mode amode; char arch_flags; int ans; char *output; int reloc = 0; relax_substateT relax = 0; expressionS e1; int r1, r2, r3; int cc; int indx; /* Initialize the expression. */ e1.X_op = O_absent; /* Initialize destination register. If the instruction we just looked at is in the delay slot of an unconditional branch, then there is no index hazard. */ if ((previous_mode == mode_cad || previous_mode == mode_ci) && condition_code == 15) this_dest = 0; previous_dest = this_dest; previous_mode = this_mode; this_dest = 0; /* Drop leading whitespace (probably not required). */ while (*str == ' ') str++; /* Get opcode mnemonic and make sure it's in lower case. */ cnt = 0; memset (mnem, '\0', 10); while ((ISALNUM (*str) || *str == '.' || *str == '_') && cnt < 10) mnem[cnt++] = TOLOWER (*str++); /* Look up mnemonic in opcode table, and get the code, the instruction format, and the flags that indicate which family members support this mnenonic. */ if (get_opcode (&opcode, &amode, &arch_flags, mnem) < 0) { as_bad ("Unknown instruction mnenonic `%s'", mnem); return; } if ((VISIUM_OPCODE_ARCH_MASK (visium_opcode_arch) & arch_flags) == 0) { as_bad ("Architecture mismatch on `%s'", mnem); return; } this_mode = amode; switch (amode) { case mode_d: /* register := Example: readmda r1 */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("Dest register required"); return; } opcode |= (r1 << 10); this_dest = r1; break; case mode_a: /* op= register Example: asld r1 */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("SourceA register required"); return; } opcode |= (r1 << 16); break; case mode_ab: /* register * register Example: mults r1,r2 */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("SourceA register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_gen_reg (&str, &r2); if (ans < 0) { as_bad ("SourceB register required"); return; } opcode |= (r1 << 16) | (r2 << 4); } else { as_bad ("SourceB register required"); return; } break; case mode_da: /* register := register Example: extb.l r1,r2 */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("Dest register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_gen_reg (&str, &r2); if (ans < 0) { as_bad ("SourceA register required"); return; } opcode |= (r1 << 10) | (r2 << 16); } else { as_bad ("SourceB register required"); return; } this_dest = r1; break; case mode_dab: /* register := register * register Example: add.l r1,r2,r3 */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("Dest register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_gen_reg (&str, &r2); if (ans < 0) { as_bad ("SourceA register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_gen_reg (&str, &r3); if (ans < 0) { as_bad ("SourceB register required"); return; } /* Got three regs, assemble instruction. */ opcode |= (r1 << 10) | (r2 << 16) | (r3 << 4); } else { as_bad ("SourceA register required"); return; } } else { as_bad ("Dest register required"); return; } this_dest = r1; break; case mode_iab: /* 5-bit immediate * register * register Example: eamwrite 3,r1,r2 */ str = parse_exp (str, &e1); str = skip_space (str); if (e1.X_op != O_absent && *str == ',') { int eam_op = e1.X_add_number; str = skip_space (str + 1); ans = parse_gen_reg (&str, &r2); if (ans < 0) { as_bad ("SourceA register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_gen_reg (&str, &r3); if (ans < 0) { as_bad ("SourceB register required"); return; } /* Got three operands, assemble instruction. */ if (eam_op < 0 || eam_op > 31) { as_bad ("eam_op out of range"); } opcode |= ((eam_op & 0x1f) << 10) | (r2 << 16) | (r3 << 4); } } else { as_bad ("EAM_OP required"); return; } break; case mode_0ab: /* zero * register * register Example: cmp.l r1,r2 */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("SourceA register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_gen_reg (&str, &r2); if (ans < 0) { as_bad ("SourceB register required"); return; } opcode |= (r1 << 16) | (r2 << 4); } else { as_bad ("SourceB register required"); return; } break; case mode_da0: /* register * register * zero Example: move.l r1,r2 */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("Dest register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_gen_reg (&str, &r2); if (ans < 0) { as_bad ("SourceA register required"); return; } opcode |= (r1 << 10) | (r2 << 16); } else { as_bad ("SourceA register required"); return; } this_dest = r1; break; case mode_cad: /* condition * register * register Example: bra tr,r1,r2 */ ans = parse_cc (&str, &cc); if (ans < 0) { as_bad ("condition code required"); return; } str = skip_space (str); if (*str == ',') { str = skip_space (str + 1); ans = parse_gen_reg (&str, &r2); if (ans < 0) { as_bad ("SourceA register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_gen_reg (&str, &r3); if (ans < 0) { as_bad ("Dest register required"); return; } /* Got three operands, assemble instruction. */ opcode |= (cc << 27) | (r2 << 16) | (r3 << 10); } else { as_bad ("Dest register required"); return; } } else { as_bad ("SourceA register required"); return; } if (previous_mode == mode_cad || previous_mode == mode_ci) as_bad ("branch instruction in delay slot"); this_dest = r3; condition_code = cc; break; case mode_das: /* register := register * 5-bit imediate/register shift count Example: asl.l r1,r2,4 */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("Dest register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_gen_reg (&str, &r2); if (ans < 0) { as_bad ("SourceA register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_gen_reg (&str, &r3); if (ans == 0) { opcode |= (r1 << 10) | (r2 << 16) | (r3 << 4); } else { str = parse_exp (str, &e1); if (e1.X_op == O_constant) { int imm = e1.X_add_number; if (imm < 0 || imm > 31) as_bad ("immediate value out of range"); opcode |= (r1 << 10) | (r2 << 16) | (1 << 9) | ((imm & 0x1f) << 4); } else { as_bad ("immediate operand required"); return; } } } } else { as_bad ("SourceA register required"); return; } this_dest = r1; break; case mode_di: /* register := 5-bit immediate Example: eamread r1,3 */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("Dest register required"); return; } str = skip_space (str); if (*str == ',') { str++; str = parse_exp (str, &e1); if (e1.X_op == O_constant) { int opnd2 = e1.X_add_number; if (opnd2 < 0 || opnd2 > 31) { as_bad ("immediate operand out of range"); return; } opcode |= (r1 << 10) | ((opnd2 & 0x1f) << 4); } else { as_bad ("immediate operand required"); return; } } else { as_bad ("immediate operand required"); return; } this_dest = r1; break; case mode_ir: /* 5-bit immediate * register, e.g. trace 1,r1 */ str = parse_exp (str, &e1); str = skip_space (str); if (e1.X_op == O_constant && *str == ',') { int opnd1 = e1.X_add_number; str = skip_space (str + 1); ans = parse_gen_reg (&str, &r2); if (ans < 0) { as_bad ("SourceA register required"); return; } /* Got two operands, assemble instruction. */ if (opnd1 < 0 || opnd1 > 31) { as_bad ("1st operand out of range"); } opcode |= ((opnd1 & 0x1f) << 10) | (r2 << 16); } else { as_bad ("Immediate operand required"); return; } break; case mode_ai: /* register *= 16-bit unsigned immediate Example: addi r1,123 */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("Dest register required"); return; } opcode |= (r1 << 16); str = skip_space (str); if (*str != ',') { as_bad ("immediate value missing"); return; } this_dest = r1; /* fall through... */ case mode_i: /* MOVIL/WRTL traditionally get an implicit "%l" applied to their immediate value. For other opcodes, unless the immediate value is decorated with "%u" or "%l" it must be in the range 0 .. 65535. */ if ((opcode & 0x7fe00000) == 0x04800000 || (opcode & 0x7fe00000) == 0x05000000) reloc = BFD_RELOC_VISIUM_LO16; else reloc = BFD_RELOC_VISIUM_IM16; str = skip_space (str + 1); if (*str == '%') { if (str[1] == 'u') reloc = BFD_RELOC_VISIUM_HI16; else if (str[1] == 'l') reloc = BFD_RELOC_VISIUM_LO16; else { as_bad ("bad char after %%"); return; } str += 2; } str = parse_exp (str, &e1); if (e1.X_op != O_absent) { if (e1.X_op == O_constant) { int imm = e1.X_add_number; if (reloc == BFD_RELOC_VISIUM_HI16) opcode |= ((imm >> 16) & 0xffff); else if (reloc == BFD_RELOC_VISIUM_LO16) opcode |= (imm & 0xffff); else { if (imm < 0 || imm > 0xffff) as_bad ("immediate value out of range"); opcode |= (imm & 0xffff); } /* No relocation is needed. */ reloc = 0; } } else { as_bad ("immediate value missing"); return; } break; case mode_bax: /* register * register * 5-bit immediate, SourceB * SourceA * Index Examples write.l (r1),r2 write.l 3(r1),r2 */ str = skip_space (str); indx = 0; if (*str != '(') { str = parse_exp (str, &e1); if (e1.X_op == O_constant) { indx = e1.X_add_number; if (indx < 0 || indx > 31) { as_bad ("Index out of range"); return; } } else { as_bad ("Index(SourceA) required"); return; } } str = skip_space (str); if (*str != '(') { as_bad ("Index(SourceA) required"); return; } str = skip_space (str + 1); ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("SourceA register required"); return; } str = skip_space (str); if (*str != ')') { as_bad ("(SourceA) required"); return; } str = skip_space (str + 1); if (*str == ',') { str = skip_space (str + 1); ans = parse_gen_reg (&str, &r2); if (ans < 0) { as_bad ("SourceB register required"); return; } } else { as_bad ("SourceB register required"); return; } opcode |= (r1 << 16) | (r2 << 4) | ((indx & 0x1f) << 10); if (indx != 0 && previous_mode == mode_cad) { /* We're in a delay slot. If the base reg is the destination of the branch, then issue an error message. Otherwise it is safe to use the base and index. */ if (previous_dest != 0 && r1 == previous_dest) { as_bad ("base register not ready"); return; } } else if (previous_dest != 0 && r1 == previous_dest && (visium_arch == VISIUM_ARCH_MCM || visium_arch == VISIUM_ARCH_MCM24 || (visium_arch == VISIUM_ARCH_DEF && indx != 0))) { as_warn ("base register not ready, NOP inserted."); /* Insert a NOP before the write instruction. */ output = frag_more (4); memset (output, 0, 4); } break; case mode_dax: /* register := register * 5-bit immediate Examples: read.b r1,(r2) read.w r1,3(r2) */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("Dest register required"); return; } str = skip_space (str); if (*str != ',') { as_bad ("SourceA required"); return; } str = skip_space (str + 1); indx = 0; if (*str != '(') { str = parse_exp (str, &e1); if (e1.X_op == O_constant) { indx = e1.X_add_number; if (indx < 0 || indx > 31) { as_bad ("Index out of range"); return; } } else { as_bad ("Immediate 0 to 31 required"); return; } } if (*str != '(') { as_bad ("(SourceA) required"); return; } str++; ans = parse_gen_reg (&str, &r2); if (ans < 0) { as_bad ("SourceA register required"); return; } str = skip_space (str); if (*str != ')') { as_bad ("(SourceA) required"); return; } str++; opcode |= (r1 << 10) | (r2 << 16) | ((indx & 0x1f) << 4); this_dest = r1; if (indx != 0 && previous_mode == mode_cad) { /* We're in a delay slot. If the base reg is the destination of the branch, then issue an error message. Otherwise it is safe to use the base and index. */ if (previous_dest != 0 && r2 == previous_dest) { as_bad ("base register not ready"); return; } } else if (previous_dest != 0 && r2 == previous_dest && (visium_arch == VISIUM_ARCH_MCM || visium_arch == VISIUM_ARCH_MCM24 || (visium_arch == VISIUM_ARCH_DEF && indx != 0))) { as_warn ("base register not ready, NOP inserted."); /* Insert a NOP before the read instruction. */ output = frag_more (4); memset (output, 0, 4); } break; case mode_s: /* special mode Example: nop */ str = skip_space (str); break; case mode_ci: /* condition * 16-bit signed word displacement Example: brr L1 */ ans = parse_cc (&str, &cc); if (ans < 0) { as_bad ("condition code required"); return; } opcode |= (cc << 27); str = skip_space (str); if (*str == ',') { str = skip_space (str + 1); str = parse_exp (str, &e1); if (e1.X_op != O_absent) { if (e1.X_op == O_constant) { int imm = e1.X_add_number; if (imm < -32768 || imm > 32767) as_bad ("immediate value out of range"); /* The GR6 doesn't correctly handle a 0 displacement so we insert a NOP and change it to -1. */ if (imm == 0 && cc != 0 && visium_arch == VISIUM_ARCH_GR6) { output = frag_more (4); memset (output, 0, 4); imm = -1; } opcode |= (imm & 0xffff); } else if (e1.X_op == O_symbol) { /* The GR6 doesn't correctly handle a 0 displacement so the instruction requires relaxation. */ if (cc != 0 && visium_arch == VISIUM_ARCH_GR6) relax = amode; else reloc = BFD_RELOC_VISIUM_REL16; } else { as_bad ("immediate value missing"); return; } } else { as_bad ("immediate value missing"); return; } } else { as_bad ("immediate value missing"); return; } if (previous_mode == mode_cad || previous_mode == mode_ci) as_bad ("branch instruction in delay slot"); condition_code = cc; break; case mode_fdab: /* float := float * float Example fadd f4,f3,f2 */ ans = parse_fp_reg (&str, &r1); if (ans < 0) { as_bad ("floating point destination register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_fp_reg (&str, &r2); if (ans < 0) { as_bad ("floating point SourceA register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_fp_reg (&str, &r3); if (ans < 0) { as_bad ("floating point SourceB register required"); return; } /* Got 3 floating regs, assemble instruction. */ opcode |= (r1 << 10) | (r2 << 16) | (r3 << 4); } else { as_bad ("floating point SourceB register required"); return; } } else { as_bad ("floating point SourceA register required"); return; } break; case mode_ifdab: /* 4-bit immediate * float * float * float Example fpinst 10,f1,f2,f3 */ str = parse_exp (str, &e1); str = skip_space (str); if (e1.X_op != O_absent && *str == ',') { int finst = e1.X_add_number; str = skip_space (str + 1); ans = parse_fp_reg (&str, &r1); if (ans < 0) { as_bad ("floating point destination register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_fp_reg (&str, &r2); if (ans < 0) { as_bad ("floating point SourceA register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_fp_reg (&str, &r3); if (ans < 0) { as_bad ("floating point SourceB register required"); return; } /* Got immediate and 3 floating regs, assemble instruction. */ if (finst < 0 || finst > 15) as_bad ("finst out of range"); opcode |= ((finst & 0xf) << 27) | (r1 << 10) | (r2 << 16) | (r3 << 4); } else { as_bad ("floating point SourceB register required"); return; } } else { as_bad ("floating point SourceA register required"); return; } } else { as_bad ("finst missing"); return; } break; case mode_idfab: /* 4-bit immediate * register * float * float Example fpuread 4,r25,f2,f3 */ str = parse_exp (str, &e1); str = skip_space (str); if (e1.X_op != O_absent && *str == ',') { int finst = e1.X_add_number; str = skip_space (str + 1); ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("destination general register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_fp_reg (&str, &r2); if (ans < 0) { as_bad ("floating point SourceA register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_fp_reg (&str, &r3); if (ans < 0) { as_bad ("floating point SourceB register required"); return; } /* Got immediate and 3 floating regs, assemble instruction. */ if (finst < 0 || finst > 15) as_bad ("finst out of range"); opcode |= ((finst & 0xf) << 27) | (r1 << 10) | (r2 << 16) | (r3 << 4); } else { as_bad ("floating point SourceB register required"); return; } } else { as_bad ("floating point SourceA register required"); return; } } else { as_bad ("finst missing"); return; } break; case mode_fda: /* float := float Example fsqrt f4,f3 */ ans = parse_fp_reg (&str, &r1); if (ans < 0) { as_bad ("floating point destination register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_fp_reg (&str, &r2); if (ans < 0) { as_bad ("floating point source register required"); return; } /* Got 2 floating regs, assemble instruction. */ opcode |= (r1 << 10) | (r2 << 16); } else { as_bad ("floating point source register required"); return; } break; case mode_fdra: /* float := register Example fload f15,r6 */ ans = parse_fp_reg (&str, &r1); if (ans < 0) { as_bad ("floating point destination register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_gen_reg (&str, &r2); if (ans < 0) { as_bad ("SourceA general register required"); return; } /* Got 2 regs, assemble instruction. */ opcode |= (r1 << 10) | (r2 << 16); } else { as_bad ("SourceA general register required"); return; } break; case mode_rdfab: /* register := float * float Example fcmp r0,f4,f8 For the GR6, register must be r0 and can be omitted. */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { if (visium_opcode_arch == VISIUM_OPCODE_ARCH_GR5) { as_bad ("Dest general register required"); return; } r1 = 0; } else { if (r1 != 0 && visium_opcode_arch != VISIUM_OPCODE_ARCH_GR5) { as_bad ("FCMP/FCMPE can only use r0 as Dest register"); return; } str = skip_space (str); if (*str == ',') str++; else { as_bad ("floating point SourceA register required"); return; } } ans = parse_fp_reg (&str, &r2); if (ans < 0) { as_bad ("floating point SourceA register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_fp_reg (&str, &r3); if (ans < 0) { as_bad ("floating point SourceB register required"); return; } /* Got 3 regs, assemble instruction. */ opcode |= (r1 << 10) | (r2 << 16) | (r3 << 4); } this_dest = r1; break; case mode_rdfa: /* register := float Example fstore r5,f12 */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("Dest general register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_fp_reg (&str, &r2); if (ans < 0) { as_bad ("floating point source register required"); return; } /* Got 2 regs, assemble instruction. */ opcode |= (r1 << 10) | (r2 << 16); } else { as_bad ("floating point source register required"); return; } this_dest = r1; break; case mode_rrr: /* register register register, all sources and destinations Example: bmd r1,r2,r3 */ ans = parse_gen_reg (&str, &r1); if (ans < 0) { as_bad ("destination address register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_gen_reg (&str, &r2); if (ans < 0) { as_bad ("source address register required"); return; } str = skip_space (str); if (*str == ',') { str++; ans = parse_gen_reg (&str, &r3); if (ans < 0) { as_bad ("count register required"); return; } /* We insist on three registers but the opcode can only use r1,r2,r3. */ if (r1 != 1 || r2 != 2 || r3 != 3) { as_bad ("BMI/BMD can only use format op r1,r2,r3"); return; } /* Opcode is unmodified by what comes out of the table. */ } else { as_bad ("register required"); return; } } else { as_bad ("register required"); return; } this_dest = r1; break; default: break; } if (relax) output = frag_var (rs_machine_dependent, 8, 4, relax, e1.X_add_symbol, e1.X_add_number, NULL); else output = frag_more (4); /* Build the 32-bit instruction in a host-endian-neutral fashion. */ output[0] = (opcode >> 24) & 0xff; output[1] = (opcode >> 16) & 0xff; output[2] = (opcode >> 8) & 0xff; output[3] = (opcode >> 0) & 0xff; if (relax) /* The size of the instruction is unknown, so tie the debug info to the start of the instruction. */ dwarf2_emit_insn (0); else { if (reloc) fix_new_exp (frag_now, output - frag_now->fr_literal, 4, &e1, reloc == BFD_RELOC_VISIUM_REL16, reloc); else visium_update_parity_bit (output); dwarf2_emit_insn (4); } if (*str != '\0') as_bad ("junk after instruction"); } void visium_cfi_frame_initial_instructions (void) { /* The CFA is in SP on function entry. */ cfi_add_CFA_def_cfa (23, 0); } int visium_regname_to_dw2regnum (char *regname) { if (!regname[0]) return -1; if (regname[0] == 'f' && regname[1] == 'p' && !regname[2]) return 22; if (regname[0] == 's' && regname[1] == 'p' && !regname[2]) return 23; if (regname[0] == 'm' && regname[1] == 'd' && !regname[3]) switch (regname[2]) { case 'b': return 32; case 'a': return 33; case 'c': return 34; default : return -1; } if (regname[0] == 'f' || regname[0] == 'r') { char *p; unsigned int regnum = strtoul (regname + 1, &p, 10); if (*p) return -1; if (regnum >= (regname[0] == 'f' ? 16 : 32)) return -1; if (regname[0] == 'f') regnum += 35; return regnum; } return -1; }