📄 tc-i860.c
字号:
/* tc-i860.c -- Assembler for the Intel i860 architecture. Copyright 1989, 1992, 1993, 1994, 1995, 1998, 1999, 2000 Free Software Foundation, Inc. Brought back from the dead and completely reworked by Jason Eckhardt <jle@cygnus.com>. 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 <ctype.h>#include <stdio.h>#include <string.h>#include "as.h"#include "subsegs.h"#include "opcode/i860.h"#include "elf/i860.h"/* Defined by default since this is primarily a SVR4/860 assembler. However, I'm trying to leave the door open for Intel syntax. Of course, if full support for anything other than SVR4 is done, then we should select this based on a command-line flag. */#define SYNTAX_SVR4/* The opcode hash table. */static struct hash_control *op_hash = NULL;/* These characters always start a comment. */const char comment_chars[] = "#!/";/* These characters start a comment at the beginning of a line. */const char line_comment_chars[] = "#/";const char line_separator_chars[] = ";";/* Characters that can be used to separate the mantissa from the exponent in floating point numbers. */const char EXP_CHARS[] = "eE";/* Characters that indicate this number is a floating point constant. As in 0f12.456 or 0d1.2345e12. */const char FLT_CHARS[] = "rRsSfFdDxXpP";/* Register prefix. */#ifdef SYNTAX_SVR4static const char reg_prefix = '%';#elsestatic const char reg_prefix = 0;#endifstruct i860_it{ char *error; unsigned long opcode; expressionS exp; enum expand_type expand; bfd_reloc_code_real_type reloc; int pcrel; valueT fup;} the_insn;static char *expr_end;/* Indicates error if a pseudo operation was expanded after a branch. */static char last_expand;/* If true, then warn if any pseudo operations were expanded. */static int target_warn_expand = 0;/* Prototypes. */static void i860_process_insn PARAMS ((char *));static void s_dual PARAMS ((int));static void s_enddual PARAMS ((int));static void s_atmp PARAMS ((int));static int i860_get_expression PARAMS ((char *));static bfd_reloc_code_real_type obtain_reloc_for_imm16 PARAMS ((fixS *, long *));#ifdef DEBUG_I860static void print_insn PARAMS ((struct i860_it *));#endifconst pseudo_typeS md_pseudo_table[] ={#ifdef OBJ_ELF {"align", s_align_bytes, 0},#endif {"dual", s_dual, 0}, {"enddual", s_enddual, 0}, {"atmp", s_atmp, 0}, {NULL, 0, 0},};/* Dual-instruction mode handling. */enum dual{ DUAL_OFF = 0, DUAL_ON, DUAL_DDOT, DUAL_ONDDOT,};static enum dual dual_mode = DUAL_OFF;/* Handle ".dual" directive. */static voids_dual (ignore) int ignore ATTRIBUTE_UNUSED;{ dual_mode = DUAL_ON;}/* Handle ".enddual" directive. */static voids_enddual (ignore) int ignore ATTRIBUTE_UNUSED;{ dual_mode = DUAL_OFF;}/* Temporary register used when expanding assembler pseudo operations. */static int atmp = 31;static voids_atmp (ignore) int ignore ATTRIBUTE_UNUSED;{ register int temp; if (strncmp (input_line_pointer, "sp", 2) == 0) { input_line_pointer += 2; atmp = 2; } else if (strncmp (input_line_pointer, "fp", 2) == 0) { input_line_pointer += 2; atmp = 3; } else if (strncmp (input_line_pointer, "r", 1) == 0) { input_line_pointer += 1; temp = get_absolute_expression (); if (temp >= 0 && temp <= 31) atmp = temp; else as_bad (_("Unknown temporary pseudo register")); } else { as_bad (_("Unknown temporary pseudo register")); } demand_empty_rest_of_line ();}/* This function is called once, at assembler startup time. It should set up all the tables and data structures that the MD part of the assembler will need. */voidmd_begin (){ const char *retval = NULL; int lose = 0; unsigned int i = 0; op_hash = hash_new (); while (i860_opcodes[i].name != NULL) { const char *name = i860_opcodes[i].name; retval = hash_insert (op_hash, name, (PTR)&i860_opcodes[i]); if (retval != NULL) { fprintf (stderr, _("internal error: can't hash `%s': %s\n"), i860_opcodes[i].name, retval); lose = 1; } do { if (i860_opcodes[i].match & i860_opcodes[i].lose) { fprintf (stderr, _("internal error: losing opcode: `%s' \"%s\"\n"), i860_opcodes[i].name, i860_opcodes[i].args); lose = 1; } ++i; } while (i860_opcodes[i].name != NULL && strcmp (i860_opcodes[i].name, name) == 0); } if (lose) as_fatal (_("Defective assembler. No assembly attempted."));}/* This is the core of the machine-dependent assembler. STR points to a machine dependent instruction. This function emits the frags/bytes it assembles to. */voidmd_assemble (str) char *str;{ char *destp; int num_opcodes = 1; int i; struct i860_it pseudo[3]; assert (str); /* Assemble the instruction. */ i860_process_insn (str); /* Check for expandable flag to produce pseudo-instructions. This is an undesirable feature that should be avoided. */ if (the_insn.expand != 0 && ! (the_insn.fup & (OP_SEL_HA | OP_SEL_H | OP_SEL_L | OP_SEL_GOT | OP_SEL_GOTOFF | OP_SEL_PLT))) { for (i = 0; i < 3; i++) pseudo[i] = the_insn; switch (the_insn.expand) { case E_DELAY: num_opcodes = 1; break; case E_MOV: if (the_insn.exp.X_add_symbol == NULL && the_insn.exp.X_op_symbol == NULL && (the_insn.exp.X_add_number < (1 << 15) && the_insn.exp.X_add_number >= -(1 << 15))) break; /* Emit "or l%const,r0,ireg_dest". */ pseudo[0].opcode = (the_insn.opcode & 0x001f0000) | 0xe4000000; pseudo[0].fup = (OP_IMM_S16 | OP_SEL_L); /* Emit "orh h%const,ireg_dest,ireg_dest". */ pseudo[1].opcode = (the_insn.opcode & 0x03ffffff) | 0xec000000 | ((the_insn.opcode & 0x001f0000) << 5); pseudo[1].fup = (OP_IMM_S16 | OP_SEL_H); num_opcodes = 2; break; case E_ADDR: if (the_insn.exp.X_add_symbol == NULL && the_insn.exp.X_op_symbol == NULL && (the_insn.exp.X_add_number < (1 << 15) && the_insn.exp.X_add_number >= -(1 << 15))) break; /* Emit "orh ha%addr_expr,r0,r31". */ pseudo[0].opcode = 0xec000000 | (atmp << 16); pseudo[0].fup = (OP_IMM_S16 | OP_SEL_HA); /* Emit "l%addr_expr(r31),ireg_dest". We pick up the fixup information from the original instruction. */ pseudo[1].opcode = (the_insn.opcode & ~0x03e00000) | (atmp << 21); pseudo[1].fup = the_insn.fup | OP_SEL_L; num_opcodes = 2; break; case E_U32: if (the_insn.exp.X_add_symbol == NULL && the_insn.exp.X_op_symbol == NULL && (the_insn.exp.X_add_number < (1 << 16) && the_insn.exp.X_add_number >= 0)) break; /* Emit "$(opcode)h h%const,ireg_src2,r31". */ pseudo[0].opcode = (the_insn.opcode & 0xf3e0ffff) | 0x0c000000 | (atmp << 16); pseudo[0].fup = (OP_IMM_S16 | OP_SEL_H); /* Emit "$(opcode) l%const,r31,ireg_dest". */ pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 | (atmp << 21); pseudo[1].fup = (OP_IMM_S16 | OP_SEL_L); num_opcodes = 2; break; case E_AND: if (the_insn.exp.X_add_symbol == NULL && the_insn.exp.X_op_symbol == NULL && (the_insn.exp.X_add_number < (1 << 16) && the_insn.exp.X_add_number >= 0)) break; /* Emit "andnot h%const,ireg_src2,r31". */ pseudo[0].opcode = (the_insn.opcode & 0x03e0ffff) | 0xd4000000 | (atmp << 16); pseudo[0].fup = (OP_IMM_S16 | OP_SEL_H); pseudo[0].exp.X_add_number = -1 - the_insn.exp.X_add_number; /* Emit "andnot l%const,r31,ireg_dest". */ pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 | (atmp << 21); pseudo[1].fup = (OP_IMM_S16 | OP_SEL_L); pseudo[1].exp.X_add_number = -1 - the_insn.exp.X_add_number; num_opcodes = 2; break; case E_S32: if (the_insn.exp.X_add_symbol == NULL && the_insn.exp.X_op_symbol == NULL && (the_insn.exp.X_add_number < (1 << 15) && the_insn.exp.X_add_number >= -(1 << 15))) break; /* Emit "orh h%const,r0,r31". */ pseudo[0].opcode = 0xec000000 | (atmp << 16); pseudo[0].fup = (OP_IMM_S16 | OP_SEL_H); /* Emit "or l%const,r31,r31". */ pseudo[1].opcode = 0xe4000000 | (atmp << 21) | (atmp << 16); pseudo[1].fup = (OP_IMM_S16 | OP_SEL_L); /* Emit "r31,ireg_src2,ireg_dest". */ pseudo[2].opcode = (the_insn.opcode & ~0x0400ffff) | (atmp << 11); pseudo[2].fup = OP_IMM_S16; num_opcodes = 3; break; default: as_fatal (_("failed sanity check.")); } the_insn = pseudo[0]; /* Warn if an opcode is expanded after a delayed branch. */ if (num_opcodes > 1 && last_expand == 1) as_warn (_("Expanded opcode after delayed branch: `%s'"), str); /* Warn if an opcode is expanded in dual mode. */ if (num_opcodes > 1 && dual_mode != DUAL_OFF) as_warn (_("Expanded opcode in dual mode: `%s'"), str); /* Notify if any expansions happen. */ if (target_warn_expand && num_opcodes > 1) as_warn (_("An instruction was expanded (%s)"), str); } i = 0; do { /* Output the opcode. Note that the i860 always reads instructions as little-endian data. */ destp = frag_more (4); number_to_chars_littleendian (destp, the_insn.opcode, 4); /* Check for expanded opcode after branch or in dual mode. */ last_expand = the_insn.pcrel; /* Output the symbol-dependent stuff. */ if (the_insn.fup != OP_NONE) { fixS *fix; fix = fix_new_exp (frag_now, destp - frag_now->fr_literal, 4, &the_insn.exp, the_insn.pcrel, the_insn.reloc); /* Despite the odd name, this is a scratch field. We use it to encode operand type information. */ fix->fx_addnumber = the_insn.fup; } the_insn = pseudo[++i]; } while (--num_opcodes > 0);}/* Assemble the instruction pointed to by STR. */static voidi860_process_insn (str) char *str;{ char *s; const char *args; char c; struct i860_opcode *insn; char *args_start; unsigned long opcode; unsigned int mask; int match = 0; int comma = 0;#if 1 /* For compiler warnings. */ args = 0; insn = 0; args_start = 0; opcode = 0;#endif for (s = str; islower (*s) || *s == '.' || *s == '3' || *s == '2' || *s == '1'; ++s) ; switch (*s) { case '\0': break; case ',': comma = 1; /*FALLTHROUGH*/ case ' ': *s++ = '\0'; break; default: as_fatal (_("Unknown opcode: `%s'"), str); } /* Check for dual mode ("d.") opcode prefix. */ if (strncmp (str, "d.", 2) == 0) { if (dual_mode == DUAL_ON) dual_mode = DUAL_ONDDOT; else dual_mode = DUAL_DDOT; str += 2; } if ((insn = (struct i860_opcode *) hash_find (op_hash, str)) == NULL) { if (dual_mode == DUAL_DDOT || dual_mode == DUAL_ONDDOT) str -= 2; as_bad (_("Unknown opcode: `%s'"), str); return; } if (comma) *--s = ','; args_start = s; for (;;) { opcode = insn->match; memset (&the_insn, '\0', sizeof (the_insn)); the_insn.reloc = BFD_RELOC_NONE; the_insn.pcrel = 0; the_insn.fup = OP_NONE; /* Build the opcode, checking as we go that the operands match. */ for (args = insn->args; ; ++args) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -