/*
 *      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 "m65.hpp"

//----------------------------------------------------------------------
void m6502_t::handle_operand(
        const op_t &x,
        bool isload,
        const insn_t &insn,
        bool *flow) const
{
  ea_t ea;
  dref_t xreftype;
  switch ( x.type )
  {
    case o_reg:
      break;
    case o_imm:
      if ( !isload )
        goto badTouch;
      xreftype = dr_O;
      goto MAKE_IMMD;
    case o_displ:
      xreftype = isload ? dr_R : dr_W;
MAKE_IMMD:
      set_immd(insn.ea);
      if ( op_adds_xrefs(get_flags(insn.ea), x.n) )
        insn.add_off_drefs(x, xreftype, m65_opflags(x));
      break;
    case o_mem:
      ea = map_data_ea(insn, x);
      insn.create_op_data(ea, x);
      insn.add_dref(ea, x.offb, isload ? dr_R : dr_W);
      // add xref to the target address
      if ( insn.itype == M65_jmpi && x.dtype == dt_word && is_loaded(ea) )
      {
        ea_t callee = get_word(ea);
        if ( callee > 32 && is_mapped(callee) ) // is good address?
        {
          add_cref(insn.ea, callee, fl_JN);
          if ( !is_defarg0(get_flags32(ea)) )
            op_plain_offset(ea, 0, 0);
        }
      }
      break;
    case o_near:
      {
        ea = map_code_ea(insn, x);
        ea_t segbase = (ea - x.addr) >> 4;
        ea_t thisseg = insn.cs;
        bool iscall = has_insn_feature(insn.itype, CF_CALL);
        insn.add_cref(
                ea,
                x.offb,
                iscall ? (segbase == thisseg ? fl_CN : fl_CF)
                       : (segbase == thisseg ? fl_JN : fl_JF));
        if ( iscall && *flow )
          *flow = func_does_return(ea);
      }
      break;
    default:
badTouch:
      const char *mnem = insn.get_canon_mnem(ph);
      warning("%a: %s,%d: bad optype %d", insn.ea, mnem, x.n, x.type);
      break;
  }
}

//----------------------------------------------------------------------
int m6502_t::emu(const insn_t &insn) const
{
  uint32 Feature = insn.get_canon_feature(ph);
  bool flow = ((Feature & CF_STOP) == 0);

  if ( Feature & CF_USE1 ) handle_operand(insn.Op1, 1, insn, &flow);
  if ( Feature & CF_USE2 ) handle_operand(insn.Op2, 1, insn, &flow);
  if ( Feature & CF_CHG1 ) handle_operand(insn.Op1, 0, insn, &flow);
  if ( Feature & CF_CHG2 ) handle_operand(insn.Op2, 0, insn, &flow);
  if ( Feature & CF_JUMP )
    remember_problem(PR_JUMP, insn.ea);

  if ( flow )
    add_cref(insn.ea, insn.ea + insn.size, fl_F);

  return 1;
}
