e8555c89 Andy McCall 1 month ago
1
#include " Debugger.hh"
2
3
#include " BreakPoint.hh"
4
#include " CPURegs.hh"
5
#include " CommandException.hh"
6
#include " Dasm.hh"
7
#include " DebugCondition.hh"
8
#include " Debuggable.hh"
9
#include " MSXCPU.hh"
10
#include " MSXCPUInterface.hh"
11
#include " MSXCliComm.hh"
12
#include " MSXMotherBoard.hh"
13
#include " ProbeBreakPoint.hh"
14
#include " Reactor.hh"
15
#include " SymbolManager.hh"
16
#include " TclArgParser.hh"
17
#include " TclObject.hh"
18
#include " WatchPoint.hh"
19
20
#include " MemBuffer.hh"
21
#include " StringOp.hh"
22
#include " narrow.hh"
23
#include " one_of.hh"
24
#include " stl.hh"
25
#include " unreachable.hh"
26
#include " xrange.hh"
27
28
#include < algorithm>
29
#include < array>
30
#include < cassert>
31
#include < memory>
32
#include < ranges>
33
34
namespace openmsx {
35
36
Debugger:: Debugger ( MSXMotherBoard& motherBoard_ )
37
: motherBoard ( motherBoard_ )
38
, cmd ( motherBoard. getCommandController ( ) ,
39
motherBoard. getStateChangeDistributor ( ) ,
40
motherBoard. getScheduler ( ) )
41
, tracer ( * this )
42
{
43
}
44
45
Debugger:: ~Debugger ( )
46
{
47
assert ( ! cpu) ;
48
assert ( debuggables. empty ( ) ) ;
49
}
50
51
void Debugger:: registerDebuggable ( std:: string name , Debuggable& debuggable )
52
{
53
assert ( ! debuggables. contains ( name ) ) ;
54
debuggables. emplace_noDuplicateCheck ( std:: move ( name ) , & debuggable) ;
55
}
56
57
void Debugger:: unregisterDebuggable ( std:: string_view name , Debuggable& debuggable )
58
{
59
assert ( debuggables. contains ( name ) ) ;
60
assert ( debuggables[ name] == & debuggable ) ; ( void ) debuggable;
61
debuggables. erase ( name ) ;
62
}
63
64
Debuggable* Debugger:: findDebuggable ( std:: string_view name )
65
{
66
auto * v = lookup ( debuggables, name ) ;
67
return v ? * v : nullptr ;
68
}
69
70
Debuggable& Debugger:: getDebuggable ( std:: string_view name )
71
{
72
Debuggable* result = findDebuggable ( name ) ;
73
if ( ! result) {
74
throw CommandException ( " No such debuggable: " , name) ;
75
}
76
return * result;
77
}
78
79
void Debugger:: registerProbe ( ProbeBase& probe )
80
{
81
auto it = std:: ranges:: lower_bound ( probes, probe. getName ( ) , { } , & ProbeBase:: getName ) ;
82
assert ( it == probes. end ( ) || ( * it) -> getName ( ) != probe. getName ( ) ) ;
83
probes. insert ( it, & probe ) ;
84
tracer. probeCreated ( * this , probe) ;
85
}
86
87
void Debugger:: unregisterProbe ( ProbeBase& probe )
88
{
89
tracer. probeRemoved ( probe ) ;
90
auto it = std:: ranges:: lower_bound ( probes, probe. getName ( ) , { } , & ProbeBase:: getName ) ;
91
assert ( it != probes. end ( ) && ( * it) -> getName ( ) == probe. getName ( ) ) ;
92
probes. erase ( it ) ;
93
}
94
95
ProbeBase* Debugger:: findProbe ( std:: string_view name )
96
{
97
auto it = std:: ranges:: lower_bound ( probes, name, { } , & ProbeBase:: getName ) ;
98
return ( it != probes. end ( ) && ( * it) -> getName ( ) == name) ? * it : nullptr ;
99
}
100
101
ProbeBase& Debugger:: getProbe ( std:: string_view name )
102
{
103
auto * result = findProbe ( name ) ;
104
if ( ! result) {
105
throw CommandException ( " No such probe: " , name) ;
106
}
107
return * result;
108
}
109
110
std:: string Debugger:: insertProbeBreakPoint (
111
TclObject command , TclObject condition ,
112
ProbeBase& probe , bool once , unsigned newId )
113
{
114
auto bp = std:: make_unique < ProbeBreakPoint> (
115
std:: move ( command ) , std:: move ( condition ) , * this , probe, once, newId ) ;
116
auto result = bp-> getIdStr ( ) ;
117
motherBoard. getMSXCliComm ( ) . update ( CliComm:: UpdateType:: DEBUG_UPDT, result, " add" ) ;
118
probeBreakPoints. push_back ( std:: move ( bp ) ) ;
119
return result;
120
}
121
122
void Debugger:: removeProbeBreakPoint ( std:: string_view name )
123
{
124
if ( name. starts_with ( ProbeBreakPoint:: prefix ) ) {
125
126
if ( auto id = StringOp:: stringToBase < 10 , unsigned > ( name. substr ( ProbeBreakPoint:: prefix. size ( ) ) ) ) {
127
if ( auto it = std:: ranges:: find ( probeBreakPoints, id, & ProbeBreakPoint:: getId ) ;
128
it != std:: end ( probeBreakPoints ) ) {
129
motherBoard. getMSXCliComm ( ) . update (
130
CliComm:: UpdateType:: DEBUG_UPDT, name, " remove" ) ;
131
move_pop_back ( probeBreakPoints, it ) ;
132
return ;
133
}
134
}
135
throw CommandException ( " No such breakpoint: " , name) ;
136
} else {
137
138
auto it = std:: ranges:: find ( probeBreakPoints, name, [ ] ( auto & bp) {
139
return bp-> getProbe ( ) . getName ( ) ;
140
} ) ;
141
if ( it == std:: end ( probeBreakPoints ) ) {
142
throw CommandException (
143
" No (unconditional) breakpoint for probe: " , name ) ;
144
}
145
motherBoard. getMSXCliComm ( ) . update (
146
CliComm:: UpdateType:: DEBUG_UPDT, ( * it) -> getIdStr ( ) , " remove" ) ;
147
move_pop_back ( probeBreakPoints, it ) ;
148
}
149
}
150
151
void Debugger:: removeProbeBreakPoint ( ProbeBreakPoint& bp )
152
{
153
motherBoard. getMSXCliComm ( ) . update (
154
CliComm:: UpdateType:: DEBUG_UPDT, bp. getIdStr ( ) , " remove" ) ;
155
move_pop_back ( probeBreakPoints, rfind_unguarded ( probeBreakPoints, & bp,
156
[ ] ( auto & v) { return v. get ( ) ; } ) ) ;
157
}
158
159
void Debugger:: transfer ( Debugger& other )
160
{
161
162
auto & cpuInterface = motherBoard. getCPUInterface ( ) ;
163
assert ( cpuInterface. getWatchPoints ( ) . empty ( ) ) ;
164
for ( const auto & wp : other. motherBoard . getCPUInterface ( ) . getWatchPoints ( ) ) {
165
cpuInterface. setWatchPoint ( std:: make_shared < WatchPoint> (
166
WatchPoint:: clone_tag{ } , * wp ) ) ;
167
}
168
169
170
assert ( probeBreakPoints. empty ( ) ) ;
171
for ( const auto & bp : other. probeBreakPoints ) {
172
if ( ProbeBase* probe = findProbe ( bp-> getProbe ( ) . getName ( ) ) ) {
173
insertProbeBreakPoint ( bp-> getCommand ( ) ,
174
bp-> getCondition ( ) ,
175
* probe, bp-> onlyOnce ( ) ,
176
bp-> getId ( ) ) ;
177
}
178
}
179
180
tracer. transfer ( other, * this ) ;
181
182
183
184
}
185
186
Interpreter& Debugger:: getInterpreter ( )
187
{
188
return motherBoard. getReactor ( ) . getInterpreter ( ) ;
189
}
190
191
std:: vector< ImGuiWatchExpr:: WatchExpr> & Debugger:: getWatchExprs ( ) const
192
{
193
return motherBoard. getReactor ( ) . getImGuiManager ( ) . watchExpr -> getWatchExprs ( ) ;
194
}
195
196
197
198
static uint16_t getAddress ( Interpreter& interp , const TclObject& token )
199
{
200
unsigned addr = token. getInt ( interp ) ;
201
if ( addr >= 0x10000 ) {
202
throw CommandException ( " Invalid address" ) ;
203
}
204
return narrow_cast < uint16_t > ( addr ) ;
205
}
206
207
Debugger:: Cmd:: Cmd ( CommandController& commandController_ ,
208
StateChangeDistributor& stateChangeDistributor_ ,
209
Scheduler& scheduler_ )
210
: RecordedCommand ( commandController_, stateChangeDistributor_,
211
scheduler_, " debug" )
212
{
213
}
214
215
bool Debugger:: Cmd:: needRecord ( std:: span< const TclObject> tokens ) const
216
{
217
218
219
220
221
if ( tokens. size ( ) < 2 ) return false ;
222
return tokens[ 1 ] . getString ( ) == one_of ( " write" , " write_block" ) ;
223
}
224
225
void Debugger:: Cmd:: execute (
226
std:: span< const TclObject> tokens , TclObject& result , EmuTime time )
227
{
228
checkNumArgs ( tokens, AtLeast{ 2 } , " subcommand ?arg ...?" ) ;
229
executeSubCommand ( tokens[ 1 ] . getString ( ) ,
230
" read" , [ & ] { read ( tokens, result ) ; } ,
231
" read_block" , [ & ] { readBlock ( tokens, result ) ; } ,
232
" write" , [ & ] { write ( tokens, result ) ; } ,
233
" write_block" , [ & ] { writeBlock ( tokens, result ) ; } ,
234
" size" , [ & ] { size ( tokens, result ) ; } ,
235
" desc" , [ & ] { desc ( tokens, result ) ; } ,
236
" list" , [ & ] { list ( result ) ; } ,
237
" step" , [ & ] { debugger ( ) . motherBoard . getCPUInterface ( ) . doStep ( ) ; } ,
238
" cont" , [ & ] { debugger ( ) . motherBoard . getCPUInterface ( ) . doContinue ( ) ; } ,
239
" disasm" , [ & ] { disasm ( tokens, result, time ) ; } ,
240
" disasm_blob" , [ & ] { disasmBlob ( tokens, result ) ; } ,
241
" break" , [ & ] { debugger ( ) . motherBoard . getCPUInterface ( ) . doBreak ( ) ; } ,
242
" breaked" , [ & ] { result = MSXCPUInterface:: isBreaked ( ) ; } ,
243
" breakpoint" , [ & ] { breakPoint ( tokens, result ) ; } ,
244
" set_bp" , [ & ] { setBreakPoint ( tokens, result ) ; } ,
245
" remove_bp" , [ & ] { removeBreakPoint ( tokens, result ) ; } ,
246
" list_bp" , [ & ] { listBreakPoints ( tokens, result ) ; } ,
247
" watchpoint" , [ & ] { watchPoint ( tokens, result ) ; } ,
248
" watchexpr" , [ & ] { watchExpr ( tokens, result ) ; } ,
249
" set_watchpoint" , [ & ] { setWatchPoint ( tokens, result ) ; } ,
250
" remove_watchpoint" , [ & ] { removeWatchPoint ( tokens, result ) ; } ,
251
" list_watchpoints" , [ & ] { listWatchPoints ( tokens, result ) ; } ,
252
" condition" , [ & ] { condition ( tokens, result ) ; } ,
253
" set_condition" , [ & ] { setCondition ( tokens, result ) ; } ,
254
" remove_condition" , [ & ] { removeCondition ( tokens, result ) ; } ,
255
" list_conditions" , [ & ] { listConditions ( tokens, result ) ; } ,
256
" probe" , [ & ] { probe ( tokens, result ) ; } ,
257
" symbols" , [ & ] { symbols ( tokens, result ) ; } ,
258
" trace" , [ & ] { auto & d = debugger ( ) ; d. tracer . execute ( d, tokens, result, time ) ; } ) ;
259
}
260
261
void Debugger:: Cmd:: list ( TclObject& result )
262
{
263
result. addListElements ( std:: views:: keys ( debugger ( ) . debuggables ) ) ;
264
}
265
266
void Debugger:: Cmd:: desc ( std:: span< const TclObject> tokens , TclObject& result )
267
{
268
checkNumArgs ( tokens, 3 , " debuggable" ) ;
269
const Debuggable& device = debugger ( ) . getDebuggable ( tokens[ 2 ] . getString ( ) ) ;
270
result = device. getDescription ( ) ;
271
}
272
273
void Debugger:: Cmd:: size ( std:: span< const TclObject> tokens , TclObject& result )
274
{
275
checkNumArgs ( tokens, 3 , " debuggable" ) ;
276
const Debuggable& device = debugger ( ) . getDebuggable ( tokens[ 2 ] . getString ( ) ) ;
277
result = device. getSize ( ) ;
278
}
279
280
void Debugger:: Cmd:: read ( std:: span< const TclObject> tokens , TclObject& result )
281
{
282
checkNumArgs ( tokens, 4 , Prefix{ 2 } , " debuggable address" ) ;
283
Debuggable& device = debugger ( ) . getDebuggable ( tokens[ 2 ] . getString ( ) ) ;
284
unsigned addr = tokens[ 3 ] . getInt ( getInterpreter ( ) ) ;
285
if ( addr >= device. getSize ( ) ) {
286
throw CommandException ( " Invalid address" ) ;
287
}
288
result = device. read ( addr ) ;
289
}
290
291
void Debugger:: Cmd:: readBlock ( std:: span< const TclObject> tokens , TclObject& result )
292
{
293
checkNumArgs ( tokens, 5 , Prefix{ 2 } , " debuggable address size" ) ;
294
auto & interp = getInterpreter ( ) ;
295
Debuggable& device = debugger ( ) . getDebuggable ( tokens[ 2 ] . getString ( ) ) ;
296
unsigned devSize = device. getSize ( ) ;
297
unsigned addr = tokens[ 3 ] . getInt ( interp ) ;
298
if ( addr >= devSize) {
299
throw CommandException ( " Invalid address" ) ;
300
}
301
unsigned num = tokens[ 4 ] . getInt ( interp ) ;
302
if ( num > ( devSize - addr) ) {
303
throw CommandException ( " Invalid size" ) ;
304
}
305
306
MemBuffer< byte> buf ( num ) ;
307
device. readBlock ( addr, buf ) ;
308
result = std:: span{ buf} ;
309
}
310
311
void Debugger:: Cmd:: write ( std:: span< const TclObject> tokens , TclObject& )
312
{
313
checkNumArgs ( tokens, 5 , Prefix{ 2 } , " debuggable address value" ) ;
314
auto & interp = getInterpreter ( ) ;
315
Debuggable& device = debugger ( ) . getDebuggable ( tokens[ 2 ] . getString ( ) ) ;
316
unsigned addr = tokens[ 3 ] . getInt ( interp ) ;
317
if ( addr >= device. getSize ( ) ) {
318
throw CommandException ( " Invalid address" ) ;
319
}
320
unsigned value = tokens[ 4 ] . getInt ( interp ) ;
321
if ( value >= 256 ) {
322
throw CommandException ( " Invalid value" ) ;
323
}
324
325
device. write ( addr, narrow_cast < byte> ( value ) ) ;
326
}
327
328
void Debugger:: Cmd:: writeBlock ( std:: span< const TclObject> tokens , TclObject& )
329
{
330
checkNumArgs ( tokens, 5 , Prefix{ 2 } , " debuggable address values" ) ;
331
Debuggable& device = debugger ( ) . getDebuggable ( tokens[ 2 ] . getString ( ) ) ;
332
unsigned devSize = device. getSize ( ) ;
333
unsigned addr = tokens[ 3 ] . getInt ( getInterpreter ( ) ) ;
334
if ( addr >= devSize) {
335
throw CommandException ( " Invalid address" ) ;
336
}
337
auto buf = tokens[ 4 ] . getBinary ( getInterpreter ( ) ) ;
338
if ( ( buf. size ( ) + addr) > devSize) {
339
throw CommandException ( " Invalid size" ) ;
340
}
341
342
for ( auto i : xrange ( buf. size ( ) ) ) {
343
device. write ( unsigned ( addr + i) , buf[ i] ) ;
344
}
345
}
346
347
static constexpr char toHex ( byte x )
348
{
349
return narrow < char > ( ( x < 10 ) ? ( x + ' 0' ) : ( x - 10 + ' A' ) ) ;
350
}
351
static constexpr void toHex ( byte x , std:: span< char , 3 > buf )
352
{
353
buf[ 0 ] = toHex ( x / 16 ) ;
354
buf[ 1 ] = toHex ( x & 15 ) ;
355
}
356
357
void Debugger:: Cmd:: disasm ( std:: span< const TclObject> tokens , TclObject& result , EmuTime time ) const
358
{
359
uint16_t address = ( tokens. size ( ) < 3 ) ? debugger ( ) . cpu -> getRegisters ( ) . getPC ( )
360
: uint16_t ( tokens[ 2 ] . getInt ( getInterpreter ( ) ) ) ;
361
std:: array< byte, 4 > outBuf;
362
std:: string dasmOutput;
363
unsigned len = dasm ( debugger ( ) . motherBoard . getCPUInterface ( ) , address, outBuf, dasmOutput, time) ;
364
dasmOutput. resize ( 19 , ' ' ) ;
365
result. addListElement ( dasmOutput ) ;
366
std:: array< char , 3 > tmp; tmp[ 2 ] = 0 ;
367
for ( auto i : xrange ( len ) ) {
368
toHex ( outBuf[ i] , tmp ) ;
369
result. addListElement ( tmp. data ( ) ) ;
370
}
371
}
372
373
void Debugger:: Cmd:: disasmBlob ( std:: span< const TclObject> tokens , TclObject& result ) const
374
{
375
checkNumArgs ( tokens, Between{ 4 , 5 } , Prefix{ 2 } , " value addr ?function?" ) ;
376
std:: span< const uint8_t > bin = tokens[ 2 ] . getBinary ( getInterpreter ( ) ) ;
377
auto len = instructionLength ( bin ) ;
378
if ( ! len || * len > bin. size ( ) ) {
379
throw CommandException ( " Blob does not contain a complete Z80 instruction" ) ;
380
}
381
std:: string dasmOutput;
382
unsigned addr = tokens[ 3 ] . getInt ( getInterpreter ( ) ) ;
383
dasm ( bin. subspan ( 0 , * len) , uint16_t ( addr) , dasmOutput,
384
[ & ] ( std:: string& out, uint16_t a) {
385
zstring_view cmdRes;
386
if ( tokens. size ( ) > 4 ) {
387
auto command = makeTclList ( tokens[ 4 ] , a ) ;
388
cmdRes = command. executeCommand ( getInterpreter ( ) ) . getString ( ) ;
389
}
390
if ( ! cmdRes. empty ( ) ) {
391
strAppend ( out, cmdRes ) ;
392
} else {
393
appendAddrAsHex ( out, a ) ;
394
}
395
} ) ;
396
dasmOutput. resize ( 19 , ' ' ) ;
397
result. addListElement ( dasmOutput ) ;
398
result. addListElement ( * len) ;
399
}
400
401
void Debugger:: Cmd:: breakPoint ( std:: span< const TclObject> tokens , TclObject& result )
402
{
403
checkNumArgs ( tokens, AtLeast{ 3 } , " subcommand ?arg ...?" ) ;
404
executeSubCommand ( tokens[ 2 ] . getString ( ) ,
405
" list" , [ & ] { breakPointList ( tokens, result ) ; } ,
406
" create" , [ & ] { breakPointCreate ( tokens, result ) ; } ,
407
" configure" , [ & ] { breakPointConfigure ( tokens, result ) ; } ,
408
" remove" , [ & ] { breakPointRemove ( tokens, result ) ; } ) ;
409
}
410
411
void Debugger:: Cmd:: watchPoint ( std:: span< const TclObject> tokens , TclObject& result )
412
{
413
checkNumArgs ( tokens, AtLeast{ 3 } , " subcommand ?arg ...?" ) ;
414
executeSubCommand ( tokens[ 2 ] . getString ( ) ,
415
" list" , [ & ] { watchPointList ( tokens, result ) ; } ,
416
" create" , [ & ] { watchPointCreate ( tokens, result ) ; } ,
417
" configure" , [ & ] { watchPointConfigure ( tokens, result ) ; } ,
418
" remove" , [ & ] { watchPointRemove ( tokens, result ) ; } ) ;
419
}
420
421
void Debugger:: Cmd:: watchExpr ( std:: span< const TclObject> tokens , TclObject& result )
422
{
423
checkNumArgs ( tokens, AtLeast{ 3 } , " subcommand ?arg ...?" ) ;
424
executeSubCommand ( tokens[ 2 ] . getString ( ) ,
425
" list" , [ & ] { watchExprList ( tokens, result ) ; } ,
426
" create" , [ & ] { watchExprCreate ( tokens, result ) ; } ,
427
" configure" , [ & ] { watchExprConfigure ( tokens, result ) ; } ,
428
" remove" , [ & ] { watchExprRemove ( tokens, result ) ; } ) ;
429
}
430
431
void Debugger:: Cmd:: condition ( std:: span< const TclObject> tokens , TclObject& result )
432
{
433
checkNumArgs ( tokens, AtLeast{ 3 } , " subcommand ?arg ...?" ) ;
434
executeSubCommand ( tokens[ 2 ] . getString ( ) ,
435
" list" , [ & ] { conditionList ( tokens, result ) ; } ,
436
" create" , [ & ] { conditionCreate ( tokens, result ) ; } ,
437
" configure" , [ & ] { conditionConfigure ( tokens, result ) ; } ,
438
" remove" , [ & ] { conditionRemove ( tokens, result ) ; } ) ;
439
}
440
441
BreakPoint* Debugger:: Cmd:: lookupBreakPoint ( std:: string_view str )
442
{
443
if ( ! str. starts_with ( BreakPoint:: prefix ) ) return nullptr ;
444
if ( auto id = StringOp:: stringToBase < 10 , unsigned > ( str. substr ( BreakPoint:: prefix. size ( ) ) ) ) {
445
auto & breakPoints = MSXCPUInterface:: getBreakPoints ( ) ;
446
if ( auto it = std:: ranges:: find ( breakPoints, id, & BreakPoint:: getId ) ;
447
it != std:: end ( breakPoints ) ) {
448
return std:: to_address ( it ) ;
449
}
450
}
451
return nullptr ;
452
}
453
454
std:: shared_ptr< WatchPoint> Debugger:: Cmd:: lookupWatchPoint ( std:: string_view str )
455
{
456
if ( ! str. starts_with ( WatchPoint:: prefix ) ) return { } ;
457
if ( auto id = StringOp:: stringToBase < 10 , unsigned > ( str. substr ( WatchPoint:: prefix. size ( ) ) ) ) {
458
auto & interface = debugger ( ) . motherBoard . getCPUInterface ( ) ;
459
auto & watchPoints = interface. getWatchPoints ( ) ;
460
if ( auto it = std:: ranges:: find ( watchPoints, id, & WatchPoint:: getId ) ;
461
it != std:: end ( watchPoints ) ) {
462
return * it;
463
}
464
}
465
return { } ;
466
}
467
468
static auto findWatchExpr ( std:: vector< ImGuiWatchExpr:: WatchExpr> & watchExprs , std:: string_view str )
469
{
470
if ( ! str. starts_with ( ImGuiWatchExpr:: WatchExpr:: prefix ) ) return std:: end ( watchExprs ) ;
471
if ( auto id = StringOp:: stringToBase < 10 , unsigned > ( str. substr ( ImGuiWatchExpr:: WatchExpr:: prefix. size ( ) ) ) ) {
472
return std:: ranges:: find ( watchExprs, id, & ImGuiWatchExpr:: WatchExpr:: getId ) ;
473
}
474
return std:: end ( watchExprs ) ;
475
}
476
477
ImGuiWatchExpr:: WatchExpr* Debugger:: Cmd:: lookupWatchExpr ( std:: string_view str )
478
{
479
auto & watchExprs = debugger ( ) . getWatchExprs ( ) ;
480
auto it = findWatchExpr ( watchExprs, str ) ;
481
return ( it != std:: end ( watchExprs ) ) ? std:: to_address ( it ) : nullptr ;
482
}
483
484
DebugCondition* Debugger:: Cmd:: lookupCondition ( std:: string_view str )
485
{
486
if ( ! str. starts_with ( DebugCondition:: prefix ) ) return { } ;
487
if ( auto id = StringOp:: stringToBase < 10 , unsigned > ( str. substr ( DebugCondition:: prefix. size ( ) ) ) ) {
488
auto & conditions = MSXCPUInterface:: getConditions ( ) ;
489
if ( auto it = std:: ranges:: find ( conditions, id, & DebugCondition:: getId ) ;
490
it != std:: end ( conditions ) ) {
491
return std:: to_address ( it ) ;
492
}
493
}
494
return { } ;
495
}
496
497
static auto formatWpAddr ( const WatchPoint& wp )
498
{
499
TclObject result;
500
result. addListElement ( wp. getBeginAddressString ( ) ) ;
501
if ( auto end = wp. getEndAddressString ( ) ; ! end. getString ( ) . empty ( ) ) {
502
result. addListElement ( end ) ;
503
}
504
return result;
505
}
506
507
void Debugger:: Cmd:: breakPointList ( std:: span< const TclObject> , TclObject& result )
508
{
509
for ( const auto & bp : MSXCPUInterface:: getBreakPoints ( ) ) {
510
TclObject dict = makeTclDict (
511
TclObject ( " -address" ) , bp. getAddressString ( ) ,
512
TclObject ( " -condition" ) , bp. getCondition ( ) ,
513
TclObject ( " -command" ) , bp. getCommand ( ) ,
514
TclObject ( " -enabled" ) , bp. isEnabled ( ) ,
515
TclObject ( " -once" ) , bp. onlyOnce ( ) ) ;
516
result. addDictKeyValue ( bp. getIdStr ( ) , std:: move ( dict ) ) ;
517
}
518
}
519
520
void Debugger:: Cmd:: watchPointList ( std:: span< const TclObject> , TclObject& result )
521
{
522
auto & interface = debugger ( ) . motherBoard . getCPUInterface ( ) ;
523
for ( const auto & wp : interface. getWatchPoints ( ) ) {
524
TclObject dict = makeTclDict (
525
TclObject ( " -type" ) , WatchPoint:: format ( wp-> getType ( ) ) ,
526
TclObject ( " -address" ) , formatWpAddr ( * wp) ,
527
TclObject ( " -condition" ) , wp-> getCondition ( ) ,
528
TclObject ( " -command" ) , wp-> getCommand ( ) ,
529
TclObject ( " -enabled" ) , wp-> isEnabled ( ) ,
530
TclObject ( " -once" ) , wp-> onlyOnce ( ) ) ;
531
result. addDictKeyValue ( wp-> getIdStr ( ) , std:: move ( dict ) ) ;
532
}
533
}
534
535
void Debugger:: Cmd:: watchExprList ( std:: span< const TclObject> , TclObject& result )
536
{
537
for ( const auto & we : debugger ( ) . getWatchExprs ( ) ) {
538
TclObject dict = makeTclDict (
539
TclObject ( " -description" ) , we. getDescription ( ) ,
540
TclObject ( " -expression" ) , we. getExpression ( ) ,
541
TclObject ( " -format" ) , we. getFormat ( ) ) ;
542
result. addDictKeyValue ( we. getIdStr ( ) , std:: move ( dict ) ) ;
543
}
544
}
545
546
void Debugger:: Cmd:: conditionList ( std:: span< const TclObject> , TclObject& result )
547
{
548
for ( const auto & cond : MSXCPUInterface:: getConditions ( ) ) {
549
TclObject dict = makeTclDict (
550
TclObject ( " -condition" ) , cond. getCondition ( ) ,
551
TclObject ( " -command" ) , cond. getCommand ( ) ,
552
TclObject ( " -enabled" ) , cond. isEnabled ( ) ,
553
TclObject ( " -once" ) , cond. onlyOnce ( ) ) ;
554
result. addDictKeyValue ( cond. getIdStr ( ) , std:: move ( dict ) ) ;
555
}
556
}
557
558
void Debugger:: Cmd:: parseCreateBreakPoint ( BreakPoint& bp , std:: span< const TclObject> tokens )
559
{
560
std:: array info = {
561
funcArg ( " -address" , [ & ] ( Interpreter& interp, const TclObject& arg) {
562
bp. setAddress ( interp, arg ) ;
563
} ) ,
564
funcArg ( " -condition" , [ & ] ( Interpreter& , const TclObject& arg) {
565
bp. setCondition ( arg ) ;
566
} ) ,
567
funcArg ( " -command" , [ & ] ( Interpreter& , const TclObject& arg) {
568
bp. setCommand ( arg ) ;
569
} ) ,
570
funcArg ( " -enabled" , [ & ] ( Interpreter& interp, const TclObject& arg) {
571
bp. setEnabled ( interp, arg ) ;
572
} ) ,
573
funcArg ( " -once" , [ & ] ( Interpreter& interp, const TclObject& arg) {
574
bp. setOnce ( interp, arg ) ;
575
} ) ,
576
} ;
577
auto arguments = parseTclArgs ( getInterpreter ( ) , tokens, info) ;
578
if ( ! arguments. empty ( ) ) throw SyntaxError ( ) ;
579
}
580
581
void Debugger:: Cmd:: parseCreateWatchPoint ( WatchPoint& wp , std:: span< const TclObject> tokens )
582
{
583
std:: array info = {
584
funcArg ( " -type" , [ & ] ( Interpreter& interp, const TclObject& arg) {
585
wp. setType ( interp, arg ) ;
586
} ) ,
587
funcArg ( " -address" , [ & ] ( Interpreter& interp, const TclObject& arg) {
588
wp. setAddress ( interp, arg ) ;
589
} ) ,
590
funcArg ( " -condition" , [ & ] ( Interpreter& , const TclObject& arg) {
591
wp. setCondition ( arg ) ;
592
} ) ,
593
funcArg ( " -command" , [ & ] ( Interpreter& , const TclObject& arg) {
594
wp. setCommand ( arg ) ;
595
} ) ,
596
funcArg ( " -enabled" , [ & ] ( Interpreter& interp, const TclObject& arg) {
597
wp. setEnabled ( interp, arg ) ;
598
} ) ,
599
funcArg ( " -once" , [ & ] ( Interpreter& interp, const TclObject& arg) {
600
wp. setOnce ( interp, arg ) ;
601
} ) ,
602
} ;
603
auto arguments = parseTclArgs ( getInterpreter ( ) , tokens, info) ;
604
if ( ! arguments. empty ( ) ) throw SyntaxError ( ) ;
605
}
606
607
void Debugger:: Cmd:: parseCreateWatchExpr ( ImGuiWatchExpr:: WatchExpr& we , std:: span< const TclObject> tokens )
608
{
609
std:: array info = {
610
funcArg ( " -description" , [ & ] ( Interpreter& , const TclObject& arg) {
611
we. setDescription ( arg ) ;
612
} ) ,
613
funcArg ( " -expression" , [ & ] ( Interpreter& , const TclObject& arg) {
614
we. setExpression ( arg ) ;
615
} ) ,
616
funcArg ( " -format" , [ & ] ( Interpreter& , const TclObject& arg) {
617
we. setFormat ( arg ) ;
618
} ) ,
619
} ;
620
auto arguments = parseTclArgs ( getInterpreter ( ) , tokens, info) ;
621
if ( ! arguments. empty ( ) ) throw SyntaxError ( ) ;
622
}
623
624
void Debugger:: Cmd:: parseCreateCondition ( DebugCondition& cond , std:: span< const TclObject> tokens )
625
{
626
std:: array info = {
627
funcArg ( " -condition" , [ & ] ( Interpreter& , const TclObject& arg) {
628
cond. setCondition ( arg ) ;
629
} ) ,
630
funcArg ( " -command" , [ & ] ( Interpreter& , const TclObject& arg) {
631
cond. setCommand ( arg ) ;
632
} ) ,
633
funcArg ( " -enabled" , [ & ] ( Interpreter& interp, const TclObject& arg) {
634
cond. setEnabled ( interp, arg ) ;
635
} ) ,
636
funcArg ( " -once" , [ & ] ( Interpreter& interp, const TclObject& arg) {
637
cond. setOnce ( interp, arg ) ;
638
} ) ,
639
} ;
640
auto arguments = parseTclArgs ( getInterpreter ( ) , tokens, info) ;
641
if ( ! arguments. empty ( ) ) throw SyntaxError ( ) ;
642
}
643
644
void Debugger:: Cmd:: breakPointCreate ( std:: span< const TclObject> tokens , TclObject& result )
645
{
646
BreakPoint bp;
647
parseCreateBreakPoint ( bp, tokens. subspan ( 3 ) ) ;
648
result = bp. getIdStr ( ) ;
649
debugger ( ) . motherBoard . getCPUInterface ( ) . insertBreakPoint ( std:: move ( bp ) ) ;
650
}
651
652
void Debugger:: Cmd:: watchPointCreate ( std:: span< const TclObject> tokens , TclObject& result )
653
{
654
auto wp = std:: make_shared < WatchPoint> ( ) ;
655
parseCreateWatchPoint ( * wp, tokens. subspan ( 3 ) ) ;
656
result = wp-> getIdStr ( ) ;
657
debugger ( ) . motherBoard . getCPUInterface ( ) . setWatchPoint ( std:: move ( wp ) ) ;
658
}
659
660
void Debugger:: Cmd:: watchExprCreate ( std:: span< const TclObject> tokens , TclObject& result )
661
{
662
ImGuiWatchExpr:: WatchExpr we;
663
parseCreateWatchExpr ( we, tokens. subspan ( 3 ) ) ;
664
result = we. getIdStr ( ) ;
665
debugger ( ) . getWatchExprs ( ) . push_back ( std:: move ( we ) ) ;
666
}
667
668
void Debugger:: Cmd:: conditionCreate ( std:: span< const TclObject> tokens , TclObject& result )
669
{
670
DebugCondition cond;
671
parseCreateCondition ( cond, tokens. subspan ( 3 ) ) ;
672
result = cond. getIdStr ( ) ;
673
debugger ( ) . motherBoard . getCPUInterface ( ) . setCondition ( std:: move ( cond ) ) ;
674
}
675
676
void Debugger:: Cmd:: breakPointConfigure ( std:: span< const TclObject> tokens , TclObject& )
677
{
678
checkNumArgs ( tokens, AtLeast{ 4 } , " id ?arg ...?" ) ;
679
auto id = tokens[ 3 ] . getString ( ) ;
680
auto * bp = lookupBreakPoint ( id ) ;
681
if ( ! bp) {
682
throw CommandException ( " No such breakpoint: " , id) ;
683
}
684
685
parseCreateBreakPoint ( * bp, tokens. subspan ( 4 ) ) ;
686
}
687
688
void Debugger:: Cmd:: watchPointConfigure ( std:: span< const TclObject> tokens , TclObject& )
689
{
690
checkNumArgs ( tokens, AtLeast{ 4 } , " id ?arg ...?" ) ;
691
auto id = tokens[ 3 ] . getString ( ) ;
692
auto wp = lookupWatchPoint ( id ) ;
693
if ( ! wp) {
694
throw CommandException ( " No such watchpoint: " , id) ;
695
}
696
auto ch = debugger ( ) . motherBoard . getCPUInterface ( ) . getScopedChangeWatchpoint ( wp ) ;
697
parseCreateWatchPoint ( * wp, tokens. subspan ( 4 ) ) ;
698
}
699
700
void Debugger:: Cmd:: watchExprConfigure ( std:: span< const TclObject> tokens , TclObject& )
701
{
702
checkNumArgs ( tokens, AtLeast{ 4 } , " id ?arg ...?" ) ;
703
auto id = tokens[ 3 ] . getString ( ) ;
704
auto * we = lookupWatchExpr ( id ) ;
705
if ( ! we) {
706
throw CommandException ( " No such watchexpr: " , id) ;
707
}
708
parseCreateWatchExpr ( * we, tokens. subspan ( 4 ) ) ;
709
}
710
711
void Debugger:: Cmd:: conditionConfigure ( std:: span< const TclObject> tokens , TclObject& )
712
{
713
checkNumArgs ( tokens, AtLeast{ 4 } , " id ?arg ...?" ) ;
714
auto id = tokens[ 3 ] . getString ( ) ;
715
auto * cond = lookupCondition ( id ) ;
716
if ( ! cond) {
717
throw CommandException ( " No such condition: " , id) ;
718
}
719
720
parseCreateCondition ( * cond, tokens. subspan ( 4 ) ) ;
721
}
722
723
void Debugger:: Cmd:: breakPointRemove ( std:: span< const TclObject> tokens , TclObject& )
724
{
725
checkNumArgs ( tokens, 4 , " id" ) ;
726
auto id = tokens[ 3 ] . getString ( ) ;
727
auto * bp = lookupBreakPoint ( id ) ;
728
if ( ! bp) {
729
throw CommandException ( " No such breakpoint: " , id) ;
730
}
731
debugger ( ) . motherBoard . getCPUInterface ( ) . removeBreakPoint ( * bp) ;
732
}
733
734
void Debugger:: Cmd:: watchPointRemove ( std:: span< const TclObject> tokens , TclObject& )
735
{
736
checkNumArgs ( tokens, 4 , " id" ) ;
737
auto id = tokens[ 3 ] . getString ( ) ;
738
auto wp = lookupWatchPoint ( id ) ;
739
if ( ! wp) {
740
throw CommandException ( " No such watchpoint: " , id) ;
741
}
742
debugger ( ) . motherBoard . getCPUInterface ( ) . removeWatchPoint ( wp ) ;
743
}
744
745
void Debugger:: Cmd:: watchExprRemove ( std:: span< const TclObject> tokens , TclObject& )
746
{
747
checkNumArgs ( tokens, 4 , " id" ) ;
748
auto id = tokens[ 3 ] . getString ( ) ;
749
750
auto & watchExprs = debugger ( ) . getWatchExprs ( ) ;
751
auto it = findWatchExpr ( watchExprs, id ) ;
752
if ( it == std:: end ( watchExprs ) ) {
753
throw CommandException ( " No such watchexpr: " , id) ;
754
}
755
756
watchExprs. erase ( it ) ;
757
}
758
759
void Debugger:: Cmd:: conditionRemove ( std:: span< const TclObject> tokens , TclObject& )
760
{
761
checkNumArgs ( tokens, 4 , " id" ) ;
762
auto id = tokens[ 3 ] . getString ( ) ;
763
auto * cond = lookupCondition ( id ) ;
764
if ( ! cond) {
765
throw CommandException ( " No such condition: " , id) ;
766
}
767
debugger ( ) . motherBoard . getCPUInterface ( ) . removeCondition ( * cond) ;
768
}
769
770
void Debugger:: Cmd:: setBreakPoint ( std:: span< const TclObject> tokens , TclObject& result )
771
{
772
checkNumArgs ( tokens, AtLeast{ 3 } , " address ?-once? ?condition? ?command?" ) ;
773
774
bool once = false ;
775
std:: array info = { flagArg ( " -once" , once) } ;
776
777
auto & interp = getInterpreter ( ) ;
778
auto arguments = parseTclArgs ( interp, tokens. subspan ( 2 ) , info ) ;
779
if ( ( arguments. size ( ) < 1 ) || ( arguments. size ( ) > 3 ) ) {
780
throw SyntaxError ( ) ;
781
}
782
783
BreakPoint bp;
784
bp. setOnce ( once ) ;
785
786
switch ( arguments. size ( ) ) {
787
case 3 :
788
bp. setCommand ( arguments[ 2 ] ) ;
789
[ [ fallthrough] ] ;
790
case 2 :
791
bp. setCondition ( arguments[ 1 ] ) ;
792
[ [ fallthrough] ] ;
793
case 1 :
794
bp. setAddress ( interp, arguments[ 0 ] ) ;
795
break ;
796
}
797
798
result = bp. getIdStr ( ) ;
799
debugger ( ) . motherBoard . getCPUInterface ( ) . insertBreakPoint ( std:: move ( bp ) ) ;
800
}
801
802
void Debugger:: Cmd:: removeBreakPoint (
803
std:: span< const TclObject> tokens , TclObject& )
804
{
805
checkNumArgs ( tokens, 3 , " id|address" ) ;
806
auto & interface = debugger ( ) . motherBoard . getCPUInterface ( ) ;
807
auto & breakPoints = MSXCPUInterface:: getBreakPoints ( ) ;
808
809
std:: string_view tmp = tokens[ 2 ] . getString ( ) ;
810
if ( tmp. starts_with ( BreakPoint:: prefix ) ) {
811
812
if ( auto id = StringOp:: stringToBase < 10 , unsigned > ( tmp. substr ( BreakPoint:: prefix. size ( ) ) ) ) {
813
if ( auto it = std:: ranges:: find ( breakPoints, id, & BreakPoint:: getId ) ;
814
it != std:: end ( breakPoints ) ) {
815
interface. removeBreakPoint ( * it) ;
816
return ;
817
}
818
}
819
throw CommandException ( " No such breakpoint: " , tmp) ;
820
} else {
821
822
uint16_t addr = getAddress ( getInterpreter ( ) , tokens[ 2 ] ) ;
823
auto it = std:: ranges:: find_if ( breakPoints, [ & ] ( auto & bp) {
824
return ( bp. getAddress ( ) == addr) &&
825
bp. getCondition ( ) . getString ( ) . empty ( ) ;
826
} ) ;
827
if ( it == breakPoints. end ( ) ) {
828
throw CommandException (
829
" No (unconditional) breakpoint at address: " , tmp ) ;
830
}
831
interface. removeBreakPoint ( * it) ;
832
}
833
}
834
835
void Debugger:: Cmd:: listBreakPoints ( std:: span< const TclObject> , TclObject& result ) const
836
{
837
std:: string res;
838
for ( const auto & bp : MSXCPUInterface:: getBreakPoints ( ) ) {
839
TclObject line = makeTclList (
840
bp. getIdStr ( ) , bp. getAddressString ( ) ,
841
bp. getCondition ( ) , bp. getCommand ( ) ) ;
842
strAppend ( res, line. getString ( ) , ' \n ' ) ;
843
}
844
result = res;
845
}
846
847
848
void Debugger:: Cmd:: setWatchPoint ( std:: span< const TclObject> tokens , TclObject& result )
849
{
850
checkNumArgs ( tokens, AtLeast{ 4 } , Prefix{ 2 } , " type address ?-once? ?condition? ?command?" ) ;
851
852
bool once = false ;
853
std:: array info = { flagArg ( " -once" , once) } ;
854
855
auto & interp = getInterpreter ( ) ;
856
auto arguments = parseTclArgs ( interp, tokens. subspan ( 2 ) , info ) ;
857
if ( ( arguments. size ( ) < 2 ) || ( arguments. size ( ) > 4 ) ) {
858
throw SyntaxError ( ) ;
859
}
860
861
auto wp = std:: make_shared < WatchPoint> ( ) ;
862
wp-> setOnce ( once ) ;
863
864
switch ( arguments. size ( ) ) {
865
case 4 :
866
wp-> setCommand ( arguments[ 3 ] ) ;
867
[ [ fallthrough] ] ;
868
case 3 :
869
wp-> setCondition ( arguments[ 2 ] ) ;
870
[ [ fallthrough] ] ;
871
case 2 :
872
wp-> setType ( interp, arguments[ 0 ] ) ;
873
wp-> setAddress ( interp, arguments[ 1 ] ) ;
874
break ;
875
default :
876
UNREACHABLE;
877
}
878
879
result = wp-> getIdStr ( ) ;
880
debugger ( ) . motherBoard . getCPUInterface ( ) . setWatchPoint ( std:: move ( wp ) ) ;
881
}
882
883
void Debugger:: Cmd:: removeWatchPoint (
884
std:: span< const TclObject> tokens , TclObject& )
885
{
886
checkNumArgs ( tokens, 3 , " id" ) ;
887
std:: string_view tmp = tokens[ 2 ] . getString ( ) ;
888
if ( tmp. starts_with ( WatchPoint:: prefix ) ) {
889
890
if ( auto id = StringOp:: stringToBase < 10 , unsigned > ( tmp. substr ( WatchPoint:: prefix. size ( ) ) ) ) {
891
auto & interface = debugger ( ) . motherBoard . getCPUInterface ( ) ;
892
for ( auto & wp : interface. getWatchPoints ( ) ) {
893
if ( wp-> getId ( ) == * id) {
894
interface. removeWatchPoint ( wp ) ;
895
return ;
896
}
897
}
898
}
899
}
900
throw CommandException ( " No such watchpoint: " , tmp) ;
901
}
902
903
void Debugger:: Cmd:: listWatchPoints (
904
std:: span< const TclObject> , TclObject& result )
905
{
906
std:: string res;
907
const auto & interface = debugger ( ) . motherBoard . getCPUInterface ( ) ;
908
for ( const auto & wp : interface. getWatchPoints ( ) ) {
909
auto address = makeTclList ( wp-> getBeginAddressString ( ) ) ;
910
if ( auto end = wp-> getEndAddressString ( ) ; ! end. getString ( ) . empty ( ) ) {
911
address. addListElement ( end ) ;
912
}
913
TclObject line = makeTclList (
914
wp-> getIdStr ( ) ,
915
WatchPoint:: format ( wp-> getType ( ) ) ,
916
address,
917
wp-> getCondition ( ) ,
918
wp-> getCommand ( ) ) ;
919
strAppend ( res, line. getString ( ) , ' \n ' ) ;
920
}
921
result = res;
922
}
923
924
925
void Debugger:: Cmd:: setCondition ( std:: span< const TclObject> tokens , TclObject& result )
926
{
927
checkNumArgs ( tokens, AtLeast{ 3 } , " condition ?-once? ?command?" ) ;
928
929
bool once = false ;
930
std:: array info = { flagArg ( " -once" , once) } ;
931
932
auto arguments = parseTclArgs ( getInterpreter ( ) , tokens. subspan ( 2 ) , info) ;
933
if ( ( arguments. size ( ) < 1 ) || ( arguments. size ( ) > 2 ) ) {
934
throw SyntaxError ( ) ;
935
}
936
937
DebugCondition dc;
938
dc. setOnce ( once ) ;
939
940
switch ( arguments. size ( ) ) {
941
case 2 :
942
dc. setCommand ( arguments[ 1 ] ) ;
943
[ [ fallthrough] ] ;
944
case 1 :
945
dc. setCondition ( arguments[ 0 ] ) ;
946
break ;
947
}
948
949
result = dc. getIdStr ( ) ;
950
debugger ( ) . motherBoard . getCPUInterface ( ) . setCondition ( std:: move ( dc ) ) ;
951
}
952
953
void Debugger:: Cmd:: removeCondition (
954
std:: span< const TclObject> tokens , TclObject& )
955
{
956
checkNumArgs ( tokens, 3 , " id" ) ;
957
958
std:: string_view tmp = tokens[ 2 ] . getString ( ) ;
959
if ( tmp. starts_with ( DebugCondition:: prefix ) ) {
960
961
if ( auto id = StringOp:: stringToBase < 10 , unsigned > ( tmp. substr ( DebugCondition:: prefix. size ( ) ) ) ) {
962
for ( auto & c : MSXCPUInterface:: getConditions ( ) ) {
963
if ( c. getId ( ) == * id) {
964
auto & interface = debugger ( ) . motherBoard . getCPUInterface ( ) ;
965
interface. removeCondition ( c ) ;
966
return ;
967
}
968
}
969
}
970
}
971
throw CommandException ( " No such condition: " , tmp) ;
972
}
973
974
void Debugger:: Cmd:: listConditions ( std:: span< const TclObject> , TclObject& result ) const
975
{
976
std:: string res;
977
for ( const auto & c : MSXCPUInterface:: getConditions ( ) ) {
978
TclObject line = makeTclList ( c. getIdStr ( ) ,
979
c. getCondition ( ) ,
980
c. getCommand ( ) ) ;
981
strAppend ( res, line. getString ( ) , ' \n ' ) ;
982
}
983
result = res;
984
}
985
986
987
void Debugger:: Cmd:: probe ( std:: span< const TclObject> tokens , TclObject& result )
988
{
989
checkNumArgs ( tokens, AtLeast{ 3 } , " subcommand ?arg ...?" ) ;
990
executeSubCommand ( tokens[ 2 ] . getString ( ) ,
991
" list" , [ & ] { probeList ( tokens, result ) ; } ,
992
" desc" , [ & ] { probeDesc ( tokens, result ) ; } ,
993
" read" , [ & ] { probeRead ( tokens, result ) ; } ,
994
" set_bp" , [ & ] { probeSetBreakPoint ( tokens, result ) ; } ,
995
" remove_bp" , [ & ] { probeRemoveBreakPoint ( tokens, result ) ; } ,
996
" list_bp" , [ & ] { probeListBreakPoints ( tokens, result ) ; } ) ;
997
}
998
void Debugger:: Cmd:: probeList ( std:: span< const TclObject> , TclObject& result )
999
{
1000
result. addListElements ( std:: views:: transform ( debugger ( ) . probes , & ProbeBase:: getName) ) ;
1001
}
1002
void Debugger:: Cmd:: probeDesc ( std:: span< const TclObject> tokens , TclObject& result )
1003
{
1004
checkNumArgs ( tokens, 4 , " probe" ) ;
1005
result = debugger ( ) . getProbe ( tokens[ 3 ] . getString ( ) ) . getDescription ( ) ;
1006
}
1007
void Debugger:: Cmd:: probeRead ( std:: span< const TclObject> tokens , TclObject& result )
1008
{
1009
checkNumArgs ( tokens, 4 , " probe" ) ;
1010
result = debugger ( ) . getProbe ( tokens[ 3 ] . getString ( ) ) . getValue ( ) ;
1011
}
1012
void Debugger:: Cmd:: probeSetBreakPoint (
1013
std:: span< const TclObject> tokens , TclObject& result )
1014
{
1015
checkNumArgs ( tokens, AtLeast{ 4 } , " probe ?-once? ?condition? ?command?" ) ;
1016
TclObject command ( " debug break" ) ;
1017
TclObject condition;
1018
ProbeBase* p;
1019
bool once = false ;
1020
1021
std:: array info = { flagArg ( " -once" , once) } ;
1022
auto arguments = parseTclArgs ( getInterpreter ( ) , tokens. subspan ( 3 ) , info) ;
1023
if ( ( arguments. size ( ) < 1 ) || ( arguments. size ( ) > 3 ) ) {
1024
throw SyntaxError ( ) ;
1025
}
1026
1027
switch ( arguments. size ( ) ) {
1028
case 3 :
1029
command = arguments[ 2 ] ;
1030
[ [ fallthrough] ] ;
1031
case 2 :
1032
condition = arguments[ 1 ] ;
1033
[ [ fallthrough] ] ;
1034
case 1 : {
1035
p = & debugger ( ) . getProbe ( arguments[ 0 ] . getString ( ) ) ;
1036
break ;
1037
}
1038
default :
1039
UNREACHABLE;
1040
}
1041
1042
result = debugger ( ) . insertProbeBreakPoint ( command, condition, * p, once ) ;
1043
}
1044
void Debugger:: Cmd:: probeRemoveBreakPoint (
1045
std:: span< const TclObject> tokens , TclObject& )
1046
{
1047
checkNumArgs ( tokens, 4 , " id" ) ;
1048
debugger ( ) . removeProbeBreakPoint ( tokens[ 3 ] . getString ( ) ) ;
1049
}
1050
void Debugger:: Cmd:: probeListBreakPoints (
1051
std:: span< const TclObject> , TclObject& result )
1052
{
1053
std:: string res;
1054
for ( const auto & p : debugger ( ) . probeBreakPoints ) {
1055
TclObject line = makeTclList ( p-> getIdStr ( ) ,
1056
p-> getProbe ( ) . getName ( ) ,
1057
p-> getCondition ( ) ,
1058
p-> getCommand ( ) ) ;
1059
strAppend ( res, line. getString ( ) , ' \n ' ) ;
1060
}
1061
result = res;
1062
}
1063
1064
SymbolManager& Debugger:: Cmd:: getSymbolManager ( )
1065
{
1066
return debugger ( ) . getMotherBoard ( ) . getReactor ( ) . getSymbolManager ( ) ;
1067
}
1068
void Debugger:: Cmd:: symbols ( std:: span< const TclObject> tokens , TclObject& result )
1069
{
1070
checkNumArgs ( tokens, AtLeast{ 3 } , " subcommand ?arg ...?" ) ;
1071
executeSubCommand ( tokens[ 2 ] . getString ( ) ,
1072
" types" , [ & ] { symbolsTypes ( tokens, result ) ; } ,
1073
" load" , [ & ] { symbolsLoad ( tokens, result ) ; } ,
1074
" remove" , [ & ] { symbolsRemove ( tokens, result ) ; } ,
1075
" files" , [ & ] { symbolsFiles ( tokens, result ) ; } ,
1076
" lookup" , [ & ] { symbolsLookup ( tokens, result ) ; } ) ;
1077
}
1078
void Debugger:: Cmd:: symbolsTypes ( std:: span< const TclObject> tokens , TclObject& result ) const
1079
{
1080
checkNumArgs ( tokens, 3 , " " ) ;
1081
for ( auto type = SymbolFile:: Type:: FIRST;
1082
type < SymbolFile:: Type:: LAST;
1083
type = static_cast < SymbolFile:: Type> ( static_cast < int > ( type) + 1 ) ) {
1084
result. addListElement ( SymbolFile:: toString ( type ) ) ;
1085
}
1086
}
1087
void Debugger:: Cmd:: symbolsLoad ( std:: span< const TclObject> tokens , TclObject& )
1088
{
1089
checkNumArgs ( tokens, Between{ 4 , 5 } , " filename ?type?" ) ;
1090
auto filename = tokens[ 3 ] . getString ( ) ;
1091
auto type = [ & ] {
1092
if ( tokens. size ( ) < 5 ) return SymbolFile:: Type:: AUTO_DETECT;
1093
auto str = tokens[ 4 ] . getString ( ) ;
1094
auto t = SymbolFile:: parseType ( str ) ;
1095
if ( ! t) throw CommandException ( " Invalid symbol file type: " , str) ;
1096
return * t;
1097
} ( ) ;
1098
try {
1099
getSymbolManager ( ) . reloadFile ( filename, SymbolManager:: LoadEmpty:: ALLOWED, type, { } , { } ) ;
1100
} catch ( MSXException& e) {
1101
throw CommandException ( " Couldn't load symbol file '" , filename, " ': " , e. getMessage ( ) ) ;
1102
}
1103
}
1104
void Debugger:: Cmd:: symbolsRemove ( std:: span< const TclObject> tokens , TclObject& )
1105
{
1106
checkNumArgs ( tokens, 4 , " filename" ) ;
1107
auto filename = tokens[ 3 ] . getString ( ) ;
1108
getSymbolManager ( ) . removeFile ( filename ) ;
1109
}
1110
void Debugger:: Cmd:: symbolsFiles ( std:: span< const TclObject> tokens , TclObject& result )
1111
{
1112
checkNumArgs ( tokens, 3 , " " ) ;
1113
for ( const auto & file : getSymbolManager ( ) . getFiles ( ) ) {
1114
result. addListElement ( TclObject ( TclObject:: MakeDictTag{ } ,
1115
" filename" , file. filename ,
1116
" type" , SymbolFile:: toString ( file. type ) ) ) ;
1117
}
1118
}
1119
void Debugger:: Cmd:: symbolsLookup ( std:: span< const TclObject> tokens , TclObject& result )
1120
{
1121
std:: string_view filename;
1122
std:: string_view name;
1123
std:: optional< int > value;
1124
std:: array info = { valueArg ( " -filename" , filename) ,
1125
valueArg ( " -name" , name) ,
1126
valueArg ( " -value" , value) } ;
1127
auto args = parseTclArgs ( getInterpreter ( ) , tokens. subspan ( 3 ) , info) ;
1128
if ( ! args. empty ( ) ) throw SyntaxError ( ) ;
1129
if ( ! filename. empty ( ) && ! name. empty ( ) && value) throw SyntaxError ( ) ;
1130
1131
for ( const auto & file : getSymbolManager ( ) . getFiles ( ) ) {
1132
if ( ! filename. empty ( ) && ( file. filename != filename) ) continue ;
1133
for ( const auto & sym : file. symbols ) {
1134
if ( ! name. empty ( ) && ( name != sym. name ) ) continue ;
1135
if ( value && ( sym. value != * value) ) continue ;
1136
1137
TclObject elem;
1138
if ( filename. empty ( ) ) elem. addDictKeyValue ( " filename" , file. filename ) ;
1139
if ( name. empty ( ) ) elem. addDictKeyValue ( " name" , sym. name ) ;
1140
if ( ! value) elem. addDictKeyValue ( " value" , sym. value ) ;
1141
result. addListElement ( std:: move ( elem ) ) ;
1142
}
1143
}
1144
}
1145
1146
1147
template < size_t N>
1148
struct FixedStr {
1149
char buf[ N] { } ;
1150
constexpr FixedStr ( ) = default ;
1151
constexpr FixedStr ( const char ( & str) [ N] ) { std:: copy_n ( str, N, buf ) ; }
1152
constexpr operator std:: string_view ( ) const { return { buf, N - 1 } ; }
1153
1154
template < size_t M>
1155
constexpr auto operator+ ( FixedStr< M> rhs ) const {
1156
FixedStr< N + M - 1 > out{ } ;
1157
std:: copy_n ( buf, N - 1 , out. buf ) ;
1158
std:: copy_n ( rhs. buf , M, out. buf + N - 1 ) ;
1159
return out;
1160
}
1161
} ;
1162
1163
1164
template < size_t N, size_t M>
1165
constexpr auto operator+ ( const char ( & lhs) [ N] , FixedStr< M> rhs ) { return FixedStr < N> ( lhs ) + rhs; }
1166
1167
1168
template < size_t N, size_t M>
1169
constexpr auto operator+ ( FixedStr< N> lhs , const char ( & rhs) [ M] ) { return lhs + FixedStr < M> ( rhs ) ; }
1170
1171
std:: string Debugger:: Cmd:: help ( std:: span< const TclObject> tokens ) const
1172
{
1173
constexpr auto generalHelp =
1174
" debug <subcommand> [<arguments>]\n "
1175
" Possible subcommands are:\n "
1176
" list returns a list of all debuggables\n "
1177
" desc returns a description of this debuggable\n "
1178
" size returns the size of this debuggable\n "
1179
" read read a byte from a debuggable\n "
1180
" write write a byte to a debuggable\n "
1181
" read_block read a whole block at once\n "
1182
" write_block write a whole block at once\n "
1183
" breakpoint breakpoint related subcommands\n "
1184
" watchpoint watchpoint related subcommands\n "
1185
" watchexpr watch expression related subcommands\n "
1186
" condition debug condition related subcommands\n "
1187
" probe probe related subcommands\n "
1188
" cont continue execution after break\n "
1189
" step execute one instruction\n "
1190
" break break CPU at current position\n "
1191
" breaked query CPU breaked status\n "
1192
" disasm disassemble instructions\n "
1193
" disasm_blob disassemble a instruction in Tcl binary string\n "
1194
" symbols manage debug symbols\n "
1195
" trace trace related subcommands\n "
1196
" The arguments are specific for each subcommand.\n "
1197
" Type 'help debug <subcommand>' for help about a specific subcommand.\n " ;
1198
1199
constexpr auto listHelp =
1200
" debug list\n "
1201
" Returns a list with the names of all 'debuggables'.\n "
1202
" These names are used in other debug subcommands.\n " ;
1203
constexpr auto descHelp =
1204
" debug desc <name>\n "
1205
" Returns a description for the debuggable with given name.\n " ;
1206
constexpr auto sizeHelp =
1207
" debug size <name>\n "
1208
" Returns the size (in bytes) of the debuggable with given name.\n " ;
1209
constexpr auto readHelp =
1210
" debug read <name> <addr>\n "
1211
" Read a byte at offset <addr> from the given debuggable.\n "
1212
" The offset must be smaller than the value returned from the "
1213
" 'size' subcommand\n "
1214
" Note that openMSX comes with a bunch of Tcl scripts that make "
1215
" some of the debug reads much more convenient (e.g. reading from "
1216
" Z80 or VDP registers). See the Console Command Reference for more "
1217
" details about these.\n " ;
1218
constexpr auto writeHelp =
1219
" debug write <name> <addr> <val>\n "
1220
" Write a byte to the given debuggable at a certain offset.\n "
1221
" The offset must be smaller than the value returned from the "
1222
" 'size' subcommand\n " ;
1223
constexpr auto readBlockHelp =
1224
" debug read_block <name> <addr> <size>\n "
1225
" Read a whole block at once. This is equivalent with repeated "
1226
" invocations of the 'read' subcommand, but using this subcommand "
1227
" may be faster. The result is a Tcl binary string (see Tcl manual).\n "
1228
" The block is specified as size/offset in the debuggable. The "
1229
" complete block must fit in the debuggable (see the 'size' "
1230
" subcommand).\n " ;
1231
constexpr auto writeBlockHelp =
1232
" debug write_block <name> <addr> <values>\n "
1233
" Write a whole block at once. This is equivalent with repeated "
1234
" invocations of the 'write' subcommand, but using this subcommand "
1235
" may be faster. The <values> argument must be a Tcl binary string "
1236
" (see Tcl manual).\n "
1237
" The block has a size and an offset in the debuggable. The "
1238
" complete block must fit in the debuggable (see the 'size' "
1239
" subcommand).\n " ;
1240
constexpr auto breakPointHelp =
1241
" debug breakpoint <subcommand> [<arguments>]\n "
1242
" Possible subcommands are:\n "
1243
" list list all active breakpoints\n "
1244
" create create a new breakpoint\n "
1245
" configure configure an existing breakpoint\n "
1246
" remove remove an existing breakpoint\n "
1247
" Type 'help debug breakpoint <subcommand>' for help about a specific subcommand.\n " ;
1248
constexpr auto watchPointHelp =
1249
" debug watchpoint <subcommand> [<arguments>]\n "
1250
" Possible subcommands are:\n "
1251
" list list all active watchpoints\n "
1252
" create create a new watchpoint\n "
1253
" configure configure an existing watchpoint\n "
1254
" remove remove an existing watchpoint\n "
1255
" Type 'help debug watchpoint <subcommand>' for help about a specific subcommand.\n " ;
1256
constexpr auto watchExprHelp =
1257
" debug watchexpr <subcommand> [<arguments>]\n "
1258
" Possible subcommands are:\n "
1259
" list list all active watch expressions\n "
1260
" create create a new watch expression\n "
1261
" configure configure an existing watch expression\n "
1262
" remove remove an existing watch expression\n "
1263
" Type 'help debug watchexpr <subcommand>' for help about a specific subcommand.\n " ;
1264
constexpr auto conditionHelp =
1265
" debug condition <subcommand> [<arguments>]\n "
1266
" Possible subcommands are:\n "
1267
" list list all active debug conditions\n "
1268
" create create a new debug conditions\n "
1269
" configure configure an existing debug condition\n "
1270
" remove remove an existing debug condition\n "
1271
" Type 'help debug condition <subcommand>' for help about a specific subcommand.\n " ;
1272
constexpr auto breakPointListHelp =
1273
" debug breakpoint list\n "
1274
" Lists all breakpoints. The result is a Tcl dict (<key>/<value>-pairs), where\n "
1275
" * <key> is the breakpoint ID\n "
1276
" * <value> is another Tcl dict containing the properties of the breakpoint.\n "
1277
" See 'help debug breakpoint create' for a description of these properties.\n " ;
1278
constexpr auto watchPointListHelp =
1279
" debug watchpoint list\n "
1280
" Lists all watchpoints. The result is a Tcl dict (<key>/<value>-pairs), where\n "
1281
" * <key> is the watchpoint ID\n "
1282
" * <value> is another Tcl dict containing the properties of the watchpoint.\n "
1283
" See 'help debug watchpoint create' for a description of these properties.\n " ;
1284
constexpr auto watchExprListHelp =
1285
" debug watchexpr list\n "
1286
" Lists all watch expressions. The result is a Tcl dict (<key>/<value>-pairs), where\n "
1287
" * <key> is the watchexpr ID\n "
1288
" * <value> is another Tcl dict containing the properties of the watch expression.\n "
1289
" See 'help debug watchexpr create' for a description of these properties.\n " ;
1290
constexpr auto conditionListHelp =
1291
" debug condition list\n "
1292
" Lists all debug conditions. The result is a Tcl dict (<key>/<value>-pairs), where\n "
1293
" * <key> is the debug condition ID\n "
1294
" * <value> is another Tcl dict containing the properties of the debug condition.\n "
1295
" See 'help debug condition create' for a description of these properties.\n " ;
1296
constexpr FixedStr addressNote = " An address is just a number [0:0xFFFF] but can also be dynamically evaluated from a known symbol by specifying {$sym(MySymbol)}\n " ;
1297
constexpr auto breakPointCreateHelp =
1298
" debug breakpoint create [<property-name> <property-value>]...\n "
1299
" Create a new breakpoint with given properties. The following properties are supported:\n "
1300
" -address the address where the breakpoint should trigger\n "
1301
" " + addressNote +
1302
" -condition a Tcl expression that must evaluate to true for the breakpoint to trigger (default = no condition)\n "
1303
" -command a Tcl command that should be executed when the breakpoint triggers (default = 'debug break')\n "
1304
" -enabled set to false to (temporarily) disable this breakpoint\n "
1305
" -once if 'true' the breakpoint is automatically removed after it triggered (default = 'false', meaning recurring)\n " ;
1306
constexpr auto watchPointCreateHelp =
1307
" debug watchpoint create [<property-name> <property-value>]...\n "
1308
" Create a new watchpoint with given properties. The following properties are supported:\n "
1309
" -type one of 'read_io', 'write_io', 'read_mem', 'write_mem'\n "
1310
" -address the address(es) where the watchpoint should trigger, can be a single address or a begin/end-pair\n "
1311
" " + addressNote +
1312
" -condition a Tcl expression that must evaluate to true for the watchpoint to trigger (default = no condition)\n "
1313
" -command a Tcl command that should be executed when the watchpoint triggers (default = 'debug break')\n "
1314
" -enabled set to false to (temporarily) disable this watchpoint\n "
1315
" -once if 'true' the watchpoint is automatically removed after it triggered (default = 'false', meaning recurring)\n " ;
1316
constexpr auto watchExprCreateHelp =
1317
" debug watchexpr create [<property-name> <property-value>]...\n "
1318
" Create a new watch expression with given properties. The following properties are supported:\n "
1319
" -description (optional) description for the expression\n "
1320
" -expression the actual Tcl expression\n "
1321
" -format (optional) format specifier\n "
1322
" Note: watch expressions are mostly a GUI feature, hover the help-marker (?) "
1323
" in the GUI (menu bar > Debugger > Watch expression) to get a more detailed explanation.\n " ;
1324
constexpr auto conditionCreateHelp =
1325
" debug condition create [<property-name> <property-value>]...\n "
1326
" Create a new debug condition with given properties. The following properties are supported:\n "
1327
" -condition a Tcl expression that must evaluate to true for the debug condition to trigger (default = no condition)\n "
1328
" -command a Tcl command that should be executed when the debug condition triggers (default = 'debug break')\n "
1329
" -enabled set to false to (temporarily) disable this condition\n "
1330
" -once if 'true' the debug condition is automatically removed after it triggered (default = 'false', meaning recurring)\n " ;
1331
constexpr auto breakPointConfigureHelp =
1332
" debug breakpoint configure <id> [<property-name> <property-value>]...\n "
1333
" Change one or more properties of an existing breakpoint.\n "
1334
" See 'help debug breakpoint create' for a description of the properties.\n " ;
1335
constexpr auto watchPointConfigureHelp =
1336
" debug watchpoint configure <id> [<property-name> <property-value>]...\n "
1337
" Change one or more properties of an existing watchpoint.\n "
1338
" See 'help debug watchpoint create' for a description of the properties.\n " ;
1339
constexpr auto watchExprConfigureHelp =
1340
" debug watchexpr configure <id> [<property-name> <property-value>]...\n "
1341
" Change one or more properties of an existing watch expression.\n "
1342
" See 'help debug watchexpr create' for a description of the properties.\n " ;
1343
constexpr auto conditionConfigureHelp =
1344
" debug condition configure <id> [<property-name> <property-value>]...\n "
1345
" Change one or more properties of an existing debug condition.\n "
1346
" See 'help debug condition create' for a description of the properties.\n " ;
1347
constexpr auto breakPointRemoveHelp =
1348
" debug breakpoint remove <id>\n "
1349
" Remove the breakpoint with given ID.\n " ;
1350
constexpr auto watchPointRemoveHelp =
1351
" debug watchpoint remove <id>\n "
1352
" Remove the watchpoint with given ID.\n " ;
1353
constexpr auto watchExprRemoveHelp =
1354
" debug watchexpr remove <id>\n "
1355
" Remove the watch expression with given ID.\n " ;
1356
constexpr auto conditionRemoveHelp =
1357
" debug condition remove <id>\n "
1358
" Remove the debug condition with given ID.\n " ;
1359
constexpr auto setBpHelp =
1360
" [deprecated] replaced by: 'debug breakpoint create <args>...'\n "
1361
" \n "
1362
" debug set_bp [-once] <addr> [<cond>] [<cmd>]\n "
1363
" Insert a new breakpoint at given address. When the CPU is about "
1364
" to execute the instruction at this address, execution will be "
1365
" breaked. At least this is the default behaviour, see next "
1366
" paragraphs.\n "
1367
" When the -once flag is given, the breakpoint is automatically "
1368
" removed after it triggered. In other words: it only triggers once.\n "
1369
" Optionally you can specify a condition. When the CPU reaches "
1370
" the breakpoint this condition is evaluated, only when the condition "
1371
" evaluated to true execution will be breaked.\n "
1372
" A condition must be specified as a Tcl expression. For example\n "
1373
" debug set_bp 0xf37d {[reg C] == 0x2F}\n "
1374
" This breaks on address 0xf37d but only when Z80 register C has the "
1375
" value 0x2F.\n "
1376
" Also optionally you can specify a command that should be "
1377
" executed when the breakpoint is reached (and condition is true). "
1378
" By default this command is 'debug break'.\n "
1379
" The result of this command is a breakpoint ID. This ID can "
1380
" later be used to remove this breakpoint again.\n " ;
1381
constexpr auto removeBpHelp =
1382
" [deprecated] replaced by: 'debug breakpoint remove <id>'\n "
1383
" \n "
1384
" debug remove_bp <id>\n "
1385
" Remove the breakpoint with given ID again. You can use the "
1386
" 'list_bp' subcommand to see all valid IDs.\n " ;
1387
constexpr auto listBpHelp =
1388
" [deprecated] replaced by: 'debug breakpoint list'\n "
1389
" \n "
1390
" debug list_bp\n "
1391
" Lists all active breakpoints. The result is printed in 4 "
1392
" columns. The first column contains the breakpoint ID. The "
1393
" second one has the address. The third has the condition "
1394
" (default condition is empty). And the last column contains "
1395
" the command that will be executed (default is 'debug break').\n " ;
1396
constexpr auto setWatchPointHelp =
1397
" [deprecated] replaced by: 'debug watchpoint create <args>...'\n "
1398
" \n "
1399
" debug set_watchpoint [-once] <type> <region> [<cond>] [<cmd>]\n "
1400
" Insert a new watchpoint of given type on the given region, "
1401
" there can be an optional -once flag, a condition and alternative "
1402
" command. See the 'set_bp' subcommand for details about these.\n "
1403
" Type must be one of the following:\n "
1404
" read_io break when CPU reads from given IO port(s)\n "
1405
" write_io break when CPU writes to given IO port(s)\n "
1406
" read_mem break when CPU reads from given memory location(s)\n "
1407
" write_mem break when CPU writes to given memory location(s)\n "
1408
" Region is either a single value, this corresponds to a single "
1409
" memory location or IO port. Otherwise region must be a list of "
1410
" two values (enclosed in braces) that specify a begin and end "
1411
" point of a whole memory region or a range of IO ports.\n "
1412
" During the execution of <cmd>, the following global Tcl "
1413
" variables are set:\n "
1414
" ::wp_last_address this is the actual address of the mem/io "
1415
" read/write that triggered the watchpoint\n "
1416
" ::wp_last_value this is the actual value that was written "
1417
" by the mem/io write that triggered the watchpoint\n "
1418
" Examples:\n "
1419
" debug set_watchpoint write_io 0x99 {[reg A] == 0x81}\n "
1420
" debug set_watchpoint read_mem {0xfbe5 0xfbef}\n " ;
1421
constexpr auto removeWatchPointHelp =
1422
" [deprecated] replaced by: 'debug watchpoint remove <id>'\n "
1423
" \n "
1424
" debug remove_watchpoint <id>\n "
1425
" Remove the watchpoint with given ID again. You can use the "
1426
" 'list_watchpoints' subcommand to see all valid IDs.\n " ;
1427
constexpr auto listWatchPointsHelp =
1428
" [deprecated] replaced by: 'debug watchpoint list'\n "
1429
" \n "
1430
" debug list_watchpoints\n "
1431
" Lists all active watchpoints. The result is similar to the "
1432
" 'list_bp' subcommand, but there is an extra column (2nd column) "
1433
" that contains the type of the watchpoint.\n " ;
1434
constexpr auto setCondHelp =
1435
" [deprecated] replaced by: 'debug condition create <args>...'\n "
1436
" \n "
1437
" debug set_condition [-once] <cond> [<cmd>]\n "
1438
" Insert a new condition. These are much like breakpoints, "
1439
" except that they are checked before every instruction "
1440
" (breakpoints are tied to a specific address).\n "
1441
" Conditions will slow down simulation MUCH more than "
1442
" breakpoints. So only use them when you don't care about "
1443
" simulation speed (when you're debugging this is usually not "
1444
" a problem).\n "
1445
" See 'help debug set_bp' for more details.\n " ;
1446
constexpr auto removeCondHelp =
1447
" [deprecated] replaced by: 'debug condition remove <id>'\n "
1448
" \n "
1449
" debug remove_condition <id>\n "
1450
" Remove the condition with given ID again. You can use the "
1451
" 'list_conditions' subcommand to see all valid IDs.\n " ;
1452
constexpr auto listCondHelp =
1453
" [deprecated] replaced by: 'debug condition list'\n "
1454
" \n "
1455
" debug list_conditions\n "
1456
" Lists all active conditions. The result is similar to the "
1457
" 'list_bp' subcommand, but without the 2nd column that would "
1458
" show the address.\n " ;
1459
constexpr auto probeHelp =
1460
" debug probe <subcommand> [<arguments>]\n "
1461
" Possible subcommands are:\n "
1462
" list returns a list of all probes\n "
1463
" desc <probe> returns a description of this probe\n "
1464
" read <probe> returns the current value of this probe\n "
1465
" set_bp <probe> [-once] [<cond>] [<cmd>] set a breakpoint on the given probe\n "
1466
" remove_bp <id> remove the given breakpoint\n "
1467
" list_bp returns a list of breakpoints that are set on probes\n " ;
1468
constexpr auto contHelp =
1469
" debug cont\n "
1470
" Continue execution after CPU was breaked.\n " ;
1471
constexpr auto stepHelp =
1472
" debug step\n "
1473
" Execute one instruction. This command is only meaningful in "
1474
" break mode.\n " ;
1475
constexpr auto breakHelp =
1476
" debug break\n "
1477
" Immediately break CPU execution. When CPU was already breaked "
1478
" this command has no effect.\n " ;
1479
constexpr auto breakedHelp =
1480
" debug breaked\n "
1481
" Query the CPU breaked status. Returns '1' when CPU was "
1482
" breaked, '0' otherwise.\n " ;
1483
constexpr auto disasmHelp =
1484
" debug disasm <addr>\n "
1485
" Disassemble the instruction at the given address. The result "
1486
" is a Tcl list. The first element in the list contains a textual "
1487
" representation of the instruction, the next elements contain the "
1488
" bytes that make up this instruction (thus the length of the "
1489
" resulting list can be used to derive the number of bytes in the "
1490
" instruction).\n "
1491
" Note that openMSX comes with a 'disasm' Tcl script that is much "
1492
" more convenient to use than this subcommand.\n "
1493
+ addressNote + " \n " ;
1494
constexpr auto disasmBlobHelp =
1495
" debug disasm_blob <value> <addr> [<function>]\n "
1496
" This is a more generic version of the disasm subcommand, but it "
1497
" works on a Tcl binary string (see Tcl manual) to disassemble a "
1498
" single instruction. The given address is used when relative "
1499
" address to jump to is necessary. The optional fuction will be "
1500
" called with an address as parameter and may return a symbol name "
1501
" that replaces that address if a symbol that matches the address "
1502
" is found.\n "
1503
+ addressNote + " \n " ;
1504
constexpr auto symbolsHelp =
1505
" debug symbols <subcommand> [<arguments>]\n "
1506
" Possible subcommands are:\n "
1507
" types returns a list of symbol file types\n "
1508
" load <filename> [<type>] load a symbol file, auto-detect type if none is given\n "
1509
" remove <filename> remove a previously loaded symbol file\n "
1510
" files returns a list of all loaded symbol files\n "
1511
" lookup [-filename <filename>] [-name <name>] [-value <value>]\n "
1512
" returns a list of symbols in an optionally given file\n "
1513
" and/or with an optionally given name\n "
1514
" and/or with an optionally given value\n "
1515
" Note: an easier syntax to lookup a symbol value based on the name is:\n "
1516
" $sym(<name>)\n " ;
1517
constexpr auto unknownHelp =
1518
" Unknown subcommand, use 'help debug' to see a list of valid "
1519
" subcommands.\n " ;
1520
1521
auto size = tokens. size ( ) ;
1522
assert ( size >= 1 ) ;
1523
if ( size == 1 ) {
1524
return generalHelp;
1525
} else if ( tokens[ 1 ] == " list" ) {
1526
return listHelp;
1527
} else if ( tokens[ 1 ] == " desc" ) {
1528
return descHelp;
1529
} else if ( tokens[ 1 ] == " size" ) {
1530
return sizeHelp;
1531
} else if ( tokens[ 1 ] == " read" ) {
1532
return readHelp;
1533
} else if ( tokens[ 1 ] == " write" ) {
1534
return writeHelp;
1535
} else if ( tokens[ 1 ] == " read_block" ) {
1536
return readBlockHelp;
1537
} else if ( tokens[ 1 ] == " write_block" ) {
1538
return writeBlockHelp;
1539
} else if ( tokens[ 1 ] == " breakpoint" ) {
1540
if ( size == 2 ) {
1541
return breakPointHelp;
1542
} else if ( tokens[ 2 ] == " list" ) {
1543
return breakPointListHelp;
1544
} else if ( tokens[ 2 ] == " create" ) {
1545
return std:: string ( breakPointCreateHelp ) ;
1546
} else if ( tokens[ 2 ] == " configure" ) {
1547
return breakPointConfigureHelp;
1548
} else if ( tokens[ 2 ] == " remove" ) {
1549
return breakPointRemoveHelp;
1550
} else {
1551
return breakPointHelp;
1552
}
1553
} else if ( tokens[ 1 ] == " watchpoint" ) {
1554
if ( size == 2 ) {
1555
return watchPointHelp;
1556
} else if ( tokens[ 2 ] == " list" ) {
1557
return watchPointListHelp;
1558
} else if ( tokens[ 2 ] == " create" ) {
1559
return std:: string ( watchPointCreateHelp ) ;
1560
} else if ( tokens[ 2 ] == " configure" ) {
1561
return watchPointConfigureHelp;
1562
} else if ( tokens[ 2 ] == " remove" ) {
1563
return watchPointRemoveHelp;
1564
} else {
1565
return watchPointHelp;
1566
}
1567
} else if ( tokens[ 1 ] == " watchexpr" ) {
1568
if ( size == 2 ) {
1569
return watchExprHelp;
1570
} else if ( tokens[ 2 ] == " list" ) {
1571
return watchExprListHelp;
1572
} else if ( tokens[ 2 ] == " create" ) {
1573
return watchExprCreateHelp;
1574
} else if ( tokens[ 2 ] == " configure" ) {
1575
return watchExprConfigureHelp;
1576
} else if ( tokens[ 2 ] == " remove" ) {
1577
return watchExprRemoveHelp;
1578
} else {
1579
return watchExprHelp;
1580
}
1581
} else if ( tokens[ 1 ] == " condition" ) {
1582
if ( size == 2 ) {
1583
return conditionHelp;
1584
} else if ( tokens[ 2 ] == " list" ) {
1585
return conditionListHelp;
1586
} else if ( tokens[ 2 ] == " create" ) {
1587
return conditionCreateHelp;
1588
} else if ( tokens[ 2 ] == " configure" ) {
1589
return conditionConfigureHelp;
1590
} else if ( tokens[ 2 ] == " remove" ) {
1591
return conditionRemoveHelp;
1592
} else {
1593
return conditionHelp;
1594
}
1595
} else if ( tokens[ 1 ] == " set_bp" ) {
1596
return setBpHelp;
1597
} else if ( tokens[ 1 ] == " remove_bp" ) {
1598
return removeBpHelp;
1599
} else if ( tokens[ 1 ] == " list_bp" ) {
1600
return listBpHelp;
1601
} else if ( tokens[ 1 ] == " set_watchpoint" ) {
1602
return setWatchPointHelp;
1603
} else if ( tokens[ 1 ] == " remove_watchpoint" ) {
1604
return removeWatchPointHelp;
1605
} else if ( tokens[ 1 ] == " list_watchpoints" ) {
1606
return listWatchPointsHelp;
1607
} else if ( tokens[ 1 ] == " set_condition" ) {
1608
return setCondHelp;
1609
} else if ( tokens[ 1 ] == " remove_condition" ) {
1610
return removeCondHelp;
1611
} else if ( tokens[ 1 ] == " list_conditions" ) {
1612
return listCondHelp;
1613
} else if ( tokens[ 1 ] == " probe" ) {
1614
return probeHelp;
1615
} else if ( tokens[ 1 ] == " cont" ) {
1616
return contHelp;
1617
} else if ( tokens[ 1 ] == " step" ) {
1618
return stepHelp;
1619
} else if ( tokens[ 1 ] == " break" ) {
1620
return breakHelp;
1621
} else if ( tokens[ 1 ] == " breaked" ) {
1622
return breakedHelp;
1623
} else if ( tokens[ 1 ] == " disasm" ) {
1624
return std:: string ( disasmHelp ) ;
1625
} else if ( tokens[ 1 ] == " disasm_blob" ) {
1626
return std:: string ( disasmBlobHelp ) ;
1627
} else if ( tokens[ 1 ] == " symbols" ) {
1628
return symbolsHelp;
1629
} else if ( tokens[ 1 ] == " trace" ) {
1630
return debugger ( ) . tracer . help ( tokens ) ;
1631
} else {
1632
return unknownHelp;
1633
}
1634
}
1635
1636
std:: vector< std:: string> Debugger:: Cmd:: getBreakPointIds ( ) const
1637
{
1638
return to_vector ( std:: views:: transform (
1639
MSXCPUInterface:: getBreakPoints ( ) ,
1640
[ ] ( const auto & bp) { return bp. getIdStr ( ) ; } ) ) ;
1641
}
1642
std:: vector< std:: string> Debugger:: Cmd:: getWatchPointIds ( ) const
1643
{
1644
return to_vector ( std:: views:: transform (
1645
debugger ( ) . motherBoard . getCPUInterface ( ) . getWatchPoints ( ) ,
1646
[ ] ( const auto & w) { return w-> getIdStr ( ) ; } ) ) ;
1647
}
1648
std:: vector< std:: string> Debugger:: Cmd:: getWatchExprIds ( ) const
1649
{
1650
return to_vector ( std:: views:: transform (
1651
debugger ( ) . getWatchExprs ( ) ,
1652
[ ] ( const auto & w) { return w. getIdStr ( ) ; } ) ) ;
1653
}
1654
std:: vector< std:: string> Debugger:: Cmd:: getConditionIds ( ) const
1655
{
1656
return to_vector ( std:: views:: transform (
1657
MSXCPUInterface:: getConditions ( ) ,
1658
[ ] ( const auto & c) { return c. getIdStr ( ) ; } ) ) ;
1659
}
1660
1661
void Debugger:: Cmd:: tabCompletion ( std:: vector< std:: string> & tokens ) const
1662
{
1663
using namespace std:: literals;
1664
static constexpr std:: array singleArgCmds = {
1665
" list" sv, " step" sv, " cont" sv, " break" sv, " breaked" sv,
1666
" list_bp" sv, " list_watchpoints" sv, " list_conditions" sv,
1667
} ;
1668
static constexpr std:: array debuggableArgCmds = {
1669
" desc" sv, " size" sv, " read" sv, " read_block" sv,
1670
" write" sv, " write_block" sv,
1671
} ;
1672
static constexpr std:: array otherCmds = {
1673
" disasm" sv, " disasm_blob" sv, " set_bp" sv, " remove_bp" sv, " set_watchpoint" sv,
1674
" remove_watchpoint" sv, " set_condition" sv, " remove_condition" sv, " trace" sv,
1675
" probe" sv, " symbols" sv, " breakpoint" sv, " watchpoint" sv, " watchexpr" sv, " condition" sv,
1676
} ;
1677
static constexpr std:: array types = {
1678
" read_io" sv, " write_io" sv, " read_mem" sv, " write_mem" sv,
1679
} ;
1680
switch ( auto size = tokens. size ( ) ; size) {
1681
case 2 : {
1682
completeString ( tokens, concatArray ( singleArgCmds, debuggableArgCmds, otherCmds ) ) ;
1683
break ;
1684
}
1685
case 3 :
1686
if ( ! contains ( singleArgCmds, tokens[ 1 ] ) ) {
1687
1688
if ( contains ( debuggableArgCmds, tokens[ 1 ] ) ) {
1689
1690
completeString ( tokens, std:: views:: keys ( debugger ( ) . debuggables ) ) ;
1691
} else if ( tokens[ 1 ] == one_of ( " breakpoint" sv, " watchpoint" sv, " watchexpr" sv, " condition" sv) ) {
1692
static constexpr std:: array subCmds = {
1693
" list" sv, " create" sv,
1694
" configure" sv, " remove" sv,
1695
} ;
1696
completeString ( tokens, subCmds ) ;
1697
} else if ( tokens[ 1 ] == " remove_bp" ) {
1698
1699
completeString ( tokens, getBreakPointIds ( ) ) ;
1700
} else if ( tokens[ 1 ] == " remove_watchpoint" ) {
1701
1702
completeString ( tokens, getWatchPointIds ( ) ) ;
1703
} else if ( tokens[ 1 ] == " remove_condition" ) {
1704
1705
completeString ( tokens, getConditionIds ( ) ) ;
1706
} else if ( tokens[ 1 ] == " set_watchpoint" ) {
1707
completeString ( tokens, types ) ;
1708
} else if ( tokens[ 1 ] == " probe" ) {
1709
static constexpr std:: array subCmds = {
1710
" list" sv, " desc" sv, " read" sv, " set_bp" sv,
1711
" remove_bp" sv, " list_bp" sv,
1712
} ;
1713
completeString ( tokens, subCmds ) ;
1714
} else if ( tokens[ 1 ] == " symbols" ) {
1715
static constexpr std:: array subCmds = {
1716
" types" sv, " load" sv, " remove" sv,
1717
" files" sv, " lookup" sv,
1718
} ;
1719
completeString ( tokens, subCmds ) ;
1720
} else if ( tokens[ 1 ] == " trace" ) {
1721
debugger ( ) . tracer . tabCompletion ( debugger ( ) , tokens) ;
1722
}
1723
}
1724
break ;
1725
default :
1726
if ( ( size == 4 ) && ( tokens[ 1 ] == " probe" ) &&
1727
( tokens[ 2 ] == one_of ( " desc" , " read" , " set_bp" ) ) ) {
1728
completeString ( tokens, std:: views:: transform (
1729
debugger ( ) . probes , & ProbeBase:: getName ) ) ;
1730
} else if ( tokens[ 1 ] == " breakpoint" ) {
1731
if ( ( size == 4 ) && tokens[ 2 ] == one_of ( " remove" sv, " configure" sv) ) {
1732
completeString ( tokens, getBreakPointIds ( ) ) ;
1733
} else if ( ( ( size >= 4 ) && ( tokens[ 2 ] == " create" ) ) ||
1734
( ( size >= 5 ) && ( tokens[ 2 ] == " configure" ) ) ) {
1735
static constexpr std:: array properties = {
1736
" -address" sv, " -command" sv, " -condition" sv, " -enabled" sv, " -once" sv,
1737
} ;
1738
completeString ( tokens, properties ) ;
1739
}
1740
} else if ( tokens[ 1 ] == " watchpoint" ) {
1741
if ( ( size == 4 ) && tokens[ 2 ] == one_of ( " remove" sv, " configure" sv) ) {
1742
completeString ( tokens, getWatchPointIds ( ) ) ;
1743
} else if ( ( ( size >= 4 ) && ( tokens[ 2 ] == " create" ) ) ||
1744
( ( size >= 5 ) && ( tokens[ 2 ] == " configure" ) ) ) {
1745
if ( tokens[ size - 2 ] == " -type" ) {
1746
completeString ( tokens, types ) ;
1747
} else {
1748
static constexpr std:: array properties = {
1749
" -type" sv, " -address" sv, " -command" sv, " -condition" sv, " -enabled" sv, " -once" sv,
1750
} ;
1751
completeString ( tokens, properties ) ;
1752
}
1753
}
1754
} else if ( tokens[ 1 ] == " watchexpr" ) {
1755
if ( ( size == 4 ) && tokens[ 2 ] == one_of ( " remove" sv, " configure" sv) ) {
1756
completeString ( tokens, getWatchExprIds ( ) ) ;
1757
} else if ( ( ( size >= 4 ) && ( tokens[ 2 ] == " create" ) ) ||
1758
( ( size >= 5 ) && ( tokens[ 2 ] == " configure" ) ) ) {
1759
static constexpr std:: array properties = {
1760
" -description" sv, " -expression" sv, " -format" sv,
1761
} ;
1762
completeString ( tokens, properties ) ;
1763
}
1764
} else if ( tokens[ 1 ] == " condition" ) {
1765
if ( ( size == 4 ) && tokens[ 2 ] == one_of ( " remove" sv, " configure" sv) ) {
1766
completeString ( tokens, getConditionIds ( ) ) ;
1767
} else if ( ( ( size >= 4 ) && ( tokens[ 2 ] == " create" ) ) ||
1768
( ( size >= 5 ) && ( tokens[ 2 ] == " configure" ) ) ) {
1769
static constexpr std:: array properties = {
1770
" -command" sv, " -condition" sv, " -enabled" sv, " -once" sv,
1771
} ;
1772
completeString ( tokens, properties ) ;
1773
}
1774
} else if ( tokens[ 1 ] == " trace" ) {
1775
debugger ( ) . tracer . tabCompletion ( debugger ( ) , tokens) ;
1776
}
1777
break ;
1778
}
1779
}
1780
1781
}