// ArmletDisassembler.cpp: implementation of the ArmletDisassembler class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "archimedes.h"
#include "ArmletDisassembler.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CArmletDisassembler::CArmletDisassembler()
{
}

CArmletDisassembler::~CArmletDisassembler()
{

}

CString CArmletDisassembler::disassemble(uint32 number, Armlet *armlet)
{
	CString output = "";

	CString opcodeLabel = "";

	// ??? debugging
	if( 0 )
	{
		TRACE("opcode = %d ", armlet->opcode);
		TRACE("rd = %d ", armlet->rd);
		TRACE("rx = %d ", armlet->rx);
		TRACE("ry = %d ", armlet->ry);
		TRACE("value = 0x%x \n", armlet->value);
		TRACE("inflags = 0x%x \n", armlet->inflags);
		TRACE("outflags = 0x%x \n", armlet->outflags);
	}

	// decode inflags
	char flagName[4] = { 'V', 'C', 'Z', 'N' };
	output += "[";
	for(int inflag=3; inflag>=0; inflag--)
	{
		if(armlet->inflags & (1<<inflag) )
		{
			output += flagName[inflag];
		}
		else
		{
			output += "x";
		}
	}
	output += "->";
	// decode outflags
	for(int outflag=3; outflag>=0; outflag--)
	{
		if(armlet->outflags & (1<<outflag) )
		{
			output += flagName[outflag];
		}
		else
		{
			output += "x";
		}
	}
	output += "] ";

	// decode opcode
	switch(armlet->opcode)
	{
		
		// implied, no operands, emit()
		case irCLEARTRANS	: opcodeLabel = "cleartrans"; break;
		case irSETTRANS		: opcodeLabel = "settrans"; break;
		case irSETPC		: opcodeLabel = "setpc"; break;

		// one variable and one value operand, emit1()
		case irMOVC			: opcodeLabel = "movc"; break;
		case irGETPC		: opcodeLabel = "getpc"; break;
		case irINTCHECK		: opcodeLabel = "intcheck"; break;
		
		// two variable operands, emit2()
		case irMOV			: opcodeLabel = "mov"; break;
		case irMVN			: opcodeLabel = "mvn"; break;
		case irTST			: opcodeLabel = "tst"; break;
		case irTEQ			: opcodeLabel = "teq"; break;
		case irCMP			: opcodeLabel = "cmp"; break;
		case irCMN			: opcodeLabel = "cmn"; break;
		case irRRX			: opcodeLabel = "rrx"; break;

		// three variable operands, emit3()
		case irAND			: opcodeLabel = "and"; break;
		case irEOR			: opcodeLabel = "eor"; break;
		case irSUB			: opcodeLabel = "sub"; break;
		case irADD			: opcodeLabel = "add"; break;
		case irADC			: opcodeLabel = "adc"; break;
		case irSBC			: opcodeLabel = "sbc"; break;
		case irORR			: opcodeLabel = "orr"; break;
		case irLSL			: opcodeLabel = "lsl"; break;
		case irLSR			: opcodeLabel = "lsr"; break;
		case irASR			: opcodeLabel = "asr"; break;
		case irROR			: opcodeLabel = "ror"; break;
		case irMUL			: opcodeLabel = "mul"; break;
		case irLDB			: opcodeLabel = "ldb"; break;
		case irSTB			: opcodeLabel = "stb"; break;
		case irLDW			: opcodeLabel = "ldw"; break;
		case irSTW			: opcodeLabel = "stw"; break;
		
		// one value operand, emitV()
		case irGOTOEQ		: opcodeLabel = "gotoeq"; break;
		case irGOTONE		: opcodeLabel = "gotone"; break;
		case irGOTOCS		: opcodeLabel = "gotocs"; break;
		case irGOTOCC		: opcodeLabel = "gotocc"; break;
		case irGOTOMI		: opcodeLabel = "gotomi"; break;
		case irGOTOPL		: opcodeLabel = "gotopl"; break;
		case irGOTOVS		: opcodeLabel = "gotovs"; break;
		case irGOTOVC		: opcodeLabel = "gotovc"; break;
		case irGOTOHI		: opcodeLabel = "gotohi"; break;
		case irGOTOLS		: opcodeLabel = "gotols"; break;
		case irGOTOGE		: opcodeLabel = "gotoge"; break;
		case irGOTOLT		: opcodeLabel = "gotolt"; break;
		case irGOTOGT		: opcodeLabel = "gotogt"; break;
		case irGOTOLE		: opcodeLabel = "gotole"; break; 
		case irGOTO			: opcodeLabel = "goto"; break;
		case irGOTONV		: opcodeLabel = "gotonv"; break;
		case irLEAVE		: opcodeLabel = "leave"; break;

		default: opcodeLabel.Format("<ERROR in disassemble armlet, undefined opcode %d>", armlet->opcode);
	}

	CString operands = "";

	// decode operands

	if(armlet->opcode < irLastImplied)
	{
		// implied, no operands, emit()
	}
	else
	{
		if(armlet->opcode < irLastOneVar)
		{
			// one variable and one value operand, emit1()

			CString value;
			value.Format("0x%x", armlet->value);

			operands = getVariableLabel(armlet->rd) + "," + value;
		}
		else
		{
			if(armlet->opcode < irLastTwoVar)
			{
				if(armlet->ry == vUNUSED)
				{
					// two variable operands that use rd rather than ry i.e. MOV and MVN
					operands = getVariableLabel(armlet->rd) + "," + getVariableLabel(armlet->rx);
				}
				else
				{
					// two variable operands, emit2()
					operands = getVariableLabel(armlet->rx) + "," + getVariableLabel(armlet->ry);
				}
			}
			else
			{
				if(armlet->opcode < irLastThreeVar)
				{
					// three variable operands, emit3()
					operands = getVariableLabel(armlet->rd) + "," + getVariableLabel(armlet->rx) + "," + getVariableLabel(armlet->ry);
				}
				else
				{
					// one value operand, emitV()
					operands.Format("0x%x", armlet->value);
				}
			}
		}
	}

	output += opcodeLabel;
	
	// if more operands allow spaces for 20 chars and 3 spaces
	if(operands != "")
	{
		while( output.GetLength() < 23)
			output += " ";
	}

	output += operands;

	if(armlet->leader)
	{
		output += " <--";
	}

	return output;
}

//
// return the string for the given variable name
//

CString CArmletDisassembler::getVariableLabel(uint variable)
{
	// bounds check before look up
	if(variable > maxTemp+1)
	{
		CString errorString;
		errorString.Format(" INVALID VARIABLE NUMBER: 0x%x ", variable);
		return errorString;
	}

	const CString labels[vMODE+2] = {
		"r0",
		"r1",
		"r2",
		"r3",
		"r4",
		"r5",
		"r6",
		"r7",
		"r8",
		"r9",
		"r10",
		"r11",
		"r12",
		"r13",
		"r14",
		"pc",
		"nflag",
		"zflag",
		"cflag",
		"vflag",
		"iflag",
		"fflag",
		"mode",
		"UNUSED - error in profiler or disassembler, this should not appear",
	};

	if(variable >= vT0)
	{
		// temp
		CString tempLabel = "";
		tempLabel.Format("t%d", variable - vT0 );
		return tempLabel;
	}
	else
	{
		return labels[variable];
	}
}
