Monday, October 26, 2015

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.


// 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;
}

1 comment: