/*
 *      TLCS900 processor module for IDA.
 *      Copyright (c) 1998-2006 Konstantin Norvatoff, <konnor@bk.ru>
 *      Freeware.
 */

#include "tosh.hpp"

// (number of bytes -1) -> dtype
static const uchar bt_type[4]= { dt_byte, dt_word, dt_tbyte, dt_dword };

//  power of 2 -> dtype
static const uchar btp_type[4]= { dt_byte, dt_word, dt_dword, uchar(-1) };

// memory reference (o_mem/o_displ)
struct MemRefDef
{
  uint32 disp;      // offset
  ushort off_pos;   // place of offset in insn (if any)
  optype_t type;    // dtype: o_mem/o_displ
  uchar flags;      // flags
  uchar base_reg;   // base reg
  uchar add_reg;    // additional reg (DAfull number)
  uchar inc_size;   // increment size (+/-4)
  uchar dtype;
};

//-----------------------------------------------------------------------------
// short reg to full reg
static uchar Reg7ToFull(uchar reg7, uchar size)
{
  reg7&=7;    // fix reg number
  // byte reg
  if ( size == 0 )
    return 0xE0+(1-(reg7&1))+(reg7&6)*2;
  // word or double wor dreg
  return 0xE0+reg7*4;
}

//-----------------------------------------------------------------------------
// set number of the reg into operand
// reg_code - byte reg num
// size - 0,1,2 (2^x bytes)
static void SetRegistr(op_t &op, uchar reg_code, uchar size)
{
  op.type=o_reg;
  op.addr=op.value=reg_code;
  op.dtype = btp_type[size&3];
}

//-----------------------------------------------------------------------------
// set register
// regcode - reg number (3 bits)
// size - 0,1,2 (2^x bytes)
static void SetRegistr7(op_t &op, uchar regcode, uchar size)
{
  SetRegistr(op, Reg7ToFull(regcode, size), size);
}

//-----------------------------------------------------------------------------
// load N ibytes and return result
static uint32 LoadDataValue(insn_t &insn, int bytes)
{
  uint32 val=0;
  for ( int i=0; i < bytes; i++ )
    val |= ((uint32)insn.get_next_byte())<<(8*i);
  return val;
}

//-----------------------------------------------------------------------------
// currnet bytes are the memory address
// len - number of bytes
static void SetDirectMemRef(insn_t &insn, op_t &op, int len)
{
  op.type = o_mem;
  // elem offset
  op.offb = (uchar)insn.size;
  // size of offset
  op.dtype = bt_type[(len-1)&3];
  // elem value
  op.addr = op.value = LoadDataValue(insn, len);
}

//-----------------------------------------------------------------------------
// code ref
static void SetJmp(insn_t &insn, op_t &op, int len)
{
  op.type = o_near;
  op.offb = (uchar)insn.size;
  op.dtype = dt_dword;
  if ( len > 0 )
  {
    // absolute
    op.addr = op.value = LoadDataValue(insn, len);
  }
  else
  {
    // relative
    len = -len;
    op.addr = LoadDataValue(insn, len);
    // sig
    if ( op.addr & (uval_t(1)<<(8*len-1)) )
    {
      op.addr |= BADADDR<<(8*len);
    }
    // target offset
    op.addr += insn.ip + insn.size;
    op.value = op.addr;
  }
}

//-----------------------------------------------------------------------------
// MemRef to Operand
static void MemRefToOp(op_t &op, const MemRefDef &mr)
{
  op.value     = mr.disp;
  op.addr      = mr.disp;
  op.dtype     = mr.dtype;
  op.reg       = mr.base_reg;
  op.specflag2 = mr.inc_size;
  op.offb      = (uchar)mr.off_pos;
  op.specval_shorts.low = mr.add_reg;
  op.specflag1 = mr.flags;
  op.type      = mr.type;
}

