/*
 *      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 "java.hpp"
#include "classfil.hpp"

static int Feature;

static char cmt0[] = "ATTENTION! Invalid local variable number";
//------------------------------------------------------------------------
static void TouchArg(op_t &x, int isload) {

  register char *p;

  switch (x.type) {

    case o_void:       // not operand
      break;

    case o_cpool:      // ConstantPool reference (index)
      if(x.ref) {
        static char cm21[] = "ATTENTION! Invalid index in constant pool",
                    cm22[] = "ATTENTION! Invalid string in constant pool";

        set_cmt_if_none(cmd.ea, x.ref == 1 ? cm22 : cm21, 0);
        QueueMark(Q_att, cmd.ea);
      } else if(x.cp_ind) {
         ea_t ea;
         short r = -1;

         switch((uchar)x.cp_type) {
           case CONSTANT_Fieldref:
            r = 1;
           case CONSTANT_InterfaceMethodref:
           case CONSTANT_Methodref:
             if(!(x._subnam | x._name | x._class)) break;
             if(x._class == curClass.ThisClass) {
               if((ea = SearchFM(x._subnam, x._sign, &r)) == BADADDR) break;
             } else {
               if(!cmd.xtrn_ip) break;
               ea = (cmd.xtrn_ip == 0xFFFF) ? curClass.startEA :
                                              curClass.xtrnEA + cmd.xtrn_ip;
               if(r < 0) r = 0;
             }
             if(r <= 0) {
               ua_add_cref(x.offb, ea, fl_CF);
               if(!r) autoCancel(ea, ea+1);
             } else ua_add_dref(x.offb, ea, ((cmd.itype == j_putstatic ||
                                          cmd.itype == j_putfield) ?
                                                               dr_W : dr_R));
            break;

           case CONSTANT_Class:
            if(cmd.xtrn_ip)
              ua_add_dref(x.offb, cmd.xtrn_ip == 0xFFFF ? curClass.startEA :
                                        curClass.xtrnEA + cmd.xtrn_ip, dr_I);
           default:
             break;
         }
      }
      break;

    case o_array:      // type!
      if(x.ref) {
        static char cmt1[] = "ATTENTION! Invalid array type";

        set_cmt_if_none(cmd.ea, cmt1, 0);
        QueueMark(Q_att, cmd.ea);
      }
      break;

    case o_imm:        //const (& #data)
      if(x.ref < 2) doImmd(cmd.ea);
      break;

    case o_mem:        // local data pool
      if(x.ref) {
        p = cmt0;
mark:
        set_cmt_if_none(cmd.ea, p, 0);
        QueueMark(Q_att, cmd.ea);
      } else {
        ua_add_dref(x.offb, curSeg.DataBase + x.addr, isload ? dr_R : dr_W);
        if(x.dtyp == dt_qword || x.dtyp == dt_double)
          ua_add_dref(x.offb, curSeg.DataBase + x.addr + 1, isload ? dr_R : dr_W);
      }
      break;

    case o_near:
      if(x.ref) {
        static char cmta[] = "ATTENTION! Invalid jump address";
        p = cmta;
        goto mark;
      }
      ua_add_cref(x.offb,curSeg.startEA + x.addr,(Feature & CF_CALL) ? fl_CN : fl_JN);
      break;

    default:
      warning("%a (%s): bad optype %d", cmd.ip, Instructions[cmd.itype].name, x.type);
      break;
  }
}

//----------------------------------------------------------------------
int emu(void) {

  Feature = Instructions[cmd.itype].feature;

  if(cmd.wid > 1) {
    static char cmt3[] = "ATTENTION! Limited usage instruction";

    set_cmt_if_none(cmd.ea, cmt3, 0);

  }

  if(cmd.itype >= j_a_software) {
    static char cmt4[] = "ATTENTION! Undocumented instruction";

    set_cmt_if_none(cmd.ea, cmt4, 0);
  }

  if(cmd.Op1.type == o_void && cmd.Op1.ref) {
    if((char)cmd.Op1.ref < 0) set_cmt_if_none(cmd.ea, cmt0, 0);
    else {
      ua_add_dref(0, cmd.Op1.addr, (cmd.itype >= j_istore_0) ? dr_W : dr_R);
      if(cmd.Op1.ref & 2)
        ua_add_dref(0, cmd.Op1.addr+1, (cmd.itype>=j_istore_0) ? dr_W : dr_R);
    }
  }

  if(Feature & CF_USE1) TouchArg(cmd.Op1, 1);
  if(Feature & CF_USE2) TouchArg(cmd.Op2, 1);
  if(Feature & CF_USE3) TouchArg(cmd.Op3, 1);

//  if(Feature & CF_JUMP) QueueMark(Q_jumps, cmd.ea);

  if(Feature & CF_CHG1) TouchArg(cmd.Op1, 0);


  if(cmd.swit) {         // tableswitch OR lookupswitch
    ulong count, addr, rnum;

    if(cmd.swit & 0200) {
      set_cmt_if_none(cmd.ea, cmt0, 0);
      QueueMark(Q_att, cmd.ea);
    }
    if(cmd.swit & 0100) {
      static char cmt5[] = "WARNING! Nonzero filler";

      set_cmt_if_none(cmd.ea, cmt5, 0);
    }

    rnum = cmd.Op2.value - 1;   // for lookupswtitch
    for(addr= cmd.Op2.addr, count= cmd.Op3.value; count; addr +=4, count--) {

      ulong refa;

      if(cmd.itype == j_lookupswitch) {
        rnum = get_long(curSeg.startEA + addr); // skip pairs
        addr += 4;
      } else ++rnum;
      refa = cmd.ip + get_long(curSeg.startEA + addr);

      if(refa < curSeg.CodeSize) {
        static char fcas[] = "case 0x00000000";
        ua_add_cref(0, (refa += curSeg.startEA), fl_JN);
        qsnprintf(&fcas[7], sizeof(fcas)-7, "%lx", rnum);
        set_cmt_if_none(refa, fcas, 0);
      }
    }
  }

  if(!(Feature & CF_STOP)) {
    register func_t *pfn;
    if(!(Feature && CF_CALL) || (pfn = get_func(cmd.ea)) == NULL ||
       !(pfn->flags & FUNC_NORET)) ua_add_cref(0, cmd.ea + cmd.size, fl_F);
  }

  return(1);
}
