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