//-----------------------------------------------------------------------------
// load memory ref
// first_code - firt insn byte
static int LoadMemRef(insn_t &insn, MemRefDef &mr, uchar first_code)
{
  memset(&mr, 0, sizeof(mr));
  mr.dtype = btp_type[(first_code>>4)&3];
  if ( (first_code&0x40) == 0 )
  {
    // ref is reg (with offset or not)
    mr.type = o_displ;
    // reg name
    mr.base_reg = Reg7ToFull(first_code, 2);
    if ( first_code&0x8 )
    {
      // offset
      mr.off_pos = insn.size;
      mr.disp = insn.get_next_byte();
    }
  }
  else
  {
    switch ( first_code & 7 )
    {
      // direct, byte
      case 0:
        mr.off_pos = insn.size;
        mr.disp = LoadDataValue(insn, 1);
        mr.type = o_mem;
        break;
      // direct, word
      case 1:
        mr.off_pos = insn.size;
        mr.disp = LoadDataValue(insn, 2);
        mr.type = o_mem;
        break;
      // direct, 24 bit
      case 2:
        mr.off_pos=insn.size;
        mr.disp=LoadDataValue(insn, 3);
        mr.type=o_mem;
        break;
      // two regs
      case 3:
        {
          uchar mem_val;
          mr.type = o_displ;
          mem_val = insn.get_next_byte();
          if ( (mem_val&2) == 0 )
          {
            // with reg
            mr.base_reg = mem_val & 0xFC;
            // and ofsset?
            if ( mem_val&1 )
            {
              mr.off_pos = insn.size;
              mr.disp = LoadDataValue(insn, 2);
            }
          }
          else
          { // two regs
            if ( (mem_val&1) == 0 )
              return 0; // wrong
            if ( (mem_val>>2) > 1 )
            {
              // LDAR!
              // check for F3/13
              //msg("Ldar Op");
              if ( first_code == 0xF3 && mem_val == 0x13 )
              {
                // yes!
                insn.itype=T900_ldar;
                // elem offset
                insn.Op2.offb=(uchar)insn.size;
                uint32 target=LoadDataValue(insn, 2);
                target+=uint32(insn.ea+4);
                insn.Op2.type=o_mem;
                // size of offset
                insn.Op2.dtype = dt_word;
                // elem value
                insn.Op2.addr = insn.Op2.value = target;
                // get reg
                mem_val=insn.get_next_byte();
                // available?
                if ( (mem_val&0xE8) != 0x20 )
                  return 0;
                SetRegistr7(insn.Op1, mem_val, ((mem_val>>4)-1)&3);
                //msg("ldar ok");
                return 1;
              }
              return 0;
            }
            mr.base_reg = insn.get_next_byte();     // 1st reg
            mr.add_reg  = insn.get_next_byte();     // 2nd reg
            if ( mem_val & 0x4 )
              mr.flags|=URB_WORD;// 2nd reg - word
          }
        }
        break;
      // inc/dec
      case 4:
      case 5:
        {
          uchar regg = insn.get_next_byte();
          if ( (regg&3) == 3 )
            return 0;
          mr.type = o_displ;
          mr.base_reg = regg&0xFC;
          mr.inc_size = 1<<(regg&3);
          // negative inc
          if ( (first_code&1) == 0 )
            mr.inc_size|=URB_DECR;
        }
        break;
    }
  }
  return 1;
}

//-----------------------------------------------------------------------------
static void SetImmData(insn_t &insn, op_t &op, int bytes)
{
  op.type = o_imm;
  op.offb = (uchar)insn.size;
  op.dtype = bt_type[(bytes-1)&3];
  op.addr = op.value = LoadDataValue(insn, bytes);
}

//-----------------------------------------------------------------------------
static void SetImm8Op(op_t &op, uchar code)
{
  op.type = o_imm;
  op.dtype = dt_byte;      // actually, it is not a byte
  op.flags |= OF_NUMBER;
  op.addr = op.value = code;
}

//-----------------------------------------------------------------------------
// set imm3 for inc/dec
static void SetImm3Op(op_t &op, uchar code)
{
  code &= 7;
  SetImm8Op(op, code ? code : 8);
}

