// $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,
};

void handle_operand(op_t &, enum opRefType);

int
emu(void)
{
	int  features;

	//
	// Determine the current instruction's features.
	//
	features = Instructions[cmd.itype].feature;

	//
	// Determine whether the instruction stops the execution flow.
	//
	if (features & CF_STOP) {
		//
		// This instruction stops execution flow.
		// Do nothing special.
		//
	} else {
		//
		// 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);
	}

	//
	// Examine each operand and determine what effect, if any,
	// it makes on the environment.
	//
	// Operands that are read
	if (features & CF_USE1)
		handle_operand(cmd.Op1, hop_READ);
	if (features & CF_USE2)
		handle_operand(cmd.Op2, hop_READ);
	if (features & CF_USE3)
		handle_operand(cmd.Op3, hop_READ);
	// Operands that are written
	if (features & CF_CHG1)
		handle_operand(cmd.Op1, hop_WRITE);
	if (features & CF_CHG2)
		handle_operand(cmd.Op2, hop_WRITE);
	if (features & CF_CHG3)
		handle_operand(cmd.Op3, hop_WRITE);

	//
	// If the instruction makes a branch, let the IDA kernel
	// know.
	//
	if (features & CF_JUMP)
		QueueMark(Q_jumps, cmd.ea); 

	return 1;
}

void
handle_operand(op_t &op, enum opRefType ref_type)
{
	ea_t       ea;
	sel_t      data_selector;

	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(
				"%08lX (%s): bad optype",
				cmd.ea,
			    Instructions[cmd.itype].name
			);
			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);
		//
		// Refresh the current address flags global cache value since
		// we have probably changed the instruction's flags in the previous
		// statement.
		//
		uFlag = getFlags(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);
		} 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("%08lX (%s): bad optype", cmd.ea,
			Instructions[cmd.itype].name);
		break;
	}
}
