CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/UI/JitCompareScreen.cpp
Views: 1401
1
#include <algorithm>
2
3
#include "UI/JitCompareScreen.h"
4
5
#include "Core/MemMap.h"
6
#include "Core/MIPS/MIPSTables.h"
7
#include "Core/MIPS/JitCommon/JitBlockCache.h"
8
#include "Core/MIPS/JitCommon/JitCommon.h"
9
#include "Core/MIPS/JitCommon/JitState.h"
10
11
JitCompareScreen::JitCompareScreen() : UIDialogScreenWithBackground() {
12
JitBlockCacheDebugInterface *blockCacheDebug = MIPSComp::jit->GetBlockCacheDebugInterface();
13
// The only defaults that make sense.
14
if (blockCacheDebug->SupportsProfiling()) {
15
listSort_ = ListSort::TIME_SPENT;
16
} else {
17
listSort_ = ListSort::BLOCK_LENGTH_DESC;
18
}
19
FillBlockList();
20
}
21
22
void JitCompareScreen::Flip() {
23
using namespace UI;
24
// If we add more, let's convert to a for loop.
25
switch (viewMode_) {
26
case ViewMode::DISASM:
27
comparisonView_->SetVisibility(V_VISIBLE);
28
blockListView_->SetVisibility(V_GONE);
29
statsView_->SetVisibility(V_GONE);
30
break;
31
case ViewMode::BLOCK_LIST:
32
comparisonView_->SetVisibility(V_GONE);
33
blockListView_->SetVisibility(V_VISIBLE);
34
statsView_->SetVisibility(V_GONE);
35
break;
36
case ViewMode::STATS:
37
comparisonView_->SetVisibility(V_GONE);
38
blockListView_->SetVisibility(V_GONE);
39
statsView_->SetVisibility(V_VISIBLE);
40
break;
41
}
42
}
43
44
// Three panes: Block chooser, MIPS view, ARM/x86 view
45
void JitCompareScreen::CreateViews() {
46
auto di = GetI18NCategory(I18NCat::DIALOG);
47
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
48
49
using namespace UI;
50
51
root_ = new LinearLayout(ORIENT_HORIZONTAL);
52
53
ScrollView *leftColumnScroll = root_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(200, FILL_PARENT)));
54
LinearLayout *leftColumn = leftColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL));
55
56
comparisonView_ = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
57
comparisonView_->SetVisibility(V_VISIBLE);
58
LinearLayout *blockTopBar = comparisonView_->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
59
blockTopBar->Add(new Button("", ImageID("I_ARROW_UP")))->OnClick.Add([this](UI::EventParams &e) {
60
viewMode_ = ViewMode::BLOCK_LIST;
61
Flip();
62
return UI::EVENT_DONE;
63
});
64
blockTopBar->Add(new Button("", ImageID("I_ARROW_LEFT")))->OnClick.Add([=](UI::EventParams &e) {
65
if (currentBlock_ >= 1)
66
currentBlock_--;
67
UpdateDisasm();
68
return UI::EVENT_DONE;
69
});
70
blockTopBar->Add(new Button("", ImageID("I_ARROW_RIGHT")))->OnClick.Add([=](UI::EventParams &e) {
71
if (currentBlock_ < blockList_.size() - 1)
72
currentBlock_++;
73
UpdateDisasm();
74
return UI::EVENT_DONE;
75
});
76
blockTopBar->Add(new Button(dev->T("Random")))->OnClick.Add([=](UI::EventParams &e) {
77
if (blockList_.empty()) {
78
return UI::EVENT_DONE;
79
}
80
currentBlock_ = rand() % blockList_.size();
81
UpdateDisasm();
82
return UI::EVENT_DONE;
83
});
84
85
blockAddr_ = blockTopBar->Add(new TextEdit("", dev->T("Block address"), ""));
86
blockAddr_->OnEnter.Handle(this, &JitCompareScreen::OnAddressChange);
87
blockName_ = blockTopBar->Add(new TextView(dev->T("No block")));
88
blockStats_ = blockTopBar->Add(new TextView(""));
89
90
LinearLayout *columns = comparisonView_->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(1.0f)));
91
92
ScrollView *midColumnScroll = columns->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
93
LinearLayout *midColumn = midColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL));
94
midColumn->SetTag("JitCompareLeftDisasm");
95
leftDisasm_ = midColumn->Add(new LinearLayout(ORIENT_VERTICAL));
96
leftDisasm_->SetSpacing(0.0f);
97
98
ScrollView *rightColumnScroll = columns->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
99
rightColumnScroll->SetTag("JitCompareRightDisasm");
100
LinearLayout *rightColumn = rightColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL));
101
rightDisasm_ = rightColumn->Add(new LinearLayout(ORIENT_VERTICAL));
102
rightDisasm_->SetSpacing(0.0f);
103
104
blockListView_ = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
105
blockListView_->SetVisibility(V_GONE);
106
107
// Should match the ListSort enum
108
static ContextMenuItem sortMenu[] = {
109
{ "Block number", "I_ARROW_UP" },
110
{ "Block length", "I_ARROW_DOWN" },
111
{ "Block length", "I_ARROW_UP" },
112
{ "Time spent", "I_ARROW_DOWN" },
113
{ "Executions", "I_ARROW_DOWN" },
114
};
115
int sortCount = ARRAY_SIZE(sortMenu);
116
if (MIPSComp::jit) {
117
JitBlockCacheDebugInterface *blockCacheDebug = MIPSComp::jit->GetBlockCacheDebugInterface();
118
if (!blockCacheDebug->SupportsProfiling()) {
119
sortCount -= 2;
120
}
121
}
122
123
LinearLayout *listTopBar = blockListView_->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
124
Button *sortButton = new Button(dev->T("Sort..."));
125
listTopBar->Add(sortButton)->OnClick.Add([this, sortButton, sortCount](UI::EventParams &e) {
126
PopupContextMenuScreen *contextMenu = new UI::PopupContextMenuScreen(sortMenu, sortCount, I18NCat::DEVELOPER, sortButton);
127
screenManager()->push(contextMenu);
128
contextMenu->OnChoice.Add([=](EventParams &e) -> UI::EventReturn {
129
if (e.a < (int)ListSort::MAX) {
130
listSort_ = (ListSort)e.a;
131
UpdateDisasm();
132
}
133
return UI::EVENT_DONE;
134
});
135
return UI::EVENT_DONE;
136
});
137
138
ScrollView *blockScroll = blockListView_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
139
blockListContainer_ = blockScroll->Add(new LinearLayout(ORIENT_VERTICAL));
140
141
statsView_ = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
142
statsView_->SetVisibility(V_GONE);
143
144
LinearLayout *statsTopBar = statsView_->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
145
ScrollView *statsScroll = statsView_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
146
statsContainer_ = statsScroll->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
147
148
// leftColumn->Add(new Choice(dev->T("By Address")))->OnClick.Handle(this, &JitCompareScreen::OnSelectBlock);
149
leftColumn->Add(new Choice(dev->T("All")))->OnClick.Add([=](UI::EventParams &e) {
150
listType_ = ListType::ALL_BLOCKS;
151
viewMode_ = ViewMode::BLOCK_LIST;
152
UpdateDisasm();
153
return UI::EVENT_DONE;
154
});
155
leftColumn->Add(new Choice(dev->T("FPU")))->OnClick.Add([=](UI::EventParams &e) {
156
listType_ = ListType::FPU_BLOCKS;
157
viewMode_ = ViewMode::BLOCK_LIST;
158
UpdateDisasm();
159
return UI::EVENT_DONE;
160
});
161
leftColumn->Add(new Choice(dev->T("VFPU")))->OnClick.Add([=](UI::EventParams &e) {
162
listType_ = ListType::VFPU_BLOCKS;
163
viewMode_ = ViewMode::BLOCK_LIST;
164
UpdateDisasm();
165
return UI::EVENT_DONE;
166
});
167
168
leftColumn->Add(new Choice(dev->T("Stats")))->OnClick.Handle(this, &JitCompareScreen::OnShowStats);
169
leftColumn->Add(new Choice(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
170
UpdateDisasm();
171
}
172
173
void JitCompareScreen::FillBlockList() {
174
JitBlockCacheDebugInterface *blockCacheDebug = MIPSComp::jit->GetBlockCacheDebugInterface();
175
blockList_.clear();
176
int64_t sumTotalNanos = 0;
177
int64_t sumExecutions = 0;
178
bool profiling = blockCacheDebug->SupportsProfiling();
179
for (int i = 0; i < blockCacheDebug->GetNumBlocks(); i++) {
180
if (!blockCacheDebug->IsValidBlock(i)) {
181
continue;
182
}
183
184
switch (listType_) {
185
case ListType::ALL_BLOCKS:
186
blockList_.push_back(i);
187
break;
188
case ListType::FPU_BLOCKS:
189
case ListType::VFPU_BLOCKS:
190
{
191
const uint64_t flags = listType_ == ListType::FPU_BLOCKS ? IS_FPU : IS_VFPU;
192
// const uint64_t antiFlags = IS_SYSCALL;
193
const uint64_t antiFlags = 0;
194
JitBlockMeta meta = blockCacheDebug->GetBlockMeta(i);
195
if (meta.valid) {
196
for (u32 addr = meta.addr; addr < meta.addr + meta.sizeInBytes; addr += 4) {
197
MIPSOpcode opcode = Memory::Read_Instruction(addr);
198
MIPSInfo info = MIPSGetInfo(opcode);
199
if ((info & flags) && !(info & antiFlags)) {
200
blockList_.push_back(i);
201
break;
202
}
203
}
204
}
205
}
206
default:
207
break;
208
}
209
210
if (profiling) {
211
JitBlockProfileStats stats = blockCacheDebug->GetBlockProfileStats(i);
212
sumTotalNanos += stats.totalNanos;
213
sumExecutions += stats.executions;
214
}
215
}
216
217
sumTotalNanos_ = sumTotalNanos;
218
sumExecutions_ = sumExecutions;
219
220
if (listSort_ == ListSort::BLOCK_NUM) {
221
// Already sorted, effectively.
222
return;
223
}
224
225
std::sort(blockList_.begin(), blockList_.end(), [=](const int &a_index, const int &b_index) {
226
// First, check metadata sorts.
227
switch (listSort_) {
228
case ListSort::BLOCK_LENGTH_DESC:
229
{
230
JitBlockMeta a_meta = blockCacheDebug->GetBlockMeta(a_index);
231
JitBlockMeta b_meta = blockCacheDebug->GetBlockMeta(b_index);
232
return a_meta.sizeInBytes > b_meta.sizeInBytes; // reverse for descending
233
}
234
case ListSort::BLOCK_LENGTH_ASC:
235
{
236
JitBlockMeta a_meta = blockCacheDebug->GetBlockMeta(a_index);
237
JitBlockMeta b_meta = blockCacheDebug->GetBlockMeta(b_index);
238
return a_meta.sizeInBytes < b_meta.sizeInBytes;
239
}
240
default:
241
break;
242
}
243
JitBlockProfileStats a_stats = blockCacheDebug->GetBlockProfileStats(a_index);
244
JitBlockProfileStats b_stats = blockCacheDebug->GetBlockProfileStats(b_index);
245
switch (listSort_) {
246
case ListSort::EXECUTIONS:
247
return a_stats.executions > b_stats.executions;
248
case ListSort::TIME_SPENT:
249
return a_stats.totalNanos > b_stats.totalNanos;
250
default:
251
return false;
252
}
253
});
254
}
255
256
void JitCompareScreen::UpdateDisasm() {
257
leftDisasm_->Clear();
258
rightDisasm_->Clear();
259
260
using namespace UI;
261
262
if (!MIPSComp::jit) {
263
return;
264
}
265
266
JitBlockCacheDebugInterface *blockCacheDebug = MIPSComp::jit->GetBlockCacheDebugInterface();
267
if (viewMode_ == ViewMode::DISASM && (currentBlock_ < 0 || currentBlock_ >= (int)blockList_.size())) {
268
viewMode_ = ViewMode::BLOCK_LIST;
269
}
270
271
FillBlockList();
272
Flip();
273
274
if (viewMode_ == ViewMode::DISASM) {
275
char temp[256];
276
snprintf(temp, sizeof(temp), "%d/%d", currentBlock_, (int)blockList_.size());
277
blockName_->SetText(temp);
278
279
int blockNum = blockList_[currentBlock_];
280
281
if (!blockCacheDebug->IsValidBlock(blockNum)) {
282
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
283
leftDisasm_->Add(new TextView(dev->T("No block")));
284
rightDisasm_->Add(new TextView(dev->T("No block")));
285
blockStats_->SetText("");
286
return;
287
}
288
289
JitBlockDebugInfo debugInfo = blockCacheDebug->GetBlockDebugInfo(blockNum);
290
snprintf(temp, sizeof(temp), "%08x", debugInfo.originalAddress);
291
blockAddr_->SetText(temp);
292
293
// Alright. First generate the MIPS disassembly.
294
295
// TODO: Need a way to communicate branch continuing.
296
for (const auto &line : debugInfo.origDisasm) {
297
leftDisasm_->Add(new TextView(line, FLAG_DYNAMIC_ASCII, false))->SetFocusable(true);
298
}
299
300
// TODO : When we have both target and IR, need a third column.
301
if (debugInfo.targetDisasm.size()) {
302
for (const auto &line : debugInfo.targetDisasm) {
303
rightDisasm_->Add(new TextView(line, FLAG_DYNAMIC_ASCII, false))->SetFocusable(true);
304
}
305
} else {
306
for (const auto &line : debugInfo.irDisasm) {
307
rightDisasm_->Add(new TextView(line, FLAG_DYNAMIC_ASCII, false))->SetFocusable(true);
308
}
309
}
310
311
int numMips = leftDisasm_->GetNumSubviews();
312
int numHost = rightDisasm_->GetNumSubviews();
313
double bloat = 100.0 * numHost / numMips;
314
if (blockCacheDebug->SupportsProfiling()) {
315
JitBlockProfileStats stats = blockCacheDebug->GetBlockProfileStats(blockNum);
316
int execs = (int)stats.executions;
317
double us = (double)stats.totalNanos / 1000000.0;
318
double percentage = 100.0 * (double)stats.totalNanos / (double)sumTotalNanos_;
319
snprintf(temp, sizeof(temp), "%d runs, %0.2f ms, %0.2f%%, bloat: %0.1f%%", execs, us, percentage, bloat);
320
} else {
321
snprintf(temp, sizeof(temp), "bloat: %0.1f%%", bloat);
322
}
323
blockStats_->SetText(temp);
324
} else if (viewMode_ == ViewMode::BLOCK_LIST) {
325
blockListContainer_->Clear();
326
bool profiling = blockCacheDebug->SupportsProfiling();
327
for (int i = 0; i < std::min(100, (int)blockList_.size()); i++) {
328
int blockNum = blockList_[i];
329
JitBlockMeta meta = blockCacheDebug->GetBlockMeta(blockNum);
330
char temp[512], small[512];
331
if (profiling) {
332
JitBlockProfileStats stats = blockCacheDebug->GetBlockProfileStats(blockNum);
333
int execs = (int)stats.executions;
334
double us = (double)stats.totalNanos / 1000000.0;
335
double percentage = 100.0 * (double)stats.totalNanos / (double)sumTotalNanos_;
336
snprintf(temp, sizeof(temp), "%08x: %d instrs (%d runs, %0.2f ms, %0.2f%%)", meta.addr, meta.sizeInBytes / 4, execs, us, percentage);
337
} else {
338
snprintf(temp, sizeof(temp), "%08x: %d instrs", meta.addr, meta.sizeInBytes / 4);
339
}
340
snprintf(small, sizeof(small), "Small text");
341
Choice *blockChoice = blockListContainer_->Add(new Choice(temp, small));
342
blockChoice->OnClick.Handle(this, &JitCompareScreen::OnBlockClick);
343
}
344
} else { // viewMode_ == ViewMode::STATS
345
statsContainer_->Clear();
346
347
BlockCacheStats bcStats;
348
blockCacheDebug->ComputeStats(bcStats);
349
350
char stats[1024];
351
snprintf(stats, sizeof(stats),
352
"Num blocks: %d\n"
353
"Average Bloat: %0.2f%%\n"
354
"Min Bloat: %0.2f%% (%08x)\n"
355
"Max Bloat: %0.2f%% (%08x)\n",
356
blockCacheDebug->GetNumBlocks(),
357
100.0 * bcStats.avgBloat,
358
100.0 * bcStats.minBloat, bcStats.minBloatBlock,
359
100.0 * bcStats.maxBloat, bcStats.maxBloatBlock);
360
361
statsContainer_->Add(new TextView(stats));
362
}
363
}
364
365
UI::EventReturn JitCompareScreen::OnBlockClick(UI::EventParams &e) {
366
int blockIndex = blockListContainer_->IndexOfSubview(e.v);
367
if (blockIndex >= 0) {
368
viewMode_ = ViewMode::DISASM;
369
currentBlock_ = blockIndex;
370
UpdateDisasm();
371
}
372
return UI::EVENT_DONE;
373
}
374
375
UI::EventReturn JitCompareScreen::OnAddressChange(UI::EventParams &e) {
376
std::lock_guard<std::recursive_mutex> guard(MIPSComp::jitLock);
377
if (!MIPSComp::jit) {
378
return UI::EVENT_DONE;
379
}
380
JitBlockCacheDebugInterface *blockCache = MIPSComp::jit->GetBlockCacheDebugInterface();
381
if (!blockCache)
382
return UI::EVENT_DONE;
383
u32 addr;
384
if (blockAddr_->GetText().size() > 8)
385
return UI::EVENT_DONE;
386
if (1 == sscanf(blockAddr_->GetText().c_str(), "%08x", &addr)) {
387
if (Memory::IsValidAddress(addr)) {
388
currentBlock_ = blockCache->GetBlockNumberFromStartAddress(addr);
389
UpdateDisasm();
390
}
391
}
392
return UI::EVENT_DONE;
393
}
394
395
UI::EventReturn JitCompareScreen::OnShowStats(UI::EventParams &e) {
396
std::lock_guard<std::recursive_mutex> guard(MIPSComp::jitLock);
397
if (!MIPSComp::jit) {
398
return UI::EVENT_DONE;
399
}
400
401
viewMode_ = ViewMode::STATS;
402
UpdateDisasm();
403
return UI::EVENT_DONE;
404
}
405
406
407
UI::EventReturn JitCompareScreen::OnSelectBlock(UI::EventParams &e) {
408
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
409
410
auto addressPrompt = new AddressPromptScreen(dev->T("Block address"));
411
addressPrompt->OnChoice.Handle(this, &JitCompareScreen::OnBlockAddress);
412
screenManager()->push(addressPrompt);
413
return UI::EVENT_DONE;
414
}
415
416
UI::EventReturn JitCompareScreen::OnBlockAddress(UI::EventParams &e) {
417
std::lock_guard<std::recursive_mutex> guard(MIPSComp::jitLock);
418
if (!MIPSComp::jit) {
419
return UI::EVENT_DONE;
420
}
421
422
JitBlockCacheDebugInterface *blockCache = MIPSComp::jit->GetBlockCacheDebugInterface();
423
if (!blockCache)
424
return UI::EVENT_DONE;
425
426
if (Memory::IsValidAddress(e.a)) {
427
currentBlock_ = blockCache->GetBlockNumberFromStartAddress(e.a);
428
} else {
429
currentBlock_ = -1;
430
}
431
UpdateDisasm();
432
return UI::EVENT_DONE;
433
}
434
435
/*
436
void JitCompareScreen::OnRandomBlock(int flag) {
437
std::lock_guard<std::recursive_mutex> guard(MIPSComp::jitLock);
438
if (!MIPSComp::jit) {
439
return;
440
}
441
JitBlockCacheDebugInterface *blockCache = MIPSComp::jit->GetBlockCacheDebugInterface();
442
if (!blockCache)
443
return;
444
445
int numBlocks = blockCache->GetNumBlocks();
446
if (numBlocks > 0) {
447
bool anyWanted = false;
448
int tries = 0;
449
while (!anyWanted && tries < numBlocks) {
450
currentBlock_ = rand() % numBlocks;
451
if (blockCache->IsValidBlock(currentBlock_)) {
452
JitBlockDebugInfo b = blockCache->GetBlockDebugInfo(currentBlock_);
453
u32 mipsBytes = (u32)b.origDisasm.size() * 4;
454
for (u32 addr = b.originalAddress; addr < b.originalAddress + mipsBytes; addr += 4) {
455
MIPSOpcode opcode = Memory::Read_Instruction(addr);
456
if (MIPSGetInfo(opcode) & flag) {
457
char temp[256];
458
MIPSDisAsm(opcode, addr, temp, sizeof(temp));
459
// INFO_LOG(Log::HLE, "Stopping at random instruction: %08x %s", addr, temp);
460
anyWanted = true;
461
break;
462
}
463
}
464
}
465
tries++;
466
}
467
468
if (!anyWanted)
469
currentBlock_ = -1;
470
}
471
UpdateDisasm();
472
}*/
473
474
void AddressPromptScreen::CreatePopupContents(UI::ViewGroup *parent) {
475
using namespace UI;
476
477
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
478
479
addrView_ = new TextView(dev->T("Enter address"), ALIGN_HCENTER, false);
480
parent->Add(addrView_);
481
482
ViewGroup *grid = new GridLayout(GridLayoutSettings(60, 40));
483
parent->Add(grid);
484
485
for (int i = 0; i < 16; ++i) {
486
char temp[16];
487
snprintf(temp, 16, " %X ", i);
488
buttons_[i] = new Button(temp);
489
grid->Add(buttons_[i])->OnClick.Handle(this, &AddressPromptScreen::OnDigitButton);
490
}
491
492
parent->Add(new Button(dev->T("Backspace")))->OnClick.Handle(this, &AddressPromptScreen::OnBackspace);
493
}
494
495
void AddressPromptScreen::OnCompleted(DialogResult result) {
496
if (result == DR_OK) {
497
UI::EventParams e{};
498
e.v = root_;
499
e.a = addr_;
500
OnChoice.Trigger(e);
501
}
502
}
503
504
UI::EventReturn AddressPromptScreen::OnDigitButton(UI::EventParams &e) {
505
for (int i = 0; i < 16; ++i) {
506
if (buttons_[i] == e.v) {
507
AddDigit(i);
508
}
509
}
510
return UI::EVENT_DONE;
511
}
512
513
UI::EventReturn AddressPromptScreen::OnBackspace(UI::EventParams &e) {
514
BackspaceDigit();
515
return UI::EVENT_DONE;
516
}
517
518
void AddressPromptScreen::AddDigit(int n) {
519
if ((addr_ & 0xF0000000) == 0) {
520
addr_ = addr_ * 16 + n;
521
}
522
UpdatePreviewDigits();
523
}
524
525
void AddressPromptScreen::BackspaceDigit() {
526
addr_ /= 16;
527
UpdatePreviewDigits();
528
}
529
530
void AddressPromptScreen::UpdatePreviewDigits() {
531
if (addr_ != 0) {
532
char temp[32];
533
snprintf(temp, 32, "%8X", addr_);
534
addrView_->SetText(temp);
535
} else {
536
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
537
addrView_->SetText(dev->T("Enter address"));
538
}
539
}
540
541
bool AddressPromptScreen::key(const KeyInput &key) {
542
if (key.flags & KEY_DOWN) {
543
if (key.keyCode >= NKCODE_0 && key.keyCode <= NKCODE_9) {
544
AddDigit(key.keyCode - NKCODE_0);
545
} else if (key.keyCode >= NKCODE_A && key.keyCode <= NKCODE_F) {
546
AddDigit(10 + key.keyCode - NKCODE_A);
547
// NKCODE_DEL is backspace.
548
} else if (key.keyCode == NKCODE_DEL) {
549
BackspaceDigit();
550
} else if (key.keyCode == NKCODE_ENTER) {
551
TriggerFinish(DR_OK);
552
} else {
553
return UIDialogScreen::key(key);
554
}
555
} else {
556
return UIDialogScreen::key(key);
557
}
558
return true;
559
}
560
561