diff options
Diffstat (limited to 'gas/config/tc-tic30.c')
-rw-r--r-- | gas/config/tc-tic30.c | 1887 |
1 files changed, 1887 insertions, 0 deletions
diff --git a/gas/config/tc-tic30.c b/gas/config/tc-tic30.c new file mode 100644 index 0000000..61ed905 --- /dev/null +++ b/gas/config/tc-tic30.c @@ -0,0 +1,1887 @@ +/* tc-c30.c -- Assembly code for the Texas Instruments TMS320C30 + Copyright (C) 1998 Free Software Foundation. + Contributed by Steven Haworth (steve@pm.cse.rmit.edu.au) + + 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. */ + +/* + Texas Instruments TMS320C30 machine specific gas. + Written by Steven Haworth (steve@pm.cse.rmit.edu.au). + Bugs & suggestions are completely welcome. This is free software. + Please help us make it better. + */ + +#include "as.h" +#include "opcode/tic30.h" + +/* put here all non-digit non-letter charcters that may occur in an operand */ +static char operand_special_chars[] = "%$-+(,)*._~/<>&^!:[@]"; +static char *ordinal_names[] = +{"first", "second", "third", "fourth", "fifth"}; + +const int md_reloc_size = 0; + +const char comment_chars[] = ";"; +const char line_comment_chars[] = "*"; +const char line_separator_chars[] = ""; + +const char *md_shortopts = ""; +struct option md_longopts[] = +{ + {NULL, no_argument, NULL, 0} +}; + +size_t md_longopts_size = sizeof (md_longopts); + +/* Chars that mean this number is a floating point constant */ +/* As in 0f12.456 */ +/* or 0d1.2345e12 */ +const char FLT_CHARS[] = "fFdDxX"; + +/* Chars that can be used to separate mant from exp in floating point nums */ +const char EXP_CHARS[] = "eE"; + +/* tables for lexical analysis */ +static char opcode_chars[256]; +static char register_chars[256]; +static char operand_chars[256]; +static char space_chars[256]; +static char identifier_chars[256]; +static char digit_chars[256]; + +/* lexical macros */ +#define is_opcode_char(x) (opcode_chars[(unsigned char) x]) +#define is_operand_char(x) (operand_chars[(unsigned char) x]) +#define is_register_char(x) (register_chars[(unsigned char) x]) +#define is_space_char(x) (space_chars[(unsigned char) x]) +#define is_identifier_char(x) (identifier_chars[(unsigned char) x]) +#define is_digit_char(x) (digit_chars[(unsigned char) x]) + +const pseudo_typeS md_pseudo_table[] = +{ + {0, 0, 0} +}; + +#undef USE_STDOUT +#define USE_STDOUT 1 + +#ifdef USE_STDARG + +#include <stdarg.h> + +int +debug (const char *string,...) +{ + if (flag_debug) + { + va_list argptr; + char str[100]; + + va_start (argptr, string); + vsprintf (str, string, argptr); + if (str[0] == '\0') + return (0); + va_end (argptr); + fputs (str, USE_STDOUT ? stdout : stderr); + return strlen (str); + } + else + return 0; +} +#else +int +debug (string, va_alist) + const char *string; + va_dcl +{ + if (flag_debug) + { + va_list argptr; + char str[100]; + int cnt; + + va_start (argptr, string); + cnt = vsprintf (str, string, argptr); + if (str[0] == NULL) + return (0); + va_end (argptr); + fputs (str, USE_STDOUT ? stdout : stderr); + return (cnt); + } + else + return 0; +} +#endif + +/* hash table for opcode lookup */ +static struct hash_control *op_hash; +/* hash table for parallel opcode lookup */ +static struct hash_control *parop_hash; +/* hash table for register lookup */ +static struct hash_control *reg_hash; +/* hash table for indirect addressing lookup */ +static struct hash_control *ind_hash; + +void +md_begin () +{ + const char *hash_err; + debug ("In md_begin()\n"); + op_hash = hash_new (); + { + const template *current_optab = tic30_optab; + for (; current_optab < tic30_optab_end; current_optab++) + { + hash_err = hash_insert (op_hash, current_optab->name, (char *) current_optab); + if (hash_err) + as_fatal ("Internal Error: Can't Hash %s: %s", current_optab->name, hash_err); + } + } + parop_hash = hash_new (); + { + const partemplate *current_parop = tic30_paroptab; + for (; current_parop < tic30_paroptab_end; current_parop++) + { + hash_err = hash_insert (parop_hash, current_parop->name, (char *) current_parop); + if (hash_err) + as_fatal ("Internal Error: Can't Hash %s: %s", current_parop->name, hash_err); + } + } + reg_hash = hash_new (); + { + const reg *current_reg = tic30_regtab; + for (; current_reg < tic30_regtab_end; current_reg++) + { + hash_err = hash_insert (reg_hash, current_reg->name, (char *) current_reg); + if (hash_err) + as_fatal ("Internal Error: Can't Hash %s: %s", current_reg->name, hash_err); + } + } + ind_hash = hash_new (); + { + const ind_addr_type *current_ind = tic30_indaddr_tab; + for (; current_ind < tic30_indaddrtab_end; current_ind++) + { + hash_err = hash_insert (ind_hash, current_ind->syntax, (char *) current_ind); + if (hash_err) + as_fatal ("Internal Error: Can't Hash %s: %s", current_ind->syntax, hash_err); + } + } + /* fill in lexical tables: opcode_chars, operand_chars, space_chars */ + { + register int c; + register char *p; + + for (c = 0; c < 256; c++) + { + if (islower (c) || isdigit (c)) + { + opcode_chars[c] = c; + register_chars[c] = c; + } + else if (isupper (c)) + { + opcode_chars[c] = tolower (c); + register_chars[c] = opcode_chars[c]; + } + else if (c == ')' || c == '(') + { + register_chars[c] = c; + } + if (isupper (c) || islower (c) || isdigit (c)) + operand_chars[c] = c; + if (isdigit (c) || c == '-') + digit_chars[c] = c; + if (isalpha (c) || c == '_' || c == '.' || isdigit (c)) + identifier_chars[c] = c; + if (c == ' ' || c == '\t') + space_chars[c] = c; + if (c == '_') + opcode_chars[c] = c; + } + for (p = operand_special_chars; *p != '\0'; p++) + operand_chars[(unsigned char) *p] = *p; + } +} + +/* Address Mode OR values */ +#define AM_Register 0x00000000 +#define AM_Direct 0x00200000 +#define AM_Indirect 0x00400000 +#define AM_Immediate 0x00600000 +#define AM_NotReq 0xFFFFFFFF + +/* PC Relative OR values */ +#define PC_Register 0x00000000 +#define PC_Relative 0x02000000 + +typedef struct +{ + unsigned op_type; + struct + { + int resolved; + unsigned address; + char *label; + expressionS direct_expr; + } + direct; + struct + { + unsigned mod; + int ARnum; + unsigned char disp; + } + indirect; + struct + { + unsigned opcode; + } + reg; + struct + { + int resolved; + int decimal_found; + float f_number; + int s_number; + unsigned int u_number; + char *label; + expressionS imm_expr; + } + immediate; +} +operand; + +int tic30_parallel_insn PARAMS ((char *)); +operand *tic30_operand PARAMS ((char *)); +char *tic30_find_parallel_insn PARAMS ((char *, char *)); + +template *opcode; + +struct tic30_insn + { + template *tm; /* Template of current instruction */ + unsigned opcode; /* Final opcode */ + int operands; /* Number of given operands */ + /* Type of operand given in instruction */ + operand *operand_type[MAX_OPERANDS]; + unsigned addressing_mode; /* Final addressing mode of instruction */ + }; + +struct tic30_insn insn; +static int found_parallel_insn; + +void +md_assemble (line) + char *line; +{ + template *opcode; + char *current_posn; + char *token_start; + char save_char; + int count; + + debug ("In md_assemble() with argument %s\n", line); + memset (&insn, '\0', sizeof (insn)); + if (found_parallel_insn) + { + debug ("Line is second part of parallel instruction\n\n"); + found_parallel_insn = 0; + return; + } + if ((current_posn = tic30_find_parallel_insn (line, input_line_pointer + 1)) == NULL) + current_posn = line; + else + found_parallel_insn = 1; + while (is_space_char (*current_posn)) + current_posn++; + token_start = current_posn; + if (!is_opcode_char (*current_posn)) + { + as_bad ("Invalid character %s in opcode", output_invalid (*current_posn)); + return; + } + /* Check if instruction is a parallel instruction by seeing if the first + character is a q. */ + if (*token_start == 'q') + { + if (tic30_parallel_insn (token_start)) + { + if (found_parallel_insn) + free (token_start); + return; + } + } + while (is_opcode_char (*current_posn)) + current_posn++; + { /* Find instruction */ + save_char = *current_posn; + *current_posn = '\0'; + opcode = (template *) hash_find (op_hash, token_start); + if (opcode) + { + debug ("Found instruction %s\n", opcode->name); + insn.tm = opcode; + } + else + { + debug ("Didn't find insn\n"); + as_bad ("Unknown TMS320C30 instruction: %s", token_start); + return; + } + *current_posn = save_char; + } + if (*current_posn != END_OF_INSN) + { /* Find operands */ + int paren_not_balanced; + int expecting_operand = 0; + int this_operand; + do + { + /* skip optional white space before operand */ + while (!is_operand_char (*current_posn) && *current_posn != END_OF_INSN) + { + if (!is_space_char (*current_posn)) + { + as_bad ("Invalid character %s before %s operand", + output_invalid (*current_posn), + ordinal_names[insn.operands]); + return; + } + current_posn++; + } + token_start = current_posn; /* after white space */ + paren_not_balanced = 0; + while (paren_not_balanced || *current_posn != ',') + { + if (*current_posn == END_OF_INSN) + { + if (paren_not_balanced) + { + as_bad ("Unbalanced parenthesis in %s operand.", + ordinal_names[insn.operands]); + return; + } + else + break; /* we are done */ + } + else if (!is_operand_char (*current_posn) && !is_space_char (*current_posn)) + { + as_bad ("Invalid character %s in %s operand", + output_invalid (*current_posn), + ordinal_names[insn.operands]); + return; + } + if (*current_posn == '(') + ++paren_not_balanced; + if (*current_posn == ')') + --paren_not_balanced; + current_posn++; + } + if (current_posn != token_start) + { /* yes, we've read in another operand */ + this_operand = insn.operands++; + if (insn.operands > MAX_OPERANDS) + { + as_bad ("Spurious operands; (%d operands/instruction max)", + MAX_OPERANDS); + return; + } + /* now parse operand adding info to 'insn' as we go along */ + save_char = *current_posn; + *current_posn = '\0'; + insn.operand_type[this_operand] = tic30_operand (token_start); + *current_posn = save_char; + if (insn.operand_type[this_operand] == NULL) + return; + } + else + { + if (expecting_operand) + { + as_bad ("Expecting operand after ','; got nothing"); + return; + } + if (*current_posn == ',') + { + as_bad ("Expecting operand before ','; got nothing"); + return; + } + } + /* now *current_posn must be either ',' or END_OF_INSN */ + if (*current_posn == ',') + { + if (*++current_posn == END_OF_INSN) + { /* just skip it, if it's \n complain */ + as_bad ("Expecting operand after ','; got nothing"); + return; + } + expecting_operand = 1; + } + } + while (*current_posn != END_OF_INSN); /* until we get end of insn */ + } + debug ("Number of operands found: %d\n", insn.operands); + /* Check that number of operands is correct */ + if (insn.operands != insn.tm->operands) + { + int i; + int numops = insn.tm->operands; + /* If operands are not the same, then see if any of the operands are not + required. Then recheck with number of given operands. If they are still not + the same, then give an error, otherwise carry on. */ + for (i = 0; i < insn.tm->operands; i++) + if (insn.tm->operand_types[i] & NotReq) + numops--; + if (insn.operands != numops) + { + as_bad ("Incorrect number of operands given"); + return; + } + } + insn.addressing_mode = AM_NotReq; + for (count = 0; count < insn.operands; count++) + { + if (insn.operand_type[count]->op_type & insn.tm->operand_types[count]) + { + debug ("Operand %d matches\n", count + 1); + /* If instruction has two operands and has an AddressMode modifier then set + addressing mode type for instruction */ + if (insn.tm->opcode_modifier == AddressMode) + { + int addr_insn = 0; + /* Store instruction uses the second operand for the address mode. */ + if ((insn.tm->operand_types[1] & (Indirect | Direct)) == (Indirect | Direct)) + addr_insn = 1; + if (insn.operand_type[addr_insn]->op_type & (AllReg)) + insn.addressing_mode = AM_Register; + else if (insn.operand_type[addr_insn]->op_type & Direct) + insn.addressing_mode = AM_Direct; + else if (insn.operand_type[addr_insn]->op_type & Indirect) + insn.addressing_mode = AM_Indirect; + else + insn.addressing_mode = AM_Immediate; + } + } + else + { + as_bad ("The %s operand doesn't match", ordinal_names[count]); + return; + } + } + /* Now set the addressing mode for 3 operand instructions. */ + if ((insn.tm->operand_types[0] & op3T1) && (insn.tm->operand_types[1] & op3T2)) + { + /* Set the addressing mode to the values used for 2 operand instructions in the + G addressing field of the opcode. */ + char *p; + switch (insn.operand_type[0]->op_type) + { + case Rn: + case ARn: + case DPReg: + case OtherReg: + if (insn.operand_type[1]->op_type & (AllReg)) + insn.addressing_mode = AM_Register; + else if (insn.operand_type[1]->op_type & Indirect) + insn.addressing_mode = AM_Direct; + else + { + /* Shouldn't make it to this stage */ + as_bad ("Incompatible first and second operands in instruction"); + return; + } + break; + case Indirect: + if (insn.operand_type[1]->op_type & (AllReg)) + insn.addressing_mode = AM_Indirect; + else if (insn.operand_type[1]->op_type & Indirect) + insn.addressing_mode = AM_Immediate; + else + { + /* Shouldn't make it to this stage */ + as_bad ("Incompatible first and second operands in instruction"); + return; + } + break; + } + /* Now make up the opcode for the 3 operand instructions. As in parallel + instructions, there will be no unresolved values, so they can be fully formed + and added to the frag table. */ + insn.opcode = insn.tm->base_opcode; + if (insn.operand_type[0]->op_type & Indirect) + { + insn.opcode |= (insn.operand_type[0]->indirect.ARnum); + insn.opcode |= (insn.operand_type[0]->indirect.mod << 3); + } + else + insn.opcode |= (insn.operand_type[0]->reg.opcode); + if (insn.operand_type[1]->op_type & Indirect) + { + insn.opcode |= (insn.operand_type[1]->indirect.ARnum << 8); + insn.opcode |= (insn.operand_type[1]->indirect.mod << 11); + } + else + insn.opcode |= (insn.operand_type[1]->reg.opcode << 8); + if (insn.operands == 3) + insn.opcode |= (insn.operand_type[2]->reg.opcode << 16); + insn.opcode |= insn.addressing_mode; + p = frag_more (INSN_SIZE); + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else + { /* Not a three operand instruction */ + char *p; + int am_insn = -1; + insn.opcode = insn.tm->base_opcode; + /* Create frag for instruction - all instructions are 4 bytes long. */ + p = frag_more (INSN_SIZE); + if ((insn.operands > 0) && (insn.tm->opcode_modifier == AddressMode)) + { + insn.opcode |= insn.addressing_mode; + if (insn.addressing_mode == AM_Indirect) + { + /* Determine which operand gives the addressing mode */ + if (insn.operand_type[0]->op_type & Indirect) + am_insn = 0; + if ((insn.operands > 1) && (insn.operand_type[1]->op_type & Indirect)) + am_insn = 1; + insn.opcode |= (insn.operand_type[am_insn]->indirect.disp); + insn.opcode |= (insn.operand_type[am_insn]->indirect.ARnum << 8); + insn.opcode |= (insn.operand_type[am_insn]->indirect.mod << 11); + if (insn.operands > 1) + insn.opcode |= (insn.operand_type[!am_insn]->reg.opcode << 16); + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else if (insn.addressing_mode == AM_Register) + { + insn.opcode |= (insn.operand_type[0]->reg.opcode); + if (insn.operands > 1) + insn.opcode |= (insn.operand_type[1]->reg.opcode << 16); + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else if (insn.addressing_mode == AM_Direct) + { + if (insn.operand_type[0]->op_type & Direct) + am_insn = 0; + if ((insn.operands > 1) && (insn.operand_type[1]->op_type & Direct)) + am_insn = 1; + if (insn.operands > 1) + insn.opcode |= (insn.operand_type[!am_insn]->reg.opcode << 16); + if (insn.operand_type[am_insn]->direct.resolved == 1) + { + /* Resolved values can be placed straight into instruction word, and output */ + insn.opcode |= (insn.operand_type[am_insn]->direct.address & 0x0000FFFF); + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else + { /* Unresolved direct addressing mode instruction */ + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + fix_new_exp (frag_now, p + 2 - (frag_now->fr_literal), 2, &insn.operand_type[am_insn]->direct.direct_expr, 0, 0); + } + } + else if (insn.addressing_mode == AM_Immediate) + { + if (insn.operand_type[0]->immediate.resolved == 1) + { + char *keeploc; + int size; + if (insn.operands > 1) + insn.opcode |= (insn.operand_type[1]->reg.opcode << 16); + switch (insn.tm->imm_arg_type) + { + case Imm_Float: + debug ("Floating point first operand\n"); + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + keeploc = input_line_pointer; + input_line_pointer = insn.operand_type[0]->immediate.label; + if (md_atof ('f', p + 2, &size) != 0) + { + as_bad ("invalid short form floating point immediate operand"); + return; + } + input_line_pointer = keeploc; + break; + case Imm_UInt: + debug ("Unsigned int first operand\n"); + if (insn.operand_type[0]->immediate.decimal_found) + as_warn ("rounding down first operand float to unsigned int"); + if (insn.operand_type[0]->immediate.u_number > 0xFFFF) + as_warn ("only lower 16-bits of first operand are used"); + insn.opcode |= (insn.operand_type[0]->immediate.u_number & 0x0000FFFFL); + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + break; + case Imm_SInt: + debug ("Int first operand\n"); + if (insn.operand_type[0]->immediate.decimal_found) + as_warn ("rounding down first operand float to signed int"); + if (insn.operand_type[0]->immediate.s_number < -32768 || + insn.operand_type[0]->immediate.s_number > 32767) + { + as_bad ("first operand is too large for 16-bit signed int"); + return; + } + insn.opcode |= (insn.operand_type[0]->immediate.s_number & 0x0000FFFFL); + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + break; + } + } + else + { /* Unresolved immediate label */ + if (insn.operands > 1) + insn.opcode |= (insn.operand_type[1]->reg.opcode << 16); + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + fix_new_exp (frag_now, p + 2 - (frag_now->fr_literal), 2, &insn.operand_type[0]->immediate.imm_expr, 0, 0); + } + } + } + else if (insn.tm->opcode_modifier == PCRel) + { + /* Conditional Branch and Call instructions */ + if ((insn.tm->operand_types[0] & (AllReg | Disp)) == (AllReg | Disp)) + { + if (insn.operand_type[0]->op_type & (AllReg)) + { + insn.opcode |= (insn.operand_type[0]->reg.opcode); + insn.opcode |= PC_Register; + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else + { + insn.opcode |= PC_Relative; + if (insn.operand_type[0]->immediate.resolved == 1) + { + insn.opcode |= (insn.operand_type[0]->immediate.s_number & 0x0000FFFF); + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else + { + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + fix_new_exp (frag_now, p + 2 - (frag_now->fr_literal), 2, &insn.operand_type[0]->immediate.imm_expr, 1, 0); + } + } + } + else if ((insn.tm->operand_types[0] & ARn) == ARn) + { + /* Decrement and Branch instructions */ + insn.opcode |= ((insn.operand_type[0]->reg.opcode - 0x08) << 22); + if (insn.operand_type[1]->op_type & (AllReg)) + { + insn.opcode |= (insn.operand_type[1]->reg.opcode); + insn.opcode |= PC_Register; + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else if (insn.operand_type[1]->immediate.resolved == 1) + { + if (insn.operand_type[0]->immediate.decimal_found) + { + as_bad ("first operand is floating point"); + return; + } + if (insn.operand_type[0]->immediate.s_number < -32768 || + insn.operand_type[0]->immediate.s_number > 32767) + { + as_bad ("first operand is too large for 16-bit signed int"); + return; + } + insn.opcode |= (insn.operand_type[1]->immediate.s_number); + insn.opcode |= PC_Relative; + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else + { + insn.opcode |= PC_Relative; + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + fix_new_exp (frag_now, p + 2 - frag_now->fr_literal, 2, &insn.operand_type[1]->immediate.imm_expr, 1, 0); + } + } + } + else if (insn.tm->operand_types[0] == IVector) + { + /* Trap instructions */ + if (insn.operand_type[0]->op_type & IVector) + insn.opcode |= (insn.operand_type[0]->immediate.u_number); + else + { /* Shouldn't get here */ + as_bad ("interrupt vector for trap instruction out of range"); + return; + } + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else if (insn.tm->opcode_modifier == StackOp || insn.tm->opcode_modifier == Rotate) + { + /* Push, Pop and Rotate instructions */ + insn.opcode |= (insn.operand_type[0]->reg.opcode << 16); + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else if ((insn.tm->operand_types[0] & (Abs24 | Direct)) == (Abs24 | Direct)) + { + /* LDP Instruction needs to be tested for before the next section */ + if (insn.operand_type[0]->op_type & Direct) + { + if (insn.operand_type[0]->direct.resolved == 1) + { + /* Direct addressing uses lower 8 bits of direct address */ + insn.opcode |= (insn.operand_type[0]->direct.address & 0x00FF0000) >> 16; + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else + { + fixS *fix; + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + fix = fix_new_exp (frag_now, p + 3 - (frag_now->fr_literal), 1, &insn.operand_type[0]->direct.direct_expr, 0, 0); + /* Ensure that the assembler doesn't complain about fitting a 24-bit + address into 8 bits. */ + fix->fx_no_overflow = 1; + } + } + else + { + if (insn.operand_type[0]->immediate.resolved == 1) + { + /* Immediate addressing uses upper 8 bits of address */ + if (insn.operand_type[0]->immediate.u_number > 0x00FFFFFF) + { + as_bad ("LDP instruction needs a 24-bit operand"); + return; + } + insn.opcode |= ((insn.operand_type[0]->immediate.u_number & 0x00FF0000) >> 16); + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else + { + fixS *fix; + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + fix = fix_new_exp (frag_now, p + 3 - (frag_now->fr_literal), 1, &insn.operand_type[0]->immediate.imm_expr, 0, 0); + fix->fx_no_overflow = 1; + } + } + } + else if (insn.tm->operand_types[0] & (Imm24)) + { + /* Unconditional Branch and Call instructions */ + if (insn.operand_type[0]->immediate.resolved == 1) + { + if (insn.operand_type[0]->immediate.u_number > 0x00FFFFFF) + as_warn ("first operand is too large for a 24-bit displacement"); + insn.opcode |= (insn.operand_type[0]->immediate.u_number & 0x00FFFFFF); + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else + { + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + fix_new_exp (frag_now, p + 1 - (frag_now->fr_literal), 3, &insn.operand_type[0]->immediate.imm_expr, 0, 0); + } + } + else if (insn.tm->operand_types[0] & NotReq) + { + /* Check for NOP instruction without arguments. */ + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + else if (insn.tm->operands == 0) + { + /* Check for instructions without operands. */ + md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE); + } + } + debug ("Addressing mode: %08X\n", insn.addressing_mode); + { + int i; + for (i = 0; i < insn.operands; i++) + { + if (insn.operand_type[i]->immediate.label) + free (insn.operand_type[i]->immediate.label); + free (insn.operand_type[i]); + } + } + debug ("Final opcode: %08X\n", insn.opcode); + debug ("\n"); +} + +struct tic30_par_insn +{ + partemplate *tm; /* Template of current parallel instruction */ + int operands[2]; /* Number of given operands for each insn */ + /* Type of operand given in instruction */ + operand *operand_type[2][MAX_OPERANDS]; + int swap_operands; /* Whether to swap operands around. */ + unsigned p_field; /* Value of p field in multiply add/sub instructions */ + unsigned opcode; /* Final opcode */ +}; + +struct tic30_par_insn p_insn; + +int +tic30_parallel_insn (char *token) +{ + static partemplate *p_opcode; + char *current_posn = token; + char *token_start; + char save_char; + + debug ("In tic30_parallel_insn with %s\n", token); + memset (&p_insn, '\0', sizeof (p_insn)); + while (is_opcode_char (*current_posn)) + current_posn++; + { /* Find instruction */ + save_char = *current_posn; + *current_posn = '\0'; + p_opcode = (partemplate *) hash_find (parop_hash, token); + if (p_opcode) + { + debug ("Found instruction %s\n", p_opcode->name); + p_insn.tm = p_opcode; + } + else + { + char first_opcode[6] = + {0}; + char second_opcode[6] = + {0}; + int i; + int current_opcode = -1; + int char_ptr = 0; + + for (i = 0; i < strlen (token); i++) + { + char ch = *(token + i); + if (ch == '_' && current_opcode == -1) + { + current_opcode = 0; + continue; + } + if (ch == '_' && current_opcode == 0) + { + current_opcode = 1; + char_ptr = 0; + continue; + } + switch (current_opcode) + { + case 0: + first_opcode[char_ptr++] = ch; + break; + case 1: + second_opcode[char_ptr++] = ch; + break; + } + } + debug ("first_opcode = %s\n", first_opcode); + debug ("second_opcode = %s\n", second_opcode); + sprintf (token, "q_%s_%s", second_opcode, first_opcode); + p_opcode = (partemplate *) hash_find (parop_hash, token); + if (p_opcode) + { + debug ("Found instruction %s\n", p_opcode->name); + p_insn.tm = p_opcode; + p_insn.swap_operands = 1; + } + else + return 0; + } + *current_posn = save_char; + } + { /* Find operands */ + int paren_not_balanced; + int expecting_operand = 0; + int found_separator = 0; + do + { + /* skip optional white space before operand */ + while (!is_operand_char (*current_posn) && *current_posn != END_OF_INSN) + { + if (!is_space_char (*current_posn) && *current_posn != PARALLEL_SEPARATOR) + { + as_bad ("Invalid character %s before %s operand", + output_invalid (*current_posn), + ordinal_names[insn.operands]); + return 1; + } + if (*current_posn == PARALLEL_SEPARATOR) + found_separator = 1; + current_posn++; + } + token_start = current_posn; /* after white space */ + paren_not_balanced = 0; + while (paren_not_balanced || *current_posn != ',') + { + if (*current_posn == END_OF_INSN) + { + if (paren_not_balanced) + { + as_bad ("Unbalanced parenthesis in %s operand.", + ordinal_names[insn.operands]); + return 1; + } + else + break; /* we are done */ + } + else if (*current_posn == PARALLEL_SEPARATOR) + { + while (is_space_char (*(current_posn - 1))) + current_posn--; + break; + } + else if (!is_operand_char (*current_posn) && !is_space_char (*current_posn)) + { + as_bad ("Invalid character %s in %s operand", + output_invalid (*current_posn), + ordinal_names[insn.operands]); + return 1; + } + if (*current_posn == '(') + ++paren_not_balanced; + if (*current_posn == ')') + --paren_not_balanced; + current_posn++; + } + if (current_posn != token_start) + { /* yes, we've read in another operand */ + p_insn.operands[found_separator]++; + if (p_insn.operands[found_separator] > MAX_OPERANDS) + { + as_bad ("Spurious operands; (%d operands/instruction max)", + MAX_OPERANDS); + return 1; + } + /* now parse operand adding info to 'insn' as we go along */ + save_char = *current_posn; + *current_posn = '\0'; + p_insn.operand_type[found_separator][p_insn.operands[found_separator] - 1] = + tic30_operand (token_start); + *current_posn = save_char; + if (!p_insn.operand_type[found_separator][p_insn.operands[found_separator] - 1]) + return 1; + } + else + { + if (expecting_operand) + { + as_bad ("Expecting operand after ','; got nothing"); + return 1; + } + if (*current_posn == ',') + { + as_bad ("Expecting operand before ','; got nothing"); + return 1; + } + } + /* now *current_posn must be either ',' or END_OF_INSN */ + if (*current_posn == ',') + { + if (*++current_posn == END_OF_INSN) + { /* just skip it, if it's \n complain */ + as_bad ("Expecting operand after ','; got nothing"); + return 1; + } + expecting_operand = 1; + } + } + while (*current_posn != END_OF_INSN); /* until we get end of insn */ + } + if (p_insn.swap_operands) + { + int temp_num, i; + operand *temp_op; + + temp_num = p_insn.operands[0]; + p_insn.operands[0] = p_insn.operands[1]; + p_insn.operands[1] = temp_num; + for (i = 0; i < MAX_OPERANDS; i++) + { + temp_op = p_insn.operand_type[0][i]; + p_insn.operand_type[0][i] = p_insn.operand_type[1][i]; + p_insn.operand_type[1][i] = temp_op; + } + } + if (p_insn.operands[0] != p_insn.tm->operands_1) + { + as_bad ("incorrect number of operands given in the first instruction"); + return 1; + } + if (p_insn.operands[1] != p_insn.tm->operands_2) + { + as_bad ("incorrect number of operands given in the second instruction"); + return 1; + } + debug ("Number of operands in first insn: %d\n", p_insn.operands[0]); + debug ("Number of operands in second insn: %d\n", p_insn.operands[1]); + { /* Now check if operands are correct */ + int count; + int num_rn = 0; + int num_ind = 0; + for (count = 0; count < 2; count++) + { + int i; + for (i = 0; i < p_insn.operands[count]; i++) + { + if ((p_insn.operand_type[count][i]->op_type & + p_insn.tm->operand_types[count][i]) == 0) + { + as_bad ("%s instruction, operand %d doesn't match", ordinal_names[count], i + 1); + return 1; + } + /* Get number of R register and indirect reference contained within the first + two operands of each instruction. This is required for the multiply + parallel instructions which require two R registers and two indirect + references, but not in any particular place. */ + if ((p_insn.operand_type[count][i]->op_type & Rn) && i < 2) + num_rn++; + else if ((p_insn.operand_type[count][i]->op_type & Indirect) && i < 2) + num_ind++; + } + } + if ((p_insn.tm->operand_types[0][0] & (Indirect | Rn)) == (Indirect | Rn)) + { + /* Check for the multiply instructions */ + if (num_rn != 2) + { + as_bad ("incorrect format for multiply parallel instruction"); + return 1; + } + if (num_ind != 2) + { /* Shouldn't get here */ + as_bad ("incorrect format for multiply parallel instruction"); + return 1; + } + if ((p_insn.operand_type[0][2]->reg.opcode != 0x00) && + (p_insn.operand_type[0][2]->reg.opcode != 0x01)) + { + as_bad ("destination for multiply can only be R0 or R1"); + return 1; + } + if ((p_insn.operand_type[1][2]->reg.opcode != 0x02) && + (p_insn.operand_type[1][2]->reg.opcode != 0x03)) + { + as_bad ("destination for add/subtract can only be R2 or R3"); + return 1; + } + /* Now determine the P field for the instruction */ + if (p_insn.operand_type[0][0]->op_type & Indirect) + { + if (p_insn.operand_type[0][1]->op_type & Indirect) + p_insn.p_field = 0x00000000; /* Ind * Ind, Rn +/- Rn */ + else if (p_insn.operand_type[1][0]->op_type & Indirect) + p_insn.p_field = 0x01000000; /* Ind * Rn, Ind +/- Rn */ + else + p_insn.p_field = 0x03000000; /* Ind * Rn, Rn +/- Ind */ + } + else + { + if (p_insn.operand_type[0][1]->op_type & Rn) + p_insn.p_field = 0x02000000; /* Rn * Rn, Ind +/- Ind */ + else if (p_insn.operand_type[1][0]->op_type & Indirect) + { + operand *temp; + p_insn.p_field = 0x01000000; /* Rn * Ind, Ind +/- Rn */ + /* Need to swap the two multiply operands around so that everything is in + its place for the opcode makeup ie so Ind * Rn, Ind +/- Rn */ + temp = p_insn.operand_type[0][0]; + p_insn.operand_type[0][0] = p_insn.operand_type[0][1]; + p_insn.operand_type[0][1] = temp; + } + else + { + operand *temp; + p_insn.p_field = 0x03000000; /* Rn * Ind, Rn +/- Ind */ + temp = p_insn.operand_type[0][0]; + p_insn.operand_type[0][0] = p_insn.operand_type[0][1]; + p_insn.operand_type[0][1] = temp; + } + } + } + } + debug ("P field: %08X\n", p_insn.p_field); + /* Finalise opcode. This is easier for parallel instructions as they have to be + fully resolved, there are no memory addresses allowed, except through indirect + addressing, so there are no labels to resolve. */ + { + p_insn.opcode = p_insn.tm->base_opcode; + switch (p_insn.tm->oporder) + { + case OO_4op1: + p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum); + p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 3); + p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8); + p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11); + p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16); + p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 22); + break; + case OO_4op2: + p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum); + p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 3); + p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.ARnum << 8); + p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.mod << 11); + p_insn.opcode |= (p_insn.operand_type[1][1]->reg.opcode << 19); + p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 22); + if (p_insn.operand_type[1][1]->reg.opcode == p_insn.operand_type[0][1]->reg.opcode) + as_warn ("loading the same register in parallel operation"); + break; + case OO_4op3: + p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.ARnum); + p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.mod << 3); + p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8); + p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11); + p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16); + p_insn.opcode |= (p_insn.operand_type[0][0]->reg.opcode << 22); + break; + case OO_5op1: + p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum); + p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 3); + p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8); + p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11); + p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16); + p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 19); + p_insn.opcode |= (p_insn.operand_type[0][2]->reg.opcode << 22); + break; + case OO_5op2: + p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.ARnum); + p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.mod << 3); + p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8); + p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11); + p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16); + p_insn.opcode |= (p_insn.operand_type[0][0]->reg.opcode << 19); + p_insn.opcode |= (p_insn.operand_type[0][2]->reg.opcode << 22); + break; + case OO_PField: + p_insn.opcode |= p_insn.p_field; + if (p_insn.operand_type[0][2]->reg.opcode == 0x01) + p_insn.opcode |= 0x00800000; + if (p_insn.operand_type[1][2]->reg.opcode == 0x03) + p_insn.opcode |= 0x00400000; + switch (p_insn.p_field) + { + case 0x00000000: + p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.ARnum); + p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.mod << 3); + p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum << 8); + p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 11); + p_insn.opcode |= (p_insn.operand_type[1][1]->reg.opcode << 16); + p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 19); + break; + case 0x01000000: + p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.ARnum); + p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.mod << 3); + p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum << 8); + p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 11); + p_insn.opcode |= (p_insn.operand_type[1][1]->reg.opcode << 16); + p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 19); + break; + case 0x02000000: + p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum); + p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 3); + p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.ARnum << 8); + p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.mod << 11); + p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 16); + p_insn.opcode |= (p_insn.operand_type[0][0]->reg.opcode << 19); + break; + case 0x03000000: + p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum); + p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 3); + p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum << 8); + p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 11); + p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16); + p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 19); + break; + } + break; + } + } /* Opcode is finalised at this point for all parallel instructions. */ + { /* Output opcode */ + char *p; + p = frag_more (INSN_SIZE); + md_number_to_chars (p, (valueT) p_insn.opcode, INSN_SIZE); + } + { + int i, j; + for (i = 0; i < 2; i++) + for (j = 0; j < p_insn.operands[i]; j++) + free (p_insn.operand_type[i][j]); + } + debug ("Final opcode: %08X\n", p_insn.opcode); + debug ("\n"); + return 1; +} + +operand * +tic30_operand (token) + char *token; +{ + int count; + char ind_buffer[strlen (token)]; + operand *current_op; + + debug ("In tic30_operand with %s\n", token); + current_op = (operand *) malloc (sizeof (operand)); + memset (current_op, '\0', sizeof (operand)); + if (*token == DIRECT_REFERENCE) + { + char *token_posn = token + 1; + int direct_label = 0; + debug ("Found direct reference\n"); + while (*token_posn) + { + if (!is_digit_char (*token_posn)) + direct_label = 1; + token_posn++; + } + if (direct_label) + { + char *save_input_line_pointer; + segT retval; + debug ("Direct reference is a label\n"); + current_op->direct.label = token + 1; + save_input_line_pointer = input_line_pointer; + input_line_pointer = token + 1; + debug ("Current input_line_pointer: %s\n", input_line_pointer); + retval = expression (¤t_op->direct.direct_expr); + debug ("Expression type: %d\n", current_op->direct.direct_expr.X_op); + debug ("Expression addnum: %d\n", current_op->direct.direct_expr.X_add_number); + debug ("Segment: %d\n", retval); + input_line_pointer = save_input_line_pointer; + if (current_op->direct.direct_expr.X_op == O_constant) + { + current_op->direct.address = current_op->direct.direct_expr.X_add_number; + current_op->direct.resolved = 1; + } + } + else + { + debug ("Direct reference is a number\n"); + current_op->direct.address = atoi (token + 1); + current_op->direct.resolved = 1; + } + current_op->op_type = Direct; + } + else if (*token == INDIRECT_REFERENCE) + { /* Indirect reference operand */ + int found_ar = 0; + int found_disp = 0; + int ar_number = -1; + int disp_number = 0; + int buffer_posn = 1; + ind_addr_type *ind_addr_op; + debug ("Found indirect reference\n"); + ind_buffer[0] = *token; + for (count = 1; count < strlen (token); count++) + { /* Strip operand */ + ind_buffer[buffer_posn] = tolower (*(token + count)); + if ((*(token + count - 1) == 'a' || *(token + count - 1) == 'A') && + (*(token + count) == 'r' || *(token + count) == 'R')) + { + /* AR reference is found, so get its number and remove it from the buffer + so it can pass through hash_find() */ + if (found_ar) + { + as_bad ("More than one AR register found in indirect reference"); + return NULL; + } + if (*(token + count + 1) < '0' || *(token + count + 1) > '7') + { + as_bad ("Illegal AR register in indirect reference"); + return NULL; + } + ar_number = *(token + count + 1) - '0'; + found_ar = 1; + count++; + } + if (*(token + count) == '(') + { + /* Parenthesis found, so check if a displacement value is inside. If so, get + the value and remove it from the buffer. */ + if (is_digit_char (*(token + count + 1))) + { + char disp[10]; + int disp_posn = 0; + + if (found_disp) + { + as_bad ("More than one displacement found in indirect reference"); + return NULL; + } + count++; + while (*(token + count) != ')') + { + if (!is_digit_char (*(token + count))) + { + as_bad ("Invalid displacement in indirect reference"); + return NULL; + } + disp[disp_posn++] = *(token + (count++)); + } + disp[disp_posn] = '\0'; + disp_number = atoi (disp); + count--; + found_disp = 1; + } + } + buffer_posn++; + } + ind_buffer[buffer_posn] = '\0'; + if (!found_ar) + { + as_bad ("AR register not found in indirect reference"); + return NULL; + } + ind_addr_op = (ind_addr_type *) hash_find (ind_hash, ind_buffer); + if (ind_addr_op) + { + debug ("Found indirect reference: %s\n", ind_addr_op->syntax); + if (ind_addr_op->displacement == IMPLIED_DISP) + { + found_disp = 1; + disp_number = 1; + } + else if ((ind_addr_op->displacement == DISP_REQUIRED) && !found_disp) + { + /* Maybe an implied displacement of 1 again */ + as_bad ("required displacement wasn't given in indirect reference"); + return 0; + } + } + else + { + as_bad ("illegal indirect reference"); + return NULL; + } + if (found_disp && (disp_number < 0 || disp_number > 255)) + { + as_bad ("displacement must be an unsigned 8-bit number"); + return NULL; + } + current_op->indirect.mod = ind_addr_op->modfield; + current_op->indirect.disp = disp_number; + current_op->indirect.ARnum = ar_number; + current_op->op_type = Indirect; + } + else + { + reg *regop = (reg *) hash_find (reg_hash, token); + if (regop) + { + debug ("Found register operand: %s\n", regop->name); + if (regop->regtype == REG_ARn) + current_op->op_type = ARn; + else if (regop->regtype == REG_Rn) + current_op->op_type = Rn; + else if (regop->regtype == REG_DP) + current_op->op_type = DPReg; + else + current_op->op_type = OtherReg; + current_op->reg.opcode = regop->opcode; + } + else + { + if (!is_digit_char (*token) || *(token + 1) == 'x' || strchr (token, 'h')) + { + char *save_input_line_pointer; + segT retval; + debug ("Probably a label: %s\n", token); + current_op->immediate.label = (char *) malloc (strlen (token) + 1); + strcpy (current_op->immediate.label, token); + current_op->immediate.label[strlen (token)] = '\0'; + save_input_line_pointer = input_line_pointer; + input_line_pointer = token; + debug ("Current input_line_pointer: %s\n", input_line_pointer); + retval = expression (¤t_op->immediate.imm_expr); + debug ("Expression type: %d\n", current_op->immediate.imm_expr.X_op); + debug ("Expression addnum: %d\n", current_op->immediate.imm_expr.X_add_number); + debug ("Segment: %d\n", retval); + input_line_pointer = save_input_line_pointer; + if (current_op->immediate.imm_expr.X_op == O_constant) + { + current_op->immediate.s_number = current_op->immediate.imm_expr.X_add_number; + current_op->immediate.u_number = (unsigned int) current_op->immediate.imm_expr.X_add_number; + current_op->immediate.resolved = 1; + } + } + else + { + unsigned count; + debug ("Found a number or displacement\n"); + for (count = 0; count < strlen (token); count++) + if (*(token + count) == '.') + current_op->immediate.decimal_found = 1; + current_op->immediate.label = (char *) malloc (strlen (token) + 1); + strcpy (current_op->immediate.label, token); + current_op->immediate.label[strlen (token)] = '\0'; + current_op->immediate.f_number = (float) atof (token); + current_op->immediate.s_number = (int) atoi (token); + current_op->immediate.u_number = (unsigned int) atoi (token); + current_op->immediate.resolved = 1; + } + current_op->op_type = Disp | Abs24 | Imm16 | Imm24; + if (current_op->immediate.u_number >= 0 && current_op->immediate.u_number <= 31) + current_op->op_type |= IVector; + } + } + return current_op; +} + +/* next_line points to the next line after the current instruction (current_line). + Search for the parallel bars, and if found, merge two lines into internal syntax + for a parallel instruction: + q_[INSN1]_[INSN2] [OPERANDS1] | [OPERANDS2] + By this stage, all comments are scrubbed, and only the bare lines are given. + */ + +#define NONE 0 +#define START_OPCODE 1 +#define END_OPCODE 2 +#define START_OPERANDS 3 +#define END_OPERANDS 4 + +char * +tic30_find_parallel_insn (current_line, next_line) + char *current_line; + char *next_line; +{ + int found_parallel = 0; + char first_opcode[256]; + char second_opcode[256]; + char first_operands[256]; + char second_operands[256]; + char *parallel_insn; + + debug ("In tic30_find_parallel_insn()\n"); + while (!is_end_of_line[(int) *next_line]) + { + if (*next_line == PARALLEL_SEPARATOR && *(next_line + 1) == PARALLEL_SEPARATOR) + { + found_parallel = 1; + next_line++; + break; + } + next_line++; + } + if (!found_parallel) + return NULL; + debug ("Found a parallel instruction\n"); + { + int i; + char *opcode, *operands, *line; + + for (i = 0; i < 2; i++) + { + if (i == 0) + { + opcode = &first_opcode[0]; + operands = &first_operands[0]; + line = current_line; + } + else + { + opcode = &second_opcode[0]; + operands = &second_operands[0]; + line = next_line; + } + { + int search_status = NONE; + int char_ptr = 0; + char c; + + while (!is_end_of_line[(int) (c = *line)] && *line) + { + if (is_opcode_char (c) && search_status == NONE) + { + opcode[char_ptr++] = tolower (c); + search_status = START_OPCODE; + } + else if (is_opcode_char (c) && search_status == START_OPCODE) + { + opcode[char_ptr++] = tolower (c); + } + else if (!is_opcode_char (c) && search_status == START_OPCODE) + { + opcode[char_ptr] = '\0'; + char_ptr = 0; + search_status = END_OPCODE; + } + else if (is_operand_char (c) && search_status == START_OPERANDS) + { + operands[char_ptr++] = c; + } + if (is_operand_char (c) && search_status == END_OPCODE) + { + operands[char_ptr++] = c; + search_status = START_OPERANDS; + } + line++; + } + if (search_status != START_OPERANDS) + return NULL; + operands[char_ptr] = '\0'; + } + } + } + parallel_insn = (char *) malloc (strlen (first_opcode) + strlen (first_operands) + + strlen (second_opcode) + strlen (second_operands) + 8); + sprintf (parallel_insn, "q_%s_%s %s | %s", first_opcode, second_opcode, first_operands, second_operands); + debug ("parallel insn = %s\n", parallel_insn); + return parallel_insn; +} + +#undef NONE +#undef START_OPCODE +#undef END_OPCODE +#undef START_OPERANDS +#undef END_OPERANDS + +/* In order to get gas to ignore any | chars at the start of a line, + this function returns true if a | is found in a line. */ + +int +tic30_unrecognized_line (c) + int c; +{ + debug ("In tc_unrecognized_line\n"); + return (c == PARALLEL_SEPARATOR); +} + +int +md_estimate_size_before_relax (fragP, segment) + fragS *fragP; + segT segment; +{ + debug ("In md_estimate_size_before_relax()\n"); + return 0; +} + +void +md_convert_frag (abfd, sec, fragP) + bfd *abfd; + segT sec; + register fragS *fragP; +{ + debug ("In md_convert_frag()\n"); +} + +int +md_apply_fix (fixP, valP) + fixS *fixP; + valueT *valP; +{ + valueT value = *valP; + + debug ("In md_apply_fix() with value = %ld\n", (long) value); + debug ("Values in fixP\n"); + debug ("fx_size = %d\n", fixP->fx_size); + debug ("fx_pcrel = %d\n", fixP->fx_pcrel); + debug ("fx_where = %d\n", fixP->fx_where); + debug ("fx_offset = %d\n", (int) fixP->fx_offset); + { + char *buf = fixP->fx_frag->fr_literal + fixP->fx_where; + value /= INSN_SIZE; + if (fixP->fx_size == 1) + { /* Special fix for LDP instruction. */ + value = (value & 0x00FF0000) >> 16; + } + debug ("new value = %ld\n", (long) value); + md_number_to_chars (buf, value, fixP->fx_size); + } + return 1; +} + +int +md_parse_option (c, arg) + int c; + char *arg; +{ + int i; + + debug ("In md_parse_option()\n"); + for (i = 0; i < c; i++) + { + printf ("%c\n", arg[c]); + } + return 0; +} + +void +md_show_usage (stream) + FILE *stream; +{ + debug ("In md_show_usage()\n"); +} + +symbolS * +md_undefined_symbol (name) + char *name; +{ + debug ("In md_undefined_symbol()\n"); + return (symbolS *) 0; +} + +valueT +md_section_align (segment, size) + segT segment; + valueT size; +{ + debug ("In md_section_align() segment = %d and size = %d\n", segment, size); + size = (size + 3) / 4; + size *= 4; + debug ("New size value = %d\n", size); + return size; +} + +long +md_pcrel_from (fixP) + fixS *fixP; +{ + int offset; + + debug ("In md_pcrel_from()\n"); + debug ("fx_where = %d\n", fixP->fx_where); + debug ("fx_size = %d\n", fixP->fx_size); + /* Find the opcode that represents the current instruction in the fr_literal + storage area, and check bit 21. Bit 21 contains whether the current instruction + is a delayed one or not, and then set the offset value appropriately. */ + if (fixP->fx_frag->fr_literal[fixP->fx_where - fixP->fx_size + 1] & 0x20) + offset = 3; + else + offset = 1; + debug ("offset = %d\n", offset); + /* PC Relative instructions have a format: + displacement = Label - (PC + offset) + This function returns PC + offset where: + fx_where - fx_size = PC + INSN_SIZE * offset = offset number of instructions + */ + return fixP->fx_where - fixP->fx_size + (INSN_SIZE * offset); +} + +char * +md_atof (what_statement_type, literalP, sizeP) + int what_statement_type; + char *literalP; + int *sizeP; +{ + int prec; + char *token; + char keepval; + unsigned long value; + /* char *atof_ieee (); */ + float float_value; + debug ("In md_atof()\n"); + debug ("precision = %c\n", what_statement_type); + debug ("literal = %s\n", literalP); + debug ("line = "); + token = input_line_pointer; + while (!is_end_of_line[(unsigned) *input_line_pointer] && (*input_line_pointer) && (*input_line_pointer != ',')) + { + debug ("%c", *input_line_pointer); + input_line_pointer++; + } + keepval = *input_line_pointer; + *input_line_pointer = '\0'; + debug ("\n"); + float_value = (float) atof (token); + *input_line_pointer = keepval; + debug ("float_value = %f\n", float_value); + switch (what_statement_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()"; + } + if (float_value == 0.0) + { + value = (prec == 2) ? 0x00008000L : 0x80000000L; + } + else + { + unsigned long exp, sign, mant, tmsfloat; + tmsfloat = *((long *) &float_value); + sign = tmsfloat & 0x80000000; + mant = tmsfloat & 0x007FFFFF; + exp = tmsfloat & 0x7F800000; + exp <<= 1; + if (exp == 0xFF000000) + { + if (mant == 0) + value = 0x7F7FFFFF; + else if (sign == 0) + value = 0x7F7FFFFF; + else + value = 0x7F800000; + } + else + { + exp -= 0x7F000000; + if (sign) + { + mant = mant & 0x007FFFFF; + mant = -mant; + mant = mant & 0x00FFFFFF; + if (mant == 0) + { + mant |= 0x00800000; + exp = (long) exp - 0x01000000; + } + } + tmsfloat = exp | mant; + value = tmsfloat; + } + if (prec == 2) + { + long exp, mant; + + if (tmsfloat == 0x80000000) + { + value = 0x8000; + } + else + { + value = 0; + exp = (tmsfloat & 0xFF000000); + exp >>= 24; + mant = tmsfloat & 0x007FFFFF; + if (tmsfloat & 0x00800000) + { + mant |= 0xFF000000; + mant += 0x00000800; + mant >>= 12; + mant |= 0x00000800; + mant &= 0x0FFF; + if (exp > 7) + value = 0x7800; + } + else + { + mant |= 0x00800000; + mant += 0x00000800; + exp += (mant >> 24); + mant >>= 12; + mant &= 0x07FF; + if (exp > 7) + value = 0x77FF; + } + if (exp < -8) + value = 0x8000; + if (value == 0) + { + mant = (exp << 12) | mant; + value = mant & 0xFFFF; + } + } + } + } + md_number_to_chars (literalP, value, prec); + *sizeP = prec; + return 0; +} + +void +md_number_to_chars (buf, val, n) + char *buf; + valueT val; + int n; +{ + debug ("In md_number_to_chars()\n"); + number_to_chars_bigendian (buf, val, n); + /* number_to_chars_littleendian(buf,val,n); */ +} + +#define F(SZ,PCREL) (((SZ) << 1) + (PCREL)) +#define MAP(SZ,PCREL,TYPE) case F(SZ,PCREL): code = (TYPE); break + +arelent * +tc_gen_reloc (section, fixP) + asection *section; + fixS *fixP; +{ + arelent *rel; + bfd_reloc_code_real_type code = 0; + + debug ("In tc_gen_reloc()\n"); + debug ("fixP.size = %d\n", fixP->fx_size); + debug ("fixP.pcrel = %d\n", fixP->fx_pcrel); + debug ("addsy.name = %s\n", S_GET_NAME (fixP->fx_addsy)); + switch (F (fixP->fx_size, fixP->fx_pcrel)) + { + MAP (1, 0, BFD_RELOC_TIC30_LDP); + MAP (2, 0, BFD_RELOC_16); + MAP (3, 0, BFD_RELOC_24); + MAP (2, 1, BFD_RELOC_16_PCREL); + MAP (4, 0, BFD_RELOC_32); + default: + as_bad ("Can not do %d byte %srelocation", fixP->fx_size, + fixP->fx_pcrel ? "pc-relative " : ""); + } +#undef MAP +#undef F + + rel = (arelent *) xmalloc (sizeof (arelent)); + assert (rel != 0); + rel->sym_ptr_ptr = &fixP->fx_addsy->bsym; + rel->address = fixP->fx_frag->fr_address + fixP->fx_where; + if (fixP->fx_pcrel) + rel->addend = fixP->fx_addnumber; + else + rel->addend = 0; + rel->howto = bfd_reloc_type_lookup (stdoutput, code); + if (!rel->howto) + { + const char *name; + name = S_GET_NAME (fixP->fx_addsy); + if (name == NULL) + name = "<unknown>"; + as_fatal ("Cannot generate relocation type for symbol %s, code %s", name, bfd_get_reloc_code_name (code)); + } + return rel; +} + +void +tc_aout_pre_write_hook () +{ + debug ("In tc_aout_pre_write_hook()\n"); +} + +void +md_operand (expressionP) + expressionS *expressionP; +{ + debug ("In md_operand()\n"); +} + +char output_invalid_buf[8]; + +char * +output_invalid (c) + char c; +{ + if (isprint (c)) + sprintf (output_invalid_buf, "'%c'", c); + else + sprintf (output_invalid_buf, "(0x%x)", (unsigned) c); + return output_invalid_buf; +} |