/*
 *      Interactive disassembler (IDA).
 *      Copyright (c) 1990-96 by Ilfak Guilfanov.
 *      ALL RIGHTS RESERVED.
 *                              FIDO:   2:5020/209
 *                              E-mail: ig@estar.msk.su
 *
 */

#include "i5.hpp"

//----------------------------------------------------------------------
inline void GetImm(insn_t &insn, op_t &x)
{
  x.type = o_imm;
  x.value = insn.get_next_byte();
}

//------------------------------------------------------------------------
inline void op_c(op_t &x)
{
  x.type = o_phrase;
  x.phrase = R_c;
}

//------------------------------------------------------------------------
static void op_ad(insn_t &insn, op_t &x)
{
  x.type = o_near;
  x.addr = insn.get_next_word();
}

static void op_a(op_t &x);
static void op_e(insn_t &insn, op_t &x);
static void op_nn(insn_t &insn, op_t &x);
static void op_ad(insn_t &insn, op_t &x);
static void op_n(insn_t &insn, op_t &x);
static void op_mm(insn_t &insn, op_t &x);

static const uint16 W    [] = { I5_add,I5_adc,I5_sub,I5_sbb,I5_ana,I5_xra,I5_ora,I5_cmp };
static const uint16 Wi   [] = { I5_adi,I5_aci,I5_sui,I5_sbi,I5_ani,I5_xri,I5_ori,I5_cpi };
static const uint16 calls[] = { I5_cnz,I5_cz, I5_cnc,I5_cc, I5_cpo,I5_cpe,I5_cp, I5_cm  };
static const uint16 jumps[] = { I5_jnz,I5_jz, I5_jnc,I5_jc, I5_jpo,I5_jpe,I5_jp, I5_jm  };
static const uint16 rets [] = { I5_rnz,I5_rz, I5_rnc,I5_rc, I5_rpo,I5_rpe,I5_rp, I5_rm  };
static const uint16 rols [] = { I5_rlc,I5_rrc,I5_ral,I5_rar,I5_daa,I5_cma,I5_stc,I5_cmc };
static const uint16 CBrols[]= { Z80_rlc,Z80_rrc,Z80_rl,Z80_rr,Z80_sla,Z80_sra,Z80_srr,Z80_srl };
static const uint16 Zrols[] = { Z80_rlca,Z80_rrca,Z80_rla,Z80_rra,I5_daa,Z80_cpl,Z80_scf,Z80_ccf };

//----------------------------------------------------------------------
static void ConvertToFunny(insn_t &insn)
{
  switch ( insn.itype )
  {
    case I5_mov:
      insn.Op1.set_shown();
      insn.Op2.set_shown();
      if ( insn.Op1.type == o_reg && insn.Op2.type == o_mem )
      {
        switch ( insn.Op1.reg )
        {
          case R_bc:
            insn.itype = A80_lbcd;
            insn.Op1.clr_shown();
            break;
          case R_de:
            insn.itype = A80_lded;
            insn.Op1.clr_shown();
            break;
          case R_sp:
            insn.itype = A80_lspd;
            insn.Op1.clr_shown();
            break;
          case R_ix:
            insn.itype = A80_lixd;
            insn.Op1.clr_shown();
            break;
          case R_iy:
            insn.itype = A80_liyd;
            insn.Op1.clr_shown();
            break;
        }
      }
      if ( insn.Op1.type == o_mem && insn.Op2.type == o_reg )
      {
        switch ( insn.Op2.reg )
        {
          case R_bc:
            insn.itype = A80_sbcd;
            insn.Op2.clr_shown();
            break;
          case R_de:
            insn.itype = A80_sded;
            insn.Op2.clr_shown();
            break;
          case R_sp:
            insn.itype = A80_sspd;
            insn.Op2.clr_shown();
            break;
          case R_ix:
            insn.itype = A80_sixd;
            insn.Op2.clr_shown();
            break;
          case R_iy:
            insn.itype = A80_siyd;
            insn.Op2.clr_shown();
            break;
        }
      }
      if ( insn.Op1.type == o_reg && insn.Op2.type == o_reg )
      {
        switch ( insn.Op1.reg )
        {
          case R_sp:
            if ( insn.Op2.reg == R_ix )
            {
              insn.Op1.clr_shown();
              insn.Op2.clr_shown();
              insn.itype = A80_spix;
            }
            if ( insn.Op2.reg == R_iy )
            {
              insn.Op1.clr_shown();
              insn.Op2.clr_shown();
              insn.itype = A80_spiy;
            }
            break;
          case R_r:
            insn.itype = A80_mvra;
            insn.Op1.clr_shown();
            insn.Op2.clr_shown();
            break;
          case R_i:
            insn.itype = A80_mvia;
            insn.Op1.clr_shown();
            insn.Op2.clr_shown();
            break;
          case R_a:
            if ( insn.Op2.reg == R_r )
            {
              insn.itype = A80_mvar;
              insn.Op1.clr_shown();
              insn.Op2.clr_shown();
            }
            if ( insn.Op2.reg == R_i )
            {
              insn.itype = A80_mvai;
              insn.Op1.clr_shown();
              insn.Op2.clr_shown();
            }
            break;
        }
      }
      break;    /* mov */
    case Z80_jp:
      if ( insn.Op2.type == o_phrase && insn.Op2.phrase == R_ix )
      {
        insn.itype = A80_pcix;
        insn.Op2.clr_shown();
        break;
      }
      if ( insn.Op2.type == o_phrase && insn.Op2.phrase == R_iy )
      {
        insn.itype = A80_pciy;
        insn.Op2.clr_shown();
        break;
      }
      break;    /* jp */
    case Z80_ex:
      insn.Op1.clr_shown();
      insn.Op2.clr_shown();
      if ( insn.Op2.reg == R_ix )
        insn.itype = A80_xtix;
      if ( insn.Op2.reg == R_iy )
        insn.itype = A80_xtiy;
      break;    /* ex */
    case I5_in:
      if ( insn.Op2.type == o_phrase && insn.Op2.reg == R_c )
      {
        insn.itype = Z80_inp;
        insn.Op2.clr_shown();
      }
      break;
    case I5_out:
      if ( insn.Op1.type == o_phrase && insn.Op1.reg == R_c )
      {
        insn.itype = Z80_outp;
        insn.Op1.clr_shown();
      }
      break;
    case Z80_cpl:
      insn.itype = I5_cma;
      break;
    case Z80_scf:
      insn.itype = I5_stc;
      break;
    case Z80_ccf:
      insn.itype = I5_cmc;
      break;
    case I5_add:
      if ( insn.Op1.type == o_reg && insn.Op1.reg == R_ix )
      {
        insn.itype = A80_addix;
        insn.Op1.clr_shown();
        if ( insn.Op2.type == o_reg && insn.Op2.reg == R_ix )
          insn.Op2.reg = R_hl;
        break;
      }
      if ( insn.Op1.type == o_reg && insn.Op1.reg == R_iy )
      {
        insn.itype = A80_addiy;
        insn.Op1.clr_shown();
        if ( insn.Op2.type == o_reg && insn.Op2.reg == R_iy )
          insn.Op2.reg = R_hl;
        break;
      }
      break;
    case I5_adc:
      if ( insn.Op1.dtype == dt_word && insn.Op1.type == o_reg )
      {
        insn.itype = A80_addc;
        insn.Op1.clr_shown();
        if ( insn.Op1.reg == R_ix )
          insn.itype = A80_addcix;
        if ( insn.Op1.reg == R_iy )
          insn.itype = A80_addciy;
        if ( insn.Op2.type == o_reg && insn.Op1.reg == insn.Op2.reg )
          insn.Op2.reg = R_hl;
      }
      break;
    case Z80_sbc:
      if ( insn.Op1.dtype == dt_word && insn.Op1.type == o_reg )
      {
        insn.itype = A80_subc;
        insn.Op1.clr_shown();
        if ( insn.Op1.reg == R_ix )
          insn.itype = A80_subcix;
        if ( insn.Op1.reg == R_iy )
          insn.itype = A80_subciy;
        if ( insn.Op2.type == o_reg && insn.Op1.reg == insn.Op2.reg )
          insn.Op2.reg = R_hl;
      }
      break;
    case Z80_jr:
      insn.Op1.clr_shown();
      switch ( insn.Op1.Cond )
      {
        case oc_c:      insn.itype = A80_jrc;    break;
        case oc_nc:     insn.itype = A80_jrnc;   break;
        case oc_z:      insn.itype = A80_jrz;    break;
        case oc_nz:     insn.itype = A80_jrnz;   break;
      }
      break;
    case Z80_rrca:      insn.itype = I5_rrc;     break;
    case Z80_rlca:      insn.itype = I5_rlc;     break;
    case Z80_rla:       insn.itype = I5_ral;     break;
    case Z80_rl:        insn.itype = I5_ral;     break;
    case Z80_rra:       insn.itype = I5_rar;     break;
    case Z80_rr:        insn.itype = I5_rar;     break;
    case Z80_cpi:       insn.itype = A80_cmpi;   break;
    case Z80_cpd:       insn.itype = A80_cmpd;   break;
    case Z80_outi:      insn.itype = A80_oti;    break;
    case Z80_outd:      insn.itype = A80_otd;    break;
    case Z80_inc:       insn.itype = I5_inr;     break;
    case Z80_dec:       insn.itype = I5_dcr;     break;
    case Z80_im:
      if ( insn.Op1.value == 0 )
        insn.itype = A80_im0;
      else if ( insn.Op1.value == 1 )
        insn.itype = A80_im1;
      else
        insn.itype = A80_im2;
      insn.Op1.clr_shown();
      break;
  }
}

//----------------------------------------------------------------------
void z80_t::ConvertToZ80(insn_t &insn)
{
  uint16 cc;
  if ( insn.itype < Z80_and )
  {
    insn.Op1.set_shown();
    insn.Op2.set_shown();
  }
  switch ( insn.itype )
  {
    case I5_aci:
      insn.itype = I5_adc;
      break;
    case I5_adi:
    case I5_dad:
      insn.itype = I5_add;
      break;
    case I5_cmp:
    case I5_cpi:
      insn.itype = Z80_cp;
      if ( !isZ380() )
        insn.Op1.clr_shown();
      break;
    case I5_ana:
    case I5_ani:
      insn.itype = Z80_and;
      if ( !isZ380() )
        insn.Op1.clr_shown();
      break;
    case I5_ora:
    case I5_ori:
      insn.itype = Z80_or;
      if ( !isZ380() )
        insn.Op1.clr_shown();
      break;
    case I5_xra:
    case I5_xri:
      insn.itype = Z80_xor;
      if ( !isZ380() )
        insn.Op1.clr_shown();
      break;
    case I5_sbi:
    case I5_sbb:
      insn.itype = Z80_sbc;
      break;
    case I5_sui:
    case I5_sub:
      insn.itype = I5_sub;
      if ( !isZ380() )
        insn.Op1.clr_shown();
      break;
    case I5_dcr:
    case I5_dcx:
      insn.itype = Z80_dec;
      break;
    case I5_inr:
    case I5_inx:
      insn.itype = Z80_inc;
      break;
    case I5_halt:
      insn.itype = Z80_halt;
      break;
    case I5_sphl:
    case I5_mov:
    case I5_mvi:
    case I5_ldax:
    case I5_lxi:
    case I5_lhld:
    case I5_shld:
    case I5_sta:
    case I5_stax:
    case I5_lda:
      insn.itype = Z80_ld;
      break;
    case I5_xchg:
    case I5_xthl:
      insn.itype = Z80_ex;
      break;
    case I5_pchl:
      insn.Op1.type = o_phrase;
      insn.Op1.reg = R_hl;
      cc = oc_not;
      goto zjump;

    case I5_call:       cc = oc_not;goto zcall;
    case I5_cnz:        cc = oc_nz; goto zcall;
    case I5_cz:         cc = oc_z;  goto zcall;
    case I5_cnc:        cc = oc_nc; goto zcall;
    case I5_cc:         cc = oc_c;  goto zcall;
    case I5_cpo:        cc = oc_po; goto zcall;
    case I5_cpe:        cc = oc_pe; goto zcall;
    case I5_cp:         cc = oc_p;  goto zcall;
    case I5_cm:         cc = oc_m;  goto zcall;
    case I5_jmp:        cc = oc_not;goto zjump;
    case I5_jnz:        cc = oc_nz; goto zjump;
    case I5_jz:         cc = oc_z;  goto zjump;
    case I5_jnc:        cc = oc_nc; goto zjump;
    case I5_jc:         cc = oc_c;  goto zjump;
    case I5_jpo:        cc = oc_po; goto zjump;
    case I5_jpe:        cc = oc_pe; goto zjump;
    case I5_jp:         cc = oc_p;  goto zjump;
    case I5_jm:         cc = oc_m;  goto zjump;
    case I5_ret:        cc = oc_not;goto zret;
    case I5_rnz:        cc = oc_nz; goto zret;
    case I5_rz:         cc = oc_z;  goto zret;
    case I5_rnc:        cc = oc_nc; goto zret;
    case I5_rc:         cc = oc_c;  goto zret;
    case I5_rpo:        cc = oc_po; goto zret;
    case I5_rpe:        cc = oc_pe; goto zret;
    case I5_rp:         cc = oc_p;  goto zret;
    case I5_rm:         cc = oc_m;  goto zret;

zret:
      insn.itype = Z80_ret;
      goto zcc;
zjump:
      insn.itype = Z80_jp;
      goto zcc;
zcall:
      insn.itype = Z80_call;
      goto zcc;
zcc:
      insn.Op2 = insn.Op1;
      insn.Op2.n = 1;
      insn.Op1.type = o_cond;
      insn.Op1.Cond = cc;
      break;

  }
}

