diff options
Diffstat (limited to 'gas/config/tc-tic80.c')
-rw-r--r-- | gas/config/tc-tic80.c | 1061 |
1 files changed, 1061 insertions, 0 deletions
diff --git a/gas/config/tc-tic80.c b/gas/config/tc-tic80.c new file mode 100644 index 00000000000..f31dba32f93 --- /dev/null +++ b/gas/config/tc-tic80.c @@ -0,0 +1,1061 @@ +/* tc-tic80.c -- Assemble for the TI TMS320C80 (MV) + Copyright (C) 1996, 1997, 1998, 1999 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 "opcode/tic80.h" + +#define internal_error(what) \ + as_fatal(_("internal error:%s:%d: %s\n"),__FILE__,__LINE__,what) +#define internal_error_a(what,arg) \ + as_fatal(_("internal error:%s:%d: %s %d\n"),__FILE__,__LINE__,what,arg) + + +/* Generic assembler global variables which must be defined by all targets. */ + +/* Characters which always start a comment. */ +const char comment_chars[] = ";"; + +/* Characters which start a comment at the beginning of a line. */ +const char line_comment_chars[] = ";*#"; + +/* Characters which may be used to separate multiple commands on a single + line. The semicolon is such a character by default and should not be + explicitly listed. */ +const char line_separator_chars[] = ""; + +/* Characters which are used to indicate an exponent in a floating + point number. */ +const char EXP_CHARS[] = "eE"; + +/* Characters which mean that a number is a floating point constant, + as in 0f1.0. */ +const char FLT_CHARS[] = "fF"; + +/* This table describes all the machine specific pseudo-ops the assembler + has to support. The fields are: + + pseudo-op name without dot + function to call to execute this pseudo-op + integer arg to pass to the function */ + +extern void obj_coff_section (); + +const pseudo_typeS md_pseudo_table[] = +{ + { "align", s_align_bytes, 4 }, /* Do byte alignment, default is a 4 byte boundary */ + { "word", cons, 4 }, /* FIXME: Should this be machine independent? */ + { "bss", s_lcomm_bytes, 1 }, + { "sect", obj_coff_section, 0}, /* For compatibility with TI tools */ + { "section", obj_coff_section, 0}, /* Standard COFF .section pseudo-op */ + { NULL, NULL, 0 } +}; + +/* Opcode hash table. */ +static struct hash_control *tic80_hash; + +static struct tic80_opcode * find_opcode PARAMS ((struct tic80_opcode *, expressionS [])); +static void build_insn PARAMS ((struct tic80_opcode *, expressionS *)); +static int get_operands PARAMS ((expressionS exp[])); +static int const_overflow PARAMS ((unsigned long num, int bits, int flags)); + +/* Replace short PC relative instructions with long form when necessary. Currently + this is off by default or when given the -no-relax option. Turning it on by using + the -relax option forces all PC relative instructions to use the long form, which + is why it is currently not the default. */ +static int tic80_relax = 0; + + +int +md_estimate_size_before_relax (fragP, segment_type) + fragS *fragP; + segT segment_type; +{ + internal_error (_("Relaxation is a luxury we can't afford")); + return (-1); +} + +/* We have no need to default values of symbols. */ + +/* ARGSUSED */ +symbolS * +md_undefined_symbol (name) + char *name; +{ + return 0; +} + +/* 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. + */ + +#define MAX_LITTLENUMS 4 + +char * +md_atof (type, litP, sizeP) + int type; + char *litP; + int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + char *atof_ieee (); + + switch (type) + { + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + 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); + + for (wordP = words; prec--;) + { + md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + return (NULL); +} + +/* Check to see if the constant value in NUM will fit in a field of + width BITS if it has flags FLAGS. */ + +static int +const_overflow (num, bits, flags) + unsigned long num; + int bits; + int flags; +{ + long min, max; + int retval = 0; + + /* Only need to check fields less than 32 bits wide */ + if (bits < 32) + if (flags & TIC80_OPERAND_SIGNED) + { + max = (1 << (bits - 1)) - 1; + min = - (1 << (bits - 1)); + retval = ((long) num > max) || ((long) num < min); + } + else + { + max = (1 << bits) - 1; + min = 0; + retval = (num > max) || (num < min); + } + return (retval); +} + +/* get_operands() parses a string of operands and fills in a passed array of + expressions in EXP. + + Note that we use O_absent expressions to record additional information + about the previous non-O_absent expression, such as ":m" or ":s" + modifiers or register numbers enclosed in parens like "(r10)". + + Returns the number of expressions that were placed in EXP. + + */ + +static int +get_operands (exp) + expressionS exp[]; +{ + char *p = input_line_pointer; + int numexp = 0; + int mflag = 0; + int sflag = 0; + int parens = 0; + + while (*p) + { + /* Skip leading whitespace */ + while (*p == ' ' || *p == '\t' || *p == ',') + { + p++; + } + + /* Check to see if we have any operands left to parse */ + if (*p == 0 || *p == '\n' || *p == '\r') + { + break; + } + + /* Notice scaling or direct memory operand modifiers and save them in + an O_absent expression after the expression that they modify. */ + + if (*p == ':') + { + p++; + exp[numexp].X_op = O_absent; + if (*p == 'm') + { + p++; + /* This is a ":m" modifier */ + exp[numexp].X_add_number = TIC80_OPERAND_M_SI | TIC80_OPERAND_M_LI; + } + else if (*p == 's') + { + p++; + /* This is a ":s" modifier */ + exp[numexp].X_add_number = TIC80_OPERAND_SCALED; + } + else + { + as_bad (_("':' not followed by 'm' or 's'")); + } + numexp++; + continue; + } + + /* Handle leading '(' on operands that use them, by recording that we + have entered a paren nesting level and then continuing. We complain + about multiple nesting. */ + + if (*p == '(') + { + if (++parens != 1) + { + as_bad (_("paren nesting")); + } + p++; + continue; + } + + /* Handle trailing ')' on operands that use them, by reducing the + nesting level and then continuing. We complain if there were too + many closures. */ + + if (*p == ')') + { + /* Record that we have left a paren group and continue */ + if (--parens < 0) + { + as_bad (_("mismatched parenthesis")); + } + p++; + continue; + } + + /* Begin operand parsing at the current scan point. */ + + input_line_pointer = p; + expression (&exp[numexp]); + + if (exp[numexp].X_op == O_illegal) + { + as_bad (_("illegal operand")); + } + else if (exp[numexp].X_op == O_absent) + { + as_bad (_("missing operand")); + } + + numexp++; + p = input_line_pointer; + } + + if (parens) + { + exp[numexp].X_op = O_absent; + exp[numexp++].X_add_number = TIC80_OPERAND_PARENS; + } + + /* Mark the end of the valid operands with an illegal expression. */ + exp[numexp].X_op = O_illegal; + + return (numexp); +} + +/* find_opcode() gets a pointer to the entry in the opcode table that + matches the instruction being assembled, or returns NULL if no such match + is found. + + First it parses all the operands and save them as expressions. Note that + we use O_absent expressions to record additional information about the + previous non-O_absent expression, such as ":m" or ":s" modifiers or + register numbers enclosed in parens like "(r10)". + + It then looks at all opcodes with the same name and uses the operands to + choose the correct opcode. */ + +static struct tic80_opcode * +find_opcode (opcode, myops) + struct tic80_opcode *opcode; + expressionS myops[]; +{ + int numexp; /* Number of expressions from parsing operands */ + int expi; /* Index of current expression to match */ + int opi; /* Index of current operand to match */ + int match = 0; /* Set to 1 when an operand match is found */ + struct tic80_opcode *opc = opcode; /* Pointer to current opcode table entry */ + const struct tic80_opcode *end; /* Pointer to end of opcode table */ + + /* First parse all the operands so we only have to do it once. There may + be more expressions generated than there are operands. */ + + numexp = get_operands (myops); + + /* For each opcode with the same name, try to match it against the parsed + operands. */ + + end = tic80_opcodes + tic80_num_opcodes; + while (!match && (opc < end) && (strcmp (opc -> name, opcode -> name) == 0)) + { + /* Start off assuming a match. If we find a mismatch, then this is + reset and the operand/expr matching loop terminates with match + equal to zero, which allows us to try the next opcode. */ + + match = 1; + + /* For each expression, try to match it against the current operand + for the current opcode. Upon any mismatch, we abandon further + matching for the current opcode table entry. */ + + for (expi = 0, opi = -1; (expi < numexp) && match; expi++) + { + int bits, flags, X_op, num; + + X_op = myops[expi].X_op; + num = myops[expi].X_add_number; + + /* The O_absent expressions apply to the same operand as the most + recent non O_absent expression. So only increment the operand + index when the current expression is not one of these special + expressions. */ + + if (X_op != O_absent) + { + opi++; + } + + flags = tic80_operands[opc -> operands[opi]].flags; + bits = tic80_operands[opc -> operands[opi]].bits; + + switch (X_op) + { + case O_register: + /* Also check that registers that are supposed to be even actually + are even. */ + if (((flags & TIC80_OPERAND_GPR) != (num & TIC80_OPERAND_GPR)) || + ((flags & TIC80_OPERAND_FPA) != (num & TIC80_OPERAND_FPA)) || + ((flags & TIC80_OPERAND_CR) != (num & TIC80_OPERAND_CR)) || + ((flags & TIC80_OPERAND_EVEN) && (num & 1)) || + const_overflow (num & ~TIC80_OPERAND_MASK, bits, flags)) + { + match = 0; + } + break; + case O_constant: + if ((flags & TIC80_OPERAND_ENDMASK) && (num == 32)) + { + /* Endmask values of 0 and 32 give identical results */ + num = 0; + } + if ((flags & (TIC80_OPERAND_FPA | TIC80_OPERAND_GPR)) || + const_overflow (num, bits, flags)) + { + match = 0; + } + break; + case O_symbol: + if ((bits < 32) && (flags & TIC80_OPERAND_PCREL) && !tic80_relax) + { + /* The default is to prefer the short form of PC relative relocations. + This is the only form that the TI assembler supports. + If the -relax option is given, we never use the short forms. + FIXME: Should be able to choose "best-fit". */ + } + else if ((bits == 32) /* && (flags & TIC80_OPERAND_BASEREL) */) + { + /* The default is to prefer the long form of base relative relocations. + This is the only form that the TI assembler supports. + If the -no-relax option is given, we always use the long form of + PC relative relocations. + FIXME: Should be able to choose "best-fit". */ + } + else + { + /* Symbols that don't match one of the above cases are + rejected as an operand. */ + match = 0; + } + break; + case O_absent: + /* If this is an O_absent expression, then it may be an expression that + supplies additional information about the operand, such as ":m" or + ":s" modifiers. Check to see that the operand matches this requirement. */ + if (!((num & TIC80_OPERAND_M_SI) && (flags & TIC80_OPERAND_M_SI) || + (num & TIC80_OPERAND_M_LI) && (flags & TIC80_OPERAND_M_LI) || + (num & TIC80_OPERAND_SCALED) && (flags & TIC80_OPERAND_SCALED))) + { + match = 0; + } + break; + case O_big: + if ((num > 0) || !(flags & TIC80_OPERAND_FLOAT)) + { + match = 0; + } + break; + case O_illegal: + case O_symbol_rva: + case O_uminus: + case O_bit_not: + case O_logical_not: + case O_multiply: + case O_divide: + case O_modulus: + case O_left_shift: + case O_right_shift: + case O_bit_inclusive_or: + case O_bit_or_not: + case O_bit_exclusive_or: + case O_bit_and: + case O_add: + case O_subtract: + case O_eq: + case O_ne: + case O_lt: + case O_le: + case O_ge: + case O_gt: + case O_logical_and: + case O_logical_or: + case O_max: + default: + internal_error_a (_("unhandled expression type"), X_op); + } + } + if (!match) + { + opc++; + } + } + + return (match ? opc : NULL); + +#if 0 + + /* Now search the opcode table table for one with operands that + matches what we've got. */ + + while (!match) + { + match = 1; + for (i = 0; opcode -> operands[i]; i++) + { + int flags = tic80_operands[opcode->operands[i]].flags; + int X_op = myops[i].X_op; + int num = myops[i].X_add_number; + + if (X_op == 0) + { + match = 0; + break; + } + + if (flags & (TIC80_OPERAND_GPR | TIC80_OPERAND_FPA | TIC80_OPERAND_CR)) + { + if ((X_op != O_register) || + ((flags & TIC80_OPERAND_GPR) != (num & TIC80_OPERAND_GPR)) || + ((flags & TIC80_OPERAND_FPA) != (num & TIC80_OPERAND_FPA)) || + ((flags & TIC80_OPERAND_CR) != (num & TIC80_OPERAND_CR))) + { + match=0; + break; + } + } + + if (((flags & TIC80_OPERAND_MINUS) && ((X_op != O_absent) || (num != TIC80_OPERAND_MINUS))) || + ((flags & TIC80_OPERAND_PLUS) && ((X_op != O_absent) || (num != TIC80_OPERAND_PLUS))) || + ((flags & TIC80_OPERAND_ATMINUS) && ((X_op != O_absent) || (num != TIC80_OPERAND_ATMINUS))) || + ((flags & TIC80_OPERAND_ATPAR) && ((X_op != O_absent) || (num != TIC80_OPERAND_ATPAR))) || + ((flags & TIC80_OPERAND_ATSIGN) && ((X_op != O_absent) || (num != TIC80_OPERAND_ATSIGN)))) + { + match=0; + break; + } + } + /* we're only done if the operands matched so far AND there + are no more to check */ + if (match && myops[i].X_op==0) + break; + else + match = 0; + + next_opcode = opcode+1; + if (next_opcode->opcode == 0) + break; + if (strcmp(next_opcode->name, opcode->name)) + break; + opcode = next_opcode; + } + + if (!match) + { + as_bad (_("bad opcode or operands")); + return (0); + } + + /* Check that all registers that are required to be even are. */ + /* Also, if any operands were marked as registers, but were really symbols */ + /* fix that here. */ + for (i=0; opcode->operands[i]; i++) + { + if ((tic80_operands[opcode->operands[i]].flags & TIC80_OPERAND_EVEN) && + (myops[i].X_add_number & 1)) + as_fatal (_("Register number must be EVEN")); + if (myops[i].X_op == O_register) + { + if (!(tic80_operands[opcode->operands[i]].flags & TIC80_OPERAND_REG)) + { + myops[i].X_op = O_symbol; + myops[i].X_add_symbol = symbol_find_or_make ((char *)myops[i].X_op_symbol); + myops[i].X_add_number = 0; + myops[i].X_op_symbol = NULL; + } + } + } + +#endif +} + +/* build_insn takes a pointer to the opcode entry in the opcode table + and the array of operand expressions and writes out the instruction. + + Note that the opcode word and extended word may be written to different + frags, with the opcode at the end of one frag and the extension at the + beginning of the next. */ + +static void +build_insn (opcode, opers) + struct tic80_opcode *opcode; + expressionS *opers; +{ + int expi; /* Index of current expression to match */ + int opi; /* Index of current operand to match */ + unsigned long insn[2]; /* Instruction and long immediate (if any) */ + char *f; /* Pointer to frag location for insn[0] */ + fragS *ffrag; /* Frag containing location f */ + char *fx = NULL; /* Pointer to frag location for insn[1] */ + fragS *fxfrag; /* Frag containing location fx */ + + /* Start with the raw opcode bits from the opcode table. */ + insn[0] = opcode -> opcode; + + /* We are going to insert at least one 32 bit opcode so get the + frag now. */ + + f = frag_more (4); + ffrag = frag_now; + + /* For each operand expression, insert the appropriate bits into the + instruction . */ + for (expi = 0, opi = -1; opers[expi].X_op != O_illegal; expi++) + { + int bits, shift, flags, X_op, num; + + X_op = opers[expi].X_op; + num = opers[expi].X_add_number; + + /* The O_absent expressions apply to the same operand as the most + recent non O_absent expression. So only increment the operand + index when the current expression is not one of these special + expressions. */ + + if (X_op != O_absent) + { + opi++; + } + + flags = tic80_operands[opcode -> operands[opi]].flags; + bits = tic80_operands[opcode -> operands[opi]].bits; + shift = tic80_operands[opcode -> operands[opi]].shift; + + switch (X_op) + { + case O_register: + num &= ~TIC80_OPERAND_MASK; + insn[0] = insn[0] | (num << shift); + break; + case O_constant: + if ((flags & TIC80_OPERAND_ENDMASK) && (num == 32)) + { + /* Endmask values of 0 and 32 give identical results */ + num = 0; + } + else if ((flags & TIC80_OPERAND_BITNUM)) + { + /* BITNUM values are stored in one's complement form */ + num = (~num & 0x1F); + } + /* Mask off upper bits, just it case it is signed and is negative */ + if (bits < 32) + { + num &= (1 << bits) - 1; + insn[0] = insn[0] | (num << shift); + } + else + { + fx = frag_more (4); + fxfrag = frag_now; + insn[1] = num; + } + break; + case O_symbol: + if (bits == 32) + { + fx = frag_more (4); + fxfrag = frag_now; + insn[1] = 0; + if (flags & TIC80_OPERAND_PCREL) + { + fix_new_exp (fxfrag, + fx - (fxfrag -> fr_literal), + 4, + &opers[expi], + 1, + R_MPPCR); + } + else + { + fix_new_exp (fxfrag, + fx - (fxfrag -> fr_literal), + 4, + &opers[expi], + 0, + R_RELLONGX); + } + } + else if (flags & TIC80_OPERAND_PCREL) + { + fix_new_exp (ffrag, + f - (ffrag -> fr_literal), + 4, /* FIXME! how is this used? */ + &opers[expi], + 1, + R_MPPCR15W); + } + else + { + internal_error (_("symbol reloc that is not PC relative or 32 bits")); + } + break; + case O_absent: + /* Each O_absent expression can indicate exactly one possible modifier. */ + if ((num & TIC80_OPERAND_M_SI) && (flags & TIC80_OPERAND_M_SI)) + { + insn[0] = insn[0] | (1 << 17); + } + else if ((num & TIC80_OPERAND_M_LI) && (flags & TIC80_OPERAND_M_LI)) + { + insn[0] = insn[0] | (1 << 15); + } + else if ((num & TIC80_OPERAND_SCALED) && (flags & TIC80_OPERAND_SCALED)) + { + insn[0] = insn[0] | (1 << 11); + } + else if ((num & TIC80_OPERAND_PARENS) && (flags & TIC80_OPERAND_PARENS)) + { + /* No code to generate, just accept and discard this expression */ + } + else + { + internal_error_a (_("unhandled operand modifier"), opers[expi].X_add_number); + } + break; + case O_big: + fx = frag_more (4); + fxfrag = frag_now; + { + int precision = 2; + long exponent_bits = 8L; + LITTLENUM_TYPE words[2]; + /* Value is still in generic_floating_point_number */ + gen_to_words (words, precision, exponent_bits); + insn[1] = (words[0] << 16) | words[1]; + } + break; + case O_illegal: + case O_symbol_rva: + case O_uminus: + case O_bit_not: + case O_logical_not: + case O_multiply: + case O_divide: + case O_modulus: + case O_left_shift: + case O_right_shift: + case O_bit_inclusive_or: + case O_bit_or_not: + case O_bit_exclusive_or: + case O_bit_and: + case O_add: + case O_subtract: + case O_eq: + case O_ne: + case O_lt: + case O_le: + case O_ge: + case O_gt: + case O_logical_and: + case O_logical_or: + case O_max: + default: + internal_error_a (_("unhandled expression"), X_op); + break; + } + } + + /* Write out the instruction, either 4 or 8 bytes. */ + + md_number_to_chars (f, insn[0], 4); + if (fx != NULL) + { + md_number_to_chars (fx, insn[1], 4); + } +} + +/* This is the main entry point for the machine-dependent assembler. Gas + calls this function for each input line which does not contain a + pseudoop. + + STR points to a NULL terminated machine dependent instruction. This + function is supposed to emit the frags/bytes it assembles to. */ + +void +md_assemble (str) + char *str; +{ + char *scan; + unsigned char *input_line_save; + struct tic80_opcode *opcode; + expressionS myops[16]; + unsigned long insn; + + /* Ensure there is something there to assemble. */ + assert (str); + + /* Drop any leading whitespace. */ + while (isspace (*str)) + { + str++; + } + + /* Isolate the mnemonic from the rest of the string by finding the first + whitespace character and zapping it to a null byte. */ + for (scan = str; *scan != '\000' && !isspace (*scan); scan++) {;} + if (*scan != '\000') + { + *scan++ = '\000'; + } + + /* Try to find this mnemonic in the hash table */ + if ((opcode = (struct tic80_opcode *) hash_find (tic80_hash, str)) == NULL) + { + as_bad (_("Invalid mnemonic: '%s'"), str); + return; + } + + str = scan; + while (isspace (*scan)) + { + scan++; + } + + input_line_save = input_line_pointer; + input_line_pointer = str; + + opcode = find_opcode (opcode, myops); + if (opcode == NULL) + { + as_bad (_("Invalid operands: '%s'"), input_line_save); + } + + input_line_pointer = input_line_save; + build_insn (opcode, myops); +} + +/* This function is called once at the start of assembly, after the command + line arguments have been parsed and all the machine independent + initializations have been completed. + + It should set up all the tables, etc., that the machine dependent part of + the assembler will need. */ + +void +md_begin () +{ + char *prev_name = ""; + register const struct tic80_opcode *op; + register const struct tic80_opcode *op_end; + const struct predefined_symbol *pdsp; + extern int coff_flags; /* Defined in obj-coff.c */ + + /* Set F_AR32WR in coff_flags, which will end up in the file header + f_flags field. */ + + coff_flags |= F_AR32WR; /* TIc80 is 32 bit little endian */ + + /* Insert unique names into hash table. The TIc80 instruction set + has many identical opcode names that have different opcodes based + on the operands. This hash table then provides a quick index to + the first opcode with a particular name in the opcode table. */ + + tic80_hash = hash_new (); + op_end = tic80_opcodes + tic80_num_opcodes; + for (op = tic80_opcodes; op < op_end; op++) + { + if (strcmp (prev_name, op -> name) != 0) + { + prev_name = (char *) op -> name; + hash_insert (tic80_hash, op -> name, (char *) op); + } + } + + /* Insert the predefined symbols into the symbol table. We use symbol_create + rather than symbol_new so that these symbols don't end up in the object + files' symbol table. Note that the values of the predefined symbols include + some upper bits that distinguish the type of the symbol (register, bitnum, + condition code, etc) and these bits must be masked away before actually + inserting the values into the instruction stream. For registers we put + these bits in the symbol table since we use them later and there is no + question that they aren't part of the register number. For constants we + can't do that since the constant can be any value, so they are masked off + before putting them into the symbol table. */ + + pdsp = NULL; + while ((pdsp = tic80_next_predefined_symbol (pdsp)) != NULL) + { + segT segment; + valueT valu; + int symtype; + + symtype = PDS_VALUE (pdsp) & TIC80_OPERAND_MASK; + switch (symtype) + { + case TIC80_OPERAND_GPR: + case TIC80_OPERAND_FPA: + case TIC80_OPERAND_CR: + segment = reg_section; + valu = PDS_VALUE (pdsp); + break; + case TIC80_OPERAND_CC: + case TIC80_OPERAND_BITNUM: + segment = absolute_section; + valu = PDS_VALUE (pdsp) & ~TIC80_OPERAND_MASK; + break; + default: + internal_error_a (_("unhandled predefined symbol bits"), symtype); + break; + } + symbol_table_insert (symbol_create (PDS_NAME (pdsp), segment, valu, + &zero_address_frag)); + } +} + + + +/* The assembler adds md_shortopts to the string passed to getopt. */ + +CONST char *md_shortopts = ""; + +/* The assembler adds md_longopts to the machine independent long options + that are passed to getopt. */ + +struct option md_longopts[] = { + +#define OPTION_RELAX (OPTION_MD_BASE) + {"relax", no_argument, NULL, OPTION_RELAX}, + +#define OPTION_NO_RELAX (OPTION_RELAX + 1) + {"no-relax", no_argument, NULL, OPTION_NO_RELAX}, + + {NULL, no_argument, NULL, 0} +}; + +size_t md_longopts_size = sizeof(md_longopts); + +/* The md_parse_option function will be called whenever getopt returns an + unrecognized code, presumably indicating a special code value which + appears in md_longopts for machine specific command line options. */ + +int +md_parse_option (c, arg) + int c; + char *arg; +{ + switch (c) + { + case OPTION_RELAX: + tic80_relax = 1; + break; + case OPTION_NO_RELAX: + tic80_relax = 0; + break; + default: + return (0); + } + return (1); +} + +/* The md_show_usage function will be called whenever a usage message is + printed. It should print a description of the machine specific options + found in md_longopts. */ + +void +md_show_usage (stream) + FILE *stream; +{ + fprintf (stream, "\ +TIc80 options:\n\ +-relax alter PC relative branch instructions to use long form when needed\n\ +-no-relax always use short PC relative branch instructions, error on overflow\n"); +} + + +/* Attempt to simplify or even eliminate a fixup. The return value is + ignored; perhaps it was once meaningful, but now it is historical. + To indicate that a fixup has been eliminated, set fixP->fx_done. + */ + +void +md_apply_fix (fixP, val) + fixS *fixP; + long val; +{ + char *dest = fixP -> fx_frag -> fr_literal + fixP -> fx_where; + int overflow; + + switch (fixP -> fx_r_type) + { + case R_RELLONGX: + md_number_to_chars (dest, (valueT) val, 4); + break; + case R_MPPCR: + val >>= 2; + val += 1; /* Target address computed from inst start */ + md_number_to_chars (dest, (valueT) val, 4); + break; + case R_MPPCR15W: + overflow = (val < -65536L) || (val > 65532L); + if (overflow) + { + as_bad_where (fixP -> fx_file, fixP -> fx_line, + _("PC offset 0x%lx outside range 0x%lx-0x%lx"), + val, -65536L, 65532L); + } + else + { + val >>= 2; + *dest++ = val & 0xFF; + val >>= 8; + *dest = (*dest & 0x80) | (val & 0x7F); + } + break; + case R_ABS: + md_number_to_chars (dest, (valueT) val, fixP -> fx_size); + break; + default: + internal_error_a (_("unhandled relocation type in fixup"), fixP -> fx_r_type); + break; + } +} + + +/* Functions concerning relocs. */ + +/* The location from which a PC relative jump should be calculated, + given a PC relative reloc. + + For the TIc80, this is the address of the 32 bit opcode containing + the PC relative field. */ + +long +md_pcrel_from (fixP) + fixS *fixP; +{ + return (fixP -> fx_frag -> fr_address + fixP -> fx_where) ; +} + +/* + * Called after relax() is finished. + * In: Address of frag. + * fr_type == rs_machine_dependent. + * fr_subtype is what the address relaxed to. + * + * Out: Any fixSs and constants are set up. + * Caller will turn frag into a ".space 0". + */ + +void +md_convert_frag (headers, seg, fragP) + object_headers *headers; + segT seg; + fragS *fragP; +{ + internal_error (_("md_convert_frag() not implemented yet")); + abort (); +} + + +/*ARGSUSED*/ +void +tc_coff_symbol_emit_hook (ignore) + symbolS *ignore; +{ +} + +#if defined OBJ_COFF + +short +tc_coff_fix2rtype (fixP) + fixS *fixP; +{ + return (fixP -> fx_r_type); +} + +#endif /* OBJ_COFF */ + +/* end of tc-tic80.c */ |