/* tc-h8300.c -- Assemble code for the Hitachi H8/300 Copyright (C) 1991, 1992 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 #include "as.h" #include "bfd.h" #define DEFINE_TABLE #define h8_opcodes ops #include "opcode/h8300.h" #include const char comment_chars[] = {';', 0}; const char line_separator_chars[] = {'$', 0}; 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 (); int Hmode; #define PSIZE (Hmode ? L_32 : L_16) #define DMODE (L_16) #define DSYMMODE (Hmode ? L_24 : L_16) int bsize = L_8; /* default branch displacement */ void h8300hmode () { Hmode = 1; } void sbranch (size) int size; { bsize = size; } const pseudo_typeS md_pseudo_table[] = { {"h8300h", h8300hmode, 0}, {"sbranch", sbranch, L_8}, {"lbranch", sbranch, L_16}, {"int", cons, 2}, {"data.b", cons, 1}, {"data.w", cons, 2}, {"data.l", cons, 4}, {"form", listing_psize, 0}, {"heading", listing_title, 0}, {"import", s_ignore, 0}, {"page", listing_eject, 0}, {"program", s_ignore, 0}, {0, 0, 0} }; const int md_reloc_size; 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"; const relax_typeS md_relax_table[1]; 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 () { struct h8_opcode *opcode; const struct reg_entry *reg; char prev_buffer[100]; int idx = 0; opcode_hash_control = hash_new (); prev_buffer[0] = 0; for (opcode = h8_opcodes; opcode->name; opcode++) { /* Strip off any . part when inserting the opcode and only enter unique codes into the hash table */ char *src = opcode->name; unsigned int len = strlen (src); char *dst = malloc (len + 1); char *buffer = dst; opcode->size = 0; while (*src) { if (*src == '.') { src++; opcode->size = *src; break; } *dst++ = *src++; } *dst++ = 0; if (strcmp (buffer, prev_buffer)) { hash_insert (opcode_hash_control, buffer, (char *) opcode); strcpy (prev_buffer, buffer); idx++; } opcode->idx = idx; /* Find the number of operands */ opcode->noperands = 0; while (opcode->args.nib[opcode->noperands] != E) opcode->noperands++; /* Find the length of the opcode in bytes */ opcode->length = 0; while (opcode->data.nib[opcode->length * 2] != E) opcode->length++; } } struct h8_exp { char *e_beg; char *e_end; expressionS e_exp; }; int dispreg; int opsize; /* Set when a register size is seen */ struct h8_op { op_type mode; unsigned reg; expressionS exp; }; /* parse operands WREG r0,r1,r2,r3,r4,r5,r6,r7,fp,sp r0l,r0h,..r7l,r7h @WREG @WREG+ @-WREG #const */ /* try and parse a reg name, returns number of chars consumed */ int parse_reg (src, mode, reg, direction) char *src; op_type *mode; unsigned int *reg; int direction; { if (src[0] == 's' && src[1] == 'p') { *mode = PSIZE | REG | direction; *reg = 7; return 2; } if (src[0] == 'c' && src[1] == 'c' && src[2] == 'r') { *mode = CCR; *reg = 0; return 3; } if (src[0] == 'f' && src[1] == 'p') { *mode = PSIZE | REG | direction; *reg = 6; return 2; } if (src[0] == 'e' && src[1] == 'r' && src[2] >= '0' && src[2] <= '7') { *mode = L_32 | REG | direction; *reg = src[2] - '0'; if (!Hmode) as_warn ("Reg only legal for H8/300-H"); return 3; } if (src[0] == 'e' && src[1] >= '0' && src[1] <= '7') { *mode = L_16 | REG | direction; *reg = src[1] - '0' + 8; if (!Hmode) as_warn ("Reg only legal for H8/300-H"); return 2; } if (src[0] == 'r') { if (src[1] >= '0' && src[1] <= '7') { if (src[2] == 'l') { *mode = L_8 | REG | direction; *reg = (src[1] - '0') + 8; return 3; } if (src[2] == 'h') { *mode = L_8 | REG | direction; *reg = (src[1] - '0'); return 3; } *mode = L_16 | REG | direction; *reg = (src[1] - '0'); return 2; } } return 0; } char * DEFUN (parse_exp, (s, op), char *s AND expressionS * op) { char *save = input_line_pointer; char *new; input_line_pointer = s; if (expression (op) == O_absent) as_bad ("missing operand"); new = input_line_pointer; input_line_pointer = save; return new; } static char * skip_colonthing (ptr, exp, mode) char *ptr; expressionS *exp; int *mode; { if (*ptr == ':') { ptr++; if (*ptr == '8') { ptr++; /* ff fill any 8 bit quantity */ exp->X_add_number |= 0xff00; } else { *mode &= ~SIZE; if (*ptr == '2') { *mode |= L_24; } else if (*ptr == '1') { *mode |= L_16; } while (isdigit (*ptr)) ptr++; } } return ptr; } /* The many forms of operand: Rn Register direct @Rn Register indirect @(exp[:16], Rn) Register indirect with displacement @Rn+ @-Rn @aa:8 absolute 8 bit @aa:16 absolute 16 bit @aa absolute 16 bit #xx[:size] immediate data @(exp:[8], pc) pc rel @@aa[:8] memory indirect */ char * colonmod24 (op, src) struct h8_op *op; char *src; { int mode = 0; src = skip_colonthing (src, &op->exp, &mode); if (!mode) { /* Choose a default mode */ if (op->exp.X_add_number < -32768 || op->exp.X_add_number > 32767) { if (Hmode) mode = L_24; else mode = L_16; } else if (op->exp.X_add_symbol || op->exp.X_op_symbol) mode = DSYMMODE; else mode = DMODE; } op->mode |= mode; return src; } static void get_operand (ptr, op, dst, direction) char **ptr; struct h8_op *op; unsigned int dst; { char *src = *ptr; op_type mode; unsigned int num; unsigned int len; unsigned int size; op->mode = E; len = parse_reg (src, &op->mode, &op->reg, direction); if (len) { *ptr = src + len; return; } if (*src == '@') { src++; if (*src == '@') { src++; src = parse_exp (src, &op->exp); src = skip_colonthing (src, &op->exp, &op->mode); *ptr = src; op->mode = MEMIND; return; } if (*src == '-') { src++; len = parse_reg (src, &mode, &num, direction); if (len == 0) { /* Oops, not a reg after all, must be ordinary exp */ src--; /* must be a symbol */ op->mode = ABS | PSIZE | direction; *ptr = skip_colonthing (parse_exp (src, &op->exp), &op->exp, &op->mode); return; } if ((mode & SIZE) != PSIZE) as_bad ("Wrong size pointer register for architecture."); op->mode = RDDEC; op->reg = num; *ptr = src + len; return; } if (*src == '(' ) { /* Disp */ src++; /* Start off assuming a 16 bit offset */ src = parse_exp (src, &op->exp); src = colonmod24 (op, src); if (*src == ')') { src++; op->mode = DISP | direction; *ptr = src; return; } if (*src != ',') { as_bad ("expected @(exp, reg16)"); return; } src++; len = parse_reg (src, &mode, &op->reg, direction); if (len == 0 || !(mode & REG)) { as_bad ("expected @(exp, reg16)"); return; } op->mode |= DISP | direction; dispreg = op->reg; src += len; src = skip_colonthing (src, &op->exp, &op->mode); if (*src != ')' && '(') { as_bad ("expected @(exp, reg16)"); return; } *ptr = src + 1; return; } len = parse_reg (src, &mode, &num, direction); if (len) { src += len; if (*src == '+') { src++; if ((mode & SIZE) != PSIZE) as_bad ("Wrong size pointer register for architecture."); op->mode = RSINC; op->reg = num; *ptr = src; return; } if ((mode & SIZE) != PSIZE) as_bad ("Wrong size pointer register for architecture."); op->mode = direction | IND | PSIZE; op->reg = num; *ptr = src; return; } else { /* must be a symbol */ op->mode = ABS | direction; src = parse_exp (src, &op->exp); *ptr = colonmod24 (op, src); return; } } if (*src == '#') { src++; op->mode = IMM; src = parse_exp (src, &op->exp); *ptr = skip_colonthing (src, &op->exp, &op->mode); return; } else { src = parse_exp (src, &op->exp); /* Trailing ':' size ? */ if (*src == ':') { if (src[1] == '1' && src[2] == '6') { op->mode = PCREL | L_16; src += 3; } else if (src[1] == '8') { op->mode = PCREL | L_8; src += 2; } else { as_bad ("expect :8 or :16 here"); } } else { op->mode = PCREL | bsize; } *ptr = src; } } static char * DEFUN (get_operands, (noperands, op_end, operand), unsigned int noperands AND char *op_end AND struct h8_op *operand) { char *ptr = op_end; switch (noperands) { case 0: operand[0].mode = 0; operand[1].mode = 0; break; case 1: ptr++; get_operand (&ptr, operand + 0, 0, SRC); if (*ptr == ',') { ptr++; get_operand (&ptr, operand + 1, 1, DST); } else { operand[1].mode = 0; } break; case 2: ptr++; get_operand (&ptr, operand + 0, 0, SRC); if (*ptr == ',') ptr++; get_operand (&ptr, operand + 1, 1, DST); break; default: abort (); } return ptr; } /* Passed a pointer to a list of opcodes which use different addressing modes, return the opcode which matches the opcodes provided */ static struct h8_opcode * get_specific (opcode, operands) struct h8_opcode *opcode; struct h8_op *operands; { struct h8_opcode *this_try = opcode; int found = 0; unsigned int this_index = opcode->idx; while (this_index == opcode->idx && !found) { unsigned int i; found = 1; this_try = opcode++; for (i = 0; i < this_try->noperands && found; i++) { op_type op = this_try->args.nib[i]; int x = operands[i].mode; if ((op & (DISP | REG)) == (DISP | REG) && ((x & DISP | REG) == (DISP | REG))) { dispreg = operands[i].reg; } else if (op & REG) { if (!(x & REG)) found = 0; if (x & L_P) { x = (x & ~L_P) | (Hmode ? L_32 : L_16); } if (op & L_P) { op = (op & ~L_P) | (Hmode ? L_32 : L_16); } opsize = op & SIZE; /* The size of the reg is v important */ if ((op & SIZE) != (x & SIZE)) found = 0; } else if ((op & ABSJMP) && (x & ABS)) { operands[i].mode &= ~ABS; operands[i].mode |= ABSJMP; /* But it may not be 24 bits long */ if (!Hmode) { operands[i].mode &= ~SIZE; operands[i].mode |= L_16; } } else if ((op & (KBIT | DBIT)) && (x & IMM)) { /* This is ok if the immediate value is sensible */ } else if (op & PCREL) { /* The size of the displacement is important */ if ((op & SIZE) != (x & SIZE)) found = 0; } else if ((op & (DISP | IMM | ABS)) && (op & (DISP | IMM | ABS)) == (x & (DISP | IMM | ABS))) { /* Got a diplacement,will fit if no size or same size as try */ if ((x & SIZE) != 0 && ((op & SIZE) != (x & SIZE))) found = 0; } else if ((op & ABSMOV) && (x & ABS)) { /* Ok */ } else if ((op & MODE) != (x & MODE)) { found = 0; } } } if (found) return this_try; else return 0; } static void DEFUN (check_operand, (operand, width, string), struct h8_op *operand AND unsigned int width AND char *string) { if (operand->exp.X_add_symbol == 0 && operand->exp.X_op_symbol == 0) { /* No symbol involved, let's look at offset, it's dangerous if any of the high bits are not 0 or ff's, find out by oring or anding with the width and seeing if the answer is 0 or all fs*/ if ((operand->exp.X_add_number & ~width) != 0 && (operand->exp.X_add_number | width) != (~0)) { as_warn ("operand %s0x%x out of range.", string, operand->exp.X_add_number); } } } static void do_a_fix_imm (offset, operand, relaxing) int offset; struct h8_op *operand; int relaxing; { int idx; int size; int where; char *t = operand->mode & IMM ? "#" : "@"; if (operand->exp.X_add_symbol == 0) { char *bytes = frag_now->fr_literal + offset; switch (operand->mode & SIZE) { case L_3: check_operand (operand, 0x7, t); bytes[0] |= (operand->exp.X_add_number) << 4; break; case L_8: check_operand (operand, 0xff, t); bytes[0] = operand->exp.X_add_number; break; case L_16: check_operand (operand, 0xffff, t); bytes[0] = operand->exp.X_add_number >> 8; bytes[1] = operand->exp.X_add_number >> 0; break; case L_24: check_operand (operand, 0xffffff, t); bytes[0] = operand->exp.X_add_number >> 16; bytes[1] = operand->exp.X_add_number >> 8; bytes[2] = operand->exp.X_add_number >> 0; break; case L_32: /* This should be done with bfd */ bytes[0] = operand->exp.X_add_number >> 24; bytes[1] = operand->exp.X_add_number >> 16; bytes[2] = operand->exp.X_add_number >> 8; bytes[3] = operand->exp.X_add_number >> 0; break; } } else { switch (operand->mode & SIZE) { default: abort (); case L_24: size = 4; where = -1; idx = relaxing ? R_MOVLB1 : R_RELLONG; break; case L_32: size = 4; where = 0; idx = R_RELLONG; break; case L_16: size = 2; where = 0; idx = relaxing ? R_MOVB1 : R_RELWORD; break; case L_8: size = 1; where = 0; idx = R_RELBYTE; } operand->exp.X_add_number = (short) operand->exp.X_add_number; fix_new_exp (frag_now, offset + where, size, &operand->exp, 0, idx); } } /* Now we know what sort of opcodes it is, lets build the bytes - */ static void build_bytes (this_try, operand) struct h8_opcode *this_try; struct h8_op *operand; { unsigned int i; char *output = frag_more (this_try->length); char *output_ptr = output; op_type *nibble_ptr = this_try->data.nib; char part; op_type c; char high; unsigned int nibble_count = 0; int immat; int nib; char asnibbles[30]; char *p = asnibbles; if (!(this_try->inbase || Hmode)) { as_warn ("Opcode `%s' only available on H8/300-H", this_try->name); } while (*nibble_ptr != E) { int d; c = *nibble_ptr++; d = (c & DST) != 0; if (c < 16) { nib = c; } else { if (c & (REG | IND | INC | DEC)) { nib = operand[d].reg; } else if ((c & DISPREG) == (DISPREG)) { nib = dispreg; } else if (c & ABSMOV) { operand[d].mode &= ~ABS; operand[d].mode |= ABSMOV; immat = nibble_count / 2; nib = 0; } else if (c & (IMM | PCREL | ABS | ABSJMP | DISP )) { operand[d].mode = c; immat = nibble_count / 2; nib = 0; } else if (c & IGNORE) { nib = 0; } else if (c & DBIT) { switch (operand[0].exp.X_add_number) { case 1: nib = c; break; case 2: nib = 0x8 | c; break; default: as_bad ("Need #1 or #2 here"); } } else if (c & KBIT) { switch (operand[0].exp.X_add_number) { case 1: nib = 0; break; case 2: nib = 8; break; case 4: if (!Hmode) as_warn ("#4 only valid in h8/300 mode."); nib = 9; break; default: as_bad ("Need #1 or #2 here"); break; } /* stop it making a fix */ operand[0].mode = 0; } if (c & B31) { nib |= 0x8; } } nibble_count++; *p++ = nib; } for (i = 0; i < this_try->length; i++) { output[i] = (asnibbles[i * 2] << 4) | asnibbles[i * 2 + 1]; } /* output any fixes */ for (i = 0; i < 2; i++) { int x = operand[i].mode; if (x & (IMM | ABS | DISP)) { do_a_fix_imm (output - frag_now->fr_literal + immat, operand + i,0); } else if (x & PCREL) { int size16 = x & L_16; int where = size16 ? 2 : 1; int size = size16 ? 2 : 1; int type = size16 ? R_PCRWORD : R_PCRBYTE; check_operand (operand + i, size16 ? 0x7fff : 0x7f, "@"); if (operand[i].exp.X_add_number & 1) { as_warn ("branch operand has odd offset (%x)\n", operand->exp.X_add_number); } operand[i].exp.X_add_number = (char) (operand[i].exp.X_add_number - 1); fix_new_exp (frag_now, output - frag_now->fr_literal + where, size, &operand[i].exp, 1, type); } else if (x & MEMIND) { check_operand (operand + i, 0xff, "@@"); fix_new_exp (frag_now, output - frag_now->fr_literal + 1, 1, &operand[i].exp, 0, R_RELBYTE); } else if (x & ABSMOV) { /* This mov is either absolute long or thru a memory loc */ do_a_fix_imm (output - frag_now->fr_literal + immat, operand + i,1); } else if (x & ABSJMP) { /* This jmp may be a jump or a branch */ check_operand (operand + i, Hmode ? 0xfffff : 0xffff, "@"); if (operand[i].exp.X_add_number & 1) { as_warn ("branch operand has odd offset (%x)\n", operand->exp.X_add_number); } operand[i].exp.X_add_number = (short) operand[i].exp.X_add_number; fix_new_exp (frag_now, output - frag_now->fr_literal, 4, &operand[i].exp, 0, R_JMPL1); } } } /* try and give an intelligent error message for common and simple to detect errors */ static void DEFUN (clever_message, (opcode, operand), struct h8_opcode *opcode AND struct h8_op *operand) { struct h8_opcode *scan = opcode; /* Find out if there was more than one possible opccode */ if ((opcode + 1)->idx != opcode->idx) { unsigned int argn; /* Only one opcode of this flavour, try and guess which operand didn't match */ for (argn = 0; argn < opcode->noperands; argn++) { switch (opcode->args.nib[argn]) { case RD16: if (operand[argn].mode != RD16) { as_bad ("destination operand must be 16 bit register"); return; } break; case RS8: if (operand[argn].mode != RS8) { as_bad ("source operand must be 8 bit register"); return; } break; case ABS16DST: if (operand[argn].mode != ABS16DST) { as_bad ("destination operand must be 16bit absolute address"); return; } break; case RD8: if (operand[argn].mode != RD8) { as_bad ("destination operand must be 8 bit register"); return; } break; case ABS16SRC: if (operand[argn].mode != ABS16SRC) { as_bad ("source operand must be 16bit absolute address"); return; } break; } } } as_bad ("invalid operands"); } /* This is the guts of the machine-dependent assembler. STR points to a machine dependent instruction. This funciton is supposed to emit the frags/bytes it assembles to. */ void DEFUN (md_assemble, (str), char *str) { char *op_start; char *op_end; unsigned int i; struct h8_op operand[2]; struct h8_opcode *opcode; struct h8_opcode *prev_opcode; char *dot = 0; char c; /* Drop leading whitespace */ while (*str == ' ') str++; /* find the op code end */ for (op_start = op_end = str; *op_end != 0 && *op_end != ' '; op_end++) { if (*op_end == '.') { dot = op_end + 1; *op_end = 0; op_end += 2; break; } } ; if (op_end == op_start) { as_bad ("can't find opcode "); } c = *op_end; *op_end = 0; opcode = (struct h8_opcode *) hash_find (opcode_hash_control, op_start); if (opcode == NULL) { as_bad ("unknown opcode"); return; } input_line_pointer = get_operands (opcode->noperands, op_end, operand); *op_end = c; prev_opcode = opcode; 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; clever_message (prev_opcode, operand); return; } if (opcode->size && dot) { if (opcode->size != *dot) { as_warn ("mismatch between opcode size and operand size"); } } build_bytes (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_ATOF()"; } 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 ""; } int md_parse_option (argP, cntP, vecP) char **argP; int *cntP; char ***vecP; { return 0; } int md_short_jump_size; void tc_aout_fix_to_chars () { printf ("call to tc_aout_fix_to_chars \n"); abort (); } void md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol) char *ptr; addressT from_addr; addressT to_addr; fragS *frag; symbolS *to_symbol; { as_fatal ("failed sanity check."); } void md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol) char *ptr; addressT from_addr, to_addr; fragS *frag; symbolS *to_symbol; { as_fatal ("failed sanity check."); } void md_convert_frag (headers, fragP) object_headers *headers; fragS *fragP; { printf ("call to md_convert_frag \n"); abort (); } valueT md_section_align (seg, size) segT seg; 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; switch (fixP->fx_size) { case 1: *buf++ = val; break; case 2: *buf++ = (val >> 8); *buf++ = val; break; case 4: *buf++ = (val >> 24); *buf++ = (val >> 16); *buf++ = (val >> 8); *buf++ = val; break; default: abort (); } } void DEFUN (md_operand, (expressionP), expressionS * expressionP) { } int md_long_jump_size; int md_estimate_size_before_relax (fragP, segment_type) register fragS *fragP; register segT segment_type; { printf ("call tomd_estimate_size_before_relax \n"); abort (); } /* Put number into target byte order */ void DEFUN (md_number_to_chars, (ptr, use, nbytes), char *ptr AND valueT use AND 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; { abort (); } void tc_coff_symbol_emit_hook () { } 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_RELWORD; break; case 1: intr->r_type = R_RELBYTE; 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; if (symbol_ptr) intr->r_symndx = symbol_ptr->sy_number; else intr->r_symndx = -1; } tc_coff_sizemachdep () { abort (); } /* end of tc-h8300.c */