//----------------------------------------------------------------------
static bool is_gameboy_insn(const insn_t &insn)
{
  switch ( insn.itype )
  {
    case Z80_adc:
    case Z80_add:
    case Z80_and:
    case Z80_bit:
    case Z80_call:
    case Z80_ccf:
    case Z80_cp:
    case Z80_cpl:
    case I5_daa:
    case Z80_dec:
    case Z80_di:
    case Z80_ei:
    case Z80_halt:
    case Z80_inc:
    case Z80_jp:
    case Z80_jr:
    case Z80_ld:
    case Z80_ldd:
    case GB_ldh:
    case Z80_ldi:
    case I5_nop:
    case Z80_or:
    case Z80_pop:
    case Z80_push:
    case Z80_res:
    case Z80_ret:
    case Z80_reti:
    case Z80_rl:
    case Z80_rla:
    case Z80_rlc:
    case Z80_rlca:
    case Z80_rr:
    case Z80_rra:
    case Z80_rrc:
    case Z80_rrca:
    case I5_rst:
    case Z80_sbc:
    case Z80_scf:
    case Z80_set:
    case Z80_sla:
    case Z80_sra:
    case Z80_srl:
    case GB_stop:
    case Z80_sub:
    case Z80_swap:
    case Z80_xor:
      break;
    default:
      return false;
  }
  return true;
}

//----------------------------------------------------------------------
static void swap_operands(insn_t &insn)
{
  op_t op = insn.Op1;
  insn.Op1 = insn.Op2;
  insn.Op2 = op;
}

//----------------------------------------------------------------------
int z80_t::i5_ana(insn_t *_insn)
{
  insn_t &insn = *_insn;

  insn.Op1.dtype = dt_byte;
  insn.Op2.dtype = dt_byte;
  insn.itype = I5_null;

  code = insn.get_next_byte();

  switch ( code & 0xC0 )
  {
    case 0x00:
      switch ( code & 0xF )
      {
        case 0:
        case 8:
          {
            int sub = ( code >> 3 ) & 7;
            switch ( sub )
            {
              case 0:
                insn.itype = I5_nop;
                break;
              case 1:                   // 08
                if ( isGB() )           // 08 bb aa LD ($aabb),SP
                {
                  insn.itype = Z80_ld;
                  op_mm(insn, insn.Op1);
                  insn.Op1.dtype = dt_word;
                  insn.Op2.type = o_reg;
                  insn.Op2.reg = R_sp;
                }
                else if ( isZ80() )
                {
                  insn.itype = Z80_ex;
                  insn.Op1.type = o_reg;
                  insn.Op1.reg  = R_af;
                  insn.Op2.type = o_reg;
                  insn.Op2.reg  = R_af2;
                }
                else
                {
                  insn.itype = I5_dsub;  // undoc
                }
                break;
              case 2:                   // 10
                if ( isGB() )           // 10 00 STOP
                {
                  if ( insn.get_next_byte() )
                    return 0;
                  insn.itype = GB_stop;
                }
                else if ( isZ80() )
                {
                  insn.itype = Z80_djnz;
                  op_e(insn, insn.Op1);
                }
                else
                {
                  insn.itype = I5_arhl;  // undoc
                }
                break;
              case 3:                   // 18
                if ( isZ80() )
                {
Z80_COMMON:
                  static const uint16 conds[] = { 0,1,2,oc_not,oc_nz,oc_z,oc_nc,oc_c };
                  insn.Op1.Cond = conds[sub];
                  insn.itype = Z80_jr;
                  insn.Op1.type = o_cond;
                  op_e(insn, insn.Op2);
                  break;
                }
                insn.itype = I5_rdel;  // undoc
                break;
              case 4:                   // 20
                if ( isZ80() )
                  goto Z80_COMMON;
                insn.itype = I5_rim;
                break;
              case 5:                   // 28
                if ( isZ80() )
                  goto Z80_COMMON;
                insn.itype = I5_ldhi;  // undoc
                GetImm(insn, insn.Op1);
                break;
              case 6:                   // 30
                if ( isZ80() )
                  goto Z80_COMMON;
                insn.itype = I5_sim;
                break;
              case 7:                   // 38
                if ( isZ80() )
                  goto Z80_COMMON;
                insn.itype = I5_ldsi;
                GetImm(insn, insn.Op1);
                break;
            }
          }
          break;
        case 1:
          insn.itype = I5_lxi;
          op_ss(insn.Op1);
          op_nn(insn, insn.Op2);
          break;

        case 9:
          insn.itype = I5_dad;
          insn.Op1.reg = R_hl;
          insn.Op1.dtype = dt_word;
          insn.Op1.type = o_reg;
          insn.Op1.clr_shown();
          op_ss(insn.Op2);
          break;
        case 0xA:
          if ( (code & 0x20) == 0 )
          {
            insn.itype = I5_ldax;
            insn.Op2.type = o_phrase;
            insn.Op2.phrase = R_bc + ((code >> 4) & 1);
            op_a(insn.Op1);
          }
          else
          {
            if ( isGB() )
            {
              insn.itype = (code & 0x10) ? Z80_ldd : Z80_ldi;
              insn.Op1.type = o_reg;
              insn.Op1.reg = R_a;
              insn.Op2.type = o_phrase;
              insn.Op2.phrase = R_hl;
              break;
            }
            insn.Op1.type = o_reg;
            if ( (code & 0x10) == 0 )
            {
              insn.itype = I5_lhld;
              insn.Op1.dtype = dt_word;
              insn.Op2.dtype = dt_word;
              insn.Op1.reg = R_hl;
            }
            else
            {
              insn.itype = I5_lda;
              insn.Op1.reg = R_a;
            }
            op_mm(insn, insn.Op2);
          }
          insn.Op1.clr_shown();
          break;
        case 2:
          if ( (code & 0x20) == 0 )
          {
            insn.itype = I5_stax;
            insn.Op1.type = o_phrase;
            insn.Op1.phrase = R_bc + ((code >> 4) & 1);
            op_a(insn.Op2);
          }
          else
          {
            if ( isGB() )
            {
              insn.itype = (code & 0x10) ? Z80_ldd : Z80_ldi;
              insn.Op1.type = o_phrase;
              insn.Op1.phrase = R_hl;
              insn.Op2.type = o_reg;
              insn.Op2.reg = R_a;
              break;
            }
            insn.Op2.type = o_reg;
            if ( (code & 0x10) == 0 )
            {
              insn.itype = I5_shld;
              insn.Op1.dtype = dt_word;
              insn.Op2.dtype = dt_word;
              insn.Op2.reg = R_hl;
            }
            else
            {
              insn.itype = I5_sta;
              insn.Op2.reg = R_a;
            }
            op_mm(insn, insn.Op1);
          }
          insn.Op2.clr_shown();
          break;
        case 3:
          insn.itype = I5_inx;
          op_ss(insn.Op1);
          break;
        case 0xB:
          insn.itype = I5_dcx;
          op_ss(insn.Op1);
          break;
        case 4:
        case 0xC:
          insn.itype = I5_inr;
          op_r1(insn.Op1);
          break;
        case 5:
        case 0xD:
          insn.itype = I5_dcr;
          op_r1(insn.Op1);
          break;
        case 6:
        case 0xE:
          insn.itype = I5_mvi;
          op_r1(insn.Op1);
          op_n (insn, insn.Op2);
          break;
        case 7:
        case 0xF:
          insn.itype = (isZ80() ? Zrols : rols)[ (code >> 3) & 7 ];
          break;
      }
      break;
    case 0x40:
      insn.itype = I5_halt;
      if ( code != 0x76 )
      {
        insn.itype = I5_mov;
        op_r1(insn.Op1);
        op_r2(insn.Op2);
      }
      break;
    case 0x80:
      op_r2(insn.Op2);
common:
      insn.Op1.type = o_reg;
      insn.Op1.reg = R_a;
      insn.itype = (insn.Op2.type == o_imm ? Wi : W)[ (code >> 3) & 7 ];
      insn.Op1.clr_shown();
      break;
    case 0xC0:                          /* 11?????? */
      switch ( code & 0xF )
      {
        case 0x0:
        case 0x8:                     /* 11???000 */
          if ( isGB() )
          {
            switch ( code )
            {
              case 0xE0:
              case 0xF0:
                insn.itype = Z80_ld;
                insn.Op1.type = o_mem;
                insn.Op1.addr = 0xFF00 + insn.get_next_byte();
                insn.Op2.type = o_reg;
                insn.Op2.reg = R_a;
                if ( code & 0x10 )
                  swap_operands(insn);
                break;
              case 0xE8:
                insn.itype = Z80_add;
                insn.Op1.type = o_reg;
                insn.Op1.reg  = R_sp;
                GetImm(insn, insn.Op2);
                break;
              case 0xF8:  // docs say this is "ld hl, sp"
                          // Daniel Filner <danf@codefrog.cx> says
                          // this is "ld hl, sp+immbyte"
                insn.itype = Z80_ld;
                insn.Op1.type = o_reg;
                insn.Op1.reg  = R_hl;
                insn.Op2.type = o_displ;
                insn.Op2.phrase = R_sp;
                insn.Op2.addr = insn.get_next_byte();
                break;
              default:
                goto RETS;
            }
            break;
          }
RETS:
          insn.itype = rets[ (code >> 3) & 7 ];
          break;
        case 0x2:
        case 0xA:                     /* 11???010 */
          if ( isGB() )
          {
            switch ( code )
            {
              case 0xE2:
              case 0xF2:
                insn.itype = Z80_ld;
                insn.auxpref |= aux_off16;
                op_c(insn.Op1);
                insn.Op2.type = o_reg;
                insn.Op2.reg = R_a;
                if ( code & 0x10 )
                  swap_operands(insn);
                break;
              case 0xEA:
              case 0xFA:
                insn.itype = Z80_ld;
                op_mm(insn, insn.Op1);
                insn.Op2.type = o_reg;
                insn.Op2.reg = R_a;
                if ( code & 0x10 )
                  swap_operands(insn);
                break;
              default:
                goto JUMPS;
            }
            break;
          }
JUMPS:
          insn.itype = jumps[ (code >> 3) & 7 ];
          op_ad(insn, insn.Op1);
          break;
        case 0x3:
        case 0xB:                     /* 11???011 */
          switch ( ( code >> 3 ) & 7 )
          {
            case 0:                   /* 11000011=C3 */
              insn.itype = I5_jmp;
              op_ad(insn, insn.Op1);
              break;
            case 1:                   // 11001011=CB - Z80 extensions
              if ( isZ80() )
              {
                code = insn.get_next_byte();
                int fn = (code>>6);
                if ( fn == 0 )
                {
                  op_r2(insn.Op1);
                  insn.itype = CBrols[ (code>>3) & 7 ];
                  if ( insn.itype == Z80_srr )
                  {
                    if ( isZ380() )
                    {
                      insn.itype = Z80_ex;
                      op_r2(insn.Op2);
                      insn.Op2.reg += R_b2;
                    }
                    else if ( isGB() )
                    {
                      insn.itype = Z80_swap;
                    }
                  }
                }
                else
                {
                  static const uint16 brs[] = { 0, Z80_bit,Z80_res,Z80_set };
                  insn.itype = brs[fn];
                  insn.Op1.type = o_imm;
                  insn.Op1.value = (code>>3) & 7;
                  op_r2(insn.Op2);
                }
              }
              else
              {
                insn.itype = I5_rstv;  // undoc
              }
              break;
            case 2:                   /* 11010011=D3 */
              insn.itype = I5_out;
              op_a(insn.Op2);
              op_n(insn, insn.Op1);
              break;
            case 3:                   // DB
              insn.itype = I5_in;
              op_a(insn.Op1);
              op_n(insn, insn.Op2);
              break;
            case 4:                   // E3
              insn.itype = I5_xthl;
              insn.Op1.type = o_phrase;
              insn.Op1.reg = R_sp;
              insn.Op1.clr_shown();
              insn.Op2.type = o_reg;
              insn.Op2.reg = R_hl;
              insn.Op2.clr_shown();
              break;
            case 5:                   // EB
              insn.itype = I5_xchg;
              insn.Op1.type = o_reg;
              insn.Op1.reg = R_de;
              insn.Op1.clr_shown();
              insn.Op2.type = o_reg;
              insn.Op2.reg = R_hl;
              insn.Op2.clr_shown();
              break;
            case 6:                   // F3
              insn.itype = I5_di;
              break;
            case 7:                   // FB
              insn.itype = I5_ei;
              break;
          }
          break;
        case 0x4:
        case 0xC:                     /* 11???100 */
          insn.itype = calls[ (code >> 3) & 7 ];
          op_ad(insn, insn.Op1);
          break;
        case 0x1:
        case 0x5:                     // 11????01
          insn.itype = (code & 4) ? I5_push : I5_pop;
          op_dd(insn.Op1);
          break;
        case 0x6:
        case 0xE:                     // 11???110
          op_n(insn, insn.Op2);
          goto common;
        case 0x7:
        case 0xF:                     // 11???111
          insn.itype = I5_rst;
          insn.Op1.type = o_imm;
          insn.Op1.value = (code >> 3) & 7;
          if ( isZ80() )
          { // workaround for bcb6 bug:
            uval_t x = insn.Op1.value;
            x <<= 3;
            insn.Op1.value = x;
          }
          break;
        case 0x9:
          switch ( (code >> 4) & 3 )
          {
            case 0:
              insn.itype = I5_ret;     // 11001001 = C9
              break;
            case 1:                   // 11011001 = D9
              if ( isGB() )
                insn.itype = Z80_reti;
              else if ( isZ80() )
                insn.itype = Z80_exx;
              else
                insn.itype = I5_shlx;  // undoc
              break;
            case 2:
              insn.itype = I5_pchl;
              break;
            case 3:
              insn.itype = I5_sphl;
              insn.Op1.type = o_reg;
              insn.Op1.reg = R_sp;
              insn.Op1.clr_shown();
              insn.Op2.type = o_reg;
              insn.Op2.reg = R_hl;
              insn.Op2.clr_shown();
              break;
          }
          break;
        case 0xD:                     /* 11??1101 */
          switch ( (code >> 4) & 3 )
          {
            case 0:
              insn.itype = I5_call;
              op_ad(insn, insn.Op1);
              break;
            case 1:           // 11011101 = DD - Z80 extensions
              if ( is8085() )
              {
                insn.itype = I5_jnx5;   // undoc
                op_ad(insn, insn.Op1);
              }
              else
              {
                z80_ixcommands(insn, true);
              }
              break;
            case 2:           // 11101101 = ED - Z80 extensions
              if ( isGB() )
                return 0;
              else if ( isZ380() )
                z380_ED(insn);
              else if ( is8085() )
                insn.itype = I5_lhlx;  // undoc
              else
                z80_misc(insn);
              break;
            case 3:           // 11111101 = FD - Z80 extensions
              if ( is8085() )
              {
                insn.itype = I5_jx5;   // undoc
                op_ad(insn, insn.Op1);
              }
              else
              {
                z80_ixcommands(insn, false);
              }
              break;
          }
          break;
      }
      break;
  }

  if ( insn.itype == I5_null )
    return 0;

  if ( isZ80() )
  {
    if ( ash.uflag & UAS_FUNNY )
      ConvertToFunny(insn);
    else
      ConvertToZ80(insn);
    if ( isGB() && !is_gameboy_insn(insn) )
      return 0;
  }

  return insn.size;

}

