⛏️ index : haiku.git

/*
 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2011-2016, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */

#include "RegistersView.h"

#include <stdio.h>

#include <new>

#include <ControlLook.h>
#include <MenuItem.h>
#include <PopUpMenu.h>
#include <Window.h>

#include "table/TableColumns.h"

#include "AppMessageCodes.h"
#include "Architecture.h"
#include "AutoDeleter.h"
#include "CpuState.h"
#include "GuiSettingsUtils.h"
#include "Register.h"
#include "UiUtils.h"


enum {
	MSG_SIMD_RENDER_FORMAT_CHANGED = 'srfc'
};


static const char*
GetLabelForSIMDFormat(int format)
{
	switch (format) {
			case SIMD_RENDER_FORMAT_INT8:
				return "8-bit integer";
			case SIMD_RENDER_FORMAT_INT16:
				return "16-bit integer";
			case SIMD_RENDER_FORMAT_INT32:
				return "32-bit integer";
			case SIMD_RENDER_FORMAT_INT64:
				return "64-bit integer";
			case SIMD_RENDER_FORMAT_FLOAT:
				return "Float";
			case SIMD_RENDER_FORMAT_DOUBLE:
				return "Double";
	}

	return "Unknown";
}


// #pragma mark - RegisterValueColumn


class RegistersView::RegisterValueColumn : public StringTableColumn {
public:
	RegisterValueColumn(int32 modelIndex, const char* title, float width,
		float minWidth, float maxWidth, uint32 truncate = B_TRUNCATE_MIDDLE,
		alignment align = B_ALIGN_RIGHT)
		:
		StringTableColumn(modelIndex, title, width, minWidth, maxWidth,
			truncate, align)
	{
	}

protected:
	virtual BField* PrepareField(const BVariant& value) const
	{
		char buffer[64];
		return StringTableColumn::PrepareField(
			BVariant(UiUtils::VariantToString(value, buffer, sizeof(buffer)),
				B_VARIANT_DONT_COPY_DATA));
	}

	virtual int CompareValues(const BVariant& a, const BVariant& b)
	{
		// If neither value is a number, compare the strings. If only one value
		// is a number, it is considered to be greater.
		if (!a.IsNumber()) {
			if (b.IsNumber())
				return -1;
			char bufferA[64];
			char bufferB[64];
			return StringTableColumn::CompareValues(
				BVariant(UiUtils::VariantToString(a, bufferA,
						sizeof(bufferA)),
					B_VARIANT_DONT_COPY_DATA),
				BVariant(UiUtils::VariantToString(b, bufferB,
						sizeof(bufferB)),
					B_VARIANT_DONT_COPY_DATA));
		}

		if (!b.IsNumber())
			return 1;

		// If either value is floating point, we compare floating point values.
		if (a.IsFloat() || b.IsFloat()) {
			double valueA = a.ToDouble();
			double valueB = b.ToDouble();
			return valueA < valueB ? -1 : (valueA == valueB ? 0 : 1);
		}

		uint64 valueA = a.ToUInt64();
		uint64 valueB = b.ToUInt64();
		return valueA < valueB ? -1 : (valueA == valueB ? 0 : 1);
	}
};


// #pragma mark - RegisterTableModel


class RegistersView::RegisterTableModel : public TableModel {
public:
	RegisterTableModel(Architecture* architecture)
		:
		fArchitecture(architecture),
		fCpuState(NULL),
		fSIMDFormat(SIMD_RENDER_FORMAT_INT16)
	{
	}

	~RegisterTableModel()
	{
	}

	void SetCpuState(CpuState* cpuState)
	{
		fCpuState = cpuState;

		NotifyRowsChanged(0, CountRows());
	}

	virtual int32 CountColumns() const
	{
		return 2;
	}

	virtual int32 CountRows() const
	{
		return fArchitecture->CountRegisters();
	}

	inline int32 SIMDRenderFormat() const
	{
		return fSIMDFormat;
	}