//-----------------------------------------------------------------------------
// condition phrase
static void SetCondOp(op_t &op, int cond)
{
  static const uchar cond_p[16] =
  {
    fCF, fCLT, fCLE, fCULE, fCPE, fCMI, fCZ,  fCC,
    fCT, fCGE, fCGT, fCUGT, fCPO, fCPL, fCNZ, fCNC
  };
  op.type = o_phrase;
  op.phrase = cond_p[cond&0xf];
}

//-----------------------------------------------------------------------------
// arith insns
static const uchar Add_List[8] =
{
  T900_add, T900_adc, T900_sub, T900_sbc,
  T900_and, T900_xor, T900_or, T900_cp
};

// shift insns (not simple)
static const uchar Shift_List[8] =
{
  T900_rlc, T900_rrc, T900_rl, T900_rr,
  T900_sla, T900_sra, T900_sll, T900_srl
};

// shift for memory cells
static const uchar Shift_List1[8] =
{
  T900_rlc_mem, T900_rrc_mem, T900_rl_mem, T900_rr_mem,
  T900_sla_mem, T900_sra_mem, T900_sll_mem, T900_srl_mem
};

// flag C
static const uchar COp_List[5] =
{
  T900_andcf, T900_orcf, T900_xorcf, T900_ldcf, T900_stcf
};

// other flag C
static const uchar COp2_List[5] =
{
  T900_res, T900_set, T900_chg, T900_bit, T900_tset
};

