1 // Copyright 2017 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "DolphinQt/Debugger/RegisterWidget.h"
6 
7 #include <utility>
8 
9 #include <QHeaderView>
10 #include <QMenu>
11 #include <QTableWidget>
12 #include <QVBoxLayout>
13 
14 #include "Core/Core.h"
15 #include "Core/HW/ProcessorInterface.h"
16 #include "Core/PowerPC/PowerPC.h"
17 #include "DolphinQt/Host.h"
18 #include "DolphinQt/Settings.h"
19 
RegisterWidget(QWidget * parent)20 RegisterWidget::RegisterWidget(QWidget* parent) : QDockWidget(parent)
21 {
22   setWindowTitle(tr("Registers"));
23   setObjectName(QStringLiteral("registers"));
24 
25   setHidden(!Settings::Instance().IsRegistersVisible() ||
26             !Settings::Instance().IsDebugModeEnabled());
27 
28   setAllowedAreas(Qt::AllDockWidgetAreas);
29 
30   CreateWidgets();
31 
32   auto& settings = Settings::GetQSettings();
33 
34   restoreGeometry(settings.value(QStringLiteral("registerwidget/geometry")).toByteArray());
35   // macOS: setHidden() needs to be evaluated before setFloating() for proper window presentation
36   // according to Settings
37   setFloating(settings.value(QStringLiteral("registerwidget/floating")).toBool());
38 
39   PopulateTable();
40   ConnectWidgets();
41 
42   connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this, &RegisterWidget::Update);
43 
44   connect(&Settings::Instance(), &Settings::RegistersVisibilityChanged, this,
45           [this](bool visible) { setHidden(!visible); });
46 
47   connect(&Settings::Instance(), &Settings::DebugModeToggled, this, [this](bool enabled) {
48     setHidden(!enabled || !Settings::Instance().IsRegistersVisible());
49   });
50 }
51 
~RegisterWidget()52 RegisterWidget::~RegisterWidget()
53 {
54   auto& settings = Settings::GetQSettings();
55 
56   settings.setValue(QStringLiteral("registerwidget/geometry"), saveGeometry());
57   settings.setValue(QStringLiteral("registerwidget/floating"), isFloating());
58 }
59 
closeEvent(QCloseEvent *)60 void RegisterWidget::closeEvent(QCloseEvent*)
61 {
62   Settings::Instance().SetRegistersVisible(false);
63 }
64 
showEvent(QShowEvent * event)65 void RegisterWidget::showEvent(QShowEvent* event)
66 {
67   Update();
68 }
69 
CreateWidgets()70 void RegisterWidget::CreateWidgets()
71 {
72   m_table = new QTableWidget;
73   m_table->setTabKeyNavigation(false);
74 
75   m_table->setColumnCount(9);
76 
77   m_table->verticalHeader()->setVisible(false);
78   m_table->verticalHeader()->setDefaultSectionSize(24);
79   m_table->setContextMenuPolicy(Qt::CustomContextMenu);
80   m_table->setSelectionMode(QAbstractItemView::NoSelection);
81   m_table->setFont(Settings::Instance().GetDebugFont());
82 
83   QStringList empty_list;
84 
85   for (auto i = 0; i < 9; i++)
86     empty_list << QString{};
87 
88   m_table->setHorizontalHeaderLabels(empty_list);
89 
90   QWidget* widget = new QWidget;
91   auto* layout = new QVBoxLayout;
92   layout->addWidget(m_table);
93   layout->setContentsMargins(2, 2, 2, 2);
94   widget->setLayout(layout);
95 
96   setWidget(widget);
97 }
98 
ConnectWidgets()99 void RegisterWidget::ConnectWidgets()
100 {
101   connect(m_table, &QTableWidget::customContextMenuRequested, this,
102           &RegisterWidget::ShowContextMenu);
103   connect(m_table, &QTableWidget::itemChanged, this, &RegisterWidget::OnItemChanged);
104 }
105 
OnItemChanged(QTableWidgetItem * item)106 void RegisterWidget::OnItemChanged(QTableWidgetItem* item)
107 {
108   if (!item->data(DATA_TYPE).isNull() && !m_updating)
109     static_cast<RegisterColumn*>(item)->SetValue();
110 }
111 
ShowContextMenu()112 void RegisterWidget::ShowContextMenu()
113 {
114   QMenu* menu = new QMenu(this);
115 
116   auto variant = m_table->currentItem()->data(DATA_TYPE);
117 
118   if (!variant.isNull())
119   {
120     auto* item = static_cast<RegisterColumn*>(m_table->currentItem());
121     auto type = static_cast<RegisterType>(item->data(DATA_TYPE).toInt());
122     auto display = item->GetDisplay();
123 
124     // i18n: This kind of "watch" is used for watching emulated memory.
125     // It's not related to timekeeping devices.
126     menu->addAction(tr("Add to &watch"), this,
127                     [this, item] { emit RequestMemoryBreakpoint(item->GetValue()); });
128     menu->addAction(tr("View &memory"), this,
129                     [this, item] { emit RequestViewInMemory(item->GetValue()); });
130     menu->addAction(tr("View &code"), this,
131                     [this, item] { emit RequestViewInCode(item->GetValue()); });
132 
133     menu->addSeparator();
134 
135     QActionGroup* group = new QActionGroup(menu);
136     group->setExclusive(true);
137 
138     auto* view_hex = menu->addAction(tr("Hexadecimal"));
139     auto* view_int = menu->addAction(tr("Signed Integer"));
140     auto* view_uint = menu->addAction(tr("Unsigned Integer"));
141     // i18n: A floating point number
142     auto* view_float = menu->addAction(tr("Float"));
143     // i18n: A double precision floating point number
144     auto* view_double = menu->addAction(tr("Double"));
145 
146     for (auto* action : {view_hex, view_int, view_uint, view_float, view_double})
147     {
148       action->setCheckable(true);
149       action->setVisible(false);
150       action->setActionGroup(group);
151     }
152 
153     switch (display)
154     {
155     case RegisterDisplay::Hex:
156       view_hex->setChecked(true);
157       break;
158     case RegisterDisplay::SInt32:
159       view_int->setChecked(true);
160       break;
161     case RegisterDisplay::UInt32:
162       view_uint->setChecked(true);
163       break;
164     case RegisterDisplay::Float:
165       view_float->setChecked(true);
166       break;
167     case RegisterDisplay::Double:
168       view_double->setChecked(true);
169       break;
170     }
171 
172     switch (type)
173     {
174     case RegisterType::gpr:
175       view_hex->setVisible(true);
176       view_int->setVisible(true);
177       view_uint->setVisible(true);
178       view_float->setVisible(true);
179       break;
180     case RegisterType::fpr:
181       view_hex->setVisible(true);
182       view_double->setVisible(true);
183       break;
184     default:
185       break;
186     }
187 
188     connect(view_hex, &QAction::triggered, [this, item] {
189       m_updating = true;
190       item->SetDisplay(RegisterDisplay::Hex);
191       m_updating = false;
192     });
193 
194     connect(view_int, &QAction::triggered, [this, item] {
195       m_updating = true;
196       item->SetDisplay(RegisterDisplay::SInt32);
197       m_updating = false;
198     });
199 
200     connect(view_uint, &QAction::triggered, [this, item] {
201       m_updating = true;
202       item->SetDisplay(RegisterDisplay::UInt32);
203       m_updating = false;
204     });
205 
206     connect(view_float, &QAction::triggered, [this, item] {
207       m_updating = true;
208       item->SetDisplay(RegisterDisplay::Float);
209       m_updating = false;
210     });
211 
212     connect(view_double, &QAction::triggered, [this, item] {
213       m_updating = true;
214       item->SetDisplay(RegisterDisplay::Double);
215       m_updating = false;
216     });
217 
218     menu->addSeparator();
219   }
220 
221   menu->addAction(tr("Update"), this, [this] { emit RequestTableUpdate(); });
222 
223   menu->exec(QCursor::pos());
224 }
225 
PopulateTable()226 void RegisterWidget::PopulateTable()
227 {
228   for (int i = 0; i < 32; i++)
229   {
230     // General purpose registers (int)
231     AddRegister(
232         i, 0, RegisterType::gpr, "r" + std::to_string(i), [i] { return GPR(i); },
233         [i](u64 value) { GPR(i) = value; });
234 
235     // Floating point registers (double)
236     AddRegister(
237         i, 2, RegisterType::fpr, "f" + std::to_string(i), [i] { return rPS(i).PS0AsU64(); },
238         [i](u64 value) { rPS(i).SetPS0(value); });
239 
240     AddRegister(
241         i, 4, RegisterType::fpr, "", [i] { return rPS(i).PS1AsU64(); },
242         [i](u64 value) { rPS(i).SetPS1(value); });
243   }
244 
245   for (int i = 0; i < 8; i++)
246   {
247     // IBAT registers
248     AddRegister(
249         i, 5, RegisterType::ibat, "IBAT" + std::to_string(i),
250         [i] {
251           return (static_cast<u64>(PowerPC::ppcState.spr[SPR_IBAT0U + i * 2]) << 32) +
252                  PowerPC::ppcState.spr[SPR_IBAT0L + i * 2];
253         },
254         nullptr);
255     // DBAT registers
256     AddRegister(
257         i + 8, 5, RegisterType::dbat, "DBAT" + std::to_string(i),
258         [i] {
259           return (static_cast<u64>(PowerPC::ppcState.spr[SPR_DBAT0U + i * 2]) << 32) +
260                  PowerPC::ppcState.spr[SPR_DBAT0L + i * 2];
261         },
262         nullptr);
263     // Graphics quantization registers
264     AddRegister(
265         i + 16, 7, RegisterType::gqr, "GQR" + std::to_string(i),
266         [i] { return PowerPC::ppcState.spr[SPR_GQR0 + i]; }, nullptr);
267   }
268 
269   // HID registers
270   AddRegister(
271       24, 7, RegisterType::hid, "HID0", [] { return PowerPC::ppcState.spr[SPR_HID0]; },
272       [](u64 value) { PowerPC::ppcState.spr[SPR_HID0] = static_cast<u32>(value); });
273   AddRegister(
274       25, 7, RegisterType::hid, "HID1", [] { return PowerPC::ppcState.spr[SPR_HID1]; },
275       [](u64 value) { PowerPC::ppcState.spr[SPR_HID1] = static_cast<u32>(value); });
276   AddRegister(
277       26, 7, RegisterType::hid, "HID2", [] { return PowerPC::ppcState.spr[SPR_HID2]; },
278       [](u64 value) { PowerPC::ppcState.spr[SPR_HID2] = static_cast<u32>(value); });
279   AddRegister(
280       27, 7, RegisterType::hid, "HID4", [] { return PowerPC::ppcState.spr[SPR_HID4]; },
281       [](u64 value) { PowerPC::ppcState.spr[SPR_HID4] = static_cast<u32>(value); });
282 
283   for (int i = 0; i < 16; i++)
284   {
285     // SR registers
286     AddRegister(
287         i, 7, RegisterType::sr, "SR" + std::to_string(i), [i] { return PowerPC::ppcState.sr[i]; },
288         [i](u64 value) { PowerPC::ppcState.sr[i] = value; });
289   }
290 
291   // Special registers
292   // TB
293   AddRegister(16, 5, RegisterType::tb, "TB", PowerPC::ReadFullTimeBaseValue, nullptr);
294 
295   // PC
296   AddRegister(
297       17, 5, RegisterType::pc, "PC", [] { return PowerPC::ppcState.pc; },
298       [](u64 value) { PowerPC::ppcState.pc = value; });
299 
300   // LR
301   AddRegister(
302       18, 5, RegisterType::lr, "LR", [] { return PowerPC::ppcState.spr[SPR_LR]; },
303       [](u64 value) { PowerPC::ppcState.spr[SPR_LR] = value; });
304 
305   // CTR
306   AddRegister(
307       19, 5, RegisterType::ctr, "CTR", [] { return PowerPC::ppcState.spr[SPR_CTR]; },
308       [](u64 value) { PowerPC::ppcState.spr[SPR_CTR] = value; });
309 
310   // CR
311   AddRegister(
312       20, 5, RegisterType::cr, "CR", [] { return PowerPC::ppcState.cr.Get(); },
313       [](u64 value) { PowerPC::ppcState.cr.Set(value); });
314 
315   // XER
316   AddRegister(
317       21, 5, RegisterType::xer, "XER", [] { return PowerPC::GetXER().Hex; },
318       [](u64 value) { PowerPC::SetXER(UReg_XER(value)); });
319 
320   // FPSCR
321   AddRegister(
322       22, 5, RegisterType::fpscr, "FPSCR", [] { return PowerPC::ppcState.fpscr.Hex; },
323       [](u64 value) { PowerPC::ppcState.fpscr = static_cast<u32>(value); });
324 
325   // MSR
326   AddRegister(
327       23, 5, RegisterType::msr, "MSR", [] { return PowerPC::ppcState.msr.Hex; },
328       [](u64 value) { PowerPC::ppcState.msr.Hex = value; });
329 
330   // SRR 0-1
331   AddRegister(
332       24, 5, RegisterType::srr, "SRR0", [] { return PowerPC::ppcState.spr[SPR_SRR0]; },
333       [](u64 value) { PowerPC::ppcState.spr[SPR_SRR0] = value; });
334   AddRegister(
335       25, 5, RegisterType::srr, "SRR1", [] { return PowerPC::ppcState.spr[SPR_SRR1]; },
336       [](u64 value) { PowerPC::ppcState.spr[SPR_SRR1] = value; });
337 
338   // Exceptions
339   AddRegister(
340       26, 5, RegisterType::exceptions, "Exceptions", [] { return PowerPC::ppcState.Exceptions; },
341       [](u64 value) { PowerPC::ppcState.Exceptions = value; });
342 
343   // Int Mask
344   AddRegister(
345       27, 5, RegisterType::int_mask, "Int Mask", [] { return ProcessorInterface::GetMask(); },
346       nullptr);
347 
348   // Int Cause
349   AddRegister(
350       28, 5, RegisterType::int_cause, "Int Cause", [] { return ProcessorInterface::GetCause(); },
351       nullptr);
352 
353   // DSISR
354   AddRegister(
355       29, 5, RegisterType::dsisr, "DSISR", [] { return PowerPC::ppcState.spr[SPR_DSISR]; },
356       [](u64 value) { PowerPC::ppcState.spr[SPR_DSISR] = value; });
357   // DAR
358   AddRegister(
359       30, 5, RegisterType::dar, "DAR", [] { return PowerPC::ppcState.spr[SPR_DAR]; },
360       [](u64 value) { PowerPC::ppcState.spr[SPR_DAR] = value; });
361 
362   // Hash Mask
363   AddRegister(
364       31, 5, RegisterType::pt_hashmask, "Hash Mask",
365       [] { return (PowerPC::ppcState.pagetable_hashmask << 6) | PowerPC::ppcState.pagetable_base; },
366       nullptr);
367 
368   emit RequestTableUpdate();
369   m_table->resizeColumnsToContents();
370 }
371 
AddRegister(int row,int column,RegisterType type,std::string register_name,std::function<u64 ()> get_reg,std::function<void (u64)> set_reg)372 void RegisterWidget::AddRegister(int row, int column, RegisterType type, std::string register_name,
373                                  std::function<u64()> get_reg, std::function<void(u64)> set_reg)
374 {
375   auto* value = new RegisterColumn(type, std::move(get_reg), std::move(set_reg));
376 
377   if (m_table->rowCount() <= row)
378     m_table->setRowCount(row + 1);
379 
380   bool has_label = !register_name.empty();
381 
382   if (has_label)
383   {
384     auto* label = new QTableWidgetItem(QString::fromStdString(register_name));
385     label->setFlags(Qt::ItemIsEnabled);
386 
387     QFont label_font = label->font();
388     label_font.setBold(true);
389     label->setFont(label_font);
390 
391     m_table->setItem(row, column, label);
392     m_table->setItem(row, column + 1, value);
393     m_table->item(row, column + 1)->setTextAlignment(Qt::AlignVCenter | Qt::AlignRight);
394   }
395   else
396   {
397     m_table->setItem(row, column, value);
398     m_table->item(row, column)->setTextAlignment(Qt::AlignVCenter | Qt::AlignRight);
399   }
400 
401   connect(this, &RegisterWidget::UpdateTable, [value] { value->RefreshValue(); });
402 }
403 
Update()404 void RegisterWidget::Update()
405 {
406   if (isVisible() && Core::GetState() == Core::State::Paused)
407   {
408     m_updating = true;
409     emit UpdateTable();
410     m_updating = false;
411   }
412 }
413