199461Sobrien/* s390-dis.c -- Disassemble S390 instructions 2218822Sdim Copyright 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. 399461Sobrien Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). 499461Sobrien 599461Sobrien This file is part of GDB, GAS and the GNU binutils. 699461Sobrien 799461Sobrien This program is free software; you can redistribute it and/or modify 899461Sobrien it under the terms of the GNU General Public License as published by 999461Sobrien the Free Software Foundation; either version 2 of the License, or 1099461Sobrien (at your option) any later version. 1199461Sobrien 1299461Sobrien This program is distributed in the hope that it will be useful, 1399461Sobrien but WITHOUT ANY WARRANTY; without even the implied warranty of 1499461Sobrien MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1599461Sobrien GNU General Public License for more details. 1699461Sobrien 1799461Sobrien You should have received a copy of the GNU General Public License 1899461Sobrien along with this program; if not, write to the Free Software 19218822Sdim Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 20218822Sdim 02110-1301, USA. */ 2199461Sobrien 2299461Sobrien#include <stdio.h> 2399461Sobrien#include "ansidecl.h" 2499461Sobrien#include "sysdep.h" 2599461Sobrien#include "dis-asm.h" 2699461Sobrien#include "opcode/s390.h" 2799461Sobrien 2899461Sobrienstatic int init_flag = 0; 2999461Sobrienstatic int opc_index[256]; 3099461Sobrienstatic int current_arch_mask = 0; 3199461Sobrien 3299461Sobrien/* Set up index table for first opcode byte. */ 3399461Sobrien 3499461Sobrienstatic void 35218822Sdiminit_disasm (struct disassemble_info *info) 3699461Sobrien{ 3799461Sobrien const struct s390_opcode *opcode; 3899461Sobrien const struct s390_opcode *opcode_end; 3999461Sobrien 4099461Sobrien memset (opc_index, 0, sizeof (opc_index)); 4199461Sobrien opcode_end = s390_opcodes + s390_num_opcodes; 4299461Sobrien for (opcode = s390_opcodes; opcode < opcode_end; opcode++) 4399461Sobrien { 4499461Sobrien opc_index[(int) opcode->opcode[0]] = opcode - s390_opcodes; 4599461Sobrien while ((opcode < opcode_end) && 4699461Sobrien (opcode[1].opcode[0] == opcode->opcode[0])) 4799461Sobrien opcode++; 4899461Sobrien } 4999461Sobrien switch (info->mach) 5099461Sobrien { 5199461Sobrien case bfd_mach_s390_31: 5299461Sobrien current_arch_mask = 1 << S390_OPCODE_ESA; 5399461Sobrien break; 5499461Sobrien case bfd_mach_s390_64: 55130561Sobrien current_arch_mask = 1 << S390_OPCODE_ZARCH; 5699461Sobrien break; 5799461Sobrien default: 5899461Sobrien abort (); 5999461Sobrien } 6099461Sobrien init_flag = 1; 6199461Sobrien} 6299461Sobrien 6399461Sobrien/* Extracts an operand value from an instruction. */ 6499461Sobrien 6599461Sobrienstatic inline unsigned int 66218822Sdims390_extract_operand (unsigned char *insn, const struct s390_operand *operand) 6799461Sobrien{ 6899461Sobrien unsigned int val; 6999461Sobrien int bits; 7099461Sobrien 7199461Sobrien /* Extract fragments of the operand byte for byte. */ 7299461Sobrien insn += operand->shift / 8; 7399461Sobrien bits = (operand->shift & 7) + operand->bits; 7499461Sobrien val = 0; 7599461Sobrien do 7699461Sobrien { 7799461Sobrien val <<= 8; 7899461Sobrien val |= (unsigned int) *insn++; 7999461Sobrien bits -= 8; 8099461Sobrien } 8199461Sobrien while (bits > 0); 8299461Sobrien val >>= -bits; 8399461Sobrien val &= ((1U << (operand->bits - 1)) << 1) - 1; 8499461Sobrien 85130561Sobrien /* Check for special long displacement case. */ 86130561Sobrien if (operand->bits == 20 && operand->shift == 20) 87130561Sobrien val = (val & 0xff) << 12 | (val & 0xfff00) >> 8; 88130561Sobrien 8999461Sobrien /* Sign extend value if the operand is signed or pc relative. */ 9099461Sobrien if ((operand->flags & (S390_OPERAND_SIGNED | S390_OPERAND_PCREL)) 9199461Sobrien && (val & (1U << (operand->bits - 1)))) 9299461Sobrien val |= (-1U << (operand->bits - 1)) << 1; 9399461Sobrien 9499461Sobrien /* Double value if the operand is pc relative. */ 9599461Sobrien if (operand->flags & S390_OPERAND_PCREL) 9699461Sobrien val <<= 1; 9799461Sobrien 98218822Sdim /* Length x in an instructions has real length x + 1. */ 9999461Sobrien if (operand->flags & S390_OPERAND_LENGTH) 10099461Sobrien val++; 10199461Sobrien return val; 10299461Sobrien} 10399461Sobrien 10499461Sobrien/* Print a S390 instruction. */ 10599461Sobrien 10699461Sobrienint 107218822Sdimprint_insn_s390 (bfd_vma memaddr, struct disassemble_info *info) 10899461Sobrien{ 10999461Sobrien bfd_byte buffer[6]; 11099461Sobrien const struct s390_opcode *opcode; 11199461Sobrien const struct s390_opcode *opcode_end; 11299461Sobrien unsigned int value; 11399461Sobrien int status, opsize, bufsize; 11499461Sobrien char separator; 11599461Sobrien 11699461Sobrien if (init_flag == 0) 11799461Sobrien init_disasm (info); 11899461Sobrien 11999461Sobrien /* The output looks better if we put 6 bytes on a line. */ 12099461Sobrien info->bytes_per_line = 6; 12199461Sobrien 12299461Sobrien /* Every S390 instruction is max 6 bytes long. */ 12399461Sobrien memset (buffer, 0, 6); 12499461Sobrien status = (*info->read_memory_func) (memaddr, buffer, 6, info); 12599461Sobrien if (status != 0) 12699461Sobrien { 12799461Sobrien for (bufsize = 0; bufsize < 6; bufsize++) 12899461Sobrien if ((*info->read_memory_func) (memaddr, buffer, bufsize + 1, info) != 0) 12999461Sobrien break; 13099461Sobrien if (bufsize <= 0) 13199461Sobrien { 13299461Sobrien (*info->memory_error_func) (status, memaddr, info); 13399461Sobrien return -1; 13499461Sobrien } 13599461Sobrien /* Opsize calculation looks strange but it works 13699461Sobrien 00xxxxxx -> 2 bytes, 01xxxxxx/10xxxxxx -> 4 bytes, 13799461Sobrien 11xxxxxx -> 6 bytes. */ 13899461Sobrien opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1; 13999461Sobrien status = opsize > bufsize; 14099461Sobrien } 14199461Sobrien else 14299461Sobrien { 14399461Sobrien bufsize = 6; 14499461Sobrien opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1; 14599461Sobrien } 14699461Sobrien 14799461Sobrien if (status == 0) 14899461Sobrien { 14999461Sobrien /* Find the first match in the opcode table. */ 15099461Sobrien opcode_end = s390_opcodes + s390_num_opcodes; 15199461Sobrien for (opcode = s390_opcodes + opc_index[(int) buffer[0]]; 15299461Sobrien (opcode < opcode_end) && (buffer[0] == opcode->opcode[0]); 15399461Sobrien opcode++) 15499461Sobrien { 15599461Sobrien const struct s390_operand *operand; 15699461Sobrien const unsigned char *opindex; 15799461Sobrien 15899461Sobrien /* Check architecture. */ 159130561Sobrien if (!(opcode->modes & current_arch_mask)) 16099461Sobrien continue; 16199461Sobrien /* Check signature of the opcode. */ 16299461Sobrien if ((buffer[1] & opcode->mask[1]) != opcode->opcode[1] 16399461Sobrien || (buffer[2] & opcode->mask[2]) != opcode->opcode[2] 16499461Sobrien || (buffer[3] & opcode->mask[3]) != opcode->opcode[3] 16599461Sobrien || (buffer[4] & opcode->mask[4]) != opcode->opcode[4] 16699461Sobrien || (buffer[5] & opcode->mask[5]) != opcode->opcode[5]) 16799461Sobrien continue; 16899461Sobrien 16999461Sobrien /* The instruction is valid. */ 17099461Sobrien if (opcode->operands[0] != 0) 17199461Sobrien (*info->fprintf_func) (info->stream, "%s\t", opcode->name); 17299461Sobrien else 17399461Sobrien (*info->fprintf_func) (info->stream, "%s", opcode->name); 17499461Sobrien 17599461Sobrien /* Extract the operands. */ 17699461Sobrien separator = 0; 17799461Sobrien for (opindex = opcode->operands; *opindex != 0; opindex++) 17899461Sobrien { 17999461Sobrien unsigned int value; 18099461Sobrien 18199461Sobrien operand = s390_operands + *opindex; 18299461Sobrien value = s390_extract_operand (buffer, operand); 18399461Sobrien 18499461Sobrien if ((operand->flags & S390_OPERAND_INDEX) && value == 0) 18599461Sobrien continue; 18699461Sobrien if ((operand->flags & S390_OPERAND_BASE) && 18799461Sobrien value == 0 && separator == '(') 18899461Sobrien { 18999461Sobrien separator = ','; 19099461Sobrien continue; 19199461Sobrien } 19299461Sobrien 19399461Sobrien if (separator) 19499461Sobrien (*info->fprintf_func) (info->stream, "%c", separator); 19599461Sobrien 19699461Sobrien if (operand->flags & S390_OPERAND_GPR) 19799461Sobrien (*info->fprintf_func) (info->stream, "%%r%i", value); 19899461Sobrien else if (operand->flags & S390_OPERAND_FPR) 19999461Sobrien (*info->fprintf_func) (info->stream, "%%f%i", value); 20099461Sobrien else if (operand->flags & S390_OPERAND_AR) 20199461Sobrien (*info->fprintf_func) (info->stream, "%%a%i", value); 20299461Sobrien else if (operand->flags & S390_OPERAND_CR) 20399461Sobrien (*info->fprintf_func) (info->stream, "%%c%i", value); 20499461Sobrien else if (operand->flags & S390_OPERAND_PCREL) 20599461Sobrien (*info->print_address_func) (memaddr + (int) value, info); 20699461Sobrien else if (operand->flags & S390_OPERAND_SIGNED) 20799461Sobrien (*info->fprintf_func) (info->stream, "%i", (int) value); 20899461Sobrien else 209218822Sdim (*info->fprintf_func) (info->stream, "%u", value); 21099461Sobrien 21199461Sobrien if (operand->flags & S390_OPERAND_DISP) 21299461Sobrien { 21399461Sobrien separator = '('; 21499461Sobrien } 21599461Sobrien else if (operand->flags & S390_OPERAND_BASE) 21699461Sobrien { 21799461Sobrien (*info->fprintf_func) (info->stream, ")"); 21899461Sobrien separator = ','; 21999461Sobrien } 22099461Sobrien else 22199461Sobrien separator = ','; 22299461Sobrien } 22399461Sobrien 22499461Sobrien /* Found instruction, printed it, return its size. */ 22599461Sobrien return opsize; 22699461Sobrien } 22799461Sobrien /* No matching instruction found, fall through to hex print. */ 22899461Sobrien } 22999461Sobrien 23099461Sobrien if (bufsize >= 4) 23199461Sobrien { 23299461Sobrien value = (unsigned int) buffer[0]; 23399461Sobrien value = (value << 8) + (unsigned int) buffer[1]; 23499461Sobrien value = (value << 8) + (unsigned int) buffer[2]; 23599461Sobrien value = (value << 8) + (unsigned int) buffer[3]; 23699461Sobrien (*info->fprintf_func) (info->stream, ".long\t0x%08x", value); 23799461Sobrien return 4; 23899461Sobrien } 23999461Sobrien else if (bufsize >= 2) 24099461Sobrien { 24199461Sobrien value = (unsigned int) buffer[0]; 24299461Sobrien value = (value << 8) + (unsigned int) buffer[1]; 24399461Sobrien (*info->fprintf_func) (info->stream, ".short\t0x%04x", value); 24499461Sobrien return 2; 24599461Sobrien } 24699461Sobrien else 24799461Sobrien { 24899461Sobrien value = (unsigned int) buffer[0]; 24999461Sobrien (*info->fprintf_func) (info->stream, ".byte\t0x%02x", value); 25099461Sobrien return 1; 25199461Sobrien } 25299461Sobrien} 253