//-----------------------------------------------------------------------------
// parse regs
static int RegAnalyser(insn_t &insn, uchar code)
{
  static const uchar reg_codes[32] =
  {
    255,        255,        255,           255,
    T900_andcf, T900_andcf, T900_res,      T900_minc1,
    T900_mul,   T900_muls,  T900_div,      T900_divs,
    T900_inc,   T900_dec,   T900_scc,      T900_scc,
    T900_add,   T900_ld,    T900_adc,      T900_ld,
    T900_sub,   T900_ld,    T900_sbc,      T900_ex,
    T900_and,   254,        T900_xor,      253,
    T900_or,    T900_rlc,   T900_cp,       T900_rlc
  };
  uchar reg_size = (code>>4) & 3;  // 0 - byte, 1 - word, 2 - long
  uchar reg_num;         // byte reg number
  if ( code & 8 )
  {
    reg_num = Reg7ToFull(code, reg_size);
  }
  else
  { // aux byte
    reg_num = insn.get_next_byte();
  }
  uchar reg_op = 0;        // Op1 is reg by default
  uchar reg_byte = insn.get_next_byte();
  insn.itype = reg_codes[(reg_byte>>3)&0x1F];
  switch ( insn.itype )
  {
    case T900_ex:
    case T900_add:
    case T900_adc:
    case T900_sub:
    case T900_sbc:
    case T900_and:
    case T900_xor:
    case T900_or:
    case T900_cp:
      SetRegistr7(insn.Op1, reg_byte, reg_size);
      reg_op=1;
      break;

    case 255:
      {
        static const uchar LCodes[] =
        {
          0,         0,         0,         T900_ld,
          T900_push, T900_pop,  T900_cpl,  T900_neg,
          T900_mul,  T900_muls, T900_div,  T900_divs,
          T900_link, T900_unlk, T900_bs1f, T900_bs1b,
          T900_daa,  0,         T900_extz, T900_exts,
          T900_paa,  0,         T900_mirr, 0,
          0,         T900_mula, 0,         0,
          T900_djnz, 0,         0,         0
        };

        if ( reg_byte >= qnumber(LCodes) )
          return 0;

        insn.itype = LCodes[reg_byte];
        switch ( insn.itype )
        {
          // illegal
          case 0:
            return 0;

          // LD r, #
          case T900_ld:
            SetImmData(insn, insn.Op2, 1<<reg_size);
            break;

          // MUL rr, #
          // DIV rr, #
          case T900_div:
          case T900_divs:
          case T900_mul:
          case T900_muls:
            SetImmData(insn, insn.Op2, 1<<reg_size);
            // hig reg used
            reg_size++;
            if ( reg_size == 3 )
              return 0;
            break;

          // LINK r, dd
          case T900_link:
            SetImmData(insn, insn.Op2, 2);
            break;

          // BS1F A,r
          case T900_bs1f:
          case T900_bs1b:
            SetRegistr7(insn.Op1, 1, 0);
            reg_op=1;
            break;
          // MULA r
          case T900_mula:
            // high reg used
            reg_size++;
            if ( reg_size == 3 )
              return 0;
            break;
          // DJNZ r, d
          case T900_djnz:
            SetJmp(insn, insn.Op2, -1);
            break;
        }
      }
      break;

    // ANDCF-STCF  XXX #, r
    case T900_andcf:
      if ( reg_byte > 0x2C )
      {
        switch ( reg_byte )
        {
          case 0x2D:
            return 0;
            // compilcated insn LDC - skip it for now
          case 0x2E:
            SetImmData(insn, insn.Op1, 1);
            reg_op=1;
            break;
          case 0x2F:
            SetImmData(insn, insn.Op2, 1);
            break;
        }
        insn.itype=T900_ldc;
      }
      else if ( (reg_byte&7) < 5 ) // not an LDC
      {
        reg_op = 1;
        insn.itype = COp_List[reg_byte&7];
        if ( reg_byte & 8 )
          SetRegistr7(insn.Op1, 1, 0);
        else
          SetImmData(insn, insn.Op1, 1);
      }
      else
      {
        return 0;
      }
      break;

    // RES-TSET
    case T900_res:
      if ( (reg_byte&7) > 4 )
        return 0;
      insn.itype = COp2_List[reg_byte&7];
      SetImmData(insn, insn.Op1, 1);
      reg_op = 1;
      break;

    // MINC/MDEC
    case T900_minc1:
      {
        static const uchar dinc[8] =
        {
          T900_minc1, T900_minc2, T900_minc4, 0,
          T900_mdec1, T900_mdec2, T900_mdec4, 0
        };
        if ( (insn.itype=dinc[reg_byte&7]) == 0 )
          return 0;
        SetImmData(insn, insn.Op1, 2);
        // fix op
        insn.Op1.value += uval_t(1)<<(reg_byte&3);
        insn.Op1.addr = insn.Op1.value;
        reg_op = 1;
      }
      break;
    // mul/div  XXX R, r
    case T900_mul:
    case T900_muls:
    case T900_div:
    case T900_divs:
      SetRegistr7(insn.Op1, reg_size == 0 ? (reg_byte&7)/2 : reg_byte, reg_size+1);
      reg_op=1;
      break;

    // INC/DEC #3, r
    case T900_inc:
    case T900_dec:
      SetImm3Op(insn.Op1, reg_byte);
      reg_op=1;
      break;

    // set SCC, r
    case T900_scc:
      SetCondOp(insn.Op1, reg_byte&0xF);
      reg_op=1;
      break;
    // LD
    case T900_ld:
      if ( reg_byte < 0x90 )
        reg_op = 1;
      if ( reg_byte < 0xA0 )
        SetRegistr7(insn.ops[1-reg_op], reg_byte, reg_size);
      else
        SetImm8Op(insn.Op2, reg_byte&7);
      break;

    // another arithmetics XXX r, #)
    case 254:
      insn.itype=Add_List[reg_byte&7];
      SetImmData(insn, insn.Op2, 1<<reg_size);
      break;
    // CP r, #3
    case 253:
      insn.itype=T900_cp;
      SetImm8Op(insn.Op2, reg_byte&7);
      break;
    // shifts
    case T900_rlc:
      insn.itype=Shift_List[reg_byte&7];
      if ( reg_byte >= 0xF8 )
      {
        SetRegistr7(insn.Op1, 1, 0);
      }
      else
      {
        uchar ShL = insn.get_next_byte();
        SetImm8Op(insn.Op1, ShL == 0 ? 16 : ShL);
      }
      reg_op = 1;
      break;
    default:
      return 0;
  }
  // set reg
  SetRegistr(insn.ops[reg_op], reg_num, reg_size);
  return insn.size;
}