//------------------------------------------------------------------------
void z80_t::op_r1(op_t &x) const
{
  uint16 mode = (code >> 3) & 7;
  if ( mode == 6 )
  {
    x.type = o_phrase;
    x.phrase = R_hl;
  }
  else
  {
    x.type = o_reg;
    x.reg = mode;
  }
}

//------------------------------------------------------------------------
void z80_t::op_r2(op_t &x) const
{
  uint16 mode = code & 7;
  if ( mode == 6 )
  {
    x.type = o_phrase;
    x.phrase = R_hl;
  }
  else
  {
    x.type = o_reg;
    x.reg = mode;
  }
}

//------------------------------------------------------------------------
void z80_t::op_ss(op_t &x) const
{
  uint16 ss = (code >> 4) & 3;
  x.type = o_reg;
  x.dtype = dt_word;
  x.reg = ss + R_bc;
  if ( ss == 3 )
    x.reg = R_sp;
}

//------------------------------------------------------------------------
void z80_t::op_dd(op_t &x) const
{
  uint16 ss = (code >> 4) & 3;
  x.type = o_reg;
  x.dtype = dt_word;
  x.reg = ss + R_bc;
}

//------------------------------------------------------------------------
static void op_nn(insn_t &insn, op_t &x)
{
  x.type = o_imm;
  x.dtype = dt_word;
  x.value = insn.get_next_word();
}

//------------------------------------------------------------------------
static void op_n(insn_t &insn, op_t &x)
{
  x.type = o_imm;
  x.value = insn.get_next_byte();
}

//------------------------------------------------------------------------
static void op_e(insn_t &insn, op_t &x)
{
  x.type = o_near;
  sval_t rel = char(insn.get_next_byte());
  x.addr = ushort(insn.ip + insn.size + rel);
}

//------------------------------------------------------------------------
static void op_a(op_t &x)
{
  x.type = o_reg;
  x.reg = R_a;
  x.clr_shown();
}

//------------------------------------------------------------------------
static void op_mm(insn_t &insn, op_t &x)
{
  x.type = o_mem;
  x.addr = insn.get_next_word();
}

//------------------------------------------------------------------------
static void op_f(op_t &x)
{
  x.type  = o_reg;
  x.dtype = dt_byte;
  x.reg   = R_f;
}

//------------------------------------------------------------------------
void z80_t::op_xdispl(insn_t &insn, op_t &x) const
{
  x.type = o_displ;
  x.phrase = isx ? R_ix : R_iy;
  x.addr = insn.get_next_byte();
}

//------------------------------------------------------------------------
void z80_t::op_ix(op_t &x) const
{
  x.type = o_reg;
  x.reg = isx ? R_ix : R_iy;
  x.dtype = dt_word;
}

//------------------------------------------------------------------------
void z80_t::op_ibyte(op_t &x, int low) const
{
  x.type = o_reg;
  x.reg = isx
        ? (low ? R_xl : R_xh)
        : (low ? R_yl : R_yh);
}

//------------------------------------------------------------------------
int z80_t::op_xbytereg(op_t &x, uint16 mode) const
{
  if ( mode == 6 )
  {
    x.type = o_phrase;
    x.phrase = R_hl;
    return 1;
  }
  else
  {
    if ( mode == R_h )
    {
      op_ibyte(x,0);
    }
    else if ( mode == R_l )
    {
      op_ibyte(x,1);
    }
    else
    {
      x.type = o_reg;
      x.reg = mode;
    }
    return 0;
  }
}

//------------------------------------------------------------------------
int z80_t::op_xr1(op_t &x) const
{
  return op_xbytereg(x,(code >> 3) & 7);
}

//------------------------------------------------------------------------
int z80_t::op_xr2(op_t &x) const
{
  return op_xbytereg(x,code & 7);
}

//------------------------------------------------------------------------
struct insndesc_t
{
  uchar code;
  ushort itype; //lint !e958 padding is required to align members
  uchar op1;
  uchar op2;
};

#define rA      (R_a+1)
#define rB      (R_b+1)
#define rC      (R_c+1)
#define rD      (R_d+1)
#define rE      (R_e+1)
#define rH      (R_h+1)
#define rL      (R_l+1)
#define rI      (R_i+1)
#define rW      (R_w+1)
#define rR      (R_r+1)
#define rBC     (R_bc+1)
#define rDE     (R_de+1)
#define rHL     (R_hl+1)
#define rIX     (R_ix+1)
#define rIY     (R_iy+1)
#define rSP     (R_sp+1)
#define rLW     (R_lw+1)
#define rIXL    (R_ixl+1)
#define rIXU    (R_ixu+1)
#define rDSR    (R_dsr+1)
#define rXSR    (R_xsr+1)
#define rIYL    (R_iyl+1)
#define rIYU    (R_iyu+1)
#define rYSR    (R_ysr+1)
#define rSR     (R_sr+1)
#define rIB     (R_ib+1)
#define rIW     (R_iw+1)
#define rXM     (R_xm+1)
#define rLCK    (R_lck+1)
#define rBC2    (R_bc2+1)
#define rDE2    (R_de2+1)
#define rHL2    (R_hl2+1)
#define rIX2    (R_ix2+1)
#define rIY2    (R_iy2+1)

#define atBC    0x30
#define atDE    0x31
#define atHL    0x32
#define atSP    0x33
#define atIX    0x34
#define atIY    0x35
#define atC     0x36
#define rip8    0x37    // ip relative displ 8bits
#define rip16   0x38    // ip relative displ 16bits
#define rip24   0x39    // ip relative displ 24bits
#define imm8    0x3A    // 8bit immval
#define imm16   0x3B    // 16bit immval
#define mem16   0x3C    // (1234h)

#define ix8     0x40    // (IX+12)
#define iy8     0x41    // (IY+12)
#define ixs     0x42    // (IX+saved_value)
#define iys     0x43    // (IY+saved_value)
#define sps     0x44    // (SP+saved_value)

// condition codes
#define ccNZ    0x50
#define ccZ     0x51
#define ccNC    0x52
#define ccC     0x53
#define ccPO    0x54
#define ccPE    0x55
#define ccP     0x56
#define ccM     0x57

#define c0      0x60
#define c1      0x61
#define c2      0x62
#define c3      0x63
#define c4      0x64
#define c5      0x65
#define c6      0x66
#define c7      0x67

static const insndesc_t cmdsDD[] =
{
  { 0x01, Z80_ld,     atBC,       rIX         },
  { 0x02, Z80_ld,     rBC,        rDE         },
  { 0x03, Z80_ld,     rIX,        atBC        },
  { 0x07, Z80_ld,     rIX,        rBC         },
  { 0x09, Z80_add,    rIX,        rBC         },
  { 0x0B, Z80_ld,     rBC,        rIX         },
  { 0x0C, Z80_ld,     rBC,        atBC        },
  { 0x0D, Z80_ld,     rBC,        atDE        },
  { 0x0F, Z80_ld,     rBC,        atHL        },
  { 0x10, Z80_djnz,   rip16                   },
  { 0x11, Z80_ld,     atDE,       rIX         },
  { 0x12, Z80_ld,     rDE,        rDE         },
  { 0x13, Z80_ld,     rIX,        atDE        },
  { 0x17, Z80_ld,     rIX,        rDE         },
  { 0x18, Z80_jr,     rip16                   },
  { 0x19, Z80_add,    rIX,        rDE         },
  { 0x1B, Z80_ld,     rDE,        rIX         },
  { 0x1C, Z80_ld,     rDE,        atBC        },
  { 0x1D, Z80_ld,     rDE,        atDE        },
  { 0x1F, Z80_ld,     rDE,        atHL        },
  { 0x20, Z80_jr,     ccNZ,       rip16       },
  { 0x21, Z80_ld,     rIX,        imm16       },
  { 0x22, Z80_ld,     mem16,      rIX         },
  { 0x23, Z80_inc,    rIX                     },
  { 0x24, Z80_inc,    rIXU                    },
  { 0x25, Z80_dec,    rIXU                    },
  { 0x26, Z80_ld,     rIXU,       imm8        },
  { 0x27, Z80_ld,     rIX,        rIY         },
  { 0x28, Z80_jr,     ccZ,        rip16       },
  { 0x29, Z80_add,    rIX,        rIX         },
  { 0x2A, Z80_ld,     rIX,        mem16       },
  { 0x2B, Z80_dec,    rIX                     },
  { 0x2C, Z80_inc,    rIXL                    },
  { 0x2D, Z80_dec,    rIXL                    },
  { 0x2E, Z80_ld,     rIXL,       imm8        },
  { 0x2F, Z80_cplw,   rHL                     },
  { 0x30, Z80_jr,     ccNC,       rip16       },
  { 0x31, Z80_ld,     atHL,       rIX         },
  { 0x32, Z80_ld,     rHL,        rDE         },
  { 0x33, Z80_ld,     rIX,        atHL        },
  { 0x34, Z80_inc,    ix8                     },
  { 0x35, Z80_dec,    ix8                     },
  { 0x36, Z80_ld,     ix8,        imm8        },
  { 0x37, Z80_ld,     rIX,        rHL         },
  { 0x38, Z80_jr,     ccC,        rip16       },
  { 0x39, Z80_add,    rIX,        rSP         },
  { 0x3B, Z80_ld,     rHL,        rIX         },
  { 0x3C, Z80_ld,     rHL,        atBC        },
  { 0x3D, Z80_ld,     rHL,        atDE        },
  { 0x3E, Z80_swap,   rIX                     },
  { 0x3F, Z80_ld,     rHL,        atHL        },
  { 0x40, Z80_inw,    rBC,        atC         },
  { 0x41, Z80_outw,   atC,        rBC         },
  { 0x44, Z80_ld,     rB,         rIXU        },
  { 0x45, Z80_ld,     rB,         rIXL        },
  { 0x46, Z80_ld,     rB,         ix8         },
  { 0x47, Z80_ldw,    rI,         rHL         },
  { 0x4C, Z80_ld,     rC,         rIXU        },
  { 0x4D, Z80_ld,     rC,         rIXL        },
  { 0x4E, Z80_ld,     rC,         ix8         },
  { 0x50, Z80_inw,    rDE,        atC         },
  { 0x51, Z80_outw,   atC,        rDE         },
  { 0x54, Z80_ld,     rD,         rIXU        },
  { 0x55, Z80_ld,     rD,         rIXL        },
  { 0x56, Z80_ld,     rD,         ix8         },
  { 0x57, Z80_ldw,    rHL,        rI          },
  { 0x5D, Z80_ld,     rE,         rIXL        },
  { 0x5E, Z80_ld,     rE,         ix8         },
  { 0x60, Z80_ld,     rIXU,       rB          },
  { 0x61, Z80_ld,     rIXU,       rC          },
  { 0x62, Z80_ld,     rIXU,       rD          },
  { 0x63, Z80_ld,     rIXU,       rE          },
  { 0x64, Z80_ld,     rIXU,       rIXU        },
  { 0x65, Z80_ld,     rIXU,       rIXL        },
  { 0x66, Z80_ld,     rH,         ix8         },
  { 0x67, Z80_ld,     rIXU,       rA          },
  { 0x68, Z80_ld,     rIXL,       rB          },
  { 0x69, Z80_ld,     rIXL,       rC          },
  { 0x6A, Z80_ld,     rIXL,       rD          },
  { 0x6B, Z80_ld,     rIXL,       rE          },
  { 0x6C, Z80_ld,     rIXL,       rIXU        },
  { 0x6D, Z80_ld,     rIXL,       rIXL        },
  { 0x6E, Z80_ld,     rL,         ix8         },
  { 0x6F, Z80_ld,     rIXL,       rA          },
  { 0x70, Z80_ld,     ix8,        rB          },
  { 0x71, Z80_ld,     ix8,        rC          },
  { 0x72, Z80_ld,     ix8,        rD          },
  { 0x73, Z80_ld,     ix8,        rE          },
  { 0x74, Z80_ld,     ix8,        rH          },
  { 0x75, Z80_ld,     ix8,        rL          },
  { 0x77, Z80_ld,     ix8,        rA          },
  { 0x78, Z80_inw,    rHL,        atC         },
  { 0x79, Z80_outw,   atC,        rHL         },
  { 0x7C, Z80_ld,     rA,         rIXU        },
  { 0x7D, Z80_ld,     rA,         rIXL        },
  { 0x7E, Z80_ld,     rA,         ix8         },
  { 0x84, Z80_add,    rA,         rIXU        },
  { 0x85, Z80_add,    rA,         rIXL        },
  { 0x86, Z80_add,    rA,         ix8         },
  { 0x87, Z80_addw,   rHL,        rIX         },
  { 0x8C, Z80_adc,    rA,         rIXU        },
  { 0x8D, Z80_adc,    rA,         rIXL        },
  { 0x8E, Z80_adc,    rA,         ix8         },
  { 0x8F, Z80_adcw,   rHL,        rIX         },
  { 0x94, Z80_sub,    rA,         rIXU        },
  { 0x95, Z80_sub,    rA,         rIXL        },
  { 0x96, Z80_sub,    rA,         ix8         },
  { 0x97, Z80_subw,   rHL,        rIX         },
  { 0x9C, Z80_sbc,    rA,         rIXU        },
  { 0x9D, Z80_sbc,    rA,         rIXL        },
  { 0x9E, Z80_sbc,    rA,         ix8         },
  { 0x9F, Z80_sbcw,   rHL,        rIX         },
  { 0xA4, Z80_and,    rA,         rIXU        },
  { 0xA5, Z80_and,    rA,         rIXL        },
  { 0xA6, Z80_and,    rA,         ix8         },
  { 0xA7, Z80_andw,   rHL,        rIX         },
  { 0xAC, Z80_xor,    rA,         rIXU        },
  { 0xAD, Z80_xor,    rA,         rIXL        },
  { 0xAE, Z80_xor,    rA,         ix8         },
  { 0xAF, Z80_xorw,   rHL,        rIX         },
  { 0xB4, Z80_or,     rA,         rIXU        },
  { 0xB5, Z80_or,     rA,         rIXL        },
  { 0xB6, Z80_or,     rA,         ix8         },
  { 0xB7, Z80_orw,    rHL,        rIX         },
  { 0xBC, Z80_cp,     rA,         rIXU        },
  { 0xBD, Z80_cp,     rA,         rIXL        },
  { 0xBE, Z80_cp,     rA,         ix8         },
  { 0xBF, Z80_cpw,    rHL,        rIX         },
  { 0xC0, Z80_ddir,   rW                      },
  { 0xC1, Z80_ddir,   rIB,        rW          },
  { 0xC2, Z80_ddir,   rIW,        rW          },
  { 0xC3, Z80_ddir,   rIB                     },
  { 0xC4, Z80_calr,   ccNZ,       rip16       },
  { 0xC6, Z80_addw,   rHL,        ix8         },
  { 0xC8, Z80_ldctl,  rSR,        rA          },
  { 0xCA, Z80_ldctl,  rSR,        imm8        },
  { 0xCC, Z80_calr,   ccZ,        rip16       },
  { 0xCD, Z80_calr,   rip16                   },
  { 0xCE, Z80_adcw,   rHL,        ix8         },
  { 0xCF, Z80_mtest,                          },
  { 0xD0, Z80_ldctl,  rA,         rXSR        },
  { 0xD4, Z80_calr,   ccNC,       rip16       },
  { 0xD6, Z80_subw,   rHL,        ix8         },
  { 0xD8, Z80_ldctl,  rXSR,       rA          },
  { 0xD9, Z80_exxx,                           },
  { 0xDA, Z80_ldctl,  rXSR,       imm8        },
  { 0xDC, Z80_calr,   ccC,        rip16       },
  { 0xDE, Z80_sbcw,   rHL,        ix8         },
  { 0xE1, Z80_pop,    rIX                     },
  { 0xE3, Z80_ex,     atSP,       rIX         },
  { 0xE4, Z80_calr,   ccPO,       rip16       },
  { 0xE5, Z80_push,   rIX                     },
  { 0xE6, Z80_andw,   rHL,        ix8         },
  { 0xE9, Z80_jp,     atIX                    },
  { 0xEC, Z80_calr,   ccPE,       rip16       },
  { 0xEE, Z80_xorw,   rHL,        ix8         },
  { 0xF3, Z80_di,     imm8                    },
  { 0xF4, Z80_calr,   ccP,        rip16       },
  { 0xF6, Z80_orw,    rHL,        ix8         },
  { 0xF7, Z80_setc,   rLW                     },
  { 0xF9, Z80_ld,     rSP,        rIX         },
  { 0xFB, Z80_ei,     imm8                    },
  { 0xFC, Z80_calr,   ccM,        rip16       },
  { 0xFE, Z80_cpw,    rHL,        ix8         },
  { 0xFF, Z80_resc,   rLW                     },
  { 0x00, 0 },
};

// the next byte after DDBC is in saved_value
static const insndesc_t cmdsDDCB[] =
{
  { 0x01, Z80_ld,     rBC,        sps         },
  { 0x02, Z80_rlcw,   ixs                     },
  { 0x03, Z80_ld,     rBC,        ixs         },
  { 0x06, Z80_rlc,    ixs                     },
  { 0x09, Z80_ld,     sps,        rBC         },
  { 0x0A, Z80_rrcw,   ixs                     },
  { 0x0B, Z80_ld,     ixs,        rBC         },
  { 0x0E, Z80_rrc,    ixs                     },
  { 0x11, Z80_ld,     rDE,        sps         },
  { 0x12, Z80_rlw,    ixs                     },
  { 0x13, Z80_ld,     rDE,        ixs         },
  { 0x16, Z80_rl,     ixs                     },
  { 0x19, Z80_ld,     sps,        rDE         },
  { 0x1A, Z80_rrw,    ixs                     },
  { 0x1B, Z80_ld,     ixs,        rDE         },
  { 0x1E, Z80_rr,     ixs                     },
  { 0x21, Z80_ld,     rIX,        sps         },
  { 0x22, Z80_slaw,   ixs                     },
  { 0x23, Z80_ld,     rIY,        ixs         },
  { 0x26, Z80_sla,    ixs                     },
  { 0x29, Z80_ld,     sps,        rIX         },
  { 0x2A, Z80_sraw,   ixs                     },
  { 0x2B, Z80_ld,     ixs,        rIY         },
  { 0x2E, Z80_sra,    ixs                     },
  { 0x31, Z80_ld,     rHL,        sps         },
  { 0x33, Z80_ld,     rHL,        ixs         },
  { 0x39, Z80_ld,     sps,        rHL         },
  { 0x3A, Z80_srlw,   ixs                     },
  { 0x3B, Z80_ld,     ixs,        rHL         },
  { 0x3E, Z80_srl,    ixs                     },
  { 0x46, Z80_bit,    c0,         ixs         },
  { 0x4E, Z80_bit,    c1,         ixs         },
  { 0x56, Z80_bit,    c2,         ixs         },
  { 0x5E, Z80_bit,    c3,         ixs         },
  { 0x66, Z80_bit,    c4,         ixs         },
  { 0x6E, Z80_bit,    c5,         ixs         },
  { 0x76, Z80_bit,    c6,         ixs         },
  { 0x7E, Z80_bit,    c7,         ixs         },
  { 0x86, Z80_res,    c0,         ixs         },
  { 0x8E, Z80_res,    c1,         ixs         },
  { 0x92, Z80_multw,  ixs                     },
  { 0x92, Z80_multw,  rHL,        ixs         },
  { 0x96, Z80_res,    c2,         ixs         },
  { 0x9A, Z80_multuw, ixs                     },
  { 0x9A, Z80_multuw, rHL,        ixs         },
  { 0x9E, Z80_res,    c3,         ixs         },
  { 0xA6, Z80_res,    c4,         ixs         },
  { 0xAE, Z80_res,    c5,         ixs         },
  { 0xB6, Z80_res,    c6,         ixs         },
  { 0xBA, Z80_divuw,  ixs                     },
  { 0xBA, Z80_divuw,  rHL,        ixs         },
  { 0xBE, Z80_res,    c7,         ixs         },
  { 0xC6, Z80_set,    c0,         ixs         },
  { 0xCE, Z80_set,    c1,         ixs         },
  { 0xD6, Z80_set,    c2,         ixs         },
  { 0xDE, Z80_set,    c3,         ixs         },
  { 0xE6, Z80_set,    c4,         ixs         },
  { 0xEE, Z80_set,    c5,         ixs         },
  { 0xF6, Z80_set,    c6,         ixs         },
  { 0xFE, Z80_set,    c7,         ixs         },
  { 0x00, 0 },
};


static const insndesc_t cmdsFD[] =
{
  { 0x01, Z80_ld,     atBC,       rIY         },
  { 0x02, Z80_ld,     rBC,        rHL         },
  { 0x03, Z80_ld,     rIY,        atBC        },
  { 0x07, Z80_ld,     rIY,        rBC         },
  { 0x09, Z80_add,    rIY,        rBC         },
  { 0x0B, Z80_ld,     rBC,        rIY         },
  { 0x0C, Z80_ld,     atBC,       rBC         },
  { 0x0D, Z80_ld,     atDE,       rBC         },
  { 0x0F, Z80_ld,     atHL,       rBC         },
  { 0x10, Z80_djnz,   rip24                   },
  { 0x11, Z80_ld,     atDE,       rIY         },
  { 0x12, Z80_ld,     rDE,        rHL         },
  { 0x13, Z80_ld,     rIY,        atDE        },
  { 0x17, Z80_ld,     rIY,        rDE         },
  { 0x18, Z80_jr,     rip24                   },
  { 0x19, Z80_add,    rIY,        rDE         },
  { 0x1B, Z80_ld,     rDE,        rIY         },
  { 0x1C, Z80_ld,     atBC,       rDE         },
  { 0x1D, Z80_ld,     atDE,       rDE         },
  { 0x1F, Z80_ld,     atHL,       rDE         },
  { 0x20, Z80_jr,     ccNZ,       rip24       },
  { 0x21, Z80_ld,     rIY,        imm16       },
  { 0x22, Z80_ld,     mem16,      rIY         },
  { 0x23, Z80_inc,    rIY                     },
  { 0x24, Z80_inc,    rIYU                    },
  { 0x25, Z80_dec,    rIYU                    },
  { 0x26, Z80_ld,     rIYU,       imm8        },
  { 0x27, Z80_ld,     rIY,        rIX         },
  { 0x28, Z80_jr,     ccZ,        rip24       },
  { 0x29, Z80_add,    rIY,        rIY         },
  { 0x2A, Z80_ld,     rIY,        mem16       },
  { 0x2B, Z80_dec,    rIY                     },
  { 0x2C, Z80_inc,    rIYL                    },
  { 0x2D, Z80_dec,    rIYL                    },
  { 0x2E, Z80_ld,     rIYL,       imm8        },
  { 0x30, Z80_jr,     ccNC,       rip24       },
  { 0x31, Z80_ld,     atHL,       rIY         },
  { 0x32, Z80_ld,     rHL,        rHL         },
  { 0x33, Z80_ld,     rIY,        atHL        },
  { 0x34, Z80_inc,    iy8                     },
  { 0x35, Z80_dec,    iy8                     },
  { 0x36, Z80_ld,     iy8,        imm8        },
  { 0x26, Z80_ld,     rIYU,       imm8        },
  { 0x37, Z80_ld,     rIY,        rHL         },
  { 0x38, Z80_jr,     ccC,        rip24       },
  { 0x39, Z80_add,    rIY,        rSP         },
  { 0x3B, Z80_ld,     rHL,        rIY         },
  { 0x3C, Z80_ld,     atBC,       rHL         },
  { 0x3D, Z80_ld,     atDE,       rHL         },
  { 0x3E, Z80_swap,   rIY                     },
  { 0x3F, Z80_ld,     atHL,       rHL         },
  { 0x44, Z80_ld,     rB,         rIYU        },
  { 0x45, Z80_ld,     rB,         rIYL        },
  { 0x46, Z80_ld,     rB,         iy8         },
  { 0x4C, Z80_ld,     rC,         rIYU        },
  { 0x4D, Z80_ld,     rC,         rIYL        },
  { 0x4E, Z80_ld,     rC,         iy8         },
  { 0x54, Z80_ld,     rD,         rIYU        },
  { 0x55, Z80_ld,     rD,         rIYL        },
  { 0x56, Z80_ld,     rD,         iy8         },
  { 0x5C, Z80_ld,     rE,         rIYU        },
  { 0x5D, Z80_ld,     rE,         rIYL        },
  { 0x5E, Z80_ld,     rE,         iy8         },
  { 0x60, Z80_ld,     rIYU,       rB          },
  { 0x61, Z80_ld,     rIYU,       rC          },
  { 0x62, Z80_ld,     rIYU,       rD          },
  { 0x63, Z80_ld,     rIYU,       rE          },
  { 0x64, Z80_ld,     rIYU,       rIYU        },
  { 0x65, Z80_ld,     rIYU,       rIYL        },
  { 0x66, Z80_ld,     rH,         iy8         },
  { 0x67, Z80_ld,     rIYU,       rA          },
  { 0x68, Z80_ld,     rIYL,       rB          },
  { 0x69, Z80_ld,     rIYL,       rC          },
  { 0x6A, Z80_ld,     rIYL,       rD          },
  { 0x6B, Z80_ld,     rIYL,       rE          },
  { 0x6C, Z80_ld,     rIYL,       rIYU        },
  { 0x6D, Z80_ld,     rIYL,       rIYL        },
  { 0x6E, Z80_ld,     rL,         iy8         },
  { 0x6F, Z80_ld,     rIYL,       rA          },
  { 0x70, Z80_ld,     iy8,        rB          },
  { 0x71, Z80_ld,     iy8,        rC          },
  { 0x72, Z80_ld,     iy8,        rD          },
  { 0x73, Z80_ld,     iy8,        rE          },
  { 0x74, Z80_ld,     iy8,        rH          },
  { 0x75, Z80_ld,     iy8,        rL          },
  { 0x77, Z80_ld,     iy8,        rA          },
  { 0x79, Z80_outw,   atC,        imm16       },
  { 0x7C, Z80_ld,     rA,         rIYU        },
  { 0x7D, Z80_ld,     rA,         rIYL        },
  { 0x7E, Z80_ld,     rA,         iy8         },
  { 0x84, Z80_add,    rA,         rIYU        },
  { 0x85, Z80_add,    rA,         rIYL        },
  { 0x86, Z80_add,    rA,         iy8         },
  { 0x87, Z80_addw,   rHL,        rIY         },
  { 0x8C, Z80_adc,    rA,         rIYU        },
  { 0x8D, Z80_adc,    rA,         rIYL        },
  { 0x8E, Z80_adc,    rA,         iy8         },
  { 0x8F, Z80_adcw,   rHL,        rIY         },
  { 0x8F, Z80_adcw,   rIY                     },
  { 0x94, Z80_sub,    rA,         rIYU        },
  { 0x95, Z80_sub,    rA,         rIYL        },
  { 0x96, Z80_sub,    rA,         iy8         },
  { 0x97, Z80_subw,   rHL,        rIY         },
  { 0x9C, Z80_sbc,    rA,         rIYU        },
  { 0x9D, Z80_sbc,    rA,         rIYL        },
  { 0x9E, Z80_sbc,    rA,         iy8         },
  { 0x9F, Z80_sbcw,   rHL,        rIY         },
  { 0xA4, Z80_and,    rA,         rIYU        },
  { 0xA5, Z80_and,    rA,         rIYL        },
  { 0xA6, Z80_and,    rA,         iy8         },
  { 0xA7, Z80_andw,   rHL,        rIY         },
  { 0xAC, Z80_xor,    rA,         rIYU        },
  { 0xAD, Z80_xor,    rA,         rIYL        },
  { 0xAE, Z80_xor,    rA,         iy8         },
  { 0xAF, Z80_xorw,   rHL,        rIY         },
  { 0xB4, Z80_or,     rA,         rIYU        },
  { 0xB5, Z80_or,     rA,         rIYL        },
  { 0xB6, Z80_or,     rA,         iy8         },
  { 0xB7, Z80_orw,    rHL,        rIY         },
  { 0xBC, Z80_cp,     rA,         rIYU        },
  { 0xBD, Z80_cp,     rA,         rIYL        },
  { 0xBE, Z80_cp,     rA,         iy8         },
  { 0xBF, Z80_cpw,    rHL,        rIY         },
  { 0xC0, Z80_ddir,   rLW                     },
  { 0xC1, Z80_ddir,   rIB,        rLW         },
  { 0xC2, Z80_ddir,   rIW,        rLW         },
  { 0xC3, Z80_ddir,   rIW                     },
  { 0xC4, Z80_calr,   ccNZ,       rip24       },
  { 0xC6, Z80_addw,   iy8                     },
  { 0xC6, Z80_addw,   rHL,        iy8         },
  { 0xCC, Z80_calr,   ccZ,        rip24       },
  { 0xCD, Z80_calr,   rip24                   },
  { 0xCE, Z80_adcw,   rHL,        iy8         },
  { 0xD0, Z80_ldctl,  rA,         rYSR        },
  { 0xD3, Z80_outaw,  imm16,      rHL         },
  { 0xD4, Z80_calr,   ccNC,       rip24       },
  { 0xD6, Z80_subw,   rHL,        iy8         },
  { 0xD8, Z80_ldctl,  rYSR,       rA          },
  { 0xD9, Z80_exxy,                           },
  { 0xDA, Z80_ldctl,  rYSR,       imm8        },
  { 0xDB, Z80_inaw,   rHL,        imm16       },
  { 0xDC, Z80_calr,   ccC,        rip24       },
  { 0xDE, Z80_sbcw,   rHL,        iy8         },
  { 0xE1, Z80_pop,    rIY                     },
  { 0xE3, Z80_ex,     atSP,       rIY         },
  { 0xE4, Z80_calr,   ccPO,       rip24       },
  { 0xE5, Z80_push,   rIY                     },
  { 0xE6, Z80_andw,   rHL,        iy8         },
  { 0xE9, Z80_jp,     atIY                    },
  { 0xEC, Z80_calr,   ccPE,       rip24       },
  { 0xEE, Z80_xorw,   rHL,        iy8         },
  { 0xF4, Z80_calr,   ccP,        rip24       },
  { 0xF5, Z80_push,   imm16                   },
  { 0xF6, Z80_orw,    rHL,        iy8         },
  { 0xF7, Z80_setc,   rXM                     },
  { 0xF9, Z80_ld,     rSP,        rIY         },
  { 0xFC, Z80_calr,   ccM,        rip24       },
  { 0xFE, Z80_cpw,    rHL,        iy8         },
  { 0x00, 0 },
};

