
#include "m7700.hpp"

// 7700 addressing modes :
typedef enum {
    A_IMPL,                 // implied
    A_IMM,                  // immediate
    A_ACC_A,                // accumulator A
    A_ACC_B,                // accumulator B
    A_DIR,                  // direct
    A_DIR_BIT,              // direct bit
    A_DIR_IND_X,            // direct indexed X
    A_DIR_IND_Y,            // direct indexed Y
    A_DIR_INDI,             // direct indirect
    A_DIR_IND_X_INDI,       // direct indexed X indirect
    A_DIR_INDI_IND_Y,       // direct indirect indexed Y
    A_DIR_INDI_LONG,        // direct indirect long
    A_DIR_INDI_LONG_IND_Y,  // direct indirect long indexed Y
    A_ABS,                  // absolute
    A_ABS_BIT,              // absolute bit
    A_ABS_IND_X,            // absolute indexed X
    A_ABS_IND_Y,            // absolute indexed Y
    A_ABS_LONG,             // absolute long
    A_ABS_LONG_IND_X,       // absolute long indexed X
    A_ABS_INDI,             // absolute indirect
    A_ABS_INDI_LONG,        // absolute indirect long
    A_ABS_IND_X_INDI,       // absolute indexed X indirect
    A_STACK,                // stack
    A_STACK_S,              // stack short
    A_STACK_L,              // stack long
    A_REL,                  // relative
    A_REL_LONG,             // relative long
    A_DIR_BIT_REL,          // direct bit relative
    A_ABS_BIT_REL,          // absolute bit relative
    A_STACK_PTR_REL,        // stack pointer relative
    A_STACK_PTR_REL_IIY,    // stack pointer relative indirect indexed Y
    A_BT                    // block transfer
} m7700_addr_mode_t;

// 7700 operation codes :
struct opcode {
    int insn;                           // instruction
    int code;                           // code of opcode
    m7700_addr_mode_t mode;             // addressing mode
};

