| /* This is the machine dependent code of the Visium Assembler. |
| |
| Copyright (C) 2005-2021 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 this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, 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 = XNEW (arelent); |
| |
| reloc->sym_ptr_ptr = XNEW (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_section_alignment (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 |
| { |
| const 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}, |
| }; |
| |
| struct visium_long_option_table |
| { |
| const char *option; /* Substring to match. */ |
| const char *help; /* Help information. */ |
| int (*func) (const char *subopt); /* Function to decode sub-option. */ |
| const char *deprecated; /* If non-null, print this message. */ |
| }; |
| |
| static int |
| visium_parse_arch (const char *str) |
| { |
| unsigned int i; |
| |
| if (strlen (str) == 0) |
| { |
| as_bad ("missing architecture name `%s'", str); |
| return 0; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE (visium_archs); i++) |
| if (strcmp (visium_archs[i].name, str) == 0) |
| { |
| visium_arch = visium_archs[i].value; |
| return 1; |
| } |
| |
| as_bad ("unknown architecture `%s'\n", str); |
| return 0; |
| } |
| |
| static struct visium_long_option_table visium_long_opts[] = |
| { |
| {"mtune=", "<arch_name>\t assemble for architecture <arch name>", |
| visium_parse_arch, NULL}, |
| {NULL, NULL, NULL, NULL} |
| }; |
| |
| int |
| md_parse_option (int c, const 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[0] + 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. */ |
| bool |
| 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. */ |
| |
| const 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 mnemonic. */ |
| if (get_opcode (&opcode, &amode, &arch_flags, mnem) < 0) |
| { |
| as_bad ("Unknown instruction mnemonic `%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"); |
| |
| /* For the GR6, BRA insns must be aligned on 64-bit boundaries. */ |
| if (visium_arch == VISIUM_ARCH_GR6) |
| do_align (3, NULL, 0, 0); |
| |
| this_dest = r3; |
| condition_code = cc; |
| break; |
| |
| case mode_das: |
| /* register := register * 5-bit immediate/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; |
| } |