// $Id: emu.cpp,v 1.6 2000/11/06 22:11:16 jeremy Exp $
//
// Copyright (c) 2000 Jeremy Cooper.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. All advertising materials mentioning features or use of this software
//    must display the following acknowledgement:
//    This product includes software developed by Jeremy Cooper.
// 4. The name of the author may not be used to endorse or promote products
//    derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

//
// TMS320C1X Processor module
//       Instruction emulation.
//
#include "../idaidp.hpp"
#include <srarea.hpp>

#include "emu.hpp"
#include "ins.hpp"
#include "reg.hpp"
#include "idp.hpp"

//
// Reference type.  Used between emu() and handle_operand() to
// flag whether an operand is written to or read from.
//
enum opRefType {
        hop_READ,
        hop_WRITE,
};

bool handle_operand(op_t &, enum opRefType);

int
idaapi emu(void)
{
        int  features;

        //
        // Determine the current instruction's features.
        //
        features = cmd.get_canon_feature();
        bool flow = (features & CF_STOP) == 0;

        //
        // Examine each operand and determine what effect, if any,
        // it makes on the environment.
        //
        // Operands that are read
        if ( features & CF_USE1 )
                flow &= handle_operand(cmd.Op1, hop_READ);
        if ( features & CF_USE2 )
                flow &= handle_operand(cmd.Op2, hop_READ);
        if ( features & CF_USE3 )
                flow &= handle_operand(cmd.Op3, hop_READ);
        // Operands that are written
        if ( features & CF_CHG1 )
                flow &= handle_operand(cmd.Op1, hop_WRITE);
        if ( features & CF_CHG2 )
                flow &= handle_operand(cmd.Op2, hop_WRITE);
        if ( features & CF_CHG3 )
                flow &= handle_operand(cmd.Op3, hop_WRITE);

        //
        // Determine whether the instruction stops the execution flow.
        //
        if ( flow ) {
                //
                // This instruction doesn't stop execution flow.
                // Add a cross reference to the next instrction.
                //
                ua_add_cref(0,cmd.ea+cmd.size,fl_F);
        }

        //
        // If the instruction makes a branch, let the IDA kernel
        // know.
        //
        if ( features & CF_JUMP )
                QueueSet(Q_jumps, cmd.ea);

        return 1;
}

bool
handle_operand(op_t &op, enum opRefType ref_type)
{
        ea_t       ea;
        sel_t      data_selector;
        bool       flow = true;

        switch ( op.type ) {
        case o_reg:
                //
                // Register operand.
                //
                //
                // Nothing needs to be calculated or examined for this
                // operand.
                //
                break;
        case o_imm:
                //
                // Immediate operand.
                //
                // Make sure that this operand reference isn't a write reference.
                // (Writing to an immediate value is not allowed and is a sure
                // sign of a badly decoded instruction).
                //
                if ( ref_type == hop_WRITE ) {
                        //
                        // Attempt to write to an immediate value.
                        // Error.
                        //
                        warning("%a: %s,%d: bad optype %d", cmd.ea, cmd.get_canon_mnem(), op.n, op.type);
                        break;
                }
                //
                // SPECIAL INSTRUCTION CASE:
                //
                // The LDPK instruction is decoded by ana() to have an immediate
                // value as an operand.  However, this immediate value is to be
                // the new data page pointer, which we must track for proper
                // memory referencing.
                //
                if ( cmd.itype == I_LDPK ) {
                        //
                        // This is an LDPK instruction.  Let the kernel know that
                        // we are changing the current data page pointer.  We track
                        // this bit as though it were a virtual segment register named
                        // I_VDS, although it is not a true register in the CPU.
                        //
                        // Determine into which data page the instruction is attempting
                        // to point.
                        //
                        if ( op.value == 0 ) {
                                //
                                // Data page 0 is being loaded.
                                //
                                data_selector = tms320c1x_dpage0;
                        } else {
                                //
                                // Data page 1 is being loaded.
                                //
                                data_selector = tms320c1x_dpage1;
                        }
                        //
                        // Notify the IDA kernel of the change.
                        //
                        splitSRarea1(
                                cmd.ea,          // The current instruction's address
                                IREG_VDS,        // The segment register being modified
                                data_selector,   // The new selector value being loaded
                                SR_auto          // How the new value was determined
                        );
                }
                //
                // Let the kernel know that the instruction's address should
                // be marked with a 'has immediate value' flag.
                // (Useful during search?)
                //
                doImmd(cmd.ea);
                break;
        case o_phrase:
                //
                // Processor-specific phrase.
                //
                // These operands have no currently trackable side effect.
                //
                break;
        case o_mem:
                //
                // Direct memory reference.
                //

                //
                // Ask the IDA kernel for the current data page pointer selector.
                //
                data_selector = getSR(cmd.ea, IREG_VDS);

                //
                // Is it known?
                //
                if ( data_selector == BADSEL ) {
                        //
                        // The current data page pointer is unknown.
                        // There is nothing to do.
                        //
                } else {
                        //
                        // The current data page pointer is known.
                        // Calculate the full effective address being referenced
                        // by this operand.
                        //
                        ea = sel2ea(data_selector) + op.addr;

                        //
                        // Generate a data cross reference from this instruction
                        // to the target address.
                        //
                        ua_add_dref(op.offb, ea, ref_type == hop_READ ? dr_R : dr_W);
                }

                //
                // TODO: DMOV, ...
                // These instructions read from the address in their operands
                // and write to the address ADJACENT to it.
                //
                break;
        case o_near:
                //
                // Code reference in current segment.
                //
                //
                // Determine the effective address of the reference.
                //
                ea = toEA(cmd.cs, op.addr);

                //
                // Is this a 'CALL' type reference, or a branch type reference?
                //
                if ( InstrIsSet(cmd.itype, CF_CALL) ) {
                        //
                        // This is a CALL type reference.  Make a cross reference
                        // that notes it.
                        //
                        ua_add_cref(op.offb, ea, fl_CN);
                        if ( !func_does_return(ea) )
                           flow = false;
                } else {
                        //
                        // This is a branch type reference.  Make a cross reference
                        // that notes it.
                        //
                        ua_add_cref(op.offb, ea, fl_JN);
                }
                break;
        default:
                //
                // Unhandled operand type.
                // Error.
                //
                warning("%a: %s,%d: bad optype %d", cmd.ea, cmd.get_canon_mnem(), op.n, op.type);
                break;
        }
        return flow;
}