//-----------------------------------------------------------------------------
// parse 2nd byte DST
static int DSTAnalyser(insn_t &insn, uchar code)
{
  // memory op number
  char memrefop = 1;
  MemRefDef mr;   // memory ref
  // main opcodes
  static const uchar dst_codes[32] =
  {
    255,        0,         255,           0,
    T900_lda,   255,       T900_lda,      0,
    T900_ld,    0,         T900_ldw,      0,
    T900_ld,    0,         0,             0,
    T900_andcf, T900_orcf, T900_xorcf,    T900_ldcf,
    T900_stcf,  T900_tset, T900_res,      T900_set,
    T900_chg,   T900_bit,  T900_jp_cond,  T900_jp_cond,
    T900_call,  T900_call, T900_ret_cond, T900_ret_cond
  };
  // get mem ref
  if ( LoadMemRef(insn, mr, code) == 0 )
    return 0;
  // check for LDAR
  if ( insn.itype == T900_ldar )
    return insn.size;
  // opcode
  uchar dst_byte = insn.get_next_byte();
  // need to check for mr.dtyp - byte by default
  mr.dtype = dt_byte;

  insn.itype=dst_codes[(dst_byte>>3)&0x1F];
  switch ( insn.itype )
  {
    case 0:
      return 0;

    // go further
    case 255:
      if ( dst_byte < 0x2D && dst_byte >= 0x28 )
      {
        // bit insn
        insn.itype = COp_List[dst_byte-0x28];
        // reg A
        SetRegistr7(insn.Op1, 1, 0);
        // mem ref
        break;
      }

      switch ( dst_byte )
      {
        // ld byte
        case 0x00:
          insn.itype=T900_ld;
          SetImmData(insn, insn.Op2, 1);
          memrefop=0;
          break;

        // ld word
        case 0x02:
          insn.itype = T900_ldw;
          SetImmData(insn, insn.Op2, 2);
          mr.dtype = dt_word;
          memrefop = 0;
          break;

        // pop byte
        case 0x04:
          insn.itype=T900_pop;
          memrefop=0;
          break;

        // pop word
        case 0x06:
          insn.itype = T900_popw;
          mr.dtype = dt_word;
          memrefop = 0;
          break;

        // ld byte xx
        case 0x14:
          insn.itype = T900_ld;
          SetDirectMemRef(insn, insn.Op2, 2);
          memrefop = 0;
          break;

        // ld word
        case 0x16:
          insn.itype = T900_ldw;
          SetDirectMemRef(insn, insn.Op2, 2);
          mr.dtype = dt_word;
          memrefop = 0;
          break;


        default:
          return 0;
      }
      break;
    // load 40, 50, 60
    case T900_ldw:
    case T900_ld:
      SetRegistr7(insn.Op2, dst_byte, (dst_byte>>4)&0x3);
      mr.dtype = btp_type[(dst_byte>>4)&3];
      memrefop = 0;
      break;
    // load 20, 30
    case T900_lda:
      {
        uchar size = ((dst_byte>>4)&0x3)-1;
        SetRegistr7(insn.Op1, dst_byte, size);
        mr.dtype = btp_type[size];
        mr.flags |= URB_LDA|URB_LDA2;// address, not data!
      }
      break;
    // branches
    case T900_jp_cond:
      if ( (dst_byte&0xF) == 0x8 )
        insn.itype=T900_jp;
      [[fallthrough]];
    case T900_call:         // set cond code
      SetCondOp(insn.Op1, dst_byte&0xF);
      mr.flags |= URB_LDA;      // address, not data!
      break;
    // return
    case T900_ret_cond:     // 1st byte == 0xb0
      if ( code != 0xB0 )
        return 0;
      if ( (dst_byte&0xF) == 0x8 )
        insn.itype=T900_ret;
      SetCondOp(insn.Op1, dst_byte&0xF);
      return insn.size;

    // ANDCF, ....
    default:
      SetImm8Op(insn.Op1, dst_byte&7);
      break;
  }
  MemRefToOp(insn.ops[uchar(memrefop)], mr);
  return insn.size;
}