// the next byte after FDBC is in saved_value
static const insndesc_t cmdsFDCB[] =
{
  { 0x02, Z80_rlcw,   iys                     },
  { 0x03, Z80_ld,     rBC,        iys         },
  { 0x06, Z80_rlc,    iys                     },
  { 0x0A, Z80_rrcw,   iys                     },
  { 0x0B, Z80_ld,     iys,        rBC         },
  { 0x0E, Z80_rrc,    iys                     },
  { 0x12, Z80_rlw,    iys                     },
  { 0x13, Z80_ld,     rDE,        iys         },
  { 0x16, Z80_rl,     iys                     },
  { 0x1A, Z80_rrw,    iys                     },
  { 0x1B, Z80_ld,     iys,        rDE         },
  { 0x1E, Z80_rr,     iys                     },
  { 0x21, Z80_ld,     rIY,        sps         },
  { 0x22, Z80_slaw,   iys                     },
  { 0x23, Z80_ld,     rIX,        iys         },
  { 0x26, Z80_sla,    iys                     },
  { 0x29, Z80_ld,     sps,        rIY         },
  { 0x2A, Z80_sraw,   iys                     },
  { 0x2B, Z80_ld,     iys,        rIX         },
  { 0x2E, Z80_sra,    iys                     },
  { 0x33, Z80_ld,     rHL,        iys         },
  { 0x3A, Z80_srlw,   iys                     },
  { 0x3B, Z80_ld,     iys,        rHL         },
  { 0x3E, Z80_srl,    iys                     },
  { 0x46, Z80_bit,    c0,         iys         },
  { 0x4E, Z80_bit,    c1,         iys         },
  { 0x56, Z80_bit,    c2,         iys         },
  { 0x5E, Z80_bit,    c3,         iys         },
  { 0x66, Z80_bit,    c4,         iys         },
  { 0x6E, Z80_bit,    c5,         iys         },
  { 0x76, Z80_bit,    c6,         iys         },
  { 0x7E, Z80_bit,    c7,         iys         },
  { 0x86, Z80_res,    c0,         iys         },
  { 0x8E, Z80_res,    c1,         iys         },
  { 0x92, Z80_multw,  iys                     },
  { 0x92, Z80_multw,  rHL,        iys         },
  { 0x96, Z80_res,    c2,         iys         },
  { 0x9A, Z80_multuw, iys                     },
  { 0x9A, Z80_multuw, rHL,        iys         },
  { 0x9E, Z80_res,    c3,         iys         },
  { 0xA6, Z80_res,    c4,         iys         },
  { 0xAE, Z80_res,    c5,         iys         },
  { 0xB6, Z80_res,    c6,         iys         },
  { 0xBA, Z80_divuw,  iys                     },
  { 0xBA, Z80_divuw,  rHL,        iys         },
  { 0xBE, Z80_res,    c7,         iys         },
  { 0xC6, Z80_set,    c0,         iys         },
  { 0xCE, Z80_set,    c1,         iys         },
  { 0xD6, Z80_set,    c2,         iys         },
  { 0xDE, Z80_set,    c3,         iys         },
  { 0xE6, Z80_set,    c4,         iys         },
  { 0xEE, Z80_set,    c5,         iys         },
  { 0xF6, Z80_set,    c6,         iys         },
  { 0xFE, Z80_set,    c7,         iys         },
  { 0x00, 0 },
};

static const insndesc_t cmdsED[] =
{
  { 0x00, Z80_in0,    rB,         imm8        },
  { 0x01, Z80_out0,   imm8,       rB          },
  { 0x02, Z80_ld,     rBC,        rBC         },
  { 0x03, Z80_ex,     rBC,        rIX         },
  { 0x04, Z80_tst,    rB                      },
  { 0x05, Z80_ex,     rBC,        rDE         },
  { 0x06, Z80_ldw,    atBC,       imm16       },
  { 0x07, Z80_ex,     rA,         rB          },
  { 0x08, Z80_in0,    rC,         imm8        },
  { 0x09, Z80_out0,   imm8,       rC          },
  { 0x0B, Z80_ex,     rBC,        rIY         },
  { 0x0C, Z80_tst,    rC                      },
  { 0x0D, Z80_ex,     rBC,        rHL         },
  { 0x0E, Z80_swap,   rBC                     },
  { 0x0F, Z80_ex,     rA,         rC          },
  { 0x10, Z80_in0,    rD,         imm8        },
  { 0x11, Z80_out0,   imm8,       rD          },
  { 0x12, Z80_ld,     rDE,        rBC         },
  { 0x13, Z80_ex,     rDE,        rIX         },
  { 0x14, Z80_tst,    rD                      },
  { 0x16, Z80_ldw,    atDE,       imm16       },
  { 0x17, Z80_ex,     rA,         rD          },
  { 0x18, Z80_in0,    rE,         imm8        },
  { 0x19, Z80_out0,   imm8,       rE          },
  { 0x1B, Z80_ex,     rDE,        rIY         },
  { 0x1C, Z80_tst,    rE                      },
  { 0x1E, Z80_swap,   rDE                     },
  { 0x1F, Z80_ex,     rA,         rE          },
  { 0x20, Z80_in0,    rH,         imm8        },
  { 0x21, Z80_out0,   imm8,       rH          },
  { 0x24, Z80_tst,    rH                      },
  { 0x27, Z80_ex,     rA,         rH          },
  { 0x28, Z80_in0,    rL,         imm8        },
  { 0x29, Z80_out0,   imm8,       rL          },
  { 0x2B, Z80_ex,     rIX,        rIY         },
  { 0x2C, Z80_tst,    rL                      },
  { 0x2F, Z80_ex,     rA,         rL          },
  { 0x30, Z80_in0,    imm8                    },
  { 0x32, Z80_ld,     rHL,        rBC         },
  { 0x33, Z80_ex,     rHL,        rIX         },
  { 0x34, Z80_tst,    atHL                    },
  { 0x36, Z80_ldw,    atHL,       imm16       },
  { 0x37, Z80_ex,     rA,         atHL        },
  { 0x38, Z80_in0,    rA,         imm8        },
  { 0x39, Z80_out0,   imm8,       rA          },
  { 0x3B, Z80_ex,     rHL,        rIY         },
  { 0x3C, Z80_tst,    rA                      },
  { 0x3E, Z80_swap,   rHL                     },
  { 0x3F, Z80_ex,     rA,         rA          },
  { 0x40, Z80_in,     rB,         atC         },
  { 0x41, Z80_out,    atC,        rB          },
  { 0x42, Z80_sbc,    rHL,        rBC         },
  { 0x43, Z80_ld,     mem16,      rBC         },
  { 0x44, Z80_neg,    rA                      },
  { 0x45, Z80_retn,                           },
  { 0x46, Z80_im,     c0                      },
  { 0x47, Z80_ld,     rI,         rA          },
  { 0x48, Z80_in,     rC,         atC         },
  { 0x49, Z80_out,    atC,        rC          },
  { 0x4A, Z80_adc,    rHL,        rBC         },
  { 0x4B, Z80_ld,     rBC,        mem16       },
  { 0x4C, Z80_mlt,    rBC                     },
  { 0x4D, Z80_reti,                           },
  { 0x4E, Z80_im,     c3                      },
  { 0x4F, Z80_ld,     rR,         rA          },
  { 0x50, Z80_in,     rD,         atC         },
  { 0x51, Z80_out,    atC,        rD          },
  { 0x52, Z80_sbc,    rHL,        rDE         },
  { 0x53, Z80_ld,     mem16,      rDE         },
  { 0x54, Z80_negw,   rHL                     },
  { 0x56, Z80_im,     c1                      },
  { 0x57, Z80_ld,     rA,         rI          },
  { 0x58, Z80_in,     rE,         atC         },
  { 0x59, Z80_out,    atC,        rE          },
  { 0x5A, Z80_adc,    rHL,        rDE         },
  { 0x5B, Z80_ld,     rDE,        mem16       },
  { 0x5C, Z80_mlt,    rDE                     },
  { 0x5E, Z80_im,     c2                      },
  { 0x5F, Z80_ld,     rA,         rR          },
  { 0x60, Z80_in,     rH,         atC         },
  { 0x61, Z80_out,    atC,        rH          },
  { 0x62, Z80_sbc,    rHL,        rHL         },
  { 0x64, Z80_tst,    imm8                    },
  { 0x65, Z80_exts,   rA                      },
  { 0x67, Z80_rrd,                            },
  { 0x68, Z80_in,     rL,         atC         },
  { 0x69, Z80_out,    atC,        rL          },
  { 0x6A, Z80_adc,    rHL,        rHL         },
  { 0x6C, Z80_mlt,    rHL                     },
  { 0x6F, Z80_rld,                            },
  { 0x71, Z80_out,    atC,        imm8        },
  { 0x72, Z80_sbc,    rHL,        rSP         },
  { 0x73, Z80_ld,     mem16,      rSP         },
  { 0x74, Z80_tstio,  imm8                    },
  { 0x75, Z80_extsw,  rHL                     },
  { 0x76, Z80_slp,                            },
  { 0x78, Z80_in,     rA,         atC         },
  { 0x79, Z80_out,    atC,        rA          },
  { 0x7A, Z80_adc,    rHL,        rSP         },
  { 0x7B, Z80_ld,     rSP,        mem16       },
  { 0x7C, Z80_mlt,    rSP                     },
  { 0x82, Z80_add,    rSP,        imm16       },
  { 0x83, Z80_otim,                           },
  { 0x84, Z80_addw,   rHL,        rBC         },
  { 0x85, Z80_addw,   rHL,        rDE         },
  { 0x86, Z80_addw,   rHL,        imm16       },
  { 0x87, Z80_addw,   rHL,        rHL         },
  { 0x8B, Z80_otdm,                           },
  { 0x8C, Z80_adcw,   rHL,        rBC         },
  { 0x8D, Z80_adcw,   rHL,        rDE         },
  { 0x8E, Z80_adcw,   rHL,        imm16       },
  { 0x8F, Z80_adcw,   rHL,        rHL         },
  { 0x92, Z80_sub,    rSP,        imm16       },
  { 0x93, Z80_otimr,                          },
  { 0x94, Z80_subw,   rHL,        rBC         },
  { 0x95, Z80_subw,   rHL,        rDE         },
  { 0x96, Z80_subw,   rHL,        imm16       },
  { 0x97, Z80_subw,   rHL,        rHL         },
  { 0x9B, Z80_otdmr,                          },
  { 0x9C, Z80_sbcw,   rHL,        rBC         },
  { 0x9D, Z80_sbcw,   rHL,        rDE         },
  { 0x9E, Z80_sbcw,   rHL,        imm16       },
  { 0x9F, Z80_sbcw,   rHL,        rHL         },
  { 0xA0, Z80_ldi,                            },
  { 0xA1, Z80_cpi,                            },
  { 0xA2, Z80_ini,                            },
  { 0xA3, Z80_outi,                           },
  { 0xA4, Z80_andw,   rHL,        rBC         },
  { 0xA5, Z80_andw,   rHL,        rDE         },
  { 0xA6, Z80_andw,   rHL,        imm16       },
  { 0xA7, Z80_andw,   rHL,        rHL         },
  { 0xA8, Z80_ldd,                            },
  { 0xA9, Z80_cpd,                            },
  { 0xAA, Z80_ind,                            },
  { 0xAB, Z80_outd,                           },
  { 0xAC, Z80_xorw,   rHL,        rBC         },
  { 0xAD, Z80_xorw,   rHL,        rDE         },
  { 0xAE, Z80_xorw,   rHL,        imm16       },
  { 0xAF, Z80_xorw,   rHL,        rHL         },
  { 0xB0, Z80_ldir,                           },
  { 0xB1, Z80_cpir,                           },
  { 0xB2, Z80_inir,                           },
  { 0xB3, Z80_otir,                           },
  { 0xB4, Z80_orw,    rHL,        rBC         },
  { 0xB5, Z80_orw,    rHL,        rDE         },
  { 0xB6, Z80_orw,    rHL,        imm16       },
  { 0xB7, Z80_orw,    rHL,        rHL         },
  { 0xB8, Z80_lddr,                           },
  { 0xB9, Z80_cpdr,                           },
  { 0xBA, Z80_indr,                           },
  { 0xBB, Z80_otdr,                           },
  { 0xBC, Z80_cpw,    rHL,        rBC         },
  { 0xBD, Z80_cpw,    rHL,        rDE         },
  { 0xBE, Z80_cpw,    rHL,        imm16       },
  { 0xBF, Z80_cpw,    rHL,        rHL         },
  { 0xC0, Z80_ldctl,  rHL,        rSR         },
  { 0xC1, Z80_pop,    rSR                     },
  { 0xC4, Z80_calr,   ccNZ,       rip8        },
  { 0xC5, Z80_push,   rSR                     },
  { 0xC6, Z80_add,    rHL,        mem16       },
  { 0xC8, Z80_ldctl,  rSR,        rHL         },
  { 0xCC, Z80_calr,   ccZ,        rip8        },
  { 0xCD, Z80_calr,   rip8                    },
  { 0xCF, Z80_btest,                          },
  { 0xD0, Z80_ldctl,  rA,         rDSR        },
  { 0xD3, Z80_outa,   mem16,      rA          },
  { 0xD4, Z80_calr,   ccNC,       rip8        },
  { 0xD6, Z80_sub,    rHL,        mem16       },
  { 0xD8, Z80_ldctl,  rDSR,       rA          },
  { 0xD9, Z80_exall,                          },
  { 0xDA, Z80_ldctl,  rDSR,       imm8        },
  { 0xDB, Z80_ina,    rA,         mem16       },
  { 0xDC, Z80_calr,   ccC,        rip8        },
  { 0xE0, Z80_ldiw,                           },
  { 0xE2, Z80_iniw,                           },
  { 0xE3, Z80_outiw,                          },
  { 0xE4, Z80_calr,   ccPO,       rip8        },
  { 0xE8, Z80_lddw,                           },
  { 0xEA, Z80_indw,                           },
  { 0xEB, Z80_outdw,                          },
  { 0xEC, Z80_calr,   ccPE,       rip8        },
  { 0xF0, Z80_ldirw,                          },
  { 0xF2, Z80_inirw,                          },
  { 0xF3, Z80_otirw,                          },
  { 0xF4, Z80_calr,   ccP,        rip8        },
  { 0xF7, Z80_setc,   rLCK                    },
  { 0xF8, Z80_lddrw,                          },
  { 0xFA, Z80_indrw,                          },
  { 0xFB, Z80_otdrw,                          },
  { 0xFC, Z80_calr,   ccM,        rip8        },
  { 0xFF, Z80_resc,   rLCK                    },
};

