/*
 *      Interactive disassembler (IDA).
 *      Version 3.05
 *      Copyright (c) 1990-95 by Ilfak Guilfanov.
 *      ALL RIGHTS RESERVED.
 *                              FIDO:   2:5020/209
 *                              E-mail: ig@estar.msk.su
 *
 */

#include "i860.hpp"

//------------------------------------------------------------------------
static void doImmdValue(void)
{
  doImmd(cmd.ea);
  switch ( cmd.itype )
  {
    case I860_and:
    case I860_andh:
    case I860_andnot:
    case I860_andnoth:
    case I860_xor:
    case I860_xorh:
      op_num(cmd.ea,1);
      break;
  }
  uFlag = getFlags(cmd.ea);
}

//----------------------------------------------------------------------
static int TouchArg(op_t &x,int isload)
{
  dref_t xreftype;
  switch ( x.type )
  {
  case o_phrase:                // 2 registers
  case o_reg:
    break;
  case o_imm:
    if ( ! isload ) goto badTouch;
    xreftype = dr_O;
    goto makeImm;
  case o_displ:
    xreftype = isload ? dr_R : dr_W;
makeImm:
    doImmdValue();
    if ( isOff(uFlag, x.n) ) ua_add_off_drefs(x, xreftype);
    break;
  case o_mem:
    ua_dodata(x.addr,x.dtyp);
    if ( !isload ) doVar(x.addr);
    ua_add_dref(x.offb,x.addr,isload ? dr_R : dr_W);
    break;
  case o_near:
    {
      int iscall = InstrIsSet(cmd.itype,CF_CALL);
      ua_add_cref(x.offb,x.addr,iscall ? fl_CN : fl_JN);
      if ( iscall ) {
        func_t *pfn = get_func(x.addr);
        if ( pfn != NULL && (pfn->flags & FUNC_NORET) ) return 0;
      }
    }
    break;
  default:
badTouch:
    warning("%a (%s): bad optype", cmd.ea, Instructions[cmd.itype].name);
    break;
  }
  return 1;
}

//----------------------------------------------------------------------
static bool isDual(ulong code)
{
  return int(code>>26) == 0x12 && (code & Dbit) != 0;
}

//----------------------------------------------------------------------
//static int isDelayed(ulong code) {
//                      // bc.t bla bnc.t br bri call calli
//  int opcode = int(code >> 26);
//  switch ( opcode ) {
//    case 0x13:
//      return ((code & 0x1F) == 2);    // calli
//    case 0x10:                // bri
//    case 0x1A:                // br
//    case 0x1B:                // call
//    case 0x1D:                // bc.t
//    case 0x1F:                // bnc.t
//    case 0x2D:                // bla
//      return 1;
//  }
//  return 0;
//}
//
//----------------------------------------------------------------------
static int isDelayedStop(ulong code)
{
                        // br bri
  int opcode = int(code >> 26);
  switch ( opcode ) {
    case 0x10:          // bri
    case 0x1A:          // br
      return 1;
  }
  return 0;
}

//----------------------------------------------------------------------
static bool canFlow(void)
{
  if ( ! isFlow(uFlag) ) return 1;             // no previous instructions
  ea_t ea = cmd.ea - 4;
  flags_t F = getFlags(ea);
  if ( isFlow(F) && isCode(F) )
  {
    if ( isDelayedStop(get_long(ea)) )         // now or later
    {
      ea -= 4;
      if ( !isCode(getFlags(ea)) || !isDual(get_long(ea)) ) return 0;
      return 1;
    }
    if ( isFlow(F) )
    {
      ea -= 4;
      return !isCode(getFlags(ea)) || !isDelayedStop(get_long(ea));
    }
  }
  return 1;
}

//----------------------------------------------------------------------
int i860_emu(void)
{
  bool funcret = 1;

  int Feature = Instructions[cmd.itype].feature;

  if ( Feature & CF_USE1 ) if ( !TouchArg(cmd.Op1,1) ) funcret = 0;
  if ( Feature & CF_USE2 ) if ( !TouchArg(cmd.Op2,1) ) funcret = 0;
  if ( Feature & CF_USE3 ) if ( !TouchArg(cmd.Op3,1) ) funcret = 0;
  if ( Feature & CF_JUMP ) QueueMark(Q_jumps,cmd.ea);

  if ( Feature & CF_CHG1 ) if ( !TouchArg(cmd.Op1,0) ) funcret = 0;
  if ( Feature & CF_CHG2 ) if ( !TouchArg(cmd.Op2,0) ) funcret = 0;
  if ( Feature & CF_CHG3 ) if ( !TouchArg(cmd.Op3,0) ) funcret = 0;

  if ( funcret && canFlow() ) ua_add_cref(0,cmd.ea+cmd.size,fl_F);
  return 1;
}