//-----------------------------------------------------------------------------
static int SRCAnalyser(insn_t &insn, uchar code)
{
  uchar memrefop=1; // number of operand with mem ref
  MemRefDef mr;
  static const uchar aa[] =
  {
    255,      0,         255,      255,
    T900_ld,  0,         T900_ex,  254,
    T900_mul, T900_muls, T900_div, T900_divs,
    T900_inc, T900_dec,  0,        253,
    T900_add, T900_add,  T900_adc, T900_adc,
    T900_sub, T900_sub,  T900_sbc, T900_sbc,
    T900_and, T900_and,  T900_xor, T900_xor,
    T900_or,  T900_or,   T900_cp,  T900_cp
  };

  if ( LoadMemRef(insn, mr, code) == 0 )
    return 0;
  uchar src_byte = insn.get_next_byte();
  insn.itype=aa[(src_byte>>3)&0x1F];
  uchar reg_size = (code>>4)&3; // 0, 1, 2
  switch ( insn.itype )
  {
    case 0:
      return 0;

    case 255:
      switch ( src_byte )
      {
        default:
          return 0;
        // push
        case 4:
          insn.itype=T900_push;
          memrefop=0;
          break;
        // rld
        case 6:
          insn.itype=T900_rld;
          SetRegistr7(insn.Op1, 1, 0);
          break;
        // rrd
        case 7:
          insn.itype=T900_rrd;
          SetRegistr7(insn.Op1, 1, 0);
          break;
        // ldi
        case 0x10:
          insn.itype=T900_ldi;
          mr.inc_size|=URB_UINC;
          mr.base_reg--;
          MemRefToOp(insn.Op1, mr);
          mr.base_reg++;
          if ( reg_size )
            insn.itype++;
          break;
        // ldir
        case 0x11:
          insn.itype=T900_ldir;
          mr.inc_size|=URB_UINC;
          mr.base_reg--;
          MemRefToOp(insn.Op1, mr);
          mr.base_reg++;
          if ( reg_size )
            insn.itype++;
          break;
        // ldd
        case 0x12:
          insn.itype=T900_ldd;
          mr.inc_size|=URB_UDEC;
          mr.base_reg--;
          MemRefToOp(insn.Op1, mr);
          mr.base_reg++;
          if ( reg_size )
            insn.itype++;
          break;
        // lddr
        case 0x13:
          insn.itype=T900_lddr;
          mr.inc_size|=URB_UDEC;
          mr.base_reg--;
          MemRefToOp(insn.Op1, mr);
          mr.base_reg++;
          if ( reg_size )
            insn.itype++;
          break;
        // cpi
        case 0x14:
          insn.itype=T900_cpi;
          mr.inc_size|=URB_UINC;
          if ( reg_size )
            SetRegistr7(insn.Op1, 0, 1);
          else
            SetRegistr7(insn.Op1, 1, 0);
          break;
        // cpir
        case 0x15:
          insn.itype=T900_cpir;
          mr.inc_size|=URB_UINC;
          if ( reg_size )
            SetRegistr7(insn.Op1, 0, 1);
          else
            SetRegistr7(insn.Op1, 1, 0);
          break;
        // cpd
        case 0x16:
          insn.itype=T900_cpd;
          mr.inc_size|=URB_UDEC;
          if ( reg_size )
            SetRegistr7(insn.Op1, 0, 1);
          else
            SetRegistr7(insn.Op1, 1, 0);
          break;
        // cpdr
        case 0x17:
          insn.itype=T900_cpdr;
          mr.inc_size|=URB_UDEC;
          if ( reg_size )
            SetRegistr7(insn.Op1, 0, 1);
          else
            SetRegistr7(insn.Op1, 1, 0);
          break;
        // ld
        case 0x19:
          if ( code&0x10 )
            insn.itype=T900_ldw;
          else
            insn.itype=T900_ld;
          SetDirectMemRef(insn, insn.Op1, 2);
          break;
      }
      break;
    // add and others
    case 254:
      insn.itype=Add_List[src_byte&7];
      SetImmData(insn, insn.Op2, 1<<((code>>4)&3));
      // word size
      if ( reg_size != 0 )
        insn.itype++;
      memrefop=0;
      break;
    // shifts  xxxx (mem)
    case 253:
      insn.itype=Shift_List1[src_byte&7];
      // word size
      if ( reg_size != 0 )
        insn.itype++;
      memrefop=0;
      break;
    // inc
    case T900_inc:
    case T900_dec:
      SetImm3Op(insn.Op1, src_byte);
      // wor size
      if ( reg_size != 0 )
        insn.itype++;
      break;

    case T900_ld:
      SetRegistr7(insn.Op1, src_byte, reg_size);
      break;
    // mul/div
    case T900_mul:
    case T900_div:
    case T900_muls:
    case T900_divs:
      SetRegistr7(insn.Op1, reg_size == 0 ? (src_byte&7)/2 : src_byte, reg_size+1);
      break;
    // ex
    case T900_ex:
      SetRegistr7(insn.Op2, src_byte, reg_size);
      memrefop=0;
      break;
    // add and others
    case T900_add:
    case T900_adc:
    case T900_sub:
    case T900_sbc:
    case T900_and:
    case T900_xor:
    case T900_or:
    case T900_cp:
      if ( src_byte&0x8 )
        memrefop=0;
      SetRegistr7(insn.ops[1-memrefop], src_byte, reg_size);
      break;
  }
  MemRefToOp(insn.ops[memrefop], mr);
  return insn.size;
}

