| 1 | /* Print Z80, Z180, EZ80 and R800 instructions |
| 2 | Copyright (C) 2005-2020 Free Software Foundation, Inc. |
| 3 | Contributed by Arnold Metselaar <arnold_m@operamail.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 program; if not, write to the Free Software |
| 19 | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| 20 | MA 02110-1301, USA. */ |
| 21 | |
| 22 | #include "sysdep.h" |
| 23 | #include "disassemble.h" |
| 24 | #include <stdio.h> |
| 25 | |
| 26 | struct buffer |
| 27 | { |
| 28 | bfd_vma base; |
| 29 | int n_fetch; |
| 30 | int n_used; |
| 31 | signed char data[6]; |
| 32 | long inss; /* instruction set bit mask, taken from bfd_mach */ |
| 33 | int nn_len; /* address length: 2 - Z80 mode, 3 - ADL mode*/ |
| 34 | } ; |
| 35 | |
| 36 | typedef int (*func)(struct buffer *, disassemble_info *, const char *); |
| 37 | |
| 38 | struct tab_elt |
| 39 | { |
| 40 | unsigned char val; |
| 41 | unsigned char mask; |
| 42 | func fp; |
| 43 | const char * text; |
| 44 | unsigned inss; /* bit mask of supported bfd_mach_* or 0 for all mach */ |
| 45 | } ; |
| 46 | |
| 47 | #define INSS_ALL 0 |
| 48 | #define INSS_Z80 ((1 << bfd_mach_z80) | (1 << bfd_mach_z80strict) | (1 << bfd_mach_z80full)) |
| 49 | #define INSS_R800 (1 << bfd_mach_r800) |
| 50 | #define INSS_GBZ80 (1 << bfd_mach_gbz80) |
| 51 | #define INSS_Z180 (1 << bfd_mach_z180) |
| 52 | #define INSS_EZ80_Z80 (1 << bfd_mach_ez80_z80) |
| 53 | #define INSS_EZ80_ADL (1 << bfd_mach_ez80_adl) |
| 54 | #define INSS_EZ80 (INSS_EZ80_ADL | INSS_EZ80_Z80) |
| 55 | |
| 56 | #define TXTSIZ 24 |
| 57 | /* Names of 16-bit registers. */ |
| 58 | static const char * rr_str[] = { "bc", "de", "hl", "sp" }; |
| 59 | /* Names of 8-bit registers. */ |
| 60 | static const char * r_str[] = { "b", "c", "d", "e", "h", "l", "(hl)", "a" }; |
| 61 | /* Texts for condition codes. */ |
| 62 | static const char * cc_str[] = { "nz", "z", "nc", "c", "po", "pe", "p", "m" }; |
| 63 | /* Instruction names for 8-bit arithmetic, operand "a" is often implicit */ |
| 64 | static const char * arit_str[] = |
| 65 | { |
| 66 | "add a,", "adc a,", "sub ", "sbc a,", "and ", "xor ", "or ", "cp " |
| 67 | } ; |
| 68 | static const char * arit_str_ez80[] = |
| 69 | { |
| 70 | "add a,", "adc a,", "sub a,", "sbc a,", "and a,", "xor a,", "or a,", "cp a," |
| 71 | } ; |
| 72 | |
| 73 | \f |
| 74 | static int |
| 75 | mach_inst (struct buffer *buf, struct tab_elt *p) |
| 76 | { |
| 77 | return !p->inss || (p->inss & buf->inss); |
| 78 | } |
| 79 | |
| 80 | static int |
| 81 | fetch_data (struct buffer *buf, disassemble_info * info, int n) |
| 82 | { |
| 83 | int r; |
| 84 | |
| 85 | if (buf->n_fetch + n > (int)sizeof(buf->data)) |
| 86 | abort (); |
| 87 | |
| 88 | r = info->read_memory_func (buf->base + buf->n_fetch, |
| 89 | (unsigned char*) buf->data + buf->n_fetch, |
| 90 | n, info); |
| 91 | if (r == 0) |
| 92 | buf->n_fetch += n; |
| 93 | return !r; |
| 94 | } |
| 95 | |
| 96 | static int |
| 97 | prt (struct buffer *buf, disassemble_info * info, const char *txt) |
| 98 | { |
| 99 | info->fprintf_func (info->stream, "%s", txt); |
| 100 | buf->n_used = buf->n_fetch; |
| 101 | return 1; |
| 102 | } |
| 103 | |
| 104 | static int |
| 105 | prt_e (struct buffer *buf, disassemble_info * info, const char *txt) |
| 106 | { |
| 107 | char e; |
| 108 | int target_addr; |
| 109 | |
| 110 | if (fetch_data (buf, info, 1)) |
| 111 | { |
| 112 | e = buf->data[1]; |
| 113 | target_addr = (buf->base + 2 + e) & 0xffff; |
| 114 | buf->n_used = buf->n_fetch; |
| 115 | info->fprintf_func (info->stream, "%s0x%04x", txt, target_addr); |
| 116 | } |
| 117 | else |
| 118 | buf->n_used = -1; |
| 119 | |
| 120 | return buf->n_used; |
| 121 | } |
| 122 | |
| 123 | static int |
| 124 | jr_cc (struct buffer *buf, disassemble_info * info, const char *txt) |
| 125 | { |
| 126 | char mytxt[TXTSIZ]; |
| 127 | |
| 128 | snprintf (mytxt, TXTSIZ, txt, cc_str[(buf->data[0] >> 3) & 3]); |
| 129 | return prt_e (buf, info, mytxt); |
| 130 | } |
| 131 | |
| 132 | static int |
| 133 | prt_nn (struct buffer *buf, disassemble_info * info, const char *txt) |
| 134 | { |
| 135 | int nn; |
| 136 | unsigned char *p; |
| 137 | int i; |
| 138 | |
| 139 | p = (unsigned char*) buf->data + buf->n_fetch; |
| 140 | if (fetch_data (buf, info, buf->nn_len)) |
| 141 | { |
| 142 | nn = 0; |
| 143 | i = buf->nn_len; |
| 144 | while (i--) |
| 145 | nn = nn * 0x100 + p[i]; |
| 146 | info->fprintf_func (info->stream, txt, nn); |
| 147 | buf->n_used = buf->n_fetch; |
| 148 | } |
| 149 | else |
| 150 | buf->n_used = -1; |
| 151 | return buf->n_used; |
| 152 | } |
| 153 | |
| 154 | static int |
| 155 | prt_rr_nn (struct buffer *buf, disassemble_info * info, const char *txt) |
| 156 | { |
| 157 | char mytxt[TXTSIZ]; |
| 158 | int rr; |
| 159 | |
| 160 | rr = (buf->data[buf->n_fetch - 1] >> 4) & 3; |
| 161 | snprintf (mytxt, TXTSIZ, txt, rr_str[rr]); |
| 162 | return prt_nn (buf, info, mytxt); |
| 163 | } |
| 164 | |
| 165 | static int |
| 166 | prt_rr (struct buffer *buf, disassemble_info * info, const char *txt) |
| 167 | { |
| 168 | info->fprintf_func (info->stream, "%s%s", txt, |
| 169 | rr_str[(buf->data[buf->n_fetch - 1] >> 4) & 3]); |
| 170 | buf->n_used = buf->n_fetch; |
| 171 | return buf->n_used; |
| 172 | } |
| 173 | |
| 174 | static int |
| 175 | prt_n (struct buffer *buf, disassemble_info * info, const char *txt) |
| 176 | { |
| 177 | int n; |
| 178 | unsigned char *p; |
| 179 | |
| 180 | p = (unsigned char*) buf->data + buf->n_fetch; |
| 181 | |
| 182 | if (fetch_data (buf, info, 1)) |
| 183 | { |
| 184 | n = p[0]; |
| 185 | info->fprintf_func (info->stream, txt, n); |
| 186 | buf->n_used = buf->n_fetch; |
| 187 | } |
| 188 | else |
| 189 | buf->n_used = -1; |
| 190 | |
| 191 | return buf->n_used; |
| 192 | } |
| 193 | |
| 194 | static int |
| 195 | prt_r_n (struct buffer *buf, disassemble_info * info, const char *txt) |
| 196 | { |
| 197 | char mytxt[TXTSIZ]; |
| 198 | int r; |
| 199 | |
| 200 | r = (buf->data[buf->n_fetch - 1] >> 3) & 7; |
| 201 | snprintf (mytxt, TXTSIZ, txt, r_str[r]); |
| 202 | return prt_n (buf, info, mytxt); |
| 203 | } |
| 204 | |
| 205 | static int |
| 206 | ld_r_n (struct buffer *buf, disassemble_info * info, const char *txt) |
| 207 | { |
| 208 | char mytxt[TXTSIZ]; |
| 209 | |
| 210 | snprintf (mytxt, TXTSIZ, txt, r_str[(buf->data[buf->n_fetch - 1] >> 3) & 7]); |
| 211 | return prt_n (buf, info, mytxt); |
| 212 | } |
| 213 | |
| 214 | static int |
| 215 | prt_r (struct buffer *buf, disassemble_info * info, const char *txt) |
| 216 | { |
| 217 | info->fprintf_func (info->stream, txt, |
| 218 | r_str[(buf->data[buf->n_fetch - 1] >> 3) & 7]); |
| 219 | buf->n_used = buf->n_fetch; |
| 220 | return buf->n_used; |
| 221 | } |
| 222 | |
| 223 | static int |
| 224 | ld_r_r (struct buffer *buf, disassemble_info * info, const char *txt) |
| 225 | { |
| 226 | info->fprintf_func (info->stream, txt, |
| 227 | r_str[(buf->data[buf->n_fetch - 1] >> 3) & 7], |
| 228 | r_str[buf->data[buf->n_fetch - 1] & 7]); |
| 229 | buf->n_used = buf->n_fetch; |
| 230 | return buf->n_used; |
| 231 | } |
| 232 | |
| 233 | static int |
| 234 | prt_d (struct buffer *buf, disassemble_info * info, const char *txt) |
| 235 | { |
| 236 | int d; |
| 237 | signed char *p; |
| 238 | |
| 239 | p = buf->data + buf->n_fetch; |
| 240 | |
| 241 | if (fetch_data (buf, info, 1)) |
| 242 | { |
| 243 | d = p[0]; |
| 244 | info->fprintf_func (info->stream, txt, d); |
| 245 | buf->n_used = buf->n_fetch; |
| 246 | } |
| 247 | else |
| 248 | buf->n_used = -1; |
| 249 | |
| 250 | return buf->n_used; |
| 251 | } |
| 252 | |
| 253 | static int |
| 254 | prt_rr_d (struct buffer *buf, disassemble_info * info, const char *txt) |
| 255 | { |
| 256 | char mytxt[TXTSIZ]; |
| 257 | int rr; |
| 258 | |
| 259 | rr = (buf->data[buf->n_fetch - 1] >> 4) & 3; |
| 260 | if (rr == 3) /* SP is not supported */ |
| 261 | return 0; |
| 262 | |
| 263 | snprintf (mytxt, TXTSIZ, txt, rr_str[rr]); |
| 264 | return prt_d (buf, info, mytxt); |
| 265 | } |
| 266 | |
| 267 | static int |
| 268 | arit_r (struct buffer *buf, disassemble_info * info, const char *txt) |
| 269 | { |
| 270 | const char * const *arit; |
| 271 | arit = (buf->inss & INSS_EZ80) ? arit_str_ez80 : arit_str; |
| 272 | info->fprintf_func (info->stream, txt, |
| 273 | arit[(buf->data[buf->n_fetch - 1] >> 3) & 7], |
| 274 | r_str[buf->data[buf->n_fetch - 1] & 7]); |
| 275 | buf->n_used = buf->n_fetch; |
| 276 | return buf->n_used; |
| 277 | } |
| 278 | |
| 279 | static int |
| 280 | prt_cc (struct buffer *buf, disassemble_info * info, const char *txt) |
| 281 | { |
| 282 | info->fprintf_func (info->stream, "%s%s", txt, |
| 283 | cc_str[(buf->data[0] >> 3) & 7]); |
| 284 | buf->n_used = buf->n_fetch; |
| 285 | return buf->n_used; |
| 286 | } |
| 287 | |
| 288 | static int |
| 289 | pop_rr (struct buffer *buf, disassemble_info * info, const char *txt) |
| 290 | { |
| 291 | static char *rr_stack[] = { "bc","de","hl","af"}; |
| 292 | |
| 293 | info->fprintf_func (info->stream, "%s %s", txt, |
| 294 | rr_stack[(buf->data[0] >> 4) & 3]); |
| 295 | buf->n_used = buf->n_fetch; |
| 296 | return buf->n_used; |
| 297 | } |
| 298 | |
| 299 | |
| 300 | static int |
| 301 | jp_cc_nn (struct buffer *buf, disassemble_info * info, const char *txt) |
| 302 | { |
| 303 | char mytxt[TXTSIZ]; |
| 304 | |
| 305 | snprintf (mytxt,TXTSIZ, |
| 306 | "%s%s,0x%%04x", txt, cc_str[(buf->data[0] >> 3) & 7]); |
| 307 | return prt_nn (buf, info, mytxt); |
| 308 | } |
| 309 | |
| 310 | static int |
| 311 | arit_n (struct buffer *buf, disassemble_info * info, const char *txt) |
| 312 | { |
| 313 | char mytxt[TXTSIZ]; |
| 314 | const char * const *arit; |
| 315 | |
| 316 | arit = (buf->inss & INSS_EZ80) ? arit_str_ez80 : arit_str; |
| 317 | snprintf (mytxt,TXTSIZ, txt, arit[(buf->data[0] >> 3) & 7]); |
| 318 | return prt_n (buf, info, mytxt); |
| 319 | } |
| 320 | |
| 321 | static int |
| 322 | rst (struct buffer *buf, disassemble_info * info, const char *txt) |
| 323 | { |
| 324 | info->fprintf_func (info->stream, txt, buf->data[0] & 0x38); |
| 325 | buf->n_used = buf->n_fetch; |
| 326 | return buf->n_used; |
| 327 | } |
| 328 | |
| 329 | \f |
| 330 | static int |
| 331 | cis (struct buffer *buf, disassemble_info * info, const char *txt ATTRIBUTE_UNUSED) |
| 332 | { |
| 333 | static char * opar[] = { "ld", "cp", "in", "out" }; |
| 334 | char * op; |
| 335 | char c; |
| 336 | |
| 337 | c = buf->data[1]; |
| 338 | op = ((0x13 & c) == 0x13) ? "ot" : (opar[c & 3]); |
| 339 | info->fprintf_func (info->stream, |
| 340 | "%s%c%s", op, |
| 341 | (c & 0x08) ? 'd' : 'i', |
| 342 | (c & 0x10) ? "r" : ""); |
| 343 | buf->n_used = 2; |
| 344 | return buf->n_used; |
| 345 | } |
| 346 | |
| 347 | static int |
| 348 | cism (struct buffer *buf, disassemble_info * info, const char *txt ATTRIBUTE_UNUSED) |
| 349 | { |
| 350 | static char * opar[] = { "in%cm%s", "ot%cm%s" }; |
| 351 | char * op; |
| 352 | char c; |
| 353 | |
| 354 | c = buf->data[1]; |
| 355 | op = opar[c & 1]; |
| 356 | info->fprintf_func (info->stream, |
| 357 | op, |
| 358 | (c & 0x08) ? 'd' : 'i', |
| 359 | (c & 0x10) ? "r" : ""); |
| 360 | buf->n_used = 2; |
| 361 | return buf->n_used; |
| 362 | } |
| 363 | |
| 364 | static int |
| 365 | cis2 (struct buffer *buf, disassemble_info * info, const char *txt ATTRIBUTE_UNUSED) |
| 366 | { |
| 367 | static char * opar[] = { "in", "out" }; |
| 368 | char * op; |
| 369 | char c; |
| 370 | |
| 371 | c = buf->data[1]; |
| 372 | op = ((0x14 & c) == 0x14) ? "ot" : (opar[c & 1]); |
| 373 | info->fprintf_func (info->stream, |
| 374 | "%s%c2%s", |
| 375 | op, |
| 376 | (c & 0x08) ? 'd' : 'i', |
| 377 | (c & 0x10) ? "r" : ""); |
| 378 | buf->n_used = 2; |
| 379 | return buf->n_used; |
| 380 | } |
| 381 | |
| 382 | static int |
| 383 | dump (struct buffer *buf, disassemble_info * info, const char *txt) |
| 384 | { |
| 385 | int i; |
| 386 | |
| 387 | info->fprintf_func (info->stream, "defb "); |
| 388 | for (i = 0; txt[i]; ++i) |
| 389 | info->fprintf_func (info->stream, i ? ", 0x%02x" : "0x%02x", |
| 390 | (unsigned char) buf->data[i]); |
| 391 | buf->n_used = i; |
| 392 | return buf->n_used; |
| 393 | } |
| 394 | \f |
| 395 | /* Table to disassemble machine codes with prefix 0xED. */ |
| 396 | struct tab_elt opc_ed[] = |
| 397 | { |
| 398 | { 0x30, 0xFE, dump, "xx", INSS_ALL }, |
| 399 | { 0x00, 0xC7, prt_r_n, "in0 %s,(0x%%02x)", INSS_Z180|INSS_EZ80 }, |
| 400 | { 0x01, 0xC7, prt_r_n, "out0 (0x%%02x),%s", INSS_Z180|INSS_EZ80 }, |
| 401 | { 0x32, 0xFF, prt_d, "lea ix,ix%+d", INSS_EZ80 }, |
| 402 | { 0x33, 0xFF, prt_d, "lea iy,iy%+d", INSS_EZ80 }, |
| 403 | { 0x02, 0xCF, prt_rr_d, "lea %s,ix%%+d", INSS_EZ80 }, |
| 404 | { 0x03, 0xCF, prt_rr_d, "lea %s,iy%%+d", INSS_EZ80 }, |
| 405 | { 0x04, 0xC7, prt_r, "tst %s", INSS_Z180}, |
| 406 | { 0x04, 0xC7, prt_r, "tst a,%s", INSS_EZ80 }, |
| 407 | { 0x07, 0xFF, prt, "ld bc,(hl)", INSS_EZ80 }, |
| 408 | { 0x0F, 0xCF, prt_rr, "ld (hl),", INSS_EZ80 }, |
| 409 | { 0x17, 0xFF, prt, "ld de,(hl)", INSS_EZ80 }, |
| 410 | { 0x27, 0xFF, prt, "ld hl,(hl)", INSS_EZ80 }, |
| 411 | { 0x36, 0xFF, prt, "ld iy,(hl)", INSS_EZ80 }, |
| 412 | { 0x37, 0xFF, prt, "ld ix,(hl)", INSS_EZ80 }, |
| 413 | { 0x3E, 0xFF, prt, "ld (hl),iy", INSS_EZ80 }, |
| 414 | { 0x3F, 0xFF, prt, "ld (hl),ix", INSS_EZ80 }, |
| 415 | { 0x70, 0xFF, prt, "in f,(c)", INSS_Z80 | INSS_R800 }, |
| 416 | { 0x70, 0xFF, dump, "xx", INSS_ALL }, |
| 417 | { 0x40, 0xC7, prt_r, "in %s,(bc)", INSS_EZ80 }, |
| 418 | { 0x40, 0xC7, prt_r, "in %s,(c)", INSS_ALL }, |
| 419 | { 0x71, 0xFF, prt, "out (c),0", INSS_Z80 }, |
| 420 | { 0x70, 0xFF, dump, "xx", INSS_ALL }, |
| 421 | { 0x41, 0xC7, prt_r, "out (bc),%s", INSS_EZ80 }, |
| 422 | { 0x41, 0xC7, prt_r, "out (c),%s", INSS_ALL }, |
| 423 | { 0x42, 0xCF, prt_rr, "sbc hl,", INSS_ALL }, |
| 424 | { 0x43, 0xCF, prt_rr_nn, "ld (0x%%04x),%s", INSS_ALL }, |
| 425 | { 0x44, 0xFF, prt, "neg", INSS_ALL }, |
| 426 | { 0x45, 0xFF, prt, "retn", INSS_ALL }, |
| 427 | { 0x46, 0xFF, prt, "im 0", INSS_ALL }, |
| 428 | { 0x47, 0xFF, prt, "ld i,a", INSS_ALL }, |
| 429 | { 0x4A, 0xCF, prt_rr, "adc hl,", INSS_ALL }, |
| 430 | { 0x4B, 0xCF, prt_rr_nn, "ld %s,(0x%%04x)", INSS_ALL }, |
| 431 | { 0x4C, 0xCF, prt_rr, "mlt ", INSS_Z180|INSS_EZ80 }, |
| 432 | { 0x4D, 0xFF, prt, "reti", INSS_ALL }, |
| 433 | { 0x4F, 0xFF, prt, "ld r,a", INSS_ALL }, |
| 434 | { 0x54, 0xFF, prt_d, "lea ix,iy%+d", INSS_EZ80 }, |
| 435 | { 0x55, 0xFF, prt_d, "lea iy,ix%+d", INSS_EZ80 }, |
| 436 | { 0x56, 0xFF, prt, "im 1", INSS_ALL }, |
| 437 | { 0x57, 0xFF, prt, "ld a,i", INSS_ALL }, |
| 438 | { 0x5E, 0xFF, prt, "im 2", INSS_ALL }, |
| 439 | { 0x5F, 0xFF, prt, "ld a,r", INSS_ALL }, |
| 440 | { 0x64, 0xFF, prt_n, "tst 0x%02x", INSS_Z180 }, |
| 441 | { 0x64, 0xFF, prt_n, "tst a,0x%02x", INSS_EZ80 }, |
| 442 | { 0x65, 0xFF, prt_d, "pea ix%+d", INSS_EZ80 }, |
| 443 | { 0x66, 0xFF, prt_d, "pea iy%+d", INSS_EZ80 }, |
| 444 | { 0x67, 0xFF, prt, "rrd", INSS_ALL }, |
| 445 | { 0x6F, 0xFF, prt, "rld", INSS_ALL }, |
| 446 | { 0x74, 0xFF, prt_n, "tstio 0x%02x", INSS_Z180|INSS_EZ80 }, |
| 447 | { 0x76, 0xFF, prt, "slp", INSS_Z180|INSS_EZ80 }, |
| 448 | { 0x82, 0xE6, cism, "", INSS_Z180|INSS_EZ80 }, |
| 449 | { 0x84, 0xC7, cis2, "", INSS_EZ80 }, |
| 450 | { 0xA0, 0xE4, cis, "", INSS_ALL }, |
| 451 | { 0x7D, 0xFF, prt, "stmix", INSS_EZ80 }, |
| 452 | { 0x7E, 0xFF, prt, "rsmix", INSS_EZ80 }, |
| 453 | { 0x6D, 0xFF, prt, "ld mb,a", INSS_EZ80 }, |
| 454 | { 0x6E, 0xFF, prt, "ld a,mb", INSS_EZ80 }, |
| 455 | { 0xC7, 0xFF, prt, "ld i,hl", INSS_EZ80 }, |
| 456 | { 0xD7, 0xFF, prt, "ld hl,i", INSS_EZ80 }, |
| 457 | { 0xC2, 0xFF, prt, "inirx", INSS_EZ80 }, |
| 458 | { 0xC3, 0xFF, prt, "otirx", INSS_EZ80 }, |
| 459 | { 0xCA, 0xFF, prt, "indrx", INSS_EZ80 }, |
| 460 | { 0xCB, 0xFF, prt, "otdrx", INSS_EZ80 }, |
| 461 | { 0xC3, 0xFF, prt, "muluw hl,bc", INSS_R800 }, |
| 462 | { 0xC5, 0xE7, prt_r, "mulub a,%s", INSS_R800 }, |
| 463 | { 0xF3, 0xFF, prt, "muluw hl,sp", INSS_R800 }, |
| 464 | { 0x00, 0x00, dump, "xx", INSS_ALL } |
| 465 | }; |
| 466 | |
| 467 | static int |
| 468 | pref_ed (struct buffer *buf, disassemble_info *info, |
| 469 | const char *txt ATTRIBUTE_UNUSED) |
| 470 | { |
| 471 | struct tab_elt *p; |
| 472 | |
| 473 | if (fetch_data(buf, info, 1)) |
| 474 | { |
| 475 | for (p = opc_ed; p->val != (buf->data[1] & p->mask) || !mach_inst(buf, p); ++p) |
| 476 | ; |
| 477 | p->fp (buf, info, p->text); |
| 478 | } |
| 479 | else |
| 480 | buf->n_used = -1; |
| 481 | |
| 482 | return buf->n_used; |
| 483 | } |
| 484 | \f |
| 485 | /* Instruction names for the instructions addressing single bits. */ |
| 486 | static char *cb1_str[] = { "", "bit", "res", "set"}; |
| 487 | /* Instruction names for shifts and rotates. */ |
| 488 | static char *cb2_str[] = |
| 489 | { |
| 490 | "rlc", "rrc", "rl", "rr", "sla", "sra", "sli", "srl" |
| 491 | }; |
| 492 | |
| 493 | static int |
| 494 | pref_cb (struct buffer *buf, disassemble_info *info, |
| 495 | const char *txt ATTRIBUTE_UNUSED) |
| 496 | { |
| 497 | const char *op_txt; |
| 498 | int idx; |
| 499 | if (fetch_data (buf, info, 1)) |
| 500 | { |
| 501 | buf->n_used = 2; |
| 502 | if ((buf->data[1] & 0xc0) == 0) |
| 503 | { |
| 504 | idx = (buf->data[1] >> 3) & 7; |
| 505 | if ((buf->inss & INSS_GBZ80) && (idx == 6)) |
| 506 | op_txt = "swap"; |
| 507 | else |
| 508 | op_txt = cb2_str[idx]; |
| 509 | info->fprintf_func (info->stream, "%s %s", |
| 510 | op_txt, |
| 511 | r_str[buf->data[1] & 7]); |
| 512 | } |
| 513 | else |
| 514 | info->fprintf_func (info->stream, "%s %d,%s", |
| 515 | cb1_str[(buf->data[1] >> 6) & 3], |
| 516 | (buf->data[1] >> 3) & 7, |
| 517 | r_str[buf->data[1] & 7]); |
| 518 | } |
| 519 | else |
| 520 | buf->n_used = -1; |
| 521 | |
| 522 | return buf->n_used; |
| 523 | } |
| 524 | \f |
| 525 | static int |
| 526 | addvv (struct buffer * buf, disassemble_info * info, const char *txt) |
| 527 | { |
| 528 | info->fprintf_func (info->stream, "add %s,%s", txt, txt); |
| 529 | |
| 530 | return buf->n_used = buf->n_fetch; |
| 531 | } |
| 532 | |
| 533 | static int |
| 534 | ld_v_v (struct buffer * buf, disassemble_info * info, const char *txt) |
| 535 | { |
| 536 | char mytxt[TXTSIZ]; |
| 537 | |
| 538 | snprintf (mytxt, TXTSIZ, "ld %s%%s,%s%%s", txt, txt); |
| 539 | return ld_r_r (buf, info, mytxt); |
| 540 | } |
| 541 | |
| 542 | static int |
| 543 | prt_d_n (struct buffer *buf, disassemble_info * info, const char *txt) |
| 544 | { |
| 545 | char mytxt[TXTSIZ]; |
| 546 | int d; |
| 547 | signed char *p; |
| 548 | |
| 549 | p = buf->data + buf->n_fetch; |
| 550 | |
| 551 | if (fetch_data (buf, info, 1)) |
| 552 | { |
| 553 | d = p[0]; |
| 554 | snprintf (mytxt, TXTSIZ, txt, d); |
| 555 | return prt_n (buf, info, mytxt); |
| 556 | } |
| 557 | else |
| 558 | buf->n_used = -1; |
| 559 | |
| 560 | return buf->n_used; |
| 561 | } |
| 562 | |
| 563 | static int |
| 564 | arit_d (struct buffer *buf, disassemble_info * info, const char *txt) |
| 565 | { |
| 566 | char mytxt[TXTSIZ]; |
| 567 | signed char c; |
| 568 | const char * const *arit; |
| 569 | |
| 570 | arit = (buf->inss & INSS_EZ80) ? arit_str_ez80 : arit_str; |
| 571 | c = buf->data[buf->n_fetch - 1]; |
| 572 | snprintf (mytxt, TXTSIZ, txt, arit[(c >> 3) & 7]); |
| 573 | return prt_d (buf, info, mytxt); |
| 574 | } |
| 575 | |
| 576 | static int |
| 577 | ld_r_d (struct buffer *buf, disassemble_info * info, const char *txt) |
| 578 | { |
| 579 | char mytxt[TXTSIZ]; |
| 580 | signed char c; |
| 581 | |
| 582 | c = buf->data[buf->n_fetch - 1]; |
| 583 | snprintf (mytxt, TXTSIZ, txt, r_str[(c >> 3) & 7]); |
| 584 | return prt_d (buf, info, mytxt); |
| 585 | } |
| 586 | |
| 587 | static int |
| 588 | ld_d_r(struct buffer *buf, disassemble_info * info, const char *txt) |
| 589 | { |
| 590 | char mytxt[TXTSIZ]; |
| 591 | signed char c; |
| 592 | |
| 593 | c = buf->data[buf->n_fetch - 1]; |
| 594 | snprintf (mytxt, TXTSIZ, txt, r_str[c & 7]); |
| 595 | return prt_d (buf, info, mytxt); |
| 596 | } |
| 597 | |
| 598 | static int |
| 599 | ld_ii_ii(struct buffer *buf, disassemble_info * info, const char *txt) |
| 600 | { |
| 601 | char mytxt[TXTSIZ]; |
| 602 | signed char c; |
| 603 | int p; |
| 604 | static const char *ii[2] = { "ix", "iy" }; |
| 605 | |
| 606 | p = (buf->data[buf->n_fetch - 2] == '\xdd') ? 0 : 1; |
| 607 | c = buf->data[buf->n_fetch - 1]; |
| 608 | if ((c & 0x07) != 0x07) |
| 609 | p = 1 - p; /* 0 -> 1, 1 -> 0 */ |
| 610 | snprintf (mytxt, TXTSIZ, txt, ii[p]); |
| 611 | return prt_d (buf, info, mytxt); |
| 612 | } |
| 613 | |
| 614 | static int |
| 615 | pref_xd_cb (struct buffer * buf, disassemble_info * info, const char *txt) |
| 616 | { |
| 617 | if (fetch_data (buf, info, 2)) |
| 618 | { |
| 619 | int d; |
| 620 | char arg[TXTSIZ]; |
| 621 | signed char *p; |
| 622 | |
| 623 | buf->n_used = 4; |
| 624 | p = buf->data; |
| 625 | d = p[2]; |
| 626 | |
| 627 | if (((p[3] & 0xC0) == 0x40) || ((p[3] & 7) == 0x06)) |
| 628 | snprintf (arg, TXTSIZ, "(%s%+d)", txt, d); |
| 629 | else |
| 630 | snprintf (arg, TXTSIZ, "(%s%+d),%s", txt, d, r_str[p[3] & 7]); |
| 631 | |
| 632 | if ((p[3] & 0xc0) == 0) |
| 633 | info->fprintf_func (info->stream, "%s %s", |
| 634 | cb2_str[(buf->data[3] >> 3) & 7], |
| 635 | arg); |
| 636 | else |
| 637 | info->fprintf_func (info->stream, "%s %d,%s", |
| 638 | cb1_str[(buf->data[3] >> 6) & 3], |
| 639 | (buf->data[3] >> 3) & 7, |
| 640 | arg); |
| 641 | } |
| 642 | else |
| 643 | buf->n_used = -1; |
| 644 | |
| 645 | return buf->n_used; |
| 646 | } |
| 647 | \f |
| 648 | /* Table to disassemble machine codes with prefix 0xDD or 0xFD. */ |
| 649 | static struct tab_elt opc_ind[] = |
| 650 | { |
| 651 | { 0x07, 0xFF, prt_d, "ld bc,(%s%%+d)", INSS_EZ80 }, |
| 652 | { 0x0F, 0xFF, prt_d, "ld (%s%%+d),bc", INSS_EZ80 }, |
| 653 | { 0x17, 0xFF, prt_d, "ld de,(%s%%+d)", INSS_EZ80 }, |
| 654 | { 0x1F, 0xFF, prt_d, "ld (%s%%+d),de", INSS_EZ80 }, |
| 655 | { 0x24, 0xF7, prt_r, "inc %s%%s", INSS_ALL }, |
| 656 | { 0x25, 0xF7, prt_r, "dec %s%%s", INSS_ALL }, |
| 657 | { 0x26, 0xF7, ld_r_n, "ld %s%%s,0x%%%%02x", INSS_ALL }, |
| 658 | { 0x27, 0xFF, prt_d, "ld hl,(%s%%+d)", INSS_EZ80 }, |
| 659 | { 0x2F, 0xFF, prt_d, "ld (%s%%+d),hl", INSS_EZ80 }, |
| 660 | { 0x21, 0xFF, prt_nn, "ld %s,0x%%04x", INSS_ALL }, |
| 661 | { 0x22, 0xFF, prt_nn, "ld (0x%%04x),%s", INSS_ALL }, |
| 662 | { 0x2A, 0xFF, prt_nn, "ld %s,(0x%%04x)", INSS_ALL }, |
| 663 | { 0x23, 0xFF, prt, "inc %s", INSS_ALL }, |
| 664 | { 0x2B, 0xFF, prt, "dec %s", INSS_ALL }, |
| 665 | { 0x29, 0xFF, addvv, "%s", INSS_ALL }, |
| 666 | { 0x31, 0xFF, ld_ii_ii, "ld %%s,(%s%%%%+d)", INSS_EZ80 }, |
| 667 | { 0x37, 0xFF, ld_ii_ii, "ld %%s,(%s%%%%+d)", INSS_EZ80 }, |
| 668 | { 0x3E, 0xFE, ld_ii_ii, "ld (%s%%%%+d),%%s", INSS_EZ80 }, |
| 669 | { 0x09, 0xCF, prt_rr, "add %s,", INSS_ALL }, |
| 670 | { 0x34, 0xFF, prt_d, "inc (%s%%+d)", INSS_ALL }, |
| 671 | { 0x35, 0xFF, prt_d, "dec (%s%%+d)", INSS_ALL }, |
| 672 | { 0x36, 0xFF, prt_d_n, "ld (%s%%+d),0x%%%%02x", INSS_ALL }, |
| 673 | |
| 674 | { 0x76, 0xFF, dump, "h", INSS_ALL }, |
| 675 | { 0x46, 0xC7, ld_r_d, "ld %%s,(%s%%%%+d)", INSS_ALL }, |
| 676 | { 0x70, 0xF8, ld_d_r, "ld (%s%%%%+d),%%s", INSS_ALL }, |
| 677 | { 0x64, 0xF6, ld_v_v, "%s", INSS_ALL }, |
| 678 | { 0x60, 0xF0, ld_r_r, "ld %s%%s,%%s", INSS_ALL }, |
| 679 | { 0x44, 0xC6, ld_r_r, "ld %%s,%s%%s", INSS_ALL }, |
| 680 | |
| 681 | { 0x86, 0xC7, arit_d, "%%s(%s%%%%+d)", INSS_ALL }, |
| 682 | { 0x84, 0xC6, arit_r, "%%s%s%%s", INSS_ALL }, |
| 683 | |
| 684 | { 0xE1, 0xFF, prt, "pop %s", INSS_ALL }, |
| 685 | { 0xE5, 0xFF, prt, "push %s", INSS_ALL }, |
| 686 | { 0xCB, 0xFF, pref_xd_cb, "%s", INSS_ALL }, |
| 687 | { 0xE3, 0xFF, prt, "ex (sp),%s", INSS_ALL }, |
| 688 | { 0xE9, 0xFF, prt, "jp (%s)", INSS_ALL }, |
| 689 | { 0xF9, 0xFF, prt, "ld sp,%s", INSS_ALL }, |
| 690 | { 0x00, 0x00, dump, "?", INSS_ALL }, |
| 691 | } ; |
| 692 | |
| 693 | static int |
| 694 | pref_ind (struct buffer *buf, disassemble_info *info, const char *txt) |
| 695 | { |
| 696 | if (fetch_data (buf, info, 1)) |
| 697 | { |
| 698 | char mytxt[TXTSIZ]; |
| 699 | struct tab_elt *p; |
| 700 | |
| 701 | for (p = opc_ind; p->val != (buf->data[1] & p->mask) || !mach_inst (buf, p); ++p) |
| 702 | ; |
| 703 | snprintf (mytxt, TXTSIZ, p->text, txt); |
| 704 | p->fp (buf, info, mytxt); |
| 705 | } |
| 706 | else |
| 707 | buf->n_used = -1; |
| 708 | |
| 709 | return buf->n_used; |
| 710 | } |
| 711 | |
| 712 | static int |
| 713 | print_insn_z80_buf (struct buffer *buf, disassemble_info *info); |
| 714 | |
| 715 | static int |
| 716 | suffix (struct buffer *buf, disassemble_info *info, const char *txt) |
| 717 | { |
| 718 | char mybuf[TXTSIZ*4]; |
| 719 | fprintf_ftype old_fprintf; |
| 720 | void *old_stream; |
| 721 | char *p; |
| 722 | |
| 723 | switch (txt[2]) |
| 724 | { |
| 725 | case 'l': /* SIL or LIL */ |
| 726 | buf->nn_len = 3; |
| 727 | break; |
| 728 | case 's': /* SIS or LIS */ |
| 729 | buf->nn_len = 2; |
| 730 | break; |
| 731 | default: |
| 732 | abort (); |
| 733 | } |
| 734 | if (!fetch_data (buf, info, 1) |
| 735 | || buf->data[1] == 0x40 |
| 736 | || buf->data[1] == 0x49 |
| 737 | || buf->data[1] == 0x52 |
| 738 | || buf->data[1] == 0x5b) |
| 739 | { |
| 740 | /* Double prefix, or end of data. */ |
| 741 | info->fprintf_func (info->stream, "nop ;%s", txt); |
| 742 | buf->n_used = 1; |
| 743 | return buf->n_used; |
| 744 | } |
| 745 | |
| 746 | old_fprintf = info->fprintf_func; |
| 747 | old_stream = info->stream; |
| 748 | info->fprintf_func = (fprintf_ftype) &sprintf; |
| 749 | info->stream = mybuf; |
| 750 | buf->base++; |
| 751 | if (print_insn_z80_buf (buf, info) >= 0) |
| 752 | buf->n_used++; |
| 753 | info->fprintf_func = old_fprintf; |
| 754 | info->stream = old_stream; |
| 755 | |
| 756 | for (p = mybuf; *p; ++p) |
| 757 | if (*p == ' ') |
| 758 | break; |
| 759 | if (*p) |
| 760 | { |
| 761 | *p++ = '\0'; |
| 762 | info->fprintf_func (info->stream, "%s.%s %s", mybuf, txt, p); |
| 763 | } |
| 764 | else |
| 765 | info->fprintf_func (info->stream, "%s.%s", mybuf, txt); |
| 766 | return buf->n_used; |
| 767 | } |
| 768 | |
| 769 | /* Table to disassemble machine codes without prefix. */ |
| 770 | static struct tab_elt opc_main[] = |
| 771 | { |
| 772 | { 0x00, 0xFF, prt, "nop", INSS_ALL }, |
| 773 | { 0x01, 0xCF, prt_rr_nn, "ld %s,0x%%04x", INSS_ALL }, |
| 774 | { 0x02, 0xFF, prt, "ld (bc),a", INSS_ALL }, |
| 775 | { 0x03, 0xCF, prt_rr, "inc ", INSS_ALL }, |
| 776 | { 0x04, 0xC7, prt_r, "inc %s", INSS_ALL }, |
| 777 | { 0x05, 0xC7, prt_r, "dec %s", INSS_ALL }, |
| 778 | { 0x06, 0xC7, ld_r_n, "ld %s,0x%%02x", INSS_ALL }, |
| 779 | { 0x07, 0xFF, prt, "rlca", INSS_ALL }, |
| 780 | { 0x08, 0xFF, prt, "ex af,af'", ~INSS_GBZ80 }, |
| 781 | { 0x09, 0xCF, prt_rr, "add hl,", INSS_ALL }, |
| 782 | { 0x0A, 0xFF, prt, "ld a,(bc)", INSS_ALL }, |
| 783 | { 0x0B, 0xCF, prt_rr, "dec ", INSS_ALL }, |
| 784 | { 0x0F, 0xFF, prt, "rrca", INSS_ALL }, |
| 785 | { 0x10, 0xFF, prt_e, "djnz ", ~INSS_GBZ80 }, |
| 786 | { 0x12, 0xFF, prt, "ld (de),a", INSS_ALL }, |
| 787 | { 0x17, 0xFF, prt, "rla", INSS_ALL }, |
| 788 | { 0x18, 0xFF, prt_e, "jr ", INSS_ALL }, |
| 789 | { 0x1A, 0xFF, prt, "ld a,(de)", INSS_ALL }, |
| 790 | { 0x1F, 0xFF, prt, "rra", INSS_ALL }, |
| 791 | { 0x20, 0xE7, jr_cc, "jr %s,", INSS_ALL }, |
| 792 | { 0x22, 0xFF, prt_nn, "ld (0x%04x),hl", ~INSS_GBZ80 }, |
| 793 | { 0x27, 0xFF, prt, "daa", INSS_ALL }, |
| 794 | { 0x2A, 0xFF, prt_nn, "ld hl,(0x%04x)", ~INSS_GBZ80 }, |
| 795 | { 0x2F, 0xFF, prt, "cpl", INSS_ALL }, |
| 796 | { 0x32, 0xFF, prt_nn, "ld (0x%04x),a", INSS_ALL }, |
| 797 | { 0x37, 0xFF, prt, "scf", INSS_ALL }, |
| 798 | { 0x3A, 0xFF, prt_nn, "ld a,(0x%04x)", INSS_ALL }, |
| 799 | { 0x3F, 0xFF, prt, "ccf", INSS_ALL }, |
| 800 | |
| 801 | { 0x76, 0xFF, prt, "halt", INSS_ALL }, |
| 802 | |
| 803 | { 0x40, 0xFF, suffix, "sis", INSS_EZ80 }, |
| 804 | { 0x49, 0xFF, suffix, "lis", INSS_EZ80 }, |
| 805 | { 0x52, 0xFF, suffix, "sil", INSS_EZ80 }, |
| 806 | { 0x5B, 0xFF, suffix, "lil", INSS_EZ80 }, |
| 807 | |
| 808 | { 0x40, 0xC0, ld_r_r, "ld %s,%s", INSS_ALL}, |
| 809 | |
| 810 | { 0x80, 0xC0, arit_r, "%s%s", INSS_ALL }, |
| 811 | |
| 812 | { 0xC0, 0xC7, prt_cc, "ret ", INSS_ALL }, |
| 813 | { 0xC1, 0xCF, pop_rr, "pop", INSS_ALL }, |
| 814 | { 0xC2, 0xC7, jp_cc_nn, "jp ", INSS_ALL }, |
| 815 | { 0xC3, 0xFF, prt_nn, "jp 0x%04x", INSS_ALL }, |
| 816 | { 0xC4, 0xC7, jp_cc_nn, "call ", INSS_ALL }, |
| 817 | { 0xC5, 0xCF, pop_rr, "push", INSS_ALL }, |
| 818 | { 0xC6, 0xC7, arit_n, "%s0x%%02x", INSS_ALL }, |
| 819 | { 0xC7, 0xC7, rst, "rst 0x%02x", INSS_ALL }, |
| 820 | { 0xC9, 0xFF, prt, "ret", INSS_ALL }, |
| 821 | { 0xCB, 0xFF, pref_cb, "", INSS_ALL }, |
| 822 | { 0xCD, 0xFF, prt_nn, "call 0x%04x", INSS_ALL }, |
| 823 | { 0xD3, 0xFF, prt_n, "out (0x%02x),a", ~INSS_GBZ80 }, |
| 824 | { 0xD9, 0xFF, prt, "exx", ~INSS_GBZ80 }, |
| 825 | { 0xDB, 0xFF, prt_n, "in a,(0x%02x)", ~INSS_GBZ80 }, |
| 826 | { 0xDD, 0xFF, pref_ind, "ix", ~INSS_GBZ80 }, |
| 827 | { 0xE3, 0xFF, prt, "ex (sp),hl", ~INSS_GBZ80 }, |
| 828 | { 0xE9, 0xFF, prt, "jp (hl)", INSS_ALL }, |
| 829 | { 0xEB, 0xFF, prt, "ex de,hl", ~INSS_GBZ80 }, |
| 830 | { 0xED, 0xFF, pref_ed, "", ~INSS_GBZ80 }, |
| 831 | { 0xF3, 0xFF, prt, "di", INSS_ALL }, |
| 832 | { 0xF9, 0xFF, prt, "ld sp,hl", ~INSS_GBZ80 }, |
| 833 | { 0xFB, 0xFF, prt, "ei", INSS_ALL }, |
| 834 | { 0xFD, 0xFF, pref_ind, "iy", ~INSS_GBZ80 }, |
| 835 | { 0x00, 0x00, prt, "????", INSS_ALL }, |
| 836 | } ; |
| 837 | |
| 838 | int |
| 839 | print_insn_z80 (bfd_vma addr, disassemble_info * info) |
| 840 | { |
| 841 | struct buffer buf; |
| 842 | |
| 843 | buf.base = addr; |
| 844 | buf.inss = 1 << info->mach; |
| 845 | buf.nn_len = info->mach == bfd_mach_ez80_adl ? 3 : 2; |
| 846 | info->bytes_per_line = (buf.inss & INSS_EZ80) ? 6 : 4; /* <ss pp oo nn mm MM> OR <pp oo nn mm> */ |
| 847 | |
| 848 | return print_insn_z80_buf (&buf, info); |
| 849 | } |
| 850 | |
| 851 | static int |
| 852 | print_insn_z80_buf (struct buffer *buf, disassemble_info *info) |
| 853 | { |
| 854 | struct tab_elt *p; |
| 855 | |
| 856 | buf->n_fetch = 0; |
| 857 | buf->n_used = 0; |
| 858 | if (! fetch_data (buf, info, 1)) |
| 859 | return -1; |
| 860 | |
| 861 | for (p = opc_main; p->val != (buf->data[0] & p->mask) || !mach_inst(buf, p); ++p) |
| 862 | ; |
| 863 | p->fp (buf, info, p->text); |
| 864 | |
| 865 | return buf->n_used; |
| 866 | } |