// 7700 instructions
static const struct opcode opcodes_7700[] = {
    // ADC
    { m7700_adc,       0x69,   A_IMM                           },
    { m7700_adc,       0x65,   A_DIR                           },
    { m7700_adc,       0x75,   A_DIR_IND_X                     },
    { m7700_adc,       0x72,   A_DIR_INDI                      },
    { m7700_adc,       0x61,   A_DIR_IND_X_INDI                },
    { m7700_adc,       0x71,   A_DIR_INDI_IND_Y                },
    { m7700_adc,       0x67,   A_DIR_INDI_LONG                 },
    { m7700_adc,       0x77,   A_DIR_INDI_LONG_IND_Y           },
    { m7700_adc,       0x6D,   A_ABS                           },
    { m7700_adc,       0x7D,   A_ABS_IND_X                     },
    { m7700_adc,       0x79,   A_ABS_IND_Y                     },
    { m7700_adc,       0x6F,   A_ABS_LONG                      },
    { m7700_adc,       0x7F,   A_ABS_LONG_IND_X                },
    { m7700_adc,       0x63,   A_STACK_PTR_REL                 },
    { m7700_adc,       0x73,   A_STACK_PTR_REL_IIY             },
    // AND
    { m7700_and,       0x29,   A_IMM                           },
    { m7700_and,       0x25,   A_DIR                           },
    { m7700_and,       0x35,   A_DIR_IND_X                     },
    { m7700_and,       0x32,   A_DIR_INDI                      },
    { m7700_and,       0x21,   A_DIR_IND_X_INDI                },
    { m7700_and,       0x31,   A_DIR_INDI_IND_Y                },
    { m7700_and,       0x27,   A_DIR_INDI_LONG                 },
    { m7700_and,       0x37,   A_DIR_INDI_LONG_IND_Y           },
    { m7700_and,       0x2D,   A_ABS                           },
    { m7700_and,       0x3D,   A_ABS_IND_X                     },
    { m7700_and,       0x39,   A_ABS_IND_Y                     },
    { m7700_and,       0x2F,   A_ABS_LONG                      },
    { m7700_and,       0x3F,   A_ABS_LONG_IND_X                },
    { m7700_and,       0x23,   A_STACK_PTR_REL                 },
    { m7700_and,       0x33,   A_STACK_PTR_REL_IIY             },
    // ASL
    { m7700_asl,       0x0A,   A_ACC_A                         },
    { m7700_asl,       0x420A, A_ACC_B                         },
    { m7700_asl,       0x06,   A_DIR                           },
    { m7700_asl,       0x16,   A_DIR_IND_X                     },
    { m7700_asl,       0x0E,   A_ABS                           },
    { m7700_asl,       0x1E,   A_ABS_IND_X                     },
    // BBC
    { m7700_bbc,       0x34,   A_DIR_BIT_REL                   },
    { m7700_bbc,       0x3C,   A_ABS_BIT_REL                   },
    // BBS
    { m7700_bbs,       0x24,   A_DIR_BIT_REL                   },
    { m7700_bbs,       0x2C,   A_ABS_BIT_REL                   },
    // BCC
    { m7700_bcc,       0x90,   A_REL                           },
    // BCS
    { m7700_bcs,       0xB0,   A_REL                           },
    // BEQ
    { m7700_beq,       0xF0,   A_REL                           },
    // BMI
    { m7700_bmi,       0x30,   A_REL                           },
    // BNE
    { m7700_bne,       0xD0,   A_REL                           },
    // BPL
    { m7700_bpl,       0x10,   A_REL                           },
    // BRA
    { m7700_bra,       0x80,   A_REL                           },
    { m7700_bra,       0x82,   A_REL_LONG                      },
#if 0 /* detected as a special insn */
    // BRK
    { m7700_brk,       0x00EA, A_IMPL                          },
#endif
    // BVC
    { m7700_bvc,       0x50,   A_REL                           },
    // BVS
    { m7700_bvs,       0x70,   A_REL                           },
    // CLB
    { m7700_clb,       0x14,   A_DIR_BIT                       },
    { m7700_clb,       0x1C,   A_ABS_BIT                       },
    // CLC
    { m7700_clc,       0x18,   A_IMPL                          },
    // CLI
    { m7700_cli,       0x58,   A_IMPL                          },
    // CLM
    { m7700_clm,       0xD8,   A_IMPL                          },
    // CLP
    { m7700_clp,       0xC2,   A_IMM                           },
    // CLV
    { m7700_clv,       0xB8,   A_IMPL                          },
    // CMP
    { m7700_cmp,       0xC9,   A_IMM                           },
    { m7700_cmp,       0xC5,   A_DIR                           },
    { m7700_cmp,       0xD5,   A_DIR_IND_X                     },
    { m7700_cmp,       0xD2,   A_DIR_INDI                      },
    { m7700_cmp,       0xC1,   A_DIR_IND_X_INDI                },
    { m7700_cmp,       0xD1,   A_DIR_INDI_IND_Y                },
    { m7700_cmp,       0xC7,   A_DIR_INDI_LONG                 },
    { m7700_cmp,       0xD7,   A_DIR_INDI_LONG_IND_Y           },
    { m7700_cmp,       0xCD,   A_ABS                           },
    { m7700_cmp,       0xDD,   A_ABS_IND_X                     },
    { m7700_cmp,       0xD9,   A_ABS_IND_Y                     },
    { m7700_cmp,       0xCF,   A_ABS_LONG                      },
    { m7700_cmp,       0xDF,   A_ABS_LONG_IND_X                },
    { m7700_cmp,       0xC3,   A_STACK_PTR_REL                 },
    { m7700_cmp,       0xD3,   A_STACK_PTR_REL_IIY             },
    // CPX
    { m7700_cpx,       0xE0,   A_IMM                           },
    { m7700_cpx,       0xE4,   A_DIR                           },
    { m7700_cpx,       0xEC,   A_ABS                           },
    // CPY
    { m7700_cpy,       0xC0,   A_IMM                           },
    { m7700_cpy,       0xC4,   A_DIR                           },
    { m7700_cpy,       0xCC,   A_ABS                           },
    // DEC
    { m7700_dec,       0x1A,   A_ACC_A                         },
    { m7700_dec,       0x421A, A_ACC_B                         },
    { m7700_dec,       0xC6,   A_DIR                           },
    { m7700_dec,       0xD6,   A_DIR_IND_X                     },
    { m7700_dec,       0xCE,   A_ABS                           },
    { m7700_dec,       0xDE,   A_ABS_IND_X                     },
    // DEX
    { m7700_dex,       0xCA,   A_IMPL                          },
    // DEY
    { m7700_dey,       0x88,   A_IMPL                          },
    // DIV
    { m7700_div,       0x8929, A_IMM                           },
    { m7700_div,       0x8925, A_DIR                           },
    { m7700_div,       0x8935, A_DIR_IND_X                     },
    { m7700_div,       0x8932, A_DIR_INDI                      },
    { m7700_div,       0x8921, A_DIR_IND_X_INDI                },
    { m7700_div,       0x8931, A_DIR_INDI_IND_Y                },
    { m7700_div,       0x8927, A_DIR_INDI_LONG                 },
    { m7700_div,       0x8937, A_DIR_INDI_LONG_IND_Y           },
    { m7700_div,       0x892D, A_ABS                           },
    { m7700_div,       0x893D, A_ABS_IND_X                     },
    { m7700_div,       0x8939, A_ABS_IND_Y                     },
    { m7700_div,       0x892F, A_ABS_LONG                      },
    { m7700_div,       0x893F, A_ABS_LONG_IND_X                },
    { m7700_div,       0x8923, A_STACK_PTR_REL                 },
    { m7700_div,       0x8933, A_STACK_PTR_REL_IIY             },
    // EOR
    { m7700_eor,       0x49,   A_IMM                           },
    { m7700_eor,       0x45,   A_DIR                           },
    { m7700_eor,       0x55,   A_DIR_IND_X                     },
    { m7700_eor,       0x52,   A_DIR_INDI                      },
    { m7700_eor,       0x41,   A_DIR_IND_X_INDI                },
    { m7700_eor,       0x51,   A_DIR_INDI_IND_Y                },
    { m7700_eor,       0x47,   A_DIR_INDI_LONG                 },
    { m7700_eor,       0x57,   A_DIR_INDI_LONG_IND_Y           },
    { m7700_eor,       0x4D,   A_ABS                           },
    { m7700_eor,       0x5D,   A_ABS_IND_X                     },
    { m7700_eor,       0x59,   A_ABS_IND_Y                     },
    { m7700_eor,       0x4F,   A_ABS_LONG                      },
    { m7700_eor,       0x5F,   A_ABS_LONG_IND_X                },
    { m7700_eor,       0x43,   A_STACK_PTR_REL                 },
    { m7700_eor,       0x53,   A_STACK_PTR_REL_IIY             },
    // INC
    { m7700_inc,       0x3A,   A_ACC_A                         },
    { m7700_inc,       0x423A, A_ACC_B                         },
    { m7700_inc,       0xE6,   A_DIR                           },
    { m7700_inc,       0xF6,   A_DIR_IND_X                     },
    { m7700_inc,       0xEE,   A_ABS                           },
    { m7700_inc,       0xFE,   A_ABS_IND_X                     },
    // INX
    { m7700_inx,       0xE8,   A_IMPL                          },
    // INY
    { m7700_iny,       0xC8,   A_IMPL                          },
    // JMP
    { m7700_jmp,       0x4C,   A_ABS                           },
    { m7700_jmp,       0x5C,   A_ABS_LONG                      },
    { m7700_jmp,       0x6C,   A_ABS_INDI                      },
    { m7700_jmp,       0xDC,   A_ABS_INDI_LONG                 },
    { m7700_jmp,       0x7C,   A_ABS_IND_X_INDI                },
    // JSR
    { m7700_jsr,       0x20,   A_ABS                           },
    { m7700_jsr,       0x22,   A_ABS_LONG                      },
    { m7700_jsr,       0xFC,   A_ABS_IND_X_INDI                },
    // LDA
    { m7700_lda,       0xA9,   A_IMM                           },
    { m7700_lda,       0xA5,   A_DIR                           },
    { m7700_lda,       0xB5,   A_DIR_IND_X                     },
    { m7700_lda,       0xB2,   A_DIR_INDI                      },
    { m7700_lda,       0xA1,   A_DIR_IND_X_INDI                },
    { m7700_lda,       0xB1,   A_DIR_INDI_IND_Y                },
    { m7700_lda,       0xA7,   A_DIR_INDI_LONG                 },
    { m7700_lda,       0xB7,   A_DIR_INDI_LONG_IND_Y           },
    { m7700_lda,       0xAD,   A_ABS                           },
    { m7700_lda,       0xBD,   A_ABS_IND_X                     },
    { m7700_lda,       0xB9,   A_ABS_IND_Y                     },
    { m7700_lda,       0xAF,   A_ABS_LONG                      },
    { m7700_lda,       0xBF,   A_ABS_LONG_IND_X                },
    { m7700_lda,       0xA3,   A_STACK_PTR_REL                 },
    { m7700_lda,       0xB3,   A_STACK_PTR_REL_IIY             },
    // LDM
    { m7700_ldm,       0x64,   A_DIR                           },
    { m7700_ldm,       0x74,   A_DIR_IND_X                     },
    { m7700_ldm,       0x9C,   A_ABS                           },
    { m7700_ldm,       0x9E,   A_ABS_IND_X                     },
    // LDT
    { m7700_ldt,       0x89C2, A_IMM                           },
    // LDX
    { m7700_ldx,       0xA2  , A_IMM                           },
    { m7700_ldx,       0xA6,   A_DIR                           },
    { m7700_ldx,       0xB6,   A_DIR_IND_Y                     },
    { m7700_ldx,       0xAE,   A_ABS                           },
    { m7700_ldx,       0xBE,   A_ABS_IND_Y                     },
    // LDY
    { m7700_ldy,       0xA0  , A_IMM                           },
    { m7700_ldy,       0xA4,   A_DIR                           },
    { m7700_ldy,       0xB4,   A_DIR_IND_X                     },
    { m7700_ldy,       0xAC,   A_ABS                           },
    { m7700_ldy,       0xBC,   A_ABS_IND_X                     },
    // LSR
    { m7700_lsr,       0x4A,   A_ACC_A                         },
    { m7700_lsr,       0x424A, A_ACC_B                         },
    { m7700_lsr,       0x46,   A_DIR                           },
    { m7700_lsr,       0x56,   A_DIR_IND_X                     },
    { m7700_lsr,       0x4E,   A_ABS                           },
    { m7700_lsr,       0x5E,   A_ABS_IND_X                     },
    // MPY
    { m7700_mpy,       0x8909, A_IMM                           },
    { m7700_mpy,       0x8905, A_DIR                           },
    { m7700_mpy,       0x8915, A_DIR_IND_X                     },
    { m7700_mpy,       0x8912, A_DIR_INDI                      },
    { m7700_mpy,       0x8901, A_DIR_IND_X_INDI                },
    { m7700_mpy,       0x8911, A_DIR_INDI_IND_Y                },
    { m7700_mpy,       0x8907, A_DIR_INDI_LONG                 },
    { m7700_mpy,       0x8917, A_DIR_INDI_LONG_IND_Y           },
    { m7700_mpy,       0x890D, A_ABS                           },
    { m7700_mpy,       0x891D, A_ABS_IND_X                     },
    { m7700_mpy,       0x8919, A_ABS_IND_Y                     },
    { m7700_mpy,       0x890F, A_ABS_LONG                      },
    { m7700_mpy,       0x891F, A_ABS_LONG_IND_X                },
    { m7700_mpy,       0x8903, A_STACK_PTR_REL                 },
    { m7700_mpy,       0x8913, A_STACK_PTR_REL_IIY             },
    // MVN
    { m7700_mvn,       0x54,   A_BT                            },
    // MVP
    { m7700_mvp,       0x44,   A_BT                            },
    // NOP
    { m7700_nop,       0xEA,   A_IMPL                          },
    // ORA
    { m7700_ora,       0x09,   A_IMM                           },
    { m7700_ora,       0x05,   A_DIR                           },
    { m7700_ora,       0x15,   A_DIR_IND_X                     },
    { m7700_ora,       0x12,   A_DIR_INDI                      },
    { m7700_ora,       0x01,   A_DIR_IND_X_INDI                },
    { m7700_ora,       0x11,   A_DIR_INDI_IND_Y                },
    { m7700_ora,       0x07,   A_DIR_INDI_LONG                 },
    { m7700_ora,       0x17,   A_DIR_INDI_LONG_IND_Y           },
    { m7700_ora,       0x0D,   A_ABS                           },
    { m7700_ora,       0x1D,   A_ABS_IND_X                     },
    { m7700_ora,       0x19,   A_ABS_IND_Y                     },
    { m7700_ora,       0x0F,   A_ABS_LONG                      },
    { m7700_ora,       0x1F,   A_ABS_LONG_IND_X                },
    { m7700_ora,       0x03,   A_STACK_PTR_REL                 },
    { m7700_ora,       0x13,   A_STACK_PTR_REL_IIY             },
    // PEA
    { m7700_pea,       0xF4,   A_STACK_L                       },
    // PEI
    { m7700_pei,       0xD4,   A_STACK_S                       },
    // PER
    { m7700_per,       0x62,   A_STACK_L                       },
    // PHA
    { m7700_pha,       0x48,   A_STACK                         },
    // PHB
    { m7700_phb,       0x4248, A_STACK                         },
    // PHD
    { m7700_phd,       0x0B,   A_STACK                         },
    // PHG
    { m7700_phg,       0x4B,   A_STACK                         },
    // PHP
    { m7700_php,       0x08,   A_STACK                         },
    // PHT
    { m7700_pht,       0x8B,   A_STACK                         },
    // PHX
    { m7700_phx,       0xDA,   A_STACK                         },
    // PHY
    { m7700_phy,       0x5A,   A_STACK                         },
    // PLA
    { m7700_pla,       0x68,   A_STACK                         },
    // PLB
    { m7700_plb,       0x4268, A_STACK                         },
    // PLD
    { m7700_pld,       0x2B,   A_STACK                         },
    // PLP
    { m7700_plp,       0x28,   A_STACK                         },
    // PLT
    { m7700_plt,       0xAB,   A_STACK                         },
    // PLX
    { m7700_plx,       0xFA,   A_STACK                         },
    // PLY
    { m7700_ply,       0x7A,   A_STACK                         },
    // PSH
    { m7700_psh,       0xEB,   A_STACK_S                       },
    // PUL
    { m7700_pul,       0xFB,   A_STACK_S                       },
    // RLA
    { m7700_rla,       0x8949, A_IMM                           },
    // ROL
    { m7700_rol,       0x2A,   A_ACC_A                         },
    { m7700_rol,       0x422A, A_ACC_B                         },
    { m7700_rol,       0x26,   A_DIR                           },
    { m7700_rol,       0x36,   A_DIR_IND_X                     },
    { m7700_rol,       0x2E,   A_ABS                           },
    { m7700_rol,       0x3E,   A_ABS_IND_X                     },
    // ROR
    { m7700_ror,       0x6A,   A_ACC_A                         },
    { m7700_ror,       0x426A, A_ACC_B                         },
    { m7700_ror,       0x66,   A_DIR                           },
    { m7700_ror,       0x76,   A_DIR_IND_X                     },
    { m7700_ror,       0x6E,   A_ABS                           },
    { m7700_ror,       0x7E,   A_ABS_IND_X                     },
    // RTI
    { m7700_rti,       0x40,   A_IMPL                          },
    // RTL
    { m7700_rtl,       0x6B,   A_IMPL                          },
    // RTS
    { m7700_rts,       0x60,   A_IMPL                          },
    // SBC
    { m7700_sbc,       0xE9,   A_IMM                           },
    { m7700_sbc,       0xE5,   A_DIR                           },
    { m7700_sbc,       0xF5,   A_DIR_IND_X                     },
    { m7700_sbc,       0xF2,   A_DIR_INDI                      },
    { m7700_sbc,       0xE1,   A_DIR_IND_X_INDI                },
    { m7700_sbc,       0xF1,   A_DIR_INDI_IND_Y                },
    { m7700_sbc,       0xE7,   A_DIR_INDI_LONG                 },
    { m7700_sbc,       0xF7,   A_DIR_INDI_LONG_IND_Y           },
    { m7700_sbc,       0xED,   A_ABS                           },
    { m7700_sbc,       0xFD,   A_ABS_IND_X                     },
    { m7700_sbc,       0xF9,   A_ABS_IND_Y                     },
    { m7700_sbc,       0xEF,   A_ABS_LONG                      },
    { m7700_sbc,       0xFF,   A_ABS_LONG_IND_X                },
    { m7700_sbc,       0xE3,   A_STACK_PTR_REL                 },
    { m7700_sbc,       0xF3,   A_STACK_PTR_REL_IIY             },
    // SEB
    { m7700_seb,       0x04,   A_DIR_BIT                       },
    { m7700_seb,       0x0C,   A_ABS_BIT                       },
    // SEC
    { m7700_sec,       0x38,   A_IMPL                          },
    // SEI
    { m7700_sei,       0x78,   A_IMPL                          },
    // SEM
    { m7700_sem,       0xF8,   A_IMPL                          },
    // SEP
    { m7700_sep,       0xE2,   A_IMM                           },
    // STA
    { m7700_sta,       0x85,   A_DIR                           },
    { m7700_sta,       0x95,   A_DIR_IND_X                     },
    { m7700_sta,       0x92,   A_DIR_INDI                      },
    { m7700_sta,       0x81,   A_DIR_IND_X_INDI                },
    { m7700_sta,       0x91,   A_DIR_INDI_IND_Y                },
    { m7700_sta,       0x87,   A_DIR_INDI_LONG                 },
    { m7700_sta,       0x97,   A_DIR_INDI_LONG_IND_Y           },
    { m7700_sta,       0x8D,   A_ABS                           },
    { m7700_sta,       0x9D,   A_ABS_IND_X                     },
    { m7700_sta,       0x99,   A_ABS_IND_Y                     },
    { m7700_sta,       0x8F,   A_ABS_LONG                      },
    { m7700_sta,       0x9F,   A_ABS_LONG_IND_X                },
    { m7700_sta,       0x83,   A_STACK_PTR_REL                 },
    { m7700_sta,       0x93,   A_STACK_PTR_REL_IIY             },
    // STP
    { m7700_stp,       0xDB,   A_IMPL                          },
    // STX
    { m7700_stx,       0x86,   A_DIR                           },
    { m7700_stx,       0x96,   A_DIR_IND_Y                     },
    { m7700_stx,       0x8E,   A_ABS                           },
    // STY
    { m7700_sty,       0x84,   A_DIR                           },
    { m7700_sty,       0x94,   A_DIR_IND_X                     },
    { m7700_sty,       0x8C,   A_ABS                           },
    // TAD
    { m7700_tad,       0x5B,   A_IMPL                          },
    // TAS
    { m7700_tas,       0x1B,   A_IMPL                          },
    // TAX
    { m7700_tax,       0xAA,   A_IMPL                          },
    // TAY
    { m7700_tay,       0xA8,   A_IMPL                          },
    // TBD
    { m7700_tbd,       0x425B, A_IMPL                          },
    // TBS
    { m7700_tbs,       0x421B, A_IMPL                          },
    // TBX
    { m7700_tbx,       0x42AA, A_IMPL                          },
    // TBY
    { m7700_tby,       0x42A8, A_IMPL                          },
    // TDA
    { m7700_tda,       0x7B,   A_IMPL                          },
    // TDB
    { m7700_tdb,       0x427B, A_IMPL                          },
    // TSA
    { m7700_tsa,       0x3B,   A_IMPL                          },
    // TSB
    { m7700_tsb,       0x423B, A_IMPL                          },
    // TSX
    { m7700_tsx,       0xBA,   A_IMPL                          },
    // TXA
    { m7700_txa,       0x8A,   A_IMPL                          },
    // TXB
    { m7700_txb,       0x428A, A_IMPL                          },
    // TXS
    { m7700_txs,       0x9A,   A_IMPL                          },
    // TXY
    { m7700_txy,       0x9B,   A_IMPL                          },
    // TYA
    { m7700_tya,       0x98,   A_IMPL                          },
    // TYB
    { m7700_tyb,       0x4298, A_IMPL                          },
    // TYX
    { m7700_tyx,       0xBB,   A_IMPL                          },
    // WIT
    { m7700_wit,       0xCB,   A_IMPL                          },
    // XAB
    { m7700_xab,       0x8928, A_IMPL                          }
};

