1 /* m68hc11-dis.c -- Motorola 68HC11 & 68HC12 disassembly
2    Copyright 1999, 2000 Free Software Foundation, Inc.
3    Written by Stephane Carrez (stcarrez@worldnet.fr)
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18 
19 #include <stdio.h>
20 
21 #include "ansidecl.h"
22 #include "opcode/m68hc11.h"
23 #include "dis-asm.h"
24 
25 static const char *const reg_name[] = {
26   "X", "Y", "SP", "PC"
27 };
28 
29 static const char *const reg_src_table[] = {
30   "A", "B", "CCR", "TMP3", "D", "X", "Y", "SP"
31 };
32 
33 static const char *const reg_dst_table[] = {
34   "A", "B", "CCR", "TMP2", "D", "X", "Y", "SP"
35 };
36 
37 #define OP_PAGE_MASK (M6811_OP_PAGE2|M6811_OP_PAGE3|M6811_OP_PAGE4)
38 
39 static int
40 read_memory (memaddr, buffer, size, info)
41      bfd_vma memaddr;
42      bfd_byte *buffer;
43      int size;
44      struct disassemble_info *info;
45 {
46   int status;
47 
48   /* Get first byte.  Only one at a time because we don't know the
49      size of the insn.  */
50   status = (*info->read_memory_func) (memaddr, buffer, size, info);
51   if (status != 0)
52     {
53       (*info->memory_error_func) (status, memaddr, info);
54       return -1;
55     }
56   return 0;
57 }
58 
59 
60 /* Read the 68HC12 indexed operand byte and print the corresponding mode.
61    Returns the number of bytes read or -1 if failure.  */
62 static int
63 print_indexed_operand (memaddr, info, mov_insn)
64      bfd_vma memaddr;
65      struct disassemble_info *info;
66      int mov_insn;
67 {
68   bfd_byte buffer[4];
69   int reg;
70   int status;
71   short sval;
72   int pos = 1;
73 
74   status = read_memory (memaddr, &buffer[0], 1, info);
75   if (status != 0)
76     {
77       return status;
78     }
79 
80   /* n,r with 5-bits signed constant.  */
81   if ((buffer[0] & 0x20) == 0)
82     {
83       reg = (buffer[0] >> 6) & 3;
84       sval = (buffer[0] & 0x1f);
85       if (sval & 0x10)
86 	sval |= 0xfff0;
87       (*info->fprintf_func) (info->stream, "%d,%s",
88 			     (int) sval, reg_name[reg]);
89     }
90 
91   /* Auto pre/post increment/decrement.  */
92   else if ((buffer[0] & 0xc0) != 0xc0)
93     {
94       const char *mode;
95 
96       reg = (buffer[0] >> 6) & 3;
97       sval = (buffer[0] & 0x0f);
98       if (sval & 0x8)
99 	{
100 	  sval |= 0xfff0;
101 	  sval = -sval;
102 	  mode = "-";
103 	}
104       else
105 	{
106 	  sval = sval + 1;
107 	  mode = "+";
108 	}
109       (*info->fprintf_func) (info->stream, "%d,%s%s%s",
110 			     (int) sval,
111 			     (buffer[0] & 0x10 ? "" : mode),
112 			     reg_name[reg], (buffer[0] & 0x10 ? mode : ""));
113     }
114 
115   /* [n,r] 16-bits offset indexed indirect.  */
116   else if ((buffer[0] & 0x07) == 3)
117     {
118       if (mov_insn)
119 	{
120 	  (*info->fprintf_func) (info->stream, "<invalid op: 0x%x>",
121 				 buffer[0] & 0x0ff);
122 	  return 0;
123 	}
124       reg = (buffer[0] >> 3) & 0x03;
125       status = read_memory (memaddr + pos, &buffer[0], 2, info);
126       if (status != 0)
127 	{
128 	  return status;
129 	}
130 
131       pos += 2;
132       sval = ((buffer[0] << 8) | (buffer[1] & 0x0FF));
133       (*info->fprintf_func) (info->stream, "[%u,%s]",
134 			     sval & 0x0ffff, reg_name[reg]);
135     }
136   else if ((buffer[0] & 0x4) == 0)
137     {
138       if (mov_insn)
139 	{
140 	  (*info->fprintf_func) (info->stream, "<invalid op: 0x%x>",
141 				 buffer[0] & 0x0ff);
142 	  return 0;
143 	}
144       reg = (buffer[0] >> 3) & 0x03;
145       status = read_memory (memaddr + pos,
146 			    &buffer[1], (buffer[0] & 0x2 ? 2 : 1), info);
147       if (status != 0)
148 	{
149 	  return status;
150 	}
151       if (buffer[0] & 2)
152 	{
153 	  sval = ((buffer[1] << 8) | (buffer[2] & 0x0FF));
154 	  sval &= 0x0FFFF;
155 	  pos += 2;
156 	}
157       else
158 	{
159 	  sval = buffer[1] & 0x00ff;
160 	  if (buffer[0] & 0x01)
161 	    sval |= 0xff00;
162 	  pos++;
163 	}
164       (*info->fprintf_func) (info->stream, "%d,%s",
165 			     (int) sval, reg_name[reg]);
166     }
167   else
168     {
169       reg = (buffer[0] >> 3) & 0x03;
170       switch (buffer[0] & 3)
171 	{
172 	case 0:
173 	  (*info->fprintf_func) (info->stream, "A,%s", reg_name[reg]);
174 	  break;
175 	case 1:
176 	  (*info->fprintf_func) (info->stream, "B,%s", reg_name[reg]);
177 	  break;
178 	case 2:
179 	  (*info->fprintf_func) (info->stream, "D,%s", reg_name[reg]);
180 	  break;
181 	case 3:
182 	default:
183 	  (*info->fprintf_func) (info->stream, "[D,%s]", reg_name[reg]);
184 	  break;
185 	}
186     }
187 
188   return pos;
189 }
190 
191 /* Disassemble one instruction at address 'memaddr'.  Returns the number
192    of bytes used by that instruction.  */
193 static int
194 print_insn (memaddr, info, arch)
195      bfd_vma memaddr;
196      struct disassemble_info *info;
197      int arch;
198 {
199   int status;
200   bfd_byte buffer[4];
201   unsigned char code;
202   long format, pos, i;
203   short sval;
204   const struct m68hc11_opcode *opcode;
205 
206   /* Get first byte.  Only one at a time because we don't know the
207      size of the insn.  */
208   status = read_memory (memaddr, buffer, 1, info);
209   if (status != 0)
210     {
211       return status;
212     }
213 
214   format = 0;
215   code = buffer[0];
216   pos = 0;
217 
218   /* Look for page2,3,4 opcodes.  */
219   if (code == M6811_OPCODE_PAGE2)
220     {
221       pos++;
222       format = M6811_OP_PAGE2;
223     }
224   else if (code == M6811_OPCODE_PAGE3 && arch == cpu6811)
225     {
226       pos++;
227       format = M6811_OP_PAGE3;
228     }
229   else if (code == M6811_OPCODE_PAGE4 && arch == cpu6811)
230     {
231       pos++;
232       format = M6811_OP_PAGE4;
233     }
234 
235   /* We are in page2,3,4; get the real opcode.  */
236   if (pos == 1)
237     {
238       status = read_memory (memaddr + pos, &buffer[1], 1, info);
239       if (status != 0)
240 	{
241 	  return status;
242 	}
243       code = buffer[1];
244     }
245 
246 
247   /* Look first for a 68HC12 alias.  All of them are 2-bytes long and
248      in page 1.  There is no operand to print.  We read the second byte
249      only when we have a possible match.  */
250   if ((arch & cpu6812) && format == 0)
251     {
252       int must_read = 1;
253 
254       /* Walk the alias table to find a code1+code2 match.  */
255       for (i = 0; i < m68hc12_num_alias; i++)
256 	{
257 	  if (m68hc12_alias[i].code1 == code)
258 	    {
259 	      if (must_read)
260 		{
261 		  status = read_memory (memaddr + pos + 1,
262 					&buffer[1], 1, info);
263 		  if (status != 0)
264 		    break;
265 
266 		  must_read = 1;
267 		}
268 	      if (m68hc12_alias[i].code2 == (unsigned char) buffer[1])
269 		{
270 		  (*info->fprintf_func) (info->stream, "%s",
271 					 m68hc12_alias[i].name);
272 		  return 2;
273 		}
274 	    }
275 	}
276     }
277 
278   pos++;
279 
280   /* Scan the opcode table until we find the opcode
281      with the corresponding page.  */
282   opcode = m68hc11_opcodes;
283   for (i = 0; i < m68hc11_num_opcodes; i++, opcode++)
284     {
285       int offset;
286 
287       if ((opcode->arch & arch) == 0)
288 	continue;
289       if (opcode->opcode != code)
290 	continue;
291       if ((opcode->format & OP_PAGE_MASK) != format)
292 	continue;
293 
294       if (opcode->format & M6812_OP_REG)
295 	{
296 	  int j;
297 	  int is_jump;
298 
299 	  if (opcode->format & M6811_OP_JUMP_REL)
300 	    is_jump = 1;
301 	  else
302 	    is_jump = 0;
303 
304 	  status = read_memory (memaddr + pos, &buffer[0], 1, info);
305 	  if (status != 0)
306 	    {
307 	      return status;
308 	    }
309 	  for (j = 0; i + j < m68hc11_num_opcodes; j++)
310 	    {
311 	      if ((opcode[j].arch & arch) == 0)
312 		continue;
313 	      if (opcode[j].opcode != code)
314 		continue;
315 	      if (is_jump)
316 		{
317 		  if (!(opcode[j].format & M6811_OP_JUMP_REL))
318 		    continue;
319 
320 		  if ((opcode[j].format & M6812_OP_IBCC_MARKER)
321 		      && (buffer[0] & 0xc0) != 0x80)
322 		    continue;
323 		  if ((opcode[j].format & M6812_OP_TBCC_MARKER)
324 		      && (buffer[0] & 0xc0) != 0x40)
325 		    continue;
326 		  if ((opcode[j].format & M6812_OP_DBCC_MARKER)
327 		      && (buffer[0] & 0xc0) != 0)
328 		    continue;
329 		  if ((opcode[j].format & M6812_OP_EQ_MARKER)
330 		      && (buffer[0] & 0x20) == 0)
331 		    break;
332 		  if (!(opcode[j].format & M6812_OP_EQ_MARKER)
333 		      && (buffer[0] & 0x20) != 0)
334 		    break;
335 		  continue;
336 		}
337 	      if (opcode[j].format & M6812_OP_EXG_MARKER && buffer[0] & 0x80)
338 		break;
339 	      if ((opcode[j].format & M6812_OP_SEX_MARKER)
340 		  && (((buffer[0] & 0x07) >= 3 && (buffer[0] & 7) <= 7))
341 		  && ((buffer[0] & 0x0f0) <= 0x20))
342 		break;
343 	      if (opcode[j].format & M6812_OP_TFR_MARKER
344 		  && !(buffer[0] & 0x80))
345 		break;
346 	    }
347 	  if (i + j < m68hc11_num_opcodes)
348 	    opcode = &opcode[j];
349 	}
350 
351       /* We have found the opcode.  Extract the operand and print it.  */
352       (*info->fprintf_func) (info->stream, "%s", opcode->name);
353 
354       format = opcode->format;
355       if (format & (M6811_OP_MASK | M6811_OP_BITMASK
356 		    | M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
357 	{
358 	  (*info->fprintf_func) (info->stream, "\t");
359 	}
360 
361       /* The movb and movw must be handled in a special way...  */
362       offset = 0;
363       if (format & (M6812_OP_IDX_P2 | M6812_OP_IND16_P2))
364 	{
365 	  if ((format & M6812_OP_IDX_P2)
366 	      && (format & (M6811_OP_IMM8 | M6811_OP_IMM16 | M6811_OP_IND16)))
367 	    offset = 1;
368 	}
369 
370       /* Operand with one more byte: - immediate, offset,
371          direct-low address.  */
372       if (format &
373 	  (M6811_OP_IMM8 | M6811_OP_IX | M6811_OP_IY | M6811_OP_DIRECT))
374 	{
375 	  status = read_memory (memaddr + pos + offset, &buffer[0], 1, info);
376 	  if (status != 0)
377 	    {
378 	      return status;
379 	    }
380 
381 	  pos++;
382 	  offset = -1;
383 	  if (format & M6811_OP_IMM8)
384 	    {
385 	      (*info->fprintf_func) (info->stream, "#%d", (int) buffer[0]);
386 	      format &= ~M6811_OP_IMM8;
387 	    }
388 	  else if (format & M6811_OP_IX)
389 	    {
390 	      /* Offsets are in range 0..255, print them unsigned.  */
391 	      (*info->fprintf_func) (info->stream, "%u,x", buffer[0] & 0x0FF);
392 	      format &= ~M6811_OP_IX;
393 	    }
394 	  else if (format & M6811_OP_IY)
395 	    {
396 	      (*info->fprintf_func) (info->stream, "%u,y", buffer[0] & 0x0FF);
397 	      format &= ~M6811_OP_IY;
398 	    }
399 	  else if (format & M6811_OP_DIRECT)
400 	    {
401 	      (*info->fprintf_func) (info->stream, "*");
402 	      (*info->print_address_func) (buffer[0] & 0x0FF, info);
403 	      format &= ~M6811_OP_DIRECT;
404 	    }
405 	}
406 
407 #define M6812_INDEXED_FLAGS (M6812_OP_IDX|M6812_OP_IDX_1|M6812_OP_IDX_2)
408       /* Analyze the 68HC12 indexed byte.  */
409       if (format & M6812_INDEXED_FLAGS)
410 	{
411 	  status = print_indexed_operand (memaddr + pos, info, 0);
412 	  if (status < 0)
413 	    {
414 	      return status;
415 	    }
416 	  pos += status;
417 	}
418 
419       /* 68HC12 dbcc/ibcc/tbcc operands.  */
420       if ((format & M6812_OP_REG) && (format & M6811_OP_JUMP_REL))
421 	{
422 	  status = read_memory (memaddr + pos, &buffer[0], 2, info);
423 	  if (status != 0)
424 	    {
425 	      return status;
426 	    }
427 	  (*info->fprintf_func) (info->stream, "%s,",
428 				 reg_src_table[buffer[0] & 0x07]);
429 	  sval = buffer[1] & 0x0ff;
430 	  if (buffer[0] & 0x10)
431 	    sval |= 0xff00;
432 
433 	  pos += 2;
434 	  (*info->print_address_func) (memaddr + pos + sval, info);
435 	  format &= ~(M6812_OP_REG | M6811_OP_JUMP_REL);
436 	}
437       else if (format & (M6812_OP_REG | M6812_OP_REG_2))
438 	{
439 	  status = read_memory (memaddr + pos, &buffer[0], 1, info);
440 	  if (status != 0)
441 	    {
442 	      return status;
443 	    }
444 
445 	  pos++;
446 	  (*info->fprintf_func) (info->stream, "%s,%s",
447 				 reg_src_table[(buffer[0] >> 4) & 7],
448 				 reg_dst_table[(buffer[0] & 7)]);
449 	}
450 
451       /* M6811_OP_BITMASK and M6811_OP_JUMP_REL must be treated separately
452          and in that order.  The brset/brclr insn have a bitmask and then
453          a relative branch offset.  */
454       if (format & M6811_OP_BITMASK)
455 	{
456 	  status = read_memory (memaddr + pos, &buffer[0], 1, info);
457 	  if (status != 0)
458 	    {
459 	      return status;
460 	    }
461 	  pos++;
462 	  (*info->fprintf_func) (info->stream, " #$%02x%s",
463 				 buffer[0] & 0x0FF,
464 				 (format & M6811_OP_JUMP_REL ? " " : ""));
465 	  format &= ~M6811_OP_BITMASK;
466 	}
467       if (format & M6811_OP_JUMP_REL)
468 	{
469 	  int val;
470 
471 	  status = read_memory (memaddr + pos, &buffer[0], 1, info);
472 	  if (status != 0)
473 	    {
474 	      return status;
475 	    }
476 
477 	  pos++;
478 	  val = (buffer[0] & 0x80) ? buffer[0] | 0xFFFFFF00 : buffer[0];
479 	  (*info->print_address_func) (memaddr + pos + val, info);
480 	  format &= ~M6811_OP_JUMP_REL;
481 	}
482       else if (format & M6812_OP_JUMP_REL16)
483 	{
484 	  int val;
485 
486 	  status = read_memory (memaddr + pos, &buffer[0], 2, info);
487 	  if (status != 0)
488 	    {
489 	      return status;
490 	    }
491 
492 	  pos += 2;
493 	  val = ((buffer[0] << 8) | (buffer[1] & 0x0FF));
494 	  if (val & 0x8000)
495 	    val |= 0xffff0000;
496 
497 	  (*info->print_address_func) (memaddr + pos + val, info);
498 	  format &= ~M6812_OP_JUMP_REL16;
499 	}
500       if (format & (M6811_OP_IMM16 | M6811_OP_IND16))
501 	{
502 	  int val;
503 
504 	  status = read_memory (memaddr + pos + offset, &buffer[0], 2, info);
505 	  if (status != 0)
506 	    {
507 	      return status;
508 	    }
509 	  if (format & M6812_OP_IDX_P2)
510 	    offset = -2;
511 	  else
512 	    offset = 0;
513 	  pos += 2;
514 
515 	  val = ((buffer[0] << 8) | (buffer[1] & 0x0FF));
516 	  val &= 0x0FFFF;
517 	  if (format & M6811_OP_IMM16)
518 	    {
519 	      format &= ~M6811_OP_IMM16;
520 	      (*info->fprintf_func) (info->stream, "#");
521 	    }
522 	  else
523 	    format &= ~M6811_OP_IND16;
524 
525 	  (*info->print_address_func) (val, info);
526 	}
527 
528       if (format & M6812_OP_IDX_P2)
529 	{
530 	  (*info->fprintf_func) (info->stream, ", ");
531 	  status = print_indexed_operand (memaddr + pos + offset, info, 1);
532 	  if (status < 0)
533 	    return status;
534 	  pos += status;
535 	}
536 
537       if (format & M6812_OP_IND16_P2)
538 	{
539 	  int val;
540 
541 	  (*info->fprintf_func) (info->stream, ", ");
542 
543 	  status = read_memory (memaddr + pos + offset, &buffer[0], 2, info);
544 	  if (status != 0)
545 	    {
546 	      return status;
547 	    }
548 	  pos += 2;
549 
550 	  val = ((buffer[0] << 8) | (buffer[1] & 0x0FF));
551 	  val &= 0x0FFFF;
552 	  (*info->print_address_func) (val, info);
553 	}
554 
555 #ifdef DEBUG
556       /* Consistency check.  'format' must be 0, so that we have handled
557          all formats; and the computed size of the insn must match the
558          opcode table content.  */
559       if (format & ~(M6811_OP_PAGE4 | M6811_OP_PAGE3 | M6811_OP_PAGE2))
560 	{
561 	  (*info->fprintf_func) (info->stream, "; Error, format: %x", format);
562 	}
563       if (pos != opcode->size)
564 	{
565 	  (*info->fprintf_func) (info->stream, "; Error, size: %d expect %d",
566 				 pos, opcode->size);
567 	}
568 #endif
569       return pos;
570     }
571 
572   /* Opcode not recognized.  */
573   if (format == M6811_OP_PAGE2 && arch & cpu6812
574       && ((code >= 0x30 && code <= 0x39) || (code >= 0x40 && code <= 0xff)))
575     (*info->fprintf_func) (info->stream, "trap\t#%d", code & 0x0ff);
576 
577   else if (format == M6811_OP_PAGE2)
578     (*info->fprintf_func) (info->stream, ".byte\t0x%02x, 0x%02x",
579 			   M6811_OPCODE_PAGE2, code);
580   else if (format == M6811_OP_PAGE3)
581     (*info->fprintf_func) (info->stream, ".byte\t0x%02x, 0x%02x",
582 			   M6811_OPCODE_PAGE3, code);
583   else if (format == M6811_OP_PAGE4)
584     (*info->fprintf_func) (info->stream, ".byte\t0x%02x, 0x%02x",
585 			   M6811_OPCODE_PAGE4, code);
586   else
587     (*info->fprintf_func) (info->stream, ".byte\t0x%02x", code);
588 
589   return pos;
590 }
591 
592 /* Disassemble one instruction at address 'memaddr'.  Returns the number
593    of bytes used by that instruction.  */
594 int
595 print_insn_m68hc11 (memaddr, info)
596      bfd_vma memaddr;
597      struct disassemble_info *info;
598 {
599   return print_insn (memaddr, info, cpu6811);
600 }
601 
602 int
603 print_insn_m68hc12 (memaddr, info)
604      bfd_vma memaddr;
605      struct disassemble_info *info;
606 {
607   return print_insn (memaddr, info, cpu6812);
608 }
609