/*
Copyright (C) 2006 Evan Teran
                   eteran@alum.rit.edu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "Operand.h"
#include "Instruction.h"
#include <iostream>
#include <sstream>
#include <iomanip>
#include <limits>

const char *Operand::RegisterName[] = {
	"",
	
	"eax", "ecx", "edx", "ebx",
	"esp", "ebp", "esi", "edi",
	
	"ax", "cx", "dx", "bx",
	"sp", "bp", "si", "di",
	
	"al", "cl", "dl", "bl",
	"ah", "ch", "dh", "bh",
	
	"es", "cs", "ss", "ds",
	"fs", "gs", "seg7", "seg8",
	
	"cr0", "cr1", "cr2", "cr3",
	"cr4", "cr5", "cr6", "cr7",
	
	"dr0", "dr1", "dr2", "dr3",
	"dr4", "dr5", "dr6", "dr7",
	
	"tr0", "tr1", "tr2", "tr3",
	"tr4", "tr5", "tr6", "tr7",
	
	"mm0", "mm1", "mm2", "mm3",
	"mm4", "mm5", "mm6", "mm7",
	
	"xmm0", "xmm1", "xmm2", "xmm3",
	"xmm4", "xmm5", "xmm6", "xmm7",
	
	"st",
	"st(0)", "st(1)", "st(2)", "st(3)",
	"st(4)", "st(5)", "st(6)", "st(7)",
};

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
Operand::Operand() {

}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
template<typename T>
std::string outputHexString(T value) {
	std::stringstream ss;
	if(value != 0) {
		ss << std::showbase << std::internal << std::hex << std::setw(sizeof(T) * 2 + 2) << std::setfill('0') << static_cast<unsigned long>(value);
	} else {
		ss << 0;
	}
	return ss.str();
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
std::string Operand::formatImmediate() const {
	std::stringstream ss;
	
	switch(m_Type) {
	case TYPE_IMMEDIATE32:
		if(dword <= std::numeric_limits<uint16_t>::max()) {
			// this will lead to a fall through, we can print smaller
		} else {
			if(sdword > -127 && sdword < 128) {
				ss << sdword;
			} else {
				ss << outputHexString(sdword);
			}
			break;
		}
	case TYPE_IMMEDIATE16:
		if(word <= std::numeric_limits<uint8_t>::max()) {
			// this will lead to a fall through, we can print smaller
		} else {		
			if(sword > -127 && sword < 128) {
				ss << sword;
			} else {
				ss << outputHexString(sword);
			}
			break;
		}
	case TYPE_IMMEDIATE8:
		if(sbyte & 0x80) {
			ss << outputHexString(byte);
		} else {
			ss << static_cast<int>(sbyte);
		}
		break;
	case TYPE_IMMEDIATE64:
		ss << outputHexString(sqword);
		break;
	default:
		break;
	}
	
	return ss.str();
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
std::string Operand::formatRelative() const {
	std::stringstream ss;
	
	switch(m_Type) {
	case TYPE_REL8:
		ss << outputHexString(sbyte + m_Owner->rva32() + m_Owner->size());
		break;
	case TYPE_REL16:
		// NOTE: intel truncates EIP to 16-bit here
		ss << outputHexString((sword + m_Owner->rva32() + m_Owner->size()) & 0xffff);
		break;
	case TYPE_REL32:
		ss << outputHexString(sdword + m_Owner->rva32() + m_Owner->size());
		break;
	default:
		break;
	}
	
	return ss.str();
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
std::string Operand::formatExpression() const {
	std::stringstream ss;

	static const char *expressionStrings[] = {
		"",
		"byte ptr ",
		"word ptr ",
		"dword ptr ",
		"fword ptr ",
		"qword ptr ",
		"tbyte ptr ",
		"xmmword ptr ",
	};

	ss << expressionStrings[m_Type - TYPE_EXPRESSION];


	if(m_Owner->prefix() & Instruction::PREFIX_CS) {
		ss << "cs:";
	} else if(m_Owner->prefix() & Instruction::PREFIX_SS) {
		ss << "ss:";
	} else if(m_Owner->prefix() & Instruction::PREFIX_DS) {
		ss << "ds:";
	} else if(m_Owner->prefix() & Instruction::PREFIX_ES) {
		ss << "es:";
	} else if(m_Owner->prefix() & Instruction::PREFIX_FS) {
		ss << "fs:";
	} else if(m_Owner->prefix() & Instruction::PREFIX_GS) {
		ss << "gs:";
	}

	bool only_disp = true;

	ss << '[';

	// the base, if any
	if(expression.base != REG_NONE) {
		ss << registerName(expression.base);
		only_disp = false;
	}

	// the index, if any
	if(expression.index != REG_NONE) {
		if(!only_disp) {
			ss << "+";
		}
		ss << registerName(expression.index);
		only_disp = false;
	}

	// the scale, if any
	if(expression.scale != 1) {
		ss << "*" << static_cast<int>(expression.scale);
	}

	// the displacement, if any
	switch(expression.displacement_type) {
	case DISP_U32:
		if(expression.u_disp32 <= std::numeric_limits<uint16_t>::max()) {
			// this will lead to a fall through, we can print smaller
		} else {
			ss << (!only_disp ? "+" : "") << outputHexString(expression.u_disp32);
			break;
		}
	case DISP_U16:
		if(expression.u_disp16 <= std::numeric_limits<uint8_t>::max()) {
			// this will lead to a fall through, we can print smaller
		} else {
			ss << (!only_disp ? "+" : "") << outputHexString(expression.u_disp16);
			break;
		}
	case DISP_U8:
		if(expression.u_disp8 != 0 || only_disp) {
			ss << (!only_disp ? "+" : "") << outputHexString(expression.u_disp8);
		}
		break;

	case DISP_S32:
		if(expression.u_disp32 <= std::numeric_limits<uint16_t>::max()) {
			// this will lead to a fall through, we can print smaller
		} else {

			if(only_disp) {
				// we only have a displacement, so we wanna display in hex since it is likely an address
				ss << outputHexString(expression.s_disp32);		
			} else {
				ss << std::showpos << static_cast<int32_t>(expression.s_disp32);
			}
			break;
		}
	case DISP_S16:
		if(expression.u_disp16 <= std::numeric_limits<uint8_t>::max()) {
			// this will lead to a fall through, we can print smaller
		} else {
			if(only_disp) {
				// we only have a displacement, so we wanna display in hex since it is likely an address
				ss << outputHexString(expression.s_disp16);		
			} else {
				ss << std::showpos << static_cast<int32_t>(expression.s_disp16);
			}
			break;
		}
	case DISP_S8:
		if(expression.s_disp8 != 0 || only_disp) {
			if(only_disp) {
				// we only have a displacement, so we wanna display in hex since it is likely an address
				ss << outputHexString(expression.s_disp8);		
			} else {
				ss << std::showpos << static_cast<int32_t>(expression.s_disp8);
			}
		}
		break;

	default:
		break;
	}
	ss << ']';
	
	return ss.str();
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
std::string Operand::formatAbsolute() const {
	std::stringstream ss;
	ss << "far " << outputHexString(absolute.seg) << ":" << outputHexString(absolute.offset);
	return ss.str();
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
std::string Operand::format() const {

	std::string ret;

	switch(generalType()) {
	case TYPE_REGISTER:
		ret = registerName(reg);
		break;
	case TYPE_IMMEDIATE:
		ret = formatImmediate();
		break;
	case TYPE_REL:
		ret = formatRelative();
		break;
	case TYPE_EXPRESSION:
		ret = formatExpression();
		break;
	case TYPE_ABSOLUTE:
		ret = formatAbsolute();
		break;
	default:
		throw invalid_operand(m_Owner->size());
		break;
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
int32_t Operand::displacement() const {
	int32_t ret = 0;
	
	switch(expression.displacement_type) {
	case DISP_U8:
		ret = static_cast<int32_t>(expression.u_disp8);
		break;
	case DISP_U16:
		ret = static_cast<int32_t>(expression.u_disp16);
		break;
	case DISP_U32:
		ret = static_cast<int32_t>(expression.u_disp32);
		break;
	case DISP_S8:
		ret = static_cast<int32_t>(expression.s_disp8);
		break;
	case DISP_S16:
		ret = static_cast<int32_t>(expression.s_disp16);
		break;
	case DISP_S32:
		ret = static_cast<int32_t>(expression.s_disp32);
		break;
	default:
		break;
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: 
//------------------------------------------------------------------------------
Operand::Type Operand::generalType() const {
	return static_cast<Type>(static_cast<unsigned int>(m_Type) & TYPE_MASK);
}
