xref: /netbsd-src/external/gpl3/binutils/dist/opcodes/s12z-dis.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
1c1a20988Schristos /* s12z-dis.c -- Freescale S12Z disassembly
2*cb63e24eSchristos    Copyright (C) 2018-2024 Free Software Foundation, Inc.
3c1a20988Schristos 
4c1a20988Schristos    This file is part of the GNU opcodes library.
5c1a20988Schristos 
6c1a20988Schristos    This library is free software; you can redistribute it and/or modify
7c1a20988Schristos    it under the terms of the GNU General Public License as published by
8c1a20988Schristos    the Free Software Foundation; either version 3, or (at your option)
9c1a20988Schristos    any later version.
10c1a20988Schristos 
11c1a20988Schristos    It is distributed in the hope that it will be useful, but WITHOUT
12c1a20988Schristos    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13c1a20988Schristos    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
14c1a20988Schristos    License for more details.
15c1a20988Schristos 
16c1a20988Schristos    You should have received a copy of the GNU General Public License
17c1a20988Schristos    along with this program; if not, write to the Free Software
18c1a20988Schristos    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
19c1a20988Schristos    MA 02110-1301, USA.  */
20c1a20988Schristos 
21c1a20988Schristos #include "sysdep.h"
22c1a20988Schristos #include <stdio.h>
234f645668Schristos #include <stdint.h>
24c1a20988Schristos #include <stdbool.h>
25c1a20988Schristos #include <assert.h>
26c1a20988Schristos 
276f4ced0bSchristos #include "opcode/s12z.h"
28c1a20988Schristos #include "bfd.h"
29c1a20988Schristos #include "dis-asm.h"
30c1a20988Schristos #include "disassemble.h"
316f4ced0bSchristos #include "s12z-opc.h"
326f4ced0bSchristos #include "opintl.h"
336f4ced0bSchristos 
346f4ced0bSchristos struct mem_read_abstraction
356f4ced0bSchristos {
366f4ced0bSchristos   struct mem_read_abstraction_base base;
376f4ced0bSchristos   bfd_vma memaddr;
386f4ced0bSchristos   struct disassemble_info* info;
396f4ced0bSchristos };
406f4ced0bSchristos 
416f4ced0bSchristos static void
advance(struct mem_read_abstraction_base * b)426f4ced0bSchristos advance (struct mem_read_abstraction_base *b)
436f4ced0bSchristos {
446f4ced0bSchristos   struct mem_read_abstraction *mra = (struct mem_read_abstraction *) b;
456f4ced0bSchristos   mra->memaddr ++;
466f4ced0bSchristos }
476f4ced0bSchristos 
486f4ced0bSchristos static bfd_vma
posn(struct mem_read_abstraction_base * b)496f4ced0bSchristos posn (struct mem_read_abstraction_base *b)
506f4ced0bSchristos {
516f4ced0bSchristos   struct mem_read_abstraction *mra = (struct mem_read_abstraction *) b;
526f4ced0bSchristos   return mra->memaddr;
536f4ced0bSchristos }
54c1a20988Schristos 
55c1a20988Schristos static int
abstract_read_memory(struct mem_read_abstraction_base * b,int offset,size_t n,bfd_byte * bytes)566f4ced0bSchristos abstract_read_memory (struct mem_read_abstraction_base *b,
576f4ced0bSchristos 		      int offset,
586f4ced0bSchristos 		      size_t n, bfd_byte *bytes)
59c1a20988Schristos {
606f4ced0bSchristos   struct mem_read_abstraction *mra = (struct mem_read_abstraction *) b;
616f4ced0bSchristos 
624f645668Schristos   int status = (*mra->info->read_memory_func) (mra->memaddr + offset,
636f4ced0bSchristos 					       bytes, n, mra->info);
64c1a20988Schristos   if (status != 0)
654f645668Schristos     (*mra->info->memory_error_func) (status, mra->memaddr + offset,
664f645668Schristos                                      mra->info);
674f645668Schristos   return status != 0 ? -1 : 0;
68c1a20988Schristos }
69c1a20988Schristos 
706f4ced0bSchristos /* Start of disassembly file.  */
71c1a20988Schristos const struct reg registers[S12Z_N_REGISTERS] =
72c1a20988Schristos   {
73c1a20988Schristos     {"d2", 2},
74c1a20988Schristos     {"d3", 2},
75c1a20988Schristos     {"d4", 2},
76c1a20988Schristos     {"d5", 2},
77c1a20988Schristos 
78c1a20988Schristos     {"d0", 1},
79c1a20988Schristos     {"d1", 1},
80c1a20988Schristos 
81c1a20988Schristos     {"d6", 4},
82c1a20988Schristos     {"d7", 4},
83c1a20988Schristos 
84c1a20988Schristos     {"x", 3},
85c1a20988Schristos     {"y", 3},
86c1a20988Schristos     {"s", 3},
87c1a20988Schristos     {"p", 3},
88c1a20988Schristos     {"cch", 1},
89c1a20988Schristos     {"ccl", 1},
90c1a20988Schristos     {"ccw", 2}
91c1a20988Schristos   };
92c1a20988Schristos 
936f4ced0bSchristos static const char *mnemonics[] =
94c1a20988Schristos   {
956f4ced0bSchristos     "!!invalid!!",
966f4ced0bSchristos     "psh",
976f4ced0bSchristos     "pul",
986f4ced0bSchristos     "tbne", "tbeq", "tbpl", "tbmi", "tbgt", "tble",
996f4ced0bSchristos     "dbne", "dbeq", "dbpl", "dbmi", "dbgt", "dble",
1006f4ced0bSchristos     "sex",
1016f4ced0bSchristos     "exg",
1026f4ced0bSchristos     "lsl", "lsr",
1036f4ced0bSchristos     "asl", "asr",
1046f4ced0bSchristos     "rol", "ror",
1056f4ced0bSchristos     "bfins", "bfext",
106c1a20988Schristos 
1076f4ced0bSchristos     "trap",
108c1a20988Schristos 
1096f4ced0bSchristos     "ld",
1106f4ced0bSchristos     "st",
1116f4ced0bSchristos     "cmp",
1126f4ced0bSchristos 
1136f4ced0bSchristos     "stop",
1146f4ced0bSchristos     "wai",
1156f4ced0bSchristos     "sys",
1166f4ced0bSchristos 
1176f4ced0bSchristos     "minu",
1186f4ced0bSchristos     "mins",
1196f4ced0bSchristos     "maxu",
1206f4ced0bSchristos     "maxs",
1216f4ced0bSchristos 
1226f4ced0bSchristos     "abs",
1236f4ced0bSchristos     "adc",
1246f4ced0bSchristos     "bit",
1256f4ced0bSchristos     "sbc",
1266f4ced0bSchristos     "rti",
1276f4ced0bSchristos     "clb",
1286f4ced0bSchristos     "eor",
1296f4ced0bSchristos 
1306f4ced0bSchristos     "sat",
1316f4ced0bSchristos 
1326f4ced0bSchristos     "nop",
1336f4ced0bSchristos     "bgnd",
1346f4ced0bSchristos     "brclr",
1356f4ced0bSchristos     "brset",
1366f4ced0bSchristos     "rts",
1376f4ced0bSchristos     "lea",
1386f4ced0bSchristos     "mov",
1396f4ced0bSchristos 
1406f4ced0bSchristos     "bra",
1416f4ced0bSchristos     "bsr",
1426f4ced0bSchristos     "bhi",
1436f4ced0bSchristos     "bls",
1446f4ced0bSchristos     "bcc",
1456f4ced0bSchristos     "bcs",
1466f4ced0bSchristos     "bne",
1476f4ced0bSchristos     "beq",
1486f4ced0bSchristos     "bvc",
1496f4ced0bSchristos     "bvs",
1506f4ced0bSchristos     "bpl",
1516f4ced0bSchristos     "bmi",
1526f4ced0bSchristos     "bge",
1536f4ced0bSchristos     "blt",
1546f4ced0bSchristos     "bgt",
1556f4ced0bSchristos     "ble",
1566f4ced0bSchristos     "inc",
1576f4ced0bSchristos     "clr",
1586f4ced0bSchristos     "dec",
1596f4ced0bSchristos 
1606f4ced0bSchristos     "add",
1616f4ced0bSchristos     "sub",
1626f4ced0bSchristos     "and",
1636f4ced0bSchristos     "or",
1646f4ced0bSchristos 
1656f4ced0bSchristos     "tfr",
1666f4ced0bSchristos     "jmp",
1676f4ced0bSchristos     "jsr",
1686f4ced0bSchristos     "com",
1696f4ced0bSchristos     "andcc",
1706f4ced0bSchristos     "neg",
1716f4ced0bSchristos     "orcc",
1726f4ced0bSchristos     "bclr",
1736f4ced0bSchristos     "bset",
1746f4ced0bSchristos     "btgl",
1756f4ced0bSchristos     "swi",
1766f4ced0bSchristos 
1776f4ced0bSchristos     "mulu",
1786f4ced0bSchristos     "divu",
1796f4ced0bSchristos     "modu",
1806f4ced0bSchristos     "macu",
1816f4ced0bSchristos     "qmulu",
1826f4ced0bSchristos 
1836f4ced0bSchristos     "muls",
1846f4ced0bSchristos     "divs",
1856f4ced0bSchristos     "mods",
1866f4ced0bSchristos     "macs",
1876f4ced0bSchristos     "qmuls",
1886f4ced0bSchristos 
1896f4ced0bSchristos     NULL
1906f4ced0bSchristos   };
1916f4ced0bSchristos 
1926f4ced0bSchristos 
193c1a20988Schristos static void
operand_separator(struct disassemble_info * info)1946f4ced0bSchristos operand_separator (struct disassemble_info *info)
195c1a20988Schristos {
1966f4ced0bSchristos   if ((info->flags & 0x2))
1976f4ced0bSchristos     (*info->fprintf_func) (info->stream, ",");
1986f4ced0bSchristos 
1996f4ced0bSchristos   (*info->fprintf_func) (info->stream, " ");
2006f4ced0bSchristos 
2016f4ced0bSchristos   info->flags |= 0x2;
2026f4ced0bSchristos }
2036f4ced0bSchristos 
2046f4ced0bSchristos /* Render the symbol name whose value is ADDR + BASE or the adddress itself if
2056f4ced0bSchristos    there is no symbol.  If BASE is non zero, then the a PC relative adddress is
2066f4ced0bSchristos    assumend (ie BASE is the value in the PC.  */
2076f4ced0bSchristos static void
decode_possible_symbol(bfd_signed_vma addr,bfd_vma base,struct disassemble_info * info,bool relative)208*cb63e24eSchristos decode_possible_symbol (bfd_signed_vma addr, bfd_vma base,
2096f4ced0bSchristos                         struct disassemble_info *info, bool relative)
210c1a20988Schristos {
211*cb63e24eSchristos   const char *fmt = relative ? "*%+" PRId64 : "%" PRId64;
2124f645668Schristos   asymbol *sym = info->symbol_at_address_func (addr + base, info);
2134f645668Schristos 
2144f645668Schristos   if (!sym)
215*cb63e24eSchristos     (*info->fprintf_func) (info->stream, fmt, (int64_t) addr);
216c1a20988Schristos   else
217c1a20988Schristos     (*info->fprintf_func) (info->stream, "%s", bfd_asymbol_name (sym));
2186f4ced0bSchristos }
219c1a20988Schristos 
2206f4ced0bSchristos 
2216f4ced0bSchristos /* Emit the disassembled text for OPR */
2226f4ced0bSchristos static void
opr_emit_disassembly(const struct operand * opr,struct disassemble_info * info)2236f4ced0bSchristos opr_emit_disassembly (const struct operand *opr,
2246f4ced0bSchristos 		      struct disassemble_info *info)
2256f4ced0bSchristos {
2266f4ced0bSchristos   operand_separator (info);
2276f4ced0bSchristos 
2286f4ced0bSchristos   switch (opr->cl)
2296f4ced0bSchristos     {
2306f4ced0bSchristos     case OPND_CL_IMMEDIATE:
2316f4ced0bSchristos       (*info->fprintf_func) (info->stream, "#%d",
2326f4ced0bSchristos 			     ((struct immediate_operand *) opr)->value);
233c1a20988Schristos       break;
2346f4ced0bSchristos     case OPND_CL_REGISTER:
235c1a20988Schristos       {
2366f4ced0bSchristos         int r = ((struct register_operand*) opr)->reg;
2376f4ced0bSchristos 
2386f4ced0bSchristos 	if (r < 0 || r >= S12Z_N_REGISTERS)
2396f4ced0bSchristos 	  (*info->fprintf_func) (info->stream, _("<illegal reg num>"));
2406f4ced0bSchristos 	else
2416f4ced0bSchristos 	  (*info->fprintf_func) (info->stream, "%s", registers[r].name);
2426f4ced0bSchristos       }
243c1a20988Schristos       break;
2446f4ced0bSchristos     case OPND_CL_REGISTER_ALL16:
2456f4ced0bSchristos       (*info->fprintf_func) (info->stream, "%s", "ALL16b");
246c1a20988Schristos       break;
2476f4ced0bSchristos     case OPND_CL_REGISTER_ALL:
2486f4ced0bSchristos       (*info->fprintf_func) (info->stream, "%s", "ALL");
249c1a20988Schristos       break;
2506f4ced0bSchristos     case OPND_CL_BIT_FIELD:
2516f4ced0bSchristos       (*info->fprintf_func) (info->stream, "#%d:%d",
2526f4ced0bSchristos                              ((struct bitfield_operand*)opr)->width,
2536f4ced0bSchristos                              ((struct bitfield_operand*)opr)->offset);
254c1a20988Schristos       break;
2556f4ced0bSchristos     case OPND_CL_SIMPLE_MEMORY:
256c1a20988Schristos       {
2576f4ced0bSchristos         struct simple_memory_operand *mo =
2586f4ced0bSchristos 	  (struct simple_memory_operand *) opr;
2596f4ced0bSchristos 	decode_possible_symbol (mo->addr, mo->base, info, mo->relative);
260c1a20988Schristos       }
261c1a20988Schristos       break;
2626f4ced0bSchristos     case OPND_CL_MEMORY:
263c1a20988Schristos       {
2646f4ced0bSchristos         int used_reg = 0;
2656f4ced0bSchristos         struct memory_operand *mo = (struct memory_operand *) opr;
2666f4ced0bSchristos 	(*info->fprintf_func) (info->stream, "%c", mo->indirect ? '[' : '(');
267c1a20988Schristos 
2686f4ced0bSchristos 	const char *fmt;
2696f4ced0bSchristos 	assert (mo->mutation == OPND_RM_NONE || mo->n_regs == 1);
2706f4ced0bSchristos 	switch (mo->mutation)
271c1a20988Schristos 	  {
2726f4ced0bSchristos 	  case OPND_RM_PRE_DEC:
2736f4ced0bSchristos 	    fmt = "-%s";
274c1a20988Schristos 	    break;
2756f4ced0bSchristos 	  case OPND_RM_PRE_INC:
2766f4ced0bSchristos 	    fmt = "+%s";
277c1a20988Schristos 	    break;
2786f4ced0bSchristos 	  case OPND_RM_POST_DEC:
2796f4ced0bSchristos 	    fmt = "%s-";
280c1a20988Schristos 	    break;
2816f4ced0bSchristos 	  case OPND_RM_POST_INC:
2826f4ced0bSchristos 	    fmt = "%s+";
283c1a20988Schristos 	    break;
2846f4ced0bSchristos 	  case OPND_RM_NONE:
285c1a20988Schristos 	  default:
2866f4ced0bSchristos 	    if (mo->n_regs < 2)
2876f4ced0bSchristos 	      (*info->fprintf_func) (info->stream, (mo->n_regs == 0) ? "%d" : "%d,", mo->base_offset);
2886f4ced0bSchristos 	    fmt = "%s";
289c1a20988Schristos 	    break;
290c1a20988Schristos 	  }
2916f4ced0bSchristos 	if (mo->n_regs > 0)
292c1a20988Schristos 	  {
2936f4ced0bSchristos 	    int r = mo->regs[0];
294c1a20988Schristos 
2956f4ced0bSchristos 	    if (r < 0 || r >= S12Z_N_REGISTERS)
2966f4ced0bSchristos 	      (*info->fprintf_func) (info->stream, fmt, _("<illegal reg num>"));
297c1a20988Schristos 	    else
2986f4ced0bSchristos 	      (*info->fprintf_func) (info->stream, fmt, registers[r].name);
299c1a20988Schristos 	  }
3006f4ced0bSchristos 	used_reg = 1;
301c1a20988Schristos 
3026f4ced0bSchristos         if (mo->n_regs > used_reg)
303c1a20988Schristos           {
3046f4ced0bSchristos 	    int r = mo->regs[used_reg];
3056f4ced0bSchristos 
3066f4ced0bSchristos 	    if (r < 0 || r >= S12Z_N_REGISTERS)
3076f4ced0bSchristos 	      (*info->fprintf_func) (info->stream, _("<illegal reg num>"));
308c1a20988Schristos 	    else
3096f4ced0bSchristos 	      (*info->fprintf_func) (info->stream, ",%s",
3106f4ced0bSchristos 				     registers[r].name);
3116f4ced0bSchristos           }
3126f4ced0bSchristos 
3136f4ced0bSchristos 	(*info->fprintf_func) (info->stream, "%c",
3146f4ced0bSchristos 			       mo->indirect ? ']' : ')');
3156f4ced0bSchristos       }
316c1a20988Schristos       break;
317c1a20988Schristos     };
318c1a20988Schristos }
319c1a20988Schristos 
3206f4ced0bSchristos #define S12Z_N_SIZES 4
3216f4ced0bSchristos static const char shift_size_table[S12Z_N_SIZES] =
322c1a20988Schristos {
323c1a20988Schristos   'b', 'w', 'p', 'l'
324c1a20988Schristos };
325c1a20988Schristos 
326c1a20988Schristos int
print_insn_s12z(bfd_vma memaddr,struct disassemble_info * info)327c1a20988Schristos print_insn_s12z (bfd_vma memaddr, struct disassemble_info* info)
328c1a20988Schristos {
3296f4ced0bSchristos   int o;
3306f4ced0bSchristos   enum optr operator = OP_INVALID;
3316f4ced0bSchristos   int n_operands = 0;
332c1a20988Schristos 
3336f4ced0bSchristos   /* The longest instruction in S12Z can have 6 operands.
3346f4ced0bSchristos      (Most have 3 or less.  Only PSH and PUL have so many.  */
3356f4ced0bSchristos   struct operand *operands[6];
3366f4ced0bSchristos 
3376f4ced0bSchristos   struct mem_read_abstraction mra;
3386f4ced0bSchristos   mra.base.read = (void *) abstract_read_memory ;
3396f4ced0bSchristos   mra.base.advance = advance ;
3406f4ced0bSchristos   mra.base.posn = posn;
3416f4ced0bSchristos   mra.memaddr = memaddr;
3426f4ced0bSchristos   mra.info = info;
3436f4ced0bSchristos 
3446f4ced0bSchristos   short osize = -1;
3456f4ced0bSchristos   int n_bytes =
3466f4ced0bSchristos     decode_s12z (&operator, &osize, &n_operands, operands,
3476f4ced0bSchristos 		 (struct mem_read_abstraction_base *) &mra);
3486f4ced0bSchristos 
3496f4ced0bSchristos   (info->fprintf_func) (info->stream, "%s", mnemonics[(long)operator]);
3506f4ced0bSchristos 
3516f4ced0bSchristos   /* Ship out size sufficies for those instructions which
3526f4ced0bSchristos      need them.  */
3536f4ced0bSchristos   if (osize == -1)
354c1a20988Schristos     {
3556f4ced0bSchristos       bool suffix = false;
3566f4ced0bSchristos 
3576f4ced0bSchristos       for (o = 0; o < n_operands; ++o)
3586f4ced0bSchristos 	{
3596f4ced0bSchristos 	  if (operands[o] && operands[o]->osize != -1)
3606f4ced0bSchristos 	    {
3616f4ced0bSchristos 	      if (!suffix)
3626f4ced0bSchristos 		{
3636f4ced0bSchristos 		  (*mra.info->fprintf_func) (mra.info->stream, "%c", '.');
3646f4ced0bSchristos 		  suffix = true;
365c1a20988Schristos 		}
3666f4ced0bSchristos 
3676f4ced0bSchristos 	      osize = operands[o]->osize;
3686f4ced0bSchristos 
3696f4ced0bSchristos 	      if (osize < 0 || osize >= S12Z_N_SIZES)
3706f4ced0bSchristos 		(*mra.info->fprintf_func) (mra.info->stream, _("<bad>"));
371c1a20988Schristos 	      else
3726f4ced0bSchristos 		(*mra.info->fprintf_func) (mra.info->stream, "%c",
3736f4ced0bSchristos 					   shift_size_table[osize]);
374c1a20988Schristos 	    }
375c1a20988Schristos 	}
376c1a20988Schristos     }
377c1a20988Schristos   else
378c1a20988Schristos     {
3796f4ced0bSchristos       if (osize < 0 || osize >= S12Z_N_SIZES)
3806f4ced0bSchristos 	(*mra.info->fprintf_func) (mra.info->stream, _(".<bad>"));
381c1a20988Schristos       else
3826f4ced0bSchristos 	(*mra.info->fprintf_func) (mra.info->stream, ".%c",
3836f4ced0bSchristos 				   shift_size_table[osize]);
3846f4ced0bSchristos     }
3856f4ced0bSchristos 
3866f4ced0bSchristos   /* Ship out the operands.  */
3876f4ced0bSchristos   for (o = 0; o < n_operands; ++o)
388c1a20988Schristos     {
3896f4ced0bSchristos       if (operands[o])
3906f4ced0bSchristos 	opr_emit_disassembly (operands[o], mra.info);
3916f4ced0bSchristos       free (operands[o]);
392c1a20988Schristos     }
393c1a20988Schristos 
3946f4ced0bSchristos   return n_bytes;
395c1a20988Schristos }
396