Disassembly
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 }