diff options
author | Mark Eichin <eichin@cygnus> | 1993-07-12 19:42:32 +0000 |
---|---|---|
committer | Mark Eichin <eichin@cygnus> | 1993-07-12 19:42:32 +0000 |
commit | 025b0302439df01169c7d9cb06493bbb53c7ce6b (patch) | |
tree | 25846278d32fbd19b92cdd1fc69ffac86a0d6f23 /gas/config/tc-sh.c | |
parent | d723cd17d5f3e2db2f32bee905f0632fba4389cf (diff) | |
download | gdb-025b0302439df01169c7d9cb06493bbb53c7ce6b.zip gdb-025b0302439df01169c7d9cb06493bbb53c7ce6b.tar.gz gdb-025b0302439df01169c7d9cb06493bbb53c7ce6b.tar.bz2 |
fix definitions of md_create_long_jump, md_create_short_jump,
md_number_to_chars, and md_section_align to correctly use valueT and addressT
Diffstat (limited to 'gas/config/tc-sh.c')
-rw-r--r-- | gas/config/tc-sh.c | 1390 |
1 files changed, 1390 insertions, 0 deletions
diff --git a/gas/config/tc-sh.c b/gas/config/tc-sh.c new file mode 100644 index 0000000..fe6c2a6 --- /dev/null +++ b/gas/config/tc-sh.c @@ -0,0 +1,1390 @@ +/* tc-sh.c -- Assemble code for the Hitachi Super-H + + Copyright (C) 1993 Free Software Foundation. + + 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + Written By Steve Chamberlain + sac@cygnus.com + */ + +#include <stdio.h> +#include "as.h" +#include "bfd.h" +#include "subsegs.h" +#define DEFINE_TABLE +#include "../opcodes/sh-opc.h" +#include <ctype.h> + +const char comment_chars[] = "!"; +const char line_separator_chars[] = ";"; +const char line_comment_chars[] = "!"; + +/* 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 (); + +const pseudo_typeS md_pseudo_table[] = +{ + {"int", cons, 4}, + {"word", cons, 2}, + {"form", listing_psize, 0}, + {"heading", listing_title, 0}, + {"import", s_ignore, 0}, + {"page", listing_eject, 0}, + {"program", s_ignore, 0}, + {0, 0, 0} +}; + +/*int md_reloc_size;*/ + +static int relax; /* set if -relax seen */ + +const char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant */ +/* As in 0f12.456 */ +/* or 0d1.2345e12 */ +const char FLT_CHARS[] = "rRsSfFdDxXpP"; + +#define C(a,b) ENCODE_RELAX(a,b) + +#define JREG 14 /* Register used as a temp when relaxing */ +#define ENCODE_RELAX(what,length) (((what) << 4) + (length)) +#define GET_WHAT(x) ((x>>4)) + +/* These are the two types of relaxable instrction */ +#define COND_JUMP 1 +#define UNCOND_JUMP 2 + +#define UNDEF_DISP 0 +#define COND8 1 +#define COND12 2 +#define COND32 3 +#define UNCOND12 1 +#define UNCOND32 2 +#define UNDEF_WORD_DISP 4 +#define END 5 + +#define UNCOND12 1 +#define UNCOND32 2 + +#define COND8_F 254 +#define COND8_M -256 +#define COND8_LENGTH 2 +#define COND12_F (4094 - 4) /* -4 since there are two extra */ +/* instructions needed */ +#define COND12_M -4096 +#define COND12_LENGTH 6 +#define COND32_F (1<<30) +#define COND32_M -(1<<30) +#define COND32_LENGTH 14 + +#define COND8_RANGE(x) ((x) > COND8_M && (x) < COND8_F) +#define COND12_RANGE(x) ((x) > COND12_M && (x) < COND12_F) + +#define UNCOND12_F 4094 +#define UNCOND12_M -4096 +#define UNCOND12_LENGTH 2 + +#define UNCOND32_F (1<<30) +#define UNCOND32_M -(1<<30) +#define UNCOND32_LENGTH 14 + + +const relax_typeS md_relax_table[C (END, 0)]; + +static struct hash_control *opcode_hash_control; /* Opcode mnemonics */ + +/* + This function is called once, at assembler startup time. This should + set up all the tables, etc that the MD part of the assembler needs + */ + +void +md_begin () +{ + sh_opcode_info *opcode; + char *prev_name = ""; + + opcode_hash_control = hash_new (); + + /* Insert unique names into hash table */ + for (opcode = sh_table; opcode->name; opcode++) + { + if (strcmp (prev_name, opcode->name)) + { + prev_name = opcode->name; + hash_insert (opcode_hash_control, opcode->name, (char *) opcode); + } + else + { + /* Make all the opcodes with the same name point to the same + string */ + opcode->name = prev_name; + } + } + + /* Initialize the relax table */ + md_relax_table[C (COND_JUMP, COND8)].rlx_forward = COND8_F; + md_relax_table[C (COND_JUMP, COND8)].rlx_backward = COND8_M; + md_relax_table[C (COND_JUMP, COND8)].rlx_length = COND8_LENGTH; + md_relax_table[C (COND_JUMP, COND8)].rlx_more = C (COND_JUMP, COND12); + + md_relax_table[C (COND_JUMP, COND12)].rlx_forward = COND12_F; + md_relax_table[C (COND_JUMP, COND12)].rlx_backward = COND12_M; + md_relax_table[C (COND_JUMP, COND12)].rlx_length = COND12_LENGTH; + md_relax_table[C (COND_JUMP, COND12)].rlx_more = C (COND_JUMP, COND32); + + md_relax_table[C (COND_JUMP, COND32)].rlx_forward = COND32_F; + md_relax_table[C (COND_JUMP, COND32)].rlx_backward = COND32_M; + md_relax_table[C (COND_JUMP, COND32)].rlx_length = COND32_LENGTH; + md_relax_table[C (COND_JUMP, COND32)].rlx_more = 0; + + + md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_forward = UNCOND12_F; + md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_backward = UNCOND12_M; + md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length = UNCOND12_LENGTH; + md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_more = C (UNCOND_JUMP, UNCOND32); + + md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_forward = UNCOND32_F; + md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_backward = UNCOND32_M; + md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length = UNCOND32_LENGTH; + md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_more = 0; + + +} + +static int reg_m; +static int reg_n; +static expressionS immediate; /* absolute expression */ + +typedef struct +{ + sh_arg_type type; + int reg; +} + +sh_operand_info; + +/* try and parse a reg name, returns number of chars consumed */ +static int +parse_reg (src, mode, reg) + char *src; + int *mode; + int *reg; +{ + if (src[0] == 'r') + { + if (src[1] == '1') + { + if (src[2] >= '0' && src[2] <= '5') + { + *mode = A_REG_N; + *reg = 10 + src[2] - '0'; + return 3; + } + } + if (src[1] >= '0' && src[1] <= '9') + { + *mode = A_REG_N; + *reg = (src[1] - '0'); + return 2; + } + } + + if (src[0] == 's' && src[1] == 'r') + { + *mode = A_SR; + return 2; + } + + if (src[0] == 's' && src[1] == 'p') + { + *mode = A_REG_N; + *reg = 15; + return 2; + } + + if (src[0] == 'p' && src[1] == 'r') + { + *mode = A_PR; + return 2; + } + if (src[0] == 'p' && src[1] == 'c') + { + *mode = A_DISP_PC; + return 2; + } + if (src[0] == 'g' && src[1] == 'b' && src[2] == 'r') + { + *mode = A_GBR; + return 3; + } + if (src[0] == 'v' && src[1] == 'b' && src[2] == 'r') + { + *mode = A_VBR; + return 3; + } + + if (src[0] == 'm' && src[1] == 'a' && src[2] == 'c') + { + if (src[3] == 'l') + { + *mode = A_MACL; + return 4; + } + if (src[3] == 'h') + { + *mode = A_MACH; + return 4; + } + } + + return 0; +} + +static +char * +parse_exp (s) + char *s; +{ + char *save; + char *new; + segT seg; + + save = input_line_pointer; + + + input_line_pointer = s; + + seg = expr (0, &immediate); + new = input_line_pointer; + input_line_pointer = save; + if (SEG_NORMAL (seg)) + return new; + switch (seg) + { + case SEG_ABSOLUTE: + case SEG_UNKNOWN: + case SEG_DIFFERENCE: + case SEG_BIG: + case SEG_REGISTER: + return new; + case SEG_ABSENT: + as_bad ("Missing operand"); + return new; + default: + as_bad ("Don't understand operand of type %s", segment_name (seg)); + return new; + } +} + + +/* The many forms of operand: + + Rn Register direct + @Rn Register indirect + @Rn+ Autoincrement + @-Rn Autodecrement + @(disp:4,Rn) + @(disp:8,GBR) + @(disp:8,PC) + + @(R0,Rn) + @(R0,GBR) + + disp:8 + disp:12 + #imm8 + pr, gbr, vbr, macl, mach + + */ + +static +char * +parse_at (src, op) + char *src; + sh_operand_info *op; +{ + int len; + int mode; + src++; + if (src[0] == '-') + { + /* Must be predecrement */ + src++; + + len = parse_reg (src, &mode, &(op->reg)); + if (mode != A_REG_N) + as_bad ("illegal register after @-"); + + op->type = A_DEC_N; + src += len; + } + else if (src[0] == '(') + { + /* Could be @(disp, rn), @(disp, gbr), @(disp, pc), @(r0, gbr) or + @(r0, rn) */ + src++; + len = parse_reg (src, &mode, &(op->reg)); + if (len && mode == A_REG_N) + { + src += len; + if (op->reg != 0) + { + as_bad ("must be @(r0,...)"); + } + if (src[0] == ',') + src++; + /* Now can be rn or gbr */ + len = parse_reg (src, &mode, &(op->reg)); + if (mode == A_GBR) + { + op->type = A_R0_GBR; + } + else if (mode == A_REG_N) + { + op->type = A_IND_R0_REG_N; + } + else + { + as_bad ("syntax error in @(r0,...)"); + } + } + else + { + /* Must be an @(disp,.. thing) */ + src = parse_exp (src); + if (src[0] == ',') + src++; + /* Now can be rn, gbr or pc */ + len = parse_reg (src, &mode, &op->reg); + if (len) + { + if (mode == A_REG_N) + { + op->type = A_DISP_REG_N; + } + else if (mode == A_GBR) + { + op->type = A_DISP_GBR; + } + else if (mode == A_DISP_PC) + { + op->type = A_DISP_PC; + } + else + { + as_bad ("syntax error in @(disp,[Rn, gbr, pc])"); + } + } + else + { + as_bad ("syntax error in @(disp,[Rn, gbr, pc])"); + } + } + src += len; + if (src[0] != ')') + as_bad ("expecting )"); + else + src++; + } + else + { + src += parse_reg (src, &mode, &(op->reg)); + if (mode != A_REG_N) + { + as_bad ("illegal register after @"); + } + if (src[0] == '+') + { + op->type = A_INC_N; + src++; + } + else + { + op->type = A_IND_N; + } + } + return src; +} + +static void +get_operand (ptr, op) + char **ptr; + sh_operand_info *op; +{ + char *src = *ptr; + int mode = -1; + unsigned int len; + + if (src[0] == '#') + { + src++; + *ptr = parse_exp (src); + op->type = A_IMM; + return; + } + + else if (src[0] == '@') + { + *ptr = parse_at (src, op); + return; + } + len = parse_reg (src, &mode, &(op->reg)); + if (len) + { + *ptr = src + len; + op->type = mode; + return; + } + else + { + /* Not a reg, the only thing left is a displacement */ + *ptr = parse_exp (src); + op->type = A_DISP_PC; + return; + } +} + +static +char * +get_operands (info, args, operand) + sh_opcode_info *info; + char *args; + sh_operand_info *operand; + +{ + char *ptr = args; + if (info->arg[0]) + { + ptr++; + + get_operand (&ptr, operand + 0); + if (info->arg[1]) + { + if (*ptr == ',') + { + ptr++; + } + get_operand (&ptr, operand + 1); + } + else + { + operand[1].type = 0; + } + } + else + { + operand[0].type = 0; + operand[1].type = 0; + } + return ptr; +} + +/* Passed a pointer to a list of opcodes which use different + addressing modes, return the opcode which matches the opcodes + provided + */ + +static +sh_opcode_info * +get_specific (opcode, operands) + sh_opcode_info *opcode; + sh_operand_info *operands; +{ + sh_opcode_info *this_try = opcode; + char *name = opcode->name; + int arg_to_test = 0; + int n = 0; + while (opcode->name) + { + this_try = opcode++; + if (this_try->name != name) + { + /* We've looked so far down the table that we've run out of + opcodes with the same name */ + return 0; + } + /* look at both operands needed by the opcodes and provided by + the user - since an arg test will often fail on the same arg + again and again, we'll try and test the last failing arg the + first on each opcode try */ + + for (n = 0; this_try->arg[n]; n++) + { + sh_operand_info *user = operands + arg_to_test; + sh_arg_type arg = this_try->arg[arg_to_test]; + switch (arg) + { + case A_IMM: + case A_BDISP12: + case A_BDISP8: + case A_DISP_GBR: + case A_DISP_PC: + case A_MACH: + case A_PR: + case A_MACL: + if (user->type != arg) + goto fail; + break; + case A_R0: + /* opcode needs r0 */ + if (user->type != A_REG_N || user->reg != 0) + goto fail; + break; + case A_R0_GBR: + if (user->type != A_R0_GBR || user->reg != 0) + goto fail; + break; + + case A_REG_N: + case A_INC_N: + case A_DEC_N: + case A_IND_N: + case A_IND_R0_REG_N: + case A_DISP_REG_N: + /* Opcode needs rn */ + if (user->type != arg) + goto fail; + reg_n = user->reg; + break; + case A_GBR: + case A_SR: + case A_VBR: + if (user->type != arg) + goto fail; + break; + + case A_REG_M: + case A_INC_M: + case A_DEC_M: + case A_IND_M: + case A_IND_R0_REG_M: + case A_DISP_REG_M: + /* Opcode needs rn */ + if (user->type != arg - A_REG_M + A_REG_N) + goto fail; + reg_m = user->reg; + break; + default: + printf ("unhandled %d\n", arg); + goto fail; + } + /* If we did 0, test 1 next, else 0 */ + arg_to_test = 1 - arg_to_test; + } + return this_try; + fail:; + } + + return 0; +} + +int +check (operand, low, high) + expressionS *operand; + int low; + int high; +{ + if (operand->X_seg != SEG_ABSOLUTE + || operand->X_add_number < low + || operand->X_add_number > high) + { + as_bad ("operand must be absolute in range %d..%d", low, high); + } + return operand->X_add_number; +} + + +static void +insert (where, how, pcrel) + char *where; + int how; + int pcrel; +{ + fix_new (frag_now, + where - frag_now->fr_literal, + 4, + immediate.X_add_symbol, + immediate.X_subtract_symbol, + immediate.X_add_number, + pcrel, + how); + +} + +static void +build_relax (opcode) + sh_opcode_info *opcode; +{ + int len; + char *p; + if (opcode->arg[0] == A_BDISP8) + { + p = frag_var (rs_machine_dependent, + md_relax_table[C (COND_JUMP, COND32)].rlx_length, + len = md_relax_table[C (COND_JUMP, COND8)].rlx_length, + C (COND_JUMP, 0), + immediate.X_add_symbol, + immediate.X_add_number, + 0); + p[0] = (opcode->nibbles[0] << 4) | (opcode->nibbles[1]); + } + else if (opcode->arg[0] == A_BDISP12) + { + p = frag_var (rs_machine_dependent, + md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length, + len = md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length, + C (UNCOND_JUMP, 0), + immediate.X_add_symbol, + immediate.X_add_number, + 0); + p[0] = (opcode->nibbles[0] << 4); + } + +} + +/* Now we know what sort of opcodes it is, lets build the bytes - + */ +static void +build_Mytes (opcode, operand) + sh_opcode_info *opcode; + sh_operand_info *operand; + +{ + int index; + char nbuf[4]; + char *output = frag_more (2); + + nbuf[0] = 0; + nbuf[1] = 0; + nbuf[2] = 0; + nbuf[3] = 0; + + for (index = 0; index < 4; index++) + { + sh_nibble_type i = opcode->nibbles[index]; + if (i < 16) + { + nbuf[index] = i; + } + else + { + switch (i) + { + case REG_N: + nbuf[index] = reg_n; + break; + case REG_M: + nbuf[index] = reg_m; + break; + case DISP_4: + insert (output + 1, R_SH_IMM4, 0); + break; + case IMM_4BY4: + insert (output + 1, R_SH_IMM4BY4, 0); + break; + case IMM_4BY2: + insert (output + 1, R_SH_IMM4BY2, 0); + break; + case IMM_4: + insert (output + 1, R_SH_IMM4, 0); + break; + case IMM_8BY4: + insert (output + 1, R_SH_IMM8BY4, 0); + break; + case IMM_8BY2: + insert (output + 1, R_SH_IMM8BY2, 0); + break; + case IMM_8: + insert (output + 1, R_SH_IMM8, 0); + break; + case PCRELIMM_8BY4: + insert (output + 1, R_SH_PCRELIMM8BY4, 0); + break; + case PCRELIMM_8BY2: + insert (output + 1, R_SH_PCRELIMM8BY2, 0); + break; + default: + printf ("failed for %d\n", i); + } + } + } + output[0] = (nbuf[0] << 4) | (nbuf[1]); + output[1] = (nbuf[2] << 4) | (nbuf[3]); +} + +/* 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_start; + unsigned char *op_end; + sh_operand_info operand[2]; + sh_opcode_info *opcode; + unsigned char *name; + + int nlen = 0; + + /* Drop leading whitespace */ + while (*str == ' ') + str++; + + /* find the op code end */ + for (name = op_start = op_end = (unsigned char *) (str); + *op_end && + !is_end_of_line[*op_end] && *op_end != ' '; + op_end++) + { + nlen++; + } + name[nlen] = 0; + + if (op_end == op_start) + { + as_bad ("can't find opcode "); + } + + opcode = (sh_opcode_info *) hash_find (opcode_hash_control, name); + + if (opcode == NULL) + { + as_bad ("unknown opcode"); + return; + } + + if (opcode->arg[0] == A_BDISP12 + || opcode->arg[0] == A_BDISP8) + { + input_line_pointer = parse_exp (op_end + 1); + build_relax (opcode); + } + else + { + input_line_pointer = 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; + } + + build_Mytes (opcode, operand); + } + +} + +void +DEFUN (tc_crawl_symbol_chain, (headers), + object_headers * headers) +{ + printf ("call to tc_crawl_symbol_chain \n"); +} + +symbolS * +DEFUN (md_undefined_symbol, (name), + char *name) +{ + return 0; +} + +void +DEFUN (tc_headers_hook, (headers), + object_headers * headers) +{ + printf ("call to tc_headers_hook \n"); +} + +void +DEFUN_VOID (md_end) +{ +} + +/* Various routines to kill one day */ +/* Equal to MAX_PRECISION in atof-ieee.c */ +#define MAX_LITTLENUMS 6 + +/* Turn a string in input_line_pointer into a floating point constant of type + type, and store the appropriate bytes in *litP. The number of LITTLENUMS + emitted is stored in *sizeP . An error message is returned, or NULL on OK. + */ +char * +md_atof (type, litP, sizeP) + char type; + char *litP; + int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + char *atof_ieee (); + + switch (type) + { + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + + case 'x': + case 'X': + prec = 6; + break; + + case 'p': + case 'P': + prec = 6; + break; + + default: + *sizeP = 0; + return "Bad call to MD_NTOF()"; + } + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + + *sizeP = prec * sizeof (LITTLENUM_TYPE); + for (wordP = words; prec--;) + { + md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + return ""; /* Someone should teach Dean about null pointers */ +} + +int +md_parse_option (argP, cntP, vecP) + char **argP; + int *cntP; + char ***vecP; + +{ + if (!strcmp (*argP, "relax")) + { + relax = 1; + **argP = 0; + } + return 1; +} + +int md_short_jump_size; + +void +tc_Nout_fix_to_chars () +{ + printf ("call to tc_Nout_fix_to_chars \n"); + abort (); +} + +void +md_create_short_jump (ptr, from_Nddr, to_Nddr, frag, to_symbol) + char *ptr; + addressT from_Nddr; + addressT to_Nddr; + fragS *frag; + symbolS *to_symbol; +{ + as_fatal ("failed sanity check."); +} + +void +md_create_long_jump (ptr, from_Nddr, to_Nddr, frag, to_symbol) + char *ptr; + addressT from_Nddr, to_Nddr; + fragS *frag; + symbolS *to_symbol; +{ + as_fatal ("failed sanity check."); +} + +/* +called after relaxing, change the frags so they know how big they are +*/ +void +md_convert_frag (headers, fragP) + object_headers *headers; + fragS *fragP; + +{ + unsigned char *buffer = (unsigned char *) (fragP->fr_fix + fragP->fr_literal); + int donerelax = 0; + int targ_addr = ((fragP->fr_symbol ? S_GET_VALUE (fragP->fr_symbol) : 0) + fragP->fr_offset); + switch (fragP->fr_subtype) + { + case C (COND_JUMP, COND8): + { + /* Get the address of the end of the instruction */ + int next_inst = fragP->fr_fix + fragP->fr_address + 2; + + int disp = targ_addr - next_inst - 2; + disp /= 2; + md_number_to_chars (buffer + 1, disp, 1); + fragP->fr_fix += 2; + fragP->fr_var = 0; + } + break; + + case C (UNCOND_JUMP, UNCOND12): + { + /* Get the address of the end of the instruction */ + int next_inst = fragP->fr_fix + fragP->fr_address + 2; + + int t; + int disp = targ_addr - next_inst - 2; + + disp /= 2; + t = buffer[0] & 0xf0; + md_number_to_chars (buffer, disp, 2); + buffer[0] = (buffer[0] & 0xf) | t; + fragP->fr_fix += 2; + fragP->fr_var = 0; + } + break; + + case C (UNCOND_JUMP, UNCOND32): + case C (UNCOND_JUMP, UNDEF_WORD_DISP): + { + /* A jump wont fit in 12 bits, make code which looks like + bra foo + mov.w @(0, PC), r14 + .long disp + foo: bra @r14 + */ + + int next_inst = + fragP->fr_fix + fragP->fr_address + UNCOND32_LENGTH; + + int disp = targ_addr - next_inst; + int t = buffer[0] & 0x10; + + disp /= 2; + + buffer[0] = 0xa0; /* branch over move and disp */ + buffer[1] = 3; + buffer[2] = 0xd0 | JREG;/* Build mov insn */ + buffer[3] = 0x00; + + buffer[4] = 0; /* space for 32 bit jump disp */ + buffer[5] = 0; + buffer[6] = 0; + buffer[7] = 0; + + buffer[10] = 0x40 | JREG; /* Build jmp @JREG */ + buffer[11] = t ? 0xb : 0x2b; + + buffer[12] = 0x20; /* build nop */ + buffer[13] = 0x0b; + + /* Make reloc for the long disp */ + fix_new (fragP, + fragP->fr_fix + 4, + 4, + fragP->fr_symbol, + 0, + fragP->fr_offset, + 0, + R_SH_IMM32); + fragP->fr_fix += UNCOND32_LENGTH; + fragP->fr_var = 0; + donerelax = 1; + + } + break; + + case C (COND_JUMP, COND12): + { + /* A bcond won't fit, so turn it into a b!cond; bra disp; nop */ + int next_inst = + fragP->fr_fix + fragP->fr_address + 6; + + int disp = targ_addr - next_inst; + disp /= 2; + md_number_to_chars (buffer + 2, disp, 2); + buffer[0] ^= 0x2; /* Toggle T/F bit */ + buffer[1] = 1; /* branch over jump and nop */ + buffer[2] = (buffer[2] & 0xf) | 0xa0; /* Build jump insn */ + buffer[4] = 0x20; /* Build nop */ + buffer[5] = 0x0b; + fragP->fr_fix += 6; + fragP->fr_var = 0; + donerelax = 1; + } + break; + + case C (COND_JUMP, COND32): + case C (COND_JUMP, UNDEF_WORD_DISP): + { + /* A bcond won't fit and it won't go into a 12 bit + displacement either, the code sequence looks like: + b!cond foop + mov.w @(n, PC), r14 + jmp @r14 + nop + .long where + foop: + */ + + int next_inst = + fragP->fr_fix + fragP->fr_address + COND32_LENGTH; + + int disp = targ_addr - next_inst; + disp /= 2; + + buffer[0] ^= 0x2; /* Toggle T/F bit */ +#define JREG 14 + buffer[1] = 5; /* branch over mov, jump, nop and ptr */ + buffer[2] = 0xd0 | JREG;/* Build mov insn */ + buffer[3] = 0x2; + buffer[4] = 0x40 | JREG;/* Build jmp @JREG */ + buffer[5] = 0x0b; + buffer[6] = 0x20; /* build nop */ + buffer[7] = 0x0b; + buffer[8] = 0; /* space for 32 bit jump disp */ + buffer[9] = 0; + buffer[10] = 0; + buffer[11] = 0; + buffer[12] = 0; + buffer[13] = 0; + /* Make reloc for the long disp */ + fix_new (fragP, + fragP->fr_fix + 8, + 4, + fragP->fr_symbol, + 0, + fragP->fr_offset, + 0, + R_SH_IMM32); + fragP->fr_fix += COND32_LENGTH; + fragP->fr_var = 0; + donerelax = 1; + } + break; + + default: + abort (); + } + + if (donerelax && !relax) + { + as_bad ("Offset doesn't fit at 0x%x, trying to get to 0x%x", + fragP->fr_address, + targ_addr); + } + +} + +valueT +DEFUN (md_section_align, (seg, size), + segT seg AND + valueT size) +{ + return ((size + (1 << section_alignment[(int) seg]) - 1) + & (-1 << section_alignment[(int) seg])); + +} + +void +md_apply_fix (fixP, val) + fixS *fixP; + long val; +{ + char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; + int addr = fixP->fx_frag->fr_address + fixP->fx_where; + if (fixP->fx_r_type == 0) + { + fixP->fx_r_type = R_SH_IMM32; + } + + switch (fixP->fx_r_type) + { + + case R_SH_IMM4: + *buf = (*buf & 0xf0) | (val & 0xf); + break; + + case R_SH_IMM4BY2: + *buf = (*buf & 0xf0) | ((val >> 1) & 0xf); + break; + + case R_SH_IMM4BY4: + *buf = (*buf & 0xf0) | ((val >> 2) & 0xf); + break; + + case R_SH_IMM8BY2: + *buf = val >> 1; + break; + + case R_SH_IMM8BY4: + *buf = val >> 2; + break; + + case R_SH_IMM8: + *buf++ = val; + break; + + case R_SH_PCRELIMM8BY4: + addr &= ~1; + + if (val & 0x3) + as_warn ("non aligned displacement at %x\n", addr); + val -= (addr + 4); + val += 3; + val /= 4; + if (val & ~0xff) + as_warn ("pcrel too far at %x\n", addr); + + *buf = val; + break; + + case R_SH_PCRELIMM8BY2: + addr &= ~1; + if (val & 0x1) + as_bad ("odd displacement at %x\n", addr); + val -= (addr + 4); + val++; + val /= 2; + if (val & ~0xff) + as_warn ("pcrel too far at %x\n", addr); + *buf = val; + break; + + case R_SH_IMM32: + *buf++ = val >> 24; + *buf++ = val >> 16; + *buf++ = val >> 8; + *buf++ = val >> 0; + break; + + default: + abort (); + } +} + +void +DEFUN (md_operand, (expressionP), expressionS * expressionP) +{ +} + +int md_long_jump_size; + +/* +called just before address relaxation, return the length +by which a fragment must grow to reach it's destination +*/ +int +md_estimate_size_before_relax (fragP, segment_type) + register fragS *fragP; + register segT segment_type; +{ + switch (fragP->fr_subtype) + { + case C (UNCOND_JUMP, UNDEF_DISP): + /* used to be a branch to somewhere which was unknown */ + if (!fragP->fr_symbol) + { + fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12); + fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length; + } + else if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type) + { + fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12); + fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length; + } + else + { + fragP->fr_subtype = C (UNCOND_JUMP, UNDEF_WORD_DISP); + fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length; + return md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length; + } + break; + + default: + abort (); + case C (COND_JUMP, UNDEF_DISP): + /* used to be a branch to somewhere which was unknown */ + if (fragP->fr_symbol + && S_GET_SEGMENT (fragP->fr_symbol) == segment_type) + { + /* Got a symbol and it's defined in this segment, become byte + sized - maybe it will fix up */ + fragP->fr_subtype = C (COND_JUMP, COND8); + fragP->fr_var = md_relax_table[C (COND_JUMP, COND8)].rlx_length; + } + else if (fragP->fr_symbol) + { + /* Its got a segment, but its not ours, so it will always be long */ + fragP->fr_subtype = C (COND_JUMP, UNDEF_WORD_DISP); + fragP->fr_var = md_relax_table[C (COND_JUMP, COND32)].rlx_length; + return md_relax_table[C (COND_JUMP, COND32)].rlx_length; + } + else + { + /* We know the abs value */ + fragP->fr_subtype = C (COND_JUMP, COND8); + fragP->fr_var = md_relax_table[C (COND_JUMP, COND8)].rlx_length; + } + + break; + } + return fragP->fr_var; +} + +/* Put number into target byte order */ + +void +md_number_to_chars (ptr, use, nbytes) + char *ptr; + valueT use; + int nbytes; +{ + switch (nbytes) + { + case 4: + *ptr++ = (use >> 24) & 0xff; + case 3: + *ptr++ = (use >> 16) & 0xff; + case 2: + *ptr++ = (use >> 8) & 0xff; + case 1: + *ptr++ = (use >> 0) & 0xff; + break; + default: + abort (); + } +} +long +md_pcrel_from (fixP) + fixS *fixP; + +{ + int gap = fixP->fx_size + fixP->fx_where + + fixP->fx_frag->fr_address; + return gap; +} + +void +tc_coff_symbol_emit_hook () +{ +} + +short +tc_coff_fix2rtype (fix_ptr) + fixS *fix_ptr; +{ + return fix_ptr->fx_r_type; +} + +void +tc_reloc_mangle (fix_ptr, intr, base) + fixS *fix_ptr; + struct internal_reloc *intr; + bfd_vma base; + +{ + symbolS *symbol_ptr; + + symbol_ptr = fix_ptr->fx_addsy; + + /* If this relocation is attached to a symbol then it's ok + to output it */ + if (fix_ptr->fx_r_type == RELOC_32) + { + /* cons likes to create reloc32's whatever the size of the reloc.. + */ + switch (fix_ptr->fx_size) + { + case 2: + intr->r_type = R_IMM16; + break; + case 1: + intr->r_type = R_IMM8; + break; + default: + abort (); + } + } + else + { + intr->r_type = fix_ptr->fx_r_type; + } + + intr->r_vaddr = fix_ptr->fx_frag->fr_address + fix_ptr->fx_where + base; + intr->r_offset = fix_ptr->fx_offset; + + /* Turn the segment of the symbol into an offset. */ + if (symbol_ptr) + { + symbolS *dot; + + dot = segment_info[S_GET_SEGMENT (symbol_ptr)].dot; + if (dot) + { + intr->r_offset += S_GET_VALUE (symbol_ptr); + intr->r_symndx = dot->sy_number; + } + else + { + intr->r_symndx = symbol_ptr->sy_number; + } + } + else + { + intr->r_symndx = -1; + } +} + +int +tc_coff_sizemachdep (frag) + fragS *frag; +{ + return md_relax_table[frag->fr_subtype].rlx_length; +} + + +/* end of tc-sh.c */ |