📄 i860.c
字号:
/* i860.c -- Assemble for the I860 Copyright (C) 1989 Free Software Foundation, Inc.This file is part of GAS, the GNU Assembler.GAS is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 1, or (at your option)any later version.GAS is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GAS; see the file COPYING. If not, write tothe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */#include <stdio.h>#include <ctype.h>#include "i860-opcode.h"#include "as.h"#include "frags.h"#include "struc-symbol.h"#include "flonum.h"#include "expr.h"#include "hash.h"#include "md.h"#include "i860.h" /* position dependent redefine */#include "write.h"#include "read.h"#include "symbols.h"void md_begin();void md_end();void md_number_to_chars();void md_assemble();char *md_atof();void md_convert_frag();void md_create_short_jump();void md_create_long_jump();int md_estimate_size_before_relax();void md_number_to_imm();void md_number_to_disp();void md_number_to_field();void md_ri_to_chars();static void i860_ip();void emit_relocations();const relax_typeS md_relax_table[] = { 0 };/* handle of the OPCODE hash table */static struct hash_control *op_hash = NULL;static void s_dual(), s_enddual();static void s_atmp();const pseudo_typeSmd_pseudo_table[] = { { "dual", s_dual, 4 }, { "enddual", s_enddual, 4 }, { "atmp", s_atmp, 4 }, { NULL, 0, 0 },};int md_short_jump_size = 4;int md_long_jump_size = 4;int omagic = OMAGIC; /* Magic number for header *//* This array holds the chars that always start a comment. If the pre-processor is disabled, these aren't very useful */char comment_chars[] = "!/"; /* JF removed '|' from 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 '/*' will always start a comment */char line_comment_chars[] = "#/";/* Chars that can be used to separate mant from exp in floating point nums */char EXP_CHARS[] = "eE";/* Chars that mean this number is a floating point constant *//* As in 0f12.456 *//* or 0d1.2345e12 */char FLT_CHARS[] = "rRsSfFdDxXpP";/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be changed in read.c . Ideally it shouldn't have to know about it at all, but nothing is ideal around here. */int size_reloc_info = sizeof(struct relocation_info);static unsigned char octal[256];#define isoctal(c) octal[c]static unsigned char toHex[256];struct i860_it { char *error; unsigned long opcode; struct nlist *nlistp; expressionS exp; int pcrel; enum expand_type expand; enum highlow_type highlow; enum reloc_type reloc;} the_insn;#ifdef __STDC__static void print_insn(struct i860_it *insn);static int getExpression(char *str);#elsestatic void print_insn();static int getExpression();#endifstatic char *expr_end;static char last_expand; /* error if expansion after branch */enum dual{ DUAL_OFF = 0, DUAL_ON, DUAL_DDOT, DUAL_ONDDOT,};static enum dual dual_mode = DUAL_OFF; /* dual-instruction mode */static voids_dual() /* floating point instructions have dual set */{ dual_mode = DUAL_ON;}static voids_enddual() /* floating point instructions have dual set */{ dual_mode = DUAL_OFF;}static int atmp = 31; /* temporary register for pseudo's */static voids_atmp(){ 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_warn("Unknown temporary pseudo register"); } else { as_warn("Unknown temporary pseudo register"); } demand_empty_rest_of_line(); return;}/* This function is called once, at assembler startup time. It should set up all the tables, etc. that the MD part of the assembler will need. */voidmd_begin(){ register char *retval = NULL; int lose = 0; register unsigned int i = 0; op_hash = hash_new(); if (op_hash == NULL) as_fatal("Virtual memory exhausted"); while (i < NUMOPCODES) { const char *name = i860_opcodes[i].name; retval = hash_insert(op_hash, name, &i860_opcodes[i]); if(retval != NULL && *retval != '\0') { 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 (i < NUMOPCODES && !strcmp(i860_opcodes[i].name, name)); } if (lose) as_fatal ("Broken assembler. No assembly attempted."); for (i = '0'; i < '8'; ++i) octal[i] = 1; for (i = '0'; i <= '9'; ++i) toHex[i] = i - '0'; for (i = 'a'; i <= 'f'; ++i) toHex[i] = i + 10 - 'a'; for (i = 'A'; i <= 'F'; ++i) toHex[i] = i + 10 - 'A';}voidmd_end(){ return;}voidmd_assemble(str) char *str;{ char *toP; int rsd; int no_opcodes = 1; int i; struct i860_it pseudo[3]; assert(str); i860_ip(str); /* check for expandable flag to produce pseudo-instructions */ if (the_insn.expand != 0 && the_insn.highlow == NO_SPEC) { for (i = 0; i < 3; i++) pseudo[i] = the_insn; switch (the_insn.expand) { case E_DELAY: no_opcodes = 1; break; case E_MOV: if (the_insn.exp.X_add_symbol == NULL && the_insn.exp.X_subtract_symbol == NULL && (the_insn.exp.X_add_number < (1 << 15) && the_insn.exp.X_add_number >= -(1 << 15))) break; /* or l%const,r0,ireg_dest */ pseudo[0].opcode = (the_insn.opcode & 0x001f0000) | 0xe4000000; pseudo[0].highlow = PAIR; /* orh h%const,ireg_dest,ireg_dest */ pseudo[1].opcode = (the_insn.opcode & 0x03ffffff) | 0xec000000 | ((the_insn.opcode & 0x001f0000) << 5); pseudo[1].highlow = HIGH; no_opcodes = 2; break; case E_ADDR: if (the_insn.exp.X_add_symbol == NULL && the_insn.exp.X_subtract_symbol == NULL) break; /* orh ha%addr_expr,r0,r31 */ pseudo[0].opcode = 0xec000000 | (atmp<<16); pseudo[0].highlow = HIGHADJ; pseudo[0].reloc = LOW0; /* must overwrite */ /* l%addr_expr(r31),ireg_dest */ pseudo[1].opcode = (the_insn.opcode & ~0x003e0000) | (atmp << 21); pseudo[1].highlow = PAIR; no_opcodes = 2; break; case E_U32: /* 2nd version emulates Intel as, not doc. */ if (the_insn.exp.X_add_symbol == NULL && the_insn.exp.X_subtract_symbol == NULL && (the_insn.exp.X_add_number < (1 << 16) && the_insn.exp.X_add_number >= 0)) break; /* $(opcode)h h%const,ireg_src2,ireg_dest pseudo[0].opcode = (the_insn.opcode & 0xf3ffffff) | 0x0c000000; */ /* $(opcode)h h%const,ireg_src2,r31 */ pseudo[0].opcode = (the_insn.opcode & 0xf3e0ffff) | 0x0c000000 | (atmp << 16); pseudo[0].highlow = HIGH; /* $(opcode) l%const,ireg_dest,ireg_dest pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 | ((the_insn.opcode & 0x001f0000) << 5); */ /* $(opcode) l%const,r31,ireg_dest */ pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 | (atmp << 21); pseudo[1].highlow = PAIR; no_opcodes = 2; break; case E_AND: /* 2nd version emulates Intel as, not doc. */ if (the_insn.exp.X_add_symbol == NULL && the_insn.exp.X_subtract_symbol == NULL && (the_insn.exp.X_add_number < (1 << 16) && the_insn.exp.X_add_number >= 0)) break; /* andnot h%const,ireg_src2,ireg_dest pseudo[0].opcode = (the_insn.opcode & 0x03ffffff) | 0xd4000000; */ /* andnot h%const,ireg_src2,r31 */ pseudo[0].opcode = (the_insn.opcode & 0x03e0ffff) | 0xd4000000 | (atmp << 16); pseudo[0].highlow = HIGH; pseudo[0].exp.X_add_number = -1 - the_insn.exp.X_add_number; /* andnot l%const,ireg_dest,ireg_dest pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 | ((the_insn.opcode & 0x001f0000) << 5); */ /* andnot l%const,r31,ireg_dest */ pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 | (atmp << 21); pseudo[1].highlow = PAIR; pseudo[1].exp.X_add_number = -1 - the_insn.exp.X_add_number; no_opcodes = 2; break; case E_S32: if (the_insn.exp.X_add_symbol == NULL && the_insn.exp.X_subtract_symbol == NULL && (the_insn.exp.X_add_number < (1 << 15) && the_insn.exp.X_add_number >= -(1 << 15))) break; /* orh h%const,r0,r31 */ pseudo[0].opcode = 0xec000000 | (atmp << 16); pseudo[0].highlow = HIGH; /* or l%const,r31,r31 */ pseudo[1].opcode = 0xe4000000 | (atmp << 21) | (atmp << 16); pseudo[1].highlow = PAIR; /* r31,ireg_src2,ireg_dest */ pseudo[2].opcode = (the_insn.opcode & ~0x0400ffff) | (atmp << 11); pseudo[2].reloc = NO_RELOC; no_opcodes = 3; break; default: abort(); } the_insn = pseudo[0]; /* check for expanded opcode after branch or in dual */ if (no_opcodes > 1 && last_expand == TRUE) as_warn("Expanded opcode after delayed branch: `%s'", str); if (no_opcodes > 1 && dual_mode != DUAL_OFF) as_warn("Expanded opcode in dual mode: `%s'", str); } i = 0; do { /* always produce at least one opcode */ toP = frag_more(4); /* put out the opcode */ md_number_to_chars(toP, the_insn.opcode, 4); /* check for expanded opcode after branch or in dual */ last_expand = the_insn.pcrel; /* put out the symbol-dependent stuff */ if (the_insn.reloc != NO_RELOC) { fix_new( frag_now, /* which frag */ (toP - frag_now->fr_literal), /* where */ 4, /* size */ the_insn.exp.X_add_symbol, the_insn.exp.X_subtract_symbol, the_insn.exp.X_add_number, the_insn.pcrel, /* merge bit fields into one argument */ (int)(((the_insn.highlow & 0x3) << 4) | (the_insn.reloc & 0xf)) ); } the_insn = pseudo[++i]; } while (--no_opcodes > 0);}static voidi860_ip(str) char *str;{ char *s; const char *args; char c; unsigned long i; struct i860_opcode *insn; char *argsStart; unsigned long opcode; unsigned int mask; int match = FALSE; int comma = 0; for (s = str; islower(*s) || *s == '.' || *s == '3'; ++s) ; switch (*s) { case '\0': break; case ',': comma = 1; /*FALLTHROUGH*/ case ' ': *s++ = '\0'; break; default: as_warn("Unknown opcode: `%s'", str); exit(1); } if (strncmp(str, "d.", 2) == 0) { /* check for d. opcode prefix */ 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_warn("Unknown opcode: `%s'", str); return; } if (comma) { *--s = ','; } argsStart = s; for (;;) { opcode = insn->match; bzero(&the_insn, sizeof(the_insn)); the_insn.reloc = NO_RELOC; /* * Build the opcode, checking as we go to make * sure that the operands match */ for (args = insn->args; ; ++args) { switch (*args) { case '\0': /* end of args */ if (*s == '\0') { match = TRUE; } break; case '+': case '(': /* these must match exactly */ case ')': case ',': case ' ': if (*s++ == *args) continue; break; case '#': /* must be at least one digit */ if (isdigit(*s++)) { while (isdigit(*s)) { ++s; } continue; } break; case '1': /* next operand must be a register */ case '2': case 'd': switch (*s) { case 'f': /* frame pointer */ s++; if (*s++ == 'p') { mask = 0x3; break; } goto error; case 's': /* stack pointer */ s++; if (*s++ == 'p') { mask= 0x2; break; } goto error; case 'r': /* any register */ s++; if (!isdigit(c = *s++)) { goto error; } if (isdigit(*s)) { if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32) { goto error; } } else { c -= '0'; } mask= c; break; default: /* not this opcode */ goto error; } /* * Got the register, now figure out where * it goes in the opcode. */ switch (*args) { case '1': opcode |= mask << 11; continue; case '2': opcode |= mask << 21; continue; case 'd': opcode |= mask << 16; continue; } break; case 'e': /* next operand is a floating point register */ case 'f': case 'g': if (*s++ == 'f' && isdigit(*s)) { mask = *s++; if (isdigit(*s)) { mask = 10 * (mask - '0') + (*s++ - '0'); if (mask >= 32) { break; } } else { mask -= '0'; } switch (*args) { case 'e': opcode |= mask << 11; continue; case 'f': opcode |= mask << 21; continue; case 'g': opcode |= mask << 16; if (dual_mode != DUAL_OFF) opcode |= (1 << 9); /* dual mode instruction */ if (dual_mode == DUAL_DDOT) dual_mode = DUAL_OFF; if (dual_mode == DUAL_ONDDOT) dual_mode = DUAL_ON; if ((opcode & (1 << 10)) && (mask == ((opcode >> 11) & 0x1f))) as_warn("Fsr1 equals fdest with Pipelining"); continue; } } break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -