/* tc-h8300.c -- Assemble code for the Renesas H8/300 Copyright (C) 1991-2024 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 3, 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, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ /* Written By Steve Chamberlain . */ #include "as.h" #include "subsegs.h" #include "dwarf2dbg.h" #define DEFINE_TABLE #define h8_opcodes ops #include "opcode/h8300.h" #include "safe-ctype.h" #include "elf/h8.h" const char comment_chars[] = ";"; const char line_comment_chars[] = "#"; #ifdef TE_LINUX const char line_separator_chars[] = "!"; #else const char line_separator_chars[] = ""; #endif static void sbranch (int); static void h8300hmode (int); static void h8300smode (int); static void h8300hnmode (int); static void h8300snmode (int); static void h8300sxmode (int); static void h8300sxnmode (int); static void pint (int); int Hmode; int Smode; int Nmode; int SXmode; static int default_mach = bfd_mach_h8300; #define PSIZE (Hmode && !Nmode ? L_32 : L_16) static int bsize = L_8; /* Default branch displacement. */ struct h8_instruction { int length; int noperands; int idx; int size; const struct h8_opcode *opcode; }; static struct h8_instruction *h8_instructions; static void h8300hmode (int arg ATTRIBUTE_UNUSED) { Hmode = 1; Smode = 0; if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300h)) as_warn (_("could not set architecture and machine")); } static void h8300smode (int arg ATTRIBUTE_UNUSED) { Smode = 1; Hmode = 1; if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300s)) as_warn (_("could not set architecture and machine")); } static void h8300hnmode (int arg ATTRIBUTE_UNUSED) { Hmode = 1; Smode = 0; Nmode = 1; if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300hn)) as_warn (_("could not set architecture and machine")); } static void h8300snmode (int arg ATTRIBUTE_UNUSED) { Smode = 1; Hmode = 1; Nmode = 1; if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300sn)) as_warn (_("could not set architecture and machine")); } static void h8300sxmode (int arg ATTRIBUTE_UNUSED) { Smode = 1; Hmode = 1; SXmode = 1; if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300sx)) as_warn (_("could not set architecture and machine")); } static void h8300sxnmode (int arg ATTRIBUTE_UNUSED) { Smode = 1; Hmode = 1; SXmode = 1; Nmode = 1; if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300sxn)) as_warn (_("could not set architecture and machine")); } static void sbranch (int size) { bsize = size; } static void pint (int arg ATTRIBUTE_UNUSED) { cons (Hmode ? 4 : 2); } /* Like obj_elf_section, but issues a warning for new sections which do not have an attribute specification. */ static void h8300_elf_section (int push) { static const char * known_data_sections [] = { ".rodata", ".tdata", ".tbss" }; static const char * known_data_prefixes [] = { ".debug", ".zdebug", ".gnu.warning" }; char * saved_ilp = input_line_pointer; const char * name; name = obj_elf_section_name (); if (name == NULL) return; if (* input_line_pointer != ',' && bfd_get_section_by_name (stdoutput, name) == NULL) { signed int i; /* Ignore this warning for well known data sections. */ for (i = ARRAY_SIZE (known_data_sections); i--;) if (strcmp (name, known_data_sections[i]) == 0) break; if (i < 0) for (i = ARRAY_SIZE (known_data_prefixes); i--;) if (startswith (name, known_data_prefixes[i])) break; if (i < 0) as_warn (_("new section '%s' defined without attributes - this might cause problems"), name); } /* FIXME: We ought to free the memory allocated by obj_elf_section_name() for 'name', but we do not know if it was taken from the obstack, via demand_copy_C_string(), or xmalloc()ed. */ input_line_pointer = saved_ilp; obj_elf_section (push); } /* 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}, {"h8300hn", h8300hnmode, 0}, {"h8300s", h8300smode, 0}, {"h8300sn", h8300snmode, 0}, {"h8300sx", h8300sxmode, 0}, {"h8300sxn", h8300sxnmode, 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}, {"section", h8300_elf_section, 0}, {"section.s", h8300_elf_section, 0}, {"sect", h8300_elf_section, 0}, {"sect.s", h8300_elf_section, 0}, {0, 0, 0} }; 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 htab_t 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 (void) { unsigned int nopcodes; struct h8_opcode *p, *p1; struct h8_instruction *pi; int idx = 0; if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, default_mach)) as_warn (_("could not set architecture and machine")); opcode_hash_control = str_htab_create (); nopcodes = sizeof (h8_opcodes) / sizeof (struct h8_opcode); h8_instructions = XNEWVEC (struct h8_instruction, nopcodes); pi = h8_instructions; p1 = h8_opcodes; /* We do a minimum amount of sorting on the opcode table; this is to make it easy to describe the mova instructions without unnecessary code duplication. Sorting only takes place inside blocks of instructions of the form X/Y, so for example mova/b, mova/w and mova/l can be intermixed. */ while (p1) { struct h8_opcode *first_skipped = 0; int len, cmplen = 0; const char *src = p1->name; char *dst, *buffer; if (p1->name == 0) break; /* Strip off any . part when inserting the opcode and only enter unique codes into the hash table. */ dst = buffer = notes_alloc (strlen (src) + 1); while (*src) { if (*src == '.') { src++; break; } if (*src == '/') cmplen = src - p1->name + 1; *dst++ = *src++; } *dst = 0; len = dst - buffer; if (cmplen == 0) cmplen = len; str_hash_insert (opcode_hash_control, buffer, pi, 0); idx++; for (p = p1; p->name; p++) { /* A negative TIME is used to indicate that we've added this opcode already. */ if (p->time == -1) continue; if (strncmp (p->name, buffer, cmplen) != 0 || (p->name[cmplen] != '\0' && p->name[cmplen] != '.' && p->name[cmplen - 1] != '/')) { if (first_skipped == 0) first_skipped = p; break; } if (strncmp (p->name, buffer, len) != 0) { if (first_skipped == 0) first_skipped = p; continue; } p->time = -1; pi->size = p->name[len] == '.' ? p->name[len + 1] : 0; pi->idx = idx; /* Find the number of operands. */ pi->noperands = 0; while (pi->noperands < 3 && p->args.nib[pi->noperands] != (op_type) E) pi->noperands++; /* Find the length of the opcode in bytes. */ pi->length = 0; while (p->data.nib[pi->length * 2] != (op_type) E) pi->length++; pi->opcode = p; pi++; } p1 = first_skipped; } /* Add entry for the NULL vector terminator. */ pi->length = 0; pi->noperands = 0; pi->idx = 0; pi->size = 0; pi->opcode = 0; linkrelax = 1; } struct h8_op { op_type mode; unsigned reg; expressionS exp; }; static void clever_message (const struct h8_instruction *, struct h8_op *); static void fix_operand_size (struct h8_op *, int); static void build_bytes (const struct h8_instruction *, struct h8_op *); static void do_a_fix_imm (int, int, struct h8_op *, int, const struct h8_instruction *); static void check_operand (struct h8_op *, unsigned int, const char *); static const struct h8_instruction * get_specific (const struct h8_instruction *, struct h8_op *, int) ; static char *get_operands (unsigned, char *, struct h8_op *); static void get_operand (char **, struct h8_op *, int); static int parse_reg (char *, op_type *, unsigned *, int); static char *skip_colonthing (char *, int *); static char *parse_exp (char *, struct h8_op *); static int constant_fits_size_p (struct h8_op *, int, int); /* 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 (char *src, op_type *mode, unsigned int *reg, int direction) { char *end; int len; /* Cribbed from get_symbol_name. */ if (!is_name_beginner (*src) || *src == '\001') return 0; end = src + 1; while ((is_part_of_name (*end) && *end != '.') || *end == '\001') end++; len = end - src; if (len == 2 && TOLOWER (src[0]) == 's' && TOLOWER (src[1]) == 'p') { *mode = PSIZE | REG | direction; *reg = 7; return len; } if (len == 3 && TOLOWER (src[0]) == 'c' && TOLOWER (src[1]) == 'c' && TOLOWER (src[2]) == 'r') { *mode = CCR; *reg = 0; return len; } if (len == 3 && TOLOWER (src[0]) == 'e' && TOLOWER (src[1]) == 'x' && TOLOWER (src[2]) == 'r') { *mode = EXR; *reg = 1; return len; } if (len == 3 && TOLOWER (src[0]) == 'v' && TOLOWER (src[1]) == 'b' && TOLOWER (src[2]) == 'r') { *mode = VBR; *reg = 6; return len; } if (len == 3 && TOLOWER (src[0]) == 's' && TOLOWER (src[1]) == 'b' && TOLOWER (src[2]) == 'r') { *mode = SBR; *reg = 7; return len; } if (len == 2 && TOLOWER (src[0]) == 'f' && TOLOWER (src[1]) == 'p') { *mode = PSIZE | REG | direction; *reg = 6; return len; } if (len == 3 && TOLOWER (src[0]) == 'e' && TOLOWER (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 && TOLOWER (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 (TOLOWER (src[0]) == 'r') { if (src[1] >= '0' && src[1] <= '7') { if (len == 3 && TOLOWER (src[2]) == 'l') { *mode = L_8 | REG | direction; *reg = (src[1] - '0') + 8; return len; } if (len == 3 && TOLOWER (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; } /* Parse an immediate or address-related constant and store it in OP. If the user also specifies the operand's size, store that size in OP->MODE, otherwise leave it for later code to decide. */ static char * parse_exp (char *src, struct h8_op *op) { char *save; save = input_line_pointer; input_line_pointer = src; expression (&op->exp); if (op->exp.X_op == O_absent) as_bad (_("missing operand")); src = input_line_pointer; input_line_pointer = save; return skip_colonthing (src, &op->mode); } /* If SRC starts with an explicit operand size, skip it and store the size in *MODE. Leave *MODE unchanged otherwise. */ static char * skip_colonthing (char *src, int *mode) { if (*src == ':') { src++; *mode &= ~SIZE; if (src[0] == '8' && !ISDIGIT (src[1])) *mode |= L_8; else if (src[0] == '2' && !ISDIGIT (src[1])) *mode |= L_2; else if (src[0] == '3' && !ISDIGIT (src[1])) *mode |= L_3; else if (src[0] == '4' && !ISDIGIT (src[1])) *mode |= L_4; else if (src[0] == '5' && !ISDIGIT (src[1])) *mode |= L_5; else if (src[0] == '2' && src[1] == '4' && !ISDIGIT (src[2])) *mode |= L_24; else if (src[0] == '3' && src[1] == '2' && !ISDIGIT (src[2])) *mode |= L_32; else if (src[0] == '1' && src[1] == '6' && !ISDIGIT (src[2])) *mode |= L_16; else as_bad (_("invalid operand size requested")); while (ISDIGIT (*src)) src++; } return src; } /* 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. */ static int constant_fits_width_p (struct h8_op *operand, offsetT width) { offsetT num; num = ((operand->exp.X_add_number & 0xffffffff) ^ 0x80000000) - 0x80000000; return (num & ~width) == 0 || (num | width) == ~0; } static int constant_fits_size_p (struct h8_op *operand, int size, int no_symbols) { offsetT num; if (no_symbols && (operand->exp.X_add_symbol != 0 || operand->exp.X_op_symbol != 0)) return 0; num = operand->exp.X_add_number & 0xffffffff; switch (size) { case L_2: return (num & ~3) == 0; case L_3: return (num & ~7) == 0; case L_3NZ: return num >= 1 && num < 8; case L_4: return (num & ~15) == 0; case L_5: return num >= 1 && num < 32; case L_8: num = (num ^ 0x80000000) - 0x80000000; return (num & ~0xFF) == 0 || (num | 0x7F) == ~0; case L_8U: return (num & ~0xFF) == 0; case L_16: num = (num ^ 0x80000000) - 0x80000000; return (num & ~0xFFFF) == 0 || (num | 0x7FFF) == ~0; case L_16U: return (num & ~0xFFFF) == 0; case L_32: return 1; default: abort (); } } static void get_operand (char **ptr, struct h8_op *op, int direction) { char *src = *ptr; op_type mode; unsigned int num; unsigned int len; op->mode = 0; /* 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 (TOLOWER (src[0]) == 'e' && TOLOWER (src[1]) == 'r' && ISDIGIT (src[2]) && src[3] == '-' && TOLOWER (src[4]) == 'e' && TOLOWER (src[5]) == 'r' && ISDIGIT (src[6])) { int low, high; low = src[2] - '0'; high = src[6] - '0'; /* Check register pair's validity as per tech note TN-H8*-193A/E from Renesas for H8S and H8SX hardware manual. */ if ( !(low == 0 && (high == 1 || high == 2 || high == 3)) && !(low == 1 && (high == 2 || high == 3 || high == 4) && SXmode) && !(low == 2 && (high == 3 || ((high == 4 || high == 5) && SXmode))) && !(low == 3 && (high == 4 || high == 5 || high == 6) && SXmode) && !(low == 4 && (high == 5 || high == 6)) && !(low == 4 && high == 7 && SXmode) && !(low == 5 && (high == 6 || high == 7) && SXmode) && !(low == 6 && high == 7 && SXmode)) 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) { src += len; if (*src == '.') { int size = op->mode & SIZE; switch (src[1]) { case 'l': case 'L': if (size != L_32) as_warn (_("mismatch between register and suffix")); op->mode = (op->mode & ~MODE) | LOWREG; break; case 'w': case 'W': if (size != L_32 && size != L_16) as_warn (_("mismatch between register and suffix")); op->mode = (op->mode & ~MODE) | LOWREG; op->mode = (op->mode & ~SIZE) | L_16; break; case 'b': case 'B': op->mode = (op->mode & ~MODE) | LOWREG; if (size != L_32 && size != L_8) as_warn (_("mismatch between register and suffix")); op->mode = (op->mode & ~MODE) | LOWREG; op->mode = (op->mode & ~SIZE) | L_8; break; default: as_warn (_("invalid suffix after register.")); break; } src += 2; } *ptr = src; return; } if (*src == '@') { src++; if (*src == '@') { *ptr = parse_exp (src + 1, op); if (op->exp.X_add_number >= 0x100) { int divisor = 1; op->mode = VECIND; /* FIXME : 2? or 4? */ if (op->exp.X_add_number >= 0x400) as_bad (_("address too high for vector table jmp/jsr")); else if (op->exp.X_add_number >= 0x200) divisor = 4; else divisor = 2; op->exp.X_add_number = op->exp.X_add_number / divisor - 0x80; } else op->mode = MEMIND; return; } if (*src == '-' || *src == '+') { len = parse_reg (src + 1, &mode, &num, direction); if (len == 0) { /* Oops, not a reg after all, must be ordinary exp. */ op->mode = ABS | direction; *ptr = parse_exp (src, op); return; } if (((mode & SIZE) != PSIZE) /* For Normal mode accept 16 bit and 32 bit pointer registers. */ && (!Nmode || ((mode & SIZE) != L_32))) as_bad (_("Wrong size pointer register for architecture.")); op->mode = src[0] == '-' ? RDPREDEC : RDPREINC; op->reg = num; *ptr = src + 1 + len; return; } if (*src == '(') { src++; /* See if this is @(ERn.x, PC). */ len = parse_reg (src, &mode, &op->reg, direction); if (len != 0 && (mode & MODE) == REG && src[len] == '.') { switch (TOLOWER (src[len + 1])) { case 'b': mode = PCIDXB | direction; break; case 'w': mode = PCIDXW | direction; break; case 'l': mode = PCIDXL | direction; break; default: mode = 0; break; } if (mode && src[len + 2] == ',' && TOLOWER (src[len + 3]) != 'p' && TOLOWER (src[len + 4]) != 'c' && src[len + 5] != ')') { *ptr = src + len + 6; op->mode |= mode; return; } /* Fall through into disp case - the grammar is somewhat ambiguous, so we should try whether it's a DISP operand after all ("ER3.L" might be a poorly named label...). */ } /* Disp. */ /* Start off assuming a 16 bit offset. */ src = parse_exp (src, op); if (*src == ')') { op->mode |= ABS | direction; *ptr = src + 1; return; } if (*src != ',') { as_bad (_("expected @(exp, reg16)")); return; } src++; len = parse_reg (src, &mode, &op->reg, direction); if (len == 0 || (mode & MODE) != REG) { as_bad (_("expected @(exp, reg16)")); return; } src += len; if (src[0] == '.') { switch (TOLOWER (src[1])) { case 'b': op->mode |= INDEXB | direction; break; case 'w': op->mode |= INDEXW | direction; break; case 'l': op->mode |= INDEXL | direction; break; default: as_bad (_("expected .L, .W or .B for register in indexed addressing mode")); } src += 2; op->reg &= 7; } else op->mode |= DISP | direction; src = skip_colonthing (src, &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) /* For Normal mode accept 16 bit and 32 bit pointer registers. */ && (!Nmode || ((mode & SIZE) != L_32))) as_bad (_("Wrong size pointer register for architecture.")); op->mode = *src == '+' ? RSPOSTINC : RSPOSTDEC; op->reg = num; src++; *ptr = src; return; } if (((mode & SIZE) != PSIZE) /* For Normal mode accept 16 bit and 32 bit pointer registers. */ && (!Nmode || ((mode & SIZE) != L_32))) 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; *ptr = parse_exp (src, op); return; } } if (*src == '#') { op->mode = IMM; *ptr = parse_exp (src + 1, op); return; } else if (startswith (src, "mach") || startswith (src, "macl") || startswith (src, "MACH") || startswith (src, "MACL")) { op->reg = TOLOWER (src[3]) == 'l'; op->mode = MACREG; *ptr = src + 4; return; } else { op->mode = PCREL; *ptr = parse_exp (src, op); } } static char * get_operands (unsigned int noperands, char *op_end, struct h8_op *operand) { char *ptr = op_end; switch (noperands) { case 0: break; case 1: ptr++; get_operand (&ptr, operand + 0, SRC); if (*ptr == ',') { ptr++; get_operand (&ptr, operand + 1, DST); } break; case 2: ptr++; get_operand (&ptr, operand + 0, SRC); if (*ptr == ',') ptr++; get_operand (&ptr, operand + 1, DST); break; case 3: ptr++; get_operand (&ptr, operand + 0, SRC); if (*ptr == ',') ptr++; get_operand (&ptr, operand + 1, DST); if (*ptr == ',') ptr++; get_operand (&ptr, operand + 2, OP3); break; default: abort (); } return ptr; } /* MOVA has special requirements. Rather than adding twice the amount of addressing modes, we simply special case it a bit. */ static void get_mova_operands (char *op_end, struct h8_op *operand) { char *ptr = op_end; if (ptr[1] != '@' || ptr[2] != '(') goto error; ptr += 3; operand[0].mode = 0; ptr = parse_exp (ptr, &operand[0]); if (*ptr !=',') goto error; ptr++; get_operand (&ptr, operand + 1, DST); if (*ptr =='.') { ptr++; switch (*ptr++) { case 'b': case 'B': operand[0].mode = (operand[0].mode & ~MODE) | INDEXB; break; case 'w': case 'W': operand[0].mode = (operand[0].mode & ~MODE) | INDEXW; break; case 'l': case 'L': operand[0].mode = (operand[0].mode & ~MODE) | INDEXL; break; default: goto error; } } else if ((operand[1].mode & MODE) == LOWREG) { switch (operand[1].mode & SIZE) { case L_8: operand[0].mode = (operand[0].mode & ~MODE) | INDEXB; break; case L_16: operand[0].mode = (operand[0].mode & ~MODE) | INDEXW; break; case L_32: operand[0].mode = (operand[0].mode & ~MODE) | INDEXL; break; default: goto error; } } else goto error; if (*ptr++ != ')' || *ptr++ != ',') goto error; get_operand (&ptr, operand + 2, OP3); /* See if we can use the short form of MOVA. */ if (((operand[1].mode & MODE) == REG || (operand[1].mode & MODE) == LOWREG) && (operand[2].mode & MODE) == REG && (operand[1].reg & 7) == (operand[2].reg & 7)) { operand[1].mode = operand[2].mode = 0; operand[0].reg = operand[2].reg & 7; } return; error: as_bad (_("expected valid addressing mode for mova: \"@(disp, ea.sz),ERn\"")); } static void get_rtsl_operands (char *ptr, struct h8_op *operand) { int mode, len, type = 0; unsigned int num, num2; ptr++; if (*ptr == '(') { ptr++; type = 1; } len = parse_reg (ptr, &mode, &num, SRC); if (len == 0 || (mode & MODE) != REG) { as_bad (_("expected register")); return; } ptr += len; if (*ptr == '-') { len = parse_reg (++ptr, &mode, &num2, SRC); if (len == 0 || (mode & MODE) != REG) { as_bad (_("expected register")); return; } ptr += len; /* CONST_xxx are used as placeholders in the opcode table. */ num = num2 - num; if (num > 3) { as_bad (_("invalid register list")); return; } } else num2 = num, num = 0; if (type == 1 && *ptr++ != ')') { as_bad (_("expected closing paren")); return; } operand[0].mode = RS32; operand[1].mode = RD32; operand[0].reg = num; operand[1].reg = num2; } /* 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 (const struct h8_instruction *instruction, struct h8_op *operands, int size) { const struct h8_instruction *this_try = instruction; const struct h8_instruction *found_other = 0, *found_mismatched = 0; int found = 0; int this_index = instruction->idx; int noperands = 0; /* There's only one ldm/stm and it's easier to just get out quick for them. */ if (OP_KIND (instruction->opcode->how) == O_LDM || OP_KIND (instruction->opcode->how) == O_STM) return this_try; while (noperands < 3 && operands[noperands].mode != 0) noperands++; while (this_index == instruction->idx && !found) { int this_size; found = 1; this_try = instruction++; this_size = this_try->opcode->how & SN; if (this_try->noperands != noperands) found = 0; else if (this_try->noperands > 0) { int i; for (i = 0; i < this_try->noperands && found; i++) { op_type op = this_try->opcode->args.nib[i]; int op_mode = op & MODE; int op_size = op & SIZE; int x = operands[i].mode; int x_mode = x & MODE; int x_size = x & SIZE; if (op_mode == LOWREG && (x_mode == REG || x_mode == LOWREG)) { if ((x_size == L_8 && (operands[i].reg & 8) == 0) || (x_size == L_16 && (operands[i].reg & 8) == 8)) as_warn (_("can't use high part of register in operand %d"), i); if (x_size != op_size) found = 0; } else if (op_mode == REG) { if (x_mode == LOWREG) x_mode = REG; if (x_mode != REG) found = 0; if (x_size == L_P) x_size = (Hmode ? L_32 : L_16); if (op_size == L_P) op_size = (Hmode ? L_32 : L_16); /* The size of the reg is v important. */ if (op_size != x_size) found = 0; } else if (op_mode & CTRL) /* control register */ { if (!(x_mode & CTRL)) found = 0; switch (x_mode) { case CCR: if (op_mode != CCR && op_mode != CCR_EXR && op_mode != CC_EX_VB_SB) found = 0; break; case EXR: if (op_mode != EXR && op_mode != CCR_EXR && op_mode != CC_EX_VB_SB) found = 0; break; case MACH: if (op_mode != MACH && op_mode != MACREG) found = 0; break; case MACL: if (op_mode != MACL && op_mode != MACREG) found = 0; break; case VBR: if (op_mode != VBR && op_mode != VBR_SBR && op_mode != CC_EX_VB_SB) found = 0; break; case SBR: if (op_mode != SBR && op_mode != VBR_SBR && op_mode != CC_EX_VB_SB) found = 0; break; } } else if ((op & ABSJMP) && (x_mode == ABS || x_mode == PCREL)) { operands[i].mode &= ~MODE; operands[i].mode |= ABSJMP; /* But it may not be 24 bits long. */ if (x_mode == ABS && !Hmode) { operands[i].mode &= ~SIZE; operands[i].mode |= L_16; } if ((operands[i].mode & SIZE) == L_32 && (op_mode & SIZE) != L_32) found = 0; } else if (x_mode == IMM && op_mode != IMM) { offsetT num = operands[i].exp.X_add_number & 0xffffffff; if (op_mode == KBIT || op_mode == DBIT) /* This is ok if the immediate value is sensible. */; else if (op_mode == CONST_2) found = num == 2; else if (op_mode == CONST_4) found = num == 4; else if (op_mode == CONST_8) found = num == 8; else if (op_mode == CONST_16) found = num == 16; else found = 0; } else if (op_mode == PCREL && op_mode == x_mode) { /* movsd, bsr/bc and bsr/bs only come in PCREL16 flavour: If x_size is L_8, promote it. */ if (OP_KIND (this_try->opcode->how) == O_MOVSD || OP_KIND (this_try->opcode->how) == O_BSRBC || OP_KIND (this_try->opcode->how) == O_BSRBS) if (x_size == L_8) x_size = L_16; /* The size of the displacement is important. */ if (op_size != x_size) found = 0; } else if ((op_mode == DISP || op_mode == IMM || op_mode == ABS || op_mode == INDEXB || op_mode == INDEXW || op_mode == INDEXL) && op_mode == x_mode) { /* Promote a L_24 to L_32 if it makes us match. */ if (x_size == L_24 && op_size == L_32) { x &= ~SIZE; x |= x_size = L_32; } if (((x_size == L_16 && op_size == L_16U) || (x_size == L_8 && op_size == L_8U) || (x_size == L_3 && op_size == L_3NZ)) /* We're deliberately more permissive for ABS modes. */ && (op_mode == ABS || constant_fits_size_p (operands + i, op_size, op & NO_SYMBOLS))) x_size = op_size; if (x_size != 0 && op_size != x_size) found = 0; else if (x_size == 0 && ! constant_fits_size_p (operands + i, op_size, op & NO_SYMBOLS)) found = 0; } else if (op_mode != x_mode) { found = 0; } } } if (found) { if ((this_try->opcode->available == AV_H8SX && ! SXmode) || (this_try->opcode->available == AV_H8S && ! Smode) || (this_try->opcode->available == AV_H8H && ! Hmode)) found = 0, found_other = this_try; else if (this_size != size && (this_size != SN && size != SN)) found_mismatched = this_try, found = 0; } } if (found) return this_try; if (found_other) { as_warn (_("Opcode `%s' with these operand types not available in %s mode"), found_other->opcode->name, (! Hmode && ! Smode ? "H8/300" : SXmode ? "H8sx" : Smode ? "H8/300S" : "H8/300H")); } else if (found_mismatched) { as_warn (_("mismatch between opcode size and operand size")); return found_mismatched; } return 0; } static void check_operand (struct h8_op *operand, unsigned int width, const 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 (! constant_fits_width_p (operand, width)) { 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 (int offset, int nibble, struct h8_op *operand, int relaxmode, const struct h8_instruction *this_try) { int idx; int size; int where; char *bytes = frag_now->fr_literal + offset; const char *t = ((operand->mode & MODE) == IMM) ? "#" : "@"; if (operand->exp.X_add_symbol == 0) { switch (operand->mode & SIZE) { case L_2: check_operand (operand, 0x3, t); bytes[0] |= (operand->exp.X_add_number & 3) << (nibble ? 0 : 4); break; case L_3: case L_3NZ: check_operand (operand, 0x7, t); bytes[0] |= (operand->exp.X_add_number & 7) << (nibble ? 0 : 4); break; case L_4: check_operand (operand, 0xF, t); bytes[0] |= (operand->exp.X_add_number & 15) << (nibble ? 0 : 4); break; case L_5: check_operand (operand, 0x1F, t); bytes[0] |= operand->exp.X_add_number & 31; break; case L_8: case L_8U: check_operand (operand, 0xff, t); bytes[0] |= operand->exp.X_add_number; break; case L_16: case L_16U: check_operand (operand, 0xffff, t); bytes[0] |= operand->exp.X_add_number >> 8; bytes[1] |= operand->exp.X_add_number >> 0; /* MOVA needs both relocs to relax the second operand properly. */ if (relaxmode != 0 && (OP_KIND(this_try->opcode->how) == O_MOVAB || OP_KIND(this_try->opcode->how) == O_MOVAW || OP_KIND(this_try->opcode->how) == O_MOVAL)) { idx = BFD_RELOC_16; fix_new_exp (frag_now, offset, 2, &operand->exp, 0, idx); } 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) { if ((operand->mode & MODE) == DISP && relaxmode == 1) idx = BFD_RELOC_H8_DISP32A16; else 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 ((operand->mode & MODE) == DISP && relaxmode == 1) idx = BFD_RELOC_H8_DISP32A16; else 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")); /* Fall through. */ case L_16: case L_16U: 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; operand->exp.X_add_number |= (bytes[0] << 8) | bytes[1]; break; case L_8: size = 1; where = 0; idx = R_RELBYTE; operand->exp.X_add_number = ((operand->exp.X_add_number & 0xff) ^ 0x80) - 0x80; operand->exp.X_add_number |= bytes[0]; } 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 (const struct h8_instruction *this_try, struct h8_op *operand) { int i; char *output = frag_more (this_try->length); const op_type *nibble_ptr = this_try->opcode->data.nib; op_type c; unsigned int nibble_count = 0; int op_at[3]; int nib = 0; int movb = 0; char asnibbles[100]; char *p = asnibbles; int high, low; if (!Hmode && this_try->opcode->available != AV_H8) as_warn (_("Opcode `%s' with these operand types not available in H8/300 mode"), this_try->opcode->name); else if (!Smode && this_try->opcode->available != AV_H8 && this_try->opcode->available != AV_H8H) as_warn (_("Opcode `%s' with these operand types not available in H8/300H mode"), this_try->opcode->name); else if (!SXmode && this_try->opcode->available != AV_H8 && this_try->opcode->available != AV_H8H && this_try->opcode->available != AV_H8S) as_warn (_("Opcode `%s' with these operand types not available in H8/300S mode"), this_try->opcode->name); while (*nibble_ptr != (op_type) E) { int d; nib = 0; c = *nibble_ptr++; d = (c & OP3) == OP3 ? 2 : (c & DST) == DST ? 1 : 0; if (c < 16) nib = c; else { int c2 = c & MODE; if (c2 == REG || c2 == LOWREG || c2 == IND || c2 == PREINC || c2 == PREDEC || c2 == POSTINC || c2 == POSTDEC) { nib = operand[d].reg; if (c2 == LOWREG) nib &= 7; } else if (c & CTRL) /* Control reg operand. */ nib = operand[d].reg; else if ((c & DISPREG) == (DISPREG)) { nib = operand[d].reg; } else if (c2 == ABS) { operand[d].mode = c; op_at[d] = nibble_count; nib = 0; } else if (c2 == IMM || c2 == PCREL || c2 == ABS || (c & ABSJMP) || c2 == DISP) { operand[d].mode = c; op_at[d] = nibble_count; nib = 0; } else if ((c & IGNORE) || (c & DATA)) nib = 0; else if (c2 == 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 (c2 == 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 & B21) nib |= 0x4; if (c & B11) nib |= 0x2; if (c & B01) nib |= 0x1; if (c2 == 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 (OP_KIND (this_try->opcode->how) == O_LDM) { high = (operand[1].reg >> 8) & 0xf; low = (operand[1].reg) & 0xf; asnibbles[2] = high - low; asnibbles[7] = high; } else if (OP_KIND (this_try->opcode->how) == O_STM) { high = (operand[0].reg >> 8) & 0xf; low = (operand[0].reg) & 0xf; asnibbles[2] = high - low; asnibbles[7] = low; } for (i = 0; i < this_try->length; i++) output[i] = (asnibbles[i * 2] << 4) | asnibbles[i * 2 + 1]; /* Note if this is a mov.b or a bit manipulation instruction there is a special relaxation which only applies. */ if ( this_try->opcode->how == O (O_MOV, SB) || this_try->opcode->how == O (O_BCLR, SB) || this_try->opcode->how == O (O_BAND, SB) || this_try->opcode->how == O (O_BIAND, SB) || this_try->opcode->how == O (O_BILD, SB) || this_try->opcode->how == O (O_BIOR, SB) || this_try->opcode->how == O (O_BIST, SB) || this_try->opcode->how == O (O_BIXOR, SB) || this_try->opcode->how == O (O_BLD, SB) || this_try->opcode->how == O (O_BNOT, SB) || this_try->opcode->how == O (O_BOR, SB) || this_try->opcode->how == O (O_BSET, SB) || this_try->opcode->how == O (O_BST, SB) || this_try->opcode->how == O (O_BTST, SB) || this_try->opcode->how == O (O_BXOR, SB)) movb = 1; /* Output any fixes. */ for (i = 0; i < this_try->noperands; i++) { int x = operand[i].mode; int x_mode = x & MODE; if (x_mode == IMM || x_mode == DISP) do_a_fix_imm (output - frag_now->fr_literal + op_at[i] / 2, op_at[i] & 1, operand + i, (x & MEMRELAX) != 0, this_try); else if (x_mode == ABS) do_a_fix_imm (output - frag_now->fr_literal + op_at[i] / 2, op_at[i] & 1, operand + i, (x & MEMRELAX) ? movb + 1 : 0, this_try); else if (x_mode == PCREL) { int size16 = (x & SIZE) == L_16; 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); if (size16) { operand[i].exp.X_add_number = ((operand[i].exp.X_add_number & 0xffff) ^ 0x8000) - 0x8000; } else { operand[i].exp.X_add_number = ((operand[i].exp.X_add_number & 0xff) ^ 0x80) - 0x80; } /* For BRA/S. */ if (! size16) operand[i].exp.X_add_number |= output[op_at[i] / 2]; fixP = fix_new_exp (frag_now, output - frag_now->fr_literal + op_at[i] / 2, size, &operand[i].exp, 1, type); fixP->fx_signed = 1; } else if (x_mode == 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_mode == VECIND) { check_operand (operand + i, 0x7f, "@@"); /* FIXME: approximating the effect of "B31" here... This is very hackish, and ought to be done a better way. */ operand[i].exp.X_add_number |= 0x80; 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; bfd_reloc_code_real_type reloc_type = R_JMPL1; /* 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. */ if ((operand->mode & SIZE) == L_32) { where = 2; reloc_type = R_RELLONG; } else where = 1; /* This jmp may be a jump or a branch. */ check_operand (operand + i, SXmode ? 0xffffffff : 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, reloc_type); } } } /* Try to give an intelligent error message for common and simple to detect errors. */ static void clever_message (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")); } /* If OPERAND is part of an address, adjust its size and value given that it addresses SIZE bytes. This function decides how big non-immediate constants are when no size was explicitly given. It also scales down the assembly-level displacement in an @(d:2,ERn) operand. */ static void fix_operand_size (struct h8_op *operand, int size) { if (SXmode && (operand->mode & MODE) == DISP) { /* If the user didn't specify an operand width, see if we can use @(d:2,ERn). */ if ((operand->mode & SIZE) == 0 && operand->exp.X_add_symbol == 0 && operand->exp.X_op_symbol == 0 && (operand->exp.X_add_number == size || operand->exp.X_add_number == size * 2 || operand->exp.X_add_number == size * 3)) operand->mode |= L_2; /* Scale down the displacement in an @(d:2,ERn) operand. X_add_number then contains the desired field value. */ if ((operand->mode & SIZE) == L_2) { if (operand->exp.X_add_number % size != 0) as_warn (_("operand/size mis-match")); operand->exp.X_add_number /= size; } } if ((operand->mode & SIZE) == 0) switch (operand->mode & MODE) { case DISP: case INDEXB: case INDEXW: case INDEXL: case ABS: /* Pick a 24-bit address unless we know that a 16-bit address is safe. get_specific() will relax L_24 into L_32 where necessary. */ if (Hmode && !Nmode && ((((addressT) operand->exp.X_add_number + 0x8000) & 0xffffffff) > 0xffff || operand->exp.X_add_symbol != 0 || operand->exp.X_op_symbol != 0)) operand->mode |= L_24; else operand->mode |= L_16; break; case PCREL: if ((((addressT) operand->exp.X_add_number + 0x80) & 0xffffffff) <= 0xff) { if (operand->exp.X_add_symbol != NULL) operand->mode |= bsize; else operand->mode |= L_8; } else operand->mode |= L_16; break; } } /* 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 (char *str) { char *op_start; char *op_end; struct h8_op operand[3]; const struct h8_instruction *instruction; const struct h8_instruction *prev_instruction; char *dot = 0; char *slash = 0; char c; int size, i; /* 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; } else if (*op_end == '/' && ! slash) slash = op_end; } if (op_end == op_start) { as_bad (_("can't find opcode ")); } c = *op_end; *op_end = 0; /* The assembler stops scanning the opcode at slashes, so it fails to make characters following them lower case. Fix them. */ if (slash) while (*++slash) *slash = TOLOWER (*slash); instruction = (const struct h8_instruction *) str_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. */ operand[0].mode = 0; operand[1].mode = 0; operand[2].mode = 0; if (OP_KIND (instruction->opcode->how) == O_MOVAB || OP_KIND (instruction->opcode->how) == O_MOVAW || OP_KIND (instruction->opcode->how) == O_MOVAL) get_mova_operands (op_end, operand); else if (OP_KIND (instruction->opcode->how) == O_RTEL || OP_KIND (instruction->opcode->how) == O_RTSL) get_rtsl_operands (op_end, operand); else get_operands (instruction->noperands, op_end, operand); *op_end = c; prev_instruction = instruction; /* Now we have operands from instruction. Let's check them out for ldm and stm. */ if (OP_KIND (instruction->opcode->how) == O_LDM) { /* The first operand must be @er7+, and the second operand must be a register pair. */ if ((operand[0].mode != RSINC) || (operand[0].reg != 7) || ((operand[1].reg & 0x80000000) == 0)) as_bad (_("invalid operand in ldm")); } else if (OP_KIND (instruction->opcode->how) == O_STM) { /* The first operand must be a register pair, and the second operand must be @-er7. */ if (((operand[0].reg & 0x80000000) == 0) || (operand[1].mode != RDDEC) || (operand[1].reg != 7)) as_bad (_("invalid operand in stm")); } size = SN; if (dot) { switch (TOLOWER (*dot)) { case 'b': size = SB; break; case 'w': size = SW; break; case 'l': size = SL; break; } } if (OP_KIND (instruction->opcode->how) == O_MOVAB || OP_KIND (instruction->opcode->how) == O_MOVAW || OP_KIND (instruction->opcode->how) == O_MOVAL) { switch (operand[0].mode & MODE) { case INDEXB: default: fix_operand_size (&operand[1], 1); break; case INDEXW: fix_operand_size (&operand[1], 2); break; case INDEXL: fix_operand_size (&operand[1], 4); break; } } else { for (i = 0; i < 3 && operand[i].mode != 0; i++) switch (size) { case SN: case SB: default: fix_operand_size (&operand[i], 1); break; case SW: fix_operand_size (&operand[i], 2); break; case SL: fix_operand_size (&operand[i], 4); 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; } build_bytes (instruction, operand); dwarf2_emit_insn (instruction->length); } symbolS * md_undefined_symbol (char *name ATTRIBUTE_UNUSED) { return 0; } /* Various routines to kill one day. */ const char * md_atof (int type, char *litP, int *sizeP) { return ieee_md_atof (type, litP, sizeP, true); } #define OPTION_H_TICK_HEX (OPTION_MD_BASE) #define OPTION_MACH (OPTION_MD_BASE+1) const char md_shortopts[] = ""; const struct option md_longopts[] = { { "h-tick-hex", no_argument, NULL, OPTION_H_TICK_HEX }, { "mach", required_argument, NULL, OPTION_MACH }, {NULL, no_argument, NULL, 0} }; const size_t md_longopts_size = sizeof (md_longopts); struct mach_func { const char *name; void (*func) (void); }; static void mach_h8300h (void) { Hmode = 1; Smode = 0; Nmode = 0; SXmode = 0; default_mach = bfd_mach_h8300h; } static void mach_h8300hn (void) { Hmode = 1; Smode = 0; Nmode = 1; SXmode = 0; default_mach = bfd_mach_h8300hn; } static void mach_h8300s (void) { Hmode = 1; Smode = 1; Nmode = 0; SXmode = 0; default_mach = bfd_mach_h8300s; } static void mach_h8300sn (void) { Hmode = 1; Smode = 1; Nmode = 1; SXmode = 0; default_mach = bfd_mach_h8300sn; } static void mach_h8300sx (void) { Hmode = 1; Smode = 1; Nmode = 0; SXmode = 1; default_mach = bfd_mach_h8300sx; } static void mach_h8300sxn (void) { Hmode = 1; Smode = 1; Nmode = 1; SXmode = 1; default_mach = bfd_mach_h8300sxn; } const struct mach_func mach_table[] = { {"h8300h", mach_h8300h}, {"h8300hn", mach_h8300hn}, {"h8300s", mach_h8300s}, {"h8300sn", mach_h8300sn}, {"h8300sx", mach_h8300sx}, {"h8300sxn", mach_h8300sxn} }; int md_parse_option (int c ATTRIBUTE_UNUSED, const char *arg ATTRIBUTE_UNUSED) { unsigned int i; switch (c) { case OPTION_H_TICK_HEX: enable_h_tick_hex = 1; break; case OPTION_MACH: for (i = 0; i < sizeof(mach_table) / sizeof(struct mach_func); i++) { if (strcasecmp (arg, mach_table[i].name) == 0) { mach_table[i].func(); break; } } if (i >= sizeof(mach_table) / sizeof(struct mach_func)) as_bad (_("Invalid argument to --mach option: %s"), arg); break; default: return 0; } return 1; } void md_show_usage (FILE *stream) { fprintf (stream, _(" H8300-specific assembler options:\n")); fprintf (stream, _("\ -mach= Set the H8300 machine type to one of:\n\ h8300h, h8300hn, h8300s, h8300sn, h8300sx, h8300sxn\n")); fprintf (stream, _("\ -h-tick-hex Support H'00 style hex constants\n")); } void tc_aout_fix_to_chars (void); void tc_aout_fix_to_chars (void) { printf (_("call to tc_aout_fix_to_chars \n")); abort (); } void md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT seg ATTRIBUTE_UNUSED, fragS *fragP ATTRIBUTE_UNUSED) { printf (_("call to md_convert_frag \n")); abort (); } valueT md_section_align (segT segment, valueT size) { int align = bfd_section_alignment (segment); return ((size + (1 << align) - 1) & (-1U << align)); } void md_apply_fix (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; case 8: /* This can arise when the .quad or .8byte pseudo-ops are used. Returning here (without setting fx_done) will cause the code to attempt to generate a reloc which will then fail with the slightly more helpful error message: "Cannot represent relocation type BFD_RELOC_64". */ return; default: abort (); } if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0) fixP->fx_done = 1; } int md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED, segT segment_type ATTRIBUTE_UNUSED) { printf (_("call to md_estimate_size_before_relax \n")); abort (); } /* Put number into target byte order. */ void md_number_to_chars (char *ptr, valueT use, int nbytes) { number_to_chars_bigendian (ptr, use, nbytes); } long md_pcrel_from (fixS *fixp) { as_bad_where (fixp->fx_file, fixp->fx_line, _("Unexpected reference to a symbol in a non-code section")); return 0; } arelent * tc_gen_reloc (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_subtract (fixp); return NULL; } } rel = XNEW (arelent); rel->sym_ptr_ptr = XNEW (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; }