Source

gb_emulator / gb_debugger_msvs / disassembly_stream.cpp

Full commit
/*  Copyright Š 2011 Chris Spencer <spencercw@gmail.com>

    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 3 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, see <http://www.gnu.org/licenses/>.  */

#include "stdafx.h"

#include "disassembly_stream.h"

#include <iomanip>
#include <limits>
#include <sstream>

using boost::shared_ptr;
using std::hex;
using std::map;
using std::numeric_limits;
using std::setfill;
using std::setw;
using std::uppercase;
using std::wostringstream;
using std::wstring;

// IDebugDisassemblyStream2 methods

HRESULT CDisassemblyStream::Read(DWORD dwInstructions,
	DISASSEMBLY_STREAM_FIELDS dwFields,
	DWORD *pdwInstructionsRead,
	DisassemblyData *prgDisassembly)
{
	if (!pdwInstructionsRead || !prgDisassembly)
	{
		return E_POINTER;
	}
	if (!dwInstructions)
	{
		return E_INVALIDARG;
	}

	memset(prgDisassembly, 0, sizeof(DisassemblyData) * dwInstructions);
	*pdwInstructionsRead = 0;

	if (instruction_ == disassembly_.end())
	{
		return S_FALSE;
	}

	for (DWORD i = 0; i != dwInstructions; ++i)
	{
		if (dwFields & DSF_ADDRESS)
		{
			wostringstream oss;
			oss << hex << uppercase << setfill(L'0') << setw(6) << instruction_->second->address;
			wstring address = oss.str();
			
			prgDisassembly[i].dwFields |= DSF_ADDRESS;
			prgDisassembly[i].bstrAddress = SysAllocString(address.c_str());
		}
		if (dwFields & DSF_ADDRESSOFFSET)
		{
			wostringstream oss;
			oss << hex << uppercase << setfill(L'0') << setw(6) << instruction_->second->address;
			wstring address = oss.str();
			
			prgDisassembly[i].dwFields |= DSF_ADDRESSOFFSET;
			prgDisassembly[i].bstrAddressOffset = SysAllocString(address.c_str());
		}
		if (dwFields & DSF_CODEBYTES)
		{
			wostringstream oss;
			oss << hex << uppercase << setfill(L'0');
			for (uint8_t j = 0; j != instruction_->second->codeBytesLen; ++j)
			{
				oss << setw(2) << static_cast<unsigned>(instruction_->second->codeBytes[j]);
				if (j != instruction_->second->codeBytesLen - 1)
				{
					oss << L" ";
				}
			}
			wstring codeBytes = oss.str();

			prgDisassembly[i].dwFields |= DSF_CODEBYTES;
			prgDisassembly[i].bstrCodeBytes = SysAllocString(codeBytes.c_str());
		}
		if (dwFields & DSF_OPCODE)
		{
			CComBSTR str(instruction_->second->opcode.c_str());
			prgDisassembly[i].dwFields |= DSF_OPCODE;
			prgDisassembly[i].bstrOpcode = str;
			str.Detach();
		}
		if (dwFields & DSF_OPERANDS)
		{
			CComBSTR str(instruction_->second->operands.c_str());
			prgDisassembly[i].dwFields |= DSF_OPERANDS;
			prgDisassembly[i].bstrOperands = str;
			str.Detach();
		}
		if (dwFields & DSF_CODELOCATIONID)
		{
			prgDisassembly[i].dwFields |= DSF_CODELOCATIONID;
			prgDisassembly[i].uCodeLocationId = instruction_->second->address;
		}
		if (dwFields & DSF_FLAGS)
		{
			prgDisassembly[i].dwFields |= DSF_FLAGS;
			if (instruction_->second->address == address_)
			{
				prgDisassembly[i].dwFlags = DF_INSTRUCTION_ACTIVE;
			}
		}

		++*pdwInstructionsRead;
		if (++instruction_ == disassembly_.end())
		{
			break;
		}
		address_ = instruction_->first;
	}

	return S_OK;
}

HRESULT CDisassemblyStream::Seek(SEEK_START dwSeekStart,
	IDebugCodeContext2 *pCodeContext,
	UINT64 uCodeLocationId,
	INT64 iInstructions)
{
	(void) pCodeContext;
	(void) uCodeLocationId;

	switch (dwSeekStart)
	{
	case SEEK_START_BEGIN:
		if (iInstructions < 0 || static_cast<size_t>(iInstructions) >= disassembly_.size())
		{
			return S_FALSE;
		}
		else
		{
			instruction_ = disassembly_.begin();
			advance(instruction_, iInstructions);
			address_ = instruction_->first;
			return S_OK;
		}

	case SEEK_START_CURRENT:
		if (iInstructions < 0)
		{
			for (; iInstructions && instruction_ != disassembly_.begin(); ++iInstructions)
			{
				--instruction_;
			}
		}
		else if (iInstructions > 0)
		{
			for (; iInstructions && instruction_ != disassembly_.end(); --iInstructions)
			{
				++instruction_;
			}
			if (instruction_ == disassembly_.end())
			{
				--instruction_;
			}
		}

		address_ = instruction_->first;
		return S_OK;

	default:
		return E_NOTIMPL;
	}
}

HRESULT CDisassemblyStream::GetCodeLocationId(IDebugCodeContext2 *pCodeContext,
	UINT64 *puCodeLocationId)
{
	CCodeContext *context = NULL;
	HRESULT hr = pCodeContext->QueryInterface(IID_GbDebuggerMsvsCodeContext,
		reinterpret_cast<void **>(&context));
	if (!SUCCEEDED(hr))
	{
		return hr;
	}

	*puCodeLocationId = context->GetAddress();
	context->Release();
	return S_OK;
}

HRESULT CDisassemblyStream::GetCodeContext(UINT64 uCodeLocationId,
	IDebugCodeContext2 **ppCodeContext)
{
	if (disassembly_.find(static_cast<uint32_t>(uCodeLocationId)) == disassembly_.end())
	{
		return E_INVALIDARG;
	}

	CComObject<CCodeContext> *context;
	CComObject<CCodeContext>::CreateInstance(&context);
	context->AddRef();
	context->Init(NULL, static_cast<uint32_t>(uCodeLocationId), module_);

	*ppCodeContext = context;
	return S_OK;
}

HRESULT CDisassemblyStream::GetCurrentLocation(UINT64 *puCodeLocationId)
{
	*puCodeLocationId = address_;
	return S_OK;
}

HRESULT CDisassemblyStream::GetDocument(BSTR bstrDocumentUrl, IDebugDocument2 **ppDocument)
{
	(void) bstrDocumentUrl;
	(void) ppDocument;

	return E_NOTIMPL;
}

HRESULT CDisassemblyStream::GetScope(DISASSEMBLY_STREAM_SCOPE *pdwScope)
{
	*pdwScope = DSS_MODULE;
	return S_OK;
}

HRESULT CDisassemblyStream::GetSize(UINT64 *pnSize)
{
	*pnSize = disassembly_.size();
	return S_OK;
}

// CDisassemblyStream methods

void CDisassemblyStream::Init(CCodeContext *context)
{
	module_ = context->GetModule();
	disassembly_ = gbd->disassembly();
	SetAddress(context->GetAddress());
}

void CDisassemblyStream::SetAddress(uint32_t address)
{
	// Find the instruction at the address of the code context
	instruction_ = disassembly_.find(address);
	if (instruction_ == disassembly_.end())
	{
		// Not found; just set it to the beginning
		instruction_ = disassembly_.begin();
	}
	address_ = instruction_->first;
}