/*
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 "ArchProcessor.h"
#include <QApplication>
#include "QCategoryList.h"
#include "Debugger.h"
#include "DebuggerCoreInterface.h"
#include "State.h"
#include "Configuration.h"
#include "Util.h"
#include <climits>
#include <QVector>
#include "FunctionInfo.h"
#include "Instruction.h"

//------------------------------------------------------------------------------
// Name: ArchProcessor()
// Desc: 
//------------------------------------------------------------------------------
ArchProcessor::ArchProcessor() : m_SplitFlags(0) {
}

//------------------------------------------------------------------------------
// Name: setupRegisterItem(QCategoryList *catagoryList, QTreeWidgetItem *parent, Register reg)
// Desc: 
//------------------------------------------------------------------------------
void ArchProcessor::setupRegisterItem(QCategoryList *catagoryList, QTreeWidgetItem *parent, Register reg) {
	
	Q_CHECK_PTR(catagoryList);
	
	QTreeWidgetItem *const p = catagoryList->addItem(parent, QString());
	
	Q_CHECK_PTR(p);

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

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void ArchProcessor::setupRegisterView(QCategoryList *catagoryList) {

	Q_CHECK_PTR(catagoryList);

	// setup the register view
	QTreeWidgetItem *const gpr = catagoryList->addCatagory(QObject::tr("General Purpose"));
	
	Q_CHECK_PTR(gpr);
	
	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_SplitFlags = new QTreeWidgetItem(m_RegisterViewItems[9]);	
	m_SplitFlags->setText(0, "c p a z s d o");
	
	QTreeWidgetItem *const segs = catagoryList->addCatagory(QObject::tr("Segments"));
	
	Q_CHECK_PTR(segs);
	
	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);

	QTreeWidgetItem *const fpu = catagoryList->addCatagory(QObject::tr("FPU"));
	
	Q_CHECK_PTR(fpu);
	
	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);

	updateRegisterView(QString());
}

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

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

	return ret;
}

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

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void ArchProcessor::doUpdateRegister(QTreeWidgetItem *item, const QString &name, edb::reg_t value) const {
	
	Q_CHECK_PTR(item);

	QString regString;
	int stringLen;
	
	if(edb::v1::getASCIIStringAtAddress(value, regString, edb::v1::config().minStringLength, 256, stringLen)) {
		item->setText(0, QString("%1: %2 ASCII \"%3\"").arg(name).arg(value, EDB_MAX_HEX, 16, QChar('0')).arg(regString));
	} else if(edb::v1::getUTF16StringAtAddress(value, regString, edb::v1::config().minStringLength, 256, stringLen)) {
		item->setText(0, QString("%1: %2 UTF16 \"%3\"").arg(name).arg(value, EDB_MAX_HEX, 16, QChar('0')).arg(regString));
	} else {
		item->setText(0, QString("%1: %2").arg(name).arg(value, EDB_MAX_HEX, 16, QChar('0')));
	}
}

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

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void ArchProcessor::updateRegisterView(const QString &defaultRegionName) {

	const State &state = edb::v1::currentState();
	const QPalette palette = QApplication::palette();

	doUpdateRegister(getRegisterItem(0), "EAX", state.eax);
	doUpdateRegister(getRegisterItem(1), "EBX", state.ebx);
	doUpdateRegister(getRegisterItem(2), "ECX", state.ecx);
	doUpdateRegister(getRegisterItem(3), "EDX", state.edx);
	doUpdateRegister(getRegisterItem(4), "EBP", state.ebp);
	doUpdateRegister(getRegisterItem(5), "ESP", state.esp);
	doUpdateRegister(getRegisterItem(6), "ESI", state.esi);
	doUpdateRegister(getRegisterItem(7), "EDI", state.edi);

	const QString symname = edb::v1::findFunctionSymbol(state.eip, defaultRegionName);

	getRegisterItem(8)->setText(0, QString("EIP: %1 <%2>").arg(state.eip, EDB_MAX_HEX, 16, QChar('0')).arg(symname));
	getRegisterItem(9)->setText(0, QString("EFLAGS: %1").arg(state.eflags, EDB_MAX_HEX, 16, QChar('0')));

	getRegisterItem(10)->setText(0, QString("CS: %1").arg(state.cs & 0xffff, 4, 16, QChar('0')));
	getRegisterItem(11)->setText(0, QString("DS: %1").arg(state.ds & 0xffff, 4, 16, QChar('0')));
	getRegisterItem(12)->setText(0, QString("ES: %1").arg(state.es & 0xffff, 4, 16, QChar('0')));
	getRegisterItem(13)->setText(0, QString("FS: %1").arg(state.fs & 0xffff, 4, 16, QChar('0')));
	getRegisterItem(14)->setText(0, QString("GS: %1").arg(state.gs & 0xffff, 4, 16, QChar('0')));
	getRegisterItem(15)->setText(0, QString("SS: %1").arg(state.ss & 0xffff, 4, 16, QChar('0')));

	// FPU
	// NOTE: this could potentially be done concurrently
	for(int i = 0; i < 8; ++i) {		
		getRegisterItem(16 + i)->setText(0, QString("ST%1: %2").arg(i).arg(state.st[i].toDouble(), 0, 'g', 16));
		getRegisterItem(16 + i)->setForeground(0, (state.st[i] != m_LastState.st[i]) ? Qt::red : palette.text());
	}

	const bool eflagsChanged = state.eflags != m_LastState.eflags;
	if(eflagsChanged) {
		QString eflagString;
		
		eflagString += ((state.eflags & 0x001) ? "C " : "c ");
		eflagString += ((state.eflags & 0x004) ? "P " : "p ");
		eflagString += ((state.eflags & 0x010) ? "A " : "a ");
		eflagString += ((state.eflags & 0x040) ? "Z " : "z ");
		eflagString += ((state.eflags & 0x080) ? "S " : "s ");
		eflagString += ((state.eflags & 0x400) ? "D " : "d ");
		eflagString += ((state.eflags & 0x800) ? "O" : "o");

		m_SplitFlags->setText(0, eflagString);
	}

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

	state.toBackup(m_LastState);
}

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

	// 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("@"));
	
	const FunctionInfo *const info = edb::v1::getFunctionInfo(funcName);
	if(info != 0) {

		QString functionCall = funcName + "(";
		
		const QVector<QChar> params(info->params());
		int i = 0;
		foreach(QChar ch, params) {
		
			edb::reg_t arg;
			edb::v1::debuggerBase->readBytes(edb::v1::currentState().esp + i * sizeof(edb::reg_t) + offset, &arg, sizeof(arg));
			
			QString paramText;
			
			switch(ch.toAscii()) {
			case 's':
			case 'f':
				do {
					QString stringParam;
					int stringLen;
					
					if(edb::v1::getASCIIStringAtAddress(arg, stringParam, edb::v1::config().minStringLength, 256, stringLen)) {
						paramText.sprintf("<0x" EDB_FMT_PTR "> \"%s\"", arg, qPrintable(stringParam));
					} else {
						char character;
						edb::v1::debuggerBase->readBytes(arg, &character, sizeof(character));
						if(character == '\0') {
							paramText.sprintf("<0x" EDB_FMT_PTR "> \"\"", arg);
						} else {						
							paramText.sprintf("0x" EDB_FMT_PTR, arg);
						}
					}
				} while(0);
				break;
					
			case 'p':
				paramText.sprintf("0x" EDB_FMT_PTR, arg);
				break;
			case 'c':
				if(safe_ctype<std::isprint>(arg) || arg == ' ') {
					paramText.sprintf("'%c'", static_cast<char>(arg));
				} else {
					paramText.sprintf("'\\x%02x'", static_cast<quint16>(arg));
				}
				break;
			case 'u':
				paramText.sprintf("%u", static_cast<unsigned int>(arg));
				break;
			case 'i':
				paramText.sprintf("%d", static_cast<int>(arg));
				break;
			}
			
			functionCall += paramText;
			
			if(i + 1 < params.size()) {
				functionCall += ", ";
			}
			++i;
		}
		
		functionCall += ")";
		ret << functionCall;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
bool ArchProcessor::jccTaken(quint8 instructionByte) const {
	
	const edb::reg_t efl = edb::v1::currentState().eflags;
	const bool cf = (efl & 0x0001) != 0;
	const bool pf = (efl & 0x0004) != 0;
	const bool zf = (efl & 0x0040) != 0;
	const bool sf = (efl & 0x0080) != 0;
	const bool of = (efl & 0x0800) != 0;
	
	bool taken = false;
	
	switch(instructionByte & 0x0e) {
	case 0x00:
		taken = of;
		break;
	case 0x02:
		taken = cf;
		break;
	case 0x04:
		taken = zf;
		break;
	case 0x06:
		taken = cf || zf;
		break;
	case 0x08:
		taken = sf;
		break;
	case 0x0a:
		taken = pf;
		break;
	case 0x0c:
		taken = sf != of;
		break;
	case 0x0e:
		taken = zf || sf != of;
		break;
	}
	
	if(instructionByte & 0x01) {
		taken = !taken;
	}
	
	return taken;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void ArchProcessor::analyzeCMov(const edb::Instruction &insn, QStringList &ret) const {
	
	const quint8 *const insn_buf = insn.buffer();

	const bool taken = jccTaken(insn_buf[1 + insn.prefixSize()]);
	
	if(taken) {
		ret << QObject::tr("move performed");
	} else {
		ret << QObject::tr("move NOT performed");
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void ArchProcessor::analyzeJump(const edb::Instruction &insn, QStringList &ret) const {

	bool taken = false;
	
	const quint8 *const insn_buf = insn.buffer();
	
	if(insn.type() == edb::Instruction::OP_JCC) {
		
		if(insn.size() - insn.prefixSize() == 2) {
			taken = jccTaken(insn_buf[0 + insn.prefixSize()]);
		} else {
			taken = jccTaken(insn_buf[1 + insn.prefixSize()]);
		}
		
		
		
	// TODO: this is not correct! 0xe3 IS an OP_JCC
	} else if(insn_buf[0] == 0xe3) {
		if(insn.prefix() & edb::Instruction::PREFIX_ADDRESS) {
			taken = (edb::v1::currentState().ecx & 0xffff) == 0;
		} else {
			taken = edb::v1::currentState().ecx == 0;
		}
	}
	
	if(taken) {
		ret << QObject::tr("jump taken");
	} else {
		ret << QObject::tr("jump NOT taken");
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void ArchProcessor::analyzeReturn(const edb::Instruction &insn, QStringList &ret) const {
	Q_UNUSED(insn);

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

	const QString symname = edb::v1::findFunctionSymbol(returnAddress);
	if(!symname.isEmpty()) {
		ret << QString(QObject::tr("return to %1 <%2>")).arg(returnAddress, EDB_MAX_HEX, 16, QChar('0')).arg(symname);
	} else {
		ret << QString(QObject::tr("return to %1")).arg(returnAddress, EDB_MAX_HEX, 16, QChar('0'));
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
edb::address_t ArchProcessor::getEffectiveAddress(const edb::Operand &op) const {
	return getEffectiveAddress(op, edb::v1::currentState());
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
edb::address_t ArchProcessor::getEffectiveAddress(const edb::Operand &op, const State &state) const {
	edb::address_t ret = 0;
	
	// TODO: get registers by index, not string! too slow
	
	if(op.valid()) {
		switch(op.generalType()) {
		case edb::Operand::TYPE_REGISTER:
			ret = state.value(op.registerName(op.reg).c_str());
			break;
		case edb::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();
				
				if(op.owner()->prefix() & edb::Instruction::PREFIX_GS) {
					ret += state.gs_base;
				}

				if(op.owner()->prefix() & edb::Instruction::PREFIX_FS) {
					ret += state.fs_base;
				}
			} while(0);
			break;
		case edb::Operand::TYPE_ABSOLUTE:
			ret = op.absolute.offset;
			if(op.owner()->prefix() & edb::Instruction::PREFIX_GS) {
				ret += state.gs_base;
			}

			if(op.owner()->prefix() & edb::Instruction::PREFIX_FS) {
				ret += state.fs_base;
			}
			break;
		case edb::Operand::TYPE_IMMEDIATE:
			break;
		case edb::Operand::TYPE_REL:
			ret = op.relativeTarget();
			break;	
		default:
			break;
		}
	}
	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void ArchProcessor::analyzeCall(const edb::Instruction &insn, QStringList &ret) const {
	
	const edb::Operand &operand = insn.operand(0);
	
	if(operand.valid()) {

		const edb::address_t effective_address	= getEffectiveAddress(operand);
		const QString tempOperand				= operand.format().c_str();
		QString temp;

		switch(operand.generalType()) {
		case edb::Operand::TYPE_REL:
		case edb::Operand::TYPE_REGISTER:
			do {
				int offset;
				const QString symname = edb::v1::findFunctionSymbol(effective_address, QString(), &offset);
				if(!symname.isEmpty()) {
					ret << temp.sprintf("%s = " EDB_FMT_PTR " <%s>", qPrintable(tempOperand), effective_address, qPrintable(symname));

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

				} else {
					ret << temp.sprintf("%s = " EDB_FMT_PTR, qPrintable(tempOperand), effective_address);
				}
			} while(0);
			break;

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

				if(ok) {
					int offset;
					const QString symname = edb::v1::findFunctionSymbol(target, QString(), &offset);
					if(!symname.isEmpty()) {
						ret << temp.sprintf("%s = [" EDB_FMT_PTR "] = " EDB_FMT_PTR " <%s>", qPrintable(tempOperand), effective_address, target, qPrintable(symname));

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

					} else {
						ret << temp.sprintf("%s = [" EDB_FMT_PTR "] = " EDB_FMT_PTR, qPrintable(tempOperand), effective_address, target);
					}
				} else {
					// could not read from the address
					ret << temp.sprintf("%s = [" EDB_FMT_PTR "] = ?", qPrintable(tempOperand), effective_address);
				}
			} while(0);
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: analyzeOperands(const edb::Instruction &insn, QStringList &ret)
// Desc: 
//------------------------------------------------------------------------------
void ArchProcessor::analyzeOperands(const edb::Instruction &insn, QStringList &ret) const {

	Q_UNUSED(insn);
	
	for(int j = 0; j < edb::Instruction::MAX_OPERANDS; ++j) {
	
		const edb::Operand &operand = insn.operand(j);

		if(operand.valid()) {

			const QString tempOperand = operand.format().c_str();
			
			switch(operand.generalType()) {
			case edb::Operand::TYPE_REL:
			case edb::Operand::TYPE_REGISTER:
				do {
					const edb::address_t effective_address = getEffectiveAddress(operand);
					ret << QString("%1 = %2").arg(tempOperand).arg(effective_address, EDB_MAX_HEX, 16, QChar('0'));
				} while(0);
				break;
			case edb::Operand::TYPE_EXPRESSION:
				do {
					const edb::address_t effective_address = getEffectiveAddress(operand);
					edb::address_t target;

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

					if(ok) {
						switch(operand.completeType()) {
						case edb::Operand::TYPE_EXPRESSION8:
							ret << QString("%1 = [%2] = %3").arg(tempOperand).arg(effective_address, EDB_MAX_HEX, 16, QChar('0')).arg(target & 0xff, 2, 16, QChar('0'));
							break;
						case edb::Operand::TYPE_EXPRESSION16:
							ret << QString("%1 = [%2] = %3").arg(tempOperand).arg(effective_address, EDB_MAX_HEX, 16, QChar('0')).arg(target & 0xffff, 4, 16, QChar('0'));
							break;
						case edb::Operand::TYPE_EXPRESSION32:
						default:
							ret << QString("%1 = [%2] = %3").arg(tempOperand).arg(effective_address, EDB_MAX_HEX, 16, QChar('0')).arg(target & 0xffffffff, 8, 16, QChar('0'));
							break;
						}
					} else {
						ret << QString("%1 = [%2] = ?").arg(tempOperand).arg(effective_address, EDB_MAX_HEX, 16, QChar('0'));
					}
				} while(0);
				break;
			default:
				break;
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: analyzeJumpTargets(const edb::Instruction &insn, QStringList &ret) const
// Desc: 
//------------------------------------------------------------------------------
void ArchProcessor::analyzeJumpTargets(const edb::Instruction &insn, QStringList &ret) const {
	const edb::address_t address		= insn.rva();
	const edb::address_t start_address	= address - 128;
	const edb::address_t end_address	= address + 127;
	
	quint8 buffer[edb::Instruction::MAX_SIZE];
	
	QString temp;
	
	for(edb::address_t addr = start_address; addr < end_address; ++addr) {
		int sz = sizeof(buffer);
		if(edb::v1::getInstructionBytes(addr, buffer, sz)) {
			edb::Instruction insn(buffer, sz, addr, std::nothrow);
			if(insn.valid() && (insn.type() == edb::Instruction::OP_JCC || insn.type() == edb::Instruction::OP_JMP)) {
				const edb::Operand &operand = insn.operand(0);
				
				if(operand.generalType() == edb::Operand::TYPE_REL) {
					const edb::address_t target = operand.relativeTarget();
					
					if(target == address) {
						ret << QString(QObject::tr("possible jump from 0x%1")).arg(addr, EDB_MAX_HEX, 16, QChar('0'));
					}
				}
			}
		}
	}
}

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

	QStringList ret;
	
	Q_CHECK_PTR(edb::v1::debuggerBase);
	
	quint8 buffer[edb::Instruction::MAX_SIZE];

	const bool ok = edb::v1::debuggerBase->readBytes(address, buffer, sizeof(buffer));
	if(ok) {
		edb::Instruction insn(buffer, sizeof(buffer), address, std::nothrow);
		if(insn.valid()) {
			// figure out the instruction type and display some information about it
			switch(insn.type()) {
			case edb::Instruction::OP_CMOVCC:
				analyzeCMov(insn, ret);
				break;
			case edb::Instruction::OP_RET:
				analyzeReturn(insn, ret);
				break;
			case edb::Instruction::OP_JCC:
				analyzeJump(insn, ret);
				// FALL THROUGH!
			case edb::Instruction::OP_JMP:
			case edb::Instruction::OP_CALL:
				analyzeCall(insn, ret);
				break;
			case edb::Instruction::OP_SYSENTER:
				// TODO: implement for a given system
				break;
			default:
				analyzeOperands(insn, ret);
				break;
			}
			
			analyzeJumpTargets(insn, ret);
			
		}
	}
	
	// eliminate duplicates
	// TODO: do this more efficiently
	ret = QStringList::fromSet(ret.toSet());
	
	return ret;
}

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

//------------------------------------------------------------------------------
// Name: isFilling(const edb::Instruction &insn) const
// Desc: 
//------------------------------------------------------------------------------
bool ArchProcessor::isFilling(const edb::Instruction &insn) const {
	bool ret = false;
	
	// fetch the operands
	if(insn.valid()) {
		const edb::Operand operands[edb::Instruction::MAX_OPERANDS] = {
			insn.operand(0),
			insn.operand(1),
			insn.operand(2)
		};

		switch(insn.type()) {
		case edb::Instruction::OP_NOP:
		case edb::Instruction::OP_INT3:
			ret = true;
			break;

		case edb::Instruction::OP_LEA:
			if(operands[0].valid() && operands[1].valid()) {
				if(operands[0].generalType() == edb::Operand::TYPE_REGISTER && operands[1].generalType() == edb::Operand::TYPE_EXPRESSION) {

					edb::Operand::Register reg1;
					edb::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 == edb::Operand::REG_NULL) {
								reg2 = operands[1].expression.index;
								ret = reg1 == reg2;
							} else if(operands[1].expression.index == edb::Operand::REG_NULL) {
								reg2 = operands[1].expression.base;
								ret = reg1 == reg2;
							}
						}
					}
				}
			}
			break;

		case edb::Instruction::OP_MOV:
			if(operands[0].valid() && operands[1].valid()) {
				if(operands[0].generalType() == edb::Operand::TYPE_REGISTER && operands[1].generalType() == edb::Operand::TYPE_REGISTER) {
					ret = operands[0].reg == operands[1].reg;
				}
			}
			break;

		default:
			break;
		}

		if(!ret) {
			if(edb::v1::config().zerosAreFilling) {	
				ret = (QByteArray::fromRawData(reinterpret_cast<const char *>(insn.buffer()), insn.size()) == QByteArray::fromRawData("\x00\x00", 2));
			}
		}
	} else {
			ret = (insn.size() == 1 && insn.buffer()[0] == 0x00);
	}
	
	return ret;
}