// 7750 instructions
static const struct opcode opcodes_7750[] = {
    // ASR
    { m7750_asr,       0x8908, A_ACC_A                         },
    { m7750_asr,       0x4208, A_ACC_B                         },
    { m7750_asr,       0x8906, A_DIR                           },
    { m7750_asr,       0x8916, A_DIR_IND_X                     },
    { m7750_asr,       0x890E, A_ABS                           },
    { m7750_asr,       0x891E, A_ABS_IND_X                     },
    // DIVS
    { m7750_divs,      0x89A9, A_IMM                           },
    { m7750_divs,      0x89A5, A_DIR                           },
    { m7750_divs,      0x89B5, A_DIR_IND_X                     },
    { m7750_divs,      0x89B2, A_DIR_INDI                      },
    { m7750_divs,      0x89A1, A_DIR_IND_X_INDI                },
    { m7750_divs,      0x89B1, A_DIR_INDI_IND_Y                },
    { m7750_divs,      0x89A7, A_DIR_INDI_LONG                 },
    { m7750_divs,      0x89B7, A_DIR_INDI_LONG_IND_Y           },
    { m7750_divs,      0x89AD, A_ABS                           },
    { m7750_divs,      0x89BD, A_ABS_IND_X                     },
    { m7750_divs,      0x89B9, A_ABS_IND_Y                     },
    { m7750_divs,      0x89AF, A_ABS_LONG                      },
    { m7750_divs,      0x89BF, A_ABS_LONG_IND_X                },
    { m7750_divs,      0x89A3, A_STACK_PTR_REL                 },
    { m7750_divs,      0x89B3, A_STACK_PTR_REL_IIY             },
    // EXTS
    { m7750_exts,      0x898B, A_ACC_A                         },
    { m7750_exts,      0x428B, A_ACC_B                         },
    // EXTZ
    { m7750_extz,      0x89AB, A_ACC_A                         },
    { m7750_extz,      0x42AB, A_ACC_B                         },
    // MPYS
    { m7750_mpys,      0x8989, A_IMM                           },
    { m7750_mpys,      0x8985, A_DIR                           },
    { m7750_mpys,      0x8995, A_DIR_IND_X                     },
    { m7750_mpys,      0x8992, A_DIR_INDI                      },
    { m7750_mpys,      0x8981, A_DIR_IND_X_INDI                },
    { m7750_mpys,      0x8991, A_DIR_INDI_IND_Y                },
    { m7750_mpys,      0x8987, A_DIR_INDI_LONG                 },
    { m7750_mpys,      0x8997, A_DIR_INDI_LONG_IND_Y           },
    { m7750_mpys,      0x898D, A_ABS                           },
    { m7750_mpys,      0x899D, A_ABS_IND_X                     },
    { m7750_mpys,      0x8999, A_ABS_IND_Y                     },
    { m7750_mpys,      0x898F, A_ABS_LONG                      },
    { m7750_mpys,      0x899F, A_ABS_LONG_IND_X                },
    { m7750_mpys,      0x8983, A_STACK_PTR_REL                 },
    { m7750_mpys,      0x8993, A_STACK_PTR_REL_IIY             }
};

