1/* m68hc11-dis.c -- Motorola 68HC11 & 68HC12 disassembly
2   Copyright (C) 1999-2022 Free Software Foundation, Inc.
3   Written by Stephane Carrez (stcarrez@nerim.fr)
4   XGATE and S12X added by James Murray (jsm@jsm-net.demon.co.uk)
5
6   This file is part of the GNU opcodes library.
7
8   This library is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3, or (at your option)
11   any later version.
12
13   It is distributed in the hope that it will be useful, but WITHOUT
14   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
16   License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
21   MA 02110-1301, USA.  */
22
23#include "sysdep.h"
24#include <stdio.h>
25
26#include "opcode/m68hc11.h"
27#include "disassemble.h"
28
29#define PC_REGNUM 3
30
31static const char *const reg_name[] =
32{
33  "X", "Y", "SP", "PC"
34};
35
36static const char *const reg_src_table[] =
37{
38  "A", "B", "CCR", "TMP3", "D", "X", "Y", "SP"
39};
40
41static const char *const reg_dst_table[] =
42{
43  "A", "B", "CCR", "TMP2", "D", "X", "Y", "SP"
44};
45
46#define OP_PAGE_MASK (M6811_OP_PAGE2|M6811_OP_PAGE3|M6811_OP_PAGE4)
47
48
49static int
50read_memory (bfd_vma memaddr, bfd_byte* buffer, int size,
51             struct disassemble_info* info)
52{
53  int status;
54
55  /* Get first byte.  Only one at a time because we don't know the
56     size of the insn.  */
57  status = (*info->read_memory_func) (memaddr, buffer, size, info);
58  if (status != 0)
59    {
60      (*info->memory_error_func) (status, memaddr, info);
61      return -1;
62    }
63  return 0;
64}
65
66
67/* Read the 68HC12 indexed operand byte and print the corresponding mode.
68   Returns the number of bytes read or -1 if failure.  */
69static int
70print_indexed_operand (bfd_vma memaddr, struct disassemble_info* info,
71		       int* indirect, int mov_insn, int pc_offset,
72		       bfd_vma endaddr, int arch)
73{
74  bfd_byte buffer[4];
75  int reg;
76  int status;
77  bfd_vma val;
78  int pos = 1;
79
80  if (indirect)
81    *indirect = 0;
82
83  status = read_memory (memaddr, &buffer[0], 1, info);
84  if (status != 0)
85    {
86      return status;
87    }
88
89  /* n,r with 5-bits signed constant.  */
90  if ((buffer[0] & 0x20) == 0)
91    {
92      reg = (buffer[0] >> 6) & 3;
93      val = ((buffer[0] & 0x1f) ^ 0x10) - 0x10;
94      /* 68HC12 requires an adjustment for movb/movw pc relative modes.  */
95      if (reg == PC_REGNUM && info->mach == bfd_mach_m6812 && mov_insn)
96	val += pc_offset;
97      (*info->fprintf_func) (info->stream, "0x%x,%s",
98			     (unsigned) val & 0xffff, reg_name[reg]);
99
100      if (reg == PC_REGNUM)
101	{
102	  (* info->fprintf_func) (info->stream, " {");
103	   /* Avoid duplicate 0x from core binutils.  */
104	  if (info->symtab_size > 0)
105	    (*info->fprintf_func) (info->stream, "0x");
106	  (* info->print_address_func) (endaddr + val, info);
107	  (* info->fprintf_func) (info->stream, "}");
108	}
109    }
110
111  /* Auto pre/post increment/decrement.  */
112  else if ((buffer[0] & 0xc0) != 0xc0)
113    {
114      const char *mode;
115
116      reg = (buffer[0] >> 6) & 3;
117      val = buffer[0] & 7;
118      if (buffer[0] & 8)
119	{
120	  val = 8 - val;
121	  mode = "-";
122	}
123      else
124	{
125	  val = val + 1;
126	  mode = "+";
127	}
128      (*info->fprintf_func) (info->stream, "%d,%s%s%s",
129			     (unsigned) val,
130			     buffer[0] & 0x10 ? "" : mode,
131			     reg_name[reg], buffer[0] & 0x10 ? mode : "");
132    }
133
134  /* [n,r] 16-bits offset indexed indirect.  */
135  else if ((buffer[0] & 0x07) == 3)
136    {
137      if ((mov_insn) && (!(arch & cpu9s12x)))
138	{
139	  (*info->fprintf_func) (info->stream, "<invalid op: 0x%x>",
140				 buffer[0] & 0x0ff);
141	  return 0;
142	}
143      reg = (buffer[0] >> 3) & 0x03;
144      status = read_memory (memaddr + pos, &buffer[0], 2, info);
145      if (status != 0)
146	return status;
147
148      pos += 2;
149      val = (buffer[0] << 8) | buffer[1];
150      (*info->fprintf_func) (info->stream, "[0x%x,%s]",
151			     (unsigned) val & 0xffff, reg_name[reg]);
152      if (indirect)
153	*indirect = 1;
154    }
155
156  /* n,r with 9 and 16 bit signed constant.  */
157  else if ((buffer[0] & 0x4) == 0)
158    {
159      if ((mov_insn) && (!(arch & cpu9s12x)))
160	{
161	  (*info->fprintf_func) (info->stream, "<invalid op: 0x%x>",
162				 buffer[0] & 0x0ff);
163	  return 0;
164	}
165
166      reg = (buffer[0] >> 3) & 0x03;
167      status = read_memory (memaddr + pos,
168			    &buffer[1], (buffer[0] & 0x2 ? 2 : 1), info);
169      if (status != 0)
170	return status;
171
172      if (buffer[0] & 2)
173	{
174	  val = (((buffer[1] << 8) | buffer[2]) ^ 0x8000) - 0x8000;
175	  pos += 2;
176	  endaddr += 2;
177	}
178      else
179	{
180	  val = buffer[1] - ((buffer[0] & 1) << 8);
181	  pos++;
182	  endaddr++;
183	}
184      (*info->fprintf_func) (info->stream, "0x%x,%s",
185			     (unsigned) val & 0xffff, reg_name[reg]);
186      if (reg == PC_REGNUM)
187	{
188	  (* info->fprintf_func) (info->stream, " {0x");
189	  (* info->print_address_func) (endaddr + val, info);
190	  (* info->fprintf_func) (info->stream, "}");
191	}
192    }
193  else
194    {
195      reg = (buffer[0] >> 3) & 0x03;
196      switch (buffer[0] & 3)
197	{
198	case 0:
199	  (*info->fprintf_func) (info->stream, "A,%s", reg_name[reg]);
200	  break;
201	case 1:
202	  (*info->fprintf_func) (info->stream, "B,%s", reg_name[reg]);
203	  break;
204	case 2:
205	  (*info->fprintf_func) (info->stream, "D,%s", reg_name[reg]);
206	  break;
207	case 3:
208	default:
209	  (*info->fprintf_func) (info->stream, "[D,%s]", reg_name[reg]);
210	  if (indirect)
211	    *indirect = 1;
212	  break;
213	}
214    }
215
216  return pos;
217}
218
219/* Disassemble one instruction at address 'memaddr'.  Returns the number
220   of bytes used by that instruction.  */
221static int
222print_insn (bfd_vma memaddr, struct disassemble_info* info, int arch)
223{
224  int status;
225  bfd_byte buffer[4];
226  unsigned int code;
227  long format, pos, i;
228  bfd_vma val;
229  const struct m68hc11_opcode *opcode;
230
231  if (arch & cpuxgate)
232    {
233      /* Get two bytes as all XGATE instructions are 16bit.  */
234      status = read_memory (memaddr, buffer, 2, info);
235      if (status != 0)
236	return status;
237
238      format = 0;
239      code = (buffer[0] << 8) + buffer[1];
240
241      /* Scan the opcode table until we find the opcode
242	 with the corresponding page.  */
243      opcode = m68hc11_opcodes;
244      for (i = 0; i < m68hc11_num_opcodes; i++, opcode++)
245	{
246	  if ((opcode->opcode != (code & opcode->xg_mask)) || (opcode->arch != cpuxgate))
247  	    continue;
248	  /* We have found the opcode.  Extract the operand and print it.  */
249	  (*info->fprintf_func) (info->stream, "%s", opcode->name);
250	  format = opcode->format;
251	  if (format & (M68XG_OP_NONE))
252	    {
253	      /* Nothing to print.  */
254	    }
255	  else if (format & M68XG_OP_IMM3)
256	    (*info->fprintf_func) (info->stream, " #0x%x", (code >> 8) & 0x7);
257	  else if (format & M68XG_OP_R_R)
258	    (*info->fprintf_func) (info->stream, " R%x, R%x",
259				   (code >> 8) & 0x7, (code >> 5) & 0x7);
260	  else if (format & M68XG_OP_R_R_R)
261	    (*info->fprintf_func) (info->stream, " R%x, R%x, R%x",
262				   (code >> 8) & 0x7, (code >> 5) & 0x7, (code >> 2) & 0x7);
263	  else if (format & M68XG_OP_RD_RB_RI)
264	    (*info->fprintf_func) (info->stream, " R%x, (R%x, R%x)",
265				   (code >> 8) & 0x7, (code >> 5) & 0x7, (code >> 2) & 0x7);
266	  else if (format & M68XG_OP_RD_RB_RIp)
267	    (*info->fprintf_func) (info->stream, " R%x, (R%x, R%x+)",
268				   (code >> 8) & 0x7, (code >> 5) & 0x7, (code >> 2) & 0x7);
269	  else if (format & M68XG_OP_RD_RB_mRI)
270	    (*info->fprintf_func) (info->stream, " R%x, (R%x, -R%x)",
271				   (code >> 8) & 0x7, (code >> 5) & 0x7, (code >> 2) & 0x7);
272	  else if (format & M68XG_OP_R_R_OFFS5)
273	    (*info->fprintf_func) (info->stream, " R%x, (R%x, #0x%x)",
274				   (code >> 8) & 0x7, (code >> 5) & 0x7, code & 0x1f);
275	  else if (format & M68XG_OP_R_IMM8)
276	    (*info->fprintf_func) (info->stream, " R%x, #0x%02x",
277				   (code >> 8) & 0x7, code & 0xff);
278	  else if (format & M68XG_OP_R_IMM4)
279	    (*info->fprintf_func) (info->stream, " R%x, #0x%x",
280				   (code >> 8) & 0x7, (code & 0xf0) >> 4);
281	  else if (format & M68XG_OP_REL9)
282	    {
283	      (*info->fprintf_func) (info->stream, " 0x");
284	      val = buffer[1] - ((buffer[0] & 1) << 8);
285	      (*info->print_address_func) (memaddr + (val << 1) + 2, info);
286	    }
287	  else if (format & M68XG_OP_REL10)
288	    {
289	      (*info->fprintf_func) (info->stream, " 0x");
290	      val = (buffer[0] << 8) | buffer[1];
291	      val = ((val & 0x3ff) ^ 0x200) - 0x200;
292	      (*info->print_address_func) (memaddr + (val << 1) + 2, info);
293	    }
294	  else if ((code & 0x00ff) == 0x00f8)
295  	    (*info->fprintf_func) (info->stream, " R%x, CCR", (code >> 8) & 0x7);
296	  else if ((code & 0x00ff) == 0x00f9)
297  	    (*info->fprintf_func) (info->stream, " CCR, R%x", (code >> 8) & 0x7);
298	  else if ((code & 0x00ff) == 0x0)
299  	    (*info->fprintf_func) (info->stream, " R%x, PC", (code >> 8) & 0x7);
300	  else if (format & M68XG_OP_R)
301  	    {
302	      /* Special cases for TFR.  */
303	      if ((code & 0xf8ff) == 0x00f8)
304		(*info->fprintf_func) (info->stream, " R%x, CCR", (code >> 8) & 0x7);
305	      else if ((code & 0xf8ff) == 0x00f9)
306		(*info->fprintf_func) (info->stream, " CCR, R%x", (code >> 8) & 0x7);
307	      else if ((code & 0xf8ff) == 0x00fa)
308		(*info->fprintf_func) (info->stream, " R%x, PC",  (code >> 8) & 0x7);
309	      else
310		(*info->fprintf_func) (info->stream, " R%x", (code >> 8) & 0x7);
311	    }
312	  else
313	    /* Opcode not recognized.  */
314	    (*info->fprintf_func) (info->stream, "Not yet handled TEST .byte\t0x%04x", code);
315	  return 2;
316	}
317
318      /* Opcode not recognized.  */
319      (*info->fprintf_func) (info->stream, ".byte\t0x%04x", code);
320      return 2; /* Everything is two bytes.  */
321    }
322
323  /* HC11 and HC12.  */
324
325  /* Get first byte.  Only one at a time because we don't know the
326     size of the insn.  */
327  status = read_memory (memaddr, buffer, 1, info);
328  if (status != 0)
329    return status;
330
331  format = 0;
332  code = buffer[0];
333  pos = 0;
334
335  /* Look for page2,3,4 opcodes.  */
336  if (code == M6811_OPCODE_PAGE2)
337    {
338      pos++;
339      format = M6811_OP_PAGE2;
340    }
341  else if (code == M6811_OPCODE_PAGE3 && arch == cpu6811)
342    {
343      pos++;
344      format = M6811_OP_PAGE3;
345    }
346  else if (code == M6811_OPCODE_PAGE4 && arch == cpu6811)
347    {
348      pos++;
349      format = M6811_OP_PAGE4;
350    }
351
352  /* We are in page2,3,4; get the real opcode.  */
353  if (pos == 1)
354    {
355      status = read_memory (memaddr + pos, &buffer[1], 1, info);
356      if (status != 0)
357	return status;
358
359      code = buffer[1];
360    }
361
362  /* Look first for a 68HC12 alias.  All of them are 2-bytes long and
363     in page 1.  There is no operand to print.  We read the second byte
364     only when we have a possible match.  */
365  if ((arch & cpu6812) && format == 0)
366    {
367      int must_read = 1;
368
369      /* Walk the alias table to find a code1+code2 match.  */
370      for (i = 0; i < m68hc12_num_alias; i++)
371	{
372	  if (m68hc12_alias[i].code1 == code)
373	    {
374	      if (must_read)
375		{
376		  status = read_memory (memaddr + pos + 1,
377					&buffer[1], 1, info);
378		  if (status != 0)
379		    break;
380
381		  must_read = 1;
382		}
383	      if (m68hc12_alias[i].code2 == (unsigned char) buffer[1])
384		{
385		  (*info->fprintf_func) (info->stream, "%s",
386					 m68hc12_alias[i].name);
387		  return 2;
388		}
389	    }
390	}
391    }
392
393  pos++;
394
395  /* Scan the opcode table until we find the opcode
396     with the corresponding page.  */
397  opcode = m68hc11_opcodes;
398  for (i = 0; i < m68hc11_num_opcodes; i++, opcode++)
399    {
400      int offset;
401      int pc_src_offset;
402      int pc_dst_offset = 0;
403
404      if ((opcode->arch & arch) == 0)
405	continue;
406      if (opcode->opcode != code)
407	continue;
408      if ((opcode->format & OP_PAGE_MASK) != format)
409	continue;
410
411      if (opcode->format & M6812_OP_REG)
412	{
413	  int j;
414	  int is_jump;
415
416	  if (opcode->format & M6811_OP_JUMP_REL)
417	    is_jump = 1;
418	  else
419	    is_jump = 0;
420
421	  status = read_memory (memaddr + pos, &buffer[0], 1, info);
422	  if (status != 0)
423	    {
424	      return status;
425	    }
426	  for (j = 0; i + j < m68hc11_num_opcodes; j++)
427	    {
428	      if ((opcode[j].arch & arch) == 0)
429		continue;
430	      if (opcode[j].opcode != code)
431		continue;
432	      if (is_jump)
433		{
434		  if (!(opcode[j].format & M6811_OP_JUMP_REL))
435		    continue;
436
437		  if ((opcode[j].format & M6812_OP_IBCC_MARKER)
438		      && (buffer[0] & 0xc0) != 0x80)
439		    continue;
440		  if ((opcode[j].format & M6812_OP_TBCC_MARKER)
441		      && (buffer[0] & 0xc0) != 0x40)
442		    continue;
443		  if ((opcode[j].format & M6812_OP_DBCC_MARKER)
444		      && (buffer[0] & 0xc0) != 0)
445		    continue;
446		  if ((opcode[j].format & M6812_OP_EQ_MARKER)
447		      && (buffer[0] & 0x20) == 0)
448		    break;
449		  if (!(opcode[j].format & M6812_OP_EQ_MARKER)
450		      && (buffer[0] & 0x20) != 0)
451		    break;
452		  continue;
453		}
454	      if (opcode[j].format & M6812_OP_EXG_MARKER && buffer[0] & 0x80)
455		break;
456	      if ((opcode[j].format & M6812_OP_SEX_MARKER)
457		  && (((buffer[0] & 0x07) >= 3 && (buffer[0] & 7) <= 7))
458		  && ((buffer[0] & 0x0f0) <= 0x20))
459		break;
460	      if ((opcode[j].format & M6812_OP_SEX_MARKER)
461		  && (arch & cpu9s12x)
462		  && ((buffer[0] == 0x4d) || (buffer[0] == 0x4e)))
463		break;
464	      if (opcode[j].format & M6812_OP_TFR_MARKER
465		  && !(buffer[0] & 0x80))
466		break;
467	    }
468	  if (i + j < m68hc11_num_opcodes)
469	    opcode = &opcode[j];
470	}
471
472      /* We have found the opcode.  Extract the operand and print it.  */
473      (*info->fprintf_func) (info->stream, "%s", opcode->name);
474
475      format = opcode->format;
476      if (format & (M6811_OP_MASK | M6811_OP_BITMASK
477		    | M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
478	{
479	  (*info->fprintf_func) (info->stream, "\t");
480	}
481
482      /* The movb and movw must be handled in a special way...
483	 The source constant 'ii' is not always at the same place.
484	 This is the same for the destination for the post-indexed byte.
485	 The 'offset' is used to do the appropriate correction.
486
487	 offset          offset
488	 for constant     for destination
489	 movb   18 OB ii hh ll       0          0
490	 18 08 xb ii          1          -1
491	 18 08 xb ff ii       2          1  9 bit
492	 18 08 xb ee ff ii    3          1  16 bit
493	 18 0C hh ll hh ll    0          0
494	 18 09 xb hh ll       1          -1
495	 18 0D xb hh ll       0          0
496	 18 0A xb xb          0          0
497
498	 movw   18 03 jj kk hh ll    0          0
499	 18 00 xb jj kk       1          -1
500	 18 04 hh ll hh ll    0          0
501	 18 01 xb hh ll       1          -1
502	 18 05 xb hh ll       0          0
503	 18 02 xb xb          0          0
504
505	 After the source operand is read, the position 'pos' is incremented
506	 this explains the negative offset for destination.
507
508	 movb/movw above are the only instructions with this matching
509	 format.  */
510      offset = ((format & M6812_OP_IDX_P2)
511		&& (format & (M6811_OP_IMM8 | M6811_OP_IMM16 |
512			      M6811_OP_IND16)));
513
514      if (offset)
515	{
516	  /* Check xb to see position of data.  */
517	  status = read_memory (memaddr + pos, &buffer[0], 1, info);
518	  if (status != 0)
519	    {
520	      return status;
521	    }
522
523	  if (((buffer[0] & 0xe0) == 0xe0) && ((buffer[0] & 0x04) == 0))
524	    {
525	      /* 9 or 16 bit.  */
526	      if ((buffer[0] & 0x02) == 0)
527		{
528		  /* 9 bit.  */
529		  offset = 2;
530		}
531	      else
532		{
533		  /* 16 bit.  */
534		  offset = 3;
535		}
536	    }
537	}
538
539      /* Operand with one more byte: - immediate, offset,
540	 direct-low address.  */
541      if (format &
542	  (M6811_OP_IMM8 | M6811_OP_IX | M6811_OP_IY | M6811_OP_DIRECT))
543	{
544	  status = read_memory (memaddr + pos + offset, &buffer[0], 1, info);
545	  if (status != 0)
546	    return status;
547
548	  /* This movb/movw is special (see above).  */
549	  if (offset < 2)
550	    {
551	      offset = -offset;
552	      pc_dst_offset = 2;
553	    }
554	  else
555	    {
556	      offset = -1;
557	      pc_dst_offset = 5;
558	    }
559	  pos++;
560
561	  if (format & M6811_OP_IMM8)
562	    {
563	      (*info->fprintf_func) (info->stream, "#0x%x", (int) buffer[0]);
564	      format &= ~M6811_OP_IMM8;
565	      /* Set PC destination offset.  */
566	      pc_dst_offset = 1;
567	    }
568	  else if (format & M6811_OP_IX)
569	    {
570	      /* Offsets are in range 0..255, print them unsigned.  */
571	      (*info->fprintf_func) (info->stream, "0x%x,x", buffer[0] & 0x0FF);
572	      format &= ~M6811_OP_IX;
573	    }
574	  else if (format & M6811_OP_IY)
575	    {
576	      (*info->fprintf_func) (info->stream, "0x%x,y", buffer[0] & 0x0FF);
577	      format &= ~M6811_OP_IY;
578	    }
579	  else if (format & M6811_OP_DIRECT)
580	    {
581	      (*info->fprintf_func) (info->stream, "*");
582	      if (info->symtab_size > 0) /* Avoid duplicate 0x. */
583		(*info->fprintf_func) (info->stream, "0x");
584	      (*info->print_address_func) (buffer[0] & 0x0FF, info);
585	      format &= ~M6811_OP_DIRECT;
586	    }
587	}
588
589#define M6812_DST_MOVE  (M6812_OP_IND16_P2 | M6812_OP_IDX_P2)
590#define M6812_INDEXED_FLAGS (M6812_OP_IDX|M6812_OP_IDX_1|M6812_OP_IDX_2)
591      /* Analyze the 68HC12 indexed byte.  */
592      if (format & M6812_INDEXED_FLAGS)
593	{
594	  int indirect;
595	  bfd_vma endaddr;
596
597	  endaddr = memaddr + pos + 1;
598	  if (format & M6811_OP_IND16)
599	    endaddr += 2;
600	  pc_src_offset = -1;
601	  pc_dst_offset = 1;
602	  status = print_indexed_operand (memaddr + pos, info, &indirect,
603					  (format & M6812_DST_MOVE),
604					  pc_src_offset, endaddr, arch);
605	  if (status < 0)
606	    return status;
607
608	  pos += status;
609
610	  /* The indirect addressing mode of the call instruction does
611	     not need the page code.  */
612	  if ((format & M6812_OP_PAGE) && indirect)
613	    format &= ~M6812_OP_PAGE;
614	}
615
616      /* 68HC12 dbcc/ibcc/tbcc operands.  */
617      if ((format & M6812_OP_REG) && (format & M6811_OP_JUMP_REL))
618	{
619	  status = read_memory (memaddr + pos, &buffer[0], 2, info);
620	  if (status != 0)
621	    return status;
622
623	  (*info->fprintf_func) (info->stream, "%s,",
624				 reg_src_table[buffer[0] & 0x07]);
625	  val = buffer[1] - ((buffer[0] & 0x10) << 4);
626
627	  pos += 2;
628	  (*info->fprintf_func) (info->stream, "0x");
629	  (*info->print_address_func) (memaddr + pos + val, info);
630	  format &= ~(M6812_OP_REG | M6811_OP_JUMP_REL);
631	}
632      else if (format & (M6812_OP_REG | M6812_OP_REG_2))
633	{
634	  status = read_memory (memaddr + pos, &buffer[0], 1, info);
635	  if (status != 0)
636	    return status;
637
638	  pos++;
639	  (*info->fprintf_func) (info->stream, "%s,%s",
640				 reg_src_table[(buffer[0] >> 4) & 7],
641				 reg_dst_table[(buffer[0] & 7)]);
642	}
643
644      if (format & (M6811_OP_IMM16 | M6811_OP_IND16))
645	{
646	  bfd_vma addr;
647	  unsigned page = 0;
648
649	  status = read_memory (memaddr + pos + offset, &buffer[0], 2, info);
650	  if (status != 0)
651	    return status;
652
653	  if (format & M6812_OP_IDX_P2)
654	    offset = -2;
655	  else
656	    offset = 0;
657	  pos += 2;
658
659	  addr = val = (buffer[0] << 8) | buffer[1];
660	  pc_dst_offset = 2;
661	  if (format & M6812_OP_PAGE)
662	    {
663	      status = read_memory (memaddr + pos + offset, buffer, 1, info);
664	      if (status != 0)
665		return status;
666
667	      page = buffer[0];
668	      if (addr >= M68HC12_BANK_BASE && addr < 0x0c000)
669		addr = (val - M68HC12_BANK_BASE + (page << M68HC12_BANK_SHIFT)
670			+ M68HC12_BANK_VIRT);
671	    }
672	  else if ((arch & cpu6812)
673		   && addr >= M68HC12_BANK_BASE && addr < 0x0c000)
674	    {
675	      unsigned cur_page;
676	      bfd_vma vaddr;
677
678	      if (memaddr >= M68HC12_BANK_VIRT)
679		cur_page = ((memaddr - M68HC12_BANK_VIRT)
680			    >> M68HC12_BANK_SHIFT);
681	      else
682		cur_page = 0;
683
684	      vaddr = (addr - M68HC12_BANK_BASE
685		       + (cur_page << M68HC12_BANK_SHIFT)) + M68HC12_BANK_VIRT;
686	      if (!info->symbol_at_address_func (addr, info)
687		  && info->symbol_at_address_func (vaddr, info))
688		addr = vaddr;
689	    }
690	  if (format & M6811_OP_IMM16)
691	    {
692	      format &= ~M6811_OP_IMM16;
693	      (*info->fprintf_func) (info->stream, "#");
694	    }
695	  else
696	    {
697	      format &= ~M6811_OP_IND16;
698	    }
699
700	  /* Avoid duplicate 0x from core binutils.  */
701	  if (info->symtab_size > 0)
702	    (*info->fprintf_func) (info->stream, "0x");
703
704	  (*info->print_address_func) (addr, info);
705	  if (format & M6812_OP_PAGE)
706	    {
707	      (* info->fprintf_func) (info->stream, " {");
708	      /* Avoid duplicate 0x from core binutils.  */
709	      if (info->symtab_size > 0)
710		(*info->fprintf_func) (info->stream, "0x");
711	      (* info->print_address_func) (val, info);
712	      (* info->fprintf_func) (info->stream, ", 0x%x}", page);
713	      format &= ~M6812_OP_PAGE;
714	      pos += 1;
715	    }
716	}
717
718      if (format & M6812_OP_IDX_P2)
719	{
720	  (*info->fprintf_func) (info->stream, ", ");
721	  status = print_indexed_operand (memaddr + pos + offset, info,
722					  0, 1, pc_dst_offset,
723					  memaddr + pos + offset + 1, arch);
724	  if (status < 0)
725	    return status;
726	  pos += status;
727	}
728
729      if (format & M6812_OP_IND16_P2)
730	{
731	  (*info->fprintf_func) (info->stream, ", ");
732
733	  status = read_memory (memaddr + pos + offset, &buffer[0], 2, info);
734	  if (status != 0)
735	    return status;
736
737	  pos += 2;
738
739	  val = (buffer[0] << 8) | buffer[1];
740	  /* Avoid duplicate 0x from core binutils.  */
741	  if (info->symtab_size > 0)
742	    (*info->fprintf_func) (info->stream, "0x");
743	  (*info->print_address_func) (val, info);
744	}
745
746      /* M6811_OP_BITMASK and M6811_OP_JUMP_REL must be treated separately
747	 and in that order.  The brset/brclr insn have a bitmask and then
748	 a relative branch offset.  */
749      if (format & M6811_OP_BITMASK)
750	{
751	  status = read_memory (memaddr + pos, &buffer[0], 1, info);
752	  if (status != 0)
753	    return status;
754
755	  pos++;
756	  (*info->fprintf_func) (info->stream, ", #0x%02x%s",
757				 buffer[0] & 0x0FF,
758				 (format & M6811_OP_JUMP_REL ? ", " : ""));
759	  format &= ~M6811_OP_BITMASK;
760	}
761      if (format & M6811_OP_JUMP_REL)
762	{
763	  status = read_memory (memaddr + pos, &buffer[0], 1, info);
764	  if (status != 0)
765	    return status;
766
767	  (*info->fprintf_func) (info->stream, "0x");
768	  pos++;
769	  val = (buffer[0] ^ 0x80) - 0x80;
770	  (*info->print_address_func) (memaddr + pos + val, info);
771	  format &= ~M6811_OP_JUMP_REL;
772	}
773      else if (format & M6812_OP_JUMP_REL16)
774	{
775	  status = read_memory (memaddr + pos, &buffer[0], 2, info);
776	  if (status != 0)
777	    return status;
778
779	  pos += 2;
780	  val = (((buffer[0] << 8) | buffer[1]) ^ 0x8000) - 0x8000;
781
782	  (*info->fprintf_func) (info->stream, "0x");
783	  (*info->print_address_func) (memaddr + pos + val, info);
784	  format &= ~M6812_OP_JUMP_REL16;
785	}
786
787      if (format & M6812_OP_PAGE)
788	{
789	  status = read_memory (memaddr + pos + offset, &buffer[0], 1, info);
790	  if (status != 0)
791	    return status;
792
793	  pos += 1;
794
795	  val = buffer[0];
796	  (*info->fprintf_func) (info->stream, ", 0x%x", (unsigned) val);
797	}
798
799#ifdef DEBUG
800      /* Consistency check.  'format' must be 0, so that we have handled
801	 all formats; and the computed size of the insn must match the
802	 opcode table content.  */
803      if (format & ~(M6811_OP_PAGE4 | M6811_OP_PAGE3 | M6811_OP_PAGE2))
804	(*info->fprintf_func) (info->stream, "; Error, format: %lx", format);
805
806      if (pos != opcode->size)
807	(*info->fprintf_func) (info->stream, "; Error, size: %ld expect %d",
808			       pos, opcode->size);
809#endif
810      return pos;
811    }
812
813  /* Opcode not recognized.  */
814  if (format == M6811_OP_PAGE2 && arch & cpu6812
815      && ((code >= 0x30 && code <= 0x39) || (code >= 0x40)))
816    (*info->fprintf_func) (info->stream, "trap\t#0x%02x", code & 0x0ff);
817
818  else if (format == M6811_OP_PAGE2)
819    (*info->fprintf_func) (info->stream, ".byte\t0x%02x, 0x%02x",
820			   M6811_OPCODE_PAGE2, code);
821  else if (format == M6811_OP_PAGE3)
822    (*info->fprintf_func) (info->stream, ".byte\t0x%02x, 0x%02x",
823			   M6811_OPCODE_PAGE3, code);
824  else if (format == M6811_OP_PAGE4)
825    (*info->fprintf_func) (info->stream, ".byte\t0x%02x, 0x%02x",
826			   M6811_OPCODE_PAGE4, code);
827  else
828    (*info->fprintf_func) (info->stream, ".byte\t0x%02x", code);
829
830  return pos;
831}
832
833/* Disassemble one instruction at address 'memaddr'.  Returns the number
834   of bytes used by that instruction.  */
835int
836print_insn_m68hc11 (bfd_vma memaddr, struct disassemble_info* info)
837{
838  return print_insn (memaddr, info, cpu6811);
839}
840
841int
842print_insn_m68hc12 (bfd_vma memaddr, struct disassemble_info* info)
843{
844  return print_insn (memaddr, info, cpu6812);
845}
846
847int
848print_insn_m9s12x (bfd_vma memaddr, struct disassemble_info* info)
849{
850  return print_insn (memaddr, info, cpu6812|cpu9s12x);
851}
852
853int
854print_insn_m9s12xg (bfd_vma memaddr, struct disassemble_info* info)
855{
856  return print_insn (memaddr, info, cpuxgate);
857}
858