[Patch 6/8]: 68HC11 port of Binutils (gas)

Stephane Carrez Stephane.Carrez@worldnet.fr
Fri Jun 16 14:49:00 GMT 2000


Hi!

Here comes the assembler for 68hc11 and 68hc12.
Documentation and tests will follow.

The ChangeLog for binutils/gas is

2000-06-16  Stephane Carrez  <stcarrez@worldnet.fr>

	* configure, Makefile.in: Regenerate.
	* configure.in (emulations): Recognize m6811 and m6812.
	* Makefile.am (CPU_TYPES, TARGET_CPU_CFILES, TARGET_CPU_HFILES):
	Added files for 68hc11 and 68hc12 assembler.
	* config/tc-m68hc11.c: Assembler for 68hc11 and 68hc12.
	* config/tc-m68hc11.h: Header definition for that assembler.
diff -Nrup --exclude-from=binutils-exclude.lst /src/gnu/cygnus/binutils/gas/Makefile.am binutils/gas/Makefile.am
--- /src/gnu/cygnus/binutils/gas/Makefile.am	Thu Jun  1 01:12:58 2000
+++ binutils/gas/Makefile.am	Thu Jun  1 11:35:31 2000
@@ -54,6 +54,7 @@ CPU_TYPES = \
 	i860 \
 	i960 \
 	m32r \
+	m68hc11 \
 	m68k \
 	m88k \
 	mcore \
@@ -225,6 +226,7 @@ TARGET_CPU_CFILES = \
 	config/tc-i860.c \
 	config/tc-i960.c \
 	config/tc-m32r.c \
+	config/tc-m68hc11.c \
 	config/tc-m68k.c \
 	config/tc-m88k.c \
 	config/tc-mcore.c \
@@ -262,6 +264,7 @@ TARGET_CPU_HFILES = \
 	config/tc-i860.h \
 	config/tc-i960.h \
 	config/tc-m32r.h \
+	config/tc-m68hc11.h \
 	config/tc-m68k.h \
 	config/tc-m88k.h \
 	config/tc-mcore.h \
@@ -1087,6 +1090,11 @@ DEPTC_m32r_elf = $(INCDIR)/bin-bugs.h $(
   $(INCDIR)/symcat.h $(srcdir)/../opcodes/m32r-desc.h \
   $(INCDIR)/opcode/cgen.h $(srcdir)/../opcodes/m32r-opc.h \
   cgen.h
+DEPTC_m68hc11_elf = $(INCDIR)/bin-bugs.h $(INCDIR)/progress.h \
+  $(srcdir)/config/obj-elf.h $(BFDDIR)/elf-bfd.h \
+  $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
+  $(INCDIR)/bfdlink.h $(srcdir)/config/tc-m68hc11.h $(INCDIR)/obstack.h \
+  subsegs.h $(INCDIR)/opcode/m68hc11.h
 DEPTC_m68k_aout = $(INCDIR)/bin-bugs.h $(INCDIR)/progress.h \
   $(srcdir)/config/obj-aout.h $(srcdir)/config/tc-m68k.h \
   $(BFDDIR)/libaout.h $(INCDIR)/bfdlink.h emul.h $(INCDIR)/obstack.h \
@@ -1814,6 +1822,9 @@ DEP_m32r_coff = $(srcdir)/config/obj-cof
 DEP_m32r_elf = $(srcdir)/config/obj-elf.h $(BFDDIR)/elf-bfd.h \
   $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
   $(INCDIR)/bfdlink.h $(srcdir)/config/tc-m32r.h
+DEP_m68hc11_elf = $(srcdir)/config/obj-elf.h $(BFDDIR)/elf-bfd.h \
+  $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
+  $(INCDIR)/bfdlink.h $(srcdir)/config/tc-m68hc11.h
 DEP_m68k_aout = $(srcdir)/config/obj-aout.h $(srcdir)/config/tc-m68k.h \
   $(BFDDIR)/libaout.h $(INCDIR)/bfdlink.h
 DEP_m68k_coff = $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-m68k.h \
diff -Nrup --exclude-from=binutils-exclude.lst /src/gnu/cygnus/binutils/gas/config/tc-m68hc11.c binutils/gas/config/tc-m68hc11.c
--- /src/gnu/cygnus/binutils/gas/config/tc-m68hc11.c	Thu Jan  1 01:00:00 1970
+++ binutils/gas/config/tc-m68hc11.c	Fri Jun 16 10:53:49 2000
@@ -0,0 +1,2831 @@
+/* tc-m68hc11.c -- Assembler code for the Motorola 68HC11 & 68HC12.
+   Copyright (C) 1999, 2000 Free Software Foundation.
+   Written by Stephane Carrez (stcarrez@worldnet.fr)
+
+   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.  */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "as.h"
+#include "subsegs.h"
+#include "opcode/m68hc11.h"
+#include "dwarf2dbg.h"
+
+struct dwarf2_line_info debug_line;
+
+const char comment_chars[] = ";!";
+const char line_comment_chars[] = "#*";
+const char line_separator_chars[] = "";
+
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+#define STATE_CONDITIONAL_BRANCH	(1)
+#define STATE_PC_RELATIVE		(2)
+#define STATE_INDEXED_OFFSET            (3)
+#define STATE_XBCC_BRANCH               (4)
+#define STATE_CONDITIONAL_BRANCH_6812	(5)
+
+#define STATE_BYTE			(0)
+#define STATE_BITS5                     (0)
+#define STATE_WORD			(1)
+#define STATE_BITS9                     (1)
+#define STATE_LONG			(2)
+#define STATE_BITS16                    (2)
+#define STATE_UNDF			(3)	/* Symbol undefined in pass1 */
+
+/* This macro has no side-effects.  */
+#define ENCODE_RELAX(what,length) (((what) << 2) + (length))
+
+#define IS_OPCODE(C1,C2)        (((C1) & 0x0FF) == ((C2) & 0x0FF))
+
+/* This table describes how you change sizes for the various types of variable
+   size expressions.  This version only supports two kinds.  */
+
+/* The fields are:
+   How far Forward this mode will reach:
+   How far Backward this mode will reach:
+   How many bytes this mode will add to the size of the frag
+   Which mode to go to if the offset won't fit in this one    */
+
+relax_typeS md_relax_table[] = {
+  {1, 1, 0, 0},			/* First entries aren't used */
+  {1, 1, 0, 0},			/* For no good reason except */
+  {1, 1, 0, 0},			/* that the VAX doesn't either */
+  {1, 1, 0, 0},
+
+  /* Relax for bcc <L>.
+     These insns are translated into b!cc +3 jmp L.  */
+  {(127), (-128), 0, ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD)},
+  {0, 0, 3, 0},
+  {1, 1, 0, 0},
+  {1, 1, 0, 0},
+
+  /* Relax for bsr <L> and bra <L>.
+     These insns are translated into jsr and jmp.  */
+  {(127), (-128), 0, ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD)},
+  {0, 0, 1, 0},
+  {1, 1, 0, 0},
+  {1, 1, 0, 0},
+
+  /* Relax for indexed offset: 5-bits, 9-bits, 16-bits.  */
+  {(15), (-16), 0, ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9)},
+  {(255), (-256), 1, ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16)},
+  {0, 0, 1, 0},
+  {1, 1, 0, 0},
+
+  /* Relax for dbeq/ibeq/tbeq r,<L>:
+     These insns are translated into db!cc +3 jmp L.  */
+  {(255), (-256), 0, ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_WORD)},
+  {0, 0, 3, 0},
+  {1, 1, 0, 0},
+  {1, 1, 0, 0},
+
+  /* Relax for bcc <L> on 68HC12.
+     These insns are translated into lbcc <L>.  */
+  {(127), (-128), 0, ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_WORD)},
+  {0, 0, 2, 0},
+  {1, 1, 0, 0},
+  {1, 1, 0, 0},
+
+};
+
+/* 68HC11 and 68HC12 registers.  They are numbered according to the 68HC12.  */
+typedef enum register_id
+{
+  REG_NONE = -1,
+  REG_A = 0,
+  REG_B = 1,
+  REG_CCR = 2,
+  REG_D = 4,
+  REG_X = 5,
+  REG_Y = 6,
+  REG_SP = 7,
+  REG_PC = 8
+} register_id;
+
+typedef struct operand
+{
+  expressionS exp;
+  register_id reg1;
+  register_id reg2;
+  int mode;
+} operand;
+
+struct m68hc11_opcode_def
+{
+  long format;
+  int min_operands;
+  int max_operands;
+  int nb_modes;
+  int used;
+  struct m68hc11_opcode *opcode;
+};
+
+static struct m68hc11_opcode_def *m68hc11_opcode_defs = 0;
+static int m68hc11_nb_opcode_defs = 0;
+
+typedef struct alias
+{
+  const char *name;
+  const char *alias;
+}
+alias;
+
+static alias alias_opcodes[] = {
+  {"cpd", "cmpd"},
+  {"cpx", "cmpx"},
+  {"cpy", "cmpy"},
+  {0, 0}
+};
+
+/* local functions */
+static register_id reg_name_search PARAMS ((char *name));
+static register_id register_name PARAMS (());
+static int check_range PARAMS ((long num, int mode));
+
+static void print_opcode_list PARAMS ((void));
+
+static void get_default_target PARAMS ((void));
+static void print_insn_format PARAMS ((char *name));
+static int get_operand PARAMS ((operand * op, int first, long opmode));
+static void fixup8 PARAMS ((expressionS * oper, int mode, int opmode));
+static void fixup16 PARAMS ((expressionS * oper, int mode, int opmode));
+static struct m68hc11_opcode *find_opcode
+PARAMS (
+	(struct m68hc11_opcode_def * opc, operand operands[],
+	 int *nb_operands));
+static void build_jump_insn
+PARAMS (
+	(struct m68hc11_opcode * opcode, operand operands[], int nb_operands,
+	 int optimize));
+
+static void build_insn PARAMS ((struct m68hc11_opcode * opcode,
+			       operand operands[], int nb_operands));
+
+/* Controls whether relative branches can be turned into long branches.
+   When the relative offset is too large, the insn are changed:
+    bra -> jmp
+    bsr -> jsr
+    bcc -> b!cc +3
+           jmp L
+    dbcc -> db!cc +3
+            jmp L
+ 
+  Setting the flag forbidds this.  */
+static short flag_fixed_branchs = 0;
+
+/* Force to use long jumps (absolute) instead of relative branches.  */
+static short flag_force_long_jumps = 0;
+
+/* Change the direct addressing mode into an absolute addressing mode
+   when the insn does not support direct addressing.
+   For example, "clr *ZD0" is normally not possible and is changed
+   into "clr ZDO".  */
+static short flag_strict_direct_addressing = 1;
+
+/* When an opcode has invalid operand, print out the syntax of the opcode
+   to stderr.  */
+static short flag_print_insn_syntax = 0;
+
+/* Dumps the list of instructions with syntax and then exit:
+   1 -> Only dumps the list (sorted by name)
+   2 -> Generate an example (or test) that can be compiled.  */
+static short flag_print_opcodes = 0;
+
+/* Opcode hash table.  */
+static struct hash_control *m68hc11_hash;
+
+/* Current cpu (either cpu6811 or cpu6812).  This is determined automagically
+   by 'get_default_target' by looking at default BFD vector.  This is overriden
+   with the -m<cpu> option.  */
+static int current_architecture = 0;
+
+/* Default cpu determined by 'get_default_target'.  */
+static const char *default_cpu;
+
+/* Number of opcodes in the sorted table (filtered by current cpu).  */
+static int num_opcodes;
+
+/* The opcodes sorted by name and filtered by current cpu.  */
+static struct m68hc11_opcode *m68hc11_sorted_opcodes;
+
+/* These are the machine dependent pseudo-ops.  These are included so
+   the assembler can work on the output from the SUN C compiler, which
+   generates these.  */
+
+/* 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[] = {
+  /* The following pseudo-ops are supported for MRI compatibility.  */
+  {"fcb", cons, 1},
+  {"fdb", cons, 2},
+  {"fcc", stringer, 1},
+  {"rmb", s_space, 0},
+  {"file", dwarf2_directive_file, 0},
+  {"loc", dwarf2_directive_loc, 0},
+
+  {0, 0, 0}
+};
+
+
+/* Options and initialization.  */
+
+CONST char *md_shortopts = "Sm:";
+
+struct option md_longopts[] = {
+#define OPTION_FORCE_LONG_BRANCH (OPTION_MD_BASE)
+  {"force-long-branchs", no_argument, NULL, OPTION_FORCE_LONG_BRANCH},
+
+#define OPTION_SHORT_BRANCHS     (OPTION_MD_BASE + 1)
+  {"short-branchs", no_argument, NULL, OPTION_SHORT_BRANCHS},
+
+#define OPTION_STRICT_DIRECT_MODE  (OPTION_MD_BASE + 2)
+  {"strict-direct-mode", no_argument, NULL, OPTION_STRICT_DIRECT_MODE},
+
+#define OPTION_PRINT_INSN_SYNTAX  (OPTION_MD_BASE + 3)
+  {"print-insn-syntax", no_argument, NULL, OPTION_PRINT_INSN_SYNTAX},
+
+#define OPTION_PRINT_OPCODES  (OPTION_MD_BASE + 4)
+  {"print-opcodes", no_argument, NULL, OPTION_PRINT_OPCODES},
+
+#define OPTION_GENERATE_EXAMPLE  (OPTION_MD_BASE + 5)
+  {"generate-example", no_argument, NULL, OPTION_GENERATE_EXAMPLE},
+
+  {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Get the target cpu for the assembler.  This is based on the configure
+   options and on the -m68hc11/-m68hc12 option.  If no option is specified,
+   we must get the default.  */
+const char *
+m68hc11_arch_format ()
+{
+  get_default_target ();
+  if (current_architecture & cpu6811)
+    return "elf32-m68hc11";
+  else
+    return "elf32-m68hc12";
+}
+
+enum bfd_architecture
+m68hc11_arch ()
+{
+  get_default_target ();
+  if (current_architecture & cpu6811)
+    return bfd_arch_m68hc11;
+  else
+    return bfd_arch_m68hc12;
+}
+
+int
+m68hc11_mach ()
+{
+  return 0;
+}
+
+
+void
+md_show_usage (stream)
+     FILE *stream;
+{
+  get_default_target ();
+  fprintf (stream, _("\
+Motorola 68HC11/68HC12 options:\n\
+  -m68hc11 | -m68hc12     specify the processor [default %s]\n\
+  --force-long-branchs    always turn relative branchs into absolute ones\n\
+  -S,--short-branchs      do not turn relative branchs into absolute ones\n\
+                          when the offset is out of range\n\
+  --strict-direct-mode    do not turn the direct mode into extended mode\n\
+                          when the instruction does not support direct mode\n\
+  --print-insn-syntax     print the syntax of instruction in case of error\n\
+  --print-opcodes         print the list of instructions with syntax\n\
+  --generate-example      generate an example of each instruction\n\
+                          (used for testing)\n"), default_cpu);
+
+}
+
+/* Try to identify the default target based on the BFD library.  */
+static void
+get_default_target ()
+{
+  const bfd_target *target;
+  bfd abfd;
+
+  if (current_architecture != 0)
+    return;
+
+  default_cpu = "unknown";
+  target = bfd_find_target (0, &abfd);
+  if (target && target->name)
+    {
+      if (strcmp (target->name, "elf32-m68hc12") == 0)
+	{
+	  current_architecture = cpu6812;
+	  default_cpu = "m68hc12";
+	}
+      else if (strcmp (target->name, "elf32-m68hc11") == 0)
+	{
+	  current_architecture = cpu6811;
+	  default_cpu = "m68hc11";
+	}
+      else
+	{
+	  as_bad (_("Default target `%s' is not supported."), target->name);
+	}
+    }
+}
+
+void
+m68hc11_print_statistics (file)
+     FILE *file;
+{
+  int i;
+  struct m68hc11_opcode_def *opc;
+
+  hash_print_statistics (file, "opcode table", m68hc11_hash);
+
+  opc = m68hc11_opcode_defs;
+  if (opc == 0 || m68hc11_nb_opcode_defs == 0)
+    return;
+
+  /* Dump the opcode statistics table.  */
+  fprintf (file, _("Name   # Modes  Min ops  Max ops  Modes mask  # Used\n"));
+  for (i = 0; i < m68hc11_nb_opcode_defs; i++, opc++)
+    {
+      fprintf (file, "%-7.7s  %5d  %7d  %7d  0x%08lx  %7d\n",
+	       opc->opcode->name,
+	       opc->nb_modes,
+	       opc->min_operands, opc->max_operands, opc->format, opc->used);
+    }
+}
+
+int
+md_parse_option (c, arg)
+     int c;
+     char *arg;
+{
+  get_default_target ();
+  switch (c)
+    {
+      /* -S means keep external to 2 bits offset rather than 16 bits one.  */
+    case OPTION_SHORT_BRANCHS:
+    case 'S':
+      flag_fixed_branchs = 1;
+      break;
+
+    case OPTION_FORCE_LONG_BRANCH:
+      flag_force_long_jumps = 1;
+      break;
+
+    case OPTION_PRINT_INSN_SYNTAX:
+      flag_print_insn_syntax = 1;
+      break;
+
+    case OPTION_PRINT_OPCODES:
+      flag_print_opcodes = 1;
+      break;
+
+    case OPTION_STRICT_DIRECT_MODE:
+      flag_strict_direct_addressing = 0;
+      break;
+
+    case OPTION_GENERATE_EXAMPLE:
+      flag_print_opcodes = 2;
+      break;
+
+    case 'm':
+      if (strcasecmp (arg, "68hc11") == 0)
+	current_architecture = cpu6811;
+      else if (strcasecmp (arg, "68hc12") == 0)
+	current_architecture = cpu6812;
+      else
+	as_bad (_("Option `%s' is not recognized."), arg);
+      break;
+
+    default:
+      return 0;
+    }
+
+  return 1;
+}
+
+symbolS *
+md_undefined_symbol (name)
+     char *name ATTRIBUTE_UNUSED;
+{
+  return 0;
+}
+
+/* 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;
+}
+
+valueT
+md_section_align (seg, addr)
+     asection *seg;
+     valueT addr;
+{
+  int align = bfd_get_section_alignment (stdoutput, seg);
+  return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+
+static int
+cmp_opcode (op1, op2)
+     struct m68hc11_opcode *op1;
+     struct m68hc11_opcode *op2;
+{
+  return strcmp (op1->name, op2->name);
+}
+
+/* Initialize the assembler.  Create the opcode hash table
+   (sorted on the names) with the M6811 opcode table
+   (from opcode library).  */
+void
+md_begin ()
+{
+  char *prev_name = "";
+  struct m68hc11_opcode *opcodes;
+  struct m68hc11_opcode_def *opc = 0;
+  int i, j;
+
+  get_default_target ();
+
+  m68hc11_hash = hash_new ();
+
+  /* Get a writable copy of the opcode table and sort it on the names.  */
+  opcodes = (struct m68hc11_opcode *) xmalloc (m68hc11_num_opcodes *
+					       sizeof (struct
+						       m68hc11_opcode));
+  m68hc11_sorted_opcodes = opcodes;
+  num_opcodes = 0;
+  for (i = 0; i < m68hc11_num_opcodes; i++)
+    {
+      if (m68hc11_opcodes[i].arch & current_architecture)
+	{
+	  opcodes[num_opcodes] = m68hc11_opcodes[i];
+	  if (opcodes[num_opcodes].name[0] == 'b'
+	      && opcodes[num_opcodes].format & M6811_OP_JUMP_REL
+	      && !(opcodes[num_opcodes].format & M6811_OP_BITMASK))
+	    {
+	      num_opcodes++;
+	      opcodes[num_opcodes] = m68hc11_opcodes[i];
+	    }
+	  num_opcodes++;
+	  for (j = 0; alias_opcodes[j].name != 0; j++)
+	    if (strcmp (m68hc11_opcodes[i].name, alias_opcodes[j].name) == 0)
+	      {
+		opcodes[num_opcodes] = m68hc11_opcodes[i];
+		opcodes[num_opcodes].name = alias_opcodes[j].alias;
+		num_opcodes++;
+		break;
+	      }
+	}
+    }
+  qsort (opcodes, num_opcodes, sizeof (struct m68hc11_opcode), cmp_opcode);
+
+  opc = (struct m68hc11_opcode_def *)
+    xmalloc (num_opcodes * sizeof (struct m68hc11_opcode_def));
+  m68hc11_opcode_defs = opc--;
+
+  /* Insert unique names into hash table.  The M6811 instruction set
+     has several identical opcode names that have different opcodes based
+     on the operands.  This hash table then provides a quick index to
+     the first opcode with a particular name in the opcode table.  */
+  for (i = 0; i < num_opcodes; i++, opcodes++)
+    {
+      int expect;
+
+      if (strcmp (prev_name, opcodes->name))
+	{
+	  prev_name = (char *) opcodes->name;
+
+	  opc++;
+	  opc->format = 0;
+	  opc->min_operands = 100;
+	  opc->max_operands = 0;
+	  opc->nb_modes = 0;
+	  opc->opcode = opcodes;
+	  opc->used = 0;
+	  hash_insert (m68hc11_hash, opcodes->name, (char *) opc);
+	}
+      opc->nb_modes++;
+      opc->format |= opcodes->format;
+
+      /* See how many operands this opcode needs.  */
+      expect = 0;
+      if (opcodes->format & M6811_OP_MASK)
+	expect++;
+      if (opcodes->format & M6811_OP_BITMASK)
+	expect++;
+      if (opcodes->format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
+	expect++;
+      if (opcodes->format & (M6812_OP_IND16_P2 | M6812_OP_IDX_P2))
+	expect++;
+
+      if (expect < opc->min_operands)
+	opc->min_operands = expect;
+      if (expect > opc->max_operands)
+	opc->max_operands = expect;
+    }
+  opc++;
+  m68hc11_nb_opcode_defs = opc - m68hc11_opcode_defs;
+
+  if (flag_print_opcodes)
+    {
+      print_opcode_list ();
+      exit (EXIT_SUCCESS);
+    }
+}
+
+void
+m68hc11_init_after_args ()
+{
+}
+
+
+/* Builtin help.  */
+
+/* Return a string that represents the operand format for the instruction.
+   When example is true, this generates an example of operand.  This is used
+   to give an example and also to generate a test.  */
+static char *
+print_opcode_format (opcode, example)
+     struct m68hc11_opcode *opcode;
+     int example;
+{
+  static char buf[128];
+  int format = opcode->format;
+  char *p;
+
+  p = buf;
+  buf[0] = 0;
+  if (format & M6811_OP_IMM8)
+    {
+      if (example)
+	sprintf (p, "#%d", rand () & 0x0FF);
+      else
+	strcpy (p, _("#<imm8>"));
+      p = &p[strlen (p)];
+    }
+
+  if (format & M6811_OP_IMM16)
+    {
+      if (example)
+	sprintf (p, "#%d", rand () & 0x0FFFF);
+      else
+	strcpy (p, _("#<imm16>"));
+      p = &p[strlen (p)];
+    }
+
+  if (format & M6811_OP_IX)
+    {
+      if (example)
+	sprintf (p, "%d,X", rand () & 0x0FF);
+      else
+	strcpy (p, _("<imm8>,X"));
+      p = &p[strlen (p)];
+    }
+
+  if (format & M6811_OP_IY)
+    {
+      if (example)
+	sprintf (p, "%d,X", rand () & 0x0FF);
+      else
+	strcpy (p, _("<imm8>,X"));
+      p = &p[strlen (p)];
+    }
+
+  if (format & M6812_OP_IDX)
+    {
+      if (example)
+	sprintf (p, "%d,X", rand () & 0x0FF);
+      else
+	strcpy (p, "n,r");
+      p = &p[strlen (p)];
+    }
+
+  if (format & M6811_OP_DIRECT)
+    {
+      if (example)
+	sprintf (p, "*Z%d", rand () & 0x0FF);
+      else
+	strcpy (p, _("*<abs8>"));
+      p = &p[strlen (p)];
+    }
+
+  if (format & M6811_OP_BITMASK)
+    {
+      if (buf[0])
+	*p++ = ' ';
+
+      if (example)
+	sprintf (p, "#$%02x", rand () & 0x0FF);
+      else
+	strcpy (p, _("#<mask>"));
+
+      p = &p[strlen (p)];
+      if (format & M6811_OP_JUMP_REL)
+	*p++ = ' ';
+    }
+
+  if (format & M6811_OP_IND16)
+    {
+      if (example)
+	sprintf (p, _("symbol%d"), rand () & 0x0FF);
+      else
+	strcpy (p, _("<abs>"));
+
+      p = &p[strlen (p)];
+    }
+
+  if (format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
+    {
+      if (example)
+	{
+	  if (format & M6811_OP_BITMASK)
+	    {
+	      sprintf (p, ".+%d", rand () & 0x7F);
+	    }
+	  else
+	    {
+	      sprintf (p, "L%d", rand () & 0x0FF);
+	    }
+	}
+      else
+	strcpy (p, _("<label>"));
+    }
+
+  return buf;
+}
+
+/* Prints the list of instructions with the possible operands.  */
+static void
+print_opcode_list ()
+{
+  int i;
+  char *prev_name = "";
+  struct m68hc11_opcode *opcodes;
+  int example = flag_print_opcodes == 2;
+
+  if (example)
+    {
+      printf (_("# Example of `%s' instructions\n\t.sect .text\n_start:\n"),
+	      default_cpu);
+    }
+
+  opcodes = m68hc11_sorted_opcodes;
+
+  /* Walk the list sorted on names (by md_begin).  We only report
+     one instruction per line, and we collect the different operand
+     formats.  */
+  for (i = 0; i < num_opcodes; i++, opcodes++)
+    {
+      char *fmt = print_opcode_format (opcodes, example);
+
+      if (example)
+	{
+	  printf ("L%d:\t", i);
+	  printf ("%s %s\n", opcodes->name, fmt);
+	}
+      else
+	{
+	  if (strcmp (prev_name, opcodes->name))
+	    {
+	      if (i > 0)
+		printf ("\n");
+
+	      printf ("%-5.5s ", opcodes->name);
+	      prev_name = (char *) opcodes->name;
+	    }
+	  if (fmt[0])
+	    printf ("  [%s]", fmt);
+	}
+    }
+  printf ("\n");
+}
+
+
+/* Print the instruction format.  This operation is called when some
+   instruction is not correct.  Instruction format is printed as an
+   error message.  */
+static void
+print_insn_format (name)
+     char *name;
+{
+  struct m68hc11_opcode_def *opc;
+  struct m68hc11_opcode *opcode;
+  char buf[128];
+
+  opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash, name);
+  if (opc == NULL)
+    {
+      as_bad (_("Instruction `%s' is not recognized."), name);
+      return;
+    }
+  opcode = opc->opcode;
+
+  as_bad (_("Instruction formats for `%s':"), name);
+  do
+    {
+      char *fmt;
+
+      fmt = print_opcode_format (opcode, 0, 0);
+      sprintf (buf, "\t%-5.5s %s", opcode->name, fmt);
+
+      as_bad ("%s", buf);
+      opcode++;
+    }
+  while (strcmp (opcode->name, name) == 0);
+}
+
+
+/* Analysis of 68HC11 and 68HC12 operands.  */
+
+/* reg_name_search() finds the register number given its name.
+   Returns the register number or REG_NONE on failure.  */
+static register_id
+reg_name_search (name)
+     char *name;
+{
+  if (strcasecmp (name, "x") == 0 || strcasecmp (name, "ix") == 0)
+    return REG_X;
+  if (strcasecmp (name, "y") == 0 || strcasecmp (name, "iy") == 0)
+    return REG_Y;
+  if (strcasecmp (name, "a") == 0)
+    return REG_A;
+  if (strcasecmp (name, "b") == 0)
+    return REG_B;
+  if (strcasecmp (name, "d") == 0)
+    return REG_D;
+  if (strcasecmp (name, "sp") == 0)
+    return REG_SP;
+  if (strcasecmp (name, "pc") == 0)
+    return REG_PC;
+  if (strcasecmp (name, "ccr") == 0)
+    return REG_CCR;
+
+  return REG_NONE;
+}
+
+static char *
+skip_whites (p)
+     char *p;
+{
+  while (*p == ' ' || *p == '\t')
+    p++;
+
+  return p;
+}
+
+/* register_name() checks the string at input_line_pointer
+   to see if it is a valid register name.  */
+static register_id
+register_name ()
+{
+  register_id reg_number;
+  char c, *p = input_line_pointer;
+
+  if (!is_name_beginner (*p++))
+    return REG_NONE;
+
+  while (is_part_of_name (*p++))
+    continue;
+
+  c = *--p;
+  if (c)
+    *p++ = 0;
+
+  /* look to see if it's in the register table.  */
+  reg_number = reg_name_search (input_line_pointer);
+  if (reg_number != REG_NONE)
+    {
+      if (c)
+	*--p = c;
+
+      input_line_pointer = p;
+      return reg_number;
+    }
+  if (c)
+    *--p = c;
+
+  return reg_number;
+}
+
+/* get_operands parses a string of operands and returns
+   an array of expressions.
+
+   Operand              mode[0]         mode[1]       exp[0]       exp[1]
+   #n                   M6811_OP_IMM16  -             O_*
+   *<exp>               M6811_OP_DIRECT -             O_*
+   .{+-}<exp>           M6811_OP_JUMP_REL -           O_*
+   <exp>                M6811_OP_IND16  -             O_*
+   ,r N,r               M6812_OP_IDX    M6812_OP_REG  O_constant   O_register
+   n,-r                 M6812_PRE_DEC   M6812_OP_REG  O_constant   O_register
+   n,+r                 M6812_PRE_INC   " "
+   n,r-                 M6812_POST_DEC  " "
+   n,r+                 M6812_POST_INC  " "
+   A,r B,r D,r          M6811_OP_REG    M6812_OP_REG  O_register   O_register
+   [D,r]                M6811_OP_IDX_2  M6812_OP_REG  O_register   O_register
+   [n,r]                M6811_OP_IDX_1  M6812_OP_REG  O_constant   O_register
+
+*/
+
+static int
+get_operand (oper, which, opmode)
+     operand *oper;
+     int which;
+     long opmode;
+{
+  char *p = input_line_pointer;
+  int mode;
+  register_id reg;
+
+  oper->exp.X_op = O_absent;
+  oper->reg1 = REG_NONE;
+  oper->reg2 = REG_NONE;
+  mode = M6811_OP_NONE;
+
+  p = skip_whites (p);
+
+  if (*p == 0 || *p == '\n' || *p == '\r')
+    {
+      input_line_pointer = p;
+      return 0;
+    }
+
+  if (*p == '*' && (opmode & (M6811_OP_DIRECT | M6811_OP_IND16)))
+    {
+      mode = M6811_OP_DIRECT;
+      p++;
+    }
+  else if (*p == '#')
+    {
+      if (!(opmode & (M6811_OP_IMM8 | M6811_OP_IMM16 | M6811_OP_BITMASK)))
+	{
+	  as_bad (_("Immediate operand is not allowed for operand %d."),
+                  which);
+	  return -1;
+	}
+
+      mode = M6811_OP_IMM16;
+      p++;
+      if (strncmp (p, "%hi", 3) == 0)
+	{
+	  p += 3;
+	  mode |= M6811_OP_HIGH_ADDR;
+	}
+      else if (strncmp (p, "%lo", 3) == 0)
+	{
+	  p += 3;
+	  mode |= M6811_OP_LOW_ADDR;
+	}
+    }
+  else if (*p == '.' && (p[1] == '+' || p[1] == '-'))
+    {
+      p++;
+      mode = M6811_OP_JUMP_REL;
+    }
+  else if (*p == '[')
+    {
+      if (current_architecture & cpu6811)
+	as_bad (_("Indirect indexed addressing is not valid for 68HC11."));
+
+      p++;
+      mode = M6812_OP_IDX_2;
+      p = skip_whites (p);
+    }
+  else if (*p == ',')		/* Special handling of ,x and ,y.  */
+    {
+      p++;
+      input_line_pointer = p;
+
+      reg = register_name ();
+      if (reg != REG_NONE)
+	{
+	  oper->reg1 = reg;
+	  oper->exp.X_op = O_constant;
+	  oper->exp.X_add_number = 0;
+	  oper->mode = M6812_OP_IDX;
+	  return 1;
+	}
+      as_bad (_("Spurious `,' or bad indirect register addressing mode."));
+      return -1;
+    }
+  input_line_pointer = p;
+
+  if (mode == M6811_OP_NONE || mode == M6812_OP_IDX_2)
+    reg = register_name ();
+  else
+    reg = REG_NONE;
+
+  if (reg != REG_NONE)
+    {
+      p = skip_whites (input_line_pointer);
+      if (*p == ']' && mode == M6812_OP_IDX_2)
+	{
+	  as_bad
+	    (_("Missing second register or offset for indexed-indirect mode."));
+	  return -1;
+	}
+
+      oper->reg1 = reg;
+      oper->mode = mode | M6812_OP_REG;
+      if (*p != ',')
+	{
+	  if (mode == M6812_OP_IDX_2)
+	    {
+	      as_bad (_("Missing second register for indexed-indirect mode."));
+	      return -1;
+	    }
+	  return 1;
+	}
+
+      p++;
+      input_line_pointer = p;
+      reg = register_name ();
+      if (reg != REG_NONE)
+	{
+	  p = skip_whites (input_line_pointer);
+	  if (mode == M6812_OP_IDX_2)
+	    {
+	      if (*p != ']')
+		{
+		  as_bad (_("Missing `]' to close indexed-indirect mode."));
+		  return -1;
+		}
+	      p++;
+	    }
+	  input_line_pointer = p;
+
+	  oper->reg2 = reg;
+	  return 1;
+	}
+      return 1;
+    }
+
+  /* In MRI mode, isolate the operand because we can't distinguish
+     operands from comments.  */
+  if (flag_mri)
+    {
+      char c = 0;
+
+      p = skip_whites (p);
+      while (*p && *p != ' ' && *p != '\t')
+	p++;
+
+      if (*p)
+	{
+	  c = *p;
+	  *p = 0;
+	}
+
+      /* Parse as an expression.  */
+      expression (&oper->exp);
+
+      if (c)
+	{
+	  *p = c;
+	}
+    }
+  else
+    {
+      expression (&oper->exp);
+    }
+
+  if (oper->exp.X_op == O_illegal)
+    {
+      as_bad (_("Illegal operand."));
+      return -1;
+    }
+  else if (oper->exp.X_op == O_absent)
+    {
+      as_bad (_("Missing operand."));
+      return -1;
+    }
+
+  p = input_line_pointer;
+
+  if (mode == M6811_OP_NONE || mode == M6811_OP_DIRECT
+      || mode == M6812_OP_IDX_2)
+    {
+      p = skip_whites (input_line_pointer);
+
+      if (*p == ',')
+	{
+	  p++;
+
+	  /* 68HC12 pre increment or decrement.  */
+	  if (mode == M6811_OP_NONE)
+	    {
+	      if (*p == '-')
+		{
+		  mode = M6812_PRE_DEC;
+		  p++;
+		  if (current_architecture & cpu6811)
+		    as_bad (_("Pre-decrement mode is not valid for 68HC11"));
+		}
+	      else if (*p == '+')
+		{
+		  mode = M6812_PRE_INC;
+		  p++;
+		  if (current_architecture & cpu6811)
+		    as_bad (_("Pre-increment mode is not valid for 68HC11"));
+		}
+	      p = skip_whites (p);
+	    }
+	  input_line_pointer = p;
+	  reg = register_name ();
+
+	  /* Backtrack... */
+	  if (which == 0 && opmode & M6812_OP_IDX_P2
+	      && reg != REG_X && reg != REG_Y
+	      && reg != REG_PC && reg != REG_SP)
+	    {
+	      reg = REG_NONE;
+	      input_line_pointer = p;
+	    }
+
+	  if (reg == REG_NONE && mode != M6811_OP_DIRECT
+	      && !(mode == M6811_OP_NONE && opmode & M6811_OP_IND16))
+	    {
+	      as_bad (_("Wrong register in register indirect mode."));
+	      return -1;
+	    }
+	  if (mode == M6812_OP_IDX_2)
+	    {
+	      p = skip_whites (input_line_pointer);
+	      if (*p++ != ']')
+		{
+		  as_bad (_("Missing `]' to close register indirect operand."));
+		  return -1;
+		}
+	      input_line_pointer = p;
+	    }
+	  if (reg != REG_NONE)
+	    {
+	      oper->reg1 = reg;
+	      if (mode == M6811_OP_NONE)
+		{
+		  p = input_line_pointer;
+		  if (*p == '-')
+		    {
+		      mode = M6812_POST_DEC;
+		      p++;
+		      if (current_architecture & cpu6811)
+			as_bad
+			  (_("Post-decrement mode is not valid for 68HC11."));
+		    }
+		  else if (*p == '+')
+		    {
+		      mode = M6812_POST_INC;
+		      p++;
+		      if (current_architecture & cpu6811)
+			as_bad
+			  (_("Post-increment mode is not valid for 68HC11."));
+		    }
+		  else
+		    mode = M6812_OP_IDX;
+
+		  input_line_pointer = p;
+		}
+	      else
+		mode |= M6812_OP_IDX;
+
+	      oper->mode = mode;
+	      return 1;
+	    }
+	}
+
+      if (mode == M6812_OP_D_IDX_2)
+	{
+	  as_bad (_("Invalid indexed indirect mode."));
+	  return -1;
+	}
+    }
+
+  /* If the mode is not known until now, this is either a label
+     or an indirect address.  */
+  if (mode == M6811_OP_NONE)
+    {
+      mode = M6811_OP_IND16 | M6811_OP_JUMP_REL;
+    }
+
+  p = input_line_pointer;
+  while (*p == ' ' || *p == '\t')
+    p++;
+  input_line_pointer = p;
+  oper->mode = mode;
+
+  return 1;
+}
+
+#define M6812_AUTO_INC_DEC (M6812_PRE_INC | M6812_PRE_DEC \
+                            | M6812_POST_INC | M6812_POST_DEC)
+
+/* Checks that the number 'num' fits for a given mode.  */
+static int
+check_range (num, mode)
+     long num;
+     int mode;
+{
+  /* Auto increment and decrement are ok for [-8..8] without 0.  */
+  if (mode & M6812_AUTO_INC_DEC)
+    {
+      return (num != 0 && num <= 8 && num >= -8);
+    }
+
+  /* The 68HC12 supports 5, 9 and 16-bits offsets.  */
+  if (mode & (M6812_INDEXED_IND | M6812_INDEXED | M6812_OP_IDX))
+    {
+      mode = M6811_OP_IND16;
+    }
+
+  if (mode & M6812_OP_JUMP_REL16)
+    mode = M6811_OP_IND16;
+
+  switch (mode)
+    {
+    case M6811_OP_IX:
+    case M6811_OP_IY:
+    case M6811_OP_DIRECT:
+      return (num >= 0 && num <= 255) ? 1 : 0;
+
+    case M6811_OP_BITMASK:
+    case M6811_OP_IMM8:
+      return (((num & 0xFFFFFF00) == 0) || ((num & 0xFFFFFF00) == 0xFFFFFF00))
+	? 1 : 0;
+
+    case M6811_OP_JUMP_REL:
+      return (num >= -128 && num <= 127) ? 1 : 0;
+
+    case M6811_OP_IND16:
+    case M6811_OP_IMM16:
+      return (((num & 0xFFFF0000) == 0) || ((num & 0xFFFF0000) == 0xFFFF0000))
+	? 1 : 0;
+
+    case M6812_OP_IBCC_MARKER:
+    case M6812_OP_TBCC_MARKER:
+    case M6812_OP_DBCC_MARKER:
+      return (num >= -256 && num <= 255) ? 1 : 0;
+
+    case M6812_OP_TRAP_ID:
+      return ((num >= 0x30 && num <= 0x39)
+	      || (num >= 0x40 && num <= 0x0ff)) ? 1 : 0;
+
+    default:
+      return 0;
+    }
+}
+
+
+/* Gas fixup generation.  */
+
+/* Put a 1 byte expression described by 'oper'.  If this expression contains
+   unresolved symbols, generate an 8-bit fixup.  */
+static void
+fixup8 (oper, mode, opmode)
+     expressionS *oper;
+     int mode;
+     int opmode;
+{
+  char *f;
+
+  f = frag_more (1);
+
+  if (oper->X_op == O_constant)
+    {
+      if (mode & M6812_OP_TRAP_ID
+	  && !check_range (oper->X_add_number, M6812_OP_TRAP_ID))
+	{
+	  static char trap_id_warn_once = 0;
+
+	  as_bad (_("Trap id `%ld' is out of range."), oper->X_add_number);
+	  if (trap_id_warn_once == 0)
+	    {
+	      trap_id_warn_once = 1;
+	      as_bad (_("Trap id must be within [0x30..0x39] or [0x40..0xff]."));
+	    }
+	}
+
+      if (!(mode & M6812_OP_TRAP_ID)
+	  && !check_range (oper->X_add_number, mode))
+	{
+	  as_bad (_("Operand out of 8-bit range: `%ld'."), oper->X_add_number);
+	}
+      number_to_chars_bigendian (f, oper->X_add_number & 0x0FF, 1);
+    }
+  else if (oper->X_op != O_register)
+    {
+      if (mode & M6812_OP_TRAP_ID)
+	as_bad (_("The trap id must be a constant."));
+
+      if (mode == M6811_OP_JUMP_REL)
+	{
+	  fixS *fixp;
+
+	  fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 1,
+			      oper, true, BFD_RELOC_8_PCREL);
+	  fixp->fx_pcrel_adjust = 1;
+	}
+      else
+	{
+	  /* Now create an 8-bit fixup.  If there was some %hi or %lo
+	     modifier, generate the reloc accordingly.  */
+	  fix_new_exp (frag_now, f - frag_now->fr_literal, 1,
+		       oper, false,
+		       ((opmode & M6811_OP_HIGH_ADDR)
+			? BFD_RELOC_M68HC11_HI8
+			: ((opmode & M6811_OP_LOW_ADDR)
+			   ? BFD_RELOC_M68HC11_LO8 : BFD_RELOC_8)));
+	}
+      number_to_chars_bigendian (f, 0, 1);
+    }
+  else
+    {
+      as_fatal (_("Operand `%x' not recognized in fixup8."), oper->X_op);
+    }
+}
+
+/* Put a 2 bytes expression described by 'oper'.  If this expression contains
+   unresolved symbols, generate a 16-bit fixup.  */
+static void
+fixup16 (oper, mode, opmode)
+     expressionS *oper;
+     int mode;
+     int opmode ATTRIBUTE_UNUSED;
+{
+  char *f;
+
+  f = frag_more (2);
+
+  if (oper->X_op == O_constant)
+    {
+      if (!check_range (oper->X_add_number, mode))
+	{
+	  as_bad (_("Operand out of 16-bit range: `%ld'."),
+                  oper->X_add_number);
+	}
+      number_to_chars_bigendian (f, oper->X_add_number & 0x0FFFF, 2);
+    }
+  else if (oper->X_op != O_register)
+    {
+      fixS *fixp;
+
+      /* Now create a 16-bit fixup.  */
+      fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 2,
+			  oper,
+			  (mode & M6812_OP_JUMP_REL16 ? true : false),
+			  (mode & M6812_OP_JUMP_REL16
+			   ? BFD_RELOC_16_PCREL : BFD_RELOC_16));
+      number_to_chars_bigendian (f, 0, 2);
+      if (mode & M6812_OP_JUMP_REL16)
+	fixp->fx_pcrel_adjust = 2;
+    }
+  else
+    {
+      as_fatal (_("Operand `%x' not recognized in fixup16."), oper->X_op);
+    }
+}
+
+
+/* 68HC11 and 68HC12 code generation.  */
+
+/* Translate the short branch/bsr instruction into a long branch.  */
+static unsigned char
+convert_branch (code)
+     unsigned char code;
+{
+  if (IS_OPCODE (code, M6812_BSR))
+    return M6812_JSR;
+  else if (IS_OPCODE (code, M6811_BSR))
+    return M6811_JSR;
+  else if (IS_OPCODE (code, M6811_BRA))
+    return (current_architecture & cpu6812) ? M6812_JMP : M6811_JMP;
+  else
+    as_fatal (_("Unexpected branch conversion with `%x'"), code);
+
+  /* Keep gcc happy.  */
+  return M6811_JSR;
+}
+
+/* Start a new insn that contains at least 'size' bytes.  Record the
+   line information of that insn in the dwarf2 debug sections.  */
+static char*
+m68hc11_new_insn (size)
+     int size;
+{
+  char* f;
+
+  f = frag_more (size);
+
+  /* Emit line number information in dwarf2 debug sections.  */
+  if (debug_type == DEBUG_DWARF2)
+    {
+      bfd_vma addr;
+          
+      dwarf2_where (&debug_line);
+      addr = frag_now->fr_address + frag_now_fix () - size;
+      dwarf2_gen_line_info (addr, &debug_line);
+    }
+  return f;
+}
+
+/* Builds a jump instruction (bra, bcc, bsr).  */
+static void
+build_jump_insn (opcode, operands, nb_operands, jmp_mode)
+     struct m68hc11_opcode *opcode;
+     operand operands[];
+     int nb_operands;
+     int jmp_mode;
+{
+  unsigned char code;
+  int insn_size;
+  char *f;
+  unsigned long n;
+
+  /* The relative branch convertion is not supported for
+     brclr and brset.  */
+  assert ((opcode->format & M6811_OP_BITMASK) == 0);
+  assert (nb_operands == 1);
+  assert (operands[0].reg1 == REG_NONE && operands[0].reg2 == REG_NONE);
+
+  code = opcode->opcode;
+  insn_size = 1;
+
+  n = operands[0].exp.X_add_number;
+
+  /* Turn into a long branch:
+     - when force long branch option (and not for jbcc pseudos),
+     - when jbcc and the constant is out of -128..127 range,
+     - when branch optimization is allowed and branch out of range.  */
+  if ((jmp_mode == 0 && flag_force_long_jumps)
+      || (operands[0].exp.X_op == O_constant
+	  && (!check_range (n, opcode->format) &&
+	      (jmp_mode == 1 || flag_fixed_branchs == 0))))
+    {
+      if (code == M6811_BSR || code == M6811_BRA || code == M6812_BSR)
+	{
+	  code = convert_branch (code);
+
+	  f = m68hc11_new_insn (1);
+	  number_to_chars_bigendian (f, code, 1);
+	}
+      else if (current_architecture & cpu6812)
+	{
+	  /* 68HC12: translate the bcc into a lbcc.  */
+	  f = m68hc11_new_insn (2);
+	  number_to_chars_bigendian (f, M6811_OPCODE_PAGE2, 1);
+	  number_to_chars_bigendian (f + 1, code, 1);
+	  fixup16 (&operands[0].exp, M6812_OP_JUMP_REL16,
+		   M6812_OP_JUMP_REL16);
+	  return;
+	}
+      else
+	{
+	  /* 68HC11: translate the bcc into b!cc +3; jmp <L>.  */
+	  f = m68hc11_new_insn (3);
+	  code ^= 1;
+	  number_to_chars_bigendian (f, code, 1);
+	  number_to_chars_bigendian (f + 1, 3, 1);
+	  number_to_chars_bigendian (f + 2, M6811_JMP, 1);
+	}
+      fixup16 (&operands[0].exp, M6811_OP_IND16, M6811_OP_IND16);
+      return;
+    }
+
+  /* Branch with a constant that must fit in 8-bits.  */
+  if (operands[0].exp.X_op == O_constant)
+    {
+      if (!check_range (n, opcode->format))
+	{
+	  as_bad (_("Operand out of range for a relative branch: `%ld'"),
+                  n);
+	}
+      else if (opcode->format & M6812_OP_JUMP_REL16)
+	{
+	  f = m68hc11_new_insn (4);
+	  number_to_chars_bigendian (f, M6811_OPCODE_PAGE2, 1);
+	  number_to_chars_bigendian (f + 1, code, 1);
+	  number_to_chars_bigendian (f + 2, n & 0x0ffff, 2);
+	}
+      else
+	{
+	  f = m68hc11_new_insn (2);
+	  number_to_chars_bigendian (f, code, 1);
+	  number_to_chars_bigendian (f + 1, n & 0x0FF, 1);
+	}
+    }
+  else if (opcode->format & M6812_OP_JUMP_REL16)
+    {
+      f = m68hc11_new_insn (2);
+      number_to_chars_bigendian (f, M6811_OPCODE_PAGE2, 1);
+      number_to_chars_bigendian (f + 1, code, 1);
+      fixup16 (&operands[0].exp, M6812_OP_JUMP_REL16, M6812_OP_JUMP_REL16);
+    }
+  else
+    {
+      char *opcode;
+
+      /* Branch offset must fit in 8-bits, don't do some relax.  */
+      if (jmp_mode == 0 && flag_fixed_branchs)
+	{
+	  opcode = m68hc11_new_insn (1);
+	  number_to_chars_bigendian (opcode, code, 1);
+	  fixup8 (&operands[0].exp, M6811_OP_JUMP_REL, M6811_OP_JUMP_REL);
+	}
+
+      /* bra/bsr made be changed into jmp/jsr.  */
+      else if (code == M6811_BSR || code == M6811_BRA || code == M6812_BSR)
+	{
+	  opcode = m68hc11_new_insn (2);
+	  number_to_chars_bigendian (opcode, code, 1);
+	  number_to_chars_bigendian (opcode + 1, 0, 1);
+	  frag_var (rs_machine_dependent, 1, 1,
+		    ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF),
+		    operands[0].exp.X_add_symbol, (offsetT) n, opcode);
+	}
+      else if (current_architecture & cpu6812)
+	{
+	  opcode = m68hc11_new_insn (2);
+	  number_to_chars_bigendian (opcode, code, 1);
+	  number_to_chars_bigendian (opcode + 1, 0, 1);
+	  frag_var (rs_machine_dependent, 2, 2,
+		    ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_UNDF),
+		    operands[0].exp.X_add_symbol, (offsetT) n, opcode);
+	}
+      else
+	{
+	  opcode = m68hc11_new_insn (2);
+	  number_to_chars_bigendian (opcode, code, 1);
+	  number_to_chars_bigendian (opcode + 1, 0, 1);
+	  frag_var (rs_machine_dependent, 3, 3,
+		    ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_UNDF),
+		    operands[0].exp.X_add_symbol, (offsetT) n, opcode);
+	}
+    }
+}
+
+/* Builds a dbne/dbeq/tbne/tbeq instruction.  */
+static void
+build_dbranch_insn (opcode, operands, nb_operands, jmp_mode)
+     struct m68hc11_opcode *opcode;
+     operand operands[];
+     int nb_operands;
+     int jmp_mode;
+{
+  unsigned char code;
+  int insn_size;
+  char *f;
+  unsigned long n;
+
+  /* The relative branch convertion is not supported for
+     brclr and brset.  */
+  assert ((opcode->format & M6811_OP_BITMASK) == 0);
+  assert (nb_operands == 2);
+  assert (operands[0].reg1 != REG_NONE);
+
+  code = opcode->opcode & 0x0FF;
+  insn_size = 1;
+
+  f = m68hc11_new_insn (1);
+  number_to_chars_bigendian (f, code, 1);
+
+  n = operands[1].exp.X_add_number;
+  code = operands[0].reg1;
+
+  if (operands[0].reg1 == REG_NONE || operands[0].reg1 == REG_CCR
+      || operands[0].reg1 == REG_PC)
+    as_bad (_("Invalid register for dbcc/tbcc instruction."));
+
+  if (opcode->format & M6812_OP_IBCC_MARKER)
+    code |= 0x80;
+  else if (opcode->format & M6812_OP_TBCC_MARKER)
+    code |= 0x40;
+
+  if (!(opcode->format & M6812_OP_EQ_MARKER))
+    code |= 0x20;
+
+  /* Turn into a long branch:
+     - when force long branch option (and not for jbcc pseudos),
+     - when jdbcc and the constant is out of -256..255 range,
+     - when branch optimization is allowed and branch out of range.  */
+  if ((jmp_mode == 0 && flag_force_long_jumps)
+      || (operands[1].exp.X_op == O_constant
+	  && (!check_range (n, M6812_OP_IBCC_MARKER) &&
+	      (jmp_mode == 1 || flag_fixed_branchs == 0))))
+    {
+      f = frag_more (2);
+      code ^= 0x20;
+      number_to_chars_bigendian (f, code, 1);
+      number_to_chars_bigendian (f + 1, M6812_JMP, 1);
+      fixup16 (&operands[0].exp, M6811_OP_IND16, M6811_OP_IND16);
+      return;
+    }
+
+  /* Branch with a constant that must fit in 9-bits.  */
+  if (operands[1].exp.X_op == O_constant)
+    {
+      if (!check_range (n, M6812_OP_IBCC_MARKER))
+	{
+	  as_bad (_("Operand out of range for a relative branch: `%ld'"),
+                  n);
+	}
+      else
+	{
+	  if ((long) n < 0)
+	    code |= 0x10;
+
+	  f = frag_more (2);
+	  number_to_chars_bigendian (f, code, 1);
+	  number_to_chars_bigendian (f + 1, n & 0x0FF, 1);
+	}
+    }
+  else
+    {
+      /* Branch offset must fit in 8-bits, don't do some relax.  */
+      if (jmp_mode == 0 && flag_fixed_branchs)
+	{
+	  fixup8 (&operands[0].exp, M6811_OP_JUMP_REL, M6811_OP_JUMP_REL);
+	}
+
+      else
+	{
+	  f = frag_more (2);
+	  number_to_chars_bigendian (f, code, 1);
+	  number_to_chars_bigendian (f + 1, 0, 1);
+	  frag_var (rs_machine_dependent, 3, 3,
+		    ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_UNDF),
+		    operands[1].exp.X_add_symbol, (offsetT) n, f);
+	}
+    }
+}
+
+#define OP_EXTENDED (M6811_OP_PAGE2 | M6811_OP_PAGE3 | M6811_OP_PAGE4)
+
+/* Assemble the post index byte for 68HC12 extended addressing modes.  */
+static int
+build_indexed_byte (op, format, move_insn)
+     operand *op;
+     int format ATTRIBUTE_UNUSED;
+     int move_insn;
+{
+  unsigned char byte = 0;
+  char *f;
+  int mode;
+  long val;
+
+  val = op->exp.X_add_number;
+  mode = op->mode;
+  if (mode & M6812_AUTO_INC_DEC)
+    {
+      byte = 0x20;
+      if (mode & (M6812_POST_INC | M6812_POST_DEC))
+	byte |= 0x10;
+
+      if (op->exp.X_op == O_constant)
+	{
+	  if (!check_range (val, mode))
+	    {
+	      as_bad (_("Increment/decrement value is out of range: `%ld'."),
+                      val);
+	    }
+	  if (mode & (M6812_POST_INC | M6812_PRE_INC))
+	    byte |= (val - 1) & 0x07;
+	  else
+	    byte |= (8 - ((val) & 7)) | 0x8;
+	}
+      switch (op->reg1)
+	{
+	case REG_NONE:
+	  as_fatal (_("Expecting a register."));
+
+	case REG_X:
+	  byte |= 0;
+	  break;
+
+	case REG_Y:
+	  byte |= 0x40;
+	  break;
+
+	case REG_SP:
+	  byte |= 0x80;
+	  break;
+
+	default:
+	  as_bad (_("Invalid register for post/pre increment."));
+	  break;
+	}
+
+      f = frag_more (1);
+      number_to_chars_bigendian (f, byte, 1);
+      return 1;
+    }
+
+  if (mode & M6812_OP_IDX)
+    {
+      switch (op->reg1)
+	{
+	case REG_X:
+	  byte = 0;
+	  break;
+
+	case REG_Y:
+	  byte = 1;
+	  break;
+
+	case REG_SP:
+	  byte = 2;
+	  break;
+
+	case REG_PC:
+	  byte = 3;
+	  break;
+
+	default:
+	  as_bad (_("Invalid register."));
+	  break;
+	}
+      if (op->exp.X_op == O_constant)
+	{
+	  if (!check_range (val, M6812_OP_IDX))
+	    {
+	      as_bad (_("Offset out of 16-bit range: %ld."), val);
+	    }
+
+	  if (move_insn && !(val >= -16 && val <= 15))
+	    {
+	      as_bad (_("Offset out of 5-bit range for movw/movb insn."));
+	      return -1;
+	    }
+
+	  if (val >= -16 && val <= 15 && !(mode & M6812_OP_IDX_2))
+	    {
+	      byte = byte << 6;
+	      byte |= val & 0x1f;
+	      f = frag_more (1);
+	      number_to_chars_bigendian (f, byte, 1);
+	      return 1;
+	    }
+	  else if (val >= -256 && val <= 255 && !(mode & M6812_OP_IDX_2))
+	    {
+	      byte = byte << 3;
+	      byte |= 0xe0;
+	      if (val < 0)
+		byte |= 0x1;
+	      f = frag_more (2);
+	      number_to_chars_bigendian (f, byte, 1);
+	      number_to_chars_bigendian (f + 1, val & 0x0FF, 1);
+	      return 2;
+	    }
+	  else
+	    {
+	      byte = byte << 3;
+	      if (mode & M6812_OP_IDX_2)
+		byte |= 0xe3;
+	      else
+		byte |= 0xe2;
+
+	      f = frag_more (3);
+	      number_to_chars_bigendian (f, byte, 1);
+	      number_to_chars_bigendian (f + 1, val & 0x0FFFF, 2);
+	      return 3;
+	    }
+	}
+      f = frag_more (1);
+      number_to_chars_bigendian (f, byte, 1);
+      /*
+         fix_new_exp (frag_now, f - frag_now->fr_literal, 2,
+         &op->exp, false, BFD_RELOC_16); */
+      frag_var (rs_machine_dependent, 2, 2,
+		ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_UNDF),
+		op->exp.X_add_symbol, val, f);
+      return 3;
+    }
+
+  if (mode & M6812_OP_REG)
+    {
+      if (mode & M6812_OP_IDX_2)
+	{
+	  if (op->reg1 != REG_D)
+	    as_bad (_("Expecting register D for indexed indirect mode."));
+	  if (move_insn)
+	    as_bad (_("Indexed indirect mode is not allowed for movb/movw."));
+
+	  byte = 0xE7;
+	}
+      else
+	{
+	  switch (op->reg1)
+	    {
+	    case REG_A:
+	      byte = 0xE4;
+	      break;
+
+	    case REG_B:
+	      byte = 0xE5;
+	      break;
+
+	    default:
+	      as_bad (_("Invalid accumulator register."));
+
+	    case REG_D:
+	      byte = 0xE6;
+	      break;
+	    }
+	}
+      switch (op->reg2)
+	{
+	case REG_X:
+	  break;
+
+	case REG_Y:
+	  byte |= (1 << 3);
+	  break;
+
+	case REG_SP:
+	  byte |= (2 << 3);
+	  break;
+
+	case REG_PC:
+	  byte |= (3 << 3);
+	  break;
+
+	default:
+	  as_bad (_("Invalid indexed register."));
+	  break;
+	}
+      f = frag_more (1);
+      number_to_chars_bigendian (f, byte, 1);
+      return 1;
+    }
+
+  as_fatal (_("Addressing mode not implemented yet."));
+  return 0;
+}
+
+/* Assemble the 68HC12 register mode byte.  */
+static int
+build_reg_mode (op, format)
+     operand *op;
+     int format;
+{
+  unsigned char byte;
+  char *f;
+
+  if (format & M6812_OP_SEX_MARKER
+      && op->reg1 != REG_A && op->reg1 != REG_B && op->reg1 != REG_CCR)
+    as_bad (_("Invalid source register for this instruction, use 'tfr'."));
+  else if (op->reg1 == REG_NONE || op->reg1 == REG_PC)
+    as_bad (_("Invalid source register."));
+
+  if (format & M6812_OP_SEX_MARKER
+      && op->reg2 != REG_D
+      && op->reg2 != REG_X && op->reg2 != REG_Y && op->reg2 != REG_SP)
+    as_bad (_("Invalid destination register for this instruction, use 'tfr'."));
+  else if (op->reg2 == REG_NONE || op->reg2 == REG_PC)
+    as_bad (_("Invalid destination register."));
+
+  byte = (op->reg1 << 4) | (op->reg2);
+  if (format & M6812_OP_EXG_MARKER)
+    byte |= 0x80;
+
+  f = frag_more (1);
+  number_to_chars_bigendian (f, byte, 1);
+  return 1;
+}
+
+/* build_insn takes a pointer to the opcode entry in the opcode table,
+   the array of operand expressions and builds the correspding instruction.
+   This operation only deals with non relative jumps insn (need special
+   handling).  */
+static void
+build_insn (opcode, operands, nb_operands)
+     struct m68hc11_opcode *opcode;
+     operand operands[];
+     int nb_operands ATTRIBUTE_UNUSED;
+{
+  int i;
+  char *f;
+  int insn_size = 1;
+  long format;
+  int move_insn = 0;
+
+  /* Put the page code instruction if there is one.  */
+  format = opcode->format;
+  if (format & OP_EXTENDED)
+    {
+      int page_code;
+
+      f = m68hc11_new_insn (2);
+      if (format & M6811_OP_PAGE2)
+	page_code = M6811_OPCODE_PAGE2;
+      else if (format & M6811_OP_PAGE3)
+	page_code = M6811_OPCODE_PAGE3;
+      else
+	page_code = M6811_OPCODE_PAGE4;
+
+      number_to_chars_bigendian (f, page_code, 1);
+      f++;
+      insn_size = 2;
+    }
+  else
+    f = m68hc11_new_insn (1);
+
+  number_to_chars_bigendian (f, opcode->opcode, 1);
+
+  i = 0;
+
+  /* The 68HC12 movb and movw instructions are special.  We have to handle
+     them in a special way.  */
+  if (format & (M6812_OP_IND16_P2 | M6812_OP_IDX_P2))
+    {
+      move_insn = 1;
+      if (format & M6812_OP_IDX)
+	{
+	  insn_size += build_indexed_byte (&operands[0], format, 1);
+	  i = 1;
+	  format &= ~M6812_OP_IDX;
+	}
+      if (format & M6812_OP_IDX_P2)
+	{
+	  insn_size += build_indexed_byte (&operands[1], format, 1);
+	  i = 0;
+	  format &= ~M6812_OP_IDX_P2;
+	}
+    }
+
+  if (format & (M6811_OP_DIRECT | M6811_OP_IMM8))
+    {
+      insn_size++;
+      fixup8 (&operands[i].exp,
+	      format & (M6811_OP_DIRECT | M6811_OP_IMM8 | M6812_OP_TRAP_ID),
+	      operands[i].mode);
+      i++;
+    }
+  else if (format & (M6811_OP_IMM16 | M6811_OP_IND16))
+    {
+      insn_size += 2;
+      fixup16 (&operands[i].exp, format & (M6811_OP_IMM16 | M6811_OP_IND16),
+	       operands[i].mode);
+      i++;
+    }
+  else if (format & (M6811_OP_IX | M6811_OP_IY))
+    {
+      if ((format & M6811_OP_IX) && (operands[0].reg1 != REG_X))
+	as_bad (_("Invalid indexed register, expecting register X."));
+      if ((format & M6811_OP_IY) && (operands[0].reg1 != REG_Y))
+	as_bad (_("Invalid indexed register, expecting register Y."));
+
+      insn_size++;
+      fixup8 (&operands[0].exp, M6811_OP_IX, operands[0].mode);
+      i = 1;
+    }
+  else if (format &
+	   (M6812_OP_IDX | M6812_OP_IDX_2 | M6812_OP_IDX_1 | M6812_OP_D_IDX))
+    {
+      insn_size += build_indexed_byte (&operands[i], format, move_insn);
+      i++;
+    }
+  else if (format & M6812_OP_REG && current_architecture & cpu6812)
+    {
+      insn_size += build_reg_mode (&operands[i], format);
+      i++;
+    }
+  if (format & M6811_OP_BITMASK)
+    {
+      insn_size++;
+      fixup8 (&operands[i].exp, M6811_OP_BITMASK, operands[i].mode);
+      i++;
+    }
+  if (format & M6811_OP_JUMP_REL)
+    {
+      insn_size++;
+      fixup8 (&operands[i].exp, M6811_OP_JUMP_REL, operands[i].mode);
+      i++;
+    }
+  else if (format & M6812_OP_IND16_P2)
+    {
+      insn_size += 2;
+      fixup16 (&operands[1].exp, M6811_OP_IND16, operands[1].mode);
+    }
+}
+
+
+/* Opcode identification and operand analysis.  */
+
+/* find() gets a pointer to an entry in the opcode table.  It must look at all
+   opcodes with the same name and use the operands to choose the correct
+   opcode.  Returns the opcode pointer if there was a match and 0 if none.  */
+static struct m68hc11_opcode *
+find (opc, operands, nb_operands)
+     struct m68hc11_opcode_def *opc;
+     operand operands[];
+     int nb_operands;
+{
+  int i, match, pos;
+  struct m68hc11_opcode *opcode;
+  struct m68hc11_opcode *op_indirect;
+
+  op_indirect = 0;
+  opcode = opc->opcode;
+
+  /* Now search the opcode table table for one with operands
+     that matches what we've got.  We're only done if the operands matched so
+     far AND there are no more to check.  */
+  for (pos = match = 0; match == 0 && pos < opc->nb_modes; pos++, opcode++)
+    {
+      int poss_indirect = 0;
+      long format = opcode->format;
+      int expect;
+
+      expect = 0;
+      if (opcode->format & M6811_OP_MASK)
+	expect++;
+      if (opcode->format & M6811_OP_BITMASK)
+	expect++;
+      if (opcode->format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
+	expect++;
+      if (opcode->format & (M6812_OP_IND16_P2 | M6812_OP_IDX_P2))
+	expect++;
+
+      for (i = 0; expect == nb_operands && i < nb_operands; i++)
+	{
+	  int mode = operands[i].mode;
+
+	  if (mode & M6811_OP_IMM16)
+	    {
+	      if (format &
+		  (M6811_OP_IMM8 | M6811_OP_IMM16 | M6811_OP_BITMASK))
+		continue;
+	      break;
+	    }
+	  if (mode == M6811_OP_DIRECT)
+	    {
+	      if (format & M6811_OP_DIRECT)
+		continue;
+
+	      /* If the operand is a page 0 operand, remember a
+	         possible <abs-16> addressing mode.  We mark
+	         this and continue to check other operands.  */
+	      if (format & M6811_OP_IND16
+		  && flag_strict_direct_addressing && op_indirect == 0)
+		{
+		  poss_indirect = 1;
+		  continue;
+		}
+	      break;
+	    }
+	  if (mode & M6811_OP_IND16)
+	    {
+	      if (i == 0 && (format & M6811_OP_IND16) != 0)
+		continue;
+	      if (i != 0 && (format & M6812_OP_IND16_P2) != 0)
+		continue;
+	      if (i == 0 && (format & M6811_OP_BITMASK))
+		break;
+	    }
+	  if (mode & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
+	    {
+	      if (format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
+		continue;
+	    }
+	  if (mode & M6812_OP_REG)
+	    {
+	      if (i == 0 && format & M6812_OP_REG
+		  && operands[i].reg2 == REG_NONE)
+		continue;
+	      if (i == 0 && format & M6812_OP_REG
+		  && format & M6812_OP_REG_2 && operands[i].reg2 != REG_NONE)
+		{
+		  continue;
+		}
+	      if (i == 0 && format & M6812_OP_D_IDX)
+		continue;
+	      if (i == 0 && (format & M6812_OP_IDX)
+		  && (format & (M6812_OP_IND16_P2 | M6812_OP_IDX_P2)))
+		continue;
+	      if (i == 1 && format & M6812_OP_IDX_P2)
+		continue;
+	      break;
+	    }
+	  if (mode & M6812_OP_IDX)
+	    {
+	      if (format & M6811_OP_IX && operands[i].reg1 == REG_X)
+		continue;
+	      if (format & M6811_OP_IY && operands[i].reg1 == REG_Y)
+		continue;
+	      if (i == 0
+		  && format & (M6812_OP_IDX | M6812_OP_IDX_1 | M6812_OP_IDX_2)
+		  && (operands[i].reg1 == REG_X
+		      || operands[i].reg1 == REG_Y
+		      || operands[i].reg1 == REG_SP
+		      || operands[i].reg1 == REG_PC))
+		continue;
+	      if (i == 1 && format & M6812_OP_IDX_P2)
+		continue;
+	    }
+	  if (mode & M6812_AUTO_INC_DEC)
+	    {
+	      if (i == 0
+		  && format & (M6812_OP_IDX | M6812_OP_IDX_1 |
+			       M6812_OP_IDX_2))
+		continue;
+	      if (i == 1 && format & M6812_OP_IDX_P2)
+		continue;
+	    }
+	  break;
+	}
+      match = i == nb_operands;
+
+      /* Operands are ok but an operand uses page 0 addressing mode
+         while the insn supports abs-16 mode.  Keep a reference to this
+         insns in case there is no insn supporting page 0 addressing.  */
+      if (match && poss_indirect)
+	{
+	  op_indirect = opcode;
+	  match = 0;
+	}
+      if (match)
+	break;
+    }
+
+  /* Page 0 addressing is used but not supported by any insn.
+     If absolute addresses are supported, we use that insn.  */
+  if (match == 0 && op_indirect)
+    {
+      opcode = op_indirect;
+      match = 1;
+    }
+
+  if (!match)
+    {
+      return (0);
+    }
+
+  return opcode;
+}
+
+
+/* Find the real opcode and its associated operands.  We use a progressive
+   approach here.  On entry, 'opc' points to the first opcode in the
+   table that matches the opcode name in the source line.  We try to
+   isolate an operand, find a possible match in the opcode table.
+   We isolate another operand if no match were found.  The table 'operands'
+   is filled while operands are recognized.
+
+   Returns the opcode pointer that matches the opcode name in the
+   source line and the associated operands.  */
+static struct m68hc11_opcode *
+find_opcode (opc, operands, nb_operands)
+     struct m68hc11_opcode_def *opc;
+     operand operands[];
+     int *nb_operands;
+{
+  struct m68hc11_opcode *opcode;
+  int i;
+
+  if (opc->max_operands == 0)
+    {
+      *nb_operands = 0;
+      return opc->opcode;
+    }
+
+  for (i = 0; i < opc->max_operands;)
+    {
+      int result;
+
+      result = get_operand (&operands[i], i, opc->format);
+      if (result <= 0)
+	{
+	  return 0;
+	}
+
+      /* Special case where the bitmask of the bclr/brclr
+         instructions is not introduced by #.
+         Example: bclr 3,x $80.  */
+      if (i == 1 && (opc->format & M6811_OP_BITMASK)
+	  && (operands[i].mode & M6811_OP_IND16))
+	{
+	  operands[i].mode = M6811_OP_IMM16;
+	}
+
+      i += result;
+      *nb_operands = i;
+      if (i >= opc->min_operands)
+	{
+	  opcode = find (opc, operands, i);
+	  if (opcode)
+	    {
+	      return opcode;
+	    }
+	}
+
+      if (*input_line_pointer == ',')
+	input_line_pointer++;
+    }
+  return 0;
+}
+
+#define M6812_XBCC_MARKER (M6812_OP_TBCC_MARKER \
+                           | M6812_OP_DBCC_MARKER \
+                           | M6812_OP_IBCC_MARKER)
+
+
+/* Gas line assembler entry point.  */
+
+/* This is the main entry point for the machine-dependent assembler.  str
+   points to a machine-dependent instruction.  This function is supposed to
+   emit the frags/bytes it assembles to.  */
+void
+md_assemble (str)
+     char *str;
+{
+  struct m68hc11_opcode_def *opc;
+  struct m68hc11_opcode *opcode;
+
+  unsigned char *op_start, *save;
+  unsigned char *op_end;
+  char name[20];
+  int nlen = 0;
+  operand operands[M6811_MAX_OPERANDS];
+  int nb_operands;
+  int branch_optimize = 0;
+  int alias_id = -1;
+
+  /* Drop leading whitespace */
+  while (*str == ' ')
+    str++;
+
+  /* Find the opcode end and get the opcode in 'name'.  The opcode is forced
+     lower case (the opcode table only has lower case op-codes).  */
+  for (op_start = op_end = (unsigned char *) (str);
+       *op_end && nlen < 20 && !is_end_of_line[*op_end] && *op_end != ' ';
+       op_end++)
+    {
+      name[nlen] = tolower (op_start[nlen]);
+      nlen++;
+    }
+  name[nlen] = 0;
+
+  if (nlen == 0)
+    {
+      as_bad (_("No instruction or missing opcode."));
+      return;
+    }
+
+  /* Find the opcode definition given its name.  */
+  opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash, name);
+
+  /* If it's not recognized, look for 'jbsr' and 'jbxx'.  These are
+     pseudo insns for relative branch.  For these branchs, we always
+     optimize them (turned into absolute branchs) even if --short-branchs
+     is given.  */
+  if (opc == NULL && name[0] == 'j' && name[1] == 'b')
+    {
+      opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash, &name[1]);
+      if (opc
+	  && (!(opc->format & M6811_OP_JUMP_REL)
+	      || (opc->format & M6811_OP_BITMASK)))
+	opc = 0;
+      if (opc)
+	branch_optimize = 1;
+    }
+
+  /* The following test should probably be removed.  This is not conform
+     to Motorola assembler specs.  */
+  if (opc == NULL && flag_mri)
+    {
+      if (*op_end == ' ' || *op_end == '\t')
+	{
+	  while (*op_end == ' ' || *op_end == '\t')
+	    op_end++;
+
+	  if (nlen < 19
+	      && (*op_end &&
+		  (is_end_of_line[op_end[1]]
+		   || op_end[1] == ' ' || op_end[1] == '\t'
+		   || !isalnum (op_end[1])))
+	      && (*op_end == 'a' || *op_end == 'b'
+		  || *op_end == 'A' || *op_end == 'B'
+		  || *op_end == 'd' || *op_end == 'D'
+		  || *op_end == 'x' || *op_end == 'X'
+		  || *op_end == 'y' || *op_end == 'Y'))
+	    {
+	      name[nlen++] = tolower (*op_end++);
+	      name[nlen] = 0;
+	      opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash,
+							     name);
+	    }
+	}
+    }
+
+  /* Identify a possible instruction alias.  There are some on the
+     68HC12 to emulate a fiew 68HC11 instructions.  */
+  if (opc == NULL && (current_architecture & cpu6812))
+    {
+      int i;
+
+      for (i = 0; i < m68hc12_num_alias; i++)
+	if (strcmp (m68hc12_alias[i].name, name) == 0)
+	  {
+	    alias_id = i;
+	    break;
+	  }
+    }
+  if (opc == NULL && alias_id < 0)
+    {
+      as_bad (_("Opcode `%s' is not recognized."), name);
+      return;
+    }
+  save = input_line_pointer;
+  input_line_pointer = op_end;
+
+  if (opc)
+    {
+      opc->used++;
+      opcode = find_opcode (opc, operands, &nb_operands);
+    }
+  else
+    opcode = 0;
+
+  if ((opcode || alias_id >= 0) && !flag_mri)
+    {
+      char *p = input_line_pointer;
+
+      while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
+	p++;
+
+      if (*p != '\n' && *p)
+	as_bad (_("Garbage at end of instruction: `%s'."), p);
+    }
+
+  input_line_pointer = save;
+
+  if (alias_id >= 0)
+    {
+      char *f = m68hc11_new_insn (m68hc12_alias[alias_id].size);
+      
+      number_to_chars_bigendian (f, m68hc12_alias[alias_id].code1, 1);
+      if (m68hc12_alias[alias_id].size > 1)
+	number_to_chars_bigendian (f + 1, m68hc12_alias[alias_id].code2, 1);
+
+      return;
+    }
+
+  /* Opcode is known but does not have valid operands.  Print out the
+     syntax for this opcode.  */
+  if (opcode == 0)
+    {
+      if (flag_print_insn_syntax)
+	print_insn_format (name);
+
+      as_bad (_("Invalid operand for `%s'"), name);
+      return;
+    }
+
+  /* Treat dbeq/ibeq/tbeq instructions in a special way.  The branch is
+     relative and must be in the range -256..255 (9-bits).  */
+  if ((opcode->format & M6812_XBCC_MARKER)
+      && (opcode->format & M6811_OP_JUMP_REL))
+    build_dbranch_insn (opcode, operands, nb_operands);
+
+  /* Relative jumps instructions are taken care of separately.  We have to make
+     sure that the relative branch is within the range -128..127.  If it's out
+     of range, the instructions are changed into absolute instructions.
+     This is not supported for the brset and brclr instructions.  */
+  else if ((opcode->format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
+	   && !(opcode->format & M6811_OP_BITMASK))
+    build_jump_insn (opcode, operands, nb_operands, branch_optimize);
+  else
+    build_insn (opcode, operands, nb_operands);
+}
+
+
+/* Relocation, relaxation and frag conversions.  */
+
+long
+md_pcrel_from_section (fixp, sec)
+     fixS *fixp;
+     segT sec;
+{
+  int adjust;
+  if (fixp->fx_addsy != (symbolS *) NULL
+      && (!S_IS_DEFINED (fixp->fx_addsy)
+          || (S_GET_SEGMENT (fixp->fx_addsy) != sec)))
+    return 0;
+
+  adjust = fixp->fx_pcrel_adjust;
+  return fixp->fx_frag->fr_address + fixp->fx_where + adjust;
+}
+
+/* If while processing a fixup, a reloc really needs to be created
+   then it is done here.  */
+arelent *
+tc_gen_reloc (section, fixp)
+     asection *section;
+     fixS *fixp;
+{
+  arelent *reloc;
+
+  reloc = (arelent *) xmalloc (sizeof (arelent));
+  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+  if (fixp->fx_r_type == 0)
+    reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16);
+  else
+    reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+  if (reloc->howto == (reloc_howto_type *) NULL)
+    {
+      as_bad_where (fixp->fx_file, fixp->fx_line,
+		    _("Relocation %d is not supported by object file format."),
+		    (int) fixp->fx_r_type);
+      return NULL;
+    }
+
+  if (!fixp->fx_pcrel)
+    reloc->addend = fixp->fx_addnumber;
+  else
+    reloc->addend = (section->vma
+		     + (fixp->fx_pcrel_adjust == 64
+			? -1 : fixp->fx_pcrel_adjust)
+		     + fixp->fx_addnumber
+		     + md_pcrel_from_section (fixp, section));
+  return reloc;
+}
+
+void
+md_convert_frag (abfd, sec, fragP)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     asection *sec ATTRIBUTE_UNUSED;
+     fragS *fragP;
+{
+  fixS *fixp;
+  long disp;
+  char *buffer_address = fragP->fr_literal;
+
+  /* Address in object code of the displacement.  */
+  register int object_address = fragP->fr_fix + fragP->fr_address;
+
+  buffer_address += fragP->fr_fix;
+
+  /* The displacement of the address, from current location.  */
+  disp = fragP->fr_symbol ? S_GET_VALUE (fragP->fr_symbol) : 0;
+  disp = (disp + fragP->fr_offset) - object_address;
+  disp += symbol_get_frag (fragP->fr_symbol)->fr_address;
+
+  switch (fragP->fr_subtype)
+    {
+    case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE):
+      fragP->fr_opcode[1] = disp;
+      break;
+
+    case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD):
+      /* This relax is only for bsr and bra.  */
+      assert (IS_OPCODE (fragP->fr_opcode[0], M6811_BSR)
+	      || IS_OPCODE (fragP->fr_opcode[0], M6811_BRA)
+	      || IS_OPCODE (fragP->fr_opcode[0], M6812_BSR));
+
+      fragP->fr_opcode[0] = convert_branch (fragP->fr_opcode[0]);
+
+      fix_new (fragP, fragP->fr_fix - 1, 2,
+	       fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_16);
+      fragP->fr_fix += 1;
+      break;
+
+    case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE):
+    case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_BYTE):
+      fragP->fr_opcode[1] = disp;
+      break;
+
+    case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD):
+      /* Invert branch.  */
+      fragP->fr_opcode[0] ^= 1;
+      fragP->fr_opcode[1] = 3;	/* Branch offset */
+      buffer_address[0] = M6811_JMP;
+      fix_new (fragP, fragP->fr_fix + 1, 2,
+	       fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_16);
+      fragP->fr_fix += 3;
+      break;
+
+    case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_WORD):
+      /* Translate branch into a long branch.  */
+      fragP->fr_opcode[1] = fragP->fr_opcode[0];
+      fragP->fr_opcode[0] = M6811_OPCODE_PAGE2;
+
+      fixp = fix_new (fragP, fragP->fr_fix, 2,
+		      fragP->fr_symbol, fragP->fr_offset, 1,
+		      BFD_RELOC_16_PCREL);
+      fixp->fx_pcrel_adjust = 2;
+      fragP->fr_fix += 2;
+      break;
+
+    case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS5):
+      fragP->fr_opcode[0] = fragP->fr_opcode[0] << 5;
+      fragP->fr_opcode[0] |= disp & 0x1f;
+      break;
+
+    case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9):
+      fragP->fr_opcode[0] = (fragP->fr_opcode[0] << 3);
+      fragP->fr_opcode[0] |= 0xE0;
+      fix_new (fragP, fragP->fr_fix + 1, 1,
+	       fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_8);
+      fragP->fr_fix += 1;
+      break;
+
+    case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16):
+      fragP->fr_opcode[0] = (fragP->fr_opcode[0] << 3);
+      fragP->fr_opcode[0] |= 0xE2;
+      fix_new (fragP, fragP->fr_fix, 2,
+	       fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_16);
+      fragP->fr_fix += 1;
+      break;
+
+    case ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_BYTE):
+      if (disp < 0)
+	fragP->fr_opcode[0] |= 0x10;
+
+      fragP->fr_opcode[1] = disp & 0x0FF;
+      break;
+
+    case ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_WORD):
+      /* Invert branch.  */
+      fragP->fr_opcode[0] ^= 0x20;
+      fragP->fr_opcode[1] = 3;	/* Branch offset.  */
+      buffer_address[0] = M6812_JMP;
+      fix_new (fragP, fragP->fr_fix + 1, 2,
+	       fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_16);
+      fragP->fr_fix += 3;
+      break;
+
+    default:
+      break;
+    }
+}
+
+/* Force truly undefined symbols to their maximum size, and generally set up
+   the frag list to be relaxed.  */
+int
+md_estimate_size_before_relax (fragP, segment)
+     fragS *fragP;
+     asection *segment;
+{
+  int old_fr_fix;
+  char *buffer_address = fragP->fr_fix + fragP->fr_literal;
+
+  old_fr_fix = fragP->fr_fix;
+
+  switch (fragP->fr_subtype)
+    {
+    case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF):
+
+      /* This relax is only for bsr and bra.  */
+      assert (IS_OPCODE (fragP->fr_opcode[0], M6811_BSR)
+	      || IS_OPCODE (fragP->fr_opcode[0], M6811_BRA)
+	      || IS_OPCODE (fragP->fr_opcode[0], M6812_BSR));
+
+      /* A relaxable case.  */
+      if (S_GET_SEGMENT (fragP->fr_symbol) == segment)
+	{
+	  fragP->fr_subtype = ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE);
+	}
+      else
+	{
+	  if (flag_fixed_branchs)
+	    as_bad_where (fragP->fr_file, fragP->fr_line,
+			  _("bra or bsr with undefined symbol."));
+
+	  /* The symbol is undefined or in a separate section.  Turn bra into a
+	     jmp and bsr into a jsr.  The insn becomes 3 bytes long (instead of
+	     2).  A fixup is necessary for the unresolved symbol address.  */
+
+	  fragP->fr_opcode[0] = convert_branch (fragP->fr_opcode[0]);
+
+	  fragP->fr_fix++;
+	  fix_new (fragP, old_fr_fix - 1, 2, fragP->fr_symbol,
+		   fragP->fr_offset, 0, BFD_RELOC_16);
+	  frag_wane (fragP);
+	}
+      break;
+
+    case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_UNDF):
+      assert (current_architecture & cpu6811);
+
+      if (S_GET_SEGMENT (fragP->fr_symbol) == segment)
+	{
+	  fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH,
+					    STATE_BYTE);
+	}
+      else
+	{
+	  fragP->fr_opcode[0] ^= 1;	/* Reverse sense of branch. */
+	  fragP->fr_opcode[1] = 3;	/* Skip next jmp insn (3 bytes) */
+
+	  /* Don't use fr_opcode[2] because this may be
+             in a different frag.  */
+	  buffer_address[0] = M6811_JMP;
+
+	  fragP->fr_fix++;
+	  fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+		   fragP->fr_offset, 0, BFD_RELOC_16);
+	  fragP->fr_fix += 2;
+	  frag_wane (fragP);
+	}
+      break;
+
+    case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_UNDF):
+      assert (current_architecture & cpu6812);
+
+      if (S_GET_SEGMENT (fragP->fr_symbol) == segment)
+	{
+	  fragP->fr_subtype = ENCODE_RELAX (STATE_INDEXED_OFFSET,
+					    STATE_BITS5);
+	}
+      else
+	{
+	  /* Switch the indexed operation to 16-bit mode.  */
+	  if ((fragP->fr_opcode[1] & 0x21) == 0x20)
+	    fragP->fr_opcode[1] = (fragP->fr_opcode[1] >> 3) | 0xc0 | 0x02;
+
+	  fragP->fr_fix++;
+	  fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+		   fragP->fr_offset, 0, BFD_RELOC_16);
+	  fragP->fr_fix += 2;
+	  frag_wane (fragP);
+	}
+      break;
+
+    case ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_UNDF):
+      assert (current_architecture & cpu6812);
+
+      if (S_GET_SEGMENT (fragP->fr_symbol) == segment)
+	{
+	  fragP->fr_subtype = ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_BYTE);
+	}
+      else
+	{
+	  fragP->fr_opcode[0] ^= 0x20;	/* Reverse sense of branch. */
+	  fragP->fr_opcode[1] = 3;	/* Skip next jmp insn (3 bytes).  */
+
+	  /* Don't use fr_opcode[2] because this may be
+             in a different frag.  */
+	  buffer_address[0] = M6812_JMP;
+
+	  fragP->fr_fix++;
+	  fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+		   fragP->fr_offset, 0, BFD_RELOC_16);
+	  fragP->fr_fix += 2;
+	  frag_wane (fragP);
+	}
+      break;
+
+    case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_UNDF):
+      assert (current_architecture & cpu6812);
+
+      if (S_GET_SEGMENT (fragP->fr_symbol) == segment)
+	{
+	  fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812,
+					    STATE_BYTE);
+	}
+      else
+	{
+	  /* Translate into a lbcc branch.  */
+	  fragP->fr_opcode[1] = fragP->fr_opcode[0];
+	  fragP->fr_opcode[0] = M6811_OPCODE_PAGE2;
+
+	  fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+		   fragP->fr_offset, 0, BFD_RELOC_16_PCREL);
+	  fragP->fr_fix += 2;
+	  frag_wane (fragP);
+	}
+      break;
+
+    default:
+      as_fatal (_("Subtype %d is not recognized."), fragP->fr_subtype);
+    }
+
+  return (fragP->fr_fix - old_fr_fix);
+}
+
+int
+md_apply_fix (fixp, valuep)
+     fixS *fixp;
+     valueT *valuep;
+{
+  char *where;
+  long value;
+  int op_type;
+
+  if (fixp->fx_addsy == (symbolS *) NULL)
+    {
+      value = *valuep;
+      fixp->fx_done = 1;
+    }
+  else if (fixp->fx_pcrel)
+    {
+      value = *valuep;
+    }
+  else
+    {
+      value = fixp->fx_offset;
+      if (fixp->fx_subsy != (symbolS *) NULL)
+	{
+	  if (S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
+	    {
+	      value -= S_GET_VALUE (fixp->fx_subsy);
+	    }
+	  else
+	    {
+	      /* We don't actually support subtracting a symbol.  */
+	      as_bad_where (fixp->fx_file, fixp->fx_line,
+			    _("Expression too complex."));
+	    }
+	}
+    }
+
+  op_type = fixp->fx_r_type;
+
+  /* Patch the instruction with the resolved operand.  Elf relocation
+     info will also be generated to take care of linker/loader fixups.
+     The 68HC11 addresses only 64Kb, we are only concerned by 8 and 16-bit
+     relocs.  BFD_RELOC_8 is basically used for .page0 access (the linker
+     will warn for overflows).  BFD_RELOC_8_PCREL should not be generated
+     because it's either resolved or turned out into non-relative insns (see
+     relax table, bcc, bra, bsr transformations)
+
+     The BFD_RELOC_32 is necessary for the support of --gstabs.  */
+  where = fixp->fx_frag->fr_literal + fixp->fx_where;
+
+  switch (fixp->fx_r_type)
+    {
+    case BFD_RELOC_32:
+      bfd_putb32 ((bfd_vma) value, (unsigned char *) where);
+      break;
+
+    case BFD_RELOC_16:
+    case BFD_RELOC_16_PCREL:
+      bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
+      if (value < -65537 || value > 65535)
+	as_bad_where (fixp->fx_file, fixp->fx_line,
+		      _("Value out of 16-bit range."));
+      break;
+
+    case BFD_RELOC_M68HC11_HI8:
+      value = value >> 8;
+      /* Fall through */
+
+    case BFD_RELOC_M68HC11_LO8:
+    case BFD_RELOC_8:
+      /*bfd_putb8 ((bfd_vma) value, (unsigned char *) where); */
+      ((bfd_byte *) where)[0] = (bfd_byte) value;
+      break;
+
+    case BFD_RELOC_8_PCREL:
+      /*bfd_putb8 ((bfd_vma) value, (unsigned char *) where); */
+      ((bfd_byte *) where)[0] = (bfd_byte) value;
+
+      if (value < -128 || value > 127)
+	as_bad_where (fixp->fx_file, fixp->fx_line,
+		      _("Value %ld too large for 8-bit PC-relative branch."),
+		      value);
+      break;
+
+    case BFD_RELOC_M68HC11_3B:
+      if (value <= 0 || value > 8)
+	as_bad_where (fixp->fx_file, fixp->fx_line,
+		      _("Auto increment/decrement offset '%ld' is out of range."),
+		      value);
+      if (where[0] & 0x8)
+	value = 8 - value;
+      else
+	value--;
+
+      where[0] = where[0] | (value & 0x07);
+      break;
+
+    default:
+      as_fatal (_("Line %d: unknown relocation type: 0x%x."),
+		fixp->fx_line, fixp->fx_r_type);
+    }
+  return 0;
+}
+
+int
+m68hc11_cleanup ()
+{
+  return 1;
+}
+
+void
+m68hc11_end_of_source ()
+{
+  segT saved_seg;
+  subsegT saved_subseg;
+  segT debug_info;
+  char* p;
+  long total_size = 0;
+  
+  if (debug_type != DEBUG_DWARF2)
+    return;
+  
+  dwarf2_finish ();
+
+  saved_seg = now_seg;
+  saved_subseg = now_subseg;
+
+  debug_info = subseg_new (".debug_info", 0);
+  bfd_set_section_flags (stdoutput, debug_info, SEC_READONLY);
+  subseg_set (debug_info, 0);
+  p = frag_more (10);
+  total_size = 12;
+  
+# define STUFF(val,size)	md_number_to_chars (p, val, size); p += size;
+  STUFF (total_size, 4); /* Length of compilation unit.  */
+  STUFF (2, 2); /* Dwarf version */
+  STUFF (0, 4);
+  STUFF (2, 1); /* Pointer size */
+  STUFF (1, 1); /* Compile unit */
+  STUFF (0, 4);
+
+  now_subseg = saved_subseg;
+  now_seg = saved_seg;
+}
diff -Nrup --exclude-from=binutils-exclude.lst /src/gnu/cygnus/binutils/gas/config/tc-m68hc11.h binutils/gas/config/tc-m68hc11.h
--- /src/gnu/cygnus/binutils/gas/config/tc-m68hc11.h	Thu Jan  1 01:00:00 1970
+++ binutils/gas/config/tc-m68hc11.h	Fri Jun 16 01:14:32 2000
@@ -0,0 +1,109 @@
+/* tc-m68hc11.h -- Header file for tc-m68hc11.c.
+   Copyright (C) 1999, 2000 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. */
+
+#define TC_M68HC11
+#define TC_M68HC12
+
+/* Define TC_M68K so that we can use the MRI mode.  */
+#define TC_M68K
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* Motorola assembler specs does not require '.' before pseudo-ops.  */
+#define NO_PSEUDO_DOT 1
+
+#if 0
+/* Treat the single quote as a string delimiter.
+   ??? This does not work at all.  */
+#define SINGLE_QUOTE_STRINGS 1
+#endif
+
+#ifndef BFD_ASSEMBLER
+#error M68HC11 support requires BFD_ASSEMBLER
+#endif
+
+/* The target BFD architecture.  */
+#define TARGET_ARCH (m68hc11_arch ())
+extern enum bfd_architecture m68hc11_arch PARAMS ((void));
+
+#define TARGET_MACH (m68hc11_mach ())
+extern int m68hc11_mach PARAMS ((void));
+
+#define TARGET_FORMAT (m68hc11_arch_format ())
+extern const char *m68hc11_arch_format PARAMS ((void));
+
+/* Specific sections:
+   - The .page0 is a data section that is mapped in [0x0000..0x00FF].
+     Page0 accesses are faster on the M68HC11. Soft registers used by GCC-m6811
+     are located in .page0.
+   - The .vectors is the data section that represents the interrupt
+     vectors.  */
+#define ELF_TC_SPECIAL_SECTIONS \
+  { ".page0",	SHT_PROGBITS,	SHF_ALLOC + SHF_WRITE	}, \
+  { ".vectors",	SHT_PROGBITS,	SHF_ALLOC + SHF_WRITE	},
+
+#define LISTING_WORD_SIZE 1	/* A word is 1 bytes */
+#define LISTING_LHS_WIDTH 4	/* One word on the first line */
+#define LISTING_LHS_WIDTH_SECOND 4	/* One word on the second line */
+#define LISTING_LHS_CONT_LINES 4	/* And 4 lines max */
+#define LISTING_HEADER "M68HC11 GAS "
+
+/* call md_pcrel_from_section, not md_pcrel_from */
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section(FIXP, SEC)
+
+/* Permit temporary numeric labels.  */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK		/* .-foo gets turned into PC relative relocs */
+
+extern void m68hc11_init_after_args PARAMS ((void));
+#define tc_init_after_args m68hc11_init_after_args
+
+extern int m68hc11_parse_long_option PARAMS ((char *));
+#define md_parse_long_option m68hc11_parse_long_option
+
+extern void m68hc11_end_of_source PARAMS ((void));
+#define md_end() m68hc11_end_of_source ()
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 1
+
+/* We don't need to handle .word strangely.  */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars           number_to_chars_bigendian
+
+/* Relax table to translate short relative branches (-128..127) into
+   absolute branches.  */
+extern struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+extern int m68hc11_cleanup PARAMS ((void));
+
+#define md_operand(x)
+#define md_after_pass_hook()	     m68hc11_cleanup()
+#define md_cleanup()		     m68hc11_cleanup()
+#define md_do_align(a,b,c,d,e)	     m68hc11_cleanup()
+#define tc_frob_label(sym) do {\
+  m68hc11_cleanup(); \
+  S_SET_VALUE (sym, (valueT) frag_now_fix ()); \
+} while (0)
+
+#define tc_print_statistics m68hc11_print_statistics
+extern void m68hc11_print_statistics PARAMS ((FILE *));
diff -Nrup --exclude-from=binutils-exclude.lst /src/gnu/cygnus/binutils/gas/configure.in binutils/gas/configure.in
--- /src/gnu/cygnus/binutils/gas/configure.in	Sun Jun 11 00:16:17 2000
+++ binutils/gas/configure.in	Fri Jun 16 00:23:53 2000
@@ -122,6 +122,7 @@ changequote([,])dnl
 changequote(,)dnl
       i[456]86)		cpu_type=i386 ;;
       ia64)		cpu_type=ia64 ;;
+      m6811|m6812)	cpu_type=m68hc11 ;;
       m680[012346]0)	cpu_type=m68k ;;
 changequote([,])dnl
       m68008)		cpu_type=m68k ;;
@@ -273,6 +274,8 @@ changequote([,])dnl
       ia64-*-linux-gnu*)    fmt=elf em=linux ;;
 
       m32r-*-*)		    fmt=elf bfd_gas=yes ;;
+
+      m68hc11-*-*|m6811-*-*|m68hc12-*-*|m6812-*-*)fmt=elf bfd_gas=yes ;;
 
       m68k-*-vxworks* | m68k-ericsson-ose | m68k-*-sunos*)
 			    fmt=aout em=sun3 ;;


More information about the Binutils mailing list