	virtual bool GetValueAt(int32 rowIndex, int32 columnIndex, BVariant& value)
	{
		if (rowIndex < 0 || rowIndex >= fArchitecture->CountRegisters())
			return false;

		const Register* reg = fArchitecture->Registers() + rowIndex;

		switch (columnIndex) {
			case 0:
				value.SetTo(reg->Name(), B_VARIANT_DONT_COPY_DATA);
				return true;
			case 1:
				if (fCpuState == NULL)
					return false;
				if (!fCpuState->GetRegisterValue(reg, value))
					value.SetTo("?", B_VARIANT_DONT_COPY_DATA);
				else if (reg->Format() == REGISTER_FORMAT_SIMD) {
					BString output;
					value.SetTo(UiUtils::FormatSIMDValue(value,
						reg->BitSize(),fSIMDFormat, output));
				}
				return true;
			default:
				return false;
		}
	}

	void SetSIMDFormat(int32 format)
	{
		if (fSIMDFormat != format) {
			fSIMDFormat = format;
			NotifyRowsChanged(0, CountRows());
		}
	}

private:
private:
	Architecture*	fArchitecture;
	CpuState*		fCpuState;
	int32			fSIMDFormat;
};


// #pragma mark - RegistersView


RegistersView::RegistersView(Architecture* architecture)
	:
	BGroupView(B_VERTICAL),
	fArchitecture(architecture),
	fCpuState(NULL),
	fRegisterTable(NULL),
	fRegisterTableModel(NULL)
{
	SetName("Registers");
}


RegistersView::~RegistersView()
{
	SetCpuState(NULL);
	fRegisterTable->SetTableModel(NULL);
	delete fRegisterTableModel;
}


/*static*/ RegistersView*
RegistersView::Create(Architecture* architecture)
{
	RegistersView* self = new RegistersView(architecture);

	try {
		self->_Init();
	} catch (...) {
		delete self;
		throw;
	}

	return self;
}


void
RegistersView::MessageReceived(BMessage* message)
{
	switch (message->what) {
		case MSG_SIMD_RENDER_FORMAT_CHANGED:
			{
				int32 format;
				if (message->FindInt32("format", &format) != B_OK)
					break;

				fRegisterTableModel->SetSIMDFormat(format);
			}
			break;

		default:
			BGroupView::MessageReceived(message);
			break;
	}
}


void
RegistersView::SetCpuState(CpuState* cpuState)
{
	if (cpuState == fCpuState)
		return;

	if (fCpuState != NULL)
		fCpuState->ReleaseReference();

	fCpuState = cpuState;

	if (fCpuState != NULL)
		fCpuState->AcquireReference();

	fRegisterTableModel->SetCpuState(fCpuState);
}


void
RegistersView::LoadSettings(const BMessage& settings)
{
	BMessage tableSettings;
	if (settings.FindMessage("registerTable", &tableSettings) == B_OK) {
		GuiSettingsUtils::UnarchiveTableSettings(tableSettings,
			fRegisterTable);
	}
}


status_t
RegistersView::SaveSettings(BMessage& settings)
{
	settings.MakeEmpty();

	BMessage tableSettings;
	status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings,
		fRegisterTable);
	if (result == B_OK)
		result = settings.AddMessage("registerTable", &tableSettings);

	return result;
}


void
RegistersView::TableRowInvoked(Table* table, int32 rowIndex)
{
}