struct opcode_flag {
    int insn;
    int flags;
#define MEM_R    OP_ADDR_R    // read access
#define MEM_W    OP_ADDR_W    // write access
};

static const struct opcode_flag opcodes_flags[] = {
    { m7700_adc,     MEM_R    },
    { m7700_and,     MEM_R    },
    { m7700_asl,     MEM_W    },
    { m7750_asr,     MEM_W    },
    { m7700_bbc,     MEM_R    },
    { m7700_bbs,     MEM_R    },
    { m7700_clb,     MEM_W    },
    { m7700_cmp,     MEM_R    },
    { m7700_cpx,     MEM_R    },
    { m7700_cpy,     MEM_R    },
    { m7700_dec,     MEM_W    },
    { m7700_div,     MEM_R    },
    { m7750_divs,    MEM_R    },
    { m7700_eor,     MEM_R    },
    { m7700_inc,     MEM_W    },
    { m7700_jmp,     MEM_R    },
    { m7700_jsr,     MEM_R    },
    { m7700_lda,     MEM_R    },
    { m7700_ldm,     MEM_W    },
    { m7700_ldx,     MEM_R    },
    { m7700_ldy,     MEM_R    },
    { m7700_lsr,     MEM_W    },
    { m7700_mpy,     MEM_R    },
    { m7750_mpys,    MEM_R    },
    { m7700_ora,     MEM_R    },
    { m7700_rol,     MEM_W    },
    { m7700_ror,     MEM_W    },
    { m7700_sbc,     MEM_R    },
    { m7700_seb,     MEM_W    },
    { m7700_sta,     MEM_W    },
    { m7700_stx,     MEM_W    },
    { m7700_sty,     MEM_W    }
};

