1/*
2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2011-2016, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7#include "RegistersView.h"
8
9#include <stdio.h>
10
11#include <new>
12
13#include <ControlLook.h>
14#include <MenuItem.h>
15#include <PopUpMenu.h>
16#include <Window.h>
17
18#include "table/TableColumns.h"
19
20#include "AppMessageCodes.h"
21#include "Architecture.h"
22#include "AutoDeleter.h"
23#include "CpuState.h"
24#include "GuiSettingsUtils.h"
25#include "Register.h"
26#include "UiUtils.h"
27
28
29enum {
30	MSG_SIMD_RENDER_FORMAT_CHANGED = 'srfc'
31};
32
33
34static const char*
35GetLabelForSIMDFormat(int format)
36{
37	switch (format) {
38			case SIMD_RENDER_FORMAT_INT8:
39				return "8-bit integer";
40			case SIMD_RENDER_FORMAT_INT16:
41				return "16-bit integer";
42			case SIMD_RENDER_FORMAT_INT32:
43				return "32-bit integer";
44			case SIMD_RENDER_FORMAT_INT64:
45				return "64-bit integer";
46			case SIMD_RENDER_FORMAT_FLOAT:
47				return "Float";
48			case SIMD_RENDER_FORMAT_DOUBLE:
49				return "Double";
50	}
51
52	return "Unknown";
53}
54
55
56// #pragma mark - RegisterValueColumn
57
58
59class RegistersView::RegisterValueColumn : public StringTableColumn {
60public:
61	RegisterValueColumn(int32 modelIndex, const char* title, float width,
62		float minWidth, float maxWidth, uint32 truncate = B_TRUNCATE_MIDDLE,
63		alignment align = B_ALIGN_RIGHT)
64		:
65		StringTableColumn(modelIndex, title, width, minWidth, maxWidth,
66			truncate, align)
67	{
68	}
69
70protected:
71	virtual BField* PrepareField(const BVariant& value) const
72	{
73		char buffer[64];
74		return StringTableColumn::PrepareField(
75			BVariant(UiUtils::VariantToString(value, buffer, sizeof(buffer)),
76				B_VARIANT_DONT_COPY_DATA));
77	}
78
79	virtual int CompareValues(const BVariant& a, const BVariant& b)
80	{
81		// If neither value is a number, compare the strings. If only one value
82		// is a number, it is considered to be greater.
83		if (!a.IsNumber()) {
84			if (b.IsNumber())
85				return -1;
86			char bufferA[64];
87			char bufferB[64];
88			return StringTableColumn::CompareValues(
89				BVariant(UiUtils::VariantToString(a, bufferA,
90						sizeof(bufferA)),
91					B_VARIANT_DONT_COPY_DATA),
92				BVariant(UiUtils::VariantToString(b, bufferB,
93						sizeof(bufferB)),
94					B_VARIANT_DONT_COPY_DATA));
95		}
96
97		if (!b.IsNumber())
98			return 1;
99
100		// If either value is floating point, we compare floating point values.
101		if (a.IsFloat() || b.IsFloat()) {
102			double valueA = a.ToDouble();
103			double valueB = b.ToDouble();
104			return valueA < valueB ? -1 : (valueA == valueB ? 0 : 1);
105		}
106
107		uint64 valueA = a.ToUInt64();
108		uint64 valueB = b.ToUInt64();
109		return valueA < valueB ? -1 : (valueA == valueB ? 0 : 1);
110	}
111};
112
113
114// #pragma mark - RegisterTableModel
115
116
117class RegistersView::RegisterTableModel : public TableModel {
118public:
119	RegisterTableModel(Architecture* architecture)
120		:
121		fArchitecture(architecture),
122		fCpuState(NULL),
123		fSIMDFormat(SIMD_RENDER_FORMAT_INT16)
124	{
125	}
126
127	~RegisterTableModel()
128	{
129	}
130
131	void SetCpuState(CpuState* cpuState)
132	{
133		fCpuState = cpuState;
134
135		NotifyRowsChanged(0, CountRows());
136	}
137
138	virtual int32 CountColumns() const
139	{
140		return 2;
141	}
142
143	virtual int32 CountRows() const
144	{
145		return fArchitecture->CountRegisters();
146	}
147
148	inline int32 SIMDRenderFormat() const
149	{
150		return fSIMDFormat;
151	}
152
153	virtual bool GetValueAt(int32 rowIndex, int32 columnIndex, BVariant& value)
154	{
155		if (rowIndex < 0 || rowIndex >= fArchitecture->CountRegisters())
156			return false;
157
158		const Register* reg = fArchitecture->Registers() + rowIndex;
159
160		switch (columnIndex) {
161			case 0:
162				value.SetTo(reg->Name(), B_VARIANT_DONT_COPY_DATA);
163				return true;
164			case 1:
165				if (fCpuState == NULL)
166					return false;
167				if (!fCpuState->GetRegisterValue(reg, value))
168					value.SetTo("?", B_VARIANT_DONT_COPY_DATA);
169				else if (reg->Format() == REGISTER_FORMAT_SIMD) {
170					BString output;
171					value.SetTo(UiUtils::FormatSIMDValue(value,
172						reg->BitSize(),fSIMDFormat, output));
173				}
174				return true;
175			default:
176				return false;
177		}
178	}
179
180	void SetSIMDFormat(int32 format)
181	{
182		if (fSIMDFormat != format) {
183			fSIMDFormat = format;
184			NotifyRowsChanged(0, CountRows());
185		}
186	}
187
188private:
189private:
190	Architecture*	fArchitecture;
191	CpuState*		fCpuState;
192	int32			fSIMDFormat;
193};
194
195
196// #pragma mark - RegistersView
197
198
199RegistersView::RegistersView(Architecture* architecture)
200	:
201	BGroupView(B_VERTICAL),
202	fArchitecture(architecture),
203	fCpuState(NULL),
204	fRegisterTable(NULL),
205	fRegisterTableModel(NULL)
206{
207	SetName("Registers");
208}
209
210
211RegistersView::~RegistersView()
212{
213	SetCpuState(NULL);
214	fRegisterTable->SetTableModel(NULL);
215	delete fRegisterTableModel;
216}
217
218
219/*static*/ RegistersView*
220RegistersView::Create(Architecture* architecture)
221{
222	RegistersView* self = new RegistersView(architecture);
223
224	try {
225		self->_Init();
226	} catch (...) {
227		delete self;
228		throw;
229	}
230
231	return self;
232}
233
234
235void
236RegistersView::MessageReceived(BMessage* message)
237{
238	switch (message->what) {
239		case MSG_SIMD_RENDER_FORMAT_CHANGED:
240			{
241				int32 format;
242				if (message->FindInt32("format", &format) != B_OK)
243					break;
244
245				fRegisterTableModel->SetSIMDFormat(format);
246			}
247			break;
248
249		default:
250			BGroupView::MessageReceived(message);
251			break;
252	}
253}
254
255
256void
257RegistersView::SetCpuState(CpuState* cpuState)
258{
259	if (cpuState == fCpuState)
260		return;
261
262	if (fCpuState != NULL)
263		fCpuState->ReleaseReference();
264
265	fCpuState = cpuState;
266
267	if (fCpuState != NULL)
268		fCpuState->AcquireReference();
269
270	fRegisterTableModel->SetCpuState(fCpuState);
271}
272
273
274void
275RegistersView::LoadSettings(const BMessage& settings)
276{
277	BMessage tableSettings;
278	if (settings.FindMessage("registerTable", &tableSettings) == B_OK) {
279		GuiSettingsUtils::UnarchiveTableSettings(tableSettings,
280			fRegisterTable);
281	}
282}
283
284
285status_t
286RegistersView::SaveSettings(BMessage& settings)
287{
288	settings.MakeEmpty();
289
290	BMessage tableSettings;
291	status_t result = GuiSettingsUtils::ArchiveTableSettings(tableSettings,
292		fRegisterTable);
293	if (result == B_OK)
294		result = settings.AddMessage("registerTable", &tableSettings);
295
296	return result;
297}
298
299
300void
301RegistersView::TableRowInvoked(Table* table, int32 rowIndex)
302{
303}
304
305
306void
307RegistersView::TableCellMouseDown(Table* table, int32 rowIndex,
308	int32 columnIndex, BPoint screenWhere,	uint32 buttons)
309{
310	if (rowIndex < 0 || rowIndex >= fArchitecture->CountRegisters())
311		return;
312
313	if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0)
314		return;
315
316	BVariant value;
317	if (!fRegisterTableModel->GetValueAt(rowIndex, 1, value))
318		return;
319
320	const Register* reg = fArchitecture->Registers() + rowIndex;
321	if (reg->Format() == REGISTER_FORMAT_FLOAT) {
322		// for floating point registers, we currently have no
323		// context menu options to display.
324		return;
325	}
326
327	BPopUpMenu* menu = new(std::nothrow) BPopUpMenu("Options");
328	if (menu == NULL)
329		return;
330
331	ObjectDeleter<BPopUpMenu> menuDeleter(menu);
332
333	if (reg->Format() == REGISTER_FORMAT_INTEGER) {
334		BMessage* message = new(std::nothrow) BMessage(MSG_SHOW_INSPECTOR_WINDOW);
335		if (message == NULL)
336			return;
337
338		message->AddUInt64("address", value.ToUInt64());
339
340		ObjectDeleter<BMessage> messageDeleter(message);
341		BMenuItem* item = new(std::nothrow) BMenuItem("Inspect", message);
342		if (item == NULL)
343			return;
344
345		messageDeleter.Detach();
346		ObjectDeleter<BMenuItem> itemDeleter(item);
347		if (!menu->AddItem(item))
348			return;
349
350		itemDeleter.Detach();
351
352		item->SetTarget(Window());
353	} else if (reg->Format() == REGISTER_FORMAT_SIMD) {
354		BMenu* formatMenu = new(std::nothrow) BMenu("Format");
355		if (formatMenu == NULL)
356			return;
357
358		ObjectDeleter<BMenu> formatMenuDeleter(formatMenu);
359		if (!menu->AddItem(formatMenu))
360			return;
361		formatMenuDeleter.Detach();
362
363		if (_AddFormatItem(formatMenu, SIMD_RENDER_FORMAT_INT8) != B_OK)
364			return;
365		if (_AddFormatItem(formatMenu, SIMD_RENDER_FORMAT_INT16) != B_OK)
366			return;
367		if (_AddFormatItem(formatMenu, SIMD_RENDER_FORMAT_INT32) != B_OK)
368			return;
369		if (_AddFormatItem(formatMenu, SIMD_RENDER_FORMAT_INT64) != B_OK)
370			return;
371		if (_AddFormatItem(formatMenu, SIMD_RENDER_FORMAT_FLOAT) != B_OK)
372			return;
373		if (_AddFormatItem(formatMenu, SIMD_RENDER_FORMAT_DOUBLE) != B_OK)
374			return;
375
376		formatMenu->SetTargetForItems(this);
377	}
378
379	menuDeleter.Detach();
380
381	BRect mouseRect(screenWhere, screenWhere);
382	mouseRect.InsetBy(-4.0, -4.0);
383	menu->Go(screenWhere, true, false, mouseRect, true);
384}
385
386
387void
388RegistersView::_Init()
389{
390	fRegisterTable = new Table("register list", 0, B_FANCY_BORDER);
391	fRegisterTable->SetFont(B_FONT_ROW, be_fixed_font);
392	AddChild(fRegisterTable->ToView());
393
394	// columns
395	const float padding = be_control_look->DefaultLabelSpacing() * 2;
396	fRegisterTable->AddColumn(new StringTableColumn(0, "Register",
397		be_plain_font->StringWidth("Register") + padding, 40, 1000,
398		B_TRUNCATE_END, B_ALIGN_LEFT));
399	fRegisterTable->AddColumn(new RegisterValueColumn(1, "Value",
400		be_fixed_font->StringWidth("0xffffffff00000000") + padding, 40, 1000,
401		B_TRUNCATE_END, B_ALIGN_RIGHT));
402
403	fRegisterTableModel = new RegisterTableModel(fArchitecture);
404	fRegisterTable->SetTableModel(fRegisterTableModel);
405
406	fRegisterTable->AddTableListener(this);
407}
408
409
410status_t
411RegistersView::_AddFormatItem(BMenu* menu, int32 format)
412{
413	BMessage* message = new(std::nothrow) BMessage(
414		MSG_SIMD_RENDER_FORMAT_CHANGED);
415	if (message == NULL)
416		return B_NO_MEMORY;
417
418	ObjectDeleter<BMessage> messageDeleter(message);
419	if (message->AddInt32("format", format) != B_OK)
420		return B_NO_MEMORY;
421
422	BMenuItem* item = new(std::nothrow) BMenuItem(
423		GetLabelForSIMDFormat(format), message);
424	if (item == NULL)
425		return B_NO_MEMORY;
426
427	messageDeleter.Detach();
428	ObjectDeleter<BMenuItem> itemDeleter(item);
429	if (!menu->AddItem(item))
430		return B_NO_MEMORY;
431
432	itemDeleter.Detach();
433
434	if (format == fRegisterTableModel->SIMDRenderFormat())
435		item->SetMarked(true);
436
437	return B_OK;
438}
439