// Profiler.h: interface for the CProfiler class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_PROFILER_H__CBCD933F_CDD7_4FB0_96B7_BDF982C2CF09__INCLUDED_)
#define AFX_PROFILER_H__CBCD933F_CDD7_4FB0_96B7_BDF982C2CF09__INCLUDED_

#include "ArmDisassembler.h"	// Added by ClassView
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "TarmacGlobals.h"

// disable warning about labels being too long in the debugger viewer, this
// doesn't affect the generated code
#pragma warning (disable : 4786)
#include <map>

#include "LinkedList.h"
#include "TestMemory.h"


//
// options for status of recompilation from recompilation of an
// individual instruction
//
// ??? unused at the moment
typedef enum
{
	recSTOP = 0,		// don't recompile and more instructions
	recCONTINUE,		// keep recompiling
	recROLLBACK			// don't recompile this instruction and stop
} recOpt;

//
// ARM condition codes
//

typedef enum
{
	ccEQ = 0,
	ccNE,
	ccCS,
	ccCC,
	ccMI,
	ccPL,
	ccVS,
	ccVC,
	ccHI,
	ccLS,
	ccGE,
	ccLT,
	ccGT,
	ccLE,
	ccAL,
	ccNV
} conditionCodes;

//
// armlet opcodes
//

// note, markers are employed at start and end of each class of opcode
// for debugging purposes
typedef enum
{
	// implied, no operands, emit()
	irFirstImplied=0,

	irCLEARTRANS,
	irSETTRANS,
	irSETPC,

	irLastImplied,
	// one variable and one value operand, emit1()
	irFirstOneVar,

	irMOVC,
	irGETPC,
	irINTCHECK,
	
	irLastOneVar,
	// two variable operands, emit2()
	irFirstTwoVar,

	irMOV,
	irMVN,
	irTST,
	irTEQ,
	irCMP,
	irCMN,
	irRRX,

	irLastTwoVar,
	// three variable operands, emit3()
	irFirstThreeVar,

	irAND,
	irEOR,
	irSUB,
	irADD,
	irADC,
	irSBC,
	irORR,
	irLSL,
	irLSR,
	irASR,
	irROR,
	irMUL,
	irLDB,
	irSTB,
	irLDW,
	irSTW,

	irLastThreeVar,
	// one value operand, emitV()
	irFirstValue,

	irGOTOEQ,
	irGOTONE,
	irGOTOCS,
	irGOTOCC,
	irGOTOMI,
	irGOTOPL,
	irGOTOVS,
	irGOTOVC,
	irGOTOHI,
	irGOTOLS,
	irGOTOGE,
	irGOTOLT,
	irGOTOGT,
	irGOTOLE, 
	irGOTO,
	irGOTONV,		// equivalent to nop, here for completeness, never emitted
	irLEAVE,
	
	irLastValue
} armletOpcodes;

//
// reason for leaving chunk (arguments for leave opcode)
//

typedef enum
{
	leaveBranchBackward,		// branch before start of chunk
	leaveBranchForward,			// branch ahead of currently recompiling instruction
	leaveDynamicPC,				// PC has been adjusted (dynamically known only)
	leaveDataAbortException,	// data abort exception has occurred
	leaveSwi,					// swi instruction executed
	leaveAddressException,		// address exception has occurred
	leaveBlockDTUser,			// block data transfer needed to temporarily switch to user mode
	leaveCoProDTPreIndex,		// coprocessor data transfer pre indexed executed
	leaveCoProDTPostIndex,		// coprocessor data transfer post indexed executed
	leaveCoProRTorDO,			// coprocessor register transfer or data operation executed
	leavePSRChanged,			// PSR has been adjusted (e.g. by TEQP in priviledged mode)
	leaveIntCheck				// intcheck opcode found an interrupt had occurred
} leaveReasons;

//
// armlet variables
//

typedef enum
{
	vR0 = 0,
	vR1,
	vR2,
	vR3,
	vR4,
	vR5,
	vR6,
	vR7,
	vR8,
	vR9,
	vR10,
	vR11,
	vR12,
	vR13,
	vR14,
	vPC,
	vNFLAG,
	vZFLAG,
	vCFLAG,
	vVFLAG,
	vIFLAG,
	vFFLAG,
	vMODE,
	vUNUSED,	// exists to fill blanks in MOV,MVN instructions which have 2 operands but access rd (unlike cmp etc.)
	vT0,
	vT1,
	vT2,
	vT3,
	vT4,
	vT5,
	vT6,
	vT7,
	vT8,
	vT9,
	vT10,
	vT11,
	vT12,
	vT13,
	vT14,
	vT15,
	vT16,
	vT17,
	vT18,
	vT19,
	vT20,
	vT21,
	vT22,
	vT23,
	vT24,
	vT25,
	vT26,
	vT27,
	vT28,
	vT29
} armletVariables;



const int maxTemp = vT29;

// combinations of flags that may be set by each instruction
const int fNONE = 0;
const int fALL = 15;
const int fN = 8;
const int fZ = 4;
const int fC = 2;
const int fV = 1;
	
//
// data structure for single Armlet intermediate representation instruction
//

struct Armlet
{
	uint8 inflags;						// bitmap of NZCV flags required
	uint8 outflags;						// bitmap of NZCV flags adjusted
	uint8 opcode;						// armlet opcode
	uint8 rd;
	union
	{
		struct { uint8 rx,ry,rz,ra; };	// 4 x 8bit operands for armlet
		uint32 value;					// 1 x 32bit operand for armlet
	};

