diff options
Diffstat (limited to 'gas')
-rw-r--r-- | gas/ChangeLog | 19 | ||||
-rw-r--r-- | gas/config/tc-sh.c | 641 | ||||
-rw-r--r-- | gas/config/tc-sh.h | 3 |
3 files changed, 625 insertions, 38 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog index 912e31c..08c005e 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,22 @@ +Thu Feb 17 00:11:08 2000 J"orn Rennecke <amylaar@cygnus.co.uk> + + * config/tc-sh.c ("elf/sh.h"): Include. + (sh_dsp, valid_arch, reg_x, reg_y, reg_efg): New static variables. + (md.begin): Initialize target_arch. + Only include opcodes in has table that match selected architecture. + (parse_reg): Recognize register names for sh-dsp. + (parse_at): Recognize post-modify addressing. + (get_operands): The leading space is now optional. + (get_specific): Remove FDREG_N support. Add support for sh-dsp + arguments. Update valid_arch. + (build_Mytes): Add support for SDT_REG_N. + (find_cooked_opcode): New function, broken out of md_assemble. + (assemble_ppi, sh_elf_final_processing): New functions. + (md_assemble): Use find_cooked_opcode and assemble_ppi. + (md_longopts, md_parse_option): New option: -dsp. + * config/tc-sh.h (elf_tc_final_processing): Define. + (sh_elf_final_processing): Declare. + Fri Feb 11 14:21:51 2000 Jeffrey A Law (law@cygnus.com) * config/tc-hppa.c (pa_build_unwind_subspace): Use subseg_new to create diff --git a/gas/config/tc-sh.c b/gas/config/tc-sh.c index 9f7a6c1..777d76c 100644 --- a/gas/config/tc-sh.c +++ b/gas/config/tc-sh.c @@ -30,6 +30,11 @@ #define DEFINE_TABLE #include "opcodes/sh-opc.h" #include <ctype.h> + +#ifdef OBJ_ELF +#include "elf/sh.h" +#endif + const char comment_chars[] = "!"; const char line_separator_chars[] = ";"; const char line_comment_chars[] = "!#"; @@ -39,16 +44,11 @@ static void s_uses PARAMS ((int)); static void sh_count_relocs PARAMS ((bfd *, segT, PTR)); static void sh_frob_section PARAMS ((bfd *, segT, PTR)); -/* This table describes all the machine specific pseudo-ops the assembler - has to support. The fields are: - pseudo-op name without dot - function to call to execute this pseudo-op - Integer arg to pass to the function - */ - void cons (); void s_align_bytes (); static void s_uacons PARAMS ((int)); +static sh_opcode_info *find_cooked_opcode PARAMS ((char **)); +static void assemble_ppi PARAMS ((char *, sh_opcode_info *)); int shl = 0; @@ -60,6 +60,13 @@ little (ignore) target_big_endian = 0; } +/* This table describes all the machine specific pseudo-ops the assembler + has to support. The fields are: + pseudo-op name without dot + function to call to execute this pseudo-op + Integer arg to pass to the function + */ + const pseudo_typeS md_pseudo_table[] = { {"int", cons, 4}, @@ -84,6 +91,14 @@ int sh_relax; /* set if -relax seen */ int sh_small; +/* Whether -dsp was seen. */ + +static int sh_dsp; + +/* The bit mask of architectures that could + accomodate the insns seen so far. */ +static int valid_arch; + const char EXP_CHARS[] = "eE"; /* Chars that mean this number is a floating point constant */ @@ -189,16 +204,20 @@ md_begin () { sh_opcode_info *opcode; char *prev_name = ""; + int target_arch; if (! shl) target_big_endian = 1; + target_arch = arch_sh1_up & ~(sh_dsp ? arch_sh3e_up : arch_sh_dsp_up); + valid_arch = target_arch; + opcode_hash_control = hash_new (); /* Insert unique names into hash table */ for (opcode = sh_table; opcode->name; opcode++) { - if (strcmp (prev_name, opcode->name)) + if (opcode->arch & target_arch && strcmp (prev_name, opcode->name)) { prev_name = opcode->name; hash_insert (opcode_hash_control, opcode->name, (char *) opcode); @@ -214,6 +233,8 @@ md_begin () static int reg_m; static int reg_n; +static int reg_x, reg_y; +static int reg_efg; static int reg_b; static expressionS immediate; /* absolute expression */ @@ -239,6 +260,23 @@ parse_reg (src, mode, reg) if (src[0] == 'r') { + if (src[1] == '1') + { + if (src[2] >= '0' && src[2] <= '5' + && ! isalnum ((unsigned char) src[3])) + { + *mode = A_REG_N; + *reg = 10 + src[2] - '0'; + return 3; + } + } + if (src[1] >= '0' && src[1] <= '9' + && ! isalnum ((unsigned char) src[2])) + { + *mode = A_REG_N; + *reg = (src[1] - '0'); + return 2; + } if (src[1] >= '0' && src[1] <= '7' && strncmp(&src[2], "_bank", 5) == 0 && ! isalnum ((unsigned char) src[7])) { @@ -246,27 +284,121 @@ parse_reg (src, mode, reg) *reg = (src[1] - '0'); return 7; } + + if (src[1] == 'e' && ! isalnum ((unsigned char) src[2])) + { + *mode = A_RE; + return 2; + } + if (src[1] == 's' && ! isalnum ((unsigned char) src[2])) + { + *mode = A_RS; + return 2; + } } - if (src[0] == 'r') + if (src[0] == 'a') { + if (src[1] == '0') + { + if (! isalnum ((unsigned char) src[2])) + { + *mode = DSP_REG_N; + *reg = A_A0_NUM; + return 2; + } + if (src[2] == 'g' && ! isalnum ((unsigned char) src[3])) + { + *mode = DSP_REG_N; + *reg = A_A0G_NUM; + return 3; + } + } if (src[1] == '1') { - if (src[2] >= '0' && src[2] <= '5' - && ! isalnum ((unsigned char) src[3])) + if (! isalnum ((unsigned char) src[2])) { - *mode = A_REG_N; - *reg = 10 + src[2] - '0'; + *mode = DSP_REG_N; + *reg = A_A1_NUM; + return 2; + } + if (src[2] == 'g' && ! isalnum ((unsigned char) src[3])) + { + *mode = DSP_REG_N; + *reg = A_A1G_NUM; return 3; } } - if (src[1] >= '0' && src[1] <= '9' - && ! isalnum ((unsigned char) src[2])) + + if (src[1] == 'x' && src[2] >= '0' && src[2] <= '1' + && ! isalnum ((unsigned char) src[3])) { *mode = A_REG_N; - *reg = (src[1] - '0'); + *reg = 4 + (src[1] - '0'); + return 3; + } + if (src[1] == 'y' && src[2] >= '0' && src[2] <= '1' + && ! isalnum ((unsigned char) src[3])) + { + *mode = A_REG_N; + *reg = 6 + (src[1] - '0'); + return 3; + } + if (src[1] == 's' && src[2] >= '0' && src[2] <= '3' + && ! isalnum ((unsigned char) src[3])) + { + int n = src[1] - '0'; + + *mode = A_REG_N; + *reg = n | ((~n & 2) << 1); + return 3; + } + } + + if (src[0] == 'i' && src[1] && ! isalnum ((unsigned char) src[3])) + { + if (src[1] == 's') + { + *mode = A_REG_N; + *reg = 8; return 2; } + if (src[1] == 'x') + { + *mode = A_REG_N; + *reg = 8; + return 2; + } + if (src[1] == 'y') + { + *mode = A_REG_N; + *reg = 9; + return 2; + } + } + + if (src[0] == 'x' && src[1] >= '0' && src[1] <= '1' + && ! isalnum ((unsigned char) src[2])) + { + *mode = DSP_REG_N; + *reg = A_X0_NUM + src[1] - '0'; + return 2; + } + + if (src[0] == 'y' && src[1] >= '0' && src[1] <= '1' + && ! isalnum ((unsigned char) src[2])) + { + *mode = DSP_REG_N; + *reg = A_Y0_NUM + src[1] - '0'; + return 2; + } + + if (src[0] == 'm' && src[1] >= '0' && src[1] <= '1' + && ! isalnum ((unsigned char) src[2])) + { + *mode = DSP_REG_N; + *reg = src[1] == '0' ? A_M0_NUM : A_M1_NUM; + return 2; } if (src[0] == 's' @@ -291,6 +423,13 @@ parse_reg (src, mode, reg) return 3; } + if (src[0] == 'd' && src[1] == 's' && src[2] == 'r' + && ! isalnum ((unsigned char) src[3])) + { + *mode = A_DSR; + return 3; + } + if (src[0] == 'd' && src[1] == 'b' && src[2] == 'r' && ! isalnum ((unsigned char) src[3])) { @@ -348,6 +487,12 @@ parse_reg (src, mode, reg) return 4; } } + if (src[0] == 'm' && src[1] == 'o' && src[2] == 'd' + && ! isalnum ((unsigned char) src[4])) + { + *mode = A_MOD; + return 3; + } if (src[0] == 'f' && src[1] == 'r') { if (src[2] == '1') @@ -605,8 +750,21 @@ parse_at (src, op) } if (src[0] == '+') { - op->type = A_INC_N; src++; + if ((src[0] == 'r' && src[1] == '8') + || (src[0] == 'i' && (src[1] == 'x' || src[1] == 's'))) + { + src += 2; + op->type = A_PMOD_N; + } + if ((src[0] == 'r' && src[1] == '9') + || (src[0] == 'i' && src[1] == 'y')) + { + src += 2; + op->type = A_PMODY_N; + } + else + op->type = A_INC_N; } else { @@ -665,7 +823,11 @@ get_operands (info, args, operand) char *ptr = args; if (info->arg[0]) { - ptr++; + /* The pre-processor will eliminate whitespace in front of '@' + after the first argument; we may be called multiple times + from assemble_ppi, so don't insist on finding whitespace here. */ + if (*ptr == ' ') + ptr++; get_operand (&ptr, operand + 0); if (info->arg[1]) @@ -774,16 +936,14 @@ get_specific (opcode, operands) case V_REG_N: case FPUL_N: case FPSCR_N: + case A_PMOD_N: + case A_PMODY_N: + case DSP_REG_N: /* Opcode needs rn */ if (user->type != arg) goto fail; reg_n = user->reg; break; - case FD_REG_N: - if (user->type != F_REG_N && user->type != D_REG_N) - goto fail; - reg_n = user->reg; - break; case DX_REG_N: if (user->type != D_REG_N && user->type != X_REG_N) goto fail; @@ -792,6 +952,10 @@ get_specific (opcode, operands) case A_GBR: case A_SR: case A_VBR: + case A_DSR: + case A_MOD: + case A_RE: + case A_RS: case A_SSR: case A_SPC: case A_SGR: @@ -812,12 +976,144 @@ get_specific (opcode, operands) case A_IND_M: case A_IND_R0_REG_M: case A_DISP_REG_M: + case DSP_REG_M: /* Opcode needs rn */ if (user->type != arg - A_REG_M + A_REG_N) goto fail; reg_m = user->reg; break; + case DSP_REG_X: + if (user->type != DSP_REG_N) + goto fail; + switch (user->reg) + { + case A_X0_NUM: + reg_x = 0; + break; + case A_X1_NUM: + reg_x = 1; + break; + case A_A0_NUM: + reg_x = 2; + break; + case A_A1_NUM: + reg_x = 3; + break; + default: + goto fail; + } + break; + + case DSP_REG_Y: + if (user->type != DSP_REG_N) + goto fail; + switch (user->reg) + { + case A_Y0_NUM: + reg_y = 0; + break; + case A_Y1_NUM: + reg_y = 1; + break; + case A_M0_NUM: + reg_y = 2; + break; + case A_M1_NUM: + reg_y = 3; + break; + default: + goto fail; + } + break; + + case DSP_REG_E: + if (user->type != DSP_REG_N) + goto fail; + switch (user->reg) + { + case A_X0_NUM: + reg_efg = 0 << 10; + break; + case A_X1_NUM: + reg_efg = 1 << 10; + break; + case A_Y0_NUM: + reg_efg = 2 << 10; + break; + case A_A1_NUM: + reg_efg = 3 << 10; + break; + default: + goto fail; + } + break; + + case DSP_REG_F: + if (user->type != DSP_REG_N) + goto fail; + switch (user->reg) + { + case A_Y0_NUM: + reg_efg |= 0 << 8; + break; + case A_Y1_NUM: + reg_efg |= 1 << 8; + break; + case A_X0_NUM: + reg_efg |= 2 << 8; + break; + case A_A1_NUM: + reg_efg |= 3 << 8; + break; + default: + goto fail; + } + break; + + case DSP_REG_G: + if (user->type != DSP_REG_N) + goto fail; + switch (user->reg) + { + case A_M0_NUM: + reg_efg |= 0 << 2; + break; + case A_M1_NUM: + reg_efg |= 1 << 2; + break; + case A_A0_NUM: + reg_efg |= 2 << 2; + break; + case A_A1_NUM: + reg_efg |= 3 << 2; + break; + default: + goto fail; + } + break; + + case A_A0: + if (user->type != DSP_REG_N || user->reg != A_A0_NUM) + goto fail; + break; + case A_X0: + if (user->type != DSP_REG_N || user->reg != A_X0_NUM) + goto fail; + break; + case A_X1: + if (user->type != DSP_REG_N || user->reg != A_X1_NUM) + goto fail; + break; + case A_Y0: + if (user->type != DSP_REG_N || user->reg != A_Y0_NUM) + goto fail; + break; + case A_Y1: + if (user->type != DSP_REG_N || user->reg != A_Y1_NUM) + goto fail; + break; + case F_REG_M: case D_REG_M: case X_REG_M: @@ -845,6 +1141,7 @@ get_specific (opcode, operands) goto fail; } } + valid_arch &= this_try->arch; return this_try; fail:; } @@ -949,6 +1246,11 @@ build_Mytes (opcode, operand) case REG_M: nbuf[index] = reg_m; break; + case SDT_REG_N: + if (reg_n < 2 || reg_n > 5) + as_bad (_("Invalid register: 'r%d'"), reg_n); + nbuf[index] = (reg_n & 3) | 4; + break; case REG_NM: nbuf[index] = reg_n | (reg_m >> 2); break; @@ -997,50 +1299,274 @@ build_Mytes (opcode, operand) } } -/* This is the guts of the machine-dependent assembler. STR points to a - machine dependent instruction. This function is supposed to emit - the frags/bytes it assembles to. - */ +/* Find an opcode at the start of *STR_P in the hash table, and set + *STR_P to the first character after the last one read. */ -void -md_assemble (str) - char *str; +static sh_opcode_info * +find_cooked_opcode (str_p) + char **str_p; { + char *str = *str_p; unsigned char *op_start; unsigned char *op_end; - sh_operand_info operand[3]; - sh_opcode_info *opcode; char name[20]; int nlen = 0; /* Drop leading whitespace */ while (*str == ' ') str++; - /* find the op code end */ + /* Find the op code end. + The pre-processor will eliminate whitespace in front of + any '@' after the first argument; we may be called from + assemble_ppi, so the opcode might be terminated by an '@'. */ for (op_start = op_end = (unsigned char *) (str); *op_end && nlen < 20 - && !is_end_of_line[*op_end] && *op_end != ' '; + && !is_end_of_line[*op_end] && *op_end != ' ' && *op_end != '@'; op_end++) { unsigned char c = op_start[nlen]; /* The machine independent code will convert CMP/EQ into cmp/EQ - because it thinks the '/' is the end of the symbol. Instead of - hacking up the machine independent code, we just deal with it - here. */ + because it thinks the '/' is the end of the symbol. Moreover, + all but the first sub-insn is a parallel processing insn won't + be capitailzed. Instead of hacking up the machine independent + code, we just deal with it here. */ c = isupper (c) ? tolower (c) : c; name[nlen] = c; nlen++; } name[nlen] = 0; + *str_p = op_end; if (nlen == 0) { as_bad (_("can't find opcode ")); } - opcode = (sh_opcode_info *) hash_find (opcode_hash_control, name); + return (sh_opcode_info *) hash_find (opcode_hash_control, name); +} + +/* Assemble a parallel processing insn. */ +#define DDT_BASE 0xf000 /* Base value for double data transfer insns */ +static void +assemble_ppi (op_end, opcode) + char *op_end; + sh_opcode_info *opcode; +{ + int movx = 0; + int movy = 0; + int cond = 0; + int field_b = 0; + char *output; + int move_code; + + /* Some insn ignore one or more register fields, e.g. psts machl,a0. + Make sure we encode a defined insn pattern. */ + reg_x = 0; + reg_y = 0; + + for (;;) + { + sh_operand_info operand[3]; + + if (opcode->arg[0] != A_END) + op_end = get_operands (opcode, op_end, operand); + opcode = get_specific (opcode, operand); + if (opcode == 0) + { + /* Couldn't find an opcode which matched the operands */ + char *where = frag_more (2); + + where[0] = 0x0; + where[1] = 0x0; + as_bad (_("invalid operands for opcode")); + return; + } + if (opcode->nibbles[0] != PPI) + as_bad (_("insn can't be combined with parallel processing insn")); + + switch (opcode->nibbles[1]) + { + + case NOPX: + if (movx) + as_bad (_("multiple movx specifications")); + movx = DDT_BASE; + break; + case NOPY: + if (movy) + as_bad (_("multiple movy specifications")); + movy = DDT_BASE; + break; + + case MOVX: + if (movx) + as_bad (_("multiple movx specifications")); + if (reg_n < 4 || reg_n > 5) + as_bad (_("invalid movx address register")); + if (opcode->nibbles[2] & 8) + { + if (reg_m == A_A1_NUM) + movx = 1 << 7; + else if (reg_m != A_A0_NUM) + as_bad (_("invalid movx dsp register")); + } + else + { + if (reg_x > 1) + as_bad (_("invalid movx dsp register")); + movx = reg_x << 7; + } + movx += ((reg_n - 4) << 9) + (opcode->nibbles[2] << 2) + DDT_BASE; + break; + + case MOVY: + if (movy) + as_bad (_("multiple movy specifications")); + if (opcode->nibbles[2] & 8) + { + /* Bit 3 in nibbles[2] is intended for bit 4 of the opcode, + so add 8 more. */ + movy = 8; + if (reg_m == A_A1_NUM) + movy += 1 << 6; + else if (reg_m != A_A0_NUM) + as_bad (_("invalid movy dsp register")); + } + else + { + if (reg_y > 1) + as_bad (_("invalid movy dsp register")); + movy = reg_y << 6; + } + if (reg_n < 6 || reg_n > 7) + as_bad (_("invalid movy address register")); + movy += ((reg_n - 6) << 8) + opcode->nibbles[2] + DDT_BASE; + break; + + case PSH: + if (immediate.X_op != O_constant) + as_bad (_("dsp immediate shift value not constant")); + field_b = ((opcode->nibbles[2] << 12) + | (immediate.X_add_number & 127) << 4 + | reg_n); + break; + case PPI3: + if (field_b) + as_bad (_("multiple parallel processing specifications")); + field_b = ((opcode->nibbles[2] << 12) + (opcode->nibbles[3] << 8) + + (reg_x << 6) + (reg_y << 4) + reg_n); + break; + case PDC: + if (cond) + as_bad (_("multiple condition specifications")); + cond = opcode->nibbles[2] << 8; + if (*op_end) + goto skip_cond_check; + break; + case PPIC: + if (field_b) + as_bad (_("multiple parallel processing specifications")); + field_b = ((opcode->nibbles[2] << 12) + (opcode->nibbles[3] << 8) + + cond + (reg_x << 6) + (reg_y << 4) + reg_n); + cond = 0; + break; + case PMUL: + if (field_b) + { + if ((field_b & 0xef00) != 0xa100) + as_bad (_("insn cannot be combined with pmuls")); + field_b -= 0x8100; + switch (field_b & 0xf) + { + case A_X0_NUM: + field_b += 0 - A_X0_NUM; + break; + case A_Y0_NUM: + field_b += 1 - A_Y0_NUM; + break; + case A_A0_NUM: + field_b += 2 - A_A0_NUM; + break; + case A_A1_NUM: + field_b += 3 - A_A1_NUM; + break; + default: + as_bad (_("bad padd / psub pmuls output operand")); + } + } + field_b += 0x4000 + reg_efg; + break; + default: + abort (); + } + if (cond) + { + as_bad (_("condition not followed by conditionalizable insn")); + cond = 0; + } + if (! *op_end) + break; + skip_cond_check: + opcode = find_cooked_opcode (&op_end); + if (opcode == NULL) + { + (as_bad + (_("unrecognized characters at end of parallel processing insn"))); + break; + } + } + + move_code = movx | movy; + if (field_b) + { + /* Parallel processing insn. */ + unsigned long ppi_code = (movx | movy | 0xf800) << 16 | field_b; + + output = frag_more (4); + if (! target_big_endian) + { + output[3] = ppi_code >> 8; + output[2] = ppi_code; + } + else + { + output[2] = ppi_code >> 8; + output[3] = ppi_code; + } + move_code |= 0xf800; + } + else + /* Just a double data transfer. */ + output = frag_more (2); + if (! target_big_endian) + { + output[1] = move_code >> 8; + output[0] = move_code; + } + else + { + output[0] = move_code >> 8; + output[1] = move_code; + } +} + +/* This is the guts of the machine-dependent assembler. STR points to a + machine dependent instruction. This function is supposed to emit + the frags/bytes it assembles to. + */ + +void +md_assemble (str) + char *str; +{ + unsigned char *op_end; + sh_operand_info operand[3]; + sh_opcode_info *opcode; + + opcode = find_cooked_opcode (&str); + op_end = str; if (opcode == NULL) { @@ -1058,6 +1584,12 @@ md_assemble (str) seg_info (now_seg)->tc_segment_info_data.in_code = 1; } + if (opcode->nibbles[0] == PPI) + { + assemble_ppi (op_end, opcode); + return; + } + if (opcode->arg[0] == A_BDISP12 || opcode->arg[0] == A_BDISP8) { @@ -1257,10 +1789,12 @@ struct option md_longopts[] = { #define OPTION_RELAX (OPTION_MD_BASE) #define OPTION_LITTLE (OPTION_MD_BASE + 1) #define OPTION_SMALL (OPTION_LITTLE + 1) +#define OPTION_DSP (OPTION_SMALL + 1) {"relax", no_argument, NULL, OPTION_RELAX}, {"little", no_argument, NULL, OPTION_LITTLE}, {"small", no_argument, NULL, OPTION_SMALL}, + {"dsp", no_argument, NULL, OPTION_DSP}, {NULL, no_argument, NULL, 0} }; size_t md_longopts_size = sizeof(md_longopts); @@ -1285,6 +1819,10 @@ md_parse_option (c, arg) sh_small = 1; break; + case OPTION_DSP: + sh_dsp = 1; + break; + default: return 0; } @@ -1886,6 +2424,33 @@ sh_fix_adjustable (fixP) return 1; } + +void sh_elf_final_processing() +{ + int val; + + /* Set file-specific flags to indicate if this code needs + a processor with the sh-dsp / sh3e ISA to execute. */ + if (valid_arch & arch_sh1) + val = EF_SH1; + else if (valid_arch & arch_sh2) + val = EF_SH2; + else if (valid_arch & arch_sh_dsp) + val = EF_SH_DSP; + else if (valid_arch & arch_sh3) + val = EF_SH3; + else if (valid_arch & arch_sh3_dsp) + val = EF_SH_DSP; + else if (valid_arch & arch_sh3e) + val = EF_SH3E; + else if (valid_arch & arch_sh4) + val = EF_SH4; + else + abort (); + + elf_elfheader (stdoutput)->e_flags &= ~EF_SH_MACH_MASK; + elf_elfheader (stdoutput)->e_flags |= val; +} #endif /* Apply a fixup to the object file. */ diff --git a/gas/config/tc-sh.h b/gas/config/tc-sh.h index f4278c2..90129c2 100644 --- a/gas/config/tc-sh.h +++ b/gas/config/tc-sh.h @@ -149,6 +149,9 @@ extern int target_big_endian; #define TARGET_FORMAT (shl ? "elf32-shl" : "elf32-sh") +#define elf_tc_final_processing sh_elf_final_processing +extern void sh_elf_final_processing PARAMS ((void)); + #endif /* OBJ_ELF */ /* end of tc-sh.h */ |