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