static const insndesc_t cmdsEDCB[] =
{
  { 0x00, Z80_rlcw,   rBC                     },
  { 0x01, Z80_rlcw,   rDE                     },
  { 0x02, Z80_rlcw,   atHL                    },
  { 0x03, Z80_rlcw,   rHL                     },
  { 0x04, Z80_rlcw,   rIX                     },
  { 0x05, Z80_rlcw,   rIY                     },
  { 0x08, Z80_rrcw,   rBC                     },
  { 0x09, Z80_rrcw,   rDE                     },
  { 0x0A, Z80_rrcw,   atHL                    },
  { 0x0B, Z80_rrcw,   rHL                     },
  { 0x0C, Z80_rrcw,   rIX                     },
  { 0x0D, Z80_rrcw,   rIY                     },
  { 0x10, Z80_rlw,    rBC                     },
  { 0x11, Z80_rlw,    rDE                     },
  { 0x12, Z80_rlw,    atHL                    },
  { 0x13, Z80_rlw,    rHL                     },
  { 0x14, Z80_rlw,    rIX                     },
  { 0x15, Z80_rlw,    rIY                     },
  { 0x18, Z80_rrw,    rBC                     },
  { 0x19, Z80_rrw,    rDE                     },
  { 0x1A, Z80_rrw,    atHL                    },
  { 0x1B, Z80_rrw,    rHL                     },
  { 0x1C, Z80_rrw,    rIX                     },
  { 0x1D, Z80_rrw,    rIY                     },
  { 0x20, Z80_slaw,   rBC                     },
  { 0x21, Z80_slaw,   rDE                     },
  { 0x22, Z80_slaw,   atHL                    },
  { 0x23, Z80_slaw,   rHL                     },
  { 0x24, Z80_slaw,   rIX                     },
  { 0x25, Z80_slaw,   rIY                     },
  { 0x28, Z80_sraw,   rBC                     },
  { 0x29, Z80_sraw,   rDE                     },
  { 0x2A, Z80_sraw,   atHL                    },
  { 0x2B, Z80_sraw,   rHL                     },
  { 0x2C, Z80_sraw,   rIX                     },
  { 0x2D, Z80_sraw,   rIY                     },
  { 0x30, Z80_ex,     rBC,        rBC2        },
  { 0x31, Z80_ex,     rDE,        rDE2        },
  { 0x33, Z80_ex,     rHL,        rHL2        },
  { 0x34, Z80_ex,     rIX,        rIX2        },
  { 0x35, Z80_ex,     rIY,        rIY2        },
  { 0x38, Z80_srlw,   rBC                     },
  { 0x39, Z80_srlw,   rDE                     },
  { 0x3A, Z80_srlw,   atHL                    },
  { 0x3B, Z80_srlw,   rHL                     },
  { 0x3C, Z80_srlw,   rIX                     },
  { 0x3D, Z80_srlw,   rIY                     },
  { 0x90, Z80_multw,  rHL,        rBC         },
  { 0x91, Z80_multw,  rHL,        rDE         },
  { 0x93, Z80_multw,  rHL,        rHL         },
  { 0x94, Z80_multw,  rHL,        rIX         },
  { 0x95, Z80_multw,  rHL,        rIY         },
  { 0x97, Z80_multw,  rHL,        imm16       },
  { 0x98, Z80_multuw, rHL,        rBC         },
  { 0x99, Z80_multuw, rHL,        rDE         },
  { 0x9B, Z80_multuw, rHL,        rHL         },
  { 0x9C, Z80_multuw, rHL,        rIX         },
  { 0x9D, Z80_multuw, rHL,        rIY         },
  { 0x9F, Z80_multuw, rHL,        imm16       },
  { 0xB8, Z80_divuw,  rHL,        rBC         },
  { 0xB9, Z80_divuw,  rHL,        rDE         },
  { 0xBB, Z80_divuw,  rHL,        rHL         },
  { 0xBC, Z80_divuw,  rHL,        rIX         },
  { 0xBD, Z80_divuw,  rHL,        rIY         },
  { 0xBF, Z80_divuw,  rHL,        imm16       },
};

//------------------------------------------------------------------------
void z80_t::load_z80_operand(insn_t &insn, op_t &x, uchar op)
{
  if ( op == 0 )
    return;
  switch ( op )
  {
    case rBC:
    case rDE:
    case rHL:
    case rIX:
    case rIY:
    case rBC2:
    case rDE2:
    case rHL2:
    case rIX2:
    case rIY2:
    case rSP:
    case rLW:
    case rIXL:
    case rIXU:
    case rDSR:
    case rXSR:
    case rIYL:
    case rIYU:
    case rYSR:
    case rSR:
    case rIB:
    case rIW:
    case rXM:
    case rLCK:
      x.dtype = dt_word;
      [[fallthrough]];
    case rA:
    case rB:
    case rC:
    case rD:
    case rE:
    case rH:
    case rL:
    case rI:
    case rW:
    case rR:
      x.type = o_reg;
      x.reg = op - 1;
      break;

    case atBC:
      x.type = o_phrase;
      x.phrase = R_bc;
      break;
    case atDE:
      x.type = o_phrase;
      x.phrase = R_de;
      break;
    case atHL:
      x.type = o_phrase;
      x.phrase = R_hl;
      break;
    case atSP:
      x.type = o_phrase;
      x.phrase = R_sp;
      break;
    case atIX:
      x.type = o_phrase;
      x.phrase = R_ix;
      break;
    case atIY:
      x.type = o_phrase;
      x.phrase = R_iy;
      break;
    case atC:
      x.type = o_phrase;
      x.phrase = R_c;
      break;

    case rip8:
      {
        sval_t disp = char(insn.get_next_byte());
        x.type = o_near;
        x.addr = insn.ip + insn.size + disp;
      }
      break;
    case rip16:
      {
        sval_t disp = short(insn.get_next_word());
        x.type = o_near;
        x.addr = insn.ip + insn.size + disp;
      }
      break;
    case rip24:
      {
        sval_t disp = insn.get_next_word();
        disp |= (sval_t((char)(insn.get_next_byte())) << 16);
        x.type = o_near;
        x.addr = insn.ip + insn.size + disp;
      }
      break;

    case imm8:
      x.type = o_imm;
      x.value = insn.get_next_byte();
      break;

    case imm16:
      x.type = o_imm;
      x.dtype = dt_word;
      x.value = insn.get_next_word();
      break;

    case mem16:
      x.type = o_mem;
      x.addr = insn.get_next_word();
      break;

    case ix8:
      x.type = o_displ;
      x.phrase = R_ix;
      x.addr = insn.get_next_byte();
      break;
    case iy8:
      x.type = o_displ;
      x.phrase = R_iy;
      x.addr = insn.get_next_byte();
      break;
    case ixs:
      x.type = o_displ;
      x.phrase = R_ix;
      x.addr = saved_value;
      break;
    case iys:
      x.type = o_displ;
      x.phrase = R_iy;
      x.addr = saved_value;
      break;
    case sps:
      x.type = o_displ;
      x.phrase = R_sp;
      x.addr = saved_value;
      break;

    case ccNZ:
    case ccZ:
    case ccNC:
    case ccC:
    case ccPO:
    case ccPE:
    case ccP:
    case ccM:
      x.type = o_cond;
      x.Cond = op - ccNZ;
      break;

    case c0:
    case c1:
    case c2:
    case c3:
    case c4:
    case c5:
    case c6:
    case c7:
      x.type = o_imm;
      x.value = op - c0;
      break;

    default:
      warning("%a: interr in z380, code=%x", insn.ea, code);
  }
}

//------------------------------------------------------------------------
bool z80_t::search_map(insn_t &insn, const insndesc_t *map, uchar _code)
{
  for ( int i=0; map[i].itype; i++ )
  {
    if ( map[i].code > _code )
      break;
    if ( map[i].code == _code )
    {
      insn.itype = map[i].itype;
      load_z80_operand(insn, insn.Op1, map[i].op1);
      load_z80_operand(insn, insn.Op2, map[i].op2);
      return true;
    }
  }
  return false;
}

//------------------------------------------------------------------------
bool z80_t::z380_insns(insn_t &insn, const insndesc_t *map, const insndesc_t *cb)
{
  code = insn.get_next_byte();
  if ( code == 0xCB )
  {
    map = cb;
    saved_value = insn.get_next_byte();
    code = insn.get_next_byte();
  }
  return search_map(insn, map, (uchar)code);
}

//------------------------------------------------------------------------
bool z80_t::z380_ED(insn_t &insn)
{
  const insndesc_t *map = cmdsED;
  code = insn.get_next_byte();
  if ( code == 0xCB )
  {
    map = cmdsEDCB;
    code = insn.get_next_byte();
  }
  return search_map(insn, map, (uchar)code);
}

