1 /* s390-dis.c -- Disassemble S390 instructions 2 Copyright 2000, 2001, 2002 Free Software Foundation, Inc. 3 Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). 4 5 This file is part of GDB, GAS and the GNU binutils. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 20 02111-1307, USA. */ 21 22 #include <stdio.h> 23 #include "ansidecl.h" 24 #include "sysdep.h" 25 #include "dis-asm.h" 26 #include "opcode/s390.h" 27 28 static int init_flag = 0; 29 static int opc_index[256]; 30 static int current_arch_mask = 0; 31 32 static void init_disasm PARAMS ((struct disassemble_info *)); 33 static unsigned int s390_extract_operand 34 PARAMS ((unsigned char *, const struct s390_operand *)); 35 36 /* Set up index table for first opcode byte. */ 37 38 static void 39 init_disasm (info) 40 struct disassemble_info *info; 41 { 42 const struct s390_opcode *opcode; 43 const struct s390_opcode *opcode_end; 44 45 memset (opc_index, 0, sizeof (opc_index)); 46 opcode_end = s390_opcodes + s390_num_opcodes; 47 for (opcode = s390_opcodes; opcode < opcode_end; opcode++) 48 { 49 opc_index[(int) opcode->opcode[0]] = opcode - s390_opcodes; 50 while ((opcode < opcode_end) && 51 (opcode[1].opcode[0] == opcode->opcode[0])) 52 opcode++; 53 } 54 switch (info->mach) 55 { 56 case bfd_mach_s390_31: 57 current_arch_mask = 1 << S390_OPCODE_ESA; 58 break; 59 case bfd_mach_s390_64: 60 current_arch_mask = 1 << S390_OPCODE_ZARCH; 61 break; 62 default: 63 abort (); 64 } 65 init_flag = 1; 66 } 67 68 /* Extracts an operand value from an instruction. */ 69 70 static inline unsigned int 71 s390_extract_operand (insn, operand) 72 unsigned char *insn; 73 const struct s390_operand *operand; 74 { 75 unsigned int val; 76 int bits; 77 78 /* Extract fragments of the operand byte for byte. */ 79 insn += operand->shift / 8; 80 bits = (operand->shift & 7) + operand->bits; 81 val = 0; 82 do 83 { 84 val <<= 8; 85 val |= (unsigned int) *insn++; 86 bits -= 8; 87 } 88 while (bits > 0); 89 val >>= -bits; 90 val &= ((1U << (operand->bits - 1)) << 1) - 1; 91 92 /* Check for special long displacement case. */ 93 if (operand->bits == 20 && operand->shift == 20) 94 val = (val & 0xff) << 12 | (val & 0xfff00) >> 8; 95 96 /* Sign extend value if the operand is signed or pc relative. */ 97 if ((operand->flags & (S390_OPERAND_SIGNED | S390_OPERAND_PCREL)) 98 && (val & (1U << (operand->bits - 1)))) 99 val |= (-1U << (operand->bits - 1)) << 1; 100 101 /* Double value if the operand is pc relative. */ 102 if (operand->flags & S390_OPERAND_PCREL) 103 val <<= 1; 104 105 /* Length x in an instructions has real length x+1. */ 106 if (operand->flags & S390_OPERAND_LENGTH) 107 val++; 108 return val; 109 } 110 111 /* Print a S390 instruction. */ 112 113 int 114 print_insn_s390 (memaddr, info) 115 bfd_vma memaddr; 116 struct disassemble_info *info; 117 { 118 bfd_byte buffer[6]; 119 const struct s390_opcode *opcode; 120 const struct s390_opcode *opcode_end; 121 unsigned int value; 122 int status, opsize, bufsize; 123 char separator; 124 125 if (init_flag == 0) 126 init_disasm (info); 127 128 /* The output looks better if we put 6 bytes on a line. */ 129 info->bytes_per_line = 6; 130 131 /* Every S390 instruction is max 6 bytes long. */ 132 memset (buffer, 0, 6); 133 status = (*info->read_memory_func) (memaddr, buffer, 6, info); 134 if (status != 0) 135 { 136 for (bufsize = 0; bufsize < 6; bufsize++) 137 if ((*info->read_memory_func) (memaddr, buffer, bufsize + 1, info) != 0) 138 break; 139 if (bufsize <= 0) 140 { 141 (*info->memory_error_func) (status, memaddr, info); 142 return -1; 143 } 144 /* Opsize calculation looks strange but it works 145 00xxxxxx -> 2 bytes, 01xxxxxx/10xxxxxx -> 4 bytes, 146 11xxxxxx -> 6 bytes. */ 147 opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1; 148 status = opsize > bufsize; 149 } 150 else 151 { 152 bufsize = 6; 153 opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1; 154 } 155 156 if (status == 0) 157 { 158 /* Find the first match in the opcode table. */ 159 opcode_end = s390_opcodes + s390_num_opcodes; 160 for (opcode = s390_opcodes + opc_index[(int) buffer[0]]; 161 (opcode < opcode_end) && (buffer[0] == opcode->opcode[0]); 162 opcode++) 163 { 164 const struct s390_operand *operand; 165 const unsigned char *opindex; 166 167 /* Check architecture. */ 168 if (!(opcode->modes & current_arch_mask)) 169 continue; 170 /* Check signature of the opcode. */ 171 if ((buffer[1] & opcode->mask[1]) != opcode->opcode[1] 172 || (buffer[2] & opcode->mask[2]) != opcode->opcode[2] 173 || (buffer[3] & opcode->mask[3]) != opcode->opcode[3] 174 || (buffer[4] & opcode->mask[4]) != opcode->opcode[4] 175 || (buffer[5] & opcode->mask[5]) != opcode->opcode[5]) 176 continue; 177 178 /* The instruction is valid. */ 179 if (opcode->operands[0] != 0) 180 (*info->fprintf_func) (info->stream, "%s\t", opcode->name); 181 else 182 (*info->fprintf_func) (info->stream, "%s", opcode->name); 183 184 /* Extract the operands. */ 185 separator = 0; 186 for (opindex = opcode->operands; *opindex != 0; opindex++) 187 { 188 unsigned int value; 189 190 operand = s390_operands + *opindex; 191 value = s390_extract_operand (buffer, operand); 192 193 if ((operand->flags & S390_OPERAND_INDEX) && value == 0) 194 continue; 195 if ((operand->flags & S390_OPERAND_BASE) && 196 value == 0 && separator == '(') 197 { 198 separator = ','; 199 continue; 200 } 201 202 if (separator) 203 (*info->fprintf_func) (info->stream, "%c", separator); 204 205 if (operand->flags & S390_OPERAND_GPR) 206 (*info->fprintf_func) (info->stream, "%%r%i", value); 207 else if (operand->flags & S390_OPERAND_FPR) 208 (*info->fprintf_func) (info->stream, "%%f%i", value); 209 else if (operand->flags & S390_OPERAND_AR) 210 (*info->fprintf_func) (info->stream, "%%a%i", value); 211 else if (operand->flags & S390_OPERAND_CR) 212 (*info->fprintf_func) (info->stream, "%%c%i", value); 213 else if (operand->flags & S390_OPERAND_PCREL) 214 (*info->print_address_func) (memaddr + (int) value, info); 215 else if (operand->flags & S390_OPERAND_SIGNED) 216 (*info->fprintf_func) (info->stream, "%i", (int) value); 217 else 218 (*info->fprintf_func) (info->stream, "%i", value); 219 220 if (operand->flags & S390_OPERAND_DISP) 221 { 222 separator = '('; 223 } 224 else if (operand->flags & S390_OPERAND_BASE) 225 { 226 (*info->fprintf_func) (info->stream, ")"); 227 separator = ','; 228 } 229 else 230 separator = ','; 231 } 232 233 /* Found instruction, printed it, return its size. */ 234 return opsize; 235 } 236 /* No matching instruction found, fall through to hex print. */ 237 } 238 239 if (bufsize >= 4) 240 { 241 value = (unsigned int) buffer[0]; 242 value = (value << 8) + (unsigned int) buffer[1]; 243 value = (value << 8) + (unsigned int) buffer[2]; 244 value = (value << 8) + (unsigned int) buffer[3]; 245 (*info->fprintf_func) (info->stream, ".long\t0x%08x", value); 246 return 4; 247 } 248 else if (bufsize >= 2) 249 { 250 value = (unsigned int) buffer[0]; 251 value = (value << 8) + (unsigned int) buffer[1]; 252 (*info->fprintf_func) (info->stream, ".short\t0x%04x", value); 253 return 2; 254 } 255 else 256 { 257 value = (unsigned int) buffer[0]; 258 (*info->fprintf_func) (info->stream, ".byte\t0x%02x", value); 259 return 1; 260 } 261 } 262