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