static bool with_acc_b = false;

static bool with_acc(void) {
    switch(cmd.itype) {
        case m7700_adc:
        case m7700_and:
        case m7700_cmp:
        case m7700_eor:
        case m7700_lda:
        case m7700_sta:
        case m7700_ora:
        case m7700_sbc:
            return true;
    }
    return false;
}

static bool imm_read_another_byte(void) {
    bool m16 = getSR(cmd.ea, rfM) == 0;
    bool x16 = getSR(cmd.ea, rfX) == 0;

    // if insn are using X flag and this flag is set to 0
    switch (cmd.itype) {
        case m7700_cpx:
        case m7700_cpy:
        case m7700_ldx:
        case m7700_ldy:
            if (x16)
                return true;
    }

    // if insn is not using M flag
    switch (cmd.itype) {
        case m7700_clp:
        case m7700_ldt:
        case m7700_pea:
        case m7700_pei:
        case m7700_per:
        case m7700_psh:
        case m7700_pul:
        case m7700_sep:
        case m7700_mvn:
        case m7700_mvp:
            return false;
    }
    // return true if M flag is set to 0
    return m16 ? true : false;
}

inline static void set_flag(op_t &x, int flag) {
    x.specflag1 |= flag;
}

inline static void set_flag(int flag) {
    cmd.auxpref |= flag;
}

