e8555c89 Andy McCall 1 month ago
1
#include " DebuggableEditor.hh"
2
3
#include " ImGuiCpp.hh"
4
#include " ImGuiDebugger.hh"
5
#include " ImGuiManager.hh"
6
#include " ImGuiOpenFile.hh"
7
#include " ImGuiSettings.hh"
8
#include " ImGuiUtils.hh"
9
#include " Shortcuts.hh"
10
11
#include " CommandException.hh"
12
#include " Debuggable.hh"
13
#include " Debugger.hh"
14
#include " Display.hh"
15
#include " File.hh"
16
#include " Interpreter.hh"
17
#include " MSXMotherBoard.hh"
18
#include " SymbolManager.hh"
19
#include " TclObject.hh"
20
#include " VideoSystem.hh"
21
22
#include " enumerate.hh"
23
#include " narrow.hh"
24
#include " unreachable.hh"
25
26
#include " CustomFont.h"
27
#include " imgui_stdlib.h"
28
29
#include < algorithm>
30
#include < array>
31
#include < bit>
32
#include < cassert>
33
#include < cstdint>
34
#include < cstdio>
35
#include < expected>
36
#include < span>
37
38
namespace openmsx {
39
40
using namespace std:: literals;
41
42
static constexpr int MidColsCount = 8 ;
43
static constexpr auto HighlightColor = IM_COL32 ( 255 , 255 , 255 , 50 ) ;
44
static constexpr auto HighlightSymbolColor = IM_COL32 ( 148 , 95 , 35 , 255 ) ;
45
46
DebuggableEditor:: DebuggableEditor ( ImGuiManager& manager_ , std:: string_view debuggableName_ , size_t index )
47
: ImGuiPart ( manager_ )
48
, symbolManager ( manager. getReactor ( ) . getSymbolManager ( ) )
49
, debuggableNameSize ( debuggableName_. size ( ) )
50
, title ( strCat ( debuggableName_, strCat_if ( index, " (" , index + 1 , ' )' ) ) )
51
{
52
}
53
54
void DebuggableEditor:: save ( ImGuiTextBuffer& buf )
55
{
56
savePersistent ( buf, * this , persistentElements ) ;
57
}
58
59
void DebuggableEditor:: loadLine ( std:: string_view name , zstring_view value )
60
{
61
loadOnePersistent ( name, value, * this , persistentElements ) ;
62
parseSearchString ( searchString ) ;
63
}
64
65
void DebuggableEditor:: loadEnd ( )
66
{
67
updateAddr = true ;
68
}
69
70
DebuggableEditor:: Sizes DebuggableEditor:: calcSizes ( unsigned memSize ) const
71
{
72
Sizes s;
73
const auto & style = ImGui:: GetStyle ( ) ;
74
75
s. addrDigitsCount = 0 ;
76
for ( unsigned n = memSize - 1 ; n > 0 ; n >>= 4 ) {
77
+ + s. addrDigitsCount ;
78
}
79
80
s. lineHeight = ImGui:: GetTextLineHeight ( ) ;
81
s. glyphWidth = ImGui:: CalcTextSize ( " F" ) . x + 1 ;
82
s. hexCellWidth = truncf ( s. glyphWidth * 2.5f ) ;
83
s. spacingBetweenMidCols = truncf ( s. hexCellWidth * 0.25f ) ;
84
s. posHexStart = float ( s. addrDigitsCount + 2 ) * s. glyphWidth ;
85
auto posHexEnd = s. posHexStart + ( s. hexCellWidth * float ( columns) ) ;
86
s. posAsciiStart = s. posAsciiEnd = posHexEnd;
87
if ( showAscii) {
88
int numMacroColumns = ( columns + MidColsCount - 1 ) / MidColsCount;
89
s. posAsciiStart = posHexEnd + s. glyphWidth + float ( numMacroColumns) * s. spacingBetweenMidCols ;
90
s. posAsciiEnd = s. posAsciiStart + float ( columns) * s. glyphWidth ;
91
}
92
s. windowWidth = s. posAsciiEnd + style. ScrollbarSize + style. WindowPadding . x * 2 + s. glyphWidth ;
93
return s;
94
}
95
96
void DebuggableEditor:: makeSnapshot ( MSXMotherBoard& motherBoard )
97
{
98
auto & debugger = motherBoard. getDebugger ( ) ;
99
if ( auto * debuggable = debugger. findDebuggable ( getDebuggableName ( ) ) ) {
100
makeSnapshot ( * debuggable) ;
101
}
102
}
103
void DebuggableEditor:: makeSnapshot ( Debuggable& debuggable )
104
{
105
snapshot. resize ( debuggable. getSize ( ) ) ;
106
debuggable. readBlock ( 0 , snapshot) ;
107
}
108
109
void DebuggableEditor:: paint ( MSXMotherBoard* motherBoard )
110
{
111
if ( ! open || ! motherBoard) return ;
112
auto & debugger = motherBoard-> getDebugger ( ) ;
113
auto * debuggable = debugger. findDebuggable ( getDebuggableName ( ) ) ;
114
if ( ! debuggable) return ;
115
116
unsigned memSize = debuggable-> getSize ( ) ;
117
columns = std:: min ( columns, narrow < int > ( memSize ) ) ;
118
auto s = calcSizes ( memSize ) ;
119
120
if ( showExportWindow) {
121
drawExport ( s, * debuggable ) ;
122
}
123
124
im:: ScopedFont sf ( manager. fontMono ) ;
125
126
ImGui:: SetNextWindowSize ( ImVec2 ( s. windowWidth , s. windowWidth * 0.60f ) , ImGuiCond_FirstUseEver) ;
127
128
im:: Window ( title. c_str ( ) , & open, ImGuiWindowFlags_NoScrollbar, [ & ] {
129
if ( ImGui:: IsWindowHovered ( ImGuiHoveredFlags_RootAndChildWindows ) &&
130
ImGui:: IsMouseReleased ( ImGuiMouseButton_Right ) ) {
131
ImGui:: OpenPopup ( " context" ) ;
132
}
133
drawContents ( s, * debuggable, memSize ) ;
134
} ) ;
135
}
136
137
[ [ nodiscard] ] static char formatAsciiData ( uint8_t val)
138
{
139
return ( val < 32 || val >= 128 ) ? ' .' : char ( val) ;
140
}
141
142
void DebuggableEditor:: drawExport ( const Sizes& s , Debuggable& debuggable )
143
{
144
auto & interp = manager. getInterpreter ( ) ;
145
const auto & style = ImGui:: GetStyle ( ) ;
146
147
im:: Window ( tmpStrCat ( " Export " , title) . c_str ( ) , & showExportWindow, [ & ] {
148
if ( ImGui:: IsWindowAppearing ( ) ) {
149
exportBegin = strCat ( " 0x" , formatAddr ( s, 0 ) ) ;
150
exportEnd = strCat ( " 0x" , formatAddr ( s, debuggable. getSize ( ) - 1 ) ) ;
151
}
152
153
std:: optional< unsigned > begin, end;
154
std:: string formatError;
155
156
ImGui:: SeparatorText ( " Input" ) ;
157
158
ImGui:: RadioButton ( " All" , & exportRange, RANGE_ALL) ;
159
if ( exportRange == RANGE_ALL) {
160
begin = 0 ;
161
end = debuggable. getSize ( ) - 1 ;
162
}
163
164
ImGui:: RadioButton ( " Range " , & exportRange, RANGE_RANGE) ;
165
auto handleAddress = [ & ] ( std:: string_view label, std:: string& text, int lowerBound) {
166
std:: optional< unsigned > addr;
167
std:: string error;
168
try {
169
auto i = TclObject ( text ) . eval ( interp ) . getInt ( interp ) ;
170
if ( ( i < 0 ) || ( i >= narrow < int > ( debuggable. getSize ( ) ) ) ) {
171
error = " out of range" ;
172
} else if ( i < lowerBound) {
173
error = " 'end' smaller than 'begin'" ;
174
} else {
175
addr = narrow < unsigned > ( i ) ;
176
}
177
} catch ( MSXException& e) {
178
error = e. getMessage ( ) ;
179
}
180
ImGui:: SameLine ( ) ;
181
ImGui:: TextUnformatted ( label ) ;
182
ImGui:: SameLine ( ) ;
183
ImGui:: SetNextItemWidth ( ImGui:: GetFontSize ( ) * 5.0f ) ;
184
im:: StyleColor ( ! addr, ImGuiCol_Text, getColor ( imColor:: ERROR ) , [ & ] {
185
ImGui:: InputText ( tmpStrCat ( " ##" , label) . c_str ( ) , & text) ;
186
if ( ImGui:: IsItemClicked ( ImGuiMouseButton_Right ) ) {
187
text = strCat ( " 0x" , formatAddr ( s, currentAddr ) ) ;
188
}
189
simpleToolTip ( [ & ] {
190
return addr ? strCat ( " 0x" , formatAddr ( s, * addr ) )
191
: error;
192
} ) ;
193
} ) ;
194
return addr;
195
} ;
196
im:: Disabled ( exportRange != RANGE_RANGE, [ & ] {
197
begin = handleAddress ( " begin:" , exportBegin, 0 ) ;
198
end = handleAddress ( " end:" , exportEnd, begin. value_or ( 0 ) ) ;
199
} ) ;
200
HelpMarker ( " Both 'begin' and 'end' are inclusive.\n "
201
" You can enter an expression line '0x100 + 7' or '[reg HL]'.\n "
202
" Right-click to enter the current cursor position." ) ;
203
204
ImGui:: SeparatorText ( " Format" ) ;
205
206
ImGui:: RadioButton ( " Raw" , & exportFormatted, EXPORT_RAW) ;
207
HelpMarker ( " Produce binary (unformatted) output" ) ;
208
209
ImGui:: RadioButton ( " Formatted" , & exportFormatted, EXPORT_FORMATTED) ;
210
HelpMarker ( " Produce formatted ASCII output" ) ;
211
im:: DisabledIndent ( exportFormatted != EXPORT_FORMATTED, [ & ] {
212
ImGui:: TextUnformatted ( " Line" ) ;
213
HelpMarker ( " Formatting options per line" ) ;
214
im:: Indent ( [ & ] {
215
ImGui:: TextUnformatted ( " Prefix:" ) ;
216
ImGui:: SameLine ( ) ;
217
ImGui:: SetNextItemWidth ( ImGui:: GetFontSize ( ) * 10.0f ) ;
218
ImGui:: InputText ( " ##prefix" , & exportPrefix) ;
219
HelpMarker ( " Output a prefix at the start of each line.\n "
220
" For example 'db ', note the space after 'db'." ) ;
221
222
ImGui:: TextUnformatted ( " Columns:" ) ;
223
ImGui:: SameLine ( ) ;
224
ImGui:: SetNextItemWidth ( 9.0f * s. glyphWidth + 2.0f * style. FramePadding . x ) ;
225
if ( ImGui:: InputInt ( " ##columns" , & exportColumns, 1 , 0 ) ) {
226
exportColumns = std:: clamp ( exportColumns, 1 , MAX_COLUMNS ) ;
227
}
228
HelpMarker ( " Output N columns of comma-separated values." ) ;
229
230
ImGui:: TextUnformatted ( " Suffix:" ) ;
231
ImGui:: SameLine ( ) ;
232
ImGui:: SetNextItemWidth ( ImGui:: GetFontSize ( ) * 10.0f ) ;
233
ImGui:: InputText ( " ##suffix" , & exportSuffix) ;
234
HelpMarker ( " Output a suffix at the end of each line.\n "
235
" This might be useful if you want a trailing comma." ) ;
236
} ) ;
237
ImGui:: TextUnformatted ( " Value" ) ;
238
HelpMarker ( " Formatting options per value (multiple per line)" ) ;
239
im:: Indent ( [ & ] {
240
ImGui:: RadioButton ( " Bin" , & exportFormat, FORMAT_BIN) ;
241
HelpMarker ( " 8 digit binary with prefix '0b'\n "
242
" corresponding to custom format '0b%08b'" ) ;
243
ImGui:: SameLine ( ) ;
244
ImGui:: RadioButton ( " Dec" , & exportFormat, FORMAT_DEC) ;
245
HelpMarker ( " 3 digit decimal right padded with spaces\n "
246
" corresponding to custom format '%3d '" ) ;
247
ImGui:: SameLine ( ) ;
248
ImGui:: RadioButton ( " Hex" , & exportFormat, FORMAT_HEX) ;
249
HelpMarker ( " 2 digit hexadecimal\n "
250
" corresponding to custom format '0x%02X '" ) ;
251
ImGui:: SameLine ( ) ;
252
ImGui:: RadioButton ( " ASCII" , & exportFormat, FORMAT_ASCII) ;
253
HelpMarker ( " As visible in the ASCII view." ) ;
254
try {
255
if ( exportFormatted == EXPORT_FORMATTED) {
256
makeTclList ( " format" , exportCustomFormat, 0 ) . executeCommand ( interp ) ;
257
}
258
} catch ( MSXException& e) {
259
formatError = e. getMessage ( ) ;
260
}
261
ImGui:: RadioButton ( " Custom" , & exportFormat, FORMAT_CUSTOM) ;
262
ImGui:: SameLine ( ) ;
263
im:: Disabled ( exportFormat != FORMAT_CUSTOM, [ & ] {
264
im:: StyleColor ( ! formatError. empty ( ) , ImGuiCol_Text, getColor ( imColor:: ERROR ) , [ & ] {
265
ImGui:: SetNextItemWidth ( ImGui:: GetFontSize ( ) * 10.0f ) ;
266
ImGui:: InputText ( " ##custom" , & exportCustomFormat) ;
267
if ( ! formatError. empty ( ) ) {
268
simpleToolTip ( formatError ) ;
269
}
270
} ) ;
271
} ) ;
272
HelpMarker ( " format string passed to the Tcl 'format' command" ) ;
273
} ) ;
274
} ) ;
275
std:: string_view formatStr = ( exportFormat == FORMAT_BIN) ? " 0b%08b" sv
276
: ( exportFormat == FORMAT_DEC) ? " %3d " sv
277
: ( exportFormat == FORMAT_HEX) ? " 0x%02X " sv
278
: exportCustomFormat;
279
280
ImGui:: SeparatorText ( " Output" ) ;
281
282
ImGui:: RadioButton ( " Clipboard" , & exportDestination, OUTPUT_CLIPBOARD) ;
283
284
ImGui:: RadioButton ( " File" , & exportDestination, OUTPUT_FILE) ;
285
ImGui:: SameLine ( ) ;
286
im:: Disabled ( exportDestination != OUTPUT_FILE, [ & ] {
287
ImGui:: SetNextItemWidth ( ImGui:: GetFontSize ( ) * 18.0f ) ;
288
ImGui:: InputText ( " ##filename" , & exportFilename) ;
289
ImGui:: SameLine ( ) ;
290
if ( ImGui:: Button ( ICON_IGFD_FOLDER_OPEN ) ) {
291
manager. openFile -> selectNewFile (
292
strCat ( " Select output file for " , title) , " " ,
293
[ & ] ( const auto & fn) { exportFilename = fn; } ) ;
294
}
295
} ) ;
296
297
bool ok = begin && end &&
298
( exportFormat != FORMAT_CUSTOM || formatError. empty ( ) ) &&
299
( exportDestination != OUTPUT_FILE || ! exportFilename. empty ( ) ) ;
300
im:: Disabled ( ! ok, [ & ] {
301
if ( ImGui:: Button ( " Export" ) ) {
302
auto fetch = [ & ] ( unsigned address) { return debuggable. read ( address ) ; } ;
303
try {
304
auto output = ( exportFormatted == EXPORT_FORMATTED)
305
? ( ( exportFormat == FORMAT_ASCII)
306
? formatToString ( fetch, * begin, * end, exportPrefix, exportColumns, exportSuffix, { } ,
307
[ ] ( uint8_t val) { return std:: string ( 1 , formatAsciiData ( val ) ) ; } )
308
: formatToString ( fetch, * begin, * end, exportPrefix, exportColumns, exportSuffix, " , " ,
309
formatStr, interp ) )
310
: rawToString ( fetch, * begin, * end ) ;
311
if ( exportDestination == OUTPUT_CLIPBOARD) {
312
manager. getReactor ( ) . getDisplay ( ) . getVideoSystem ( ) . setClipboardText ( output ) ;
313
} else {
314
File file ( exportFilename, File:: OpenMode:: TRUNCATE ) ;
315
file. write ( std:: span ( std:: bit_cast < const uint8_t * > ( output. data ( ) ) , output. size ( ) ) ) ;
316
}
317
exportStatus = " Export successful" ;
318
exportStatusTimeout = 3.0f ;
319
} catch ( MSXException& e) {
320
exportStatus = e. getMessage ( ) ;
321
exportStatusTimeout = 5.0f ;
322
manager. printError ( e. getMessage ( ) ) ;
323
}
324
}
325
} ) ;
326
if ( exportStatusTimeout > 0.0f ) {
327
const auto & io = ImGui:: GetIO ( ) ;
328
exportStatusTimeout -= io. DeltaTime ;
329
330
ImGui:: SameLine ( ) ;
331
ImGui:: TextUnformatted ( exportStatus ) ;
332
}
333
334
} ) ;
335
}
336
337
[ [ nodiscard] ] static unsigned DataTypeGetSize ( ImGuiDataType dataType )
338
{
339
std:: array< unsigned , 8 > sizes = { 1 , 1 , 2 , 2 , 4 , 4 , 8 , 8 } ;
340
assert ( dataType >= 0 && dataType < 8 ) ;
341
return sizes[ dataType] ;
342
}
343
344
[ [ nodiscard] ] static std:: optional< int > parseHexDigit ( char c )
345
{
346
if ( ' 0' <= c && c <= ' 9' ) return c - ' 0' ;
347
if ( ' a' <= c && c <= ' f' ) return c - ' a' + 10 ;
348
if ( ' A' <= c && c <= ' F' ) return c - ' A' + 10 ;
349
return std:: nullopt;
350
}
351
352
[ [ nodiscard] ] static std:: optional< uint8_t > parseDataValue ( std:: string_view str )
353
{
354
if ( str. size ( ) == 1 ) {
355
return parseHexDigit ( str[ 0 ] ) ;
356
} else if ( str. size ( ) == 2 ) {
357
if ( auto digit0 = parseHexDigit ( str[ 0 ] ) ) {
358
if ( auto digit1 = parseHexDigit ( str[ 1 ] ) ) {
359
return 16 * * digit0 + * digit1;
360
}
361
}
362
}
363
return std:: nullopt;
364
}
365
366
[ [ nodiscard] ] static std:: expected< unsigned , std:: string> parseAddressExpr (
367
std:: string_view str , const SymbolManager& symbolManager , Interpreter& interp )
368
{
369
if ( str. empty ( ) ) return 0 ;
370
371
372
373
if ( auto addr = symbolManager. parseSymbolOrValue ( str ) ) {
374
return * addr;
375
}
376
377
try {
378
return TclObject ( str ) . eval ( interp ) . getInt ( interp ) ;
379
} catch ( CommandException& e) {
380
return std:: unexpected ( e. getMessage ( ) ) ;
381
}
382
}
383
384
[ [ nodiscard] ] static std:: string formatData ( uint8_t val)
385
{
386
return strCat ( hex_string < 2 , HexCase:: upper> ( val ) ) ;
387
}
388
389
[ [ nodiscard] ] std:: string DebuggableEditor:: formatAddr ( const Sizes& s, unsigned addr) const
390
{
391
return strCat ( hex_string < HexCase:: upper> ( Digits{ size_t ( s. addrDigitsCount ) } , addr ) ) ;
392
}
393
void DebuggableEditor:: setStrings ( const Sizes& s , Debuggable& debuggable )
394
{
395
addrStr = strCat ( " 0x" , formatAddr ( s, currentAddr ) ) ;
396
auto b = debuggable. read ( currentAddr ) ;
397
if ( dataEditingActive == HEX ) dataInput = formatData ( b ) ;
398
if ( dataEditingActive == ASCII) dataInput = std:: string ( 1 , formatAsciiData ( b ) ) ;
399
}
400
bool DebuggableEditor:: setAddr ( const Sizes& s , Debuggable& debuggable , unsigned memSize , unsigned addr )
401
{
402
addr = std:: min ( addr, memSize - 1 ) ;
403
if ( currentAddr == addr) return false ;
404
currentAddr = addr;
405
setStrings ( s, debuggable ) ;
406
return true ;
407
}
408
void DebuggableEditor:: scrollAddr ( const Sizes& s , Debuggable& debuggable , unsigned memSize , unsigned addr , bool forceScroll )
409
{
410
if ( setAddr ( s, debuggable, memSize, addr ) || forceScroll) {
411
im:: Child ( " ##scrolling" , [ & ] {
412
int row = narrow < int > ( currentAddr ) / columns;
413
ImGui:: SetScrollFromPosY ( ImGui:: GetCursorStartPos ( ) . y + float ( row) * ImGui:: GetTextLineHeight ( ) ) ;
414
} ) ;
415
}
416
}
417
418
void DebuggableEditor:: drawContents ( const Sizes& s , Debuggable& debuggable , unsigned memSize )
419
{
420
const auto & style = ImGui:: GetStyle ( ) ;
421
if ( updateAddr) {
422
updateAddr = false ;
423
scrollAddr ( s, debuggable, memSize, currentAddr, true ) ;
424
} else {
425
426
setAddr ( s, debuggable, memSize, currentAddr ) ;
427
}
428
429
float footerHeight = 0.0f ;
430
if ( showAddress) {
431
footerHeight += style. ItemSpacing . y + ImGui:: GetFrameHeightWithSpacing ( ) ;
432
}
433
if ( showSearch) {
434
footerHeight += style. ItemSpacing . y + 2 * ImGui:: GetFrameHeightWithSpacing ( ) ;
435
}
436
if ( showDataPreview) {
437
footerHeight += style. ItemSpacing . y + ImGui:: GetFrameHeightWithSpacing ( ) + 3 * ImGui:: GetTextLineHeightWithSpacing ( ) ;
438
}
439
if ( showSymbolInfo) {
440
footerHeight += style. ItemSpacing . y + ImGui:: GetFrameHeightWithSpacing ( ) ;
441
}
442
443
444
int cFlags = ImGuiWindowFlags_NoMove;
445
446
447
448
449
cFlags |= ImGuiWindowFlags_HorizontalScrollbar;
450
ImGui:: BeginChild ( " ##scrolling" , ImVec2 ( 0 , - footerHeight) , ImGuiChildFlags_None, cFlags) ;
451
ImGui:: PushStyleVar ( ImGuiStyleVar_FramePadding, ImVec2 ( 0 , 0 ) ) ;
452
ImGui:: PushStyleVar ( ImGuiStyleVar_ItemSpacing, ImVec2 ( 0 , 0 ) ) ;
453
454
std:: optional< unsigned > nextAddr;
455
456
457
auto * drawList = ImGui:: GetWindowDrawList ( ) ;
458
ImVec2 windowPos = ImGui:: GetWindowPos ( ) ;
459
if ( showAscii) {
460
drawList-> AddLine ( ImVec2 ( windowPos. x + s. posAsciiStart - s. glyphWidth , windowPos. y ) ,
461
ImVec2 ( windowPos. x + s. posAsciiStart - s. glyphWidth , windowPos. y + 9999 ) ,
462
ImGui:: GetColorU32 ( ImGuiCol_Border ) ) ;
463
}
464
465
auto handleInput = [ & ] ( unsigned addr, int width, auto formatData, auto parseData, int extraFlags = 0 ) {
466
467
if ( dataEditingTakeFocus) {
468
ImGui:: SetKeyboardFocusHere ( ) ;
469
setStrings ( s, debuggable ) ;
470
}
471
struct UserData {
472
473
static int Callback ( ImGuiInputTextCallbackData* data ) {
474
auto * userData = static_cast < UserData* > ( data-> UserData ) ;
475
if ( userData-> cursorPos != - 1 ) {
476
data-> CursorPos = userData-> cursorPos ;
477
}
478
if ( ! data-> HasSelection ( ) ) {
479
userData-> cursorPos = data-> CursorPos ;
480
}
481
if ( data-> SelectionStart == 0 && data-> SelectionEnd == data-> BufTextLen ) {
482
483
484
data-> DeleteChars ( 0 , data-> BufTextLen ) ;
485
userData-> format ( data ) ;
486
487
488
data-> SelectionStart = 0 ;
489
data-> CursorPos = 0 ;
490
}
491
return 0 ;
492
}
493
std:: function< void ( ImGuiInputTextCallbackData* data) > format;
494
int cursorPos = - 1 ;
495
} ;
496
UserData userData;
497
userData. format = formatData;
498
if ( resetCursor) {
499
resetCursor = false ;
500
userData. cursorPos = 0 ;
501
}
502
ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue
503
| ImGuiInputTextFlags_AutoSelectAll
504
| ImGuiInputTextFlags_NoHorizontalScroll
505
| ImGuiInputTextFlags_CallbackAlways
506
| ImGuiInputTextFlags_AlwaysOverwrite
507
| extraFlags;
508
ImGui:: SetNextItemWidth ( s. glyphWidth * float ( width) ) ;
509
bool dataWrite = false ;
510
im:: ID ( int ( addr) , [ & ] {
511
if ( ImGui:: InputText ( " ##data" , & dataInput, flags, UserData:: Callback, & userData) ) {
512
dataWrite = true ;
513
} else if ( ! ImGui:: IsItemActive ( ) ) {
514
setStrings ( s, debuggable ) ;
515
}
516
} ) ;
517
518
if ( addrMode == CURSOR && ImGui:: IsItemActive ( ) ) {
519
if ( ImGui:: IsKeyChordPressed ( ImGuiKey_Home | ImGuiMod_Ctrl ) ) {
520
nextAddr = 0 ;
521
updateAddr = true ;
522
} else if ( ImGui:: IsKeyPressed ( ImGuiKey_Home ) ) {
523
nextAddr = currentAddr - ( currentAddr % unsigned ( columns) ) ;
524
}
525
if ( ImGui:: IsKeyChordPressed ( ImGuiKey_End | ImGuiMod_Ctrl ) ) {
526
nextAddr = memSize - 1 ;
527
updateAddr = true ;
528
529
530
resetCursor = true ;
531
} else if ( ImGui:: IsKeyPressed ( ImGuiKey_End ) ) {
532
auto tmp = currentAddr - ( currentAddr % unsigned ( columns) ) + columns - 1 ;
533
nextAddr = std:: min ( tmp, memSize - 1 ) ;
534
resetCursor = true ;
535
}
536
if ( ( int ( currentAddr) >= columns) &&
537
ImGui:: IsKeyPressed ( ImGuiKey_UpArrow ) ) {
538
nextAddr = currentAddr - columns;
539
}
540
if ( ( int ( currentAddr) < int ( memSize - columns) ) &&
541
ImGui:: IsKeyPressed ( ImGuiKey_DownArrow ) ) {
542
nextAddr = currentAddr + columns;
543
}
544
if ( ( int ( currentAddr) > 0 ) &&
545
ImGui:: IsKeyPressed ( ImGuiKey_LeftArrow ) ) {
546
nextAddr = currentAddr - 1 ;
547
}
548
if ( ImGui:: IsKeyPressed ( ImGuiKey_RightArrow ) ) {
549
550
551
nextAddr = std:: min ( currentAddr + 1 , memSize - 1 ) ;
552
resetCursor = true ;
553
}
554
if ( ! switchedTab && ImGui:: IsKeyPressed ( ImGuiKey_Tab ) ) {
555
dataEditingActive = ( dataEditingActive == HEX) ? ASCII : HEX;
556
nextAddr = currentAddr;
557
switchedTab = true ;
558
} else {
559
switchedTab = false ;
560
}
561
}
562
dataEditingTakeFocus = false ;
563
dataWrite |= userData. cursorPos >= width;
564
if ( nextAddr) {
565
dataWrite = false ;
566
dataEditingTakeFocus = true ;
567
}
568
if ( dataWrite) {
569
if ( auto value = parseData ( dataInput ) ) {
570
debuggable. write ( addr, * value ) ;
571
assert ( ! nextAddr) ;
572
nextAddr = currentAddr + 1 ;
573
}
574
}
575
} ;
576
577
auto & debugger = manager. debugger ;
578
if ( ( snapshot. size ( ) != memSize) && debugger-> needSnapshot ( ) ) {
579
makeSnapshot ( debuggable ) ;
580
}
581
bool drawChanges = ( snapshot. size ( ) == memSize) && debugger-> needDrawChanges ( ) ;
582
auto changedColor = debugger-> getChangesColor ( ) ;
583
auto greyColor = getColor ( imColor:: TEXT_DISABLED ) ;
584
585
const auto totalLineCount = int ( ( memSize + columns - 1 ) / columns) ;
586
im:: ListClipper ( totalLineCount, - 1 , s. lineHeight , [ & ] ( int line) {
587
auto addr = unsigned ( line) * columns;
588
ImGui:: StrCat ( formatAddr ( s, addr ) , ' :' ) ;
589
590
auto previewDataTypeSize = DataTypeGetSize ( previewDataType ) ;
591
auto inside = [ ] ( unsigned a, unsigned start, unsigned size) {
592
return ( start <= a) && ( a < ( start + size) ) ;
593
} ;
594
auto highLightDataPreview = [ & ] ( unsigned a) {
595
return inside ( a, currentAddr, previewDataTypeSize ) ;
596
} ;
597
auto highLightSearch = [ & ] ( unsigned a) {
598
if ( ! searchPattern) return false ;
599
auto len = narrow < unsigned > ( searchPattern-> size ( ) ) ;
600
if ( searchHighlight == static_cast < int > ( SearchHighlight:: SINGLE) ) {
601
if ( searchResult) {
602
return inside ( a, * searchResult, len ) ;
603
}
604
} else if ( searchHighlight == static_cast < int > ( SearchHighlight:: ALL) ) {
605
int start = std:: max ( 0 , int ( a - len + 1 ) ) ;
606
for ( unsigned i = start; i <= a; + + i) {
607
if ( match ( debuggable, memSize, i ) ) return true ;
608
}
609
}
610
return false ;
611
} ;
612
auto highLight = [ & ] ( unsigned a) {
613
return highLightDataPreview ( a ) || highLightSearch ( a ) ;
614
} ;
615
616
617
for ( int n = 0 ; n < columns && addr < memSize; + + n, + + addr) {
618
int macroColumn = n / MidColsCount;
619
float bytePosX = s. posHexStart + float ( n) * s. hexCellWidth
620
+ float ( macroColumn) * s. spacingBetweenMidCols ;
621
ImGui:: SameLine ( bytePosX ) ;
622
623
624
if ( highLight ( addr ) ) {
625
ImVec2 pos = ImGui:: GetCursorScreenPos ( ) ;
626
float highlightWidth = s. glyphWidth * 2 ;
627
if ( highLight ( addr + 1 ) ) {
628
highlightWidth = s. hexCellWidth ;
629
if ( n > 0 && ( n + 1 ) < columns && ( ( n + 1 ) % MidColsCount) == 0 ) {
630
highlightWidth += s. spacingBetweenMidCols ;
631
}
632
}
633
drawList-> AddRectFilled ( pos, ImVec2 ( pos. x + highlightWidth, pos. y + s. lineHeight ) , HighlightColor ) ;
634
}
635
636
637
if ( showSymbolInfo) {
638
auto symbol = symbolManager. lookupValue ( narrow_cast < uint16_t > ( addr ) ) ;
639
if ( ! symbol. empty ( ) ) {
640
float highlightWidth = s. glyphWidth * 2 ;
641
ImVec2 pos = ImGui:: GetCursorScreenPos ( ) ;
642
drawList-> AddRectFilled ( pos, ImVec2 ( pos. x + highlightWidth, pos. y + s. lineHeight ) , HighlightSymbolColor ) ;
643
}
644
}
645
646
if ( currentAddr == addr && ( dataEditingActive == HEX) ) {
647
handleInput ( addr, 2 ,
648
[ & ] ( ImGuiInputTextCallbackData* data) {
649
auto valStr = formatData ( debuggable. read ( addr ) ) ;
650
data-> InsertChars ( 0 , valStr. data ( ) , valStr. data ( ) + valStr. size ( ) ) ;
651
data-> SelectionEnd = 2 ;
652
} ,
653
[ & ] ( std:: string_view data) {
654
return parseDataValue ( data ) ;
655
} ,
656
ImGuiInputTextFlags_CharsHexadecimal ) ;
657
} else {
658
uint8_t b = debuggable. read ( addr ) ;
659
bool changed = drawChanges && ( b != snapshot[ addr] ) ;
660
bool grey = ( b == 0 ) && greyOutZeroes;
661
im:: StyleColor ( changed || grey, ImGuiCol_Text, changed ? changedColor : greyColor, [ & ] {
662
ImGui:: StrCat ( formatData ( b ) , ' ' ) ;
663
} ) ;
664
if ( ImGui:: IsItemHovered ( ) && ImGui:: IsMouseClicked ( 0 ) ) {
665
dataEditingActive = HEX;
666
dataEditingTakeFocus = true ;
667
nextAddr = addr;
668
}
669
}
670
}
671
672
if ( showAscii) {
673
674
ImGui:: SameLine ( s. posAsciiStart ) ;
675
gl:: vec2 pos = ImGui:: GetCursorPos ( ) ;
676
gl:: vec2 scrnPos = ImGui:: GetCursorScreenPos ( ) ;
677
addr = unsigned ( line) * columns;
678
679
im:: ID ( line, [ & ] {
680
681
682
if ( ImGui:: InvisibleButton ( " ascii" , ImVec2 ( s. posAsciiEnd - s. posAsciiStart , s. lineHeight ) ) ) {
683
dataEditingActive = ASCII;
684
dataEditingTakeFocus = true ;
685
nextAddr = addr + unsigned ( ( ImGui:: GetIO ( ) . MousePos . x - scrnPos. x ) / s. glyphWidth ) ;
686
}
687
} ) ;
688
689
for ( int n = 0 ; n < columns && addr < memSize; + + n, + + addr) {
690
if ( highLight ( addr ) ) {
691
auto start = scrnPos + gl:: vec2 ( float ( n) * s. glyphWidth , 0.0f ) ;
692
drawList-> AddRectFilled ( start, start + gl:: vec2 ( s. glyphWidth , s. lineHeight ) , ImGui:: GetColorU32 ( HighlightColor ) ) ;
693
}
694
695
ImGui:: SetCursorPos ( pos ) ;
696
if ( currentAddr == addr && ( dataEditingActive == ASCII) ) {
697
handleInput ( addr, 1 ,
698
[ & ] ( ImGuiInputTextCallbackData* data) {
699
char valChar = formatAsciiData ( debuggable. read ( addr ) ) ;
700
data-> InsertChars ( 0 , & valChar, & valChar + 1 ) ;
701
data-> SelectionEnd = 1 ;
702
} ,
703
[ & ] ( std:: string_view data) -> std :: optional < uint8_t > {
704
if ( data. empty ( ) ) return { } ;
705
uint8_t b = data[ 0 ] ;
706
if ( b < 32 || b >= 128 ) return { } ;
707
return b;
708
} ) ;
709
} else {
710
uint8_t c = debuggable. read ( addr ) ;
711
bool changed = drawChanges && ( c != snapshot[ addr] ) ;
712
char display = formatAsciiData ( c ) ;
713
bool grey = display != char ( c) ;
714
im:: StyleColor ( changed || grey, ImGuiCol_Text, changed ? changedColor : greyColor, [ & ] {
715
ImGui:: TextUnformatted ( & display, & display + 1 ) ;
716
} ) ;
717
}
718
pos. x += s. glyphWidth ;
719
}
720
}
721
} ) ;
722
ImGui:: PopStyleVar ( 2 ) ;
723
ImGui:: EndChild ( ) ;
724
725
if ( nextAddr) {
726
setAddr ( s, debuggable, memSize, * nextAddr ) ;
727
dataEditingTakeFocus = true ;
728
addrMode = CURSOR;
729
}
730
731
if ( showAddress) {
732
bool forceScroll = ImGui:: IsWindowAppearing ( ) ;
733
734
ImGui:: Separator ( ) ;
735
ImGui:: AlignTextToFramePadding ( ) ;
736
ImGui:: TextUnformatted ( " Address" ) ;
737
ImGui:: SameLine ( ) ;
738
ImGui:: SetNextItemWidth ( 2.0f * style. FramePadding . x + ImGui:: CalcTextSize ( " Expression" ) . x + ImGui:: GetFrameHeight ( ) ) ;
739
if ( ImGui:: Combo ( " ##mode" , & addrMode, " Cursor\0 Expression\0 Link BC\0 Link DE\0 Link HL\0 Link IX\0 Link IY\0 " ) ) {
740
dataEditingTakeFocus = true ;
741
forceScroll = true ;
742
if ( addrMode >= 2 ) {
743
static constexpr std:: array linkExpr = {
744
" [reg bc]" , " [reg de]" , " [reg hl]" , " [reg ix]" , " [reg iy]"
745
} ;
746
addrExpr = linkExpr[ addrMode - 2 ] ;
747
addrMode = EXPRESSION;
748
}
749
}
750
ImGui:: SameLine ( ) ;
751
752
std:: string* as = addrMode == CURSOR ? & addrStr : & addrExpr;
753
auto addr = parseAddressExpr ( * as, symbolManager, manager. getInterpreter ( ) ) ;
754
im:: StyleColor ( ! addr, ImGuiCol_Text, getColor ( imColor:: ERROR ) , [ & ] {
755
if ( addrMode == EXPRESSION && addr) {
756
scrollAddr ( s, debuggable, memSize, * addr, forceScroll ) ;
757
}
758
if ( manager. getShortcuts ( ) . checkShortcut ( Shortcuts:: ID:: HEX_GOTO_ADDR ) ) {
759
ImGui:: SetKeyboardFocusHere ( ) ;
760
}
761
ImGui:: SetNextItemWidth ( 15.0f * ImGui:: GetFontSize ( ) ) ;
762
if ( ImGui:: InputText ( " ##addr" , as, ImGuiInputTextFlags_EnterReturnsTrue) ) {
763
if ( auto addr2 = parseAddressExpr ( addrStr, symbolManager, manager. getInterpreter ( ) ) ) {
764
scrollAddr ( s, debuggable, memSize, * addr2, forceScroll ) ;
765
dataEditingTakeFocus = true ;
766
}
767
}
768
simpleToolTip ( [ & ] {
769
return addr ? strCat ( " 0x" , formatAddr ( s, * addr ) )
770
: addr. error ( ) ;
771
} ) ;
772
} ) ;
773
im:: Font ( manager. fontProp , [ & ] {
774
HelpMarker ( " Address-mode:\n "
775
" Cursor: view the cursor position\n "
776
" Expression: continuously re-evaluate an expression and view that address\n "
777
" \n "
778
" Addresses can be entered as:\n "
779
" Decimal or hexadecimal values (e.g. 0x1234)\n "
780
" A calculation like 0x1234 + 7*22\n "
781
" The name of a label (e.g. CHPUT)\n "
782
" A Tcl expression (e.g. [reg hl] to follow the content of register HL)\n "
783
" \n "
784
" Right-click to configure this view." ) ;
785
} ) ;
786
}
787
if ( showSearch) {
788
ImGui:: Separator ( ) ;
789
drawSearch ( s, debuggable, memSize ) ;
790
}
791
if ( showDataPreview) {
792
ImGui:: Separator ( ) ;
793
drawPreviewLine ( s, debuggable, memSize ) ;
794
}
795
if ( showSymbolInfo) {
796
ImGui:: Separator ( ) ;
797
ImGui:: AlignTextToFramePadding ( ) ;
798
auto symbol = symbolManager. lookupValue ( narrow_cast < uint16_t > ( currentAddr ) ) ;
799
if ( ! symbol. empty ( ) ) {
800
ImGui:: Text ( " Current symbol: %s " , symbol[ 0 ] -> name . c_str ( ) ) ;
801
} else {
802
ImGui:: TextUnformatted ( " No symbol for this address defined" sv) ;
803
}
804
}
805
806
im:: Popup ( " context" , [ & ] {
807
im:: ScopedFont sf ( manager. fontProp ) ;
808
ImGui:: SetNextItemWidth ( 8.5f * s. glyphWidth + 2.0f * style. FramePadding . x ) ;
809
if ( ImGui:: InputInt ( " Columns" , & columns, 1 , 0 ) ) {
810
columns = std:: clamp ( columns, 1 , MAX_COLUMNS ) ;
811
}
812
ImGui:: Checkbox ( " Show Address bar" , & showAddress) ;
813
ImGui:: Checkbox ( " Show Search pane" , & showSearch) ;
814
ImGui:: Checkbox ( " Show Data Preview" , & showDataPreview) ;
815
ImGui:: Checkbox ( " Show ASCII" , & showAscii) ;
816
ImGui:: Checkbox ( " Show Symbol info" , & showSymbolInfo) ;
817
ImGui:: Checkbox ( " Grey out zeroes" , & greyOutZeroes) ;
818
im:: Menu ( " Highlight changes" , [ & ] {
819
debugger-> configureChangesMenu ( ) ;
820
} ) ;
821
ImGui:: Separator ( ) ;
822
if ( ImGui:: MenuItem ( " Export..." ) ) {
823
showExportWindow = true ;
824
}
825
} ) ;
826
im:: Popup ( " NotFound" , [ & ] {
827
ImGui:: TextUnformatted ( " Not found" ) ;
828
} ) ;
829
}
830
831
[ [ nodiscard] ] static const char * DataTypeGetDesc ( ImGuiDataType dataType )
832
{
833
std:: array< const char * , 8 > desc = {
834
" Int8" , " Uint8" , " Int16" , " Uint16" , " Int32" , " Uint32" , " Int64" , " Uint64"
835
} ;
836
assert ( dataType >= 0 && dataType < 8 ) ;
837
return desc[ dataType] ;
838
}
839
840
template < typename T>
841
[ [ nodiscard] ] static T read ( std:: span< const uint8_t > buf )
842
{
843
assert ( buf. size ( ) >= sizeof ( T ) ) ;
844
T t = 0 ;
845
memcpy ( & t, buf. data ( ) , sizeof ( T ) ) ;
846
return t;
847
}
848
849
static void formatDec ( std:: span< const uint8_t > buf , ImGuiDataType dataType )
850
{
851
switch ( dataType) {
852
case ImGuiDataType_S8:
853
ImGui:: StrCat ( read < int8_t > ( buf ) ) ;
854
break ;
855
case ImGuiDataType_U8:
856
ImGui:: StrCat ( read < uint8_t > ( buf ) ) ;
857
break ;
858
case ImGuiDataType_S16:
859
ImGui:: StrCat ( read < int16_t > ( buf ) ) ;
860
break ;
861
case ImGuiDataType_U16:
862
ImGui:: StrCat ( read < uint16_t > ( buf ) ) ;
863
break ;
864
case ImGuiDataType_S32:
865
ImGui:: StrCat ( read < int32_t > ( buf ) ) ;
866
break ;
867
case ImGuiDataType_U32:
868
ImGui:: StrCat ( read < uint32_t > ( buf ) ) ;
869
break ;
870
case ImGuiDataType_S64:
871
ImGui:: StrCat ( read < int64_t > ( buf ) ) ;
872
break ;
873
case ImGuiDataType_U64:
874
ImGui:: StrCat ( read < uint64_t > ( buf ) ) ;
875
break ;
876
default :
877
UNREACHABLE;
878
}
879
}
880
881
static void formatHex ( std:: span< const uint8_t > buf , ImGuiDataType data_type )
882
{
883
switch ( data_type) {
884
case ImGuiDataType_S8:
885
case ImGuiDataType_U8:
886
ImGui:: StrCat ( hex_string < 2 > ( read < uint8_t > ( buf ) ) ) ;
887
break ;
888
case ImGuiDataType_S16:
889
case ImGuiDataType_U16:
890
ImGui:: StrCat ( hex_string < 4 > ( read < uint16_t > ( buf ) ) ) ;
891
break ;
892
case ImGuiDataType_S32:
893
case ImGuiDataType_U32:
894
ImGui:: StrCat ( hex_string < 8 > ( read < uint32_t > ( buf ) ) ) ;
895
break ;
896
case ImGuiDataType_S64:
897
case ImGuiDataType_U64:
898
ImGui:: StrCat ( hex_string < 16 > ( read < uint64_t > ( buf ) ) ) ;
899
break ;
900
default :
901
UNREACHABLE;
902
}
903
}
904
905
static void formatBin ( std:: span< const uint8_t > buf )
906
{
907
for ( int i = int ( buf. size ( ) ) - 1 ; i >= 0 ; - - i) {
908
ImGui:: StrCat ( bin_string < 8 > ( buf[ i] ) ) ;
909
if ( i != 0 ) ImGui:: SameLine ( ) ;
910
}
911
}
912
913
void DebuggableEditor:: parseSearchString ( std:: string_view str )
914
{
915
searchPattern. reset ( ) ;
916
searchResult. reset ( ) ;
917
std:: vector< uint8_t > result;
918
919
if ( searchType == static_cast < int > ( SearchType:: ASCII) ) {
920
const auto * begin = std:: bit_cast < const uint8_t * > ( str. data ( ) ) ;
921
const auto * end = begin + str. size ( ) ;
922
result. assign ( begin, end ) ;
923
} else {
924
assert ( searchType == static_cast < int > ( SearchType:: HEX) ) ;
925
std:: optional< int > partial;
926
for ( char c : str) {
927
if ( c == ' ' ) continue ;
928
auto digit = parseHexDigit ( c ) ;
929
if ( ! digit) return ;
930
if ( partial) {
931
result. push_back ( narrow < uint8_t > ( 16 * * partial + * digit) ) ;
932
partial. reset ( ) ;
933
} else {
934
partial = * digit;
935
}
936
}
937
if ( partial) return ;
938
}
939
940
searchPattern = std:: move ( result ) ;
941
}
942
943
void DebuggableEditor:: drawSearch ( const Sizes& s , Debuggable& debuggable , unsigned memSize )
944
{
945
const auto & style = ImGui:: GetStyle ( ) ;
946
947
bool doSearch = false ;
948
auto buttonSize = ImGui:: CalcTextSize ( " Search" ) . x + 2.0f * style. FramePadding . x ;
949
ImGui:: SetNextItemWidth ( - ( buttonSize + style. WindowPadding . x ) ) ;
950
im:: StyleColor ( ! searchPattern, ImGuiCol_Text, getColor ( imColor:: ERROR ) , [ & ] {
951
auto callback = [ ] ( ImGuiInputTextCallbackData* data) {
952
if ( data-> EventFlag == ImGuiInputTextFlags_CallbackEdit) {
953
auto & self = * static_cast < DebuggableEditor* > ( data-> UserData ) ;
954
self. parseSearchString ( std:: string_view ( data-> Buf , data-> BufTextLen ) ) ;
955
}
956
return 0 ;
957
} ;
958
ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue
959
| ImGuiInputTextFlags_CallbackEdit;
960
if ( ImGui:: InputText ( " ##search_string" , & searchString, flags, callback, this ) ) {
961
doSearch = true ;
962
}
963
} ) ;
964
ImGui:: SameLine ( ) ;
965
im:: Disabled ( ! searchPattern, [ & ] {
966
doSearch |= ImGui:: Button ( " Search" ) ;
967
} ) ;
968
if ( ! searchPattern) {
969
simpleToolTip ( " Must be an even number of hex digits, optionally separated by spaces" ) ;
970
}
971
if ( searchPattern && doSearch) {
972
search ( s, debuggable, memSize ) ;
973
}
974
975
auto arrowSize = ImGui:: GetFrameHeight ( ) ;
976
auto extra = arrowSize + 2.0f * style. FramePadding . x ;
977
ImGui:: AlignTextToFramePadding ( ) ;
978
ImGui:: TextUnformatted ( " Type" ) ;
979
ImGui:: SameLine ( ) ;
980
ImGui:: SetNextItemWidth ( ImGui:: CalcTextSize ( " ASCII" ) . x + extra) ;
981
if ( ImGui:: Combo ( " ##search_type" , & searchType, " Hex\0 ASCII\0 \0 " ) ) {
982
parseSearchString ( searchString ) ;
983
}
984
985
ImGui:: SameLine ( 0.0f , 2 * ImGui:: GetFontSize ( ) ) ;
986
ImGui:: TextUnformatted ( " Direction" ) ;
987
ImGui:: SameLine ( ) ;
988
ImGui:: SetNextItemWidth ( ImGui:: CalcTextSize ( " Backwards" ) . x + extra) ;
989
ImGui:: Combo ( " ##search_direction" , & searchDirection, " Forwards\0 Backwards\0 \0 " ) ;
990
991
ImGui:: SameLine ( 0.0f , 2 * ImGui:: GetFontSize ( ) ) ;
992
ImGui:: TextUnformatted ( " Highlight" ) ;
993
ImGui:: SameLine ( ) ;
994
ImGui:: SetNextItemWidth ( ImGui:: CalcTextSize ( " Single" ) . x + extra) ;
995
ImGui:: Combo ( " ##search_highlight" , & searchHighlight, " None\0 Single\0 All\0 \0 " ) ;
996
}
997
998
bool DebuggableEditor:: match ( Debuggable& debuggable , unsigned memSize , unsigned addr )
999
{
1000
assert ( searchPattern ) ;
1001
if ( ( addr + searchPattern-> size ( ) ) > memSize) return false ;
1002
for ( auto [ i, c] : enumerate ( * searchPattern) ) {
1003
if ( debuggable. read ( narrow < unsigned > ( addr + i ) ) != c) return false ;
1004
}
1005
return true ;
1006
}
1007
1008
void DebuggableEditor:: search ( const Sizes& s , Debuggable& debuggable , unsigned memSize )
1009
{
1010
std:: optional< unsigned > found;
1011
auto test = [ & ] ( unsigned addr) {
1012
if ( match ( debuggable, memSize, addr ) ) {
1013
found = addr;
1014
return true ;
1015
}
1016
return false ;
1017
} ;
1018
if ( searchDirection == static_cast < int > ( SearchDirection:: FWD) ) {
1019
for ( unsigned addr = currentAddr + 1 ; addr < memSize; + + addr) {
1020
if ( test ( addr ) ) break ;
1021
}
1022
if ( ! found) {
1023
for ( unsigned addr = 0 ; addr <= currentAddr; + + addr) {
1024
if ( test ( addr ) ) break ;
1025
}
1026
}
1027
} else {
1028
for ( int addr = currentAddr - 1 ; addr > 0 ; - - addr) {
1029
if ( test ( unsigned ( addr) ) ) break ;
1030
}
1031
if ( ! found) {
1032
for ( int addr = memSize - 1 ; addr >= int ( currentAddr) ; - - addr) {
1033
if ( test ( unsigned ( addr) ) ) break ;
1034
}
1035
}
1036
}
1037
if ( found) {
1038
searchResult = * found;
1039
scrollAddr ( s, debuggable, memSize, * found, false ) ;
1040
dataEditingTakeFocus = true ;
1041
addrMode = CURSOR;
1042
} else {
1043
searchResult. reset ( ) ;
1044
ImGui:: OpenPopup ( " NotFound" ) ;
1045
}
1046
}
1047
1048
void DebuggableEditor:: drawPreviewLine ( const Sizes& s , Debuggable& debuggable , unsigned memSize )
1049
{
1050
const auto & style = ImGui:: GetStyle ( ) ;
1051
ImGui:: AlignTextToFramePadding ( ) ;
1052
ImGui:: TextUnformatted ( " Preview as:" sv) ;
1053
ImGui:: SameLine ( ) ;
1054
ImGui:: SetNextItemWidth ( ( s. glyphWidth * 10.0f ) + style. FramePadding . x * 2.0f + style. ItemInnerSpacing . x ) ;
1055
if ( ImGui:: BeginCombo ( " ##combo_type" , DataTypeGetDesc ( previewDataType ) , ImGuiComboFlags_HeightLargest) ) {
1056
for ( ImGuiDataType n = 0 ; n < 8 ; + + n) {
1057
if ( ImGui:: Selectable ( DataTypeGetDesc ( n ) , previewDataType == n) ) {
1058
previewDataType = n;
1059
}
1060
}
1061
ImGui:: EndCombo ( ) ;
1062
}
1063
ImGui:: SameLine ( ) ;
1064
ImGui:: SetNextItemWidth ( ( s. glyphWidth * 6.0f ) + style. FramePadding . x * 2.0f + style. ItemInnerSpacing . x ) ;
1065
ImGui:: Combo ( " ##combo_endianess" , & previewEndianess, " LE\0 BE\0 \0 " ) ;
1066
1067
std:: array< uint8_t , 8 > dataBuf = { } ;
1068
auto elemSize = DataTypeGetSize ( previewDataType ) ;
1069
for ( auto i : xrange ( elemSize ) ) {
1070
auto addr = currentAddr + i;
1071
dataBuf[ i] = ( addr < memSize) ? debuggable. read ( addr ) : 0 ;
1072
}
1073
1074
static constexpr bool nativeIsLittle = std:: endian:: native == std:: endian:: little;
1075
if ( bool previewIsLittle = previewEndianess == LE;
1076
nativeIsLittle != previewIsLittle) {
1077
std:: reverse ( dataBuf. begin ( ) , dataBuf. begin ( ) + elemSize ) ;
1078
}
1079
1080
ImGui:: TextUnformatted ( " Dec " sv) ;
1081
ImGui:: SameLine ( ) ;
1082
formatDec ( dataBuf, previewDataType ) ;
1083
1084
ImGui:: TextUnformatted ( " Hex " sv) ;
1085
ImGui:: SameLine ( ) ;
1086
formatHex ( dataBuf, previewDataType ) ;
1087
1088
ImGui:: TextUnformatted ( " Bin " sv) ;
1089
ImGui:: SameLine ( ) ;
1090
formatBin ( subspan ( dataBuf, 0 , elemSize ) ) ;
1091
}
1092
1093
}