//------------------------------------------------------------------------
void z80_t::z80_ixcommands(insn_t &insn, bool _isx)
{
  if ( isGB() )
    return;
  if ( isZ380() )
  {
    if ( _isx )
      z380_insns(insn, cmdsDD, cmdsDDCB);
    else
      z380_insns(insn, cmdsFD, cmdsFDCB);
    return;
  }

  isx = _isx;
  code = insn.get_next_byte();
  switch ( (code>>4) & 0xF )
  {
    case 0:                             /* 0000???? */
    case 1:                             /* 0001???? */
      if ( (code & 0xF) == 9 )
      {
        insn.itype = I5_add;
        op_ix(insn.Op1);
        op_ss(insn.Op2);
        insn.Op1.dtype = dt_word;
        insn.Op2.dtype = dt_word;
      }
      break;
    case 2:                             /* 0010???? */
      insn.Op1.dtype = dt_word;
      insn.Op2.dtype = dt_word;
      (code & 4) ? op_ibyte(insn.Op1,code & 8) : op_ix(insn.Op1);
      switch ( code & 0xF )
      {
        case 1:
          insn.itype = I5_lxi;
          op_nn(insn, insn.Op2);
          break;
        case 2:
          insn.itype = I5_mov;
          op_mm(insn, insn.Op1);
          op_ix(insn.Op2);
          break;
        case 3:
          insn.itype = I5_inx;
          break;
        case 4:
        case 0xC:
          insn.itype = I5_inr;
          break;
        case 5:
        case 0xD:
          insn.itype = I5_dcr;
          break;
        case 6:
        case 0xE:
          insn.itype = I5_mvi;
          op_n(insn, insn.Op2);
          break;
        case 9:
          insn.itype = I5_add;
          op_ix(insn.Op2);
          break;
        case 0xA:
          insn.itype = I5_mov;
          op_mm(insn, insn.Op2);
          break;
        case 0xB:
          insn.itype = I5_dcx;
          break;
      }
      break;
    case 3:
      switch ( code & 0xF )
      {
        case 4:
          insn.itype = I5_inr;
          op_xdispl(insn, insn.Op1);
          break;
        case 5:
          insn.itype = I5_dcr;
          op_xdispl(insn, insn.Op1);
          break;
        case 6:
          insn.itype = I5_mvi;
          op_xdispl(insn, insn.Op1);
          op_n(insn, insn.Op2);
          break;
        case 9:
          insn.itype = I5_add;
          op_ix(insn.Op1);
          op_ss(insn.Op2);
          insn.Op1.dtype = dt_word;
          insn.Op2.dtype = dt_word;
          break;
      }
      break;
    case 4:
    case 5:
      if ( (code & 6) == 4 )
      {
        insn.itype = I5_mov;
        op_xr1(insn.Op1);
        op_ibyte(insn.Op2,code & 1);
        break;
      }
      /* no break */
    case 6:
      if ( (code & 6) == 4 )
        break;
      if ( (code & 7) == 6 )
      {
        if ( op_xr1(insn.Op1) )
          break;   // mem,mem not allowed
        if ( code == 0x66 )
          insn.Op1.reg = R_h;
        if ( code == 0x6E )
          insn.Op1.reg = R_l;
        op_xdispl(insn, insn.Op2);
        insn.itype = I5_mov;
        break;
      }
      if ( (code & 0xF0) == 0x60 )
      {
        op_ibyte(insn.Op1,code & 8);
        op_xr2(insn.Op2);
        insn.itype = I5_mov;
      }
      break;
    case 7:
      switch ( code & 0xF )
      {
        case 0: case 1: case 2: case 3: case 4: case 5: case 7:
          op_xdispl(insn, insn.Op1);
          if ( !op_xr2(insn.Op2) )
            insn.itype = I5_mov;
          if ( code == 0x74 )
            insn.Op2.reg = R_h;
          if ( code == 0x75 )
            insn.Op2.reg = R_l;
          break;
        case 0xE:
          op_xdispl(insn, insn.Op2);
          op_a(insn.Op1);
          insn.itype = I5_mov;           // to show all regs
          break;
        case 0xC:
        case 0xD:
          op_ibyte(insn.Op2,code & 1);
          op_a(insn.Op1);
          insn.itype = I5_mov;           // to show all regs
          break;
      }
      break;
    case 8:
    case 9:
    case 0xA:
    case 0xB:
      if ( (code & 4) == 4 && (code & 7) != 7 )
      {
        int type = (code >> 3) & 7;
        insn.itype = W[type];
        op_a(insn.Op1);
        ((code & 7) == 6) ? op_xdispl(insn, insn.Op2)
                          : op_ibyte(insn.Op2,code & 1);
//      if ( type == 0 || type == 1 || type == 3 ) insn.Op1.clr_show();
      }
      break;
    case 0xC:
      if ( code == 0xCB )
      {
        op_xdispl(insn, insn.Op2);
        code = insn.get_next_byte();
        if ( (code & 7) == 6 )
        {
          int type = (code>>6) & 3;
          if ( type == 0 )
          {
            insn.itype = CBrols[ (code >> 3) & 7 ];
            op_a(insn.Op1);
          }
          else
          {
            static const uint16 brs[] = { I5_null, Z80_bit, Z80_res, Z80_set };
            insn.itype = brs[type];
            insn.Op1.type = o_imm;
            insn.Op1.value = (code >> 3) & 7;
          }
        }
      }
      break;
    case 0xE:
      switch ( code & 0xF )
      {
        case 1:
          insn.itype = I5_pop;
          op_ix(insn.Op1);
          break;
        case 3:
          insn.itype = Z80_ex;
          insn.Op1.type   = o_phrase;
          insn.Op1.phrase = R_sp;
          insn.Op1.dtype  = dt_word;
          op_ix(insn.Op2);
          break;
        case 5:
          insn.itype = I5_push;
          op_ix(insn.Op1);
          break;
        case 9:
          insn.itype = Z80_jp;
          insn.Op1.type = o_cond;
          insn.Op1.Cond = oc_not;
          insn.Op2.type   = o_phrase;
          insn.Op2.phrase = isx ? R_ix : R_iy;
          insn.Op2.dtype  = dt_code;
          break;
        case 0xD:
          code = insn.get_next_byte();
          switch ( code )
          {
            case 0x42:
            case 0x4A:
            case 0x52:
            case 0x5A:
            case 0x62:
            case 0x6A:
            case 0x72:
            case 0x7A:
              insn.itype = (code & 8) ? I5_adc : Z80_sbc;
              op_ix(insn.Op1);
              op_ss(insn.Op2);
              if ( insn.Op2.reg == R_hl )
                op_ix(insn.Op2);
              break;
            case 0x60:
            case 0x68:
              insn.itype = I5_in;
              op_ibyte(insn.Op1,code & 8);
              op_c(insn.Op2);
              break;
            case 0x61:
            case 0x69:
              insn.itype = I5_out;
              op_c(insn.Op1);
              op_ibyte(insn.Op2,code & 8);
              break;
          }
          break;
      }
      // fallthrough
    case 0xF:
      if ( code == 0xF9 )
      {
        insn.itype = I5_mov;
        insn.Op1.type   = o_reg;
        insn.Op1.phrase = R_sp;
        insn.Op1.dtype  = dt_word;
        op_ix(insn.Op2);
      }
      break;
  }
}


//------------------------------------------------------------------------
void z80_t::z80_misc(insn_t &insn)
{
  code = insn.get_next_byte();
  switch ( code )
  {
    case 0x40:
    case 0x48:
    case 0x50:
    case 0x58:
    case 0x60:
    case 0x68:
    case 0x78:
      insn.itype = I5_in;
      op_r1(insn.Op1);
      op_c(insn.Op2);
      break;
    case 0x41:
    case 0x49:
    case 0x51:
    case 0x59:
    case 0x61:
    case 0x69:
    case 0x79:
      insn.itype = I5_out;
      op_c(insn.Op1);
      op_r1(insn.Op2);
      break;
    case 0x42:
    case 0x4A:
    case 0x52:
    case 0x5A:
    case 0x62:
    case 0x6A:
    case 0x72:
    case 0x7A:
      insn.itype = (code & 8) ? I5_adc : Z80_sbc;
      insn.Op1.type  = o_reg;
      insn.Op1.reg   = R_hl;
      insn.Op1.dtype = dt_word;
      op_ss(insn.Op2);
      break;
    case 0x43:
    case 0x53:
    case 0x73:
      insn.itype = I5_mov;
      op_mm(insn, insn.Op1);
      op_ss(insn.Op2);
      insn.Op1.dtype  = dt_word;
      insn.Op2.dtype  = dt_word;
      break;
    case 0x44:  insn.itype = Z80_neg;    break;
    case 0x45:  insn.itype = Z80_retn;   break;
    case 0x46:
      insn.itype = Z80_im;
      insn.Op1.type = o_imm;
      insn.Op1.value = 0;
      break;
    case 0x47:
      insn.itype = I5_mov;               // to show all regs
      insn.Op1.type = o_reg;
      insn.Op1.reg = R_i;
      op_a(insn.Op2);
      break;
    case 0x4B:
    case 0x5B:
    case 0x7B:
      insn.itype = I5_mov;
      op_ss(insn.Op1);
      op_mm(insn, insn.Op2);
      insn.Op1.dtype  = dt_word;
      insn.Op2.dtype  = dt_word;
      break;
    case 0x4D:
      insn.itype = Z80_reti;
      break;
    case 0x4F:
      insn.itype = I5_mov;               // to show all regs
      insn.Op1.type = o_reg;
      insn.Op1.reg = R_r;
      op_a(insn.Op2);
      break;
    case 0x56:
      insn.itype = Z80_im;
      insn.Op1.type = o_imm;
      insn.Op1.value = 1;
      break;
    case 0x5E:
      insn.itype = Z80_im;
      insn.Op1.type = o_imm;
      insn.Op1.value = 2;
      break;
    case 0x57:
      insn.itype = I5_mov;               // to show all regs
      op_a(insn.Op1);
      insn.Op2.type = o_reg;
      insn.Op2.reg = R_i;
      break;
    case 0x5F:
      insn.itype = I5_mov;               // to show all regs
      op_a(insn.Op1);
      insn.Op2.type = o_reg;
      insn.Op2.reg = R_r;
      break;
    case 0x67:  insn.itype = Z80_rrd;    break;
    case 0x6F:  insn.itype = Z80_rld;    break;

    case 0xA0:  insn.itype = Z80_ldi;    break;
    case 0xA1:  insn.itype = Z80_cpi;    break;
    case 0xA2:  insn.itype = Z80_ini;    break;
    case 0xA3:  insn.itype = Z80_outi;   break;
    case 0xA8:  insn.itype = Z80_ldd;    break;
    case 0xA9:  insn.itype = Z80_cpd;    break;
    case 0xAA:  insn.itype = Z80_ind;    break;
    case 0xAB:  insn.itype = Z80_outd;   break;
    case 0xB0:  insn.itype = Z80_ldir;   break;
    case 0xB1:  insn.itype = Z80_cpir;   break;
    case 0xB2:  insn.itype = Z80_inir;   break;
    case 0xB3:  insn.itype = Z80_otir;   break;
    case 0xB8:  insn.itype = Z80_lddr;   break;
    case 0xB9:  insn.itype = Z80_cpdr;   break;
    case 0xBA:  insn.itype = Z80_indr;   break;
    case 0xBB:  insn.itype = Z80_otdr;   break;
//
//      HD64180 extensions
//
    case 0x76:  if ( is64180() ) insn.itype = HD_slp;    break;
    case 0x83:  if ( is64180() ) insn.itype = HD_otim;   break;
    case 0x93:  if ( is64180() ) insn.itype = HD_otimr;  break;
    case 0x8B:  if ( is64180() ) insn.itype = HD_otdm;   break;
    case 0x9B:  if ( is64180() ) insn.itype = HD_otdmr;  break;
    case 0x64:
      if ( is64180() )
      {
        insn.itype = HD_tst;
        op_n(insn, insn.Op1);
      }
      break;
    case 0x74:
      if ( is64180() )
      {
        insn.itype = HD_tstio;
        op_n(insn, insn.Op1);
      }
      break;
    default:
// I did not find an assembler that understands this...
//      if ( (code & 0xC0) == 0x40 && (code & 6) == 0 )        // undocumented
//      {
//        insn.itype = (code & 1) ? I5_out : I5_in;
//        op_r1(insn.Op1);
//        break;
//      }
//--------
      if ( is64180() )
      {
        switch ( code & 0xC0 )
        {
          case 0:
            switch ( code & 7 )
            {
              case 0:
                insn.itype = HD_in0;
                op_r1(insn.Op1);
                op_n(insn, insn.Op2);
                if ( insn.Op1.type == o_phrase )
                  op_f(insn.Op1);
                break;
              case 1:
                insn.itype = HD_out0;
                op_n(insn, insn.Op1);
                op_r1(insn.Op2);
                break;
              case 4:
                insn.itype = HD_tst;
                op_r1(insn.Op1);
                break;
            }
            break;
          case 0x40:
            if ( code == 0x70 )
            {
              insn.itype = I5_in;
              op_f(insn.Op1);
              op_c(insn.Op2);
              break;
            }
            if ( (code & 0xF) == 0xC )
            {
              insn.itype = HD_mlt;
              op_ss(insn.Op1);
            }
            break;
        }
      }
      break;
  }
}
