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