Anyone needing a Z-80 disassembler is free to use what they want from this.
// Associate shiftMask to a StartInfo
struct ShiftMaskAndIndexStartInfo {
// upper nibble is shift amount. Bottom nibble is mask
uint8_t shiftMaskInfo;
// A start index
uint8_t indexStart;
};
// Information to create an instruction
struct InstructionInfo
{
// 8 bit mask of relevant bits
uint8_t mask;
// expected value after anding with mask
uint8_t value;
// 7-6: type 5-0: index
uint8_t opcodeInfo;
// arg0 info
uint8_t argInfoIndex0;
// arg1 info
uint8_t argInfoIndex1;
};
// Opcode builder
struct OpcodeBuildInfo {
// 8 bit mask of relevant bits
uint8_t mask;
// Expected value after anding with mask
uint8_t value;
// Letter to append if (b & mask) == value
char letter;
};
// Keep synched with InstructionTableArray
enum InstructionTableIndices
{
TABLE_MAIN_00,
TABLE_MAIN_40,
TABLE_MAIN_80,
TABLE_MAIN_C0,
TABLE_CB,
TABLE_ED,
TABLE_COUNT
};
// keep synched with LiteralStringArray
enum LiteralStringIndices {
DA_LIT_Q,
DA_LIT_nop,
DA_LIT_djnz,
DA_LIT_jr,
DA_LIT_ld,
DA_LIT_add,
DA_LIT_ex,
DA_LIT_halt,
DA_LIT_ret,
DA_LIT_call,
DA_LIT_jp,
DA_LIT_exx,
DA_LIT_rst,
DA_LIT_neg,
DA_LIT_reti,
DA_LIT_retn,
DA_LIT_im,
DA_LIT_inc,
DA_LIT_dec,
DA_LIT_adc,
DA_LIT_sub,
DA_LIT_sbc,
DA_LIT_and,
DA_LIT_xor,
DA_LIT_or,
DA_LIT_cp,
DA_LIT_daa,
DA_LIT_cpl,
DA_LIT_scf,
DA_LIT_ccf,
DA_LIT_pop,
DA_LIT_push,
DA_LIT_out,
DA_LIT_in,
DA_LIT_di,
DA_LIT_ei,
DA_LIT_bit,
DA_LIT_set,
DA_LIT_res,
DA_LIT_nz,
DA_LIT_z,
DA_LIT_nc,
DA_LIT_c,
DA_LIT_po,
DA_LIT_pe,
DA_LIT_p,
DA_LIT_m,
DA_LIT_bc,
DA_LIT_de,
DA_LIT_hl,
DA_LIT_ix,
DA_LIT_iy,
DA_LIT_sp,
DA_LIT_af,
DA_LIT_a,
DA_LIT_b,
DA_LIT_d,
DA_LIT_e,
DA_LIT_h,
DA_LIT_l,
DA_LIT_i,
DA_LIT_r,
DA_LIT_af_alt,
DA_LIT_COUNT
};
// keep synced with enum DisassembleLiteralIndices
static const char* const LiteralStringArray[DA_LIT_COUNT] =
{
"?",
"nop",
"djnz",
"jr",
"ld",
"add",
"ex",
"halt",
"ret",
"call",
"jp",
"exx",
"rst",
"neg",
"reti",
"retn",
"im",
"inc",
"dec",
"adc",
"sub",
"sbc",
"and",
"xor",
"or",
"cp",
"daa",
"cpl",
"scf",
"ccf",
"pop",
"push",
"out",
"in",
"di",
"ei",
"bit",
"set",
"res",
"nz",
"z",
"nc",
"c",
"po",
"pe",
"p",
"m",
"bc",
"de",
"hl",
"ix",
"iy",
"sp",
"af",
"a",
"b",
"d",
"e",
"h",
"l",
"i",
"r",
"af'"
};
/*
0: @alu : add, adc, sub, sbc, and, xor, or, cp
1 : @bitsetres : -, bit, set, res
2 : @daacplscfccf : daa, cpl, scf, ccf
3 : @incdec : inc, dec
4 : @poppush : pop, push
5 : @outin : out, in
6 : @inout : in, out
7 : @diei : di, ei
8 : @sbcadc : sbc, adc
*/
enum OpcodeByMaskInfoIndices
{
OPCODE_MASKABLE_ALU, // 10@@ @rrr -- 11@@ @110
OPCODE_MASKABLE_BIT, // @@## #rrr
OPCODE_MASKABLE_DAA, // 001@ @111
OPCODE_MASKABLE_INC_RRS, // 00rr @011
OPCODE_MASKABLE_INC_R, // 00rr r10@
OPCODE_MASKABLE_POP, // 11rr 0@01
OPCODE_MASKABLE_INOUT, // 01rr r00@
OPCODE_MASKABLE_OUTIN, // 1110 @011
OPCODE_MASKABLE_DIEI, // 1111 @011
OPCODE_MASKABLE_SBC, // 01rr @010
OPCODE_MASKABLE_COUNT,
};
enum OpcodeByMaskStartIndices
{
MASKABLE_ALU = 0,
MASKABLE_BIT = MASKABLE_ALU + 8-1, // first element of bit can't exist, so collapse
MASKABLE_DAA = MASKABLE_BIT + 4,
MASKABLE_INC = MASKABLE_DAA + 4,
MASKABLE_POP = MASKABLE_INC + 2,
MASKABLE_INOUT = MASKABLE_POP + 2,
MASKABLE_OUTIN = MASKABLE_INOUT + 2 - 1,
MASKABLE_DIEI = MASKABLE_OUTIN + 2,
MASKABLE_SBC = MASKABLE_DIEI + 2,
MASKABLE_ARRAY_COUNT = MASKABLE_SBC + 2
};
static const struct ShiftMaskAndIndexStartInfo OpcodeByMaskShiftMaskArray[OPCODE_MASKABLE_COUNT] =
{
{
0x37, MASKABLE_ALU
},
{
0x63, MASKABLE_BIT
},
{
0x33, MASKABLE_DAA
},
{ // r
0x31, MASKABLE_INC
},
{ // rrs
0x01, MASKABLE_INC
},
{
0x21, MASKABLE_POP
},
{
0x01, MASKABLE_INOUT
},
{
0x31, MASKABLE_OUTIN
},
{
0x31, MASKABLE_DIEI
},
{
0x31, MASKABLE_SBC
},
};
static const uint8_t OpcodeByMaskArray[MASKABLE_ARRAY_COUNT] =
{
// MASKABLE_ALU = 0
DA_LIT_add, // 0
DA_LIT_adc,
DA_LIT_sub,
DA_LIT_sbc,
DA_LIT_and,
DA_LIT_xor,
DA_LIT_or,
// MASKABLE_BIT = 7
DA_LIT_cp,
DA_LIT_bit, // 8
DA_LIT_res,
DA_LIT_set,
// MASKABLE_DAA
DA_LIT_daa, // 11
DA_LIT_cpl,
DA_LIT_scf,
DA_LIT_ccf,
// MASKABLE_INC
DA_LIT_inc, // 15
DA_LIT_dec,
// MASKABLE_POP
DA_LIT_pop, // 17
DA_LIT_push,
// MASKABLE_INOUT
DA_LIT_in, // 19
// MASKABLE_OUTIN
DA_LIT_out, // 20
DA_LIT_in,
// MASKABLE_EIDI
DA_LIT_di, // 22
DA_LIT_ei,
// MASKABLE_SBC
DA_LIT_sbc, // 24
DA_LIT_adc
};
// ROTATE Main-00-00; ED-80-00; CB-02-00 "r"
// SHIFT CB-02-02 "s"
// LD ED-07-00 "ld"
// CP ED-07-01 "cp"
// IN ED-07-02 "in"
// OT ED-07-03 "ot
// CARRY MAIN/CB-01-01 "c"
// LOGIGAL CB-03-03 "l"
// ARITH/Acc CB-03-02 "a"
// LEFT Main-08-00; "l"
// RIGHT Main-08-08; "r"
// INC ED-88-80 "i"
// DEC ED-88-88 "d"
// REPEAT ED-f0-b0 "r"
enum OpcodeByBuilderIndices
{
OPCODE_BUILD_RLCA,
OPCODE_BUILD_RRD,
OPCODE_BUILD_LDI,
OPCODE_BUILD_RLC
};
// sync with OpcodeBuilderArray and OpcodeByBuilderIndices
static const uint8_t OpcodeBuilderIndexToOpcodeBuilderArrayStartIndex[] = {
0,
6,
11,
24,
33
};
// synch with OpcodeBuilderIndexToOpcodeBuilderArrayStartIndex
static const struct OpcodeBuildInfo OpcodeBuilderArray[32] = {
//MAIN table
//{
// main = 0 (5)
{ 0x00, 0x00, 'r' },
{ 0x08, 0x00, 'l' },
{ 0x08, 0x08, 'r' },
{ 0x10, 0x00, 'c' },
{ 0x00, 0x00, 'a' },
{ 0x00, 0x00, 0 },
//},
// ED table
//{
{ 0x00, 0x00, 'r' },
{ 0xc8, 0x40, 'r' },
{ 0xc8, 0x48, 'l' },
{ 0x00, 0x00, 'd' },
{ 0x00, 0x00, 0 },
//},
//{
{ 0xc7, 0x80, 'l' },
{ 0xc7, 0x80, 'd' },
{ 0xc7, 0x81, 'c' },
{ 0xc7, 0x81, 'p' },
{ 0xc7, 0x82, 'i' },
{ 0xc7, 0x82, 'n' },
{ 0xc7, 0x83, 'o' },
{ 0xf7, 0xa3, 'u' },
{ 0xc7, 0x83, 't' },
{ 0x88, 0x80, 'i' },
{ 0x88, 0x88, 'd' },
{ 0xf0, 0xb0, 'r' },
{ 0x00, 0x00, 0 },
//},
// CB table
//{
{ 0x20, 0x00, 'r' },
{ 0x20, 0x20, 's' },
{ 0x08, 0x00, 'l' },
{ 0x08, 0x08, 'r' },
{ 0x30, 0x00, 'c' },
{ 0x30, 0x20, 'a' },
{ 0x30, 0x30, 'l' },
{ 0x00, 0x00, 0 },
//}
};
#define ARG_TYPE_SPECIAL (0x00)
#define ARG_TYPE_LIT (0x10)
#define ARG_TYPE_MASK (0x20)
#define ARG_TYPE_SWAPPABLE (0x40)
#define ARG_TYPE_CONTENTS (0x80)
enum ArgByMaskStartIndices
{
ARG_ARR_FL = 0,
ARG_ARR_RRS = ARG_ARR_FL + 8,
ARG_ARR_RRA = ARG_ARR_RRS + 4,
ARG_ARR_HLA = ARG_ARR_RRA + 4,
ARG_ARR_R = ARG_ARR_HLA + 2,
ARG_ARR_IR = ARG_ARR_R + 8,
ARG_ARR_IM = ARG_ARR_IR + 2,
ARG_ARR_COUNT = ARG_ARR_IM + 4
};
static const uint8_t ArgByMaskArray[ARG_ARR_COUNT] =
{
DA_LIT_nz, // 0
DA_LIT_z,
DA_LIT_nc,
DA_LIT_c,
DA_LIT_po,
DA_LIT_pe,
DA_LIT_p,
DA_LIT_m,
DA_LIT_bc, // 8
DA_LIT_de,
DA_LIT_hl,
DA_LIT_sp,
DA_LIT_bc, // 12
DA_LIT_de,
DA_LIT_hl,
DA_LIT_af,
DA_LIT_hl, // 16
DA_LIT_a,
DA_LIT_b, // 18
DA_LIT_c,
DA_LIT_d,
DA_LIT_e,
DA_LIT_h,
DA_LIT_l,
DA_LIT_hl | ARG_TYPE_CONTENTS,
DA_LIT_a,
DA_LIT_i, // 42
DA_LIT_r,
};
/*
//
#define ARG_TYPE_MASK (0x01)
// mask, index
#define ARG_TYPE_LIT (0x02)
// (int8_t)(*(pc)) + pc; pc++;
#define ARG_TYPE_REL (0x03)
#define ARG_TYPE_IMM8 (0x04)
#define ARG_TYPE_IMM16 (0x05)
#define ARG_TYPE_ASCII (0x06)
#define ARG_TYPE_RST (0x07)
*/
// index to next table ED/CB/DDFD
#define OPCODE_TYPE_LIT (0x00)
// index
#define OPCODE_TYPE_ARR (0x40)
// mask, index
#define OPCODE_TYPE_BUILD (0x80)
// index
#define OPCODE_TYPE_SUB (0xc0)
// [8:mask],[8:val],[2:opcode, 6:index0] [1: swappable 3: arg0 3: arg1]
// index 0 is literal index for literal, or maskables index for maskable. For build, the build index
// swapbit is there only if swappable. Swap bit is 3 for main, 4 for ED
// arginfo is:
// if 00 no arg
// if 01 mask [8: mask(-sss-mmm)] [8: arg_arr index] or 3bit is mask index, 5 bits is arg_arr index
// if 02 lit [8: lit index]
// if 03 uint8 get next byte
// if 04 RelAddr get next byte, add to location
// if 05 uint16 get next word
// bit 8 swappable
enum ArgInfo {
ARG_INFO_NONE,
ARG_INFO_rel, // 1
ARG_INFO_imm8,
ARG_INFO_imm16,
ARG_INFO_bit,
ARG_INFO_rst,
ARG_INFO_im,
ARG_INFO_LIT_a = ARG_TYPE_LIT, // 4
ARG_INFO_LIT_af,
ARG_INFO_LIT_af_alt,
ARG_INFO_LIT_c,
ARG_INFO_LIT_de,
ARG_INFO_LIT_hl, // 8
ARG_INFO_LIT_sp,
//ARG_INFO_LIT_q,
ARG_INFO_MASK_30_RRS = ARG_TYPE_MASK,
ARG_INFO_MASK_30_RRA,
ARG_INFO_MASK_07_R,
ARG_INFO_MASK_38_R,
ARG_INFO_MASK_18_FL,
ARG_INFO_MASK_38_FL,
ARG_INFO_MASK_08_IR,
};
/*
enum ArgMaskIndex {
ARG_MASK_30_RRS,
ARG_MASK_30_RRA,
ARG_MASK_07_R,
ARG_MASK_38_R,
ARG_MASK_30_FL,
ARG_MASK_38_FL,
ARG_MASK_38_RST,
ARG_MASK_80_IR,
ARG_MASK_COUNT,
};
*/
// sync with ArgInfo from ARG_TYPE_LIT
static const uint8_t ArgByLitIndexToStringLiteralIndex[] = {
DA_LIT_a,
DA_LIT_af,
DA_LIT_af_alt,
DA_LIT_c,
DA_LIT_de,
DA_LIT_hl,
DA_LIT_sp
};
// sync with ArgInfo from ARG_TYPE_MASK
static const struct ShiftMaskAndIndexStartInfo ArgShiftMaskStartIndex[] =
{
{ /*0x30*/ 0x43, ARG_ARR_RRS },
{ /*0x30*/ 0x43, ARG_ARR_RRA },
{ /*0x07*/ 0x07, ARG_ARR_R },
{ /*0x38*/ 0x37, ARG_ARR_R },
{ /*0x18*/ 0x33, ARG_ARR_FL },
{ /*0x38*/ 0x37, ARG_ARR_FL },
{ /*0x08*/ 0x31, ARG_ARR_IR },
};
char* buildOpcode(uint8_t opcode, uint8_t table, char* line)
{
const struct OpcodeBuildInfo* cand = &OpcodeBuilderArray[OpcodeBuilderIndexToOpcodeBuilderArrayStartIndex[table]];
while (cand->letter != 0)
{
if ((opcode & cand->mask) == cand->value)
{
*line++ = cand->letter;
}
cand++;
}
*line++ = '\0';
return line;
}
static const struct InstructionInfo InstructionInfoTableMain_00[] =
{
{
0xff,
0x00,
DA_LIT_nop | OPCODE_TYPE_LIT,
ARG_INFO_NONE,
ARG_INFO_NONE,
},
{
0xff,
0x10,
DA_LIT_djnz | OPCODE_TYPE_LIT,
ARG_INFO_rel,
ARG_INFO_NONE
},
{
0xe7,
0x20,
DA_LIT_jr | OPCODE_TYPE_LIT,
ARG_INFO_MASK_18_FL,
ARG_INFO_rel,
},
{
0xcf,
0x01,
DA_LIT_ld | OPCODE_TYPE_LIT,
ARG_INFO_MASK_30_RRS,
ARG_INFO_imm16
},
{
0xe7,
0x02,
DA_LIT_ld | OPCODE_TYPE_LIT,
ARG_INFO_MASK_30_RRS | ARG_TYPE_SWAPPABLE | ARG_TYPE_CONTENTS, // swap on 0x08
ARG_INFO_LIT_a
},
{
0xcf,
0x09,
DA_LIT_add | OPCODE_TYPE_LIT,
ARG_INFO_LIT_hl,
ARG_INFO_MASK_30_RRS
},
{
0xf7,
0x32,
DA_LIT_ld | OPCODE_TYPE_LIT,
ARG_INFO_imm16 | ARG_TYPE_SWAPPABLE | ARG_TYPE_CONTENTS, // swap on 0x08
ARG_INFO_LIT_a
},
{
0xf7,
0x22,
DA_LIT_ld | OPCODE_TYPE_LIT,
ARG_INFO_imm16 | ARG_TYPE_SWAPPABLE | ARG_TYPE_CONTENTS, // swap on 0x08
ARG_INFO_LIT_hl
},
{
0xc7,
0x03,
OPCODE_MASKABLE_INC_RRS | OPCODE_TYPE_ARR,
ARG_INFO_MASK_30_RRS,
ARG_INFO_NONE
},
{
0xc6,
0x04,
OPCODE_MASKABLE_INC_R | OPCODE_TYPE_ARR,
ARG_INFO_MASK_38_R,
ARG_INFO_NONE
},
{
0xc7,
0x06,
DA_LIT_ld | OPCODE_TYPE_LIT,
ARG_INFO_MASK_38_R,
ARG_INFO_imm8
},
{
0xff,
0x08,
DA_LIT_ex | OPCODE_TYPE_LIT,
ARG_INFO_LIT_af,
ARG_INFO_LIT_af_alt
},
{
0xff,
0x18,
DA_LIT_jr | OPCODE_TYPE_LIT,
ARG_INFO_rel,
ARG_INFO_NONE
},
{
0xe7,
0x27,
OPCODE_MASKABLE_DAA | OPCODE_TYPE_ARR,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{
0xe7,
0x07,
OPCODE_BUILD_RLCA | OPCODE_TYPE_BUILD,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{
0x00,
0x00,
DA_LIT_Q | OPCODE_TYPE_LIT,
ARG_INFO_NONE,
ARG_INFO_NONE
}
};
static const struct InstructionInfo InstructionInfoTableMain_40[] =
{
{ // Must be before ld r,r
0xff,
0x76,
DA_LIT_halt | OPCODE_TYPE_LIT,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{ // Must be after halt
0xc0,
0x40,
DA_LIT_ld | OPCODE_TYPE_LIT,
ARG_INFO_MASK_38_R,
ARG_INFO_MASK_07_R
},
{
0x00,
0x00,
DA_LIT_Q | OPCODE_TYPE_LIT,
ARG_INFO_NONE,
ARG_INFO_NONE
}
};
static const struct InstructionInfo InstructionInfoTableMain_80[] =
{
{
0xc0,
0x80,
OPCODE_MASKABLE_ALU | OPCODE_TYPE_ARR,
ARG_INFO_LIT_a,
ARG_INFO_MASK_07_R
},
{
0x00,
0x00,
DA_LIT_Q | OPCODE_TYPE_LIT,
ARG_INFO_NONE,
ARG_INFO_NONE
}
};
static const struct InstructionInfo InstructionInfoTableMain_c0[] =
{
{
0xc7,
0xc0,
DA_LIT_ret | OPCODE_TYPE_LIT,
ARG_INFO_MASK_38_FL,
ARG_INFO_NONE
},
{
0xff,
0xcd,
DA_LIT_call | OPCODE_TYPE_LIT,
ARG_INFO_imm16,
ARG_INFO_NONE
},
{
0xff,
0xc3,
DA_LIT_jp | OPCODE_TYPE_LIT,
ARG_INFO_imm16,
ARG_INFO_NONE
},
{
0xff,
0xe9,
DA_LIT_jp | OPCODE_TYPE_LIT,
ARG_INFO_LIT_hl | ARG_TYPE_CONTENTS,
ARG_INFO_NONE
},
{
0xff,
0xf9,
DA_LIT_ld | OPCODE_TYPE_LIT,
ARG_INFO_LIT_sp,
ARG_INFO_LIT_hl
},
{
0xcb,
0xc1,
OPCODE_MASKABLE_POP | OPCODE_TYPE_ARR,
ARG_INFO_MASK_30_RRA,
ARG_INFO_NONE
},
{
0xff,
0xc9,
DA_LIT_ret | OPCODE_TYPE_LIT,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{
0xff,
0xd9,
DA_LIT_exx | OPCODE_TYPE_LIT,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{
0xc7,
0xc2,
DA_LIT_jp | OPCODE_TYPE_LIT,
ARG_INFO_MASK_38_FL,
ARG_INFO_imm16
},
{
0xc7,
0xc4,
DA_LIT_call | OPCODE_TYPE_LIT,
ARG_INFO_MASK_38_FL,
ARG_INFO_imm16
},
{
0xc7,
0xc7,
DA_LIT_rst | OPCODE_TYPE_LIT,
ARG_INFO_rst,
ARG_INFO_NONE
},
{
0xc7,
0xc6,
OPCODE_MASKABLE_ALU | OPCODE_TYPE_ARR,
ARG_INFO_LIT_a,
ARG_INFO_imm8
},
{
0xf7,
0xd3,
OPCODE_MASKABLE_OUTIN | OPCODE_TYPE_ARR,
ARG_INFO_imm8 | ARG_TYPE_CONTENTS | ARG_TYPE_SWAPPABLE, // swap on 0x08
ARG_INFO_LIT_a
},
{
0xf7,
0xf3,
OPCODE_MASKABLE_DIEI | OPCODE_TYPE_ARR,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{
0xff,
0xe3,
DA_LIT_ex | OPCODE_TYPE_LIT,
ARG_INFO_LIT_sp | ARG_TYPE_CONTENTS,
ARG_INFO_LIT_hl
},
{
0xff,
0xeb,
DA_LIT_ex | OPCODE_TYPE_LIT,
ARG_INFO_LIT_de,
ARG_INFO_LIT_hl
},
{
0xff,
0xcb,
TABLE_CB | OPCODE_TYPE_SUB,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{
0xff,
0xed,
TABLE_ED | OPCODE_TYPE_SUB,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{
0xfd,
0xfd,
0 | OPCODE_TYPE_SUB,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{
0x00,
0x00,
DA_LIT_Q | OPCODE_TYPE_LIT,
ARG_INFO_NONE,
ARG_INFO_NONE
}
};
static const struct InstructionInfo InstructionInfoTableCB[] =
{
// CB
{
0xc0,
0x00,
OPCODE_BUILD_RLC | OPCODE_TYPE_BUILD,
ARG_INFO_MASK_07_R,
ARG_INFO_NONE
},
{
0x00,
0x00,
OPCODE_MASKABLE_BIT | OPCODE_TYPE_ARR,
ARG_INFO_bit,
ARG_INFO_MASK_07_R
},
{
0x00,
0x00,
DA_LIT_Q | OPCODE_TYPE_LIT,
ARG_INFO_NONE,
ARG_INFO_NONE
}
};
static const struct InstructionInfo InstructionInfoTableED[] =
{
// ED
{
0xc6,
0x40,
OPCODE_MASKABLE_INOUT | OPCODE_TYPE_ARR,
ARG_INFO_MASK_38_R | ARG_TYPE_SWAPPABLE, // TODO swap on 0x01
ARG_INFO_LIT_c | ARG_TYPE_CONTENTS
},
{
0xc7,
0x42,
OPCODE_MASKABLE_SBC | OPCODE_TYPE_ARR,
ARG_INFO_LIT_hl,
ARG_INFO_MASK_30_RRS
},
{
0xc7,
0x43,
DA_LIT_ld | OPCODE_TYPE_LIT,
ARG_INFO_imm16 | ARG_TYPE_CONTENTS | ARG_TYPE_SWAPPABLE, // TODO swap in 0x08
ARG_INFO_MASK_30_RRS
},
{
0xc7,
0x44,
DA_LIT_neg | OPCODE_TYPE_LIT,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{
0xff,
0x4d,
DA_LIT_reti | OPCODE_TYPE_LIT,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{
0xc7,
0x45,
DA_LIT_retn | OPCODE_TYPE_LIT,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{
0xe7,
0x47,
DA_LIT_ld | OPCODE_TYPE_LIT,
ARG_INFO_MASK_08_IR | ARG_TYPE_SWAPPABLE, // TODO swap not working -- on bit 4 0x10
ARG_INFO_LIT_a
},
{
0xf7,
0x67,
OPCODE_BUILD_RRD | OPCODE_TYPE_BUILD,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{
0xc7,
0x46,
DA_LIT_im | OPCODE_TYPE_LIT,
ARG_INFO_im,
ARG_INFO_NONE
},
{
0xe4,
0xa0,
OPCODE_BUILD_LDI | OPCODE_TYPE_BUILD,
ARG_INFO_NONE,
ARG_INFO_NONE
},
{
0x00,
0x00,
DA_LIT_Q | OPCODE_TYPE_LIT,
ARG_INFO_NONE,
ARG_INFO_NONE
}
};
// sync with InstructionTableIndices
static const struct InstructionInfo* InstructionInfoTableArray[TABLE_COUNT] =
{
InstructionInfoTableMain_00,
InstructionInfoTableMain_40,
InstructionInfoTableMain_80,
InstructionInfoTableMain_c0,
InstructionInfoTableCB,
InstructionInfoTableED
};
uint8_t shiftMaskToByte(uint8_t b, uint8_t shiftMaskByte)
{
return (b >> (shiftMaskByte >> 4)) & shiftMaskByte & 0x0f;
}
static const char IM_0Q12[4] = { '0', '?', '1', '2' };
static const char* const FORMAT1C = "%c";
static const char* const FORMAT1 = "%01xh";
static const char* const FORMAT2 = "%02xh";
static const char* const FORMAT4 = "%04xh";
static const char* const FORMAT_CONTENTS = "(%s)";
uint8_t* disassemble(uint8_t* loc, char* line)
{
uint8_t b; // instruction byte being disassembled
uint8_t table = 0;
const struct InstructionInfo* entry;
bool swap = false;
uint8_t argCount = 0;
char arg0[8];
char arg1[8];
char* args[2] = { arg0, arg1 };
uint8_t opcodeInfo;
uint8_t opcodeIndex;
uint16_t offset;
uint8_t hl_ix_iy = 0;
char offsetStr[6] = { 0 };
*line = 0;
b = *loc++;
table = b >> 6;
// See if we need a special table
// 1100 1011 cb
// 1110 1101 ed
// 11-0 1--1
// 1101 1101
// 1111 1101
// 11-1 1101
if ((b & 0xdf) == 0xdd)
{
if (b == 0xdd)
{
b = *loc++;
table = b >> 6;
hl_ix_iy = 1;
}
else if (b == 0xfd)
{
b = *loc++;
table = b >> 6;
hl_ix_iy = 2;
}
}
if ((b & 0xd9) == 0xc9)
{
if (b == 0xcb)
{
b = *loc++;
table = TABLE_CB;
}
else if (b == 0xed)
{
b = *loc++;
table = TABLE_ED;
}
}
entry = InstructionInfoTableArray[table];
while (entry->mask != 0)
{
uint8_t opcodeType;
if ((b & entry->mask) == entry->value)
{
break;
}
entry++;
}
// got here so we found something to process
opcodeInfo = entry->opcodeInfo;
opcodeIndex = entry->opcodeInfo & 0x3f;
if ((opcodeInfo & 0x80) == 0)
{
if ((opcodeInfo & 0x40) == 0)
{
// OPCODE_TYPE_LIT
strcpy(line, LiteralStringArray[opcodeIndex]);
}
else
{
// OPCODE_TYPE_ARR
const struct ShiftMaskAndIndexStartInfo* opcodeByMaskInfo = &OpcodeByMaskShiftMaskArray[opcodeIndex];
uint8_t index = opcodeByMaskInfo->indexStart + shiftMaskToByte(b, opcodeByMaskInfo->shiftMaskInfo);
strcpy(line, LiteralStringArray[OpcodeByMaskArray[index]]);
}
}
else
{
// OPCODE_TYPE_BIT
buildOpcode(b, opcodeIndex, line);
}
for(argCount = 0; argCount < 2; argCount++)
{
uint8_t argInfo = (&entry->argInfoIndex0)[argCount];
char buffer[8];
const char* argText;
char* arg = args[argCount];
uint16_t argData;
const char* format = NULL;
uint8_t lit = 0xff;
bool contents = false;
// no more arguments
if (argInfo == 0)
{
break;
}
// extra info --swappable/contents
if ( ((argInfo & ARG_TYPE_SWAPPABLE) != 0) && !swap)
{
if (table < 4)
{
swap = (b & 0x08) != 0;
}
else if (table == TABLE_ED)
{
uint8_t swapMask;
// entryMask bit
// c6 0/0x01
// c7 3/0x08
// e7 4/0x10
if ((entry->mask & 0x20) == 0)
{
if ((entry->mask & 0x01) == 0)
{
swapMask = 0x01;
}
else
{
swapMask = 0x08;
}
}
else
{
swapMask = 0x10;
}
swap = (b & swapMask) != 0;
}
}
contents = (argInfo & ARG_TYPE_CONTENTS) != 0;
argInfo &= 0x3f; // remove swap/contents bits
// either set lit or argText
if (argInfo < ARG_TYPE_LIT)
{
// Specials
switch (argInfo & 0x07)
{
case ARG_INFO_rel:
argData = (uint16_t)(loc - 1) + (int8_t)(*loc);
loc++;
format = FORMAT4;
break;
case ARG_INFO_imm8:
argData = (uint8_t)(*loc);
loc++;
format = FORMAT2;
break;
case ARG_INFO_imm16:
argData = *(uint16_t*)loc;
loc += 2;
format = FORMAT4;
break;
case ARG_INFO_bit:
argData = (b >> 3) & 0x07;
format = FORMAT1;
break;
case ARG_INFO_rst:
argData = (b & 0x38);
format = FORMAT2;
break;
case ARG_INFO_im:
argData = IM_0Q12[(b & 0x18) >> 3];
format = FORMAT1C;
break;
}
sprintf(buffer, format, argData);
argText = &buffer[0];
}
else if (argInfo < ARG_TYPE_MASK)
{
// Lit
lit = ArgByLitIndexToStringLiteralIndex[argInfo & 0x0f];
}
else
{
// mask
const struct ShiftMaskAndIndexStartInfo* maskInfo = &ArgShiftMaskStartIndex[argInfo & 0x0f];
//printf("MASKINGO: %02x b: %02x -> %d\r\n", maskInfo->shiftMaskInfo, b, shiftMaskToByte(b, maskInfo->shiftMaskInfo));
lit = ArgByMaskArray[maskInfo->indexStart + shiftMaskToByte(b, maskInfo->shiftMaskInfo)];
}
// indexed lit might have the contents flag set
if ((lit != 0xff) && ((lit & ARG_TYPE_CONTENTS) != 0))
{
lit &= 0x3f;
contents = true;
}
// Convert DD/FD, hl to ix/iy and (hl) to (ix/iy+(int8_t)d)
if ((hl_ix_iy > 0) && (lit == DA_LIT_hl))
{
if (!contents || (table < 4 && b == 0xe9))
{
lit = (DA_LIT_hl + hl_ix_iy);
}
else
{
offset = (int16_t)*(int8_t*)loc++;
sprintf(buffer, "%s%c%02x", LiteralStringArray[DA_LIT_hl + hl_ix_iy], (offset < 0) ? '-' : '+', abs(offset));
argText = &buffer[0];
lit = 0xff; // switch lit out and argText in
}
}
if (lit != 0xff)
{
argText = LiteralStringArray[lit];
}
if (contents)
{
sprintf(arg, "(%s)", argText);
}
else
{
strcpy(arg, argText);
}
argInfo >>= 4;
}
if (swap)
{
args[0] = arg1;
args[1] = arg0;
}
if (argCount > 0)
{
strcat(line, " ");
strcat(line, args[0]);
}
if (argCount > 1)
{
strcat(line, ", ");
line = strcat(line, args[1]);
}
return loc;
}
Awesome! Great work.
ReplyDelete