📄 sparc.c
字号:
/*- * This code is derived from software copyrighted by the Free Software * Foundation. * * Modified 1993 by Chris Torek at Lawrence Berkeley Laboratory. */#ifndef lintstatic char sccsid[] = "@(#)sparc.c 5.2 (Berkeley) 4/12/93";#endif /* not lint *//* sparc.c -- Assemble for the SPARC 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 "sparc-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 "sparc.h"#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();void emit_relocations();static void sparc_ip();const relax_typeS md_relax_table[] = { 0 };/* handle of the OPCODE hash table */static struct hash_control *op_hash = NULL;static void s_seg(), s_proc(), s_data1(), s_reserve(), s_common();extern void s_globl(), s_long(), s_short(), s_space(), cons();const pseudo_typeSmd_pseudo_table[] = { { "common", s_common, 0 }, { "global", s_globl, 0 }, { "half", cons, 2 }, { "proc", s_proc, 0 }, { "reserve", s_reserve, 0 }, { "seg", s_seg, 0 }, { "skip", s_space, 0 }, { "word", cons, 4 }, { NULL, 0, 0 },};int md_short_jump_size = 4;int md_long_jump_size = 4;int omagic = (0x103 << 16) | 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 reloc_info_sparc);static unsigned char octal[256];#define isoctal(c) octal[c]static unsigned char toHex[256];/* * anull bit - causes the branch delay slot instructions to not be executed */#define ANNUL (1 << 29)struct sparc_it { char *error; unsigned long opcode; struct nlist *nlistp; expressionS exp; int pcrel; enum reloc_type reloc;} the_insn, set_insn;#ifdef __STDC__static void print_insn(struct sparc_it *insn);static int getExpression(char *str);#elsestatic void print_insn();static int getExpression();#endifstatic char *expr_end;static int special_case;#define SPECIAL_CASE_SET 1/* * sort of like s_lcomm * */static voids_reserve(){ char *name; char c; char *p; int temp; symbolS *symbolP; name = input_line_pointer; c = get_symbol_end(); p = input_line_pointer; *p = c; SKIP_WHITESPACE(); if ( * input_line_pointer != ',' ) { as_warn("Expected comma after name"); ignore_rest_of_line(); return; } input_line_pointer ++; if ((temp = get_absolute_expression()) < 0) { as_warn("BSS length (%d.) <0! Ignored.", temp); ignore_rest_of_line(); return; } *p = 0; symbolP = symbol_find_or_make(name); *p = c; if (strncmp(input_line_pointer, ",\"bss\"", 6) != 0) { as_warn("bad .reserve segment: `%s'", input_line_pointer); return; } input_line_pointer += 6; if (symbolP->sy_other == 0 && symbolP->sy_desc == 0 && ((symbolP->sy_type == N_BSS && symbolP->sy_value == local_bss_counter) || ((symbolP->sy_type & N_TYPE) == N_UNDF && symbolP->sy_value == 0))) { symbolP->sy_value = local_bss_counter; symbolP->sy_type = N_BSS; symbolP->sy_frag = & bss_address_frag; local_bss_counter += temp; } else { as_warn( "Ignoring attempt to re-define symbol from %d. to %d.", symbolP->sy_value, local_bss_counter ); } demand_empty_rest_of_line(); return;}static voids_common(){ register char *name; register char c; register char *p; register int temp; register symbolS * symbolP; name = input_line_pointer; c = get_symbol_end(); /* just after name is now '\0' */ p = input_line_pointer; *p = c; SKIP_WHITESPACE(); if ( * input_line_pointer != ',' ) { as_warn("Expected comma after symbol-name"); ignore_rest_of_line(); return; } input_line_pointer ++; /* skip ',' */ if ( (temp = get_absolute_expression ()) < 0 ) { as_warn(".COMMon length (%d.) <0! Ignored.", temp); ignore_rest_of_line(); return; } *p = 0; symbolP = symbol_find_or_make (name); *p = c; if ( (symbolP->sy_type & N_TYPE) != N_UNDF || symbolP->sy_other != 0 || symbolP->sy_desc != 0) { as_warn( "Ignoring attempt to re-define symbol"); ignore_rest_of_line(); return; } if (symbolP->sy_value) { if (symbolP->sy_value != temp) { as_warn( "Length of .comm \"%s\" is already %d. Not changed to %d.", symbolP->sy_name, symbolP->sy_value, temp); } } else { symbolP->sy_value = temp; symbolP->sy_type |= N_EXT; } know(symbolP->sy_frag == &zero_address_frag); if (strncmp(input_line_pointer, ",\"bss\"", 6) != 0) { p=input_line_pointer; while(*p && *p!='\n') p++; c= *p; *p='\0'; as_warn("bad .common segment: `%s'", input_line_pointer); *p=c; return; } input_line_pointer += 6; demand_empty_rest_of_line(); return;}static voids_seg(){ if (strncmp(input_line_pointer, "\"text\"", 6) == 0) { input_line_pointer += 6; s_text(); return; } if (strncmp(input_line_pointer, "\"data\"", 6) == 0) { input_line_pointer += 6; s_data(); return; } if (strncmp(input_line_pointer, "\"data1\"", 7) == 0) { input_line_pointer += 7; s_data1(); return; } as_warn("Unknown segment type"); demand_empty_rest_of_line(); return;}static voids_data1(){ subseg_new(SEG_DATA, 1); demand_empty_rest_of_line(); return;}static voids_proc(){ extern char is_end_of_line[]; while (!is_end_of_line[*input_line_pointer]) { ++input_line_pointer; } ++input_line_pointer; 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 = sparc_opcodes[i].name; retval = hash_insert(op_hash, name, &sparc_opcodes[i]); if(retval != NULL && *retval != '\0') { fprintf (stderr, "internal error: can't hash `%s': %s\n", sparc_opcodes[i].name, retval); lose = 1; } do { if (sparc_opcodes[i].match & sparc_opcodes[i].lose) { fprintf (stderr, "internal error: losing opcode: `%s' \"%s\"\n", sparc_opcodes[i].name, sparc_opcodes[i].args); lose = 1; } ++i; } while (i < NUMOPCODES && !strcmp(sparc_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; assert(str); sparc_ip(str); toP = frag_more(4); /* put out the opcode */ md_number_to_chars(toP, the_insn.opcode, 4); /* put out the symbol-dependent stuff */ if (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, the_insn.reloc ); } switch (special_case) { case SPECIAL_CASE_SET: special_case = 0; assert(the_insn.reloc == RELOC_HI22); if (the_insn.exp.X_seg == SEG_ABSOLUTE && the_insn.exp.X_add_symbol == 0 && the_insn.exp.X_subtract_symbol == 0 && (the_insn.exp.X_add_number & 0x3ff) == 0) return; toP = frag_more(4); rsd = (the_insn.opcode >> 25) & 0x1f; the_insn.opcode = 0x80102000 | (rsd << 25) | (rsd << 14); md_number_to_chars(toP, the_insn.opcode, 4); 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, RELOC_LO10 ); return; case 0: return; default: abort(); }}static voidsparc_ip(str) char *str;{ char *s; const char *args; char c; unsigned long i; struct sparc_opcode *insn; char *argsStart; unsigned long opcode; unsigned int mask; int match = FALSE; int comma = 0; for (s = str; islower(*s) || (*s >= '0' && *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 ((insn = (struct sparc_opcode *) hash_find(op_hash, str)) == NULL) { 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 '+': if (*s == '+') { ++s; continue; } if (*s == '-') { continue; } break; 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 'C': /* coprocessor state register */ if (strncmp(s, "%csr", 4) == 0) { s += 4; continue; } break; case 'b': /* next operand is a coprocessor register */ case 'c': case 'D': if (*s++ == '%' && *s++ == 'c' && isdigit(*s)) { mask = *s++; if (isdigit(*s)) { mask = 10 * (mask - '0') + (*s++ - '0'); if (mask >= 32) { break; } } else { mask -= '0'; } switch (*args) { case 'b': opcode |= mask << 14; continue; case 'c': opcode |= mask; continue; case 'D': opcode |= mask << 25; continue; } } break; case 'r': /* next operand must be a register */ case 'R': case '1': case '2': case 'd': if (*s++ == '%') { switch (c = *s++) { case 'f': /* frame pointer */ if (*s++ == 'p') { mask = 0x1e; break; } goto error; case 'g': /* global register */ if (isoctal(c = *s++)) { mask = c - '0'; break; } goto error; case 'i': /* in register */ if (isoctal(c = *s++)) { mask = c - '0' + 24; break; } goto error; case 'l': /* local register */ if (isoctal(c = *s++)) { mask= (c - '0' + 16) ; break; } goto error; case 'o': /* out register */ if (isoctal(c = *s++)) { mask= (c - '0' + 8) ; break; } goto error; case 's': /* stack pointer */ if (*s++ == 'p') { mask= 0xe; break; } goto error; case 'r': /* any register */ if (!isdigit(c = *s++)) { goto error; } /* FALLTHROUGH */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (isdigit(*s)) { if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32) { goto error; } } else { c -= '0'; } mask= c; break; default: goto error; } /* * Got the register, now figure out where * it goes in the opcode. */ switch (*args) { case '1': opcode |= mask << 14; continue; case '2': opcode |= mask; continue; case 'd': opcode |= mask << 25; continue; case 'r': opcode |= (mask << 25) | (mask << 14); continue; case 'R': opcode |= (mask << 25) | mask; continue; } } break; case 'e': /* next operand is a floating point register */ case 'f': case 'g': if (*s++ == '%' && *s++ == 'f' && isdigit(*s)) { mask = *s++; if (isdigit(*s)) { mask = 10 * (mask - '0') + (*s++ - '0');
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -