// $Id: out.cpp,v 1.7 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.

//
// IDA TMS320C1X processor module.
//     Instruction display routines 
//
#include "../idaidp.hpp"

#include <srarea.hpp>

#include "ins.hpp"
#include "out.hpp"
#include "reg.hpp"

void outReg(int);
void outPhrase(int);
void outImmediate(op_t &, int);
void outNear(op_t &);

//
// outSegStart()
//
// [ This function is named in our processor_t.segstart member ]
//
// Generate assembly text before the start of a segment.
//
void
outSegStart(ea_t ea)
{
	gen_cmt_line("A segment starts here.");
}

//
// outSegEnd()
//
// [ This function is named in our processor_t.segend member ]
//
// Generate assembly text after the end of a segment.
//
void
outSegEnd(ea_t ea)
{
	gen_cmt_line("A segment ends here.");
}

//
// outHeader()
//
// [ This function is named in our processor_t.header member ]
//
// Generate an assembly header for the top of the file.
//
void
outHeader(void)
{
	gen_cmt_line("Processor       : %s", inf.procName);
	gen_cmt_line("Target assembler: %s", ash.name);
}

//
// outFooter()
//
// [ This function is named in our processor_t.footer member ]
//
// Generate an assembly footer for the bottom of the file.
//
void
outFooter(void)
{
	gen_cmt_line("End of file");
}

////////////////////////////////////////////////////////////////////////////
//
// DISASSEMBLY OPERAND HELPER FUNCTIONS
//
////////////////////////////////////////////////////////////////////////////

//
// outReg
// Display a register name in the register color.
// 
inline
void
outReg(int rgnum)
{
	out_register(ph.regNames[rgnum]);
}

//
// outPhrase(phrase)
// Output a TMS320C1X-specific operand phrase.
//
void
outPhrase(int phrase)
{
	//
	// Complex phrase operand.
	// (Processor specific)
	//
	switch (phrase) {
	case IPH_AR:
		//
		// Current address register, indirect.
		//
		out_symbol('*');
		break;
	case IPH_AR_INCR:
		//
		// Current address register, indirect, post-increment.
		//
		out_symbol('*');
		out_symbol('+');
		break;
	case IPH_AR_DECR:
		//
		// Current address register, indirect, post-decrement.
		//
		out_symbol('*');
		out_symbol('-');
		break;
	}
}

//
// outImmediate(operand, flags)
//
// Generate text for an immediate numerical value in the given
// operand.
//
void
outImmediate(op_t &op, int flags)
{
	OutValue(op, flags);
}

//
// outNear(operand)
//
// Display an operand that is known to reference another piece of
// of code.
//
void
outNear(op_t &op)
{
	ea_t        ea;
	const char *target_name;

	//
	// Calculate the effective address of this code reference.
	//
	ea = toEA(cmd.cs, op.addr);

	//
	// Find or create a name for the code address that this operand
	// references so that we can output that name in the operand's
	// place.
	//
	target_name = get_name_expr(cmd.ea+op.offb, op.n, ea, op.addr);
	if (target_name == NULL) {
		//
		// The code address didn't have a name.  Default to
		// displaying the address as a number.
		//
		OutValue(op, OOF_ADDR | OOF_NUMBER | OOFS_NOSIGN);

		//
		// Let the user know that he or she should look at this 
		// instruction and attempt to name the address that it
		// references.
		//
		QueueMark(Q_noName, cmd.ea);
	} else {
		//
		// The code address either had a name or one has just been
		// created for it.  Display that name in this operand's place.
		//
		OutLine(target_name);  // Out 'line' is a misnomer, Out 'string'
		                       // is more correct.
	}
}	


//
// outNear(operand)
//
// Display an operand that is known to reference data RAM.
//
void
outMem(op_t &op)
{
	ea_t        ea;
	sel_t       data_selector;
	const char *target_name;

	//
	// Ask the IDA kernel for the value of the current data page
	// pointer for execution of this instruction.
	//
	data_selector = getSR(cmd.ea, IREG_VDS);

	//
	// Is it known?
	//
	if (data_selector == BADSEL) {
		//
		// The current data page pointer is not known.
		//
		//
		// Display the current operand as a regular number and
		// return.
		//			
		OutValue(op, OOF_ADDR);
		return;
	}

	//
	// The current data page pointer is known.  Use it to calculate the
	// effective address of the memory being referenced by this
	// operand.
	//
	ea = sel2ea(data_selector) + op.addr;
	 
	//
	// Find or create a name for the data address that this operand
	// references so that we can output that name in the operand's
	// place.
	//
	target_name = get_name_expr(cmd.ea+op.offb, op.n, ea, op.addr);

	//
	// Was a name found or created?
	//
	if (target_name == NULL) {
		//
		// No name was found and no name was created.
		// Display the current operand as a regular number.
		//
		OutValue(op, OOF_ADDR);

		//
		// Let the user know that he or she should look at this 
		// instruction and attempt to name the address that it
		// references.
		//
		QueueMark(Q_noName, cmd.ea);
	} else {
		//
		// A name has been found or created for the address being
		// referenced by this operand.  Display that name in the
		// operand's place.
		//
		OutLine(target_name);  // Out 'line' is a misnomer, Out 'string'
		                       // is more correct.
	}
}

//
// outOp(operand)
//
// [ This function is named in our processor_t.u_outop member ]
//
// Generate the text representation of the given instruction operand.
// Called directly by the IDA kernel in response to a call to
// out_one_operand().  This roundabout calling method allows IDA to
// overide the display of each operand within an instruction
// if the user so chooses to provide a manual operand in its place.
//
int
outOp(op_t &op)
{
	switch(op.type) {
	case o_reg:
		//
		// Register operand.
		//
		outReg(op.reg);
		break;
	case o_phrase:
		//
		// Complex phrase.
		// (Processor specific)
		//
		outPhrase(op.phrase);
		break;
	case o_imm:
		//
		// Immediate value.
		//
		outImmediate(op, 0);
		break;
	case o_near:
		//
		// Code reference.
		//
		outNear(op);
		break;
	case o_mem:
		//
		// Data memory reference.
		//
		outMem(op);
		break;
	default:
		break;
	}

	return 1;
}

//
// out()
//
// [ This function is named in our processor_t.u_out member ]
//
void
out(void)
{
	char buf[MAXSTR];

	//
	// An unseen parameter to this function is the global 'cmd' structure
	// which holds all the information about the instruction that we
	// are being asked to display.
	//

	//
	// Place the address of our temporary output buffer in the u_line
	// global variable so that we can use the helper output functions.
	//
	u_line = buf;

	//
	// This call to OutMnem() is a helper function in the IDA kernel that
	// displays an instruction mnemonic for the current instruction.
	// It does so by taking the integer value in cmd.itype and using it
	// as an index into the array that we named in this processor module's
	// processor_t.instruc member.  From this indexed element comes the
	// instruction mnemonic to be displayed.
	//
	// Like most of the IDA kernel helper functions, OutMnem() expects
	// the global variable u_line to be initialized by the caller.
	//
	OutMnem();

	//
	// If the current instruction has a non-empty first operand,
	// then display it.
	//
	if (cmd.Op1.type != o_void) {
		//
		// This call to out_one_operand() is another IDA kernel function that
		// is mandatory for a properly behaved processor module.
		//
		// Normally, this helper function turns around and calls the function
		// named in our processor_t.u_outop member with a reference to
		// the current instruction's operand numbered in the first argument.
		// However, if through the course of interacting with the
		// disassembly the user chooses to manually override the specified
		// operand in this instruction, the IDA kernel will forego the call
		// to u_outop() -- instead calling an internal IDA routine to
		// display the user's manually entered operand.
		//
		out_one_operand(0);
	}
	//
	// Display the second operand, if non-empty.
	//
	if (cmd.Op2.type != o_void) {
		//
		// This call to out_symbol() is another helper function in the
		// IDA kernel.  It writes the specified character to the current
		// buffer, using the user-configurable 'symbol' color.
		// 
		out_symbol(',');
		OutChar(' ');
		out_one_operand(1);
	}
	//
	// Finally, display the third operand, if non-empty.
	//
	if (cmd.Op3.type != o_void) {
		out_symbol(',');
		OutChar(' ');
		out_one_operand(2);
	}

	//
	// Now our temporary buffer (which has been stored in the global
	// pointer 'u_line') holds the entire colored text line to be displayed
	// for this instruction.  We need to terminate it properly.
	//
	*u_line = '\0';

	//
	// The global variable 'g1_comm' is a flag used by the MakeLine()
	// function (which we will call next).  When set it will add any comments
	// that the user has entered for this instruction to the line.
	// 
	gl_comm = 1;

	//
	// Tell IDA to display our constructed line.
	//
	MakeLine(buf);
}
