In order to make debugging easier, I thought it might to have a disassembler. It’s fairly table driven. It needs a lot of work still. I think it disassembles valid instructions, but doesn’t guarantee that invalid instructions are handled in any particular way. By valid, I mean the officially documented instructions. It is not optimized for speed. Rather, my goal was code size. Whether I met that or not is something I’m wondering. All the little special cases add code size.

Anyone needing a Z-80 disassembler is free to use what they want from this.

   1 // Associate shiftMask to a StartInfo
   2 struct ShiftMaskAndIndexStartInfo {
   3     // upper nibble is shift amount. Bottom nibble is mask
   4     uint8_t shiftMaskInfo;
   5 
   6     // A start index
   7     uint8_t indexStart;
   8 };
   9 
  10 // Information to create an instruction
  11 struct InstructionInfo
  12 {
  13     // 8 bit mask of relevant bits
  14     uint8_t mask;
  15 
  16     // expected value after anding with mask
  17     uint8_t value;
  18 
  19     // 7-6: type  5-0: index
  20     uint8_t opcodeInfo;
  21 
  22     // arg0 info
  23     uint8_t argInfoIndex0;
  24 
  25     // arg1 info
  26     uint8_t argInfoIndex1;
  27 };
  28 
  29 // Opcode builder
  30 struct OpcodeBuildInfo {
  31     // 8 bit mask of relevant bits
  32     uint8_t mask;
  33 
  34     // Expected value after anding with mask
  35     uint8_t value;
  36 
  37     // Letter to append if (b & mask) == value
  38     char letter;
  39 };
  40 
  41 
  42 // Keep synched with InstructionTableArray
  43 enum InstructionTableIndices
  44 {
  45     TABLE_MAIN_00,
  46     TABLE_MAIN_40,
  47     TABLE_MAIN_80,
  48     TABLE_MAIN_C0,
  49     TABLE_CB,
  50     TABLE_ED,
  51     TABLE_COUNT
  52 };
  53 
  54 // keep synched with LiteralStringArray
  55 enum LiteralStringIndices {
  56     DA_LIT_Q,
  57     DA_LIT_nop,
  58     DA_LIT_djnz,
  59     DA_LIT_jr,
  60     DA_LIT_ld,
  61     DA_LIT_add,
  62     DA_LIT_ex,
  63     DA_LIT_halt,
  64     DA_LIT_ret,
  65     DA_LIT_call,
  66     DA_LIT_jp,
  67     DA_LIT_exx,
  68     DA_LIT_rst,
  69     DA_LIT_neg,
  70     DA_LIT_reti,
  71     DA_LIT_retn,
  72     DA_LIT_im,
  73     DA_LIT_inc,
  74     DA_LIT_dec,
  75     DA_LIT_adc,
  76     DA_LIT_sub,
  77     DA_LIT_sbc,
  78     DA_LIT_and,
  79     DA_LIT_xor,
  80     DA_LIT_or,
  81     DA_LIT_cp,
  82     DA_LIT_daa,
  83     DA_LIT_cpl,
  84     DA_LIT_scf,
  85     DA_LIT_ccf,
  86     DA_LIT_pop,
  87     DA_LIT_push,
  88     DA_LIT_out,
  89     DA_LIT_in,
  90     DA_LIT_di,
  91     DA_LIT_ei,
  92     DA_LIT_bit,
  93     DA_LIT_set,
  94     DA_LIT_res,
  95     DA_LIT_nz,
  96     DA_LIT_z,
  97     DA_LIT_nc,
  98     DA_LIT_c,
  99     DA_LIT_po,
 100     DA_LIT_pe,
 101     DA_LIT_p,
 102     DA_LIT_m,
 103     DA_LIT_bc,
 104     DA_LIT_de,
 105     DA_LIT_hl,
 106     DA_LIT_ix,
 107     DA_LIT_iy,
 108     DA_LIT_sp,
 109     DA_LIT_af,
 110     DA_LIT_a,
 111     DA_LIT_b,
 112     DA_LIT_d,
 113     DA_LIT_e,
 114     DA_LIT_h,
 115     DA_LIT_l,
 116     DA_LIT_i,
 117     DA_LIT_r,
 118     DA_LIT_af_alt,
 119     DA_LIT_COUNT
 120 };
 121 
 122 // keep synced with enum DisassembleLiteralIndices
 123 static const char* const LiteralStringArray[DA_LIT_COUNT] =
 124 {
 125     "?",
 126     "nop",
 127     "djnz",
 128     "jr",
 129     "ld",
 130     "add",
 131     "ex",
 132     "halt",
 133     "ret",
 134     "call",
 135     "jp",
 136     "exx",
 137     "rst",
 138     "neg",
 139     "reti",
 140     "retn",
 141     "im",
 142     "inc",
 143     "dec",
 144     "adc",
 145     "sub",
 146     "sbc",
 147     "and",
 148     "xor",
 149     "or",
 150     "cp",
 151     "daa",
 152     "cpl",
 153     "scf",
 154     "ccf",
 155     "pop",
 156     "push",
 157     "out",
 158     "in",
 159     "di",
 160     "ei",
 161     "bit",
 162     "set",
 163     "res",
 164     "nz",
 165     "z",
 166     "nc",
 167     "c",
 168     "po",
 169     "pe",
 170     "p",
 171     "m",
 172     "bc",
 173     "de",
 174     "hl",
 175     "ix",
 176     "iy",
 177     "sp",
 178     "af",
 179     "a",
 180     "b",
 181     "d",
 182     "e",
 183     "h",
 184     "l",
 185     "i",
 186     "r",
 187     "af'"
 188 };
 189 
 190 /*
 191 0: @alu : add, adc, sub, sbc, and, xor, or, cp
 192 1 : @bitsetres : -, bit, set, res
 193 2 : @daacplscfccf : daa, cpl, scf, ccf
 194 3 : @incdec : inc, dec
 195 4 : @poppush : pop, push
 196 5 : @outin : out, in
 197 6 : @inout : in, out
 198 7 : @diei : di, ei
 199 8 : @sbcadc : sbc, adc
 200 */
 201 
 202 enum OpcodeByMaskInfoIndices
 203 {
 204     OPCODE_MASKABLE_ALU, // 10@@ @rrr -- 11@@ @110
 205     OPCODE_MASKABLE_BIT, // @@## #rrr
 206     OPCODE_MASKABLE_DAA, // 001@ @111
 207     OPCODE_MASKABLE_INC_RRS, // 00rr @011
 208     OPCODE_MASKABLE_INC_R, // 00rr r10@
 209     OPCODE_MASKABLE_POP, // 11rr 0@01
 210     OPCODE_MASKABLE_INOUT, // 01rr r00@
 211     OPCODE_MASKABLE_OUTIN, // 1110 @011
 212     OPCODE_MASKABLE_DIEI, // 1111 @011
 213     OPCODE_MASKABLE_SBC, // 01rr @010
 214     OPCODE_MASKABLE_COUNT,
 215 };
 216 
 217 enum OpcodeByMaskStartIndices
 218 {
 219     MASKABLE_ALU = 0,
 220     MASKABLE_BIT = MASKABLE_ALU + 8-1, // first element of bit can't exist, so collapse
 221     MASKABLE_DAA = MASKABLE_BIT + 4,
 222     MASKABLE_INC = MASKABLE_DAA + 4,
 223     MASKABLE_POP = MASKABLE_INC + 2,
 224     MASKABLE_INOUT = MASKABLE_POP + 2,
 225     MASKABLE_OUTIN = MASKABLE_INOUT + 2 - 1,
 226     MASKABLE_DIEI = MASKABLE_OUTIN + 2,
 227     MASKABLE_SBC = MASKABLE_DIEI + 2,
 228     MASKABLE_ARRAY_COUNT = MASKABLE_SBC + 2
 229 };
 230 
 231 static const struct ShiftMaskAndIndexStartInfo OpcodeByMaskShiftMaskArray[OPCODE_MASKABLE_COUNT] =
 232 {
 233     {
 234         0x37, MASKABLE_ALU
 235     },
 236     {
 237         0x63, MASKABLE_BIT
 238     },
 239     {
 240         0x33, MASKABLE_DAA
 241     },
 242     { // r
 243         0x31, MASKABLE_INC
 244     },
 245     { // rrs
 246         0x01, MASKABLE_INC
 247     },
 248     {
 249         0x21, MASKABLE_POP
 250     },
 251     {
 252         0x01, MASKABLE_INOUT
 253     },
 254     {
 255         0x31, MASKABLE_OUTIN
 256     },
 257     {
 258         0x31, MASKABLE_DIEI
 259     },
 260     {
 261         0x31, MASKABLE_SBC
 262     },
 263 };
 264 
 265 static const uint8_t OpcodeByMaskArray[MASKABLE_ARRAY_COUNT] =
 266 {
 267     // MASKABLE_ALU = 0
 268     DA_LIT_add, // 0
 269     DA_LIT_adc,
 270     DA_LIT_sub,
 271     DA_LIT_sbc,
 272     DA_LIT_and,
 273     DA_LIT_xor,
 274     DA_LIT_or,
 275     // MASKABLE_BIT = 7
 276     DA_LIT_cp,
 277 
 278     DA_LIT_bit, // 8
 279     DA_LIT_res,
 280     DA_LIT_set,
 281 
 282     // MASKABLE_DAA
 283     DA_LIT_daa, // 11
 284     DA_LIT_cpl,
 285     DA_LIT_scf,
 286     DA_LIT_ccf,
 287 
 288     // MASKABLE_INC
 289     DA_LIT_inc, // 15
 290     DA_LIT_dec,
 291 
 292     // MASKABLE_POP
 293     DA_LIT_pop, // 17
 294     DA_LIT_push,
 295 
 296     // MASKABLE_INOUT
 297     DA_LIT_in, // 19
 298     // MASKABLE_OUTIN
 299     DA_LIT_out, // 20
 300     DA_LIT_in,
 301 
 302     // MASKABLE_EIDI
 303     DA_LIT_di, // 22
 304     DA_LIT_ei,
 305 
 306     // MASKABLE_SBC
 307     DA_LIT_sbc, // 24
 308     DA_LIT_adc
 309 };
 310 
 311 // ROTATE Main-00-00; ED-80-00; CB-02-00 "r"
 312 // SHIFT CB-02-02 "s"
 313 // LD ED-07-00 "ld"
 314 // CP ED-07-01 "cp"
 315 // IN ED-07-02 "in"
 316 // OT ED-07-03 "ot
 317 // CARRY MAIN/CB-01-01 "c"
 318 // LOGIGAL CB-03-03 "l"
 319 // ARITH/Acc CB-03-02 "a"
 320 // LEFT Main-08-00; "l"
 321 // RIGHT Main-08-08; "r"
 322 // INC ED-88-80 "i"
 323 // DEC ED-88-88 "d"
 324 // REPEAT ED-f0-b0 "r"
 325 
 326 enum OpcodeByBuilderIndices
 327 {
 328     OPCODE_BUILD_RLCA,
 329     OPCODE_BUILD_RRD,
 330     OPCODE_BUILD_LDI,
 331     OPCODE_BUILD_RLC
 332 };
 333 
 334 // sync with OpcodeBuilderArray and OpcodeByBuilderIndices
 335 static const uint8_t OpcodeBuilderIndexToOpcodeBuilderArrayStartIndex[] = {
 336     0,
 337     6,
 338     11,
 339     24,
 340     33
 341 };
 342 
 343 // synch with OpcodeBuilderIndexToOpcodeBuilderArrayStartIndex
 344 static const struct OpcodeBuildInfo OpcodeBuilderArray[32] = {
 345     //MAIN table
 346     //{
 347     // main = 0 (5)
 348     { 0x00, 0x00, 'r' },
 349     { 0x08, 0x00, 'l' },
 350     { 0x08, 0x08, 'r' },
 351     { 0x10, 0x00, 'c' },
 352     { 0x00, 0x00, 'a' },
 353     { 0x00, 0x00, 0 },
 354     //},
 355     // ED table
 356     //{
 357     { 0x00, 0x00, 'r' },
 358     { 0xc8, 0x40, 'r' },
 359     { 0xc8, 0x48, 'l' },
 360     { 0x00, 0x00, 'd' },
 361     { 0x00, 0x00, 0 },
 362     //},
 363     //{
 364     { 0xc7, 0x80, 'l' },
 365     { 0xc7, 0x80, 'd' },
 366     { 0xc7, 0x81, 'c' },
 367     { 0xc7, 0x81, 'p' },
 368     { 0xc7, 0x82, 'i' },
 369     { 0xc7, 0x82, 'n' },
 370     { 0xc7, 0x83, 'o' },
 371     { 0xf7, 0xa3, 'u' },
 372     { 0xc7, 0x83, 't' },
 373     { 0x88, 0x80, 'i' },
 374     { 0x88, 0x88, 'd' },
 375     { 0xf0, 0xb0, 'r' },
 376     { 0x00, 0x00, 0 },
 377     //},
 378     // CB table
 379     //{
 380     { 0x20, 0x00, 'r' },
 381     { 0x20, 0x20, 's' },
 382     { 0x08, 0x00, 'l' },
 383     { 0x08, 0x08, 'r' },
 384     { 0x30, 0x00, 'c' },
 385     { 0x30, 0x20, 'a' },
 386     { 0x30, 0x30, 'l' },
 387     { 0x00, 0x00, 0 },
 388     //}
 389 };
 390 
 391 
 392 #define ARG_TYPE_SPECIAL    (0x00)
 393 #define ARG_TYPE_LIT        (0x10)
 394 #define ARG_TYPE_MASK       (0x20)
 395 #define ARG_TYPE_SWAPPABLE  (0x40)
 396 #define ARG_TYPE_CONTENTS   (0x80)
 397 
 398 enum ArgByMaskStartIndices
 399 {
 400     ARG_ARR_FL = 0,
 401     ARG_ARR_RRS = ARG_ARR_FL + 8,
 402     ARG_ARR_RRA = ARG_ARR_RRS + 4,
 403     ARG_ARR_HLA = ARG_ARR_RRA + 4,
 404     ARG_ARR_R = ARG_ARR_HLA + 2,
 405     ARG_ARR_IR = ARG_ARR_R + 8,
 406     ARG_ARR_IM = ARG_ARR_IR + 2,
 407     ARG_ARR_COUNT = ARG_ARR_IM + 4
 408 };
 409 
 410 static const uint8_t ArgByMaskArray[ARG_ARR_COUNT] =
 411 {
 412     DA_LIT_nz, // 0
 413     DA_LIT_z,
 414     DA_LIT_nc,
 415     DA_LIT_c,
 416     DA_LIT_po,
 417     DA_LIT_pe,
 418     DA_LIT_p,
 419     DA_LIT_m,
 420 
 421     DA_LIT_bc, // 8
 422     DA_LIT_de,
 423     DA_LIT_hl,
 424     DA_LIT_sp,
 425 
 426     DA_LIT_bc, // 12
 427     DA_LIT_de,
 428     DA_LIT_hl,
 429     DA_LIT_af,
 430 
 431     DA_LIT_hl, // 16
 432     DA_LIT_a,
 433 
 434     DA_LIT_b, // 18
 435     DA_LIT_c,
 436     DA_LIT_d,
 437     DA_LIT_e,
 438     DA_LIT_h,
 439     DA_LIT_l,
 440     DA_LIT_hl | ARG_TYPE_CONTENTS,
 441     DA_LIT_a,
 442 
 443     DA_LIT_i, // 42
 444     DA_LIT_r,
 445 };
 446 
 447 /*
 448 // 
 449 #define ARG_TYPE_MASK (0x01)
 450 // mask, index
 451 #define ARG_TYPE_LIT (0x02)
 452 // (int8_t)(*(pc)) + pc; pc++;
 453 #define ARG_TYPE_REL (0x03)
 454 #define ARG_TYPE_IMM8 (0x04)
 455 #define ARG_TYPE_IMM16 (0x05)
 456 #define ARG_TYPE_ASCII (0x06)
 457 #define ARG_TYPE_RST (0x07)
 458 */
 459 
 460 // index to next table ED/CB/DDFD
 461 #define OPCODE_TYPE_LIT (0x00)
 462 // index
 463 #define OPCODE_TYPE_ARR (0x40)
 464 // mask, index
 465 #define OPCODE_TYPE_BUILD (0x80)
 466 // index
 467 #define OPCODE_TYPE_SUB (0xc0)
 468 
 469 
 470 // [8:mask],[8:val],[2:opcode, 6:index0] [1: swappable 3: arg0 3: arg1]
 471 // index 0 is literal index for literal, or maskables index for maskable. For build, the build index
 472 // swapbit is there only if swappable. Swap bit is 3 for main, 4 for ED
 473 // arginfo is:
 474 //  if 00 no arg
 475 //  if 01 mask [8: mask(-sss-mmm)] [8: arg_arr index] or 3bit is mask index, 5 bits is arg_arr index
 476 //  if 02 lit [8: lit index]
 477 //  if 03 uint8 get next byte
 478 //  if 04 RelAddr get next byte, add to location
 479 //  if 05 uint16 get next word
 480 //  bit 8 swappable
 481 enum ArgInfo {
 482     ARG_INFO_NONE,
 483     ARG_INFO_rel, // 1
 484     ARG_INFO_imm8,
 485     ARG_INFO_imm16,
 486     ARG_INFO_bit,
 487     ARG_INFO_rst,
 488     ARG_INFO_im,
 489 
 490     ARG_INFO_LIT_a = ARG_TYPE_LIT, // 4
 491     ARG_INFO_LIT_af,
 492     ARG_INFO_LIT_af_alt,
 493     ARG_INFO_LIT_c,
 494     ARG_INFO_LIT_de,
 495 
 496     ARG_INFO_LIT_hl, // 8
 497     ARG_INFO_LIT_sp,
 498     //ARG_INFO_LIT_q,
 499 
 500     ARG_INFO_MASK_30_RRS = ARG_TYPE_MASK,
 501     ARG_INFO_MASK_30_RRA,
 502     ARG_INFO_MASK_07_R,
 503 
 504     ARG_INFO_MASK_38_R,
 505     ARG_INFO_MASK_18_FL,
 506     ARG_INFO_MASK_38_FL,
 507     ARG_INFO_MASK_08_IR,
 508 };
 509 
 510 /*
 511 enum ArgMaskIndex {
 512     ARG_MASK_30_RRS,
 513     ARG_MASK_30_RRA,
 514     ARG_MASK_07_R,
 515     ARG_MASK_38_R,
 516 
 517     ARG_MASK_30_FL,
 518     ARG_MASK_38_FL,
 519     ARG_MASK_38_RST,
 520     ARG_MASK_80_IR,
 521 
 522     ARG_MASK_COUNT,
 523 };
 524 */
 525 
 526 // sync with ArgInfo from ARG_TYPE_LIT
 527 static const uint8_t ArgByLitIndexToStringLiteralIndex[] = {
 528     DA_LIT_a,
 529     DA_LIT_af,
 530     DA_LIT_af_alt,
 531     DA_LIT_c,
 532 
 533     DA_LIT_de,
 534     DA_LIT_hl,
 535     DA_LIT_sp
 536 };
 537 
 538 // sync with ArgInfo from ARG_TYPE_MASK
 539 static const struct ShiftMaskAndIndexStartInfo ArgShiftMaskStartIndex[] =
 540 {
 541     { /*0x30*/ 0x43, ARG_ARR_RRS },
 542     { /*0x30*/ 0x43, ARG_ARR_RRA },
 543     { /*0x07*/ 0x07, ARG_ARR_R },
 544     { /*0x38*/ 0x37, ARG_ARR_R },
 545 
 546     { /*0x18*/ 0x33, ARG_ARR_FL },
 547     { /*0x38*/ 0x37, ARG_ARR_FL },
 548     { /*0x08*/ 0x31, ARG_ARR_IR },
 549 };
 550 
 551 char* buildOpcode(uint8_t opcode, uint8_t table, char* line)
 552 {
 553     const struct OpcodeBuildInfo* cand = &OpcodeBuilderArray[OpcodeBuilderIndexToOpcodeBuilderArrayStartIndex[table]];
 554     while (cand->letter != 0)
 555     {
 556         if ((opcode & cand->mask) == cand->value)
 557         {
 558             *line++ = cand->letter;
 559         }
 560         cand++;
 561     }
 562     *line++ = '\0';
 563     return line;
 564 }
 565 
 566 static const struct InstructionInfo InstructionInfoTableMain_00[] =
 567 {
 568     {
 569         0xff,
 570         0x00,
 571         DA_LIT_nop | OPCODE_TYPE_LIT,
 572         ARG_INFO_NONE,
 573         ARG_INFO_NONE,
 574     },
 575     {
 576         0xff,
 577         0x10,
 578         DA_LIT_djnz | OPCODE_TYPE_LIT,
 579         ARG_INFO_rel,
 580         ARG_INFO_NONE
 581     },
 582     {
 583         0xe7,
 584         0x20,
 585         DA_LIT_jr | OPCODE_TYPE_LIT,
 586         ARG_INFO_MASK_18_FL,
 587         ARG_INFO_rel,
 588     },
 589     {
 590         0xcf,
 591         0x01,
 592         DA_LIT_ld | OPCODE_TYPE_LIT,
 593         ARG_INFO_MASK_30_RRS,
 594         ARG_INFO_imm16
 595     },
 596     {
 597         0xe7,
 598         0x02,
 599         DA_LIT_ld | OPCODE_TYPE_LIT,
 600         ARG_INFO_MASK_30_RRS | ARG_TYPE_SWAPPABLE | ARG_TYPE_CONTENTS, // swap on 0x08
 601         ARG_INFO_LIT_a
 602     },
 603     {
 604         0xcf,
 605         0x09,
 606         DA_LIT_add | OPCODE_TYPE_LIT,
 607         ARG_INFO_LIT_hl,
 608         ARG_INFO_MASK_30_RRS
 609     },
 610     {
 611         0xf7,
 612         0x32,
 613         DA_LIT_ld | OPCODE_TYPE_LIT,
 614         ARG_INFO_imm16 | ARG_TYPE_SWAPPABLE | ARG_TYPE_CONTENTS, // swap on 0x08
 615         ARG_INFO_LIT_a
 616     },
 617     {
 618         0xf7,
 619         0x22,
 620         DA_LIT_ld | OPCODE_TYPE_LIT,
 621         ARG_INFO_imm16 | ARG_TYPE_SWAPPABLE | ARG_TYPE_CONTENTS, // swap on 0x08
 622         ARG_INFO_LIT_hl
 623     },
 624     {
 625         0xc7,
 626         0x03,
 627         OPCODE_MASKABLE_INC_RRS | OPCODE_TYPE_ARR,
 628         ARG_INFO_MASK_30_RRS,
 629         ARG_INFO_NONE
 630     },
 631     {
 632         0xc6,
 633         0x04,
 634         OPCODE_MASKABLE_INC_R | OPCODE_TYPE_ARR,
 635         ARG_INFO_MASK_38_R,
 636         ARG_INFO_NONE
 637     },
 638     {
 639         0xc7,
 640         0x06,
 641         DA_LIT_ld | OPCODE_TYPE_LIT,
 642         ARG_INFO_MASK_38_R,
 643         ARG_INFO_imm8
 644     },
 645     {
 646         0xff,
 647         0x08,
 648         DA_LIT_ex | OPCODE_TYPE_LIT,
 649         ARG_INFO_LIT_af,
 650         ARG_INFO_LIT_af_alt
 651     },
 652     {
 653         0xff,
 654         0x18,
 655         DA_LIT_jr | OPCODE_TYPE_LIT,
 656         ARG_INFO_rel,
 657         ARG_INFO_NONE
 658     },
 659     {
 660         0xe7,
 661         0x27,
 662         OPCODE_MASKABLE_DAA | OPCODE_TYPE_ARR,
 663         ARG_INFO_NONE,
 664         ARG_INFO_NONE
 665     },
 666     {
 667         0xe7,
 668         0x07,
 669         OPCODE_BUILD_RLCA | OPCODE_TYPE_BUILD,
 670         ARG_INFO_NONE,
 671         ARG_INFO_NONE
 672     },
 673     {
 674         0x00,
 675         0x00,
 676         DA_LIT_Q | OPCODE_TYPE_LIT,
 677         ARG_INFO_NONE,
 678         ARG_INFO_NONE
 679     }
 680 };
 681 static const struct InstructionInfo InstructionInfoTableMain_40[] =
 682 {
 683     { // Must be before ld r,r
 684         0xff,
 685         0x76,
 686         DA_LIT_halt | OPCODE_TYPE_LIT,
 687         ARG_INFO_NONE,
 688         ARG_INFO_NONE
 689     },
 690 
 691     { //  Must be after halt
 692         0xc0,
 693         0x40,
 694         DA_LIT_ld | OPCODE_TYPE_LIT,
 695         ARG_INFO_MASK_38_R,
 696         ARG_INFO_MASK_07_R
 697     },
 698     {
 699         0x00,
 700         0x00,
 701         DA_LIT_Q | OPCODE_TYPE_LIT,
 702         ARG_INFO_NONE,
 703         ARG_INFO_NONE
 704     }
 705 };
 706 static const struct InstructionInfo InstructionInfoTableMain_80[] =
 707 {
 708     {
 709         0xc0,
 710         0x80,
 711         OPCODE_MASKABLE_ALU | OPCODE_TYPE_ARR,
 712         ARG_INFO_LIT_a,
 713         ARG_INFO_MASK_07_R
 714     },
 715     {
 716         0x00,
 717         0x00,
 718         DA_LIT_Q | OPCODE_TYPE_LIT,
 719         ARG_INFO_NONE,
 720         ARG_INFO_NONE
 721     }
 722 };
 723 static const struct InstructionInfo InstructionInfoTableMain_c0[] =
 724 {
 725     {
 726         0xc7,
 727         0xc0,
 728         DA_LIT_ret | OPCODE_TYPE_LIT,
 729         ARG_INFO_MASK_38_FL,
 730         ARG_INFO_NONE
 731     },
 732     {
 733         0xff,
 734         0xcd,
 735         DA_LIT_call | OPCODE_TYPE_LIT,
 736         ARG_INFO_imm16,
 737         ARG_INFO_NONE
 738     },
 739     {
 740         0xff,
 741         0xc3,
 742         DA_LIT_jp | OPCODE_TYPE_LIT,
 743         ARG_INFO_imm16,
 744         ARG_INFO_NONE
 745     },
 746     {
 747         0xff,
 748         0xe9,
 749         DA_LIT_jp | OPCODE_TYPE_LIT,
 750         ARG_INFO_LIT_hl | ARG_TYPE_CONTENTS,
 751         ARG_INFO_NONE
 752     },
 753     {
 754         0xff,
 755         0xf9,
 756         DA_LIT_ld | OPCODE_TYPE_LIT,
 757         ARG_INFO_LIT_sp,
 758         ARG_INFO_LIT_hl
 759     },
 760     {
 761         0xcb,
 762         0xc1,
 763         OPCODE_MASKABLE_POP | OPCODE_TYPE_ARR,
 764         ARG_INFO_MASK_30_RRA,
 765         ARG_INFO_NONE
 766     },
 767     {
 768         0xff,
 769         0xc9,
 770         DA_LIT_ret | OPCODE_TYPE_LIT,
 771         ARG_INFO_NONE,
 772         ARG_INFO_NONE
 773     },
 774     {
 775         0xff,
 776         0xd9,
 777         DA_LIT_exx | OPCODE_TYPE_LIT,
 778         ARG_INFO_NONE,
 779         ARG_INFO_NONE
 780     },
 781     {
 782         0xc7,
 783         0xc2,
 784         DA_LIT_jp | OPCODE_TYPE_LIT,
 785         ARG_INFO_MASK_38_FL,
 786         ARG_INFO_imm16
 787     },
 788     {
 789         0xc7,
 790         0xc4,
 791         DA_LIT_call | OPCODE_TYPE_LIT,
 792         ARG_INFO_MASK_38_FL,
 793         ARG_INFO_imm16
 794     },
 795     {
 796         0xc7,
 797         0xc7,
 798         DA_LIT_rst | OPCODE_TYPE_LIT,
 799         ARG_INFO_rst,
 800         ARG_INFO_NONE
 801     },
 802     {
 803         0xc7,
 804         0xc6,
 805         OPCODE_MASKABLE_ALU | OPCODE_TYPE_ARR,
 806         ARG_INFO_LIT_a,
 807         ARG_INFO_imm8
 808     },
 809     {
 810         0xf7,
 811         0xd3,
 812         OPCODE_MASKABLE_OUTIN | OPCODE_TYPE_ARR,
 813         ARG_INFO_imm8 | ARG_TYPE_CONTENTS | ARG_TYPE_SWAPPABLE, // swap on 0x08
 814         ARG_INFO_LIT_a
 815     },
 816     {
 817         0xf7,
 818         0xf3,
 819         OPCODE_MASKABLE_DIEI | OPCODE_TYPE_ARR,
 820         ARG_INFO_NONE,
 821         ARG_INFO_NONE
 822     },
 823     {
 824         0xff,
 825         0xe3,
 826         DA_LIT_ex | OPCODE_TYPE_LIT,
 827         ARG_INFO_LIT_sp | ARG_TYPE_CONTENTS,
 828         ARG_INFO_LIT_hl
 829     },
 830     {
 831         0xff,
 832         0xeb,
 833         DA_LIT_ex | OPCODE_TYPE_LIT,
 834         ARG_INFO_LIT_de,
 835         ARG_INFO_LIT_hl
 836     },
 837     {
 838         0xff,
 839         0xcb,
 840         TABLE_CB | OPCODE_TYPE_SUB,
 841         ARG_INFO_NONE,
 842         ARG_INFO_NONE
 843     },
 844     {
 845         0xff,
 846         0xed,
 847         TABLE_ED | OPCODE_TYPE_SUB,
 848         ARG_INFO_NONE,
 849         ARG_INFO_NONE
 850     },
 851     {
 852         0xfd,
 853         0xfd,
 854         0 | OPCODE_TYPE_SUB,
 855         ARG_INFO_NONE,
 856         ARG_INFO_NONE
 857     },
 858     {
 859         0x00,
 860         0x00,
 861         DA_LIT_Q | OPCODE_TYPE_LIT,
 862         ARG_INFO_NONE,
 863         ARG_INFO_NONE
 864     }
 865 };
 866 
 867 static const struct InstructionInfo InstructionInfoTableCB[] =
 868 {
 869     // CB
 870     {
 871         0xc0,
 872         0x00,
 873         OPCODE_BUILD_RLC | OPCODE_TYPE_BUILD,
 874         ARG_INFO_MASK_07_R,
 875         ARG_INFO_NONE
 876     },
 877     {
 878         0x00,
 879         0x00,
 880         OPCODE_MASKABLE_BIT | OPCODE_TYPE_ARR,
 881         ARG_INFO_bit,
 882         ARG_INFO_MASK_07_R
 883     },
 884     {
 885         0x00,
 886         0x00,
 887         DA_LIT_Q | OPCODE_TYPE_LIT,
 888         ARG_INFO_NONE,
 889         ARG_INFO_NONE
 890     }
 891 };
 892 
 893 static const struct InstructionInfo InstructionInfoTableED[] =
 894 {
 895     // ED
 896     {
 897         0xc6,
 898         0x40,
 899         OPCODE_MASKABLE_INOUT | OPCODE_TYPE_ARR,
 900         ARG_INFO_MASK_38_R | ARG_TYPE_SWAPPABLE, // TODO swap on 0x01
 901         ARG_INFO_LIT_c | ARG_TYPE_CONTENTS
 902     },
 903     {
 904         0xc7,
 905         0x42,
 906         OPCODE_MASKABLE_SBC | OPCODE_TYPE_ARR,
 907         ARG_INFO_LIT_hl,
 908         ARG_INFO_MASK_30_RRS
 909     },
 910     {
 911         0xc7,
 912         0x43,
 913         DA_LIT_ld | OPCODE_TYPE_LIT,
 914         ARG_INFO_imm16 | ARG_TYPE_CONTENTS | ARG_TYPE_SWAPPABLE, // TODO swap in 0x08
 915         ARG_INFO_MASK_30_RRS
 916     },
 917     {
 918         0xc7,
 919         0x44,
 920         DA_LIT_neg | OPCODE_TYPE_LIT,
 921         ARG_INFO_NONE,
 922         ARG_INFO_NONE
 923     },
 924     {
 925         0xff,
 926         0x4d,
 927         DA_LIT_reti | OPCODE_TYPE_LIT,
 928         ARG_INFO_NONE,
 929         ARG_INFO_NONE
 930     },
 931     {
 932         0xc7,
 933         0x45,
 934         DA_LIT_retn | OPCODE_TYPE_LIT,
 935         ARG_INFO_NONE,
 936         ARG_INFO_NONE
 937     },
 938     {
 939         0xe7,
 940         0x47,
 941         DA_LIT_ld | OPCODE_TYPE_LIT,
 942         ARG_INFO_MASK_08_IR | ARG_TYPE_SWAPPABLE, // TODO swap not working -- on bit 4 0x10
 943         ARG_INFO_LIT_a
 944     },
 945     {
 946         0xf7,
 947         0x67,
 948         OPCODE_BUILD_RRD | OPCODE_TYPE_BUILD,
 949         ARG_INFO_NONE,
 950         ARG_INFO_NONE
 951     },
 952     {
 953         0xc7,
 954         0x46,
 955         DA_LIT_im | OPCODE_TYPE_LIT,
 956         ARG_INFO_im,
 957         ARG_INFO_NONE
 958     },
 959     {
 960         0xe4,
 961         0xa0,
 962         OPCODE_BUILD_LDI | OPCODE_TYPE_BUILD,
 963         ARG_INFO_NONE,
 964         ARG_INFO_NONE
 965     },
 966     {
 967         0x00,
 968         0x00,
 969         DA_LIT_Q | OPCODE_TYPE_LIT,
 970         ARG_INFO_NONE,
 971         ARG_INFO_NONE
 972     }
 973 };
 974 
 975 // sync with InstructionTableIndices
 976 static const struct InstructionInfo* InstructionInfoTableArray[TABLE_COUNT] =
 977 {
 978     InstructionInfoTableMain_00,
 979     InstructionInfoTableMain_40,
 980     InstructionInfoTableMain_80,
 981     InstructionInfoTableMain_c0,
 982     InstructionInfoTableCB,
 983     InstructionInfoTableED
 984 };
 985 
 986 uint8_t shiftMaskToByte(uint8_t b, uint8_t shiftMaskByte)
 987 {
 988     return (b >> (shiftMaskByte >> 4)) & shiftMaskByte & 0x0f;
 989 }
 990 
 991 static const char IM_0Q12[4] = { '0', '?', '1', '2' };
 992 static const char* const FORMAT1C = "%c";
 993 static const char* const FORMAT1 = "%01xh";
 994 static const char* const FORMAT2 = "%02xh";
 995 static const char* const FORMAT4 = "%04xh";
 996 static const char* const FORMAT_CONTENTS = "(%s)";
 997 
 998 uint8_t* disassemble(uint8_t* loc, char* line)
 999 {
1000     uint8_t b; // instruction byte being disassembled
1001     uint8_t table = 0;
1002     const struct InstructionInfo* entry;
1003     bool swap = false;
1004     uint8_t argCount = 0;
1005     char arg0[8];
1006     char arg1[8];
1007     char* args[2] = { arg0, arg1 };
1008     uint8_t opcodeInfo;
1009     uint8_t opcodeIndex;
1010     uint16_t offset;
1011     uint8_t hl_ix_iy = 0;
1012     char offsetStr[6] = { 0 };
1013 
1014     *line = 0;
1015     b = *loc++;
1016     table = b >> 6;
1017 
1018     // See if we need a special table
1019     // 1100 1011 cb
1020     // 1110 1101 ed
1021     // 11-0 1--1
1022     // 1101 1101
1023     // 1111 1101
1024     // 11-1 1101
1025     if ((b & 0xdf) == 0xdd)
1026     {
1027         if (b == 0xdd)
1028         {
1029             b = *loc++;
1030             table = b >> 6;
1031             hl_ix_iy = 1;
1032         }
1033         else if (b == 0xfd)
1034         {
1035             b = *loc++;
1036             table = b >> 6;
1037             hl_ix_iy = 2;
1038         }
1039     }
1040     if ((b & 0xd9) == 0xc9)
1041     {
1042         if (b == 0xcb)
1043         {
1044             b = *loc++;
1045             table = TABLE_CB;
1046         }
1047         else if (b == 0xed)
1048         {
1049             b = *loc++;
1050             table = TABLE_ED;
1051         }
1052     }
1053 
1054     entry = InstructionInfoTableArray[table];
1055     while (entry->mask != 0)
1056     {
1057         uint8_t opcodeType;
1058         if ((b & entry->mask) == entry->value)
1059         {
1060             break;
1061         }
1062         entry++;
1063     }
1064 
1065     // got here so we found something to process
1066     opcodeInfo = entry->opcodeInfo;
1067     opcodeIndex = entry->opcodeInfo & 0x3f;
1068     if ((opcodeInfo & 0x80) == 0)
1069     {
1070         if ((opcodeInfo & 0x40) == 0)
1071         {
1072             // OPCODE_TYPE_LIT
1073             strcpy(line, LiteralStringArray[opcodeIndex]);
1074         }
1075         else
1076         {
1077             // OPCODE_TYPE_ARR
1078             const struct ShiftMaskAndIndexStartInfo* opcodeByMaskInfo = &OpcodeByMaskShiftMaskArray[opcodeIndex];
1079             uint8_t index = opcodeByMaskInfo->indexStart + shiftMaskToByte(b, opcodeByMaskInfo->shiftMaskInfo);
1080             strcpy(line, LiteralStringArray[OpcodeByMaskArray[index]]);
1081         }
1082     }
1083     else
1084     {
1085         // OPCODE_TYPE_BIT
1086         buildOpcode(b, opcodeIndex, line);
1087     }
1088 
1089     for(argCount = 0; argCount < 2; argCount++)
1090     {
1091         uint8_t argInfo = (&entry->argInfoIndex0)[argCount];
1092         char buffer[8];
1093         const char* argText;
1094         char* arg = args[argCount];
1095         uint16_t argData;
1096         const char* format = NULL;
1097         uint8_t lit = 0xff;
1098         bool contents = false;
1099 
1100         // no more arguments
1101         if (argInfo == 0)
1102         {
1103             break;
1104         }
1105 
1106         // extra info --swappable/contents
1107         if ( ((argInfo & ARG_TYPE_SWAPPABLE) != 0) && !swap)
1108         {
1109             if (table < 4)
1110             {
1111                 swap = (b & 0x08) != 0;
1112             }
1113             else if (table == TABLE_ED)
1114             {
1115                 uint8_t swapMask;
1116                 // entryMask bit
1117                 //   c6       0/0x01
1118                 //   c7       3/0x08
1119                 //   e7       4/0x10
1120                 if ((entry->mask & 0x20) == 0)
1121                 {
1122                     if ((entry->mask & 0x01) == 0)
1123                     {
1124                         swapMask = 0x01;
1125                     }
1126                     else
1127                     {
1128                         swapMask = 0x08;
1129                     }
1130                 }
1131                 else
1132                 {
1133                     swapMask = 0x10;
1134                 }
1135                 swap = (b & swapMask) != 0;
1136             }
1137         }
1138         contents = (argInfo & ARG_TYPE_CONTENTS) != 0;
1139 
1140         argInfo &= 0x3f; // remove swap/contents bits
1141         // either set lit or argText
1142         if (argInfo < ARG_TYPE_LIT)
1143         {
1144             // Specials
1145             switch (argInfo & 0x07)
1146             {
1147             case ARG_INFO_rel:
1148                 argData = (uint16_t)(loc - 1) + (int8_t)(*loc);
1149                 loc++;
1150                 format = FORMAT4;
1151                 break;
1152             case ARG_INFO_imm8:
1153                 argData = (uint8_t)(*loc);
1154                 loc++;
1155                 format = FORMAT2;
1156                 break;
1157             case ARG_INFO_imm16:
1158                 argData = *(uint16_t*)loc;
1159                 loc += 2;
1160                 format = FORMAT4;
1161                 break;
1162             case ARG_INFO_bit:
1163                 argData = (b >> 3) & 0x07;
1164                 format = FORMAT1;
1165                 break;
1166             case ARG_INFO_rst:
1167                 argData = (b & 0x38);
1168                 format = FORMAT2;
1169                 break;
1170             case ARG_INFO_im:
1171                 argData = IM_0Q12[(b & 0x18) >> 3];
1172                 format = FORMAT1C;
1173                 break;
1174             }
1175             sprintf(buffer, format, argData);
1176             argText = &buffer[0];
1177         }
1178         else if (argInfo < ARG_TYPE_MASK)
1179         {
1180             // Lit
1181             lit = ArgByLitIndexToStringLiteralIndex[argInfo & 0x0f];
1182         }
1183         else
1184         {
1185             // mask
1186             const struct ShiftMaskAndIndexStartInfo* maskInfo = &ArgShiftMaskStartIndex[argInfo & 0x0f];
1187             //printf("MASKINGO: %02x b: %02x -> %d\r\n", maskInfo->shiftMaskInfo, b, shiftMaskToByte(b, maskInfo->shiftMaskInfo));
1188             lit = ArgByMaskArray[maskInfo->indexStart + shiftMaskToByte(b, maskInfo->shiftMaskInfo)];
1189         }
1190 
1191         // indexed lit might have the contents flag set
1192         if ((lit != 0xff) && ((lit & ARG_TYPE_CONTENTS) != 0))
1193         {
1194             lit &= 0x3f;
1195             contents = true;
1196         }
1197 
1198         // Convert DD/FD, hl to ix/iy and (hl) to (ix/iy+(int8_t)d)
1199         if ((hl_ix_iy > 0) && (lit == DA_LIT_hl))
1200         {
1201             if (!contents || (table < 4 && b == 0xe9))
1202             {
1203                 lit = (DA_LIT_hl + hl_ix_iy);
1204             }
1205             else
1206             {
1207                 offset = (int16_t)*(int8_t*)loc++;
1208                 sprintf(buffer, "%s%c%02x", LiteralStringArray[DA_LIT_hl + hl_ix_iy], (offset < 0) ? '-' : '+', abs(offset));
1209                 argText = &buffer[0];
1210                 lit = 0xff; // switch lit out and argText in
1211             }
1212         }
1213 
1214         if (lit != 0xff)
1215         {
1216             argText = LiteralStringArray[lit];
1217         }
1218 
1219         if (contents)
1220         {
1221             sprintf(arg, "(%s)", argText);
1222         }
1223         else
1224         {
1225             strcpy(arg, argText);
1226         }
1227 
1228         argInfo >>= 4;
1229     }
1230     if (swap)
1231     {
1232         args[0] = arg1;
1233         args[1] = arg0;
1234     }
1235     if (argCount > 0)
1236     {
1237         strcat(line, " ");
1238         strcat(line, args[0]);
1239     }
1240     if (argCount > 1)
1241     {
1242         strcat(line, ", ");
1243         line = strcat(line, args[1]);
1244     }
1245     return loc;
1246 }