/* tc-h8300.c -- Assemble code for the Renesas H8/300 Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Written By Steve Chamberlain . */ #include #include "as.h" #include "subsegs.h" #include "bfd.h" #define DEFINE_TABLE #define h8_opcodes ops #include "opcode/h8300.h" #include "safe-ctype.h" #ifdef OBJ_ELF #include "elf/h8.h" #endif const char comment_chars[] = ";"; const char line_comment_chars[] = "#"; const char line_separator_chars[] = ""; void cons PARAMS ((int)); void sbranch PARAMS ((int)); void h8300hmode PARAMS ((int)); void h8300smode PARAMS ((int)); static void pint PARAMS ((int)); int Hmode; int Smode; #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. */ struct h8_instruction { int length; int noperands; int idx; int size; const struct h8_opcode *opcode; }; struct h8_instruction *h8_instructions; void h8300hmode (arg) int arg ATTRIBUTE_UNUSED; { Hmode = 1; Smode = 0; #ifdef BFD_ASSEMBLER if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300h)) as_warn (_("could not set architecture and machine")); #endif } void h8300smode (arg) int arg ATTRIBUTE_UNUSED; { Smode = 1; Hmode = 1; #ifdef BFD_ASSEMBLER if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300s)) as_warn (_("could not set architecture and machine")); #endif } void sbranch (size) int size; { bsize = size; } static void pint (arg) int arg ATTRIBUTE_UNUSED; { cons (Hmode ? 4 : 2); } /* 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[] = { {"h8300h", h8300hmode, 0}, {"h8300s", h8300smode, 0}, {"sbranch", sbranch, L_8}, {"lbranch", sbranch, L_16}, {"int", pint, 0}, {"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"; 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 () { unsigned int nopcodes; const struct h8_opcode *p; struct h8_instruction *pi; char prev_buffer[100]; int idx = 0; #ifdef BFD_ASSEMBLER if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300)) as_warn (_("could not set architecture and machine")); #endif opcode_hash_control = hash_new (); prev_buffer[0] = 0; nopcodes = sizeof (h8_opcodes) / sizeof (struct h8_opcode); h8_instructions = (struct h8_instruction *) xmalloc (nopcodes * sizeof (struct h8_instruction)); for (p = h8_opcodes, pi = h8_instructions; p->name; p++, pi++) { /* Strip off any . part when inserting the opcode and only enter unique codes into the hash table. */ char *src = p->name; unsigned int len = strlen (src); char *dst = malloc (len + 1); char *buffer = dst; pi->size = 0; while (*src) { if (*src == '.') { src++; pi->size = *src; break; } *dst++ = *src++; } *dst++ = 0; if (strcmp (buffer, prev_buffer)) { hash_insert (opcode_hash_control, buffer, (char *) pi); strcpy (prev_buffer, buffer); idx++; } pi->idx = idx; /* Find the number of operands. */ pi->noperands = 0; while (p->args.nib[pi->noperands] != E) pi->noperands++; /* Find the length of the opcode in bytes. */ pi->length = 0; while (p->data.nib[pi->length * 2] != E) pi->length++; pi->opcode = p; } /* Add entry for the NULL vector terminator. */ pi->length = 0; pi->noperands = 0; pi->idx = 0; pi->size = 0; pi->opcode = p; linkrelax = 1; } 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; }; static void clever_message PARAMS ((const struct h8_instruction *, struct h8_op *)); static void build_bytes PARAMS ((const struct h8_instruction *, struct h8_op *)); static void do_a_fix_imm PARAMS ((int, struct h8_op *, int)); static void check_operand PARAMS ((struct h8_op *, unsigned int, char *)); static const struct h8_instruction * get_specific PARAMS ((const struct h8_instruction *, struct h8_op *, int)); static char * get_operands PARAMS ((unsigned, char *, struct h8_op *)); static void get_operand PARAMS ((char **, struct h8_op *, unsigned, int)); static char * skip_colonthing PARAMS ((char *, expressionS *, int *)); static char * parse_exp PARAMS ((char *, expressionS *)); static int parse_reg PARAMS ((char *, op_type *, unsigned *, int)); char * colonmod24 PARAMS ((struct h8_op *, char *)); /* parse operands WREG r0,r1,r2,r3,r4,r5,r6,r7,fp,sp r0l,r0h,..r7l,r7h @WREG @WREG+ @-WREG #const ccr */ /* Try to parse a reg name. Return the number of chars consumed. */ static int parse_reg (src, mode, reg, direction) char *src; op_type *mode; unsigned int *reg; int direction; { char *end; int len; /* Cribbed from get_symbol_end. */ if (!is_name_beginner (*src) || *src == '\001') return 0; end = src + 1; while (is_part_of_name (*end) || *end == '\001') end++; len = end - src; if (len == 2 && src[0] == 's' && src[1] == 'p') { *mode = PSIZE | REG | direction; *reg = 7; return len; } if (len == 3 && src[0] == 'c' && src[1] == 'c' && src[2] == 'r') { *mode = CCR; *reg = 0; return len; } if (len == 3 && src[0] == 'e' && src[1] == 'x' && src[2] == 'r') { *mode = EXR; *reg = 0; return len; } if (len == 2 && src[0] == 'f' && src[1] == 'p') { *mode = PSIZE | REG | direction; *reg = 6; return len; } if (len == 3 && 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 not valid for H8/300")); return len; } if (len == 2 && src[0] == 'e' && src[1] >= '0' && src[1] <= '7') { *mode = L_16 | REG | direction; *reg = src[1] - '0' + 8; if (!Hmode) as_warn (_("Reg not valid for H8/300")); return len; } if (src[0] == 'r') { if (src[1] >= '0' && src[1] <= '7') { if (len == 3 && src[2] == 'l') { *mode = L_8 | REG | direction; *reg = (src[1] - '0') + 8; return len; } if (len == 3 && src[2] == 'h') { *mode = L_8 | REG | direction; *reg = (src[1] - '0'); return len; } if (len == 2) { *mode = L_16 | REG | direction; *reg = (src[1] - '0'); return len; } } } return 0; } static char * parse_exp (s, op) char *s; expressionS *op; { char *save = input_line_pointer; char *new; input_line_pointer = s; expression (op); if (op->X_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 ATTRIBUTE_UNUSED; int *mode; { if (*ptr == ':') { ptr++; *mode &= ~SIZE; if (*ptr == '8') { ptr++; /* ff fill any 8 bit quantity. */ /* exp->X_add_number -= 0x100; */ *mode |= L_8; } else { if (*ptr == '2') { *mode |= L_24; } else if (*ptr == '3') { *mode |= L_32; } 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 ATTRIBUTE_UNUSED; int direction; { char *src = *ptr; op_type mode; unsigned int num; unsigned int len; op->mode = E; /* Check for '(' and ')' for instructions ldm and stm. */ if (src[0] == '(' && src[8] == ')') ++ src; /* Gross. Gross. ldm and stm have a format not easily handled by get_operand. We deal with it explicitly here. */ if (src[0] == 'e' && src[1] == 'r' && ISDIGIT (src[2]) && src[3] == '-' && src[4] == 'e' && src[5] == 'r' && ISDIGIT (src[6])) { int low, high; low = src[2] - '0'; high = src[6] - '0'; if (high < low) as_bad (_("Invalid register list for ldm/stm\n")); if (low % 2) as_bad (_("Invalid register list for ldm/stm\n")); if (high - low > 3) as_bad (_("Invalid register list for ldm/stm\n")); if (high - low != 1 && low % 4) as_bad (_("Invalid register list for ldm/stm\n")); /* Even sicker. We encode two registers into op->reg. One for the low register to save, the other for the high register to save; we also set the high bit in op->reg so we know this is "very special". */ op->reg = 0x80000000 | (high << 8) | low; op->mode = REG; if (src[7] == ')') *ptr = src + 8; else *ptr = src + 7; return; } 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 |= ABS | 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 if (strncmp (src, "mach", 4) == 0 || strncmp (src, "macl", 4) == 0) { op->reg = src[3] == 'l'; op->mode = MACREG; *ptr = src + 4; 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 * get_operands (noperands, op_end, operand) unsigned int noperands; char *op_end; 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 const struct h8_instruction * get_specific (instruction, operands, size) const struct h8_instruction *instruction; struct h8_op *operands; int size; { const struct h8_instruction *this_try = instruction; int found = 0; int this_index = instruction->idx; /* There's only one ldm/stm and it's easier to just get out quick for them. */ if (strcmp (instruction->opcode->name, "stm.l") == 0 || strcmp (instruction->opcode->name, "ldm.l") == 0) return this_try; while (this_index == instruction->idx && !found) { found = 1; this_try = instruction++; if (this_try->noperands == 0) { int this_size; this_size = this_try->opcode->how & SN; if (this_size != size && (this_size != SB || size != SN)) found = 0; } else { int i; for (i = 0; i < this_try->noperands && found; i++) { op_type op = this_try->opcode->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))) { /* Promote a L_24 to L_32 if it makes us match. */ if ((x & L_24) && (op & L_32)) { x &= ~L_24; x |= L_32; } /* Promote an L8 to L_16 if it makes us match. */ if (op & ABS && op & L_8 && op & DISP) { if (x & L_16) found = 1; } else if ((x & SIZE) != 0 && ((op & SIZE) != (x & SIZE))) found = 0; } else if ((op & MACREG) != (x & MACREG)) { found = 0; } else if ((op & MODE) != (x & MODE)) { found = 0; } } } } if (found) return this_try; else return 0; } static void check_operand (operand, width, string) struct h8_op *operand; unsigned int width; 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) != (unsigned)(~0)) { if (width == 255 && (operand->exp.X_add_number & 0xff00) == 0xff00) { /* Just ignore this one - which happens when trying to fit a 16 bit address truncated into an 8 bit address of something like bset. */ } else if (strcmp (string, "@") == 0 && width == 0xffff && (operand->exp.X_add_number & 0xff8000) == 0xff8000) { /* Just ignore this one - which happens when trying to fit a 24 bit address truncated into a 16 bit address of something like mov.w. */ } else { as_warn (_("operand %s0x%lx out of range."), string, (unsigned long) operand->exp.X_add_number); } } } } /* RELAXMODE has one of 3 values: 0 Output a "normal" reloc, no relaxing possible for this insn/reloc 1 Output a relaxable 24bit absolute mov.w address relocation (may relax into a 16bit absolute address). 2 Output a relaxable 16/24 absolute mov.b address relocation (may relax into an 8bit absolute address). */ static void do_a_fix_imm (offset, operand, relaxmode) int offset; struct h8_op *operand; int relaxmode; { 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_2: check_operand (operand, 0x3, t); bytes[0] |= (operand->exp.X_add_number) << 4; break; 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; if (relaxmode != 0) { idx = (relaxmode == 2) ? R_MOV24B1 : R_MOVL1; fix_new_exp (frag_now, offset, 4, &operand->exp, 0, idx); } break; } } else { switch (operand->mode & SIZE) { case L_24: case L_32: size = 4; where = (operand->mode & SIZE) == L_24 ? -1 : 0; if (relaxmode == 2) idx = R_MOV24B1; else if (relaxmode == 1) idx = R_MOVL1; else idx = R_RELLONG; break; default: as_bad (_("Can't work out size of operand.\n")); case L_16: size = 2; where = 0; if (relaxmode == 2) idx = R_MOV16B1; else idx = R_RELWORD; operand->exp.X_add_number = ((operand->exp.X_add_number & 0xffff) ^ 0x8000) - 0x8000; break; case L_8: size = 1; where = 0; idx = R_RELBYTE; operand->exp.X_add_number = ((operand->exp.X_add_number & 0xff) ^ 0x80) - 0x80; } fix_new_exp (frag_now, offset + where, size, &operand->exp, 0, idx); } } /* Now we know what sort of opcodes it is, let's build the bytes. */ static void build_bytes (this_try, operand) const struct h8_instruction *this_try; struct h8_op *operand; { int i; char *output = frag_more (this_try->length); op_type *nibble_ptr = this_try->opcode->data.nib; op_type c; unsigned int nibble_count = 0; int absat = 0; int immat = 0; int nib = 0; int movb = 0; char asnibbles[30]; char *p = asnibbles; if (!(this_try->opcode->inbase || Hmode)) as_warn (_("Opcode `%s' with these operand types not available in H8/300 mode"), this_try->opcode->name); while (*nibble_ptr != E) { int d; c = *nibble_ptr++; d = (c & (DST | SRC_IN_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 & ABS) { operand[d].mode = c; absat = 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 not valid on H8/300.")); nib = 9; break; default: as_bad (_("Need #1 or #2 here")); break; } /* Stop it making a fix. */ operand[0].mode = 0; } if (c & MEMRELAX) operand[d].mode |= MEMRELAX; if (c & B31) nib |= 0x8; if (c & MACREG) { if (operand[0].mode == MACREG) /* stmac has mac[hl] as the first operand. */ nib = 2 + operand[0].reg; else /* ldmac has mac[hl] as the second operand. */ nib = 2 + operand[1].reg; } } nibble_count++; *p++ = nib; } /* Disgusting. Why, oh why didn't someone ask us for advice on the assembler format. */ if (strcmp (this_try->opcode->name, "stm.l") == 0 || strcmp (this_try->opcode->name, "ldm.l") == 0) { int high, low; high = (operand[this_try->opcode->name[0] == 'l' ? 1 : 0].reg >> 8) & 0xf; low = operand[this_try->opcode->name[0] == 'l' ? 1 : 0].reg & 0xf; asnibbles[2] = high - low; asnibbles[7] = (this_try->opcode->name[0] == 'l') ? high : low; } for (i = 0; i < this_try->length; i++) output[i] = (asnibbles[i * 2] << 4) | asnibbles[i * 2 + 1]; /* Note if this is a movb instruction -- there's a special relaxation which only applies to them. */ if (strcmp (this_try->opcode->name, "mov.b") == 0) movb = 1; /* Output any fixes. */ for (i = 0; i < 2; i++) { int x = operand[i].mode; if (x & (IMM | DISP)) do_a_fix_imm (output - frag_now->fr_literal + immat, operand + i, (x & MEMRELAX) != 0); else if (x & ABS) do_a_fix_imm (output - frag_now->fr_literal + absat, operand + i, (x & MEMRELAX) ? movb + 1 : 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; fixS *fixP; check_operand (operand + i, size16 ? 0x7fff : 0x7f, "@"); if (operand[i].exp.X_add_number & 1) as_warn (_("branch operand has odd offset (%lx)\n"), (unsigned long) operand->exp.X_add_number); #ifndef OBJ_ELF /* The COFF port has always been off by one, changing it now would be an incompatible change, so we leave it as-is. We don't want to do this for ELF as we want to be compatible with the proposed ELF format from Hitachi. */ operand[i].exp.X_add_number -= 1; #endif operand[i].exp.X_add_number = ((operand[i].exp.X_add_number & 0xff) ^ 0x80) - 0x80; fixP = fix_new_exp (frag_now, output - frag_now->fr_literal + where, size, &operand[i].exp, 1, type); fixP->fx_signed = 1; } 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_MEM_INDIRECT); } else if (x & ABSJMP) { int where = 0; #ifdef OBJ_ELF /* To be compatible with the proposed H8 ELF format, we want the relocation's offset to point to the first byte that will be modified, not to the start of the instruction. */ where += 1; #endif /* This jmp may be a jump or a branch. */ check_operand (operand + i, Hmode ? 0xffffff : 0xffff, "@"); if (operand[i].exp.X_add_number & 1) as_warn (_("branch operand has odd offset (%lx)\n"), (unsigned long) operand->exp.X_add_number); if (!Hmode) operand[i].exp.X_add_number = ((operand[i].exp.X_add_number & 0xffff) ^ 0x8000) - 0x8000; fix_new_exp (frag_now, output - frag_now->fr_literal + where, 4, &operand[i].exp, 0, R_JMPL1); } } } /* Try to give an intelligent error message for common and simple to detect errors. */ static void clever_message (instruction, operand) const struct h8_instruction *instruction; struct h8_op *operand; { /* Find out if there was more than one possible opcode. */ if ((instruction + 1)->idx != instruction->idx) { int argn; /* Only one opcode of this flavour, try to guess which operand didn't match. */ for (argn = 0; argn < instruction->noperands; argn++) { switch (instruction->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 function is supposed to emit the frags/bytes it assembles. */ void md_assemble (str) char *str; { char *op_start; char *op_end; struct h8_op operand[2]; const struct h8_instruction *instruction; const struct h8_instruction *prev_instruction; char *dot = 0; char c; int size; /* 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; instruction = (const struct h8_instruction *) hash_find (opcode_hash_control, op_start); if (instruction == NULL) { as_bad (_("unknown opcode")); return; } /* We used to set input_line_pointer to the result of get_operands, but that is wrong. Our caller assumes we don't change it. */ (void) get_operands (instruction->noperands, op_end, operand); *op_end = c; prev_instruction = instruction; size = SN; if (dot) { switch (*dot) { case 'b': size = SB; break; case 'w': size = SW; break; case 'l': size = SL; break; } } instruction = get_specific (instruction, operand, size); if (instruction == 0) { /* Couldn't find an opcode which matched the operands. */ char *where = frag_more (2); where[0] = 0x0; where[1] = 0x0; clever_message (prev_instruction, operand); return; } if (instruction->size && dot) { if (instruction->size != *dot) { as_warn (_("mismatch between opcode size and operand size")); } } build_bytes (instruction, operand); } #ifndef BFD_ASSEMBLER void tc_crawl_symbol_chain (headers) object_headers *headers ATTRIBUTE_UNUSED; { printf (_("call to tc_crawl_symbol_chain \n")); } #endif symbolS * md_undefined_symbol (name) char *name ATTRIBUTE_UNUSED; { return 0; } #ifndef BFD_ASSEMBLER void tc_headers_hook (headers) object_headers *headers ATTRIBUTE_UNUSED; { printf (_("call to tc_headers_hook \n")); } #endif /* 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; 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 0; } const char *md_shortopts = ""; struct option md_longopts[] = { {NULL, no_argument, NULL, 0} }; size_t md_longopts_size = sizeof (md_longopts); int md_parse_option (c, arg) int c ATTRIBUTE_UNUSED; char *arg ATTRIBUTE_UNUSED; { return 0; } void md_show_usage (stream) FILE *stream ATTRIBUTE_UNUSED; { } void tc_aout_fix_to_chars PARAMS ((void)); void tc_aout_fix_to_chars () { printf (_("call to tc_aout_fix_to_chars \n")); abort (); } void md_convert_frag (headers, seg, fragP) #ifdef BFD_ASSEMBLER bfd *headers ATTRIBUTE_UNUSED; #else object_headers *headers ATTRIBUTE_UNUSED; #endif segT seg ATTRIBUTE_UNUSED; fragS *fragP ATTRIBUTE_UNUSED; { printf (_("call to md_convert_frag \n")); abort (); } #ifdef BFD_ASSEMBLER valueT md_section_align (segment, size) segT segment; valueT size; { int align = bfd_get_section_alignment (stdoutput, segment); return ((size + (1 << align) - 1) & (-1 << align)); } #else valueT md_section_align (seg, size) segT seg; valueT size; { return ((size + (1 << section_alignment[(int) seg]) - 1) & (-1 << section_alignment[(int) seg])); } #endif void md_apply_fix3 (fixP, valP, seg) fixS *fixP; valueT *valP; segT seg ATTRIBUTE_UNUSED; { char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; long val = *valP; 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 (); } if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0) fixP->fx_done = 1; } int md_estimate_size_before_relax (fragP, segment_type) register fragS *fragP ATTRIBUTE_UNUSED; register segT segment_type ATTRIBUTE_UNUSED; { printf (_("call tomd_estimate_size_before_relax \n")); abort (); } /* Put number into target byte order. */ void md_number_to_chars (ptr, use, nbytes) char *ptr; valueT use; int nbytes; { number_to_chars_bigendian (ptr, use, nbytes); } long md_pcrel_from (fixP) fixS *fixP ATTRIBUTE_UNUSED; { abort (); } #ifndef BFD_ASSEMBLER 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 == TC_CONS_RELOC) { /* cons likes to create reloc32's whatever the size of the reloc.. */ switch (fix_ptr->fx_size) { case 4: intr->r_type = R_RELLONG; break; 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) { if (symbol_ptr->sy_number != -1) intr->r_symndx = symbol_ptr->sy_number; else { symbolS *segsym; /* This case arises when a reference is made to `.'. */ segsym = seg_info (S_GET_SEGMENT (symbol_ptr))->dot; if (segsym == NULL) intr->r_symndx = -1; else { intr->r_symndx = segsym->sy_number; intr->r_offset += S_GET_VALUE (symbol_ptr); } } } else intr->r_symndx = -1; } #else /* BFD_ASSEMBLER */ arelent * tc_gen_reloc (section, fixp) asection *section ATTRIBUTE_UNUSED; fixS *fixp; { arelent *rel; bfd_reloc_code_real_type r_type; if (fixp->fx_addsy && fixp->fx_subsy) { if ((S_GET_SEGMENT (fixp->fx_addsy) != S_GET_SEGMENT (fixp->fx_subsy)) || S_GET_SEGMENT (fixp->fx_addsy) == undefined_section) { as_bad_where (fixp->fx_file, fixp->fx_line, "Difference of symbols in different sections is not supported"); return NULL; } } rel = (arelent *) xmalloc (sizeof (arelent)); rel->sym_ptr_ptr = (asymbol **) 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 = fixp->fx_offset; r_type = fixp->fx_r_type; #define DEBUG 0 #if DEBUG fprintf (stderr, "%s\n", bfd_get_reloc_code_name (r_type)); fflush(stderr); #endif rel->howto = bfd_reloc_type_lookup (stdoutput, r_type); if (rel->howto == NULL) { as_bad_where (fixp->fx_file, fixp->fx_line, _("Cannot represent relocation type %s"), bfd_get_reloc_code_name (r_type)); return NULL; } return rel; } #endif