/*
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 "i386ArchProcessor.h"
#include "QCatagoryList.h"
#include "Debugger.h"
#include "DebuggerCoreInterface.h"
#include "Instruction.h"
#include "State.h"
#include "Configuration.h"
#include "Util.h"

const i386ArchProcessor::Function i386ArchProcessor::funcs[] = {
	// initialize our system with some libc definitions
	{ "memchr", FunctionInfo('p', 'c', 'u') },
	{ "memcmp", FunctionInfo('p', 'p', 'u') },
	{ "memcpy", FunctionInfo('p', 'p', 'u') },
	{ "memmove", FunctionInfo('p', 'p', 'u') },
	{ "memset", FunctionInfo('p', 'c', 'u') },
	{ "strcat", FunctionInfo('s', 's') },
	{ "strchr", FunctionInfo('s', 'c') },
	{ "strcmp", FunctionInfo('s', 's') },
	{ "strcoll", FunctionInfo('s', 's') },
	{ "strcpy", FunctionInfo('p', 's') },
	{ "strcspn", FunctionInfo('s', 's') },
	{ "strerror", FunctionInfo('i') },
	{ "strlen", FunctionInfo('s') },
	{ "strncat", FunctionInfo('s', 's', 'u') },
	{ "strncmp", FunctionInfo('s', 's', 'u') },
	{ "strncpy", FunctionInfo('p', 's', 'u') },
	{ "strpbrk", FunctionInfo('s', 's') },
	{ "strrchr", FunctionInfo('s', 'c') },
	{ "strspn", FunctionInfo('s', 's') },
	{ "strstr", FunctionInfo('s', 's') },
	{ "strtok", FunctionInfo('s', 's') },
	{ "strxfrm", FunctionInfo('p', 's', 'u') },
	{ "strdup", FunctionInfo('s') },
	{ "strndup", FunctionInfo('s', 'u') },

	{ "fopen", FunctionInfo('s', 'p') },
	{ "fclose", FunctionInfo('p') },
	{ "fread", FunctionInfo('p', 'u', 'u', 'p') },
	{ "fwrite", FunctionInfo('p', 'u', 'u', 'p') },

	{ "free", FunctionInfo('p') },
	{ "exit", FunctionInfo('i') },
	{ "printf", FunctionInfo('f') },
	{ "fputc", FunctionInfo('c', 'p') },
	{ "fputs", FunctionInfo('s', 'p') },
	{ "putchar", FunctionInfo('c') },
	{ "puts", FunctionInfo('s') },
	{ "remove", FunctionInfo('s') },
	{ "setlocale", FunctionInfo('i', 's') },

	// other functions
	{ "sbrk", FunctionInfo('p') },
	{ "__errno_location", FunctionInfo() },
	{ "__libc_start_main", FunctionInfo('p', 'i', 'p', 'p', 'p', 'p', 'p') },
	{ "getpagesize", FunctionInfo() },
	{ "ioctl", FunctionInfo('i', 'i', 'p') },
	{ "isatty", FunctionInfo('i') },
	{ "poll", FunctionInfo('p', 'u', 'i') },
	{ "getenv", FunctionInfo('s') },
	{ "setenv", FunctionInfo('s', 's', 'i') },
	{ "bindtextdomain", FunctionInfo('s', 's') },
	{ "textdomain", FunctionInfo('s') },
	{ "getopt", FunctionInfo('i', 'p', 's') },
	{ "getopt_long", FunctionInfo('i', 'p', 's', 'p', 'p') },
};

QMap<QString, FunctionInfo> i386ArchProcessor::m_FunctionInfo;
int i386ArchProcessor::m_FuncInitialized = 0;

//------------------------------------------------------------------------------
// Name: i386ArchProcessor()
// Desc: 
//------------------------------------------------------------------------------
i386ArchProcessor::i386ArchProcessor() : m_SplitEFlags(0) {

	if(q_atomic_test_and_set_int(&m_FuncInitialized, 0, 1)) {	
		const size_t count = sizeof(funcs)/sizeof(funcs[0]);
		for(const Function *p = funcs; p != funcs + count; ++p) {
			m_FunctionInfo.insert(p->name, p->f);
		}
	}
}

//------------------------------------------------------------------------------
// Name: setupRegisterItem(QCatagoryList *catagoryList, QTreeWidgetItem *parent, Register reg)
// Desc: 
//------------------------------------------------------------------------------
void i386ArchProcessor::setupRegisterItem(QCatagoryList *catagoryList, QTreeWidgetItem *parent, Register reg) {
	
	Q_ASSERT(catagoryList != 0);
	
	QTreeWidgetItem *const p = catagoryList->addItem(parent, "");
	
	Q_ASSERT(p != 0);

	p->setData(0, 1, reg);
	m_RegisterViewItems.push_back(p);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void i386ArchProcessor::setupRegisterView(QCatagoryList *catagoryList) {

	Q_ASSERT(catagoryList != 0);

	// setup the register view
	QTreeWidgetItem *const gpr = catagoryList->addCatagory("General Purpose");
	
	Q_ASSERT(gpr != 0);
	
	setupRegisterItem(catagoryList, gpr, REG_EAX);
	setupRegisterItem(catagoryList, gpr, REG_EBX);
	setupRegisterItem(catagoryList, gpr, REG_ECX);
	setupRegisterItem(catagoryList, gpr, REG_EDX);
	setupRegisterItem(catagoryList, gpr, REG_EBP);
	setupRegisterItem(catagoryList, gpr, REG_ESP);
	setupRegisterItem(catagoryList, gpr, REG_ESI);
	setupRegisterItem(catagoryList, gpr, REG_EDI);
	setupRegisterItem(catagoryList, gpr, REG_EIP);
	setupRegisterItem(catagoryList, gpr, REG_EFLAGS);
	
	// split eflags view
	m_SplitEFlags = new QTreeWidgetItem(m_RegisterViewItems[9]);	
	m_SplitEFlags->setText(0, "c p a z s d o");
	
	QTreeWidgetItem *const segs = catagoryList->addCatagory("Segments");
	
	Q_ASSERT(segs != 0);
	
	setupRegisterItem(catagoryList, segs, REG_CS);
	setupRegisterItem(catagoryList, segs, REG_DS);
	setupRegisterItem(catagoryList, segs, REG_ES);
	setupRegisterItem(catagoryList, segs, REG_FS);
	setupRegisterItem(catagoryList, segs, REG_GS);
	setupRegisterItem(catagoryList, segs, REG_SS);
#ifdef FPU
	QTreeWidgetItem *const fpu = catagoryList->addCatagory("FPU");
	
	Q_ASSERT(fpu != 0);
	
	setupRegisterItem(catagoryList, fpu, REG_ST0);
	setupRegisterItem(catagoryList, fpu, REG_ST1);
	setupRegisterItem(catagoryList, fpu, REG_ST2);
	setupRegisterItem(catagoryList, fpu, REG_ST3);
	setupRegisterItem(catagoryList, fpu, REG_ST4);
	setupRegisterItem(catagoryList, fpu, REG_ST5);
	setupRegisterItem(catagoryList, fpu, REG_ST6);
	setupRegisterItem(catagoryList, fpu, REG_ST7);
#endif

	updateRegisterView("");
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
edb::reg_t *i386ArchProcessor::valueFromItem(const QTreeWidgetItem &item, unsigned int mode) {

	const Register reg = static_cast<Register>(item.data(0, 1).toUInt());
	edb::reg_t *ret = 0;
	
	switch(reg) {
	case REG_EAX:		if(mode & State::REG_GPR) ret = &Debugger::currentState().eax; break;
	case REG_EBX:		if(mode & State::REG_GPR) ret = &Debugger::currentState().ebx; break;
	case REG_ECX:		if(mode & State::REG_GPR) ret = &Debugger::currentState().ecx; break;
	case REG_EDX:		if(mode & State::REG_GPR) ret = &Debugger::currentState().edx; break;
	case REG_EBP:		if(mode & State::REG_GPR) ret = &Debugger::currentState().ebp; break;
	case REG_ESP:		if(mode & State::REG_GPR) ret = &Debugger::currentState().esp; break;
	case REG_ESI:		if(mode & State::REG_GPR) ret = &Debugger::currentState().esi; break;
	case REG_EDI:		if(mode & State::REG_GPR) ret = &Debugger::currentState().edi; break;
	case REG_EFLAGS:	if(mode & State::REG_COND) ret = &Debugger::currentState().eflags; break;
	case REG_EIP:		if(mode & State::REG_IP) ret = &Debugger::currentState().eip; break;
	case REG_CS:		if(mode & State::REG_SEG) ret = &Debugger::currentState()._cs; break;
	case REG_DS:		if(mode & State::REG_SEG) ret = &Debugger::currentState()._ds; break;
	case REG_ES:		if(mode & State::REG_SEG) ret = &Debugger::currentState()._es; break;
	case REG_FS:		if(mode & State::REG_SEG) ret = &Debugger::currentState()._fs; break;
	case REG_GS:		if(mode & State::REG_SEG) ret = &Debugger::currentState()._gs; break;
	case REG_SS:		if(mode & State::REG_SEG) ret = &Debugger::currentState()._ss; break;
	/*
	case REG_ST0:		if(mode & State::REG_FPU) ret = &Debugger::currentState().st[0]; break;
	case REG_ST1:		if(mode & State::REG_FPU) ret = &Debugger::currentState().st[1]; break;
	case REG_ST2:		if(mode & State::REG_FPU) ret = &Debugger::currentState().st[2]; break;
	case REG_ST3:		if(mode & State::REG_FPU) ret = &Debugger::currentState().st[3]; break;
	case REG_ST4:		if(mode & State::REG_FPU) ret = &Debugger::currentState().st[4]; break;
	case REG_ST5:		if(mode & State::REG_FPU) ret = &Debugger::currentState().st[5]; break;
	case REG_ST6		if(mode & State::REG_FPU) ret = &Debugger::currentState().st[6]; break;
	case REG_ST7:		if(mode & State::REG_FPU) ret = &Debugger::currentState().st[7]; break;
	*/
	default:
		break;
	}

	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