//-----------------------------------------------------------------------------
static void ClearOperand(op_t &op)
{
  op.dtype = dt_byte;
  op.type = o_void;
  op.specflag1 = 0;
  op.specflag2 = 0;
  op.offb = 0;
  op.offo = 0;
  op.reg = 0;
  op.value = 0;
  op.addr = 0;
  op.specval = 0;
}

//-----------------------------------------------------------------------------
int idaapi T900_ana(insn_t *_insn)
{
  insn_t &insn = *_insn;

  ClearOperand(insn.Op1);
  ClearOperand(insn.Op2);
  ClearOperand(insn.Op3);

  uchar code = insn.get_next_byte();
  // split u pto two parts
  if ( code&0x80 )
  {
    // check for illegal
    if ( (code&0xF8) == 0xF8 )
    {
      // SWI (F8-FF)
      insn.itype=T900_swi;
      // trap number
      SetImm8Op(insn.Op1, code&7);
      // trap addres - is not working for now
      insn.Op1.addr = 0xFFFF00+(code&7)*4;
      insn.Op1.value = insn.Op1.addr;
      return insn.size;
    }
    if ( code == 0xF7 )
    {
      // LDX
      insn.itype=T900_ldx;
      // skip zero byte
      if ( insn.get_next_byte() != 0 )
        return 0;
      // address is in regs
      SetDirectMemRef(insn, insn.Op1, 1);
      // skip zero byte
      if ( insn.get_next_byte() != 0 )
        return 0;
      // data
      SetImmData(insn, insn.Op2, 1);
      // length is 6 bytes
      insn.size = 6;
      return insn.size;
    }
    // unknow codes (C6, D6, E6, F6)
    if ( (code & 0xCF) == 0xC6 )
      return 0;
    // large general reg (C8, D8, E8)
    if ( (code & 0x48) == 0x48 )
      return RegAnalyser(insn, code);
    // is smart reg ? (C7, D7, E7, F7)
    if ( (code & 0xCF) == 0xC7 )
      return RegAnalyser(insn, code);
    // memref
    // segments  dst (B0, B8, F0)
    if ( (code & 0xB0) == 0xB0 )
      return DSTAnalyser(insn, code);
    // src
    return SRCAnalyser(insn, code);
  }
  // low part
  else if ( code < 0x20 )
  {
    static const uchar FirstOp[] =
    {
      T900_nop,  T900_normal, T900_push, T900_pop,
      T900_max,  T900_halt,   T900_ei,   T900_reti,
      T900_ld,   T900_push,   T900_ldw,  T900_pushw,
      T900_incf, T900_decf,   T900_ret,  T900_retd,
      T900_rcf,  T900_scf,    T900_ccf,  T900_zcf,
      T900_push, T900_pop,    T900_ex,   T900_ldf,
      T900_push, T900_pop,    T900_jp,   T900_jp,
      T900_call, T900_call,   T900_calr, 0
    };
    insn.itype = FirstOp[code];
    switch ( insn.itype )
    {
      case 0x00:
        return 0;

      case T900_push:
      case T900_pop:
        switch ( code&0x18 )
        {
          case 0x00:
            insn.Op1.type=o_phrase;
            insn.Op1.phrase=fSR;
            break;
          // push only
          case 0x08:
            SetImmData(insn, insn.Op1, 1);
            break;
          // xxx A
          case 0x10:
            SetRegistr7(insn.Op1, 1, 0);
            break;
          // xxx F
          case 0x18:
            insn.Op1.type=o_phrase;
            insn.Op1.phrase=fSF;
            break;
        }
        break;
      // ei
      case T900_ei:   // next byte is imm
        SetImmData(insn, insn.Op1, 1);
        if ( insn.Op1.value == 7 )
        {
          insn.itype=T900_di;
          insn.Op1.type=o_void;
        }
        break;
      // ld (n), n
      case T900_ld:
        SetDirectMemRef(insn, insn.Op1, 1);
        SetImmData(insn, insn.Op2, 1);
        break;

      // ldw
      case T900_ldw:
        SetDirectMemRef(insn, insn.Op1, 1);
        SetImmData(insn, insn.Op2, 2);
        break;
      // pushW
      // retd
      case T900_pushw:
      case T900_retd:
        SetImmData(insn, insn.Op1, 2);
        break;
      // ex F, F'
      case T900_ex:
        insn.Op1.type   = o_phrase;
        insn.Op1.phrase = fSF;
        insn.Op2.type   = o_phrase;
        insn.Op2.phrase = fSF1;
        break;
      // ldf
      case T900_ldf:
        SetImmData(insn, insn.Op1, 1);
        break;

      case T900_jp:
      case T900_call:
        SetJmp(insn, insn.Op1, 2+(code&1));
        insn.Op1.specflag1 |= URB_LDA;
        break;

      // callr 16
      case T900_calr:
        SetJmp(insn, insn.Op1, -2);
        insn.Op1.specflag1 |= URB_LDA;
        break;
    }
  }
  else
  {
    switch ( code & 0x78 )
    {
      // ld
      case 0x20:
      case 0x30:
      case 0x40:
        insn.itype=T900_ld;
        SetRegistr7(insn.Op1, code, (code>>4)-2);
        SetImmData(insn, insn.Op2, 1<<((code>>4)-2));
        break;
      // push
      case 0x28:
      case 0x38:
        insn.itype=T900_push;
        SetRegistr7(insn.Op1, code, (code>>4)-1);
        break;
      // pop
      case 0x48:
      case 0x58:
        insn.itype=T900_pop;
        SetRegistr7(insn.Op1, code, (code>>4)-3);
        break;
      // reserved
      case 0x50:
        return 0;
      // JR
      case 0x60:
      case 0x68:
        if ( (code&0xF) == 0x8 )
          insn.itype = T900_jr;
        else
          insn.itype = T900_jr_cond;
        SetCondOp(insn.Op1, code&0xF);
        SetJmp(insn, insn.Op2, -1);
        insn.Op2.specflag1|=URB_LDA;
        break;
      // JRL
      case 0x70:
      case 0x78:
        if ( (code&0xF) == 0x8 )
          insn.itype=T900_jrl;
        else
          insn.itype=T900_jrl_cond;
        SetCondOp(insn.Op1, code&0xF);
        SetJmp(insn, insn.Op2, -2);
        insn.Op2.specflag1 |= URB_LDA;
        break;
    }
  }
  return insn.size;
}