inline static void set_op_reg(op_t &op, int reg) {
    op.type = o_reg;
    op.reg = reg;
    op.dtyp = dt_word; // XXX not sure
}

// shortcuts
#define set_op_acc_a(op)     set_op_reg(op, rA)
#define set_op_acc_b(op)     set_op_reg(op, rB)
#define set_op_ind_x(op)     set_op_reg(op, rX)
#define set_op_ind_y(op)     set_op_reg(op, rY)

#define my_next_byte()       ua_next_byte()
#define my_next_word()       ua_next_byte() | (ua_next_byte() << 8)
#define my_next_3bytes()     ua_next_byte() | (ua_next_byte() << 8) | (ua_next_byte() << 16)

inline static int d_typ2addr (char d_typ) {
    switch (d_typ) {
        case dt_byte:   return my_next_byte();
        case dt_word:   return my_next_word();

        // special case: dt_dword is 3 bytes long here
        case dt_dword:  return my_next_3bytes();
    }
    IDA_ERROR("Invalid d_typ in set_op_mem()");
    return -1; /* never reached */
}

inline static void set_op_imm(op_t &op, char d_typ) {
    op.type = o_imm;
    op.value = d_typ2addr(d_typ);
    op.dtyp = d_typ;
    // when operating on 16-bit data in immediate addressing
    // mode with data lenght selection flag set to 0, the
    // bytes-count increases by 1
    if (imm_read_another_byte()) {
        op.value |= my_next_byte() << 8;
        op.dtyp = dt_word;
    }
}

