/* s12z-dis.c -- Freescale S12Z disassembly Copyright (C) 2018-2023 Free Software Foundation, Inc. This file is part of the GNU opcodes library. This library 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 3, or (at your option) any later version. It 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sysdep.h" #include #include #include #include #include "opcode/s12z.h" #include "bfd.h" #include "dis-asm.h" #include "disassemble.h" #include "s12z-opc.h" #include "opintl.h" struct mem_read_abstraction { struct mem_read_abstraction_base base; bfd_vma memaddr; struct disassemble_info* info; }; static void advance (struct mem_read_abstraction_base *b) { struct mem_read_abstraction *mra = (struct mem_read_abstraction *) b; mra->memaddr ++; } static bfd_vma posn (struct mem_read_abstraction_base *b) { struct mem_read_abstraction *mra = (struct mem_read_abstraction *) b; return mra->memaddr; } static int abstract_read_memory (struct mem_read_abstraction_base *b, int offset, size_t n, bfd_byte *bytes) { struct mem_read_abstraction *mra = (struct mem_read_abstraction *) b; int status = (*mra->info->read_memory_func) (mra->memaddr + offset, bytes, n, mra->info); if (status != 0) (*mra->info->memory_error_func) (status, mra->memaddr + offset, mra->info); return status != 0 ? -1 : 0; } /* Start of disassembly file. */ const struct reg registers[S12Z_N_REGISTERS] = { {"d2", 2}, {"d3", 2}, {"d4", 2}, {"d5", 2}, {"d0", 1}, {"d1", 1}, {"d6", 4}, {"d7", 4}, {"x", 3}, {"y", 3}, {"s", 3}, {"p", 3}, {"cch", 1}, {"ccl", 1}, {"ccw", 2} }; static const char *mnemonics[] = { "!!invalid!!", "psh", "pul", "tbne", "tbeq", "tbpl", "tbmi", "tbgt", "tble", "dbne", "dbeq", "dbpl", "dbmi", "dbgt", "dble", "sex", "exg", "lsl", "lsr", "asl", "asr", "rol", "ror", "bfins", "bfext", "trap", "ld", "st", "cmp", "stop", "wai", "sys", "minu", "mins", "maxu", "maxs", "abs", "adc", "bit", "sbc", "rti", "clb", "eor", "sat", "nop", "bgnd", "brclr", "brset", "rts", "lea", "mov", "bra", "bsr", "bhi", "bls", "bcc", "bcs", "bne", "beq", "bvc", "bvs", "bpl", "bmi", "bge", "blt", "bgt", "ble", "inc", "clr", "dec", "add", "sub", "and", "or", "tfr", "jmp", "jsr", "com", "andcc", "neg", "orcc", "bclr", "bset", "btgl", "swi", "mulu", "divu", "modu", "macu", "qmulu", "muls", "divs", "mods", "macs", "qmuls", NULL }; static void operand_separator (struct disassemble_info *info) { if ((info->flags & 0x2)) (*info->fprintf_func) (info->stream, ","); (*info->fprintf_func) (info->stream, " "); info->flags |= 0x2; } /* Render the symbol name whose value is ADDR + BASE or the adddress itself if there is no symbol. If BASE is non zero, then the a PC relative adddress is assumend (ie BASE is the value in the PC. */ static void decode_possible_symbol (bfd_signed_vma addr, bfd_vma base, struct disassemble_info *info, bool relative) { const char *fmt = relative ? "*%+" PRId64 : "%" PRId64; asymbol *sym = info->symbol_at_address_func (addr + base, info); if (!sym) (*info->fprintf_func) (info->stream, fmt, (int64_t) addr); else (*info->fprintf_func) (info->stream, "%s", bfd_asymbol_name (sym)); } /* Emit the disassembled text for OPR */ static void opr_emit_disassembly (const struct operand *opr, struct disassemble_info *info) { operand_separator (info); switch (opr->cl) { case OPND_CL_IMMEDIATE: (*info->fprintf_func) (info->stream, "#%d", ((struct immediate_operand *) opr)->value); break; case OPND_CL_REGISTER: { int r = ((struct register_operand*) opr)->reg; if (r < 0 || r >= S12Z_N_REGISTERS) (*info->fprintf_func) (info->stream, _("")); else (*info->fprintf_func) (info->stream, "%s", registers[r].name); } break; case OPND_CL_REGISTER_ALL16: (*info->fprintf_func) (info->stream, "%s", "ALL16b"); break; case OPND_CL_REGISTER_ALL: (*info->fprintf_func) (info->stream, "%s", "ALL"); break; case OPND_CL_BIT_FIELD: (*info->fprintf_func) (info->stream, "#%d:%d", ((struct bitfield_operand*)opr)->width, ((struct bitfield_operand*)opr)->offset); break; case OPND_CL_SIMPLE_MEMORY: { struct simple_memory_operand *mo = (struct simple_memory_operand *) opr; decode_possible_symbol (mo->addr, mo->base, info, mo->relative); } break; case OPND_CL_MEMORY: { int used_reg = 0; struct memory_operand *mo = (struct memory_operand *) opr; (*info->fprintf_func) (info->stream, "%c", mo->indirect ? '[' : '('); const char *fmt; assert (mo->mutation == OPND_RM_NONE || mo->n_regs == 1); switch (mo->mutation) { case OPND_RM_PRE_DEC: fmt = "-%s"; break; case OPND_RM_PRE_INC: fmt = "+%s"; break; case OPND_RM_POST_DEC: fmt = "%s-"; break; case OPND_RM_POST_INC: fmt = "%s+"; break; case OPND_RM_NONE: default: if (mo->n_regs < 2) (*info->fprintf_func) (info->stream, (mo->n_regs == 0) ? "%d" : "%d,", mo->base_offset); fmt = "%s"; break; } if (mo->n_regs > 0) { int r = mo->regs[0]; if (r < 0 || r >= S12Z_N_REGISTERS) (*info->fprintf_func) (info->stream, fmt, _("")); else (*info->fprintf_func) (info->stream, fmt, registers[r].name); } used_reg = 1; if (mo->n_regs > used_reg) { int r = mo->regs[used_reg]; if (r < 0 || r >= S12Z_N_REGISTERS) (*info->fprintf_func) (info->stream, _("")); else (*info->fprintf_func) (info->stream, ",%s", registers[r].name); } (*info->fprintf_func) (info->stream, "%c", mo->indirect ? ']' : ')'); } break; }; } #define S12Z_N_SIZES 4 static const char shift_size_table[S12Z_N_SIZES] = { 'b', 'w', 'p', 'l' }; int print_insn_s12z (bfd_vma memaddr, struct disassemble_info* info) { int o; enum optr operator = OP_INVALID; int n_operands = 0; /* The longest instruction in S12Z can have 6 operands. (Most have 3 or less. Only PSH and PUL have so many. */ struct operand *operands[6]; struct mem_read_abstraction mra; mra.base.read = (void *) abstract_read_memory ; mra.base.advance = advance ; mra.base.posn = posn; mra.memaddr = memaddr; mra.info = info; short osize = -1; int n_bytes = decode_s12z (&operator, &osize, &n_operands, operands, (struct mem_read_abstraction_base *) &mra); (info->fprintf_func) (info->stream, "%s", mnemonics[(long)operator]); /* Ship out size sufficies for those instructions which need them. */ if (osize == -1) { bool suffix = false; for (o = 0; o < n_operands; ++o) { if (operands[o] && operands[o]->osize != -1) { if (!suffix) { (*mra.info->fprintf_func) (mra.info->stream, "%c", '.'); suffix = true; } osize = operands[o]->osize; if (osize < 0 || osize >= S12Z_N_SIZES) (*mra.info->fprintf_func) (mra.info->stream, _("")); else (*mra.info->fprintf_func) (mra.info->stream, "%c", shift_size_table[osize]); } } } else { if (osize < 0 || osize >= S12Z_N_SIZES) (*mra.info->fprintf_func) (mra.info->stream, _(".")); else (*mra.info->fprintf_func) (mra.info->stream, ".%c", shift_size_table[osize]); } /* Ship out the operands. */ for (o = 0; o < n_operands; ++o) { if (operands[o]) opr_emit_disassembly (operands[o], mra.info); free (operands[o]); } return n_bytes; }