1 //============================================================================
2 //
3 // SSSS tt lll lll
4 // SS SS tt ll ll
5 // SS tttttt eeee ll ll aaaa
6 // SSSS tt ee ee ll ll aa
7 // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8 // SS SS tt ee ll ll aa aa
9 // SSSS ttt eeeee llll llll aaaaa
10 //
11 // Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony
12 // and the Stella Team
13 //
14 // See the file "License.txt" for information on usage and redistribution of
15 // this file, and for a DISCLAIMER OF ALL WARRANTIES.
16 //============================================================================
17
18 #include "bspf.hxx"
19
20 #include "Dialog.hxx"
21 #include "Debugger.hxx"
22 #include "CartDebug.hxx"
23 #include "CpuDebug.hxx"
24 #include "RiotDebug.hxx"
25 #include "ControlLowLevel.hxx"
26 #include "TIADebug.hxx"
27 #include "TiaOutputWidget.hxx"
28 #include "DebuggerParser.hxx"
29 #include "YaccParser.hxx"
30 #include "M6502.hxx"
31 #include "Expression.hxx"
32 #include "FSNode.hxx"
33 #include "OSystem.hxx"
34 #include "System.hxx"
35 #include "M6502.hxx"
36 #include "Settings.hxx"
37 #include "PromptWidget.hxx"
38 #include "RomWidget.hxx"
39 #include "ProgressDialog.hxx"
40 #include "BrowserDialog.hxx"
41 #include "FrameBuffer.hxx"
42 #include "TimerManager.hxx"
43 #include "Vec.hxx"
44
45 #include "Base.hxx"
46 using Common::Base;
47 using std::hex;
48 using std::dec;
49 using std::setfill;
50 using std::setw;
51 using std::right;
52
53 #ifdef CHEATCODE_SUPPORT
54 #include "Cheat.hxx"
55 #include "CheatManager.hxx"
56 #endif
57
58 #include "DebuggerParser.hxx"
59
60 // TODO - use C++ streams instead of nasty C-strings and pointers
61
62 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DebuggerParser(Debugger & d,Settings & s)63 DebuggerParser::DebuggerParser(Debugger& d, Settings& s)
64 : debugger{d},
65 settings{s}
66 {
67 }
68
69 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
70 // main entry point: PromptWidget calls this method.
run(const string & command)71 string DebuggerParser::run(const string& command)
72 {
73 #if 0
74 // this was our parser test code. Left for reference.
75 static Expression *lastExpression;
76
77 // special case: parser testing
78 if(strncmp(command.c_str(), "expr ", 5) == 0) {
79 delete lastExpression;
80 commandResult = "parser test: status==";
81 int status = YaccParser::parse(command.c_str() + 5);
82 commandResult += debugger.valueToString(status);
83 commandResult += ", result==";
84 if(status == 0) {
85 lastExpression = YaccParser::getResult();
86 commandResult += debugger.valueToString(lastExpression->evaluate());
87 } else {
88 // delete lastExpression; // NO! lastExpression isn't valid (not 0 either)
89 // It's the result of casting the last token
90 // to Expression* (because of yacc's union).
91 // As such, we can't and don't need to delete it
92 // (However, it means yacc leaks memory on error)
93 commandResult += "ERROR - ";
94 commandResult += YaccParser::errorMessage();
95 }
96 return commandResult;
97 }
98
99 if(command == "expr") {
100 if(lastExpression)
101 commandResult = "result==" + debugger.valueToString(lastExpression->evaluate());
102 else
103 commandResult = "no valid expr";
104 return commandResult;
105 }
106 #endif
107
108 string verb;
109 getArgs(command, verb);
110 commandResult.str("");
111
112 for(int i = 0; i < int(commands.size()); ++i)
113 {
114 if(BSPF::equalsIgnoreCase(verb, commands[i].cmdString))
115 {
116 if(validateArgs(i))
117 {
118 myCommand = i;
119 if(commands[i].refreshRequired)
120 debugger.baseDialog()->saveConfig();
121 commands[i].executor(this);
122 }
123
124 if(commands[i].refreshRequired)
125 debugger.baseDialog()->loadConfig();
126
127 return commandResult.str();
128 }
129 }
130
131 return red("No such command (try \"help\")");
132 }
133
134 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
exec(const FilesystemNode & file,StringList * history)135 string DebuggerParser::exec(const FilesystemNode& file, StringList* history)
136 {
137 if(file.exists())
138 {
139 stringstream in;
140 try { file.read(in); }
141 catch(...) { return red("script file \'" + file.getShortPath() + "\' not found"); }
142
143 ostringstream buf;
144 int count = 0;
145 string command;
146 while( !in.eof() )
147 {
148 if(!getline(in, command))
149 break;
150
151 run(command);
152 if (history != nullptr)
153 history->push_back(command);
154 count++;
155 }
156 buf << "\nExecuted " << count << " command" << (count != 1 ? "s" : "") << " from \""
157 << file.getShortPath() << "\"";
158
159 return buf.str();
160 }
161 else
162 return red("script file \'" + file.getShortPath() + "\' not found");
163 }
164
165 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
outputCommandError(const string & errorMsg,int command)166 void DebuggerParser::outputCommandError(const string& errorMsg, int command)
167 {
168 string example = commands[command].extendedDesc.substr(commands[command].extendedDesc.find("Example:"));
169
170 commandResult << red(errorMsg);
171 if(!example.empty())
172 commandResult << endl << example;
173 }
174
175 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
176 // Completion-related stuff:
177 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
getCompletions(const char * in,StringList & completions) const178 void DebuggerParser::getCompletions(const char* in, StringList& completions) const
179 {
180 // cerr << "Attempting to complete \"" << in << "\"" << endl;
181 for(const auto& c: commands)
182 {
183 if(BSPF::matchesCamelCase(c.cmdString, in))
184 completions.push_back(c.cmdString);
185 }
186 }
187
188 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
189 // Evaluate expression. Expressions always evaluate to a 16-bit value if
190 // they're valid, or -1 if they're not.
191 // decipher_arg may be called by the GUI as needed. It is also called
192 // internally by DebuggerParser::run()
decipher_arg(const string & str)193 int DebuggerParser::decipher_arg(const string& str)
194 {
195 bool derefByte=false, derefWord=false, lobyte=false, hibyte=false, bin=false, dec=false;
196 int result;
197 string arg = str;
198
199 Base::Fmt defaultBase = Base::format();
200
201 if(defaultBase == Base::Fmt::_2) {
202 bin=true; dec=false;
203 } else if(defaultBase == Base::Fmt::_10) {
204 bin=false; dec=true;
205 } else {
206 bin=false; dec=false;
207 }
208
209 if(arg.substr(0, 1) == "*") {
210 derefByte = true;
211 arg.erase(0, 1);
212 } else if(arg.substr(0, 1) == "@") {
213 derefWord = true;
214 arg.erase(0, 1);
215 }
216
217 if(arg.substr(0, 1) == "<") {
218 lobyte = true;
219 arg.erase(0, 1);
220 } else if(arg.substr(0, 1) == ">") {
221 hibyte = true;
222 arg.erase(0, 1);
223 }
224
225 if(arg.substr(0, 1) == "\\") {
226 dec = false;
227 bin = true;
228 arg.erase(0, 1);
229 } else if(arg.substr(0, 1) == "#") {
230 dec = true;
231 bin = false;
232 arg.erase(0, 1);
233 } else if(arg.substr(0, 1) == "$") {
234 dec = false;
235 bin = false;
236 arg.erase(0, 1);
237 }
238
239 // Special cases (registers):
240 const CpuState& state = static_cast<const CpuState&>(debugger.cpuDebug().getState());
241 if(arg == "a" && str != "$a") result = state.A;
242 else if(arg == "x") result = state.X;
243 else if(arg == "y") result = state.Y;
244 else if(arg == "p") result = state.PS;
245 else if(arg == "s") result = state.SP;
246 else if(arg == "pc" || arg == ".") result = state.PC;
247 else { // Not a special, must be a regular arg: check for label first
248 const char* a = arg.c_str();
249 result = debugger.cartDebug().getAddress(arg);
250
251 if(result < 0) { // if not label, must be a number
252 if(bin) { // treat as binary
253 result = 0;
254 while(*a != '\0') {
255 result <<= 1;
256 switch(*a++) {
257 case '1':
258 result++;
259 break;
260
261 case '0':
262 break;
263
264 default:
265 return -1;
266 }
267 }
268 } else if(dec) {
269 result = 0;
270 while(*a != '\0') {
271 int digit = (*a++) - '0';
272 if(digit < 0 || digit > 9)
273 return -1;
274
275 result = (result * 10) + digit;
276 }
277 } else { // must be hex.
278 result = 0;
279 while(*a != '\0') {
280 int hex = -1;
281 char d = *a++;
282 if(d >= '0' && d <= '9') hex = d - '0';
283 else if(d >= 'a' && d <= 'f') hex = d - 'a' + 10;
284 else if(d >= 'A' && d <= 'F') hex = d - 'A' + 10;
285 if(hex < 0)
286 return -1;
287
288 result = (result << 4) + hex;
289 }
290 }
291 }
292 }
293
294 if(lobyte) result &= 0xff;
295 else if(hibyte) result = (result >> 8) & 0xff;
296
297 // dereference if we're supposed to:
298 if(derefByte) result = debugger.peek(result);
299 if(derefWord) result = debugger.dpeek(result);
300
301 return result;
302 }
303
304 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
showWatches()305 string DebuggerParser::showWatches()
306 {
307 ostringstream buf;
308 for(uInt32 i = 0; i < myWatches.size(); ++i)
309 {
310 if(myWatches[i] != "")
311 {
312 // Clear the args, since we're going to pass them to eval()
313 argStrings.clear();
314 args.clear();
315
316 argCount = 1;
317 argStrings.push_back(myWatches[i]);
318 args.push_back(decipher_arg(argStrings[0]));
319 if(args[0] < 0)
320 buf << "BAD WATCH " << (i+1) << ": " << argStrings[0] << endl;
321 else
322 buf << " watch #" << (i+1) << " (" << argStrings[0] << ") -> " << eval() << endl;
323 }
324 }
325 return buf.str();
326 }
327
328 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
329 // Private methods below
330 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
331
332 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
getArgs(const string & command,string & verb)333 bool DebuggerParser::getArgs(const string& command, string& verb)
334 {
335 ParseState state = ParseState::IN_COMMAND;
336 uInt32 i = 0, length = uInt32(command.length());
337 string curArg = "";
338 verb = "";
339
340 argStrings.clear();
341 args.clear();
342
343 // cerr << "Parsing \"" << command << "\"" << ", length = " << command.length() << endl;
344
345 // First, pick apart string into space-separated tokens.
346 // The first token is the command verb, the rest go in an array
347 do
348 {
349 char c = command[i++];
350 switch(state)
351 {
352 case ParseState::IN_COMMAND:
353 if(c == ' ')
354 state = ParseState::IN_SPACE;
355 else
356 verb += c;
357 break;
358 case ParseState::IN_SPACE:
359 if(c == '{')
360 state = ParseState::IN_BRACE;
361 else if(c != ' ') {
362 state = ParseState::IN_ARG;
363 curArg += c;
364 }
365 break;
366 case ParseState::IN_BRACE:
367 if(c == '}') {
368 state = ParseState::IN_SPACE;
369 argStrings.push_back(curArg);
370 // cerr << "{" << curArg << "}" << endl;
371 curArg = "";
372 } else {
373 curArg += c;
374 }
375 break;
376 case ParseState::IN_ARG:
377 if(c == ' ') {
378 state = ParseState::IN_SPACE;
379 argStrings.push_back(curArg);
380 curArg = "";
381 } else {
382 curArg += c;
383 }
384 break;
385 } // switch(state)
386 }
387 while(i < length);
388
389 // Take care of the last argument, if there is one
390 if(curArg != "")
391 argStrings.push_back(curArg);
392
393 argCount = uInt32(argStrings.size());
394
395 for(uInt32 arg = 0; arg < argCount; ++arg)
396 {
397 if(!YaccParser::parse(argStrings[arg].c_str()))
398 {
399 unique_ptr<Expression> expr(YaccParser::getResult());
400 args.push_back(expr->evaluate());
401 }
402 else
403 args.push_back(-1);
404 }
405
406 return true;
407 }
408
409 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
validateArgs(int cmd)410 bool DebuggerParser::validateArgs(int cmd)
411 {
412 // cerr << "entering validateArgs(" << cmd << ")" << endl;
413 bool required = commands[cmd].parmsRequired;
414 Parameters* p = commands[cmd].parms.data();
415
416 if(argCount == 0)
417 {
418 if(required)
419 {
420 void(commandResult.str());
421 outputCommandError("missing required argument(s)", cmd);
422 return false; // needed args. didn't get 'em.
423 }
424 else
425 return true; // no args needed, no args got
426 }
427
428 // Figure out how many arguments are required by the command
429 uInt32 count = 0, argRequiredCount = 0;
430 while(*p != Parameters::ARG_END_ARGS && *p != Parameters::ARG_MULTI_BYTE)
431 {
432 ++count;
433 ++p;
434 }
435
436 // Evil hack: some commands intentionally take multiple arguments
437 // In this case, the required number of arguments is unbounded
438 argRequiredCount = (*p == Parameters::ARG_END_ARGS) ? count : argCount;
439
440 p = commands[cmd].parms.data();
441 uInt32 curCount = 0;
442
443 do {
444 if(curCount >= argCount)
445 break;
446
447 uInt32 curArgInt = args[curCount];
448 string& curArgStr = argStrings[curCount];
449
450 switch(*p)
451 {
452 case Parameters::ARG_DWORD:
453 #if 0 // TODO - do we need error checking at all here?
454 if(curArgInt > 0xffffffff)
455 {
456 commandResult.str(red("invalid word argument (must be 0-$ffffffff)"));
457 return false;
458 }
459 #endif
460 break;
461
462 case Parameters::ARG_WORD:
463 if(curArgInt > 0xffff)
464 {
465 commandResult.str(red("invalid word argument (must be 0-$ffff)"));
466 return false;
467 }
468 break;
469
470 case Parameters::ARG_BYTE:
471 if(curArgInt > 0xff)
472 {
473 commandResult.str(red("invalid byte argument (must be 0-$ff)"));
474 return false;
475 }
476 break;
477
478 case Parameters::ARG_BOOL:
479 if(curArgInt != 0 && curArgInt != 1)
480 {
481 commandResult.str(red("invalid boolean argument (must be 0 or 1)"));
482 return false;
483 }
484 break;
485
486 case Parameters::ARG_BASE_SPCL:
487 if(curArgInt != 2 && curArgInt != 10 && curArgInt != 16
488 && curArgStr != "hex" && curArgStr != "dec" && curArgStr != "bin")
489 {
490 commandResult.str(red("invalid base (must be #2, #10, #16, \"bin\", \"dec\", or \"hex\")"));
491 return false;
492 }
493 break;
494
495 case Parameters::ARG_LABEL:
496 case Parameters::ARG_FILE:
497 break; // TODO: validate these (for now any string's allowed)
498
499 case Parameters::ARG_MULTI_BYTE:
500 case Parameters::ARG_MULTI_WORD:
501 break; // FIXME: validate these (for now, any number's allowed)
502
503 case Parameters::ARG_END_ARGS:
504 break;
505 }
506 ++curCount;
507 ++p;
508
509 } while(*p != Parameters::ARG_END_ARGS && curCount < argRequiredCount);
510
511 /*
512 cerr << "curCount = " << curCount << endl
513 << "argRequiredCount = " << argRequiredCount << endl
514 << "*p = " << *p << endl << endl;
515 */
516
517 if(curCount < argRequiredCount)
518 {
519 void(commandResult.str());
520 outputCommandError("missing required argument(s)", cmd);
521 return false;
522 }
523 else if(argCount > curCount)
524 {
525 void(commandResult.str());
526 outputCommandError("too many arguments", cmd);
527 return false;
528 }
529
530 return true;
531 }
532
533 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
eval()534 string DebuggerParser::eval()
535 {
536 ostringstream buf;
537 for(uInt32 i = 0; i < argCount; ++i)
538 {
539 if(args[i] < 0x10000)
540 {
541 string rlabel = debugger.cartDebug().getLabel(args[i], true);
542 string wlabel = debugger.cartDebug().getLabel(args[i], false);
543 bool validR = rlabel != "" && rlabel[0] != '$',
544 validW = wlabel != "" && wlabel[0] != '$';
545 if(validR && validW)
546 {
547 if(rlabel == wlabel)
548 buf << rlabel << "(R/W): ";
549 else
550 buf << rlabel << "(R) / " << wlabel << "(W): ";
551 }
552 else if(validR)
553 buf << rlabel << "(R): ";
554 else if(validW)
555 buf << wlabel << "(W): ";
556 }
557
558 buf << "$" << Base::toString(args[i], Base::Fmt::_16);
559
560 if(args[i] < 0x10000)
561 buf << " %" << Base::toString(args[i], Base::Fmt::_2);
562
563 buf << " #" << int(args[i]);
564 if(i != argCount - 1)
565 buf << endl;
566 }
567
568 return buf.str();
569 }
570
571 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cartName() const572 const string& DebuggerParser::cartName() const
573 {
574 return debugger.myOSystem.console().properties().get(PropType::Cart_Name);
575 }
576
577 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
listTraps(bool listCond)578 void DebuggerParser::listTraps(bool listCond)
579 {
580 StringList names = debugger.m6502().getCondTrapNames();
581
582 commandResult << (listCond ? "trapifs:" : "traps:") << endl;
583 for(uInt32 i = 0; i < names.size(); ++i)
584 {
585 bool hasCond = names[i] != "";
586 if(hasCond == listCond)
587 {
588 commandResult << Base::toString(i) << ": ";
589 if(myTraps[i]->read && myTraps[i]->write)
590 commandResult << "read|write";
591 else if(myTraps[i]->read)
592 commandResult << "read ";
593 else if(myTraps[i]->write)
594 commandResult << " write";
595 else
596 commandResult << "none";
597
598 if(hasCond)
599 commandResult << " " << names[i];
600 commandResult << " " << debugger.cartDebug().getLabel(myTraps[i]->begin, true, 4);
601 if(myTraps[i]->begin != myTraps[i]->end)
602 commandResult << " " << debugger.cartDebug().getLabel(myTraps[i]->end, true, 4);
603 commandResult << trapStatus(*myTraps[i]);
604 commandResult << " + mirrors";
605 if(i != (names.size() - 1)) commandResult << endl;
606 }
607 }
608 }
609
610 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
trapStatus(const Trap & trap)611 string DebuggerParser::trapStatus(const Trap& trap)
612 {
613 stringstream result;
614 string lblb = debugger.cartDebug().getLabel(trap.begin, !trap.write);
615 string lble = debugger.cartDebug().getLabel(trap.end, !trap.write);
616
617 if(lblb != "") {
618 result << " (";
619 result << lblb;
620 }
621
622 if(trap.begin != trap.end)
623 {
624 if(lble != "")
625 {
626 if (lblb != "")
627 result << " ";
628 else
629 result << " (";
630 result << lble;
631 }
632 }
633 if (lblb != "" || lble != "")
634 result << ")";
635
636 return result.str();
637 }
638
639 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
saveScriptFile(string file)640 string DebuggerParser::saveScriptFile(string file)
641 {
642 stringstream out;
643 Debugger::FunctionDefMap funcs = debugger.getFunctionDefMap();
644 for(const auto& [name, cmd]: funcs)
645 if (!debugger.isBuiltinFunction(name))
646 out << "function " << name << " {" << cmd << "}" << endl;
647
648 for(const auto& w: myWatches)
649 out << "watch " << w << endl;
650
651 for(const auto& bp: debugger.breakPoints().getBreakpoints())
652 out << "break " << Base::toString(bp.addr) << " " << Base::toString(bp.bank) << endl;
653
654 StringList conds = debugger.m6502().getCondBreakNames();
655 for(const auto& cond : conds)
656 out << "breakIf {" << cond << "}" << endl;
657
658 conds = debugger.m6502().getCondSaveStateNames();
659 for(const auto& cond : conds)
660 out << "saveStateIf {" << cond << "}" << endl;
661
662 StringList names = debugger.m6502().getCondTrapNames();
663 for(uInt32 i = 0; i < myTraps.size(); ++i)
664 {
665 bool read = myTraps[i]->read;
666 bool write = myTraps[i]->write;
667 bool hasCond = names[i] != "";
668
669 if(read && write)
670 out << "trap";
671 else if(read)
672 out << "trapRead";
673 else if(write)
674 out << "trapWrite";
675 if(hasCond)
676 out << "if {" << names[i] << "}";
677 out << " " << Base::toString(myTraps[i]->begin);
678 if(myTraps[i]->begin != myTraps[i]->end)
679 out << " " << Base::toString(myTraps[i]->end);
680 out << endl;
681 }
682
683 // Append 'script' extension when necessary
684 if(file.find_last_of('.') == string::npos)
685 file += ".script";
686
687 // Use user dir if no path is provided
688 if(file.find_first_of(FilesystemNode::PATH_SEPARATOR) == string::npos)
689 file = debugger.myOSystem.userDir().getPath() + file;
690
691 FilesystemNode node(file);
692
693 if(node.exists() || out.str().length())
694 {
695 try
696 {
697 node.write(out);
698 }
699 catch(...)
700 {
701 return "Unable to save script to " + node.getShortPath();
702 }
703
704 return "saved " + node.getShortPath() + " OK";
705 }
706 else
707 return "nothing to save";
708 }
709
710 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
saveDump(const FilesystemNode & node,const stringstream & out,ostringstream & result)711 void DebuggerParser::saveDump(const FilesystemNode& node, const stringstream& out,
712 ostringstream& result)
713 {
714 try
715 {
716 node.write(out);
717 result << " to file " << node.getShortPath();
718 }
719 catch(...)
720 {
721 result.str(red("Unable to append dump to file " + node.getShortPath()));
722 }
723 }
724
725 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
executeDirective(Device::AccessType type)726 void DebuggerParser::executeDirective(Device::AccessType type)
727 {
728 if(argCount != 2)
729 {
730 outputCommandError("specify start and end of range only", myCommand);
731 return;
732 }
733 else if(args[1] < args[0])
734 {
735 commandResult << red("start address must be <= end address");
736 return;
737 }
738
739 bool result = debugger.cartDebug().addDirective(type, args[0], args[1]);
740
741 commandResult << (result ? "added " : "removed ");
742 debugger.cartDebug().AccessTypeAsString(commandResult, type);
743 commandResult << " directive on range $"
744 << hex << args[0] << " $" << hex << args[1];
745 debugger.rom().invalidate();
746 }
747
748 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
749 // executor methods for commands[] array. All are void, no args.
750 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
751
752 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
753 // "a"
executeA()754 void DebuggerParser::executeA()
755 {
756 debugger.cpuDebug().setA(uInt8(args[0]));
757 }
758
759 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
760 // "aud"
executeAud()761 void DebuggerParser::executeAud()
762 {
763 executeDirective(Device::AUD);
764 }
765
766 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
767 // "autoSave"
executeAutoSave()768 void DebuggerParser::executeAutoSave()
769 {
770 bool enable = !settings.getBool("dbg.autosave");
771
772 settings.setValue("dbg.autosave", enable);
773 commandResult << "autoSave " << (enable ? "enabled" : "disabled");
774 }
775
776 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
777 // "base"
executeBase()778 void DebuggerParser::executeBase()
779 {
780 if(args[0] == 2 || argStrings[0] == "bin")
781 Base::setFormat(Base::Fmt::_2);
782 else if(args[0] == 10 || argStrings[0] == "dec")
783 Base::setFormat(Base::Fmt::_10);
784 else if(args[0] == 16 || argStrings[0] == "hex")
785 Base::setFormat(Base::Fmt::_16);
786
787 commandResult << "default number base set to ";
788 switch(Base::format()) {
789 case Base::Fmt::_2:
790 commandResult << "#2/bin";
791 break;
792
793 case Base::Fmt::_10:
794 commandResult << "#10/dec";
795 break;
796
797 case Base::Fmt::_16:
798 commandResult << "#16/hex";
799 break;
800
801 default:
802 commandResult << red("UNKNOWN");
803 break;
804 }
805 }
806
807 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
808 // "bCol"
executeBCol()809 void DebuggerParser::executeBCol()
810 {
811 executeDirective(Device::BCOL);
812 }
813
814 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
815 // "break"
executeBreak()816 void DebuggerParser::executeBreak()
817 {
818 uInt16 addr;
819 uInt8 bank;
820 uInt32 romBankCount = debugger.cartDebug().romBankCount();
821
822 if(argCount == 0)
823 addr = debugger.cpuDebug().pc();
824 else
825 addr = args[0];
826
827 if(argCount < 2)
828 bank = debugger.cartDebug().getBank(addr);
829 else
830 {
831 bank = args[1];
832 if(bank >= romBankCount && bank != 0xff)
833 {
834 commandResult << red("invalid bank");
835 return;
836 }
837 }
838 if(bank != 0xff)
839 {
840 bool set = debugger.toggleBreakPoint(addr, bank);
841
842 if(set)
843 commandResult << "set";
844 else
845 commandResult << "cleared";
846
847 commandResult << " breakpoint at $" << Base::HEX4 << addr << " + mirrors";
848 if(romBankCount > 1)
849 commandResult << " in bank #" << std::dec << int(bank);
850 }
851 else
852 {
853 for(int i = 0; i < debugger.cartDebug().romBankCount(); ++i)
854 {
855 bool set = debugger.toggleBreakPoint(addr, i);
856
857 if(i)
858 commandResult << endl;
859
860 if(set)
861 commandResult << "set";
862 else
863 commandResult << "cleared";
864
865 commandResult << " breakpoint at $" << Base::HEX4 << addr << " + mirrors";
866 if(romBankCount > 1)
867 commandResult << " in bank #" << std::dec << int(bank);
868 }
869 }
870 }
871
872 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
873 // "breakIf"
executeBreakIf()874 void DebuggerParser::executeBreakIf()
875 {
876 int res = YaccParser::parse(argStrings[0].c_str());
877 if(res == 0)
878 {
879 string condition = argStrings[0];
880 for(uInt32 i = 0; i < debugger.m6502().getCondBreakNames().size(); ++i)
881 {
882 if(condition == debugger.m6502().getCondBreakNames()[i])
883 {
884 args[0] = i;
885 executeDelBreakIf();
886 return;
887 }
888 }
889 uInt32 ret = debugger.m6502().addCondBreak(
890 YaccParser::getResult(), argStrings[0]);
891 commandResult << "added breakIf " << Base::toString(ret);
892 }
893 else
894 commandResult << red("invalid expression");
895 }
896
897 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
898 // "breakLabel"
executeBreakLabel()899 void DebuggerParser::executeBreakLabel()
900 {
901 uInt16 addr;
902
903 if(argCount == 0)
904 addr = debugger.cpuDebug().pc();
905 else
906 addr = args[0];
907
908 bool set = debugger.toggleBreakPoint(addr, BreakpointMap::ANY_BANK);
909
910 commandResult << (set ? "set" : "cleared");
911 commandResult << " breakpoint at $" << Base::HEX4 << addr << " (no mirrors)";
912 }
913
914 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
915 // "c"
executeC()916 void DebuggerParser::executeC()
917 {
918 if(argCount == 0)
919 debugger.cpuDebug().toggleC();
920 else if(argCount == 1)
921 debugger.cpuDebug().setC(args[0]);
922 }
923
924 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
925 // "cheat"
926 // (see Stella manual for different cheat types)
executeCheat()927 void DebuggerParser::executeCheat()
928 {
929 #ifdef CHEATCODE_SUPPORT
930 if(argCount == 0)
931 {
932 outputCommandError("missing cheat code", myCommand);
933 return;
934 }
935
936 for(uInt32 arg = 0; arg < argCount; ++arg)
937 {
938 const string& cheat = argStrings[arg];
939 if(debugger.myOSystem.cheat().add("DBG", cheat))
940 commandResult << "cheat code " << cheat << " enabled" << endl;
941 else
942 commandResult << red("invalid cheat code ") << cheat << endl;
943 }
944 #else
945 commandResult << red("Cheat support not enabled\n");
946 #endif
947 }
948
949 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
950 // "clearBreaks"
executeClearBreaks()951 void DebuggerParser::executeClearBreaks()
952 {
953 debugger.clearAllBreakPoints();
954 debugger.m6502().clearCondBreaks();
955 commandResult << "all breakpoints cleared";
956 }
957
958 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
959 // "clearConfig"
executeClearConfig()960 void DebuggerParser::executeClearConfig()
961 {
962 if(argCount == 1)
963 commandResult << debugger.cartDebug().clearConfig(args[0]);
964 else
965 commandResult << debugger.cartDebug().clearConfig();
966 }
967
968 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
969 // "clearHistory"
executeClearHistory()970 void DebuggerParser::executeClearHistory()
971 {
972 debugger.prompt().clearHistory();
973 commandResult << "";
974 }
975
976 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
977 // "clearBreaks"
executeClearSaveStateIfs()978 void DebuggerParser::executeClearSaveStateIfs()
979 {
980 debugger.m6502().clearCondSaveStates();
981 commandResult << "all saveState points cleared";
982 }
983
984 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
985 // "clearTraps"
executeClearTraps()986 void DebuggerParser::executeClearTraps()
987 {
988 debugger.clearAllTraps();
989 debugger.m6502().clearCondTraps();
990 myTraps.clear();
991 commandResult << "all traps cleared";
992 }
993
994 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
995 // "clearWatches"
executeClearWatches()996 void DebuggerParser::executeClearWatches()
997 {
998 myWatches.clear();
999 commandResult << "all watches cleared";
1000 }
1001
1002 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1003 // "cls"
executeCls()1004 void DebuggerParser::executeCls()
1005 {
1006 debugger.prompt().clearScreen();
1007 commandResult << "";
1008 }
1009
1010 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1011 // "code"
executeCode()1012 void DebuggerParser::executeCode()
1013 {
1014 executeDirective(Device::CODE);
1015 }
1016
1017 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1018 // "col"
executeCol()1019 void DebuggerParser::executeCol()
1020 {
1021 executeDirective(Device::COL);
1022 }
1023
1024 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1025 // "colorTest"
executeColorTest()1026 void DebuggerParser::executeColorTest()
1027 {
1028 commandResult << "test color: "
1029 << char((args[0]>>1) | 0x80)
1030 << inverse(" ");
1031 }
1032
1033 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1034 // "d"
executeD()1035 void DebuggerParser::executeD()
1036 {
1037 if(argCount == 0)
1038 debugger.cpuDebug().toggleD();
1039 else if(argCount == 1)
1040 debugger.cpuDebug().setD(args[0]);
1041 }
1042
1043 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1044 // "data"
executeData()1045 void DebuggerParser::executeData()
1046 {
1047 executeDirective(Device::DATA);
1048 }
1049
1050 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1051 // "debugColors"
executeDebugColors()1052 void DebuggerParser::executeDebugColors()
1053 {
1054 commandResult << debugger.tiaDebug().debugColors();
1055 }
1056
1057 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1058 // "define"
executeDefine()1059 void DebuggerParser::executeDefine()
1060 {
1061 // TODO: check if label already defined?
1062 debugger.cartDebug().addLabel(argStrings[0], args[1]);
1063 debugger.rom().invalidate();
1064 }
1065
1066 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1067 // "delBreakIf"
executeDelBreakIf()1068 void DebuggerParser::executeDelBreakIf()
1069 {
1070 if (debugger.m6502().delCondBreak(args[0]))
1071 commandResult << "removed breakIf " << Base::toString(args[0]);
1072 else
1073 commandResult << red("no such breakIf");
1074 }
1075
1076 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1077 // "delFunction"
executeDelFunction()1078 void DebuggerParser::executeDelFunction()
1079 {
1080 if(debugger.delFunction(argStrings[0]))
1081 commandResult << "removed function " << argStrings[0];
1082 else
1083 commandResult << "function " << argStrings[0] << " built-in or not found";
1084 }
1085
1086 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1087 // "delSaveStateIf"
executeDelSaveStateIf()1088 void DebuggerParser::executeDelSaveStateIf()
1089 {
1090 if(debugger.m6502().delCondSaveState(args[0]))
1091 commandResult << "removed saveStateIf " << Base::toString(args[0]);
1092 else
1093 commandResult << red("no such saveStateIf");
1094 }
1095
1096 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1097 // "delTrap"
executeDelTrap()1098 void DebuggerParser::executeDelTrap()
1099 {
1100 int index = args[0];
1101
1102 if(debugger.m6502().delCondTrap(index))
1103 {
1104 for(uInt32 addr = myTraps[index]->begin; addr <= myTraps[index]->end; ++addr)
1105 executeTrapRW(addr, myTraps[index]->read, myTraps[index]->write, false);
1106 // @sa666666: please check this:
1107 Vec::removeAt(myTraps, index);
1108 commandResult << "removed trap " << Base::toString(index);
1109 }
1110 else
1111 commandResult << red("no such trap");
1112 }
1113
1114 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1115 // "delWatch"
executeDelWatch()1116 void DebuggerParser::executeDelWatch()
1117 {
1118 int which = args[0] - 1;
1119 if(which >= 0 && which < int(myWatches.size()))
1120 {
1121 Vec::removeAt(myWatches, which);
1122 commandResult << "removed watch";
1123 }
1124 else
1125 commandResult << red("no such watch");
1126 }
1127
1128 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1129 // "disAsm"
executeDisAsm()1130 void DebuggerParser::executeDisAsm()
1131 {
1132 int start, lines = 20;
1133
1134 if(argCount == 0) {
1135 start = debugger.cpuDebug().pc();
1136 } else if(argCount == 1) {
1137 start = args[0];
1138 } else if(argCount == 2) {
1139 start = args[0];
1140 lines = args[1];
1141 } else {
1142 outputCommandError("wrong number of arguments", myCommand);
1143 return;
1144 }
1145
1146 commandResult << debugger.cartDebug().disassembleLines(start, lines);
1147 }
1148
1149 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1150 // "dump"
executeDump()1151 void DebuggerParser::executeDump()
1152 {
1153 auto dump = [&](ostream& os, int start, int end)
1154 {
1155 for(int i = start; i <= end; i += 16)
1156 {
1157 // Print label every 16 bytes
1158 os << Base::toString(i) << ": ";
1159
1160 for(int j = i; j < i+16 && j <= end; ++j)
1161 {
1162 os << Base::toString(debugger.peek(j)) << " ";
1163 if(j == i+7 && j != end) os << "- ";
1164 }
1165 os << endl;
1166 }
1167 };
1168
1169 // Error checking
1170 if(argCount == 0 || argCount > 4)
1171 {
1172 outputCommandError("wrong number of arguments", myCommand);
1173 return;
1174 }
1175 if(argCount > 1 && args[1] < args[0])
1176 {
1177 commandResult << red("start address must be <= end address");
1178 return;
1179 }
1180
1181 if(argCount == 1)
1182 dump(commandResult, args[0], args[0] + 127);
1183 else if(argCount == 2 || args[2] == 0)
1184 dump(commandResult, args[0], args[1]);
1185 else
1186 {
1187 if((args[2] & 0x07) == 0)
1188 {
1189 commandResult << red("dump flags must be 1..7");
1190 return;
1191 }
1192 if(argCount == 4 && argStrings[3] != "?")
1193 {
1194 commandResult << red("browser dialog parameter must be '?'");
1195 return;
1196 }
1197
1198 ostringstream path;
1199 path << debugger.myOSystem.userDir() << cartName() << "_dbg_";
1200 if(execDepth > 0)
1201 path << execPrefix;
1202 else
1203 path << std::hex << std::setw(8) << std::setfill('0')
1204 << uInt32(TimerManager::getTicks() / 1000);
1205 path << ".dump";
1206
1207 commandResult << "dumped ";
1208
1209 stringstream out;
1210 if((args[2] & 0x01) != 0)
1211 {
1212 // dump memory
1213 dump(out, args[0], args[1]);
1214 commandResult << "bytes from $" << hex << args[0] << " to $" << hex << args[1];
1215 if((args[2] & 0x06) != 0)
1216 commandResult << ", ";
1217 }
1218 if((args[2] & 0x02) != 0)
1219 {
1220 // dump CPU state
1221 CpuDebug& cpu = debugger.cpuDebug();
1222 out << " <PC>PC SP A X Y - - N V B D I Z C -\n";
1223 out << "XC: "
1224 << Base::toString(cpu.pc() & 0xff) << " " // PC lsb
1225 << Base::toString(cpu.pc() >> 8) << " " // PC msb
1226 << Base::toString(cpu.sp()) << " " // SP
1227 << Base::toString(cpu.a()) << " " // A
1228 << Base::toString(cpu.x()) << " " // X
1229 << Base::toString(cpu.y()) << " " // Y
1230 << Base::toString(0) << " " // unused
1231 << Base::toString(0) << " - " // unused
1232 << Base::toString(cpu.n()) << " " // N (flag)
1233 << Base::toString(cpu.v()) << " " // V (flag)
1234 << Base::toString(cpu.b()) << " " // B (flag)
1235 << Base::toString(cpu.d()) << " " // D (flag)
1236 << Base::toString(cpu.i()) << " " // I (flag)
1237 << Base::toString(cpu.z()) << " " // Z (flag)
1238 << Base::toString(cpu.c()) << " " // C (flag)
1239 << Base::toString(0) << " " // unused
1240 << endl;
1241 commandResult << "CPU state";
1242 if((args[2] & 0x04) != 0)
1243 commandResult << ", ";
1244 }
1245 if((args[2] & 0x04) != 0)
1246 {
1247 // dump SWCHx/INPTx state
1248 out << " SWA - SWB - IT - - - I0 I1 I2 I3 I4 I5 - -\n";
1249 out << "XS: "
1250 << Base::toString(debugger.peek(0x280)) << " " // SWCHA
1251 << Base::toString(0) << " " // unused
1252 << Base::toString(debugger.peek(0x282)) << " " // SWCHB
1253 << Base::toString(0) << " " // unused
1254 << Base::toString(debugger.peek(0x284)) << " " // INTIM
1255 << Base::toString(0) << " " // unused
1256 << Base::toString(0) << " " // unused
1257 << Base::toString(0) << " - " // unused
1258 << Base::toString(debugger.peek(TIARegister::INPT0)) << " "
1259 << Base::toString(debugger.peek(TIARegister::INPT1)) << " "
1260 << Base::toString(debugger.peek(TIARegister::INPT2)) << " "
1261 << Base::toString(debugger.peek(TIARegister::INPT3)) << " "
1262 << Base::toString(debugger.peek(TIARegister::INPT4)) << " "
1263 << Base::toString(debugger.peek(TIARegister::INPT5)) << " "
1264 << Base::toString(0) << " " // unused
1265 << Base::toString(0) << " " // unused
1266 << endl;
1267 commandResult << "switches and fire buttons";
1268 }
1269
1270 if(argCount == 4)
1271 {
1272 // FIXME: C++ doesn't currently allow capture of stringstreams
1273 // So we pass a copy of its contents, then re-create the
1274 // stream inside the lambda
1275 // Maybe this will change in a future version
1276 const string outStr = out.str();
1277 const string resultStr = commandResult.str();
1278
1279 DebuggerDialog* dlg = debugger.myDialog;
1280 BrowserDialog::show(dlg, "Save Dump as", path.str(),
1281 BrowserDialog::Mode::FileSave,
1282 [this, dlg, outStr, resultStr]
1283 (bool OK, const FilesystemNode& node)
1284 {
1285 if(OK)
1286 {
1287 stringstream localOut(outStr);
1288 ostringstream localResult(resultStr, std::ios_base::app);
1289
1290 saveDump(node, localOut, localResult);
1291 dlg->prompt().print(localResult.str() + '\n');
1292 }
1293 dlg->prompt().printPrompt();
1294 });
1295 // avoid printing a new prompt
1296 commandResult.str("_NO_PROMPT");
1297 }
1298 else
1299 saveDump(FilesystemNode(path.str()), out, commandResult);
1300 }
1301 }
1302
1303 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1304 // "exec"
executeExec()1305 void DebuggerParser::executeExec()
1306 {
1307 // Append 'script' extension when necessary
1308 string file = argStrings[0];
1309 if(file.find_last_of('.') == string::npos)
1310 file += ".script";
1311 FilesystemNode node(file);
1312 if (!node.exists())
1313 node = FilesystemNode(debugger.myOSystem.userDir().getPath() + file);
1314
1315 if (argCount == 2) {
1316 execPrefix = argStrings[1];
1317 }
1318 else {
1319 ostringstream prefix;
1320 prefix << std::hex << std::setw(8) << std::setfill('0')
1321 << uInt32(TimerManager::getTicks()/1000);
1322 execPrefix = prefix.str();
1323 }
1324
1325 // make sure the commands are added to prompt history
1326 StringList history;
1327
1328 ++execDepth;
1329 commandResult << exec(node, &history);
1330 --execDepth;
1331
1332 for(const auto& item : history)
1333 debugger.prompt().addToHistory(item.c_str());
1334 }
1335
1336 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1337 // "exitRom"
executeExitRom()1338 void DebuggerParser::executeExitRom()
1339 {
1340 debugger.exit(true);
1341 }
1342
1343 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1344 // "frame"
executeFrame()1345 void DebuggerParser::executeFrame()
1346 {
1347 int count = 1;
1348 if(argCount != 0) count = args[0];
1349 debugger.nextFrame(count);
1350 commandResult << "advanced " << dec << count << " frame(s)";
1351 }
1352
1353 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1354 // "function"
executeFunction()1355 void DebuggerParser::executeFunction()
1356 {
1357 if(args[0] >= 0)
1358 {
1359 commandResult << red("name already in use");
1360 return;
1361 }
1362
1363 int res = YaccParser::parse(argStrings[1].c_str());
1364 if(res == 0)
1365 {
1366 debugger.addFunction(argStrings[0], argStrings[1], YaccParser::getResult());
1367 commandResult << "added function " << argStrings[0] << " -> " << argStrings[1];
1368 }
1369 else
1370 commandResult << red("invalid expression");
1371 }
1372
1373 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1374 // "gfx"
executeGfx()1375 void DebuggerParser::executeGfx()
1376 {
1377 executeDirective(Device::GFX);
1378 }
1379
1380 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1381 // "help"
executeHelp()1382 void DebuggerParser::executeHelp()
1383 {
1384 if(argCount == 0) // normal help, show all commands
1385 {
1386 // Find length of longest command
1387 uInt32 clen = 0;
1388 for(const auto& c: commands)
1389 {
1390 uInt32 len = uInt32(c.cmdString.length());
1391 if(len > clen) clen = len;
1392 }
1393
1394 commandResult << setfill(' ');
1395 for(const auto& c: commands)
1396 commandResult << setw(clen) << right << c.cmdString
1397 << " - " << c.description << endl;
1398
1399 commandResult << debugger.builtinHelp();
1400 }
1401 else // get help for specific command
1402 {
1403 for(const auto& c: commands)
1404 {
1405 if(argStrings[0] == c.cmdString)
1406 {
1407 commandResult << " " << red(c.description) << endl << c.extendedDesc;
1408 break;
1409 }
1410 }
1411 }
1412 }
1413
1414 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1415 // "joy0Up"
executeJoy0Up()1416 void DebuggerParser::executeJoy0Up()
1417 {
1418 ControllerLowLevel lport(debugger.myOSystem.console().leftController());
1419 if(argCount == 0)
1420 lport.togglePin(Controller::DigitalPin::One);
1421 else if(argCount == 1)
1422 lport.setPin(Controller::DigitalPin::One, args[0] != 0);
1423 }
1424
1425 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1426 // "joy0Down"
executeJoy0Down()1427 void DebuggerParser::executeJoy0Down()
1428 {
1429 ControllerLowLevel lport(debugger.myOSystem.console().leftController());
1430 if(argCount == 0)
1431 lport.togglePin(Controller::DigitalPin::Two);
1432 else if(argCount == 1)
1433 lport.setPin(Controller::DigitalPin::Two, args[0] != 0);
1434 }
1435
1436 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1437 // "joy0Left"
executeJoy0Left()1438 void DebuggerParser::executeJoy0Left()
1439 {
1440 ControllerLowLevel lport(debugger.myOSystem.console().leftController());
1441 if(argCount == 0)
1442 lport.togglePin(Controller::DigitalPin::Three);
1443 else if(argCount == 1)
1444 lport.setPin(Controller::DigitalPin::Three, args[0] != 0);
1445 }
1446
1447 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1448 // "joy0Right"
executeJoy0Right()1449 void DebuggerParser::executeJoy0Right()
1450 {
1451 ControllerLowLevel lport(debugger.myOSystem.console().leftController());
1452 if(argCount == 0)
1453 lport.togglePin(Controller::DigitalPin::Four);
1454 else if(argCount == 1)
1455 lport.setPin(Controller::DigitalPin::Four, args[0] != 0);
1456 }
1457
1458 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1459 // "joy0Fire"
executeJoy0Fire()1460 void DebuggerParser::executeJoy0Fire()
1461 {
1462 ControllerLowLevel lport(debugger.myOSystem.console().leftController());
1463 if(argCount == 0)
1464 lport.togglePin(Controller::DigitalPin::Six);
1465 else if(argCount == 1)
1466 lport.setPin(Controller::DigitalPin::Six, args[0] != 0);
1467 }
1468
1469 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1470 // "joy1Up"
executeJoy1Up()1471 void DebuggerParser::executeJoy1Up()
1472 {
1473 ControllerLowLevel rport(debugger.myOSystem.console().rightController());
1474 if(argCount == 0)
1475 rport.togglePin(Controller::DigitalPin::One);
1476 else if(argCount == 1)
1477 rport.setPin(Controller::DigitalPin::One, args[0] != 0);
1478 }
1479
1480 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1481 // "joy1Down"
executeJoy1Down()1482 void DebuggerParser::executeJoy1Down()
1483 {
1484 ControllerLowLevel rport(debugger.myOSystem.console().rightController());
1485 if(argCount == 0)
1486 rport.togglePin(Controller::DigitalPin::Two);
1487 else if(argCount == 1)
1488 rport.setPin(Controller::DigitalPin::Two, args[0] != 0);
1489 }
1490
1491 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1492 // "joy1Left"
executeJoy1Left()1493 void DebuggerParser::executeJoy1Left()
1494 {
1495 ControllerLowLevel rport(debugger.myOSystem.console().rightController());
1496 if(argCount == 0)
1497 rport.togglePin(Controller::DigitalPin::Three);
1498 else if(argCount == 1)
1499 rport.setPin(Controller::DigitalPin::Three, args[0] != 0);
1500 }
1501
1502 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1503 // "joy1Right"
executeJoy1Right()1504 void DebuggerParser::executeJoy1Right()
1505 {
1506 ControllerLowLevel rport(debugger.myOSystem.console().rightController());
1507 if(argCount == 0)
1508 rport.togglePin(Controller::DigitalPin::Four);
1509 else if(argCount == 1)
1510 rport.setPin(Controller::DigitalPin::Four, args[0] != 0);
1511 }
1512
1513 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1514 // "joy1Fire"
executeJoy1Fire()1515 void DebuggerParser::executeJoy1Fire()
1516 {
1517 ControllerLowLevel rport(debugger.myOSystem.console().rightController());
1518 if(argCount == 0)
1519 rport.togglePin(Controller::DigitalPin::Six);
1520 else if(argCount == 1)
1521 rport.setPin(Controller::DigitalPin::Six, args[0] != 0);
1522 }
1523
1524 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1525 // "jump"
executeJump()1526 void DebuggerParser::executeJump()
1527 {
1528 int line = -1;
1529 int address = args[0];
1530
1531 // The specific address we want may not exist (it may be part of a data section)
1532 // If so, scroll backward a little until we find it
1533 while(((line = debugger.cartDebug().addressToLine(address)) == -1) &&
1534 (address >= 0))
1535 address--;
1536
1537 if(line >= 0 && address >= 0)
1538 {
1539 debugger.rom().scrollTo(line);
1540 commandResult << "disassembly scrolled to address $" << Base::HEX4 << address;
1541 }
1542 else
1543 commandResult << "address $" << Base::HEX4 << args[0] << " doesn't exist";
1544 }
1545
1546 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1547 // "listBreaks"
executeListBreaks()1548 void DebuggerParser::executeListBreaks()
1549 {
1550 stringstream buf;
1551 int count = 0;
1552 uInt32 romBankCount = debugger.cartDebug().romBankCount();
1553
1554 for(const auto& bp : debugger.breakPoints().getBreakpoints())
1555 {
1556 if(romBankCount == 1)
1557 {
1558 buf << debugger.cartDebug().getLabel(bp.addr, true, 4) << " ";
1559 if(!(++count % 8)) buf << endl;
1560 }
1561 else
1562 {
1563 if(count % 6)
1564 buf << ", ";
1565 buf << debugger.cartDebug().getLabel(bp.addr, true, 4);
1566 if(bp.bank != 255)
1567 buf << " #" << int(bp.bank);
1568 else
1569 buf << " *";
1570 if(!(++count % 6)) buf << endl;
1571 }
1572 }
1573 if(count)
1574 commandResult << "breaks:" << endl << buf.str();
1575
1576 StringList conds = debugger.m6502().getCondBreakNames();
1577
1578 if(conds.size() > 0)
1579 {
1580 if(count)
1581 commandResult << endl;
1582 commandResult << "BreakIfs:" << endl;
1583 for(uInt32 i = 0; i < conds.size(); ++i)
1584 {
1585 commandResult << Base::toString(i) << ": " << conds[i];
1586 if(i != (conds.size() - 1)) commandResult << endl;
1587 }
1588 }
1589
1590 if(commandResult.str() == "")
1591 commandResult << "no breakpoints set";
1592 }
1593
1594 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1595 // "listConfig"
executeListConfig()1596 void DebuggerParser::executeListConfig()
1597 {
1598 if(argCount == 1)
1599 commandResult << debugger.cartDebug().listConfig(args[0]);
1600 else
1601 commandResult << debugger.cartDebug().listConfig();
1602 }
1603
1604 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1605 // "listFunctions"
executeListFunctions()1606 void DebuggerParser::executeListFunctions()
1607 {
1608 const Debugger::FunctionDefMap& functions = debugger.getFunctionDefMap();
1609
1610 if(functions.size() > 0)
1611 for(const auto& [name, cmd]: functions)
1612 commandResult << name << " -> " << cmd << endl;
1613 else
1614 commandResult << "no user-defined functions";
1615 }
1616
1617 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1618 // "listSaveStateIfs"
executeListSaveStateIfs()1619 void DebuggerParser::executeListSaveStateIfs()
1620 {
1621 ostringstream buf;
1622
1623 StringList conds = debugger.m6502().getCondSaveStateNames();
1624 if(conds.size() > 0)
1625 {
1626 commandResult << "saveStateIf:" << endl;
1627 for(uInt32 i = 0; i < conds.size(); ++i)
1628 {
1629 commandResult << Base::toString(i) << ": " << conds[i];
1630 if(i != (conds.size() - 1)) commandResult << endl;
1631 }
1632 }
1633
1634 if(commandResult.str() == "")
1635 commandResult << "no savestateifs defined";
1636 }
1637
1638 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1639 // "listTraps"
executeListTraps()1640 void DebuggerParser::executeListTraps()
1641 {
1642 StringList names = debugger.m6502().getCondTrapNames();
1643
1644 if(myTraps.size() != names.size())
1645 {
1646 commandResult << "Internal error! Different trap sizes.";
1647 return;
1648 }
1649
1650 if (names.size() > 0)
1651 {
1652 bool trapFound = false, trapifFound = false;
1653 for(uInt32 i = 0; i < names.size(); ++i)
1654 if(names[i] == "")
1655 trapFound = true;
1656 else
1657 trapifFound = true;
1658
1659 if(trapFound)
1660 listTraps(false);
1661 if(trapifFound)
1662 listTraps(true);
1663 }
1664 else
1665 commandResult << "no traps set";
1666 }
1667
1668 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1669 // "loadAllStates"
executeLoadAllStates()1670 void DebuggerParser::executeLoadAllStates()
1671 {
1672 debugger.loadAllStates();
1673 }
1674
1675 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1676 // "loadConfig"
executeLoadConfig()1677 void DebuggerParser::executeLoadConfig()
1678 {
1679 commandResult << debugger.cartDebug().loadConfigFile();
1680 }
1681
1682 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1683 // "loadState"
executeLoadState()1684 void DebuggerParser::executeLoadState()
1685 {
1686 if(args[0] >= 0 && args[0] <= 9)
1687 debugger.loadState(args[0]);
1688 else
1689 commandResult << red("invalid slot (must be 0-9)");
1690 }
1691
1692 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
executeLogBreaks()1693 void DebuggerParser::executeLogBreaks()
1694 {
1695 bool enable = !debugger.mySystem.m6502().getLogBreaks();
1696
1697 debugger.mySystem.m6502().setLogBreaks(enable);
1698 settings.setValue("dbg.logbreaks", enable);
1699 commandResult << "logBreaks " << (enable ? "enabled" : "disabled");
1700 }
1701
1702 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1703 // "n"
executeN()1704 void DebuggerParser::executeN()
1705 {
1706 if(argCount == 0)
1707 debugger.cpuDebug().toggleN();
1708 else if(argCount == 1)
1709 debugger.cpuDebug().setN(args[0]);
1710 }
1711
1712 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1713 // "palette"
executePalette()1714 void DebuggerParser::executePalette()
1715 {
1716 commandResult << debugger.tiaDebug().palette();
1717 }
1718
1719 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1720 // "pc"
executePc()1721 void DebuggerParser::executePc()
1722 {
1723 debugger.cpuDebug().setPC(args[0]);
1724 }
1725
1726 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1727 // "pCol"
executePCol()1728 void DebuggerParser::executePCol()
1729 {
1730 executeDirective(Device::PCOL);
1731 }
1732
1733 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1734 // "pGfx"
executePGfx()1735 void DebuggerParser::executePGfx()
1736 {
1737 executeDirective(Device::PGFX);
1738 }
1739
1740 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1741 // "print"
executePrint()1742 void DebuggerParser::executePrint()
1743 {
1744 commandResult << eval();
1745 }
1746
1747 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1748 // "ram"
executeRam()1749 void DebuggerParser::executeRam()
1750 {
1751 if(argCount == 0)
1752 commandResult << debugger.cartDebug().toString();
1753 else
1754 commandResult << debugger.setRAM(args);
1755 }
1756
1757 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1758 // "reset"
executeReset()1759 void DebuggerParser::executeReset()
1760 {
1761 debugger.reset();
1762 debugger.rom().invalidate();
1763
1764 ControllerLowLevel lport(debugger.myOSystem.console().leftController());
1765 ControllerLowLevel rport(debugger.myOSystem.console().rightController());
1766 lport.resetDigitalPins();
1767 rport.resetDigitalPins();
1768
1769 commandResult << "reset system";
1770 }
1771
1772 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1773 // "rewind"
executeRewind()1774 void DebuggerParser::executeRewind()
1775 {
1776 executeWinds(false);
1777 }
1778
1779 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1780 // "riot"
executeRiot()1781 void DebuggerParser::executeRiot()
1782 {
1783 commandResult << debugger.riotDebug().toString();
1784 }
1785
1786 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1787 // "rom"
executeRom()1788 void DebuggerParser::executeRom()
1789 {
1790 uInt16 addr = args[0];
1791 for(uInt32 i = 1; i < argCount; ++i)
1792 {
1793 if(!(debugger.patchROM(addr++, args[i])))
1794 {
1795 commandResult << red("patching ROM unsupported for this cart type");
1796 return;
1797 }
1798 }
1799
1800 // Normally the run() method calls loadConfig() on the debugger,
1801 // which results in all child widgets being redrawn.
1802 // The RomWidget is a special case, since we don't want to re-disassemble
1803 // any more than necessary. So we only do it by calling the following
1804 // method ...
1805 debugger.rom().invalidate();
1806
1807 commandResult << "changed " << (args.size() - 1) << " location(s)";
1808 }
1809
1810 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1811 // "row"
executeRow()1812 void DebuggerParser::executeRow()
1813 {
1814 executeDirective(Device::ROW);
1815 }
1816
1817 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1818 // "run"
executeRun()1819 void DebuggerParser::executeRun()
1820 {
1821 debugger.saveOldState();
1822 debugger.exit(false);
1823 commandResult << "_EXIT_DEBUGGER"; // See PromptWidget for more info
1824 }
1825
1826 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1827 // "runTo"
executeRunTo()1828 void DebuggerParser::executeRunTo()
1829 {
1830 const CartDebug& cartdbg = debugger.cartDebug();
1831 const CartDebug::DisassemblyList& list = cartdbg.disassembly().list;
1832
1833 debugger.saveOldState();
1834
1835 uInt32 count = 0, max_iterations = uInt32(list.size());
1836
1837 // Create a progress dialog box to show the progress searching through the
1838 // disassembly, since this may be a time-consuming operation
1839 ostringstream buf;
1840 ProgressDialog progress(debugger.baseDialog(), debugger.lfont());
1841
1842 buf << "runTo searching through " << max_iterations << " disassembled instructions"
1843 << progress.ELLIPSIS;
1844 progress.setMessage(buf.str());
1845 progress.setRange(0, max_iterations, 5);
1846 progress.open();
1847
1848 bool done = false;
1849 do {
1850 debugger.step(false);
1851
1852 // Update romlist to point to current PC
1853 int pcline = cartdbg.addressToLine(debugger.cpuDebug().pc());
1854 if(pcline >= 0)
1855 {
1856 const string& next = list[pcline].disasm;
1857 done = (BSPF::findIgnoreCase(next, argStrings[0]) != string::npos);
1858 }
1859 // Update the progress bar
1860 progress.incProgress();
1861 } while(!done && ++count < max_iterations && !progress.isCancelled());
1862
1863 progress.close();
1864
1865 if(done)
1866 commandResult
1867 << "found " << argStrings[0] << " in " << dec << count
1868 << " disassembled instructions";
1869 else
1870 commandResult
1871 << argStrings[0] << " not found in " << dec << count
1872 << " disassembled instructions";
1873 }
1874
1875 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1876 // "runToPc"
executeRunToPc()1877 void DebuggerParser::executeRunToPc()
1878 {
1879 const CartDebug& cartdbg = debugger.cartDebug();
1880 const CartDebug::DisassemblyList& list = cartdbg.disassembly().list;
1881
1882 debugger.saveOldState();
1883
1884 uInt32 count = 0;
1885 bool done = false;
1886 // Create a progress dialog box to show the progress searching through the
1887 // disassembly, since this may be a time-consuming operation
1888 ostringstream buf;
1889 ProgressDialog progress(debugger.baseDialog(), debugger.lfont());
1890
1891 buf << " runTo PC running" << progress.ELLIPSIS << " ";
1892 progress.setMessage(buf.str());
1893 progress.setRange(0, 100000, 5);
1894 progress.open();
1895
1896 do {
1897 debugger.step(false);
1898
1899 // Update romlist to point to current PC
1900 int pcline = cartdbg.addressToLine(debugger.cpuDebug().pc());
1901 done = (pcline >= 0) && (list[pcline].address == args[0]);
1902 progress.incProgress();
1903 ++count;
1904 } while(!done && !progress.isCancelled());
1905 progress.close();
1906
1907 if(done)
1908 commandResult
1909 << "Set PC to $" << Base::HEX4 << args[0] << " in "
1910 << dec << count << " instructions";
1911 else
1912 commandResult
1913 << "PC $" << Base::HEX4 << args[0] << " not reached or found in "
1914 << dec << count << " instructions";
1915 }
1916
1917 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1918 // "s"
executeS()1919 void DebuggerParser::executeS()
1920 {
1921 debugger.cpuDebug().setSP(uInt8(args[0]));
1922 }
1923
1924 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1925 // "save"
executeSave()1926 void DebuggerParser::executeSave()
1927 {
1928 DebuggerDialog* dlg = debugger.myDialog;
1929 const string fileName = dlg->instance().userDir().getPath() + cartName() + ".script";
1930
1931 if(argCount)
1932 {
1933 if(argStrings[0] == "?")
1934 {
1935 BrowserDialog::show(dlg, "Save Workbench as", fileName,
1936 BrowserDialog::Mode::FileSave,
1937 [this, dlg](bool OK, const FilesystemNode& node)
1938 {
1939 if(OK)
1940 dlg->prompt().print(saveScriptFile(node.getPath()) + '\n');
1941 dlg->prompt().printPrompt();
1942 });
1943 // avoid printing a new prompt
1944 commandResult.str("_NO_PROMPT");
1945 }
1946 else
1947 commandResult << saveScriptFile(argStrings[0]);
1948 }
1949 else
1950 commandResult << saveScriptFile(fileName);
1951 }
1952
1953 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1954 // "saveAccess"
executeSaveAccess()1955 void DebuggerParser::executeSaveAccess()
1956 {
1957 if(argCount && argStrings[0] == "?")
1958 {
1959 DebuggerDialog* dlg = debugger.myDialog;
1960
1961 BrowserDialog::show(dlg, "Save Access Counters as",
1962 dlg->instance().userDir().getPath() + cartName() + ".csv",
1963 BrowserDialog::Mode::FileSave,
1964 [this, dlg](bool OK, const FilesystemNode& node)
1965 {
1966 if(OK)
1967 dlg->prompt().print(debugger.cartDebug().saveAccessFile(node.getPath()) + '\n');
1968 dlg->prompt().printPrompt();
1969 });
1970 // avoid printing a new prompt
1971 commandResult.str("_NO_PROMPT");
1972 }
1973 else
1974 commandResult << debugger.cartDebug().saveAccessFile();
1975 }
1976
1977 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1978 // "saveConfig"
executeSaveConfig()1979 void DebuggerParser::executeSaveConfig()
1980 {
1981 commandResult << debugger.cartDebug().saveConfigFile();
1982 }
1983
1984 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1985 // "saveDis"
executeSaveDisassembly()1986 void DebuggerParser::executeSaveDisassembly()
1987 {
1988 if(argCount && argStrings[0] == "?")
1989 {
1990 DebuggerDialog* dlg = debugger.myDialog;
1991
1992 BrowserDialog::show(dlg, "Save Disassembly as",
1993 dlg->instance().userDir().getPath() + cartName() + ".asm",
1994 BrowserDialog::Mode::FileSave,
1995 [this, dlg](bool OK, const FilesystemNode& node)
1996 {
1997 if(OK)
1998 dlg->prompt().print(debugger.cartDebug().saveDisassembly(node.getPath()) + '\n');
1999 dlg->prompt().printPrompt();
2000 });
2001 // avoid printing a new prompt
2002 commandResult.str("_NO_PROMPT");
2003 }
2004 else
2005 commandResult << debugger.cartDebug().saveDisassembly();
2006 }
2007
2008 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2009 // "saveRom"
executeSaveRom()2010 void DebuggerParser::executeSaveRom()
2011 {
2012 if(argCount && argStrings[0] == "?")
2013 {
2014 DebuggerDialog* dlg = debugger.myDialog;
2015
2016 BrowserDialog::show(dlg, "Save ROM as",
2017 dlg->instance().userDir().getPath() + cartName() + ".a26",
2018 BrowserDialog::Mode::FileSave,
2019 [this, dlg](bool OK, const FilesystemNode& node)
2020 {
2021 if(OK)
2022 dlg->prompt().print(debugger.cartDebug().saveRom(node.getPath()) + '\n');
2023 dlg->prompt().printPrompt();
2024 });
2025 // avoid printing a new prompt
2026 commandResult.str("_NO_PROMPT");
2027 }
2028 else
2029 commandResult << debugger.cartDebug().saveRom();
2030 }
2031
2032 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2033 // "saveSes"
executeSaveSes()2034 void DebuggerParser::executeSaveSes()
2035 {
2036 ostringstream filename;
2037 auto timeinfo = BSPF::localTime();
2038 filename << std::put_time(&timeinfo, "session_%F_%H-%M-%S.txt");
2039
2040 if(argCount && argStrings[0] == "?")
2041 {
2042 DebuggerDialog* dlg = debugger.myDialog;
2043
2044 BrowserDialog::show(dlg, "Save Session as",
2045 dlg->instance().userDir().getPath() + filename.str(),
2046 BrowserDialog::Mode::FileSave,
2047 [this, dlg](bool OK, const FilesystemNode& node)
2048 {
2049 if(OK)
2050 dlg->prompt().print(debugger.prompt().saveBuffer(node) + '\n');
2051 dlg->prompt().printPrompt();
2052 });
2053 // avoid printing a new prompt
2054 commandResult.str("_NO_PROMPT");
2055 }
2056 else
2057 {
2058 ostringstream path;
2059
2060 if(argCount)
2061 path << argStrings[0];
2062 else
2063 path << debugger.myOSystem.userDir() << filename.str();
2064
2065 commandResult << debugger.prompt().saveBuffer(FilesystemNode(path.str()));
2066 }
2067 }
2068
2069 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2070 // "saveSnap"
executeSaveSnap()2071 void DebuggerParser::executeSaveSnap()
2072 {
2073 debugger.tiaOutput().saveSnapshot(execDepth, execPrefix);
2074 }
2075
2076 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2077 // "saveAllStates"
executeSaveAllStates()2078 void DebuggerParser::executeSaveAllStates()
2079 {
2080 debugger.saveAllStates();
2081 }
2082
2083 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2084 // "saveState"
executeSaveState()2085 void DebuggerParser::executeSaveState()
2086 {
2087 if(args[0] >= 0 && args[0] <= 9)
2088 debugger.saveState(args[0]);
2089 else
2090 commandResult << red("invalid slot (must be 0-9)");
2091 }
2092
2093 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2094 // "saveStateIf"
executeSaveStateIf()2095 void DebuggerParser::executeSaveStateIf()
2096 {
2097 int res = YaccParser::parse(argStrings[0].c_str());
2098 if(res == 0)
2099 {
2100 string condition = argStrings[0];
2101 for(uInt32 i = 0; i < debugger.m6502().getCondSaveStateNames().size(); ++i)
2102 {
2103 if(condition == debugger.m6502().getCondSaveStateNames()[i])
2104 {
2105 args[0] = i;
2106 executeDelSaveStateIf();
2107 return;
2108 }
2109 }
2110 uInt32 ret = debugger.m6502().addCondSaveState(
2111 YaccParser::getResult(), argStrings[0]);
2112 commandResult << "added saveStateIf " << Base::toString(ret);
2113 }
2114 else
2115 commandResult << red("invalid expression");
2116 }
2117
2118 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2119 // "scanLine"
executeScanLine()2120 void DebuggerParser::executeScanLine()
2121 {
2122 int count = 1;
2123 if(argCount != 0) count = args[0];
2124 debugger.nextScanline(count);
2125 commandResult << "advanced " << dec << count << " scanLine(s)";
2126 }
2127
2128 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2129 // "step"
executeStep()2130 void DebuggerParser::executeStep()
2131 {
2132 commandResult
2133 << "executed " << dec << debugger.step() << " cycles";
2134 }
2135
2136 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2137 // "stepWhile"
executeStepWhile()2138 void DebuggerParser::executeStepWhile()
2139 {
2140 int res = YaccParser::parse(argStrings[0].c_str());
2141 if(res != 0) {
2142 commandResult << red("invalid expression");
2143 return;
2144 }
2145 Expression* expr = YaccParser::getResult();
2146 int ncycles = 0;
2147 uInt32 count = 0;
2148
2149 // Create a progress dialog box to show the progress searching through the
2150 // disassembly, since this may be a time-consuming operation
2151 ostringstream buf;
2152 ProgressDialog progress(debugger.baseDialog(), debugger.lfont());
2153
2154 buf << "stepWhile running through disassembled instructions"
2155 << progress.ELLIPSIS;
2156 progress.setMessage(buf.str());
2157 progress.setRange(0, 100000, 5);
2158 progress.open();
2159
2160 do {
2161 ncycles += debugger.step(false);
2162
2163 progress.incProgress();
2164 ++count;
2165 } while (expr->evaluate() && !progress.isCancelled());
2166
2167 progress.close();
2168 commandResult << "executed " << ncycles << " cycles";
2169 }
2170
2171 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2172 // "tia"
executeTia()2173 void DebuggerParser::executeTia()
2174 {
2175 commandResult << debugger.tiaDebug().toString();
2176 }
2177
2178 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2179 // "trace"
executeTrace()2180 void DebuggerParser::executeTrace()
2181 {
2182 commandResult << "executed " << dec << debugger.trace() << " cycles";
2183 }
2184
2185 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2186 // "trap"
executeTrap()2187 void DebuggerParser::executeTrap()
2188 {
2189 executeTraps(true, true, "trap");
2190 }
2191
2192 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2193 // "trapIf"
executeTrapIf()2194 void DebuggerParser::executeTrapIf()
2195 {
2196 executeTraps(true, true, "trapIf", true);
2197 }
2198
2199 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2200 // "trapRead"
executeTrapRead()2201 void DebuggerParser::executeTrapRead()
2202 {
2203 executeTraps(true, false, "trapRead");
2204 }
2205
2206 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2207 // "trapReadIf"
executeTrapReadIf()2208 void DebuggerParser::executeTrapReadIf()
2209 {
2210 executeTraps(true, false, "trapReadIf", true);
2211 }
2212
2213 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2214 // "trapWrite"
executeTrapWrite()2215 void DebuggerParser::executeTrapWrite()
2216 {
2217 executeTraps(false, true, "trapWrite");
2218 }
2219
2220 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2221 // "trapWriteIf"
executeTrapWriteIf()2222 void DebuggerParser::executeTrapWriteIf()
2223 {
2224 executeTraps(false, true, "trapWriteIf", true);
2225 }
2226
2227 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2228 // Wrapper function for trap(if)s
executeTraps(bool read,bool write,const string & command,bool hasCond)2229 void DebuggerParser::executeTraps(bool read, bool write, const string& command,
2230 bool hasCond)
2231 {
2232 uInt32 ofs = hasCond ? 1 : 0;
2233 uInt32 begin = args[ofs];
2234 uInt32 end = argCount == 2 + ofs ? args[1 + ofs] : begin;
2235
2236 if(argCount < 1 + ofs)
2237 {
2238 outputCommandError("missing required argument(s)", myCommand);
2239 return;
2240 }
2241 if(argCount > 2 + ofs)
2242 {
2243 outputCommandError("too many arguments", myCommand);
2244 return;
2245 }
2246 if(begin > 0xFFFF || end > 0xFFFF)
2247 {
2248 commandResult << red("invalid word argument(s) (must be 0-$ffff)");
2249 return;
2250 }
2251 if(begin > end)
2252 {
2253 commandResult << red("start address must be <= end address");
2254 return;
2255 }
2256
2257 // base addresses of mirrors
2258 uInt32 beginRead = debugger.getBaseAddress(begin, true);
2259 uInt32 endRead = debugger.getBaseAddress(end, true);
2260 uInt32 beginWrite = debugger.getBaseAddress(begin, false);
2261 uInt32 endWrite = debugger.getBaseAddress(end, false);
2262 stringstream conditionBuf;
2263
2264 // parenthesize provided and address range condition(s) (begin)
2265 if(hasCond)
2266 conditionBuf << "(" << argStrings[0] << ")&&(";
2267
2268 // add address range condition(s) to provided condition
2269 if(read)
2270 {
2271 if(beginRead != endRead)
2272 conditionBuf << "__lastBaseRead>=" << Base::toString(beginRead) << "&&__lastBaseRead<=" << Base::toString(endRead);
2273 else
2274 conditionBuf << "__lastBaseRead==" << Base::toString(beginRead);
2275 }
2276 if(read && write)
2277 conditionBuf << "||";
2278 if(write)
2279 {
2280 if(beginWrite != endWrite)
2281 conditionBuf << "__lastBaseWrite>=" << Base::toString(beginWrite) << "&&__lastBaseWrite<=" << Base::toString(endWrite);
2282 else
2283 conditionBuf << "__lastBaseWrite==" << Base::toString(beginWrite);
2284 }
2285 // parenthesize provided condition (end)
2286 if(hasCond)
2287 conditionBuf << ")";
2288
2289 const string condition = conditionBuf.str();
2290
2291 int res = YaccParser::parse(condition.c_str());
2292 if(res == 0)
2293 {
2294 // duplicates will remove each other
2295 bool add = true;
2296 for(uInt32 i = 0; i < myTraps.size(); ++i)
2297 {
2298 if(myTraps[i]->begin == begin && myTraps[i]->end == end &&
2299 myTraps[i]->read == read && myTraps[i]->write == write &&
2300 myTraps[i]->condition == condition)
2301 {
2302 if(debugger.m6502().delCondTrap(i))
2303 {
2304 add = false;
2305 // @sa666666: please check this:
2306 Vec::removeAt(myTraps, i);
2307 commandResult << "removed trap " << Base::toString(i);
2308 break;
2309 }
2310 commandResult << "Internal error! Duplicate trap removal failed!";
2311 return;
2312 }
2313 }
2314 if(add)
2315 {
2316 uInt32 ret = debugger.m6502().addCondTrap(
2317 YaccParser::getResult(), hasCond ? argStrings[0] : "");
2318 commandResult << "added trap " << Base::toString(ret);
2319
2320 myTraps.emplace_back(make_unique<Trap>(read, write, begin, end, condition));
2321 }
2322
2323 for(uInt32 addr = begin; addr <= end; ++addr)
2324 executeTrapRW(addr, read, write, add);
2325 }
2326 else
2327 {
2328 commandResult << red("invalid expression");
2329 }
2330 }
2331
2332 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2333 // wrapper function for trap(if)/trapRead(if)/trapWrite(if) commands
executeTrapRW(uInt32 addr,bool read,bool write,bool add)2334 void DebuggerParser::executeTrapRW(uInt32 addr, bool read, bool write, bool add)
2335 {
2336 switch(debugger.cartDebug().addressType(addr))
2337 {
2338 case CartDebug::AddrType::TIA:
2339 {
2340 for(uInt32 i = 0; i <= 0xFFFF; ++i)
2341 {
2342 if((i & 0x1080) == 0x0000)
2343 {
2344 // @sa666666: This seems wrong. E.g. trapRead 40 4f will never trigger
2345 if(read && (i & 0x000F) == (addr & 0x000F))
2346 add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
2347 if(write && (i & 0x003F) == (addr & 0x003F))
2348 add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
2349 }
2350 }
2351 break;
2352 }
2353 case CartDebug::AddrType::IO:
2354 {
2355 for(uInt32 i = 0; i <= 0xFFFF; ++i)
2356 {
2357 if((i & 0x1280) == 0x0280 && (i & 0x029F) == (addr & 0x029F))
2358 {
2359 if(read)
2360 add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
2361 if(write)
2362 add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
2363 }
2364 }
2365 break;
2366 }
2367 case CartDebug::AddrType::ZPRAM:
2368 {
2369 for(uInt32 i = 0; i <= 0xFFFF; ++i)
2370 {
2371 if((i & 0x1280) == 0x0080 && (i & 0x00FF) == (addr & 0x00FF))
2372 {
2373 if(read)
2374 add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
2375 if(write)
2376 add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
2377 }
2378 }
2379 break;
2380 }
2381 case CartDebug::AddrType::ROM:
2382 {
2383 if(addr >= 0x1000 && addr <= 0xFFFF)
2384 {
2385 for(uInt32 i = 0x1000; i <= 0xFFFF; ++i)
2386 {
2387 if((i % 0x2000 >= 0x1000) && (i & 0x0FFF) == (addr & 0x0FFF))
2388 {
2389 if(read)
2390 add ? debugger.addReadTrap(i) : debugger.removeReadTrap(i);
2391 if(write)
2392 add ? debugger.addWriteTrap(i) : debugger.removeWriteTrap(i);
2393 }
2394 }
2395 }
2396 break;
2397 }
2398 }
2399 }
2400
2401 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2402 // "type"
executeType()2403 void DebuggerParser::executeType()
2404 {
2405 uInt32 beg = args[0];
2406 uInt32 end = argCount >= 2 ? args[1] : beg;
2407 if(beg > end) std::swap(beg, end);
2408
2409 for(uInt32 i = beg; i <= end; ++i)
2410 {
2411 commandResult << Base::HEX4 << i << ": ";
2412 debugger.cartDebug().accessTypeAsString(commandResult, i);
2413 commandResult << endl;
2414 }
2415 }
2416
2417 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2418 // "uHex"
executeUHex()2419 void DebuggerParser::executeUHex()
2420 {
2421 bool enable = !Base::hexUppercase();
2422 Base::setHexUppercase(enable);
2423
2424 settings.setValue("dbg.uHex", enable);
2425 debugger.rom().invalidate();
2426
2427 commandResult << "uppercase HEX " << (enable ? "enabled" : "disabled");
2428 }
2429
2430 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2431 // "undef"
executeUndef()2432 void DebuggerParser::executeUndef()
2433 {
2434 if(debugger.cartDebug().removeLabel(argStrings[0]))
2435 {
2436 debugger.rom().invalidate();
2437 commandResult << argStrings[0] + " now undefined";
2438 }
2439 else
2440 commandResult << red("no such label");
2441 }
2442
2443 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2444 // "unwind"
executeUnwind()2445 void DebuggerParser::executeUnwind()
2446 {
2447 executeWinds(true);
2448 }
2449
2450 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2451 // "v"
executeV()2452 void DebuggerParser::executeV()
2453 {
2454 if(argCount == 0)
2455 debugger.cpuDebug().toggleV();
2456 else if(argCount == 1)
2457 debugger.cpuDebug().setV(args[0]);
2458 }
2459
2460 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2461 // "watch"
executeWatch()2462 void DebuggerParser::executeWatch()
2463 {
2464 myWatches.push_back(argStrings[0]);
2465 commandResult << "added watch \"" << argStrings[0] << "\"";
2466 }
2467
2468 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2469 // wrapper function for rewind/unwind commands
executeWinds(bool unwind)2470 void DebuggerParser::executeWinds(bool unwind)
2471 {
2472 uInt16 states;
2473 string type = unwind ? "unwind" : "rewind";
2474 string message;
2475
2476 if(argCount == 0)
2477 states = 1;
2478 else
2479 states = args[0];
2480
2481 uInt16 winds = unwind ? debugger.unwindStates(states, message) : debugger.rewindStates(states, message);
2482 if(winds > 0)
2483 {
2484 debugger.rom().invalidate();
2485 commandResult << type << " by " << winds << " state" << (winds > 1 ? "s" : "");
2486 commandResult << " (~" << message << ")";
2487 }
2488 else
2489 commandResult << "no states left to " << type;
2490 }
2491
2492 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2493 // "x"
executeX()2494 void DebuggerParser::executeX()
2495 {
2496 debugger.cpuDebug().setX(uInt8(args[0]));
2497 }
2498
2499 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2500 // "y"
executeY()2501 void DebuggerParser::executeY()
2502 {
2503 debugger.cpuDebug().setY(uInt8(args[0]));
2504 }
2505
2506 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2507 // "z"
executeZ()2508 void DebuggerParser::executeZ()
2509 {
2510 if(argCount == 0)
2511 debugger.cpuDebug().toggleZ();
2512 else if(argCount == 1)
2513 debugger.cpuDebug().setZ(args[0]);
2514 }
2515
2516 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2517 // List of all commands available to the parser
2518 DebuggerParser::CommandArray DebuggerParser::commands = { {
2519 {
2520 "a",
2521 "Set Accumulator to <value>",
2522 "Valid value is 0 - ff\nExample: a ff, a #10",
2523 true,
2524 true,
2525 { Parameters::ARG_BYTE, Parameters::ARG_END_ARGS },
2526 std::mem_fn(&DebuggerParser::executeA)
2527 },
2528
2529 {
2530 "aud",
2531 "Mark 'AUD' range in disassembly",
2532 "Start and end of range required\nExample: aud f000 f010",
2533 true,
2534 false,
2535 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
2536 std::mem_fn(&DebuggerParser::executeAud)
2537 },
2538
2539 {
2540 "autoSave",
2541 "Toggle automatic saving of commands (see 'save')",
2542 "Example: autoSave (no parameters)",
2543 false,
2544 true,
2545 { Parameters::ARG_END_ARGS },
2546 std::mem_fn(&DebuggerParser::executeAutoSave)
2547 },
2548
2549 {
2550 "base",
2551 "Set default number base to <base>",
2552 "Base is #2, #10, #16, bin, dec or hex\nExample: base hex",
2553 true,
2554 true,
2555 { Parameters::ARG_BASE_SPCL, Parameters::ARG_END_ARGS },
2556 std::mem_fn(&DebuggerParser::executeBase)
2557 },
2558
2559 {
2560 "bCol",
2561 "Mark 'bCol' range in disassembly",
2562 "Start and end of range required\nExample: bCol f000 f010",
2563 true,
2564 false,
2565 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
2566 std::mem_fn(&DebuggerParser::executeBCol)
2567 },
2568
2569
2570 {
2571 "break",
2572 "Break at <address> and <bank>",
2573 "Set/clear breakpoint on address (and all mirrors) and bank\nDefault are current PC and bank, valid address is 0 - ffff\n"
2574 "Example: break, break f000, break 7654 3\n break ff00 ff (= all banks)",
2575 false,
2576 true,
2577 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
2578 std::mem_fn(&DebuggerParser::executeBreak)
2579 },
2580
2581 {
2582 "breakIf",
2583 "Set/clear breakpoint on <condition>",
2584 "Condition can include multiple items, see documentation\nExample: breakIf _scan>100",
2585 true,
2586 true,
2587 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
2588 std::mem_fn(&DebuggerParser::executeBreakIf)
2589 },
2590
2591 {
2592 "breakLabel",
2593 "Set/clear breakpoint on <address> (no mirrors, all banks)",
2594 "Example: breakLabel, breakLabel MainLoop",
2595 false,
2596 true,
2597 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
2598 std::mem_fn(&DebuggerParser::executeBreakLabel)
2599 },
2600
2601 {
2602 "c",
2603 "Carry Flag: set (0 or 1), or toggle (no arg)",
2604 "Example: c, c 0, c 1",
2605 false,
2606 true,
2607 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
2608 std::mem_fn(&DebuggerParser::executeC)
2609 },
2610
2611 {
2612 "cheat",
2613 "Use a cheat code (see manual for cheat types)",
2614 "Example: cheat 0040, cheat abff00",
2615 false,
2616 false,
2617 { Parameters::ARG_LABEL, Parameters::ARG_END_ARGS },
2618 std::mem_fn(&DebuggerParser::executeCheat)
2619 },
2620
2621 {
2622 "clearBreaks",
2623 "Clear all breakpoints",
2624 "Example: clearBreaks (no parameters)",
2625 false,
2626 true,
2627 { Parameters::ARG_END_ARGS },
2628 std::mem_fn(&DebuggerParser::executeClearBreaks)
2629 },
2630
2631 {
2632 "clearConfig",
2633 "Clear Distella config directives [bank xx]",
2634 "Example: clearConfig 0, clearConfig 1",
2635 false,
2636 false,
2637 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
2638 std::mem_fn(&DebuggerParser::executeClearConfig)
2639 },
2640
2641 {
2642 "clearHistory",
2643 "Clear the prompt history",
2644 "Example: clearhisotry (no parameters)",
2645 false,
2646 true,
2647 { Parameters::ARG_END_ARGS },
2648 std::mem_fn(&DebuggerParser::executeClearHistory)
2649 },
2650
2651 {
2652 "clearSaveStateIfs",
2653 "Clear all saveState points",
2654 "Example: ClearSaveStateIfss (no parameters)",
2655 false,
2656 true,
2657 { Parameters::ARG_END_ARGS },
2658 std::mem_fn(&DebuggerParser::executeClearSaveStateIfs)
2659 },
2660
2661 {
2662 "clearTraps",
2663 "Clear all traps",
2664 "All traps cleared, including any mirrored ones\nExample: clearTraps (no parameters)",
2665 false,
2666 false,
2667 { Parameters::ARG_END_ARGS },
2668 std::mem_fn(&DebuggerParser::executeClearTraps)
2669 },
2670
2671 {
2672 "clearWatches",
2673 "Clear all watches",
2674 "Example: clearWatches (no parameters)",
2675 false,
2676 false,
2677 { Parameters::ARG_END_ARGS },
2678 std::mem_fn(&DebuggerParser::executeClearWatches)
2679 },
2680
2681 {
2682 "cls",
2683 "Clear prompt area of text",
2684 "Completely clears screen, but keeps history of commands",
2685 false,
2686 false,
2687 { Parameters::ARG_END_ARGS },
2688 std::mem_fn(&DebuggerParser::executeCls)
2689 },
2690
2691 {
2692 "code",
2693 "Mark 'CODE' range in disassembly",
2694 "Start and end of range required\nExample: code f000 f010",
2695 true,
2696 false,
2697 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
2698 std::mem_fn(&DebuggerParser::executeCode)
2699 },
2700
2701 {
2702 "col",
2703 "Mark 'COL' range in disassembly",
2704 "Start and end of range required\nExample: col f000 f010",
2705 true,
2706 false,
2707 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
2708 std::mem_fn(&DebuggerParser::executeCol)
2709 },
2710
2711 {
2712 "colorTest",
2713 "Show value xx as TIA color",
2714 "Shows a color swatch for the given value\nExample: colorTest 1f",
2715 true,
2716 false,
2717 { Parameters::ARG_BYTE, Parameters::ARG_END_ARGS },
2718 std::mem_fn(&DebuggerParser::executeColorTest)
2719 },
2720
2721 {
2722 "d",
2723 "Decimal Flag: set (0 or 1), or toggle (no arg)",
2724 "Example: d, d 0, d 1",
2725 false,
2726 true,
2727 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
2728 std::mem_fn(&DebuggerParser::executeD)
2729 },
2730
2731 {
2732 "data",
2733 "Mark 'DATA' range in disassembly",
2734 "Start and end of range required\nExample: data f000 f010",
2735 true,
2736 false,
2737 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
2738 std::mem_fn(&DebuggerParser::executeData)
2739 },
2740
2741 {
2742 "debugColors",
2743 "Show Fixed Debug Colors information",
2744 "Example: debugColors (no parameters)",
2745 false,
2746 false,
2747 { Parameters::ARG_END_ARGS },
2748 std::mem_fn(&DebuggerParser::executeDebugColors)
2749 },
2750
2751 {
2752 "define",
2753 "Define label xx for address yy",
2754 "Example: define LABEL1 f100",
2755 true,
2756 true,
2757 { Parameters::ARG_LABEL, Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
2758 std::mem_fn(&DebuggerParser::executeDefine)
2759 },
2760
2761 {
2762 "delBreakIf",
2763 "Delete conditional breakIf <xx>",
2764 "Example: delBreakIf 0",
2765 true,
2766 false,
2767 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
2768 std::mem_fn(&DebuggerParser::executeDelBreakIf)
2769 },
2770
2771 {
2772 "delFunction",
2773 "Delete function with label xx",
2774 "Example: delFunction FUNC1",
2775 true,
2776 false,
2777 { Parameters::ARG_LABEL, Parameters::ARG_END_ARGS },
2778 std::mem_fn(&DebuggerParser::executeDelFunction)
2779 },
2780
2781 {
2782 "delSaveStateIf",
2783 "Delete conditional saveState point <xx>",
2784 "Example: delSaveStateIf 0",
2785 true,
2786 false,
2787 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
2788 std::mem_fn(&DebuggerParser::executeDelSaveStateIf)
2789 },
2790
2791 {
2792 "delTrap",
2793 "Delete trap <xx>",
2794 "Example: delTrap 0",
2795 true,
2796 false,
2797 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
2798 std::mem_fn(&DebuggerParser::executeDelTrap)
2799 },
2800
2801 {
2802 "delWatch",
2803 "Delete watch <xx>",
2804 "Example: delWatch 0",
2805 true,
2806 false,
2807 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
2808 std::mem_fn(&DebuggerParser::executeDelWatch)
2809 },
2810
2811 {
2812 "disAsm",
2813 "Disassemble address xx [yy lines] (default=PC)",
2814 "Disassembles from starting address <xx> (default=PC) for <yy> lines\n"
2815 "Example: disAsm, disAsm f000 100",
2816 false,
2817 false,
2818 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
2819 std::mem_fn(&DebuggerParser::executeDisAsm)
2820 },
2821
2822 {
2823 "dump",
2824 "Dump data at address <xx> [to yy] [1: memory; 2: CPU state; 4: input regs] [?]",
2825 "Example:\n"
2826 " dump f000 - dumps 128 bytes from f000\n"
2827 " dump f000 f0ff - dumps all bytes from f000 to f0ff\n"
2828 " dump f000 f0ff 7 - dumps all bytes from f000 to f0ff,\n"
2829 " CPU state and input registers into a file in user dir,\n"
2830 " dump f000 f0ff 7 ? - same, but with a browser dialog\n",
2831 true,
2832 false,
2833 { Parameters::ARG_WORD, Parameters::ARG_WORD, Parameters::ARG_BYTE, Parameters::ARG_LABEL },
2834 std::mem_fn(&DebuggerParser::executeDump)
2835 },
2836
2837 {
2838 "exec",
2839 "Execute script file <xx> [prefix]",
2840 "Example: exec script.dat, exec auto.txt",
2841 true,
2842 true,
2843 { Parameters::ARG_FILE, Parameters::ARG_LABEL, Parameters::ARG_MULTI_BYTE },
2844 std::mem_fn(&DebuggerParser::executeExec)
2845 },
2846
2847 {
2848 "exitRom",
2849 "Exit emulator, return to ROM launcher",
2850 "Self-explanatory",
2851 false,
2852 false,
2853 { Parameters::ARG_END_ARGS },
2854 std::mem_fn(&DebuggerParser::executeExitRom)
2855 },
2856
2857 {
2858 "frame",
2859 "Advance emulation by <xx> frames (default=1)",
2860 "Example: frame, frame 100",
2861 false,
2862 true,
2863 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
2864 std::mem_fn(&DebuggerParser::executeFrame)
2865 },
2866
2867 {
2868 "function",
2869 "Define function name xx for expression yy",
2870 "Example: function FUNC1 { ... }",
2871 true,
2872 false,
2873 { Parameters::ARG_LABEL, Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
2874 std::mem_fn(&DebuggerParser::executeFunction)
2875 },
2876
2877 {
2878 "gfx",
2879 "Mark 'GFX' range in disassembly",
2880 "Start and end of range required\nExample: gfx f000 f010",
2881 true,
2882 false,
2883 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
2884 std::mem_fn(&DebuggerParser::executeGfx)
2885 },
2886
2887 {
2888 "help",
2889 "help <command>",
2890 "Show all commands, or give function for help on that command\n"
2891 "Example: help, help code",
2892 false,
2893 false,
2894 { Parameters::ARG_LABEL, Parameters::ARG_END_ARGS },
2895 std::mem_fn(&DebuggerParser::executeHelp)
2896 },
2897
2898 {
2899 "joy0Up",
2900 "Set joystick 0 up direction to value <x> (0 or 1), or toggle (no arg)",
2901 "Example: joy0Up 0",
2902 false,
2903 true,
2904 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
2905 std::mem_fn(&DebuggerParser::executeJoy0Up)
2906 },
2907
2908 {
2909 "joy0Down",
2910 "Set joystick 0 down direction to value <x> (0 or 1), or toggle (no arg)",
2911 "Example: joy0Down 0",
2912 false,
2913 true,
2914 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
2915 std::mem_fn(&DebuggerParser::executeJoy0Down)
2916 },
2917
2918 {
2919 "joy0Left",
2920 "Set joystick 0 left direction to value <x> (0 or 1), or toggle (no arg)",
2921 "Example: joy0Left 0",
2922 false,
2923 true,
2924 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
2925 std::mem_fn(&DebuggerParser::executeJoy0Left)
2926 },
2927
2928 {
2929 "joy0Right",
2930 "Set joystick 0 right direction to value <x> (0 or 1), or toggle (no arg)",
2931 "Example: joy0Left 0",
2932 false,
2933 true,
2934 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
2935 std::mem_fn(&DebuggerParser::executeJoy0Right)
2936 },
2937
2938 {
2939 "joy0Fire",
2940 "Set joystick 0 fire button to value <x> (0 or 1), or toggle (no arg)",
2941 "Example: joy0Fire 0",
2942 false,
2943 true,
2944 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
2945 std::mem_fn(&DebuggerParser::executeJoy0Fire)
2946 },
2947
2948 {
2949 "joy1Up",
2950 "Set joystick 1 up direction to value <x> (0 or 1), or toggle (no arg)",
2951 "Example: joy1Up 0",
2952 false,
2953 true,
2954 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
2955 std::mem_fn(&DebuggerParser::executeJoy1Up)
2956 },
2957
2958 {
2959 "joy1Down",
2960 "Set joystick 1 down direction to value <x> (0 or 1), or toggle (no arg)",
2961 "Example: joy1Down 0",
2962 false,
2963 true,
2964 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
2965 std::mem_fn(&DebuggerParser::executeJoy1Down)
2966 },
2967
2968 {
2969 "joy1Left",
2970 "Set joystick 1 left direction to value <x> (0 or 1), or toggle (no arg)",
2971 "Example: joy1Left 0",
2972 false,
2973 true,
2974 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
2975 std::mem_fn(&DebuggerParser::executeJoy1Left)
2976 },
2977
2978 {
2979 "joy1Right",
2980 "Set joystick 1 right direction to value <x> (0 or 1), or toggle (no arg)",
2981 "Example: joy1Left 0",
2982 false,
2983 true,
2984 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
2985 std::mem_fn(&DebuggerParser::executeJoy1Right)
2986 },
2987
2988 {
2989 "joy1Fire",
2990 "Set joystick 1 fire button to value <x> (0 or 1), or toggle (no arg)",
2991 "Example: joy1Fire 0",
2992 false,
2993 true,
2994 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
2995 std::mem_fn(&DebuggerParser::executeJoy1Fire)
2996 },
2997
2998 {
2999 "jump",
3000 "Scroll disassembly to address xx",
3001 "Moves disassembly listing to address <xx>\nExample: jump f400",
3002 true,
3003 false,
3004 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
3005 std::mem_fn(&DebuggerParser::executeJump)
3006 },
3007
3008 {
3009 "listBreaks",
3010 "List breakpoints",
3011 "Example: listBreaks (no parameters)",
3012 false,
3013 false,
3014 { Parameters::ARG_END_ARGS },
3015 std::mem_fn(&DebuggerParser::executeListBreaks)
3016 },
3017
3018 {
3019 "listConfig",
3020 "List Distella config directives [bank xx]",
3021 "Example: listConfig 0, listConfig 1",
3022 false,
3023 false,
3024 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
3025 std::mem_fn(&DebuggerParser::executeListConfig)
3026 },
3027
3028 {
3029 "listFunctions",
3030 "List user-defined functions",
3031 "Example: listFunctions (no parameters)",
3032 false,
3033 false,
3034 { Parameters::ARG_END_ARGS },
3035 std::mem_fn(&DebuggerParser::executeListFunctions)
3036 },
3037
3038 {
3039 "listSaveStateIfs",
3040 "List saveState points",
3041 "Example: listSaveStateIfs (no parameters)",
3042 false,
3043 false,
3044 { Parameters::ARG_END_ARGS },
3045 std::mem_fn(&DebuggerParser::executeListSaveStateIfs)
3046 },
3047
3048 {
3049 "listTraps",
3050 "List traps",
3051 "Lists all traps (read and/or write)\nExample: listTraps (no parameters)",
3052 false,
3053 false,
3054 { Parameters::ARG_END_ARGS },
3055 std::mem_fn(&DebuggerParser::executeListTraps)
3056 },
3057
3058 {
3059 "loadConfig",
3060 "Load Distella config file",
3061 "Example: loadConfig",
3062 false,
3063 true,
3064 { Parameters::ARG_END_ARGS },
3065 std::mem_fn(&DebuggerParser::executeLoadConfig)
3066 },
3067
3068 {
3069 "loadAllStates",
3070 "Load all emulator states",
3071 "Example: loadAllStates (no parameters)",
3072 false,
3073 true,
3074 { Parameters::ARG_END_ARGS },
3075 std::mem_fn(&DebuggerParser::executeLoadAllStates)
3076 },
3077
3078 {
3079 "loadState",
3080 "Load emulator state xx (0-9)",
3081 "Example: loadState 0, loadState 9",
3082 true,
3083 true,
3084 { Parameters::ARG_BYTE, Parameters::ARG_END_ARGS },
3085 std::mem_fn(&DebuggerParser::executeLoadState)
3086 },
3087
3088 {
3089 "logBreaks",
3090 "Toggle logging of breaks/traps and continue emulation",
3091 "Example: logBreaks (no parameters)",
3092 false,
3093 true,
3094 { Parameters::ARG_END_ARGS },
3095 std::mem_fn(&DebuggerParser::executeLogBreaks)
3096 },
3097
3098 {
3099 "n",
3100 "Negative Flag: set (0 or 1), or toggle (no arg)",
3101 "Example: n, n 0, n 1",
3102 false,
3103 true,
3104 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
3105 std::mem_fn(&DebuggerParser::executeN)
3106 },
3107
3108 {
3109 "palette",
3110 "Show current TIA palette",
3111 "Example: palette (no parameters)",
3112 false,
3113 false,
3114 { Parameters::ARG_END_ARGS },
3115 std::mem_fn(&DebuggerParser::executePalette)
3116 },
3117
3118 {
3119 "pc",
3120 "Set Program Counter to address xx",
3121 "Example: pc f000",
3122 true,
3123 true,
3124 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
3125 std::mem_fn(&DebuggerParser::executePc)
3126 },
3127
3128 {
3129 "pCol",
3130 "Mark 'pCol' range in disassembly",
3131 "Start and end of range required\nExample: col f000 f010",
3132 true,
3133 false,
3134 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
3135 std::mem_fn(&DebuggerParser::executePCol)
3136 },
3137
3138 {
3139 "pGfx",
3140 "Mark 'pGfx' range in disassembly",
3141 "Start and end of range required\nExample: pGfx f000 f010",
3142 true,
3143 false,
3144 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
3145 std::mem_fn(&DebuggerParser::executePGfx)
3146 },
3147
3148 {
3149 "print",
3150 "Evaluate/print expression xx in hex/dec/binary",
3151 "Almost anything can be printed (constants, expressions, registers)\n"
3152 "Example: print pc, print f000",
3153 true,
3154 false,
3155 { Parameters::ARG_DWORD, Parameters::ARG_END_ARGS },
3156 std::mem_fn(&DebuggerParser::executePrint)
3157 },
3158
3159 {
3160 "ram",
3161 "Show ZP RAM, or set address xx to yy1 [yy2 ...]",
3162 "Example: ram, ram 80 00 ...",
3163 false,
3164 true,
3165 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
3166 std::mem_fn(&DebuggerParser::executeRam)
3167 },
3168
3169 {
3170 "reset",
3171 "Reset system to power-on state",
3172 "System is completely reset, just as if it was just powered on",
3173 false,
3174 true,
3175 { Parameters::ARG_END_ARGS },
3176 std::mem_fn(&DebuggerParser::executeReset)
3177 },
3178
3179 {
3180 "rewind",
3181 "Rewind state by one or [xx] steps/traces/scanlines/frames...",
3182 "Example: rewind, rewind 5",
3183 false,
3184 true,
3185 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
3186 std::mem_fn(&DebuggerParser::executeRewind)
3187 },
3188
3189 {
3190 "riot",
3191 "Show RIOT timer/input status",
3192 "Display text-based output of the contents of the RIOT tab",
3193 false,
3194 false,
3195 { Parameters::ARG_END_ARGS },
3196 std::mem_fn(&DebuggerParser::executeRiot)
3197 },
3198
3199 {
3200 "rom",
3201 "Set ROM address xx to yy1 [yy2 ...]",
3202 "What happens here depends on the current bankswitching scheme\n"
3203 "Example: rom f000 00 01 ff ...",
3204 true,
3205 true,
3206 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
3207 std::mem_fn(&DebuggerParser::executeRom)
3208 },
3209
3210 {
3211 "row",
3212 "Mark 'ROW' range in disassembly",
3213 "Start and end of range required\nExample: row f000 f010",
3214 true,
3215 false,
3216 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
3217 std::mem_fn(&DebuggerParser::executeRow)
3218 },
3219
3220 {
3221 "run",
3222 "Exit debugger, return to emulator",
3223 "Self-explanatory",
3224 false,
3225 false,
3226 { Parameters::ARG_END_ARGS },
3227 std::mem_fn(&DebuggerParser::executeRun)
3228 },
3229
3230 {
3231 "runTo",
3232 "Run until string xx in disassembly",
3233 "Advance until the given string is detected in the disassembly\n"
3234 "Example: runTo lda",
3235 true,
3236 true,
3237 { Parameters::ARG_LABEL, Parameters::ARG_END_ARGS },
3238 std::mem_fn(&DebuggerParser::executeRunTo)
3239 },
3240
3241 {
3242 "runToPc",
3243 "Run until PC is set to value xx",
3244 "Example: runToPc f200",
3245 true,
3246 true,
3247 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
3248 std::mem_fn(&DebuggerParser::executeRunToPc)
3249 },
3250
3251 {
3252 "s",
3253 "Set Stack Pointer to value xx",
3254 "Accepts 8-bit value, Example: s f0",
3255 true,
3256 true,
3257 { Parameters::ARG_BYTE, Parameters::ARG_END_ARGS },
3258 std::mem_fn(&DebuggerParser::executeS)
3259 },
3260
3261 {
3262 "save",
3263 "Save breaks, watches, traps and functions to file [xx or ?]",
3264 "Example: save, save commands.script, save ?\n"
3265 "NOTE: saves to user dir by default",
3266 false,
3267 false,
3268 { Parameters::ARG_FILE, Parameters::ARG_END_ARGS },
3269 std::mem_fn(&DebuggerParser::executeSave)
3270 },
3271
3272 {
3273 "saveAccess",
3274 "Save the access counters to CSV file [?]",
3275 "Example: saveAccess, saveAccess ?\n"
3276 "NOTE: saves to user dir by default",
3277 false,
3278 false,
3279 { Parameters::ARG_LABEL, Parameters::ARG_END_ARGS },
3280 std::mem_fn(&DebuggerParser::executeSaveAccess)
3281 },
3282
3283 {
3284 "saveConfig",
3285 "Save Distella config file (with default name)",
3286 "Example: saveConfig",
3287 false,
3288 false,
3289 { Parameters::ARG_END_ARGS },
3290 std::mem_fn(&DebuggerParser::executeSaveConfig)
3291 },
3292
3293 {
3294 "saveDis",
3295 "Save Distella disassembly to file [?]",
3296 "Example: saveDis, saveDis ?\n"
3297 "NOTE: saves to user dir by default",
3298 false,
3299 false,
3300 { Parameters::ARG_LABEL, Parameters::ARG_END_ARGS },
3301 std::mem_fn(&DebuggerParser::executeSaveDisassembly)
3302 },
3303
3304 {
3305 "saveRom",
3306 "Save (possibly patched) ROM to file [?]",
3307 "Example: saveRom, saveRom ?\n"
3308 "NOTE: saves to user dir by default",
3309 false,
3310 false,
3311 { Parameters::ARG_LABEL, Parameters::ARG_END_ARGS },
3312 std::mem_fn(&DebuggerParser::executeSaveRom)
3313 },
3314
3315 {
3316 "saveSes",
3317 "Save console session to file [?]",
3318 "Example: saveSes, saveSes ?\n"
3319 "NOTE: saves to user dir by default",
3320 false,
3321 false,
3322 { Parameters::ARG_LABEL, Parameters::ARG_END_ARGS },
3323 std::mem_fn(&DebuggerParser::executeSaveSes)
3324 },
3325
3326 {
3327 "saveSnap",
3328 "Save current TIA image to PNG file",
3329 "Save snapshot to current snapshot save directory\n"
3330 "Example: saveSnap (no parameters)",
3331 false,
3332 false,
3333 { Parameters::ARG_END_ARGS },
3334 std::mem_fn(&DebuggerParser::executeSaveSnap)
3335 },
3336
3337 {
3338 "saveAllStates",
3339 "Save all emulator states",
3340 "Example: saveAllStates (no parameters)",
3341 false,
3342 false,
3343 { Parameters::ARG_END_ARGS },
3344 std::mem_fn(&DebuggerParser::executeSaveAllStates)
3345 },
3346
3347 {
3348 "saveState",
3349 "Save emulator state xx (valid args 0-9)",
3350 "Example: saveState 0, saveState 9",
3351 true,
3352 false,
3353 { Parameters::ARG_BYTE, Parameters::ARG_END_ARGS },
3354 std::mem_fn(&DebuggerParser::executeSaveState)
3355 },
3356
3357 {
3358 "saveStateIf",
3359 "Create saveState on <condition>",
3360 "Condition can include multiple items, see documentation\nExample: saveStateIf pc==f000",
3361 true,
3362 false,
3363 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
3364 std::mem_fn(&DebuggerParser::executeSaveStateIf)
3365 },
3366
3367 {
3368 "scanLine",
3369 "Advance emulation by <xx> scanlines (default=1)",
3370 "Example: scanLine, scanLine 100",
3371 false,
3372 true,
3373 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
3374 std::mem_fn(&DebuggerParser::executeScanLine)
3375 },
3376
3377 {
3378 "step",
3379 "Single step CPU [with count xx]",
3380 "Example: step, step 100",
3381 false,
3382 true,
3383 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
3384 std::mem_fn(&DebuggerParser::executeStep)
3385 },
3386
3387 {
3388 "stepWhile",
3389 "Single step CPU while <condition> is true",
3390 "Example: stepWhile pc!=$f2a9",
3391 true,
3392 true,
3393 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
3394 std::mem_fn(&DebuggerParser::executeStepWhile)
3395 },
3396
3397 {
3398 "tia",
3399 "Show TIA state",
3400 "Display text-based output of the contents of the TIA tab",
3401 false,
3402 false,
3403 { Parameters::ARG_END_ARGS },
3404 std::mem_fn(&DebuggerParser::executeTia)
3405 },
3406
3407 {
3408 "trace",
3409 "Single step CPU over subroutines [with count xx]",
3410 "Example: trace, trace 100",
3411 false,
3412 true,
3413 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
3414 std::mem_fn(&DebuggerParser::executeTrace)
3415 },
3416
3417 {
3418 "trap",
3419 "Trap read/write access to address(es) xx [yy]",
3420 "Set/clear a R/W trap on the given address(es) and all mirrors\n"
3421 "Example: trap f000, trap f000 f100",
3422 true,
3423 false,
3424 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
3425 std::mem_fn(&DebuggerParser::executeTrap)
3426 },
3427
3428 {
3429 "trapIf",
3430 "On <condition> trap R/W access to address(es) xx [yy]",
3431 "Set/clear a conditional R/W trap on the given address(es) and all mirrors\nCondition can include multiple items.\n"
3432 "Example: trapIf _scan>#100 GRP0, trapIf _bank==1 f000 f100",
3433 true,
3434 false,
3435 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
3436 std::mem_fn(&DebuggerParser::executeTrapIf)
3437 },
3438
3439 {
3440 "trapRead",
3441 "Trap read access to address(es) xx [yy]",
3442 "Set/clear a read trap on the given address(es) and all mirrors\n"
3443 "Example: trapRead f000, trapRead f000 f100",
3444 true,
3445 false,
3446 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
3447 std::mem_fn(&DebuggerParser::executeTrapRead)
3448 },
3449
3450 {
3451 "trapReadIf",
3452 "On <condition> trap read access to address(es) xx [yy]",
3453 "Set/clear a conditional read trap on the given address(es) and all mirrors\nCondition can include multiple items.\n"
3454 "Example: trapReadIf _scan>#100 GRP0, trapReadIf _bank==1 f000 f100",
3455 true,
3456 false,
3457 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
3458 std::mem_fn(&DebuggerParser::executeTrapReadIf)
3459 },
3460
3461 {
3462 "trapWrite",
3463 "Trap write access to address(es) xx [yy]",
3464 "Set/clear a write trap on the given address(es) and all mirrors\n"
3465 "Example: trapWrite f000, trapWrite f000 f100",
3466 true,
3467 false,
3468 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
3469 std::mem_fn(&DebuggerParser::executeTrapWrite)
3470 },
3471
3472 {
3473 "trapWriteIf",
3474 "On <condition> trap write access to address(es) xx [yy]",
3475 "Set/clear a conditional write trap on the given address(es) and all mirrors\nCondition can include multiple items.\n"
3476 "Example: trapWriteIf _scan>#100 GRP0, trapWriteIf _bank==1 f000 f100",
3477 true,
3478 false,
3479 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
3480 std::mem_fn(&DebuggerParser::executeTrapWriteIf)
3481 },
3482
3483 {
3484 "type",
3485 "Show access type for address xx [yy]",
3486 "Example: type f000, type f000 f010",
3487 true,
3488 false,
3489 { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE },
3490 std::mem_fn(&DebuggerParser::executeType)
3491 },
3492
3493 {
3494 "uHex",
3495 "Toggle upper/lowercase HEX display",
3496 "Note: not all hex output can be changed\n"
3497 "Example: uHex (no parameters)",
3498 false,
3499 true,
3500 { Parameters::ARG_END_ARGS },
3501 std::mem_fn(&DebuggerParser::executeUHex)
3502 },
3503
3504 {
3505 "undef",
3506 "Undefine label xx (if defined)",
3507 "Example: undef LABEL1",
3508 true,
3509 true,
3510 { Parameters::ARG_LABEL, Parameters::ARG_END_ARGS },
3511 std::mem_fn(&DebuggerParser::executeUndef)
3512 },
3513
3514 {
3515 "unwind",
3516 "Unwind state by one or [xx] steps/traces/scanlines/frames...",
3517 "Example: unwind, unwind 5",
3518 false,
3519 true,
3520 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
3521 std::mem_fn(&DebuggerParser::executeUnwind)
3522 },
3523
3524 {
3525 "v",
3526 "Overflow Flag: set (0 or 1), or toggle (no arg)",
3527 "Example: v, v 0, v 1",
3528 false,
3529 true,
3530 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
3531 std::mem_fn(&DebuggerParser::executeV)
3532 },
3533
3534 {
3535 "watch",
3536 "Print contents of address xx before every prompt",
3537 "Example: watch ram_80",
3538 true,
3539 false,
3540 { Parameters::ARG_WORD, Parameters::ARG_END_ARGS },
3541 std::mem_fn(&DebuggerParser::executeWatch)
3542 },
3543
3544 {
3545 "x",
3546 "Set X Register to value xx",
3547 "Valid value is 0 - ff\nExample: x ff, x #10",
3548 true,
3549 true,
3550 { Parameters::ARG_BYTE, Parameters::ARG_END_ARGS },
3551 std::mem_fn(&DebuggerParser::executeX)
3552 },
3553
3554 {
3555 "y",
3556 "Set Y Register to value xx",
3557 "Valid value is 0 - ff\nExample: y ff, y #10",
3558 true,
3559 true,
3560 { Parameters::ARG_BYTE, Parameters::ARG_END_ARGS },
3561 std::mem_fn(&DebuggerParser::executeY)
3562 },
3563
3564 {
3565 "z",
3566 "Zero Flag: set (0 or 1), or toggle (no arg)",
3567 "Example: z, z 0, z 1",
3568 false,
3569 true,
3570 { Parameters::ARG_BOOL, Parameters::ARG_END_ARGS },
3571 std::mem_fn(&DebuggerParser::executeZ)
3572 }
3573 } };
3574