
#include "m740.hpp"

static void handle_imm(op_t &op) {
    doImmd(cmd.ea);
    bool in_hex = false;
    switch (cmd.itype) {
        case m740_and:
        case m740_ora:
            in_hex = true;
            break;
    }
    if (in_hex) op_hex(cmd.ea, op.n);
    uFlag = getFlags(cmd.ea);
}

static void handle_operand(op_t &op) {
    switch (op.type) {

        // code address
        case o_near:
            {
                enum cref_t mode = fl_JN;

                if (cmd.itype == m740_jsr)
                    mode = fl_CN;
                
                ua_add_cref(op.offb, toEA(cmd.cs, op.addr), mode);
            }
            break;

        // data address
        case o_mem:
            {
                enum dref_t mode = dr_U;

                if (is_addr_ind(op))            mode = dr_R;    /* NOT dr_O */
                else if (is_addr_read(op))      mode = dr_R;
                else if (is_addr_write(op))     mode = dr_W;
        
                ua_add_dref(op.offb, toEA(cmd.cs, op.addr), mode);
                ua_dodata(op.addr, op.dtyp);
            }
            break;
            
        // immediate
        case o_imm:
            handle_imm(op);
            // if the value was converted to an offset, then create a data xref:
            if (isOff(uFlag, op.n)) ua_add_off_drefs(op, dr_O);
            break;

        // displ
        case o_displ:
            if (isOff(uFlag, op.n)) {
                ua_add_off_drefs(op, dr_O);
                ua_dodata(op.addr, op.dtyp);
            }
            break;
            
        // reg - do nothing
        case o_reg:
        case o_void:
            break;
        
        default:
            IDA_ERROR("Invalid op.type in handle_operand()");
    }
}

// emulate an instruction
int emu(void) {
    int feature = Instructions[cmd.itype].feature;
    bool flow = ((feature & CF_STOP) == 0);

    if (cmd.Op1.type != o_void)            handle_operand(cmd.Op1);
    if (cmd.Op2.type != o_void)            handle_operand(cmd.Op2);
    if (cmd.Op3.type != o_void)            handle_operand(cmd.Op3);
    
/*
     we can't use this code
   
    if (feature & CF_USE1)    handle_operand(cmd.Op1, 1);
    if (feature & CF_USE2)    handle_operand(cmd.Op2, 1);
    if (feature & CF_USE3)    handle_operand(cmd.Op3, 1);
*/
  
    // we don't use CF_JUMP
    //if (feature & CF_JUMP)    
    switch (cmd.itype) {
        case m740_jmp:
        case m740_jsr:
            if (cmd.Op1.type != o_void && is_addr_ind(cmd.Op1))
                QueueMark(Q_jumps, cmd.ea);
            break;
    }

/*
    if (feature & CF_CHG1)    handle_operand(cmd.Op1, 0);
    if (feature & CF_CHG2)    handle_operand(cmd.Op2, 0);
    if (feature & CF_CHG3)    handle_operand(cmd.Op3, 0);
*/

    if (flow) {
        // skip the next byte if the current insn is brk
        if (cmd.itype == m740_brk) {
            ua_add_cref(0, cmd.ea + cmd.size + 1, fl_JN);
            doByte(cmd.ea + cmd.size, 1);
        }
        else {
            ua_add_cref(0, cmd.ea + cmd.size, fl_F);
        }
    }
        
    return 1;
}