inline static void set_op_bit(op_t &op) {
    set_op_imm(op, dt_byte);
    op.type = o_bit;
}

inline static void set_op_addr(op_t &op, char d_typ) {
    op.type = o_near;
    op.dtyp = with_acc() ? dt_word : d_typ;
    op.addr = d_typ2addr(d_typ);
}

inline static void set_op_mem(op_t &op, char d_typ, int flags) {
    op.type = o_mem;
    op.dtyp = with_acc() ? dt_word : d_typ;
    op.addr = d_typ2addr(d_typ);
    set_flag(op, flags);
}

inline static void set_op_displ(op_t &op, char d_typ, int reg) {
    op.type = o_displ;
    op.dtyp = with_acc() ? dt_word : d_typ;
    op.addr = d_typ2addr(d_typ);
    op.reg = reg;
}

#define x       cmd.Operands[n]
#define next_x  cmd.Operands[n + 1]

// get an opcode flags struct from insn
static const int get_opcode_flags(const int insn) {
    for (int i = 0; i < qnumber(opcodes_flags); i++) {
        if (opcodes_flags[i].insn != insn)
            continue;

        return opcodes_flags[i].flags;
    }
    return 0;
}

static inline bool is_jmp(void) { return cmd.itype == m7700_jmp || cmd.itype == m7700_jsr; }

static void set_op_mem_dr_rel(op_t &op) {
    sel_t s = getSR(cmd.ea, rDR);
    // if the value of the DR register is known,
    // we can compute the absolute address
    if (s != BADSEL) {
        op.addr += s;
        op.dtyp = dt_word; // address is 32-bit long now
    }
    set_flag(op, OP_ADDR_DR_REL);
}

// fill cmd struct according to the specified addressing mode
static void fill_cmd(m7700_addr_mode_t mode) {
    int n = 0;      // current operand
    const int curflags = get_opcode_flags(cmd.itype); // current flags

    // if the first operand should be an accumulator (either A or B),
    // just fill accordingly the cmd structure
    if (with_acc()) {
        if (with_acc_b)
            set_op_acc_b(x);
        else
            set_op_acc_a(x);
        n++;
    }

    // the LDM instruction operands are always preceded by an immediate value,
    // but this immediate value is always at the end of the operation code.
    if (cmd.itype == m7700_ldm) { n++; }

    switch(mode) {
        case A_IMPL:                 // implied
            // nothing to do !
            break;

        case A_IMM:                  // immediate
            set_op_imm(x, dt_byte);
            break;

        case A_ACC_A:                // accumulator A
            set_op_acc_a(x);
            break;

        case A_ACC_B:                // accumulator B
            set_op_acc_b(x);
            break;

        case A_DIR:                  // direct
            set_op_mem(x, dt_byte, curflags);
            set_op_mem_dr_rel(x);
            break;

        case A_DIR_BIT:              // direct bit
            set_op_mem(next_x, dt_byte, curflags);
            set_op_mem_dr_rel(next_x);
            set_op_bit(x);
            break;

        case A_DIR_IND_X:            // direct indexed X
            set_op_displ(x, dt_byte, rX);
            set_op_mem_dr_rel(x);
            break;

        case A_DIR_IND_Y:            // direct indexed Y
            set_op_displ(x, dt_byte, rY);
            set_op_mem_dr_rel(x);
            break;

        case A_DIR_INDI:             // direct indirect
            set_op_mem(x, dt_byte, curflags);
            set_flag(x, OP_ADDR_IND);
            set_op_mem_dr_rel(x);
            break;

        case A_DIR_IND_X_INDI:       // direct indexed X indirect
            set_op_displ(x, dt_byte, rX);
            set_flag(x, OP_DISPL_IND);
            set_op_mem_dr_rel(x);
            break;

        case A_DIR_INDI_IND_Y:       // direct indirect indexed Y
            set_op_displ(x, dt_byte, rY);
            set_flag(x, OP_DISPL_IND_P1);
            set_op_mem_dr_rel(x);
            break;

        case A_DIR_INDI_LONG:         // direct indirect long
            set_op_mem(x, dt_byte, curflags);
            set_flag(x, OP_ADDR_IND);
            set_op_mem_dr_rel(x);
            set_flag(INSN_LONG_FORMAT);
            break;

        case A_DIR_INDI_LONG_IND_Y:   // direct indirect long indexed Y
            set_op_displ(x, dt_byte, rY);
            set_flag(x, OP_DISPL_IND_P1);
            set_op_mem_dr_rel(x);
            set_flag(INSN_LONG_FORMAT);
            break;

        case A_ABS:                  // absolute
            if (is_jmp())
                set_op_addr(x, dt_word);
            else
                set_op_mem(x, dt_word, curflags);
            break;

        case A_ABS_BIT:              // absolute bit
            set_op_mem(next_x, dt_word, curflags);
            set_op_bit(x);
            break;

        case A_ABS_IND_X:            // absolute indexed X
            set_op_displ(x, dt_word, rX);
            break;

        case A_ABS_IND_Y:            // absolute indexed Y
            set_op_displ(x, dt_word, rY);
            break;

        case A_ABS_LONG:             // absolute long
            if (is_jmp()) {
                set_op_addr(x, dt_dword);
                set_flag(INSN_LONG_FORMAT);
            }
            else
                set_op_mem(x, dt_dword, curflags);
            break;

        case A_ABS_LONG_IND_X:       // absolute long indexed X
            set_op_displ(x, dt_dword, rX);
            break;

        case A_ABS_INDI:             // absolute indirect
            set_op_mem(x, dt_word, curflags);
            set_flag(x, OP_ADDR_IND);
            break;

        case A_ABS_INDI_LONG:        // absolute indirect long
            set_op_mem(x, dt_word, curflags);
            set_flag(x, OP_ADDR_IND);
            if (is_jmp())
                set_flag(INSN_LONG_FORMAT);
            break;

        case A_ABS_IND_X_INDI:       // absolute indexed X indirect
            set_op_displ(x, dt_word, rX);
            set_flag(x, OP_DISPL_IND);
            break;

        case A_STACK:                // stack
            // nothing to do !
            break;

        case A_STACK_S:              // stack short
            set_op_imm(x, dt_byte);
            break;

        case A_STACK_L:              // stack long
            set_op_imm(x, dt_word);
            break;

        case A_REL:                  // relative
            set_op_addr(x, dt_byte);
            x.addr = (signed char) x.addr + cmd.ip + cmd.size;
            break;

        case A_REL_LONG:             // relative long
            set_op_addr(x, dt_word);
            x.addr = (signed short) x.addr + cmd.ip + cmd.size;
            set_flag(INSN_LONG_FORMAT);
            break;

        case A_DIR_BIT_REL:          // direct bit relative
            set_op_mem(next_x, dt_byte, curflags);
            set_op_mem_dr_rel(x);
            set_op_bit(x);
            n += 2;
            set_op_addr(x, dt_byte);
            x.addr = (signed char) x.addr + cmd.ip + cmd.size;
            break;

        case A_ABS_BIT_REL:          // absolute bit relative
            set_op_mem(next_x, dt_word, curflags);
            set_op_bit(x);
            n += 2;
            set_op_addr(x, dt_byte);
            x.addr = (signed char) x.addr + cmd.ip + cmd.size;
            break;

        case A_STACK_PTR_REL:        // stack pointer relative
            set_op_displ(x, dt_byte, rS);
            break;

        case A_STACK_PTR_REL_IIY:    // stack pointer relative indirect indexed Y
            set_op_displ(x, dt_byte, rS);
            set_flag(x, OP_DISPL_IND);
            set_op_ind_y(next_x);
            break;

        case A_BT:                   // block transfer
            set_op_imm(x, dt_byte);
            set_flag(x, OP_IMM_WITHOUT_SHARP);
            n += 2;
            set_op_imm(x, dt_byte);
            set_flag(x, OP_IMM_WITHOUT_SHARP);
            break;

        default:
            IDA_ERROR("invalid addressing mode!");
    }

    // now we can read immediate from operation code, and fill the first operand
    // as it should be
    if (cmd.itype == m7700_ldm) {
        n = 0;
        set_op_imm(x, dt_byte);
        /*
        if (x.dtyp == dt_word)
           set_flag(INSN_LONG_FORMAT);
        */
    }
}