void
RegistersView::TableCellMouseDown(Table* table, int32 rowIndex,
	int32 columnIndex, BPoint screenWhere,	uint32 buttons)
{
	if (rowIndex < 0 || rowIndex >= fArchitecture->CountRegisters())
		return;

	if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0)
		return;

	BVariant value;
	if (!fRegisterTableModel->GetValueAt(rowIndex, 1, value))
		return;

	const Register* reg = fArchitecture->Registers() + rowIndex;
	if (reg->Format() == REGISTER_FORMAT_FLOAT) {
		// for floating point registers, we currently have no
		// context menu options to display.
		return;
	}

	BPopUpMenu* menu = new(std::nothrow) BPopUpMenu("Options");
	if (menu == NULL)
		return;

	ObjectDeleter<BPopUpMenu> menuDeleter(menu);

	if (reg->Format() == REGISTER_FORMAT_INTEGER) {
		BMessage* message = new(std::nothrow) BMessage(MSG_SHOW_INSPECTOR_WINDOW);
		if (message == NULL)
			return;

		message->AddUInt64("address", value.ToUInt64());

		ObjectDeleter<BMessage> messageDeleter(message);
		BMenuItem* item = new(std::nothrow) BMenuItem("Inspect", message);
		if (item == NULL)
			return;

		messageDeleter.Detach();
		ObjectDeleter<BMenuItem> itemDeleter(item);
		if (!menu->AddItem(item))
			return;

		itemDeleter.Detach();

		item->SetTarget(Window());
	} else if (reg->Format() == REGISTER_FORMAT_SIMD) {
		BMenu* formatMenu = new(std::nothrow) BMenu("Format");
		if (formatMenu == NULL)
			return;

		ObjectDeleter<BMenu> formatMenuDeleter(formatMenu);
		if (!menu->AddItem(formatMenu))
			return;
		formatMenuDeleter.Detach();

		if (_AddFormatItem(formatMenu, SIMD_RENDER_FORMAT_INT8) != B_OK)
			return;
		if (_AddFormatItem(formatMenu, SIMD_RENDER_FORMAT_INT16) != B_OK)
			return;
		if (_AddFormatItem(formatMenu, SIMD_RENDER_FORMAT_INT32) != B_OK)
			return;
		if (_AddFormatItem(formatMenu, SIMD_RENDER_FORMAT_INT64) != B_OK)
			return;
		if (_AddFormatItem(formatMenu, SIMD_RENDER_FORMAT_FLOAT) != B_OK)
			return;
		if (_AddFormatItem(formatMenu, SIMD_RENDER_FORMAT_DOUBLE) != B_OK)
			return;

		formatMenu->SetTargetForItems(this);
	}

	menuDeleter.Detach();

	BRect mouseRect(screenWhere, screenWhere);
	mouseRect.InsetBy(-4.0, -4.0);
	menu->Go(screenWhere, true, false, mouseRect, true);
}


void
RegistersView::_Init()
{
	fRegisterTable = new Table("register list", 0, B_FANCY_BORDER);
	fRegisterTable->SetFont(B_FONT_ROW, be_fixed_font);
	AddChild(fRegisterTable->ToView());

	// columns
	const float padding = be_control_look->DefaultLabelSpacing() * 2;
	fRegisterTable->AddColumn(new StringTableColumn(0, "Register",
		be_plain_font->StringWidth("Register") + padding, 40, 1000,
		B_TRUNCATE_END, B_ALIGN_LEFT));
	fRegisterTable->AddColumn(new RegisterValueColumn(1, "Value",
		be_fixed_font->StringWidth("0xffffffff00000000") + padding, 40, 1000,
		B_TRUNCATE_END, B_ALIGN_RIGHT));

	fRegisterTableModel = new RegisterTableModel(fArchitecture);
	fRegisterTable->SetTableModel(fRegisterTableModel);

	fRegisterTable->AddTableListener(this);
}


status_t
RegistersView::_AddFormatItem(BMenu* menu, int32 format)
{
	BMessage* message = new(std::nothrow) BMessage(
		MSG_SIMD_RENDER_FORMAT_CHANGED);
	if (message == NULL)
		return B_NO_MEMORY;

	ObjectDeleter<BMessage> messageDeleter(message);
	if (message->AddInt32("format", format) != B_OK)
		return B_NO_MEMORY;

	BMenuItem* item = new(std::nothrow) BMenuItem(
		GetLabelForSIMDFormat(format), message);
	if (item == NULL)
		return B_NO_MEMORY;

	messageDeleter.Detach();
	ObjectDeleter<BMenuItem> itemDeleter(item);
	if (!menu->AddItem(item))
		return B_NO_MEMORY;

	itemDeleter.Detach();

	if (format == fRegisterTableModel->SIMDRenderFormat())
		item->SetMarked(true);

	return B_OK;
}