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