// get an opcode struct from code
static const struct opcode * get_opcode(int code) {
    // check in the 7700 opcodes
    for (int i = 0; i < qnumber(opcodes_7700); i++) {
        if (opcodes_7700[i].code != code)
            continue;

        return &opcodes_7700[i];
    }
    // check in the 7750 opcodes
    if (ptype == prc_m7750) {
        for (int i = 0; i < qnumber(opcodes_7750); i++) {
            if (opcodes_7750[i].code != code)
                continue;

            return &opcodes_7750[i];
        }
    }
    // check in the 7700 opcodes with a FF00 mask if equals to 4200
    if (((code & 0xFF00) >> 8) == 0x42) {
        code &= 0x00FF;
        with_acc_b = true;
        return get_opcode(code & 0x00FF);
    }
    return NULL;
}

// analyze an instruction
int ana(void) {
    const struct opcode *op;
    int code;

    with_acc_b = false;

    // read a byte
    code = ua_next_byte();

    // detect BRK insn
    if (code == 0x00 && get_byte(cmd.ea + 1) == 0xEA) {
        cmd.itype = m7700_brk;
        cmd.size += 1;
        goto ana_finished;
    }

    // some instructions have their opcodes represented in 2 bytes,
    // so we need to pick an another byte
    if (code == 0x42 || code == 0x89) {
        code = (code << 8) + ua_next_byte();
    }

    // analyze and return corresponding opcode struct
    op = get_opcode(code);
    if (op == NULL)     // no instruction was found..
        return 0;

    // fill the cmd struct
    cmd.itype = op->insn;
    fill_cmd(op->mode);

ana_finished:

    return cmd.size;
}

void interr(const char *file, const int line, const char *format, ...) {
    const char *name = NULL;
    char b[200];
    va_list va;

    IDA_ASSERT(file != NULL && format != NULL, "Invalid args in interr()");

    va_start(va, format);

    if (cmd.itype < m7700_last) {
        name = Instructions[cmd.itype].name;
    }
    else {
        cmd.itype = m7700_null;
    }

    qvsnprintf(b, sizeof b, format, va);

    warning("%s:%d : %a(%s) : internal error => %s",
        file, line, cmd.ea, name, b);

    va_end(va);
}

