diff options
-rw-r--r-- | gas/config/tc-d10v.c | 240 |
1 files changed, 188 insertions, 52 deletions
diff --git a/gas/config/tc-d10v.c b/gas/config/tc-d10v.c index e95b3c8..6c800b2 100644 --- a/gas/config/tc-d10v.c +++ b/gas/config/tc-d10v.c @@ -26,7 +26,7 @@ #include "opcode/d10v.h" #include "elf/ppc.h" -const char comment_chars[] = "#;"; +const char comment_chars[] = ";"; const char line_comment_chars[] = "#"; const char line_separator_chars[] = ""; const char *md_shortopts = "O"; @@ -35,6 +35,8 @@ const char FLT_CHARS[] = "dD"; int Optimizing = 0; +#define AT_WORD (-1) + /* fixups */ #define MAX_INSN_FIXUPS (5) struct d10v_fixup @@ -42,6 +44,8 @@ struct d10v_fixup expressionS exp; int operand; int pcrel; + int size; + bfd_reloc_code_real_type reloc; }; typedef struct _fixups @@ -79,9 +83,12 @@ struct option md_longopts[] = { }; size_t md_longopts_size = sizeof(md_longopts); +static void d10v_dot_word PARAMS ((int)); + /* The target specific pseudo-ops which we support. */ const pseudo_typeS md_pseudo_table[] = { + { "word", d10v_dot_word, 2 }, { NULL, NULL, 0 } }; @@ -362,6 +369,7 @@ get_reloc (op) return (BFD_RELOC_16); } + /* get_operands parses a string of operands and returns an array of expressions */ @@ -412,7 +420,6 @@ get_operands (exp) input_line_pointer = p; - /* check to see if it might be a register name */ if (!register_name (&exp[numops])) { @@ -420,6 +427,20 @@ get_operands (exp) expression (&exp[numops]); } + if (!strncasecmp (input_line_pointer, "@word", 5)) + { + if (exp[numops].X_op == O_register) + { + /* if it looked like a register name but was followed by "@word" */ + /* then it was really a symbol, so change it to one */ + exp[numops].X_op = O_symbol; + exp[numops].X_add_symbol = symbol_find_or_make ((char *)exp[numops].X_op_symbol); + exp[numops].X_op_symbol = NULL; + } + exp[numops].X_add_number = AT_WORD; + input_line_pointer += 5; + } + if (exp[numops].X_op == O_illegal) as_bad ("illegal operand"); else if (exp[numops].X_op == O_absent) @@ -519,6 +540,21 @@ build_insn (opcode, opers, insn) if (fixups->fc >= MAX_INSN_FIXUPS) as_fatal ("too many fixups"); + + if (opers[i].X_op == O_symbol && number == AT_WORD) + { + number = opers[i].X_add_number = 0; + fixups->fix[fixups->fc].reloc = BFD_RELOC_D10V_18; + } else + fixups->fix[fixups->fc].reloc = + get_reloc((struct d10v_operand *)&d10v_operands[opcode->operands[i]]); + + if (fixups->fix[fixups->fc].reloc == BFD_RELOC_16 || + fixups->fix[fixups->fc].reloc == BFD_RELOC_D10V_18) + fixups->fix[fixups->fc].size = 2; + else + fixups->fix[fixups->fc].size = 4; + fixups->fix[fixups->fc].exp = opers[i]; fixups->fix[fixups->fc].operand = opcode->operands[i]; fixups->fix[fixups->fc].pcrel = (flags & OPERAND_ADDR) ? true : false; @@ -548,7 +584,7 @@ write_long (opcode, insn, fx) unsigned long insn; Fixups *fx; { - int i; + int i, where; char *f = frag_more(4); insn |= FM11; @@ -557,17 +593,23 @@ write_long (opcode, insn, fx) for (i=0; i < fx->fc; i++) { - if (get_reloc((struct d10v_operand *)&d10v_operands[fx->fix[i].operand])) + if (fx->fix[i].reloc) { + where = f - frag_now->fr_literal; + if (fx->fix[i].size == 2) + where += 2; /* - printf("fix_new_exp: where:%x size:4\n ",f - frag_now->fr_literal); + printf("fix_new_exp: where:%x size:%d\n ",where,fx->fix[i].size); print_expr_1(stdout,&(fx->fix[i].exp)); printf("\n"); */ + if (fx->fix[i].reloc == BFD_RELOC_D10V_18) + fx->fix[i].operand |= 4096; + fix_new_exp (frag_now, - f - frag_now->fr_literal, - 4, + where, + fx->fix[i].size, &(fx->fix[i].exp), fx->fix[i].pcrel, fx->fix[i].operand|2048); @@ -585,7 +627,7 @@ write_1_short (opcode, insn, fx) Fixups *fx; { char *f = frag_more(4); - int i; + int i, where; if (opcode->exec_type & PARONLY) as_fatal ("Instruction must be executed in parallel with another instruction."); @@ -602,23 +644,28 @@ write_1_short (opcode, insn, fx) number_to_chars_bigendian (f, insn, 4); for (i=0; i < fx->fc; i++) { - bfd_reloc_code_real_type reloc; - reloc = get_reloc((struct d10v_operand *)&d10v_operands[fx->fix[i].operand]); - if (reloc) + if (fx->fix[i].reloc) { + where = f - frag_now->fr_literal; + if (fx->fix[i].size == 2) + where += 2; + /* - printf("fix_new_exp: where:%x size:4\n ",f - frag_now->fr_literal); + printf("fix_new_exp: where:%x size:%d\n ",where, fx->fix[i].size); print_expr_1(stdout,&(fx->fix[i].exp)); printf("\n"); */ + if (fx->fix[i].reloc == BFD_RELOC_D10V_18) + fx->fix[i].operand |= 4096; + /* if it's an R reloc, we may have to switch it to L */ - if ( (reloc == BFD_RELOC_D10V_10_PCREL_R) && (opcode->unit != IU) ) + if ( (fx->fix[i].reloc == BFD_RELOC_D10V_10_PCREL_R) && (opcode->unit != IU) ) fx->fix[i].operand |= 1024; fix_new_exp (frag_now, - f - frag_now->fr_literal, - 4, + where, + fx->fix[i].size, &(fx->fix[i].exp), fx->fix[i].pcrel, fx->fix[i].operand|2048); @@ -638,7 +685,7 @@ write_2_short (opcode1, insn1, opcode2, insn2, exec_type, fx) { unsigned long insn; char *f; - int i,j; + int i,j, where; if ( (exec_type != 1) && ((opcode1->exec_type & PARONLY) || (opcode2->exec_type & PARONLY))) @@ -729,23 +776,29 @@ write_2_short (opcode1, insn1, opcode2, insn2, exec_type, fx) for (j=0; j<2; j++) { - bfd_reloc_code_real_type reloc; for (i=0; i < fx->fc; i++) { - reloc = get_reloc((struct d10v_operand *)&d10v_operands[fx->fix[i].operand]); - if (reloc) + if (fx->fix[i].reloc) { - if ( (reloc == BFD_RELOC_D10V_10_PCREL_R) && (j == 0) ) + where = f - frag_now->fr_literal; + if (fx->fix[i].size == 2) + where += 2; + + if ( (fx->fix[i].reloc == BFD_RELOC_D10V_10_PCREL_R) && (j == 0) ) fx->fix[i].operand |= 1024; + if (fx->fix[i].reloc == BFD_RELOC_D10V_18) + fx->fix[i].operand |= 4096; + /* - printf("fix_new_exp: where:%x reloc:%d\n ",f - frag_now->fr_literal,fx->fix[i].operand); + printf("fix_new_exp: where:%x reloc:%d\n ",where,fx->fix[i].operand); print_expr_1(stdout,&(fx->fix[i].exp)); printf("\n"); */ + fix_new_exp (frag_now, - f - frag_now->fr_literal, - 4, + where, + fx->fix[i].size, &(fx->fix[i].exp), fx->fix[i].pcrel, fx->fix[i].operand|2048); @@ -1013,7 +1066,7 @@ do_assemble (str, opcode) input_line_pointer = save; insn = build_insn ((*opcode), myops, 0); - /* printf("sub-insn = %lx\n",insn); */ + /* printf("sub-insn = %lx\n",insn); */ return (insn); } @@ -1037,7 +1090,7 @@ find_opcode (opcode, myops) if (opcode->format == OPCODE_FAKE) { int opnum = opcode->operands[0]; - + if (myops[opnum].X_op == O_register) { myops[opnum].X_op = O_symbol; @@ -1064,9 +1117,29 @@ find_opcode (opcode, myops) } else { - int value = obstack_next_free(&frchain_now->frch_obstack) - frag_now->fr_literal - - S_GET_VALUE(myops[opnum].X_add_symbol); - if (!check_range (value, bits, flags)) + fragS *f; + long value; + /* calculate the current address by running through the previous frags */ + /* and adding our current offset */ + for (value = 0, f = frchain_now->frch_root; f; f = f->fr_next) + value += f->fr_fix; + + if (flags & OPERAND_ADDR) + value = S_GET_VALUE(myops[opnum].X_add_symbol) - value - + (obstack_next_free(&frchain_now->frch_obstack) - frag_now->fr_literal); + else + value = S_GET_VALUE(myops[opnum].X_add_symbol); + + if (myops[opnum].X_add_number == AT_WORD) + { + if (bits > 4) + { + bits += 2; + if (!check_range (value, bits, flags)) + return next_opcode; + } + } + else if (!check_range (value, bits, flags)) return next_opcode; } next_opcode++; @@ -1092,7 +1165,7 @@ find_opcode (opcode, myops) int flags = d10v_operands[opcode->operands[i]].flags; int X_op = myops[i].X_op; int num = myops[i].X_add_number; - + if (X_op==0) { match=0; @@ -1121,12 +1194,13 @@ find_opcode (opcode, myops) break; } } - /* we're only done if the operands matched so far AND there are no more to check */ if (match && myops[i].X_op==0) break; - + else + match = 0; + next_opcode = opcode+1; if (next_opcode->opcode == 0) break; @@ -1237,14 +1311,14 @@ md_apply_fix3 (fixp, valuep, seg) else { /* We don't actually support subtracting a symbol. */ - as_bad_where (fixp->fx_file, fixp->fx_line, + as_bad_where (fixp->fx_file, fixp->fx_line, "expression too complex"); } } } /* printf("md_apply_fix: value=0x%x type=0x%x where=0x%x size=%d line=%d\n", value, fixp->fx_r_type,fixp->fx_where,fixp->fx_size, fixp->fx_line); */ - + op_type = fixp->fx_r_type; if (op_type & 2048) { @@ -1255,6 +1329,11 @@ md_apply_fix3 (fixp, valuep, seg) fixp->fx_r_type = BFD_RELOC_D10V_10_PCREL_L; left = 1; } + else if (op_type & 4096) + { + op_type -= 4096; + fixp->fx_r_type = BFD_RELOC_D10V_18; + } else fixp->fx_r_type = get_reloc((struct d10v_operand *)&d10v_operands[op_type]); } @@ -1269,33 +1348,29 @@ md_apply_fix3 (fixp, valuep, seg) case BFD_RELOC_D10V_10_PCREL_L: case BFD_RELOC_D10V_10_PCREL_R: case BFD_RELOC_D10V_18_PCREL: + case BFD_RELOC_D10V_18: /* instruction addresses are always right-shifted by 2 */ value >>= 2; + if (fixp->fx_size == 2) + bfd_putb16 ((bfd_vma) value, (unsigned char *) where); + else + { + /* printf(" insn=%x value=%x where=%x pcrel=%x\n",insn,value,fixp->fx_where,fixp->fx_pcrel); */ + insn = d10v_insert_operand (insn, op_type, (offsetT)value, left, fixp); + /* printf(" new insn=%x\n",insn); */ + bfd_putb32 ((bfd_vma) insn, (unsigned char *) where); + } break; case BFD_RELOC_32: bfd_putb32 ((bfd_vma) value, (unsigned char *) where); - return 1; + break; case BFD_RELOC_16: - if (fixp->fx_size == 2) - { - bfd_putb16 ((bfd_vma) value, (unsigned char *) where); - return 1; - } - default: + bfd_putb16 ((bfd_vma) value, (unsigned char *) where); break; + default: + as_fatal ("line %d: unknown relocation type: 0x%x",fixp->fx_line,fixp->fx_r_type); } - - /* printf(" insn=%x value=%x where=%x pcrel=%x\n",insn,value,fixp->fx_where,fixp->fx_pcrel); */ - insn = d10v_insert_operand (insn, op_type, (offsetT)value, left, fixp); - /* printf(" new insn=%x\n",insn); */ - - bfd_putb32 ((bfd_vma) insn, (unsigned char *) where); - - if (fixp->fx_done) - return 1; - - fixp->fx_addnumber = value; - return 1; + return 0; } @@ -1321,3 +1396,64 @@ d10v_cleanup (done) } return 1; } + +/* Like normal .word, except support @word */ +/* clobbers input_line_pointer, checks end-of-line. */ +static void +d10v_dot_word (nbytes) + register int nbytes; /* 1=.byte, 2=.word, 4=.long */ +{ + expressionS exp; + bfd_reloc_code_real_type reloc; + char *p; + int offset; + + if (is_it_end_of_statement ()) + { + demand_empty_rest_of_line (); + return; + } + + do + { + expression (&exp); + if (!strncasecmp (input_line_pointer, "@word", 5)) + { + exp.X_add_number = 0; + input_line_pointer += 5; + + p = frag_more (2); + fix_new_exp (frag_now, p - frag_now->fr_literal, 2, + &exp, 0, BFD_RELOC_D10V_18); + } + else + emit_expr (&exp, 2); + } + while (*input_line_pointer++ == ','); + + input_line_pointer--; /* Put terminator back into stream. */ + demand_empty_rest_of_line (); +} + + +/* Mitsubishi asked that we support some old syntax that apparently */ +/* had immediate operands starting with '#'. This is in some of their */ +/* sample code but is not documented (although it appears in some */ +/* examples in their assembler manual). For now, we'll solve this */ +/* compatibility problem by simply ignoring any '#' at the beginning */ +/* of an operand. */ + +/* operands that begin with '#' should fall through to here */ +/* from expr.c */ + +void +md_operand (expressionP) + expressionS *expressionP; +{ + if (*input_line_pointer == '#') + { + input_line_pointer++; + expression (expressionP); + } +} + |