diff options
Diffstat (limited to 'binutils-2.25/gas/config/tc-tic30.c')
-rw-r--r-- | binutils-2.25/gas/config/tc-tic30.c | 2003 |
1 files changed, 2003 insertions, 0 deletions
diff --git a/binutils-2.25/gas/config/tc-tic30.c b/binutils-2.25/gas/config/tc-tic30.c new file mode 100644 index 00000000..570c833d --- /dev/null +++ b/binutils-2.25/gas/config/tc-tic30.c @@ -0,0 +1,2003 @@ +/* tc-c30.c -- Assembly code for the Texas Instruments TMS320C30 + Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007, 2009 + Free Software Foundation, Inc. + 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 3, 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, 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 "safe-ctype.h" +#include "opcode/tic30.h" + +/* Put here all non-digit non-letter characters that may occur in an + operand. */ +static char operand_special_chars[] = "%$-+(,)*._~/<>&^!:[@]"; +static char *ordinal_names[] = +{ + N_("first"), N_("second"), N_("third"), N_("fourth"), N_("fifth") +}; + +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} +}; + +static int ATTRIBUTE_PRINTF_1 +debug (const char *string, ...) +{ + if (flag_debug) + { + char str[100]; + + VA_OPEN (argptr, string); + VA_FIXEDARG (argptr, const char *, string); + vsprintf (str, string, argptr); + VA_CLOSE (argptr); + if (str[0] == '\0') + return (0); + fputs (str, USE_STDOUT ? stdout : stderr); + return strlen (str); + } + else + return 0; +} + +/* 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 (void) +{ + const char *hash_err; + + debug ("In md_begin()\n"); + op_hash = hash_new (); + + { + const insn_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. */ + { + int c; + 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; + +insn_template *opcode; + +struct tic30_insn +{ + insn_template *tm; /* Template of current instruction. */ + unsigned opcode; /* Final opcode. */ + unsigned 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; + +static char output_invalid_buf[sizeof (unsigned char) * 2 + 6]; + +static char * +output_invalid (char c) +{ + if (ISPRINT (c)) + snprintf (output_invalid_buf, sizeof (output_invalid_buf), + "'%c'", c); + else + snprintf (output_invalid_buf, sizeof (output_invalid_buf), + "(0x%x)", (unsigned char) c); + return output_invalid_buf; +} + +/* 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 + +static char * +tic30_find_parallel_insn (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[(unsigned char) *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 *op, *operands, *line; + + for (i = 0; i < 2; i++) + { + if (i == 0) + { + op = &first_opcode[0]; + operands = &first_operands[0]; + line = current_line; + } + else + { + op = &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[(unsigned char) (c = *line)]) + { + if (is_opcode_char (c) && search_status == NONE) + { + op[char_ptr++] = TOLOWER (c); + search_status = START_OPCODE; + } + else if (is_opcode_char (c) && search_status == START_OPCODE) + op[char_ptr++] = TOLOWER (c); + else if (!is_opcode_char (c) && search_status == START_OPCODE) + { + op[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 = 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 + +static operand * +tic30_operand (char *token) +{ + unsigned int count; + char ind_buffer[strlen (token)]; + operand *current_op; + + debug ("In tic30_operand with %s\n", token); + current_op = malloc (sizeof (* current_op)); + 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: %ld\n", + (long) current_op->direct.direct_expr.X_add_number); + debug ("Segment: %p\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 = 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: %ld\n", + (long) current_op->immediate.imm_expr.X_add_number); + debug ("Segment: %p\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 + { + 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 = 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 <= 31) + current_op->op_type |= IVector; + } + } + return current_op; +} + +struct tic30_par_insn +{ + partemplate *tm; /* Template of current parallel instruction. */ + unsigned 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; + +static 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}; + unsigned 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; + 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; + } + 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); + } + + 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++) + { + unsigned 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; + } + + { + char *p; + + p = frag_more (INSN_SIZE); + md_number_to_chars (p, (valueT) p_insn.opcode, INSN_SIZE); + } + + { + unsigned 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; +} + +/* 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 (int c) +{ + debug ("In tc_unrecognized_line\n"); + return (c == PARALLEL_SEPARATOR); +} + +int +md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED, + segT segment ATTRIBUTE_UNUSED) +{ + debug ("In md_estimate_size_before_relax()\n"); + return 0; +} + +void +md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, + segT sec ATTRIBUTE_UNUSED, + register fragS *fragP ATTRIBUTE_UNUSED) +{ + debug ("In md_convert_frag()\n"); +} + +void +md_apply_fix (fixS *fixP, + valueT *valP, + segT seg ATTRIBUTE_UNUSED) +{ + 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 = %ld\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); + } + + if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0) + fixP->fx_done = 1; +} + +int +md_parse_option (int c ATTRIBUTE_UNUSED, + char *arg ATTRIBUTE_UNUSED) +{ + debug ("In md_parse_option()\n"); + return 0; +} + +void +md_show_usage (FILE *stream ATTRIBUTE_UNUSED) +{ + debug ("In md_show_usage()\n"); +} + +symbolS * +md_undefined_symbol (char *name ATTRIBUTE_UNUSED) +{ + debug ("In md_undefined_symbol()\n"); + return (symbolS *) 0; +} + +valueT +md_section_align (segT segment, valueT size) +{ + debug ("In md_section_align() segment = %p and size = %lu\n", + segment, (unsigned long) size); + size = (size + 3) / 4; + size *= 4; + debug ("New size value = %lu\n", (unsigned long) size); + return size; +} + +long +md_pcrel_from (fixS *fixP) +{ + int offset; + + debug ("In md_pcrel_from()\n"); + debug ("fx_where = %ld\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 (int what_statement_type, + char *literalP, + int *sizeP) +{ + int prec; + char *token; + char keepval; + unsigned long value; + 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 char) *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 _("Unrecognized or unsupported floating point constant"); + } + + if (float_value == 0.0) + value = (prec == 2) ? 0x00008000L : 0x80000000L; + else + { + unsigned long exp, sign, mant, tmsfloat; + union + { + float f; + long l; + } + converter; + + converter.f = float_value; + tmsfloat = converter.l; + 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 expon, mantis; + + if (tmsfloat == 0x80000000) + value = 0x8000; + else + { + value = 0; + expon = (tmsfloat & 0xFF000000); + expon >>= 24; + mantis = tmsfloat & 0x007FFFFF; + if (tmsfloat & 0x00800000) + { + mantis |= 0xFF000000; + mantis += 0x00000800; + mantis >>= 12; + mantis |= 0x00000800; + mantis &= 0x0FFF; + if (expon > 7) + value = 0x7800; + } + else + { + mantis |= 0x00800000; + mantis += 0x00000800; + expon += (mantis >> 24); + mantis >>= 12; + mantis &= 0x07FF; + if (expon > 7) + value = 0x77FF; + } + if (expon < -8) + value = 0x8000; + if (value == 0) + { + mantis = (expon << 12) | mantis; + value = mantis & 0xFFFF; + } + } + } + } + md_number_to_chars (literalP, value, prec); + *sizeP = prec; + return NULL; +} + +void +md_number_to_chars (char *buf, valueT val, int n) +{ + debug ("In md_number_to_chars()\n"); + number_to_chars_bigendian (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 (asection *section ATTRIBUTE_UNUSED, 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 = xmalloc (sizeof (* rel)); + gas_assert (rel != 0); + rel->sym_ptr_ptr = xmalloc (sizeof (asymbol *)); + *rel->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy); + rel->address = fixP->fx_frag->fr_address + fixP->fx_where; + 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 +md_operand (expressionS *expressionP ATTRIBUTE_UNUSED) +{ + debug ("In md_operand()\n"); +} + +void +md_assemble (char *line) +{ + insn_template *op; + char *current_posn; + char *token_start; + char save_char; + unsigned 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'; + op = (insn_template *) hash_find (op_hash, token_start); + if (op) + { + debug ("Found instruction %s\n", op->name); + insn.tm = op; + } + 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; + 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; + } + 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); + } + + debug ("Number of operands found: %d\n", insn.operands); + + /* Check that number of operands is correct. */ + if (insn.operands != insn.tm->operands) + { + unsigned int i; + unsigned 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); + { + unsigned 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"); +} |