QTreeWidgetItem *i386ArchProcessor::getRegisterItem(unsigned int index) {
	Q_ASSERT(index < static_cast<unsigned int>(m_RegisterViewItems.size()));
	return m_RegisterViewItems[index];
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void i386ArchProcessor::doUpdateRegister(QTreeWidgetItem *item, const QString &name, edb::reg_t value) const {
	
	Q_ASSERT(item != 0);

	QString regString;
	int stringLen;
	// TODO: uses elided text for ascii string?
	if(Debugger::getASCIIStringAtAddress(value, regString, Debugger::config()->minAsciiStringLength, 128, stringLen)) {
		item->setText(0, QString().sprintf("%s: %08x ASCII \"%s\"", qPrintable(name), value, qPrintable(regString)));
	} else {
		item->setText(0, QString().sprintf("%s: %08x", qPrintable(name), value));
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void i386ArchProcessor::reset() {
	m_LastState.clear();
	m_SplitEFlags->setText(0, "c p a z s d o");
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void i386ArchProcessor::updateRegisterView(const QString &defaultRegionName) {
	doUpdateRegister(getRegisterItem(0), "EAX", Debugger::currentState().eax);
	doUpdateRegister(getRegisterItem(1), "EBX", Debugger::currentState().ebx);
	doUpdateRegister(getRegisterItem(2), "ECX", Debugger::currentState().ecx);
	doUpdateRegister(getRegisterItem(3), "EDX", Debugger::currentState().edx);
	doUpdateRegister(getRegisterItem(4), "EBP", Debugger::currentState().ebp);
	doUpdateRegister(getRegisterItem(5), "ESP", Debugger::currentState().esp);
	doUpdateRegister(getRegisterItem(6), "ESI", Debugger::currentState().esi);
	doUpdateRegister(getRegisterItem(7), "EDI", Debugger::currentState().edi);

	const QString symname = Debugger::findFunctionSymbol(Debugger::currentState().eip, defaultRegionName);
	getRegisterItem(8)->setText(0, QString().sprintf("EIP: %08x <%s>", Debugger::currentState().eip, qPrintable(symname)));
	getRegisterItem(9)->setText(0, QString().sprintf("EFLAGS: %08x", Debugger::currentState().eflags));

	getRegisterItem(10)->setText(0, QString().sprintf("CS: %04x", Debugger::currentState().cs & 0xffff));
	getRegisterItem(11)->setText(0, QString().sprintf("DS: %04x", Debugger::currentState().ds & 0xffff));
	getRegisterItem(12)->setText(0, QString().sprintf("ES: %04x", Debugger::currentState().es & 0xffff));
	getRegisterItem(13)->setText(0, QString().sprintf("FS: %04x", Debugger::currentState().fs & 0xffff));
	getRegisterItem(14)->setText(0, QString().sprintf("GS: %04x", Debugger::currentState().gs & 0xffff));
	getRegisterItem(15)->setText(0, QString().sprintf("SS: %04x", Debugger::currentState().ss & 0xffff));

	// FPU
#ifdef FPU
	for(int i = 0; i < 8; ++i) {
		getRegisterItem(16 + i)->setText(0, QString().sprintf("ST%d: %.16lg", i, Debugger::currentState().st[i].toDouble()));
		getRegisterItem(16 + i)->setForeground(0, QBrush((Debugger::currentState().st[i] != m_LastState.st[i]) ? Qt::red : Qt::black));
	}
#endif

	const bool eflagsChanged = Debugger::currentState().eflags != m_LastState.eflags;
	if(eflagsChanged) {
		QString eflagString;
		if(Debugger::currentState().eflags & 0x001) eflagString += "C "; else eflagString += "c ";
		if(Debugger::currentState().eflags & 0x004) eflagString += "P "; else eflagString += "p ";
		if(Debugger::currentState().eflags & 0x010) eflagString += "A "; else eflagString += "a ";
		if(Debugger::currentState().eflags & 0x040) eflagString += "Z "; else eflagString += "z ";
		if(Debugger::currentState().eflags & 0x080) eflagString += "S "; else eflagString += "s ";
		if(Debugger::currentState().eflags & 0x400) eflagString += "D "; else eflagString += "d ";
		if(Debugger::currentState().eflags & 0x800) eflagString += "O "; else eflagString += "o ";
		m_SplitEFlags->setText(0, eflagString);
	}

	// highlight any changed registers
	getRegisterItem(0)->setForeground(0, QBrush((Debugger::currentState().eax != m_LastState.eax) ? Qt::red : Qt::black));
	getRegisterItem(1)->setForeground(0, QBrush((Debugger::currentState().ebx != m_LastState.ebx) ? Qt::red : Qt::black));
	getRegisterItem(2)->setForeground(0, QBrush((Debugger::currentState().ecx != m_LastState.ecx) ? Qt::red : Qt::black));
	getRegisterItem(3)->setForeground(0, QBrush((Debugger::currentState().edx != m_LastState.edx) ? Qt::red : Qt::black));
	getRegisterItem(4)->setForeground(0, QBrush((Debugger::currentState().ebp != m_LastState.ebp) ? Qt::red : Qt::black));
	getRegisterItem(5)->setForeground(0, QBrush((Debugger::currentState().esp != m_LastState.esp) ? Qt::red : Qt::black));
	getRegisterItem(6)->setForeground(0, QBrush((Debugger::currentState().esi != m_LastState.esi) ? Qt::red : Qt::black));
	getRegisterItem(7)->setForeground(0, QBrush((Debugger::currentState().edi != m_LastState.edi) ? Qt::red : Qt::black));
	getRegisterItem(8)->setForeground(0, QBrush((Debugger::currentState().eip != m_LastState.eip) ? Qt::red : Qt::black));
	getRegisterItem(9)->setForeground(0, QBrush(eflagsChanged ? Qt::red : Qt::black));

	Debugger::currentState().toBackup(m_LastState);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
QStringList i386ArchProcessor::doFunctionParamterResolution(const QString &symname, int offset) const {

	QStringList ret;

	// we will always be removing the last 2 chars "+0" from the string as well
	// as chopping the region prefix we like to prepend to symbols
	QString funcName;
	const int indexColon = symname.indexOf("::");

	if(indexColon != -1) {
		funcName = symname.left(symname.length() - 2).mid(indexColon + 2);
	}

	// safe not to check for -1, it means "rest of string" for the mid function
	funcName = funcName.mid(0, funcName.indexOf("@"));
	
	QMap<QString, FunctionInfo>::const_iterator it = m_FunctionInfo.find(funcName);
	if(it != m_FunctionInfo.end()) {
	
		QString functionCall = funcName + "(";
		
		const QVector<QChar> params(it->params());
		int i = 0;
		foreach(QChar ch, params) {
		
			edb::reg_t arg;
			Debugger::debuggerBase->readBytes(Debugger::currentState().esp + i * sizeof(edb::reg_t) + offset, &arg, sizeof(arg));
			
			switch(ch.toAscii()) {
			case 's':
			case 'f':
				do {
					QString stringParam;
					int stringLen;
					if(Debugger::getASCIIStringAtAddress(arg, stringParam, Debugger::config()->minAsciiStringLength, INT_MAX, stringLen)) {
						functionCall += QString().sprintf("<0x%08x> \"%s\"", arg, qPrintable(stringParam));
					} else {
						char character;
						Debugger::debuggerBase->readBytes(arg, &character, sizeof(character));
						if(character == '\0') {
							functionCall += QString().sprintf("<0x%08x> \"\"", arg);
						} else {						
							functionCall += QString().sprintf("0x%08x", arg);
						}
					}
				} while(0);
				break;
					
			case 'p':
				functionCall += QString().sprintf("0x%08x", arg);
				break;
			case 'c':
				if(safe_ctype<std::isprint>(arg) || arg == ' ') {
					functionCall += QString().sprintf("'%c'", arg);
				} else {
					functionCall += QString().sprintf("'\\x%02x'", arg);
				}
				break;
			case 'u':
				functionCall += QString().sprintf("%u", arg);
				break;
			case 'i':
				functionCall += QString().sprintf("%d", arg);
				break;
			}
			
			if(i + 1 < params.size()) {
				functionCall += ", ";
			}
			++i;
		}
		
		functionCall += ")";
		ret.push_back(functionCall);
	}

	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
bool i386ArchProcessor::jccTaken(uint8_t instructionByte) const {
	
	const bool cf = (Debugger::currentState().eflags & 0x0001) != 0;
	const bool pf = (Debugger::currentState().eflags & 0x0004) != 0;
	const bool zf = (Debugger::currentState().eflags & 0x0040) != 0;
	const bool sf = (Debugger::currentState().eflags & 0x0080) != 0;
	const bool of = (Debugger::currentState().eflags & 0x0800) != 0;
	
	bool taken = false;
	
	switch(instructionByte & 0x0f) {
	case 0x00:
		taken = of;
		break;
	case 0x01:
		taken = !of;
		break;
	case 0x02:
		taken = cf;
		break;
	case 0x03:
		taken = !cf;
		break;
	case 0x04:
		taken = zf;
		break;
	case 0x05:
		taken = !zf;
		break;
	case 0x06:
		taken = cf || zf;
		break;
	case 0x07:
		taken = !cf && !zf;
		break;
	case 0x08:
		taken = sf;
		break;
	case 0x09:
		taken = !sf;
		break;
	case 0x0a:
		taken = pf;
		break;
	case 0x0b:
		taken = pf;
		break;
	case 0x0c:
		taken = sf != of;
		break;
	case 0x0d:
		taken = sf == of;
		break;
	case 0x0e:
		taken = zf || sf != of;
		break;
	case 0x0f:
		taken = !zf && sf == of;
		break;
	}
	
	return taken;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void i386ArchProcessor::analyzeCMov(const Operand *operands, const Instruction &insn, QStringList &ret, const uint8_t *insn_buf) const {
	Q_UNUSED(operands);

	bool taken = false;
	
	if(insn.type() == Instruction::OP_CMOVCC) {
		taken = jccTaken(insn_buf[1 + insn.prefixSize()]);
	}
	
	if(taken) {
		ret.push_back("move performed");
	} else {
		ret.push_back("move NOT performed");
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void i386ArchProcessor::analyzeJump(const Operand *operands, const Instruction &insn, QStringList &ret, const uint8_t *insn_buf) const {

	Q_UNUSED(operands);

	bool taken = false;
	
	if(insn.type() == Instruction::OP_JCC) {
		
		if(insn.size() - insn.prefixSize() == 2) {
			taken = jccTaken(insn_buf[0 + insn.prefixSize()]);
		} else {
			taken = jccTaken(insn_buf[1 + insn.prefixSize()]);
		}
		
	} else if(insn_buf[0] == 0xe3) {
		if(insn.prefix() & Instruction::PREFIX_ADDRESS) {
			taken = (Debugger::currentState().ecx & 0xffff) == 0;
		} else {
			taken = Debugger::currentState().ecx == 0;
		}
	}
	
	if(taken) {
		ret.push_back("jump taken");
	} else {
		ret.push_back("jump NOT taken");
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void i386ArchProcessor::analyzeReturn(const Operand *operands, const Instruction &insn, QStringList &ret, const uint8_t *insn_buf) const {

	Q_UNUSED(operands);
	Q_UNUSED(insn);
	Q_UNUSED(insn_buf);

	edb::address_t returnAddress;
	Debugger::debuggerBase->readBytes(Debugger::currentState().esp, &returnAddress, sizeof(returnAddress));

	const QString symname = Debugger::findFunctionSymbol(returnAddress, "");
	if(!symname.isEmpty()) {
		ret.push_back(QString().sprintf("Return to %08x <%s>", returnAddress, qPrintable(symname)));
	} else {
		ret.push_back(QString().sprintf("Return to %08x", returnAddress));
	}
}

edb::address_t i386ArchProcessor::getEffectiveAddress(const Operand &op) const {
	return getEffectiveAddress(op, Debugger::currentState());
}

edb::address_t i386ArchProcessor::getEffectiveAddress(const Operand &op, const State &state) const {
	uint32_t ret = 0;
	
	// TODO: get registers by index, not string! too slow
	
	if(op.valid()) {
	
		Instruction *const owner = op.owner();
		
		Q_ASSERT(owner != 0);

		switch(op.generalType()) {

		case Operand::TYPE_REGISTER:
			ret = state.value(op.registerName(op.reg).c_str());
			break;
		case Operand::TYPE_EXPRESSION:
			do {
				const edb::reg_t base		= state.value(op.registerName(op.expression.base).c_str());
				const edb::reg_t index	= state.value(op.registerName(op.expression.index).c_str());
				ret								= base + index * op.expression.scale + op.displacement();
			} while(0);
			break;
		case Operand::TYPE_ABSOLUTE:
			// TODO: find a way to take into account the segment base/limit
			ret = op.absolute.offset;
			break;
		case Operand::TYPE_IMMEDIATE:
			break;
		case Operand::TYPE_REL:
			if(op.completeType() == Operand::TYPE_REL8) {
				ret = owner->rva32() + owner->size() + op.sbyte;
			} else if(op.completeType() == Operand::TYPE_REL32) {
				ret = owner->rva32() + owner->size() + op.sdword;
			} else if(op.completeType() == Operand::TYPE_REL16) {
				// NOTE: intel truncates EIP to 16-bit
				ret = (owner->rva32() + owner->size() + op.sword) & 0xffff;
			}
			break;	
		default:
			break;
		}
	}
	return ret;
}


//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void i386ArchProcessor::analyzeCall(const Operand *operands, const Instruction &insn, QStringList &ret, const uint8_t *insn_buf) const {
	
	Q_ASSERT(operands != 0);
	Q_UNUSED(insn_buf);
	
	if(operands[0].valid()) {

		const edb::address_t effective_address = getEffectiveAddress(operands[0]);

		const QString tmp = operands[0].format().c_str();

		switch(operands[0].generalType()) {
		case Operand::TYPE_REL:
		case Operand::TYPE_REGISTER:
			do {
				int offset;
				const QString symname = Debugger::findFunctionSymbol(effective_address, "", &offset);
				if(!symname.isEmpty()) {
					ret.push_back(QString().sprintf("%s = %08x <%s>", qPrintable(tmp), effective_address, qPrintable(symname)));

					if(offset == 0) {
						if(insn.type() == Instruction::OP_CALL) {
							ret += doFunctionParamterResolution(symname, 0);
						} else {
							ret += doFunctionParamterResolution(symname, 4);
						}
					}

				} else {
					ret.push_back(QString().sprintf("%s = %08x", qPrintable(tmp), effective_address));
				}
			} while(0);
			break;

		case Operand::TYPE_EXPRESSION:
		default:
			do {
				edb::address_t target;
				const bool ok = Debugger::debuggerBase->readBytes(effective_address, &target, sizeof(target));

				if(ok) {
					int offset;
					const QString symname = Debugger::findFunctionSymbol(target, "", &offset);
					if(!symname.isEmpty()) {
						ret.push_back(QString().sprintf("%s = [%08x] = %08x <%s>", qPrintable(tmp), effective_address, target, qPrintable(symname)));

						if(offset == 0) {
							if(insn.type() == Instruction::OP_CALL) {
								ret += doFunctionParamterResolution(symname, 0);
							} else {
								ret += doFunctionParamterResolution(symname, 4);
							}
						}

					} else {
						ret.push_back(QString().sprintf("%s = [%08x] = %08x", qPrintable(tmp), effective_address, target));
					}
				} else {
					// could not read from the address
					ret.push_back(QString().sprintf("%s = [%08x] = ?", qPrintable(tmp), effective_address));
				}
			} while(0);
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: analyzeOperands(const Operand *operands, const Instruction &insn, QStringList &ret, const uint8_t *insn_buf)
// Desc: 
//------------------------------------------------------------------------------
void i386ArchProcessor::analyzeOperands(const Operand *operands, const Instruction &insn, QStringList &ret, const uint8_t *insn_buf) const {

	Q_UNUSED(insn);
	Q_UNUSED(insn_buf);
	Q_ASSERT(operands != 0);
	
	for(int j = 0; j < 3; ++j) {

		if(operands[j].valid()) {

			const QString tmp = operands[j].format().c_str();

			switch(operands[j].generalType()) {
			case Operand::TYPE_REL:
			case Operand::TYPE_REGISTER:
				do {
					const edb::address_t effective_address = getEffectiveAddress(operands[j]);
					ret.push_back(QString().sprintf("%s = %08x", qPrintable(tmp), effective_address));
				} while(0);
				break;
			case Operand::TYPE_EXPRESSION:
				do {
					const edb::address_t effective_address = getEffectiveAddress(operands[j]);
					edb::address_t target;

					const bool ok = Debugger::debuggerBase->readBytes(effective_address, &target, sizeof(target));

					if(ok) {
						switch(operands[j].completeType()) {
						case Operand::TYPE_EXPRESSION8:
							ret.push_back(QString().sprintf("%s = [%08x] = %02x", qPrintable(tmp), effective_address, target & 0xff));
							break;
						case Operand::TYPE_EXPRESSION16:
							ret.push_back(QString().sprintf("%s = [%08x] = %04x", qPrintable(tmp), effective_address, target & 0xffff));
							break;
						case Operand::TYPE_EXPRESSION32:
						default:
							ret.push_back(QString().sprintf("%s = [%08x] = %08x", qPrintable(tmp), effective_address, target));
							break;
						}
						
					} else {
						ret.push_back(QString().sprintf("%s = [%08x] = ?", qPrintable(tmp), effective_address));
					}
				} while(0);
				break;
			default:
				break;
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: updateInstructionInfo(edb::address_t address)
// Desc: 
//------------------------------------------------------------------------------
QStringList i386ArchProcessor::updateInstructionInfo(edb::address_t address) {

	QStringList ret;
	
	if(Debugger::debuggerBase != 0) {
		uint8_t buffer[Instruction::MAX_SIZE];
		
		const bool ok = Debugger::debuggerBase->readBytes(address, buffer, sizeof(buffer));
		if(ok) {
			Instruction insn(buffer, sizeof(buffer), address, std::nothrow);
			if(insn.valid()) {

				// fetch the 3 possible operands
				const Operand operands[3] = {
					insn.operand(0),
					insn.operand(1),
					insn.operand(2)
				};

				// figure out the instruction type and display some information about it
				switch(insn.type()) {
				case Instruction::OP_CMOVCC:
					analyzeCMov(operands, insn, ret, buffer);
					break;
				case Instruction::OP_RET:
					analyzeReturn(operands, insn, ret, buffer);
					break;
				case Instruction::OP_JCC:
					analyzeJump(operands, insn, ret, buffer);
					// FALL THROUGH!
				case Instruction::OP_JMP:
				case Instruction::OP_CALL:
					analyzeCall(operands, insn, ret, buffer);
					break;
				default:
					analyzeOperands(operands, insn, ret, buffer);
					break;
				}
			}
		}
	}
	
	// eliminate duplicates
	ret = QStringList::fromSet(ret.toSet());
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
bool i386ArchProcessor::canStepOver(const Instruction &insn) const {
	return insn.valid() && (insn.type() == Instruction::OP_CALL || (insn.prefix() & (Instruction::PREFIX_REPNE | Instruction::PREFIX_REP)));
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
bool i386ArchProcessor::isFilling(const Instruction &insn, const QByteArray &bytes) const {
	bool ret = false;
	
	// fetch the operands
	const Operand operands[3] = {
		insn.operand(0),
		insn.operand(1),
		insn.operand(2)
	};
	
	switch(insn.type()) {
	case Instruction::OP_NOP:
	case Instruction::OP_INT3:
		ret = true;
		break;
	case Instruction::OP_LEA:
		if(operands[0].valid() && operands[1].valid()) {
			if(operands[0].generalType() == Operand::TYPE_REGISTER && operands[1].generalType() == Operand::TYPE_EXPRESSION) {

				Operand::Register reg1;
				Operand::Register reg2;

				reg1 = operands[0].reg;

				if(operands[1].expression.scale == 1) {
					if(operands[1].expression.u_disp32 == 0) {

						if(operands[1].expression.base == Operand::REG_NONE) {
							reg2 = operands[1].expression.index;
							ret = (reg1 == reg2);
						} else if(operands[1].expression.index == Operand::REG_NONE) {
							reg2 = operands[1].expression.base;
							ret = (reg1 == reg2);
						}
					}
				}
			}
		}
		break;
	case Instruction::OP_MOV:
		if(operands[0].valid() && operands[1].valid()) {
			if(operands[0].generalType() == Operand::TYPE_REGISTER && operands[1].generalType() == Operand::TYPE_REGISTER) {
				ret = (operands[0].reg == operands[1].reg);
			}
		}
		break;
	default:
		break;
	}
	
	if(!ret) {
		if(Debugger::config()->zerosAreFilling) {			
			ret = bytes == QByteArray::fromRawData("\x00\x00", 2);
		}
	}
	
	return ret;
}