	BOOL leader;						// is this armlet the leader of a basic block? - filled in by generator

	uint32 armAddress;					// address of ARM instruction this armlet is part of
	BOOL continued;						// does this armlet continue a previous ARM instruction
};

#include "ArmletDisassembler.h"
#include "Optimiser.h"
#include "CodeCache.h"

class COptimiser;

//
// information about a block of conditionally executed instructions
//

struct ConditionalBlockInfo
{
	uint32 startArmlet;					// number of 1st armlet in block i.e. the GOTOcc
	uint32 endArmlet;					// number of last armlet in block
										// such that endArmlet+1 is the destination of the block's initial GOTO
	uint8 conditionCode;				// condition code of the block
};

//
// debug options
//

const BOOL useTestMemory = TRUE;

//
// Profiler class
// 

class CProfiler  
{
public:
	void setCodeCache(CCodeCache* aCodeCache);
	
	BOOL translateSingleDataSwap(uint32 address, uint32 instruction);
	BOOL translateMultiply(uint32 address, uint32 instruction);
	uint32 readWord(uint32 address);
	BOOL translateARMtoArmlets(uint32 address, uint32 instruction);
	void recompile(uint32 address);
	CProfiler();
	virtual ~CProfiler();

private:
	void checkPBit(uint32 address, uint32 instruction, uint8 op1, uint8 op2, uint8 outFlags);
	uint8 getCondFlags(uint8 conditionCode);
	uint8 useNextTemp();
	void emit(uint8 opcode, uint8 inflags, uint8 outflags);
	void emitV(uint8 opcode, uint32 value, uint8 inflags, uint8 outflags);
	void emit1(uint8 opcode, uint8 rd, uint32 value, uint8 inflags, uint8 outflags);
	void emit2(uint8 opcode, uint8 rx, uint8 ry, uint8 inflags, uint8 outflags);
	void emit3(uint8 opcode, uint8 rd, uint8 rx, uint8 ry, uint8 inflags, uint8 outflags);

	BOOL translateMultiplyOrDataProcessing(uint32 address, uint32 instruction);
	BOOL translateSingleDataSwapOrDataProcessing(uint32 address, uint32 instruction);
	BOOL translateDataProcessing(uint32 address, uint32 instruction);
	BOOL translateSingleDTImmOffsetPostIndex(uint32 address, uint32 instruction);
	BOOL translateSingleDTImmOffsetPreIndex(uint32 address, uint32 instruction);
	BOOL translateSingleDTRegOffsetPostIndex(uint32 address, uint32 instruction);
	BOOL translateSingleDTRegOffsetPreIndex(uint32 address, uint32 instruction);
	BOOL translateBlockDataTransfer(uint32 address, uint32 instruction);
	BOOL translateBranch(uint32 address, uint32 instruction);
	BOOL translateBranchWithLink(uint32 address, uint32 instruction);
	BOOL translateCoProDTPreIndex(uint32 address, uint32 instruction);
	BOOL translateCoProDTPostIndex(uint32 address, uint32 instruction);
	BOOL translateCoProRegTransferOrDataOperation(uint32 address, uint32 instruction);
	BOOL translateSoftwareInterrupt(uint32 address, uint32 instruction);

private:
	void emitInterruptCheck(uint32 address, BOOL updatePC);
	void translateBranchInternal(uint32 destination, uint8 gotoOpcode);
	uint8 invertConditionCode(uint8 conditionCode);
	Armlet* getEmit(uint8 opcode, uint32 value, uint8 rd, uint8 inflags, uint8 outflags);
	void resetAvailableTemps();
	BOOL checkRdForPC(uint8 rd, BOOL sFlag, uint32 address);
	uint32 basicBlockCount;				// number of instructions since last adjustment to PC
	uint8 nextFreeTemp;					// temp variable to use next time one is needed
	BOOL isExtendedInstruction(uint32 instruction);
	BOOL gotoGenerated;					// set if last instruction used a goto in it's body, e.g. a B back inside chunk	
	CLinkedList* conditionalBlockList;	// linked list of all ConditionalBlockInfo's for the current chunk
	ConditionalBlockInfo* currentConditionalBlockInfo; // pointer to info about the current conditional block
	CLinkedList* armletList;			// linked list of all armlets, valid during recompilation only
	Armlet* lastArmlet;					// pointer to last armlet emitted
	int armletCounter;					// number of the last armlet added to chunk (first armlet is 0)
	uint32 startAddress;				// ARM address to start of current chunk
	std::map<uint32, uint32> addressToArmletNumber;
										// mapping from ARM instruction to armlet number
	BOOL inConditionalBlock;			// TRUE if current instruction is part of a conditional block
	BOOL gotoBackpatchNeeded;			// TRUE if there is a goto that needs backpatching
	Armlet* lastGoto;					// the last goto that needs backpatching
	uint8 previousARMConditionCode;		// the previous ARM instruction's condition code
	BOOL flagsAdjusted;					// set if previous ARM instruction adjusted NZCV
										// and therefore can't extend conditional execution
	
	uint32* immediateCarry;		// look up tables for immediate decoding
	uint32* immediateValue;
	COptimiser* optimiser;				// optimiser

	// debugging
	CTestMemory* testMemory;			// test memory buffer
	CStdioFile armReportFile;
	
	
};

#endif // !defined(AFX_PROFILER_H__CBCD933F_CDD7_4FB0_96B7_BDF982C2CF09__INCLUDED_)
