diff options
Diffstat (limited to 'opcodes/s390-dis.c')
-rw-r--r-- | opcodes/s390-dis.c | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/opcodes/s390-dis.c b/opcodes/s390-dis.c new file mode 100644 index 0000000..6409d5f --- /dev/null +++ b/opcodes/s390-dis.c @@ -0,0 +1,231 @@ +/* s390-dis.c -- Disassemble S390 instructions + Copyright (C) 2000, 2001 Free Software Foundation, Inc. + Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). + + This file is part of GDB, GAS and the GNU binutils. + + 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 "sysdep.h" +#include "dis-asm.h" +#include "opcode/s390.h" + +static int init_flag = 0; +static int opc_index[256]; +static int current_arch_mask = 0; + +/* Set up index table for first opcode byte */ +static void +init_disasm(info) + struct disassemble_info *info ATTRIBUTE_UNUSED; +{ + const struct s390_opcode *opcode; + const struct s390_opcode *opcode_end; + + memset(opc_index, 0, sizeof(opc_index)); + opcode_end = s390_opcodes + s390_num_opcodes; + for (opcode = s390_opcodes; opcode < opcode_end; opcode++) { + opc_index[(int) opcode->opcode[0]] = opcode - s390_opcodes; + while ((opcode < opcode_end) && + (opcode[1].opcode[0] == opcode->opcode[0])) + opcode++; + } + switch (info->mach) { + case bfd_mach_s390_esa: + current_arch_mask = 1 << S390_OPCODE_ESA; + break; + case bfd_mach_s390_esame: + current_arch_mask = 1 << S390_OPCODE_ESAME; + break; + default: + abort(); + } + init_flag = 1; +} + +/* Extracts an operand value from an instruction. */ + +static inline unsigned int +s390_extract_operand (insn, operand) + unsigned char *insn; + const struct s390_operand *operand; +{ + unsigned int val; + int bits; + + /* extract fragments of the operand byte for byte */ + insn += operand->shift/8; + bits = (operand->shift & 7) + operand->bits; + val = 0; + do { + val <<= 8; + val |= (unsigned int) *insn++; + bits -= 8; + } while (bits > 0); + val >>= -bits; + val &= ((1U << (operand->bits-1))<<1) - 1; + + /* sign extend value if the operand is signed or pc relative */ + if ((operand->flags & (S390_OPERAND_SIGNED|S390_OPERAND_PCREL)) && + (val & (1U << (operand->bits-1)))) + val |= (-1U << (operand->bits-1))<<1; + + /* double value if the operand is pc relative */ + if (operand->flags & S390_OPERAND_PCREL) + val <<= 1; + + /* length x in an instructions has real length x+1 */ + if (operand->flags & S390_OPERAND_LENGTH) + val++; + return val; +} + +/* Print a S390 instruction. */ + +int +print_insn_s390 (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + bfd_byte buffer[6]; + const struct s390_opcode *opcode; + const struct s390_opcode *opcode_end; + unsigned int value; + int status, opsize, bufsize; + char separator; + + if (init_flag == 0) + init_disasm(info); + + /* The output looks better if we put 6 bytes on a line. */ + info->bytes_per_line = 6; + + /* Every S390 instruction is max 6 bytes long. */ + memset(buffer, 0, 6); + status = (*info->read_memory_func) (memaddr, buffer, 6, info); + if (status != 0) { + for (bufsize = 0; bufsize < 6; bufsize++) + if ((*info->read_memory_func) (memaddr, buffer, bufsize+1, info) != 0) + break; + if (bufsize <= 0) { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + /* Opsize calculation looks strange but it works + 00xxxxxx -> 2 bytes, 01xxxxxx/10xxxxxx -> 4 bytes, + 11xxxxxx -> 6 bytes. */ + opsize = ((((buffer[0]>>6)+1)>>1)+1)<<1; + status = opsize > bufsize; + } else { + bufsize = 6; + opsize = ((((buffer[0]>>6)+1)>>1)+1)<<1; + } + + if (status == 0) { + /* Find the first match in the opcode table. */ + opcode_end = s390_opcodes + s390_num_opcodes; + for (opcode = s390_opcodes + opc_index[(int) buffer[0]]; + (opcode < opcode_end) && (buffer[0] == opcode->opcode[0]); + opcode++) { + const struct s390_operand *operand; + const unsigned char *opindex; + + /* check architecture */ + if (!(opcode->architecture & current_arch_mask)) + continue; + /* check signature of the opcode */ + if ((buffer[1] & opcode->mask[1]) != opcode->opcode[1] || + (buffer[2] & opcode->mask[2]) != opcode->opcode[2] || + (buffer[3] & opcode->mask[3]) != opcode->opcode[3] || + (buffer[4] & opcode->mask[4]) != opcode->opcode[4] || + (buffer[5] & opcode->mask[5]) != opcode->opcode[5]) + continue; + + /* the instruction is valid */ + if (opcode->operands[0] != 0) + (*info->fprintf_func) (info->stream, "%s\t", opcode->name); + else + (*info->fprintf_func) (info->stream, "%s", opcode->name); + + /* Extract the operands. */ + separator = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) { + unsigned int value; + + operand = s390_operands + *opindex; + value = s390_extract_operand(buffer, operand); + + if ((operand->flags & S390_OPERAND_INDEX) && value == 0) + continue; + if ((operand->flags & S390_OPERAND_BASE) && + value == 0 && separator == '(') { + separator = ','; + continue; + } + + if (separator) + (*info->fprintf_func) (info->stream, "%c", separator); + + if (operand->flags & S390_OPERAND_GPR) + (*info->fprintf_func) (info->stream, "%%r%i", value); + else if (operand->flags & S390_OPERAND_FPR) + (*info->fprintf_func) (info->stream, "%%f%i", value); + else if (operand->flags & S390_OPERAND_AR) + (*info->fprintf_func) (info->stream, "%%a%i", value); + else if (operand->flags & S390_OPERAND_CR) + (*info->fprintf_func) (info->stream, "%%c%i", value); + else if (operand->flags & S390_OPERAND_PCREL) + (*info->print_address_func) (memaddr + (int) value, info); + else if (operand->flags & S390_OPERAND_SIGNED) + (*info->fprintf_func) (info->stream, "%i", (int) value); + else + (*info->fprintf_func) (info->stream, "%i", value); + + if (operand->flags & S390_OPERAND_DISP) { + separator = '('; + } else if (operand->flags & S390_OPERAND_BASE) { + (*info->fprintf_func) (info->stream, ")"); + separator = ','; + } else + separator = ','; + } + + /* found instruction, printed it, return its size */ + return opsize; + } + /* no matching instruction found, fall through to hex print */ + } + + if (bufsize >= 4) { + value = (unsigned int) buffer[0]; + value = (value << 8) + (unsigned int) buffer[1]; + value = (value << 8) + (unsigned int) buffer[2]; + value = (value << 8) + (unsigned int) buffer[3]; + (*info->fprintf_func) (info->stream,".long\t0x%08x", value); + return 4; + } else if (bufsize >= 2) { + value = (unsigned int) buffer[0]; + value = (value << 8) + (unsigned int) buffer[1]; + (*info->fprintf_func) (info->stream,".short\t0x%04x", value); + return 2; + } else { + value = (unsigned int) buffer[0]; + (*info->fprintf_func) (info->stream,".byte\t0x%02x", value); + return 1; + } +} |