diff options
Diffstat (limited to 'opcodes/m68hc11-dis.c')
-rw-r--r-- | opcodes/m68hc11-dis.c | 608 |
1 files changed, 608 insertions, 0 deletions
diff --git a/opcodes/m68hc11-dis.c b/opcodes/m68hc11-dis.c new file mode 100644 index 0000000..2ef241c --- /dev/null +++ b/opcodes/m68hc11-dis.c @@ -0,0 +1,608 @@ +/* m68hc11-dis.c -- Motorola 68HC11 & 68HC12 disassembly + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + Written by Stephane Carrez (stcarrez@worldnet.fr) + +This program 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 2 of the License, or +(at your option) any later version. + +This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <stdio.h> + +#include "ansidecl.h" +#include "opcode/m68hc11.h" +#include "dis-asm.h" + +static const char *const reg_name[] = { + "X", "Y", "SP", "PC" +}; + +static const char *const reg_src_table[] = { + "A", "B", "CCR", "TMP3", "D", "X", "Y", "SP" +}; + +static const char *const reg_dst_table[] = { + "A", "B", "CCR", "TMP2", "D", "X", "Y", "SP" +}; + +#define OP_PAGE_MASK (M6811_OP_PAGE2|M6811_OP_PAGE3|M6811_OP_PAGE4) + +static int +read_memory (memaddr, buffer, size, info) + bfd_vma memaddr; + bfd_byte *buffer; + int size; + struct disassemble_info *info; +{ + int status; + + /* Get first byte. Only one at a time because we don't know the + size of the insn. */ + status = (*info->read_memory_func) (memaddr, buffer, size, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + return 0; +} + + +/* Read the 68HC12 indexed operand byte and print the corresponding mode. + Returns the number of bytes read or -1 if failure. */ +static int +print_indexed_operand (memaddr, info, mov_insn) + bfd_vma memaddr; + struct disassemble_info *info; + int mov_insn; +{ + bfd_byte buffer[4]; + int reg; + int status; + short sval; + int pos = 1; + + status = read_memory (memaddr, &buffer[0], 1, info); + if (status != 0) + { + return status; + } + + /* n,r with 5-bits signed constant. */ + if ((buffer[0] & 0x20) == 0) + { + reg = (buffer[0] >> 6) & 3; + sval = (buffer[0] & 0x1f); + if (sval & 0x10) + sval |= 0xfff0; + (*info->fprintf_func) (info->stream, "%d,%s", + (int) sval, reg_name[reg]); + } + + /* Auto pre/post increment/decrement. */ + else if ((buffer[0] & 0xc0) != 0xc0) + { + const char *mode; + + reg = (buffer[0] >> 6) & 3; + sval = (buffer[0] & 0x0f); + if (sval & 0x8) + { + sval |= 0xfff0; + sval = -sval; + mode = "-"; + } + else + { + sval = sval + 1; + mode = "+"; + } + (*info->fprintf_func) (info->stream, "%d,%s%s%s", + (int) sval, + (buffer[0] & 0x10 ? "" : mode), + reg_name[reg], (buffer[0] & 0x10 ? mode : "")); + } + + /* [n,r] 16-bits offset indexed indirect. */ + else if ((buffer[0] & 0x07) == 3) + { + if (mov_insn) + { + (*info->fprintf_func) (info->stream, "<invalid op: 0x%x>", + buffer[0] & 0x0ff); + return 0; + } + reg = (buffer[0] >> 3) & 0x03; + status = read_memory (memaddr + pos, &buffer[0], 2, info); + if (status != 0) + { + return status; + } + + pos += 2; + sval = ((buffer[0] << 8) | (buffer[1] & 0x0FF)); + (*info->fprintf_func) (info->stream, "[%u,%s]", + sval & 0x0ffff, reg_name[reg]); + } + else if ((buffer[0] & 0x4) == 0) + { + if (mov_insn) + { + (*info->fprintf_func) (info->stream, "<invalid op: 0x%x>", + buffer[0] & 0x0ff); + return 0; + } + reg = (buffer[0] >> 3) & 0x03; + status = read_memory (memaddr + pos, + &buffer[1], (buffer[0] & 0x2 ? 2 : 1), info); + if (status != 0) + { + return status; + } + if (buffer[0] & 2) + { + sval = ((buffer[1] << 8) | (buffer[2] & 0x0FF)); + sval &= 0x0FFFF; + pos += 2; + } + else + { + sval = buffer[1] & 0x00ff; + if (buffer[0] & 0x01) + sval |= 0xff00; + pos++; + } + (*info->fprintf_func) (info->stream, "%d,%s", + (int) sval, reg_name[reg]); + } + else + { + reg = (buffer[0] >> 3) & 0x03; + switch (buffer[0] & 3) + { + case 0: + (*info->fprintf_func) (info->stream, "A,%s", reg_name[reg]); + break; + case 1: + (*info->fprintf_func) (info->stream, "B,%s", reg_name[reg]); + break; + case 2: + (*info->fprintf_func) (info->stream, "D,%s", reg_name[reg]); + break; + case 3: + default: + (*info->fprintf_func) (info->stream, "[D,%s]", reg_name[reg]); + break; + } + } + + return pos; +} + +/* Disassemble one instruction at address 'memaddr'. Returns the number + of bytes used by that instruction. */ +static int +print_insn (memaddr, info, arch) + bfd_vma memaddr; + struct disassemble_info *info; + int arch; +{ + int status; + bfd_byte buffer[4]; + unsigned char code; + long format, pos, i; + short sval; + const struct m68hc11_opcode *opcode; + + /* Get first byte. Only one at a time because we don't know the + size of the insn. */ + status = read_memory (memaddr, buffer, 1, info); + if (status != 0) + { + return status; + } + + format = 0; + code = buffer[0]; + pos = 0; + + /* Look for page2,3,4 opcodes. */ + if (code == M6811_OPCODE_PAGE2) + { + pos++; + format = M6811_OP_PAGE2; + } + else if (code == M6811_OPCODE_PAGE3 && arch == cpu6811) + { + pos++; + format = M6811_OP_PAGE3; + } + else if (code == M6811_OPCODE_PAGE4 && arch == cpu6811) + { + pos++; + format = M6811_OP_PAGE4; + } + + /* We are in page2,3,4; get the real opcode. */ + if (pos == 1) + { + status = read_memory (memaddr + pos, &buffer[1], 1, info); + if (status != 0) + { + return status; + } + code = buffer[1]; + } + + + /* Look first for a 68HC12 alias. All of them are 2-bytes long and + in page 1. There is no operand to print. We read the second byte + only when we have a possible match. */ + if ((arch & cpu6812) && format == 0) + { + int must_read = 1; + + /* Walk the alias table to find a code1+code2 match. */ + for (i = 0; i < m68hc12_num_alias; i++) + { + if (m68hc12_alias[i].code1 == code) + { + if (must_read) + { + status = read_memory (memaddr + pos + 1, + &buffer[1], 1, info); + if (status != 0) + break; + + must_read = 1; + } + if (m68hc12_alias[i].code2 == (unsigned char) buffer[1]) + { + (*info->fprintf_func) (info->stream, "%s", + m68hc12_alias[i].name); + return 2; + } + } + } + } + + pos++; + + /* Scan the opcode table until we find the opcode + with the corresponding page. */ + opcode = m68hc11_opcodes; + for (i = 0; i < m68hc11_num_opcodes; i++, opcode++) + { + int offset; + + if ((opcode->arch & arch) == 0) + continue; + if (opcode->opcode != code) + continue; + if ((opcode->format & OP_PAGE_MASK) != format) + continue; + + if (opcode->format & M6812_OP_REG) + { + int j; + int is_jump; + + if (opcode->format & M6811_OP_JUMP_REL) + is_jump = 1; + else + is_jump = 0; + + status = read_memory (memaddr + pos, &buffer[0], 1, info); + if (status != 0) + { + return status; + } + for (j = 0; i + j < m68hc11_num_opcodes; j++) + { + if ((opcode[j].arch & arch) == 0) + continue; + if (opcode[j].opcode != code) + continue; + if (is_jump) + { + if (!(opcode[j].format & M6811_OP_JUMP_REL)) + continue; + + if ((opcode[j].format & M6812_OP_IBCC_MARKER) + && (buffer[0] & 0xc0) != 0x80) + continue; + if ((opcode[j].format & M6812_OP_TBCC_MARKER) + && (buffer[0] & 0xc0) != 0x40) + continue; + if ((opcode[j].format & M6812_OP_DBCC_MARKER) + && (buffer[0] & 0xc0) != 0) + continue; + if ((opcode[j].format & M6812_OP_EQ_MARKER) + && (buffer[0] & 0x20) == 0) + break; + if (!(opcode[j].format & M6812_OP_EQ_MARKER) + && (buffer[0] & 0x20) != 0) + break; + continue; + } + if (opcode[j].format & M6812_OP_EXG_MARKER && buffer[0] & 0x80) + break; + if ((opcode[j].format & M6812_OP_SEX_MARKER) + && (((buffer[0] & 0x07) >= 3 && (buffer[0] & 7) <= 7)) + && ((buffer[0] & 0x0f0) <= 0x20)) + break; + if (opcode[j].format & M6812_OP_TFR_MARKER + && !(buffer[0] & 0x80)) + break; + } + if (i + j < m68hc11_num_opcodes) + opcode = &opcode[j]; + } + + /* We have found the opcode. Extract the operand and print it. */ + (*info->fprintf_func) (info->stream, "%s", opcode->name); + + format = opcode->format; + if (format & (M6811_OP_MASK | M6811_OP_BITMASK + | M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16)) + { + (*info->fprintf_func) (info->stream, "\t"); + } + + /* The movb and movw must be handled in a special way... */ + offset = 0; + if (format & (M6812_OP_IDX_P2 | M6812_OP_IND16_P2)) + { + if ((format & M6812_OP_IDX_P2) + && (format & (M6811_OP_IMM8 | M6811_OP_IMM16 | M6811_OP_IND16))) + offset = 1; + } + + /* Operand with one more byte: - immediate, offset, + direct-low address. */ + if (format & + (M6811_OP_IMM8 | M6811_OP_IX | M6811_OP_IY | M6811_OP_DIRECT)) + { + status = read_memory (memaddr + pos + offset, &buffer[0], 1, info); + if (status != 0) + { + return status; + } + + pos++; + offset = -1; + if (format & M6811_OP_IMM8) + { + (*info->fprintf_func) (info->stream, "#%d", (int) buffer[0]); + format &= ~M6811_OP_IMM8; + } + else if (format & M6811_OP_IX) + { + /* Offsets are in range 0..255, print them unsigned. */ + (*info->fprintf_func) (info->stream, "%u,x", buffer[0] & 0x0FF); + format &= ~M6811_OP_IX; + } + else if (format & M6811_OP_IY) + { + (*info->fprintf_func) (info->stream, "%u,y", buffer[0] & 0x0FF); + format &= ~M6811_OP_IY; + } + else if (format & M6811_OP_DIRECT) + { + (*info->fprintf_func) (info->stream, "*"); + (*info->print_address_func) (buffer[0] & 0x0FF, info); + format &= ~M6811_OP_DIRECT; + } + } + +#define M6812_INDEXED_FLAGS (M6812_OP_IDX|M6812_OP_IDX_1|M6812_OP_IDX_2) + /* Analyze the 68HC12 indexed byte. */ + if (format & M6812_INDEXED_FLAGS) + { + status = print_indexed_operand (memaddr + pos, info, 0); + if (status < 0) + { + return status; + } + pos += status; + } + + /* 68HC12 dbcc/ibcc/tbcc operands. */ + if ((format & M6812_OP_REG) && (format & M6811_OP_JUMP_REL)) + { + status = read_memory (memaddr + pos, &buffer[0], 2, info); + if (status != 0) + { + return status; + } + (*info->fprintf_func) (info->stream, "%s,", + reg_src_table[buffer[0] & 0x07]); + sval = buffer[1] & 0x0ff; + if (buffer[0] & 0x10) + sval |= 0xff00; + + pos += 2; + (*info->print_address_func) (memaddr + pos + sval, info); + format &= ~(M6812_OP_REG | M6811_OP_JUMP_REL); + } + else if (format & (M6812_OP_REG | M6812_OP_REG_2)) + { + status = read_memory (memaddr + pos, &buffer[0], 1, info); + if (status != 0) + { + return status; + } + + pos++; + (*info->fprintf_func) (info->stream, "%s,%s", + reg_src_table[(buffer[0] >> 4) & 7], + reg_dst_table[(buffer[0] & 7)]); + } + + /* M6811_OP_BITMASK and M6811_OP_JUMP_REL must be treated separately + and in that order. The brset/brclr insn have a bitmask and then + a relative branch offset. */ + if (format & M6811_OP_BITMASK) + { + status = read_memory (memaddr + pos, &buffer[0], 1, info); + if (status != 0) + { + return status; + } + pos++; + (*info->fprintf_func) (info->stream, " #$%02x%s", + buffer[0] & 0x0FF, + (format & M6811_OP_JUMP_REL ? " " : "")); + format &= ~M6811_OP_BITMASK; + } + if (format & M6811_OP_JUMP_REL) + { + int val; + + status = read_memory (memaddr + pos, &buffer[0], 1, info); + if (status != 0) + { + return status; + } + + pos++; + val = (buffer[0] & 0x80) ? buffer[0] | 0xFFFFFF00 : buffer[0]; + (*info->print_address_func) (memaddr + pos + val, info); + format &= ~M6811_OP_JUMP_REL; + } + else if (format & M6812_OP_JUMP_REL16) + { + int val; + + status = read_memory (memaddr + pos, &buffer[0], 2, info); + if (status != 0) + { + return status; + } + + pos += 2; + val = ((buffer[0] << 8) | (buffer[1] & 0x0FF)); + if (val & 0x8000) + val |= 0xffff0000; + + (*info->print_address_func) (memaddr + pos + val, info); + format &= ~M6812_OP_JUMP_REL16; + } + if (format & (M6811_OP_IMM16 | M6811_OP_IND16)) + { + int val; + + status = read_memory (memaddr + pos + offset, &buffer[0], 2, info); + if (status != 0) + { + return status; + } + if (format & M6812_OP_IDX_P2) + offset = -2; + else + offset = 0; + pos += 2; + + val = ((buffer[0] << 8) | (buffer[1] & 0x0FF)); + val &= 0x0FFFF; + if (format & M6811_OP_IMM16) + { + format &= ~M6811_OP_IMM16; + (*info->fprintf_func) (info->stream, "#"); + } + else + format &= ~M6811_OP_IND16; + + (*info->print_address_func) (val, info); + } + + if (format & M6812_OP_IDX_P2) + { + (*info->fprintf_func) (info->stream, ", "); + status = print_indexed_operand (memaddr + pos + offset, info, 1); + if (status < 0) + return status; + pos += status; + } + + if (format & M6812_OP_IND16_P2) + { + int val; + + (*info->fprintf_func) (info->stream, ", "); + + status = read_memory (memaddr + pos + offset, &buffer[0], 2, info); + if (status != 0) + { + return status; + } + pos += 2; + + val = ((buffer[0] << 8) | (buffer[1] & 0x0FF)); + val &= 0x0FFFF; + (*info->print_address_func) (val, info); + } + +#ifdef DEBUG + /* Consistency check. 'format' must be 0, so that we have handled + all formats; and the computed size of the insn must match the + opcode table content. */ + if (format & ~(M6811_OP_PAGE4 | M6811_OP_PAGE3 | M6811_OP_PAGE2)) + { + (*info->fprintf_func) (info->stream, "; Error, format: %x", format); + } + if (pos != opcode->size) + { + (*info->fprintf_func) (info->stream, "; Error, size: %d expect %d", + pos, opcode->size); + } +#endif + return pos; + } + + /* Opcode not recognized. */ + if (format == M6811_OP_PAGE2 && arch & cpu6812 + && ((code >= 0x30 && code <= 0x39) || (code >= 0x40 && code <= 0xff))) + (*info->fprintf_func) (info->stream, "trap\t#%d", code & 0x0ff); + + else if (format == M6811_OP_PAGE2) + (*info->fprintf_func) (info->stream, ".byte\t0x%02x, 0x%02x", + M6811_OPCODE_PAGE2, code); + else if (format == M6811_OP_PAGE3) + (*info->fprintf_func) (info->stream, ".byte\t0x%02x, 0x%02x", + M6811_OPCODE_PAGE3, code); + else if (format == M6811_OP_PAGE4) + (*info->fprintf_func) (info->stream, ".byte\t0x%02x, 0x%02x", + M6811_OPCODE_PAGE4, code); + else + (*info->fprintf_func) (info->stream, ".byte\t0x%02x", code); + + return pos; +} + +/* Disassemble one instruction at address 'memaddr'. Returns the number + of bytes used by that instruction. */ +int +print_insn_m68hc11 (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + return print_insn (memaddr, info, cpu6811); +} + +int +print_insn_m68hc12 (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + return print_insn (memaddr, info, cpu6812); +} |