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-2014 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 // $Id: DebuggerParser.cxx 2838 2014-01-17 23:34:03Z stephena $
18 //============================================================================
19 
20 #include <fstream>
21 #include "bspf.hxx"
22 
23 #include "Dialog.hxx"
24 #include "Debugger.hxx"
25 #include "CartDebug.hxx"
26 #include "CpuDebug.hxx"
27 #include "RiotDebug.hxx"
28 #include "TIADebug.hxx"
29 #include "DebuggerParser.hxx"
30 #include "YaccParser.hxx"
31 #include "M6502.hxx"
32 #include "Expression.hxx"
33 #include "FSNode.hxx"
34 #include "PromptWidget.hxx"
35 #include "RomWidget.hxx"
36 #include "ProgressDialog.hxx"
37 #include "PackedBitArray.hxx"
38 
39 #include "Base.hxx"
40 using namespace Common;
41 
42 #ifdef CHEATCODE_SUPPORT
43   #include "Cheat.hxx"
44   #include "CheatManager.hxx"
45 #endif
46 
47 #include "DebuggerParser.hxx"
48 
49 // Call the pointed-to method on the this object. Whew.
50 #define CALL_METHOD(method) ( (this->*method)() )
51 
52 
53 // TODO - use C++ streams instead of nasty C-strings and pointers
54 
55 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DebuggerParser(Debugger & d,Settings & s)56 DebuggerParser::DebuggerParser(Debugger& d, Settings& s)
57   : debugger(d),
58     settings(s)
59 {
60 }
61 
62 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
~DebuggerParser()63 DebuggerParser::~DebuggerParser()
64 {
65   args.clear();
66   argStrings.clear();
67   watches.clear();
68 }
69 
70 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
71 // main entry point: PromptWidget calls this method.
run(const string & command)72 string DebuggerParser::run(const string& command)
73 {
74   /*
75     // this was our parser test code. Left for reference.
76   static Expression *lastExpression;
77 
78   // special case: parser testing
79   if(strncmp(command.c_str(), "expr ", 5) == 0) {
80     delete lastExpression;
81     commandResult = "parser test: status==";
82     int status = YaccParser::parse(command.c_str() + 5);
83     commandResult += debugger.valueToString(status);
84     commandResult += ", result==";
85     if(status == 0) {
86       lastExpression = YaccParser::getResult();
87       commandResult += debugger.valueToString(lastExpression->evaluate());
88     } else {
89       //  delete lastExpression; // NO! lastExpression isn't valid (not 0 either)
90                                 // It's the result of casting the last token
91                                 // to Expression* (because of yacc's union).
92                                 // As such, we can't and don't need to delete it
93                                 // (However, it means yacc leaks memory on error)
94       commandResult += "ERROR - ";
95       commandResult += YaccParser::errorMessage();
96     }
97     return commandResult;
98   }
99 
100   if(command == "expr") {
101     if(lastExpression)
102       commandResult = "result==" + debugger.valueToString(lastExpression->evaluate());
103     else
104       commandResult = "no valid expr";
105     return commandResult;
106   }
107   */
108 
109   string verb;
110   getArgs(command, verb);
111 #ifdef EXPR_REF_COUNT
112   extern int refCount;
113   cerr << "Expression count: " << refCount << endl;
114 #endif
115   commandResult.str("");
116 
117   for(int i = 0; i < kNumCommands; ++i)
118   {
119     if(BSPF_equalsIgnoreCase(verb, commands[i].cmdString))
120     {
121       if(validateArgs(i))
122         CALL_METHOD(commands[i].executor);
123 
124       if(commands[i].refreshRequired)
125         debugger.myBaseDialog->loadConfig();
126 
127       return commandResult.str();
128     }
129   }
130 
131   return "No such command (try \"help\")";
132 }
133 
134 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
exec(const FilesystemNode & file)135 string DebuggerParser::exec(const FilesystemNode& file)
136 {
137   if(file.exists())
138   {
139     ifstream in(file.getPath().c_str());
140     if(!in.is_open())
141       return red("autoexec 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       count++;
153     }
154     buf << "Executed " << count << " commands from \""
155         << file.getShortPath() << "\"";
156 
157     return buf.str();
158   }
159   else
160     return red("autoexec file \'" + file.getShortPath() + "\' not found");
161 }
162 
163 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
164 // Completion-related stuff:
165 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
getCompletions(const char * in,StringList & completions) const166 void DebuggerParser::getCompletions(const char* in, StringList& completions) const
167 {
168   // cerr << "Attempting to complete \"" << in << "\"" << endl;
169   for(int i = 0; i < kNumCommands; ++i)
170   {
171     if(BSPF_startsWithIgnoreCase(commands[i].cmdString.c_str(), in))
172       completions.push_back(commands[i].cmdString);
173   }
174 }
175 
176 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
177 // Evaluate expression. Expressions always evaluate to a 16-bit value if
178 // they're valid, or -1 if they're not.
179 // decipher_arg may be called by the GUI as needed. It is also called
180 // internally by DebuggerParser::run()
decipher_arg(const string & str)181 int DebuggerParser::decipher_arg(const string& str)
182 {
183   bool derefByte=false, derefWord=false, lobyte=false, hibyte=false, bin=false, dec=false;
184   int result;
185   string arg = str;
186 
187   Base::Format defaultBase = Base::format();
188 
189   if(defaultBase == Base::F_2) {
190     bin=true; dec=false;
191   } else if(defaultBase == Base::F_10) {
192     bin=false; dec=true;
193   } else {
194     bin=false; dec=false;
195   }
196 
197   if(arg.substr(0, 1) == "*") {
198     derefByte = true;
199     arg.erase(0, 1);
200   } else if(arg.substr(0, 1) == "@") {
201     derefWord = true;
202     arg.erase(0, 1);
203   }
204 
205   if(arg.substr(0, 1) == "<") {
206     lobyte = true;
207     arg.erase(0, 1);
208   } else if(arg.substr(0, 1) == ">") {
209     hibyte = true;
210     arg.erase(0, 1);
211   }
212 
213   if(arg.substr(0, 1) == "\\") {
214     bin = true;
215     dec = false;
216     arg.erase(0, 1);
217   } else if(arg.substr(0, 1) == "#") {
218     dec = true;
219     bin = false;
220     arg.erase(0, 1);
221   } else if(arg.substr(0, 1) == "$") {
222     dec = false;
223     bin = false;
224     arg.erase(0, 1);
225   }
226 
227   // sanity check mutually exclusive options:
228   if(derefByte && derefWord) return -1;
229   if(lobyte && hibyte) return -1;
230   if(bin && dec) return -1;
231 
232   // Special cases (registers):
233   CpuState& state = (CpuState&) debugger.cpuDebug().getState();
234   if(arg == "a") result = state.A;
235   else if(arg == "x") result = state.X;
236   else if(arg == "y") result = state.Y;
237   else if(arg == "p") result = state.PS;
238   else if(arg == "s") result = state.SP;
239   else if(arg == "pc" || arg == ".") result = state.PC;
240   else { // Not a special, must be a regular arg: check for label first
241     const char* a = arg.c_str();
242     result = debugger.cartDebug().getAddress(arg);
243 
244     if(result < 0) { // if not label, must be a number
245       if(bin) { // treat as binary
246         result = 0;
247         while(*a != '\0') {
248           result <<= 1;
249           switch(*a++) {
250             case '1':
251               result++;
252               break;
253 
254             case '0':
255               break;
256 
257             default:
258               return -1;
259           }
260         }
261       } else if(dec) {
262         result = 0;
263         while(*a != '\0') {
264           int digit = (*a++) - '0';
265           if(digit < 0 || digit > 9)
266             return -1;
267 
268           result = (result * 10) + digit;
269         }
270       } else { // must be hex.
271         result = 0;
272         while(*a != '\0') {
273           int hex = -1;
274           char d = *a++;
275           if(d >= '0' && d <= '9')  hex = d - '0';
276           else if(d >= 'a' && d <= 'f') hex = d - 'a' + 10;
277           else if(d >= 'A' && d <= 'F') hex = d - 'A' + 10;
278           if(hex < 0)
279             return -1;
280 
281           result = (result << 4) + hex;
282         }
283       }
284     }
285   }
286 
287   if(lobyte) result &= 0xff;
288   else if(hibyte) result = (result >> 8) & 0xff;
289 
290   // dereference if we're supposed to:
291   if(derefByte) result = debugger.peek(result);
292   if(derefWord) result = debugger.dpeek(result);
293 
294   return result;
295 }
296 
297 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
showWatches()298 string DebuggerParser::showWatches()
299 {
300   ostringstream buf;
301   for(unsigned int i = 0; i < watches.size(); i++)
302   {
303     if(watches[i] != "")
304     {
305       // Clear the args, since we're going to pass them to eval()
306       argStrings.clear();
307       args.clear();
308 
309       argCount = 1;
310       argStrings.push_back(watches[i]);
311       args.push_back(decipher_arg(argStrings[0]));
312       if(args[0] < 0)
313         buf << "BAD WATCH " << (i+1) << ": " << argStrings[0] << endl;
314       else
315         buf << " watch #" << (i+1) << " (" << argStrings[0] << ") -> " << eval() << endl;
316     }
317   }
318   return buf.str();
319 }
320 
321 
322 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
323 // Private methods below
324 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
325 
326 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
getArgs(const string & command,string & verb)327 bool DebuggerParser::getArgs(const string& command, string& verb)
328 {
329   int state = kIN_COMMAND, i = 0, length = command.length();
330   string curArg = "";
331   verb = "";
332 
333   argStrings.clear();
334   args.clear();
335 
336   // cerr << "Parsing \"" << command << "\"" << ", length = " << command.length() << endl;
337 
338   // First, pick apart string into space-separated tokens.
339   // The first token is the command verb, the rest go in an array
340   do
341   {
342     char c = command[i++];
343     switch(state)
344     {
345       case kIN_COMMAND:
346         if(c == ' ')
347           state = kIN_SPACE;
348         else
349           verb += c;
350         break;
351       case kIN_SPACE:
352         if(c == '{')
353           state = kIN_BRACE;
354         else if(c != ' ') {
355           state = kIN_ARG;
356           curArg += c;
357         }
358         break;
359       case kIN_BRACE:
360         if(c == '}') {
361           state = kIN_SPACE;
362           argStrings.push_back(curArg);
363           //  cerr << "{" << curArg << "}" << endl;
364           curArg = "";
365         } else {
366           curArg += c;
367         }
368         break;
369       case kIN_ARG:
370         if(c == ' ') {
371           state = kIN_SPACE;
372           argStrings.push_back(curArg);
373           curArg = "";
374         } else {
375           curArg += c;
376         }
377         break;
378     }  // switch(state)
379   }
380   while(i < length);
381 
382   // Take care of the last argument, if there is one
383   if(curArg != "")
384     argStrings.push_back(curArg);
385 
386   argCount = argStrings.size();
387   /*
388   cerr << "verb = " << verb << endl;
389   cerr << "arguments (" << argCount << "):\n";
390   for(int x = 0; x < argCount; x++)
391     cerr << "command " << x << ": " << argStrings[x] << endl;
392   */
393 
394   /*
395   // Now decipher each argument, in turn.
396   for(int i=0; i<argCount; i++) {
397     int temp = decipher_arg(argStrings[i]);
398     args.push_back(temp); // value maybe -1, if not expression argument
399                           // (validate_args will decide whether that's OK, not us.)
400   }
401   */
402 
403   for(int i = 0; i < argCount; i++) {
404     int err = YaccParser::parse(argStrings[i].c_str());
405     if(err) {
406       args.push_back(-1);
407     } else {
408       Expression* e = YaccParser::getResult();
409       args.push_back( e->evaluate() );
410       delete e;
411     }
412   }
413 
414   return true;
415 }
416 
417 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
validateArgs(int cmd)418 bool DebuggerParser::validateArgs(int cmd)
419 {
420   // cerr << "entering validateArgs(" << cmd << ")" << endl;
421   bool required = commands[cmd].parmsRequired;
422   parameters *p = commands[cmd].parms;
423 
424   if(argCount == 0)
425   {
426     if(required)
427     {
428       commandResult.str(red("missing required argument(s)"));
429       return false; // needed args. didn't get 'em.
430     }
431     else
432       return true;  // no args needed, no args got
433   }
434 
435   // Figure out how many arguments are required by the command
436   int count = 0, argRequiredCount = 0;
437   while(*p != kARG_END_ARGS && *p != kARG_MULTI_BYTE)
438   {
439     count++;
440     p++;
441   }
442 
443   // Evil hack: some commands intentionally take multiple arguments
444   // In this case, the required number of arguments is unbounded
445   argRequiredCount = (*p == kARG_END_ARGS) ? count : argCount;
446 
447   p = commands[cmd].parms;
448   int curCount = 0;
449 
450   do {
451     if(curCount >= argCount)
452       break;
453 
454     int curArgInt     = args[curCount];
455     string& curArgStr = argStrings[curCount];
456 
457     switch(*p)
458     {
459       case kARG_WORD:
460         if(curArgInt < 0 || curArgInt > 0xffff)
461         {
462           commandResult.str(red("invalid word argument (must be 0-$ffff)"));
463           return false;
464         }
465         break;
466 
467       case kARG_BYTE:
468         if(curArgInt < 0 || curArgInt > 0xff)
469         {
470           commandResult.str(red("invalid byte argument (must be 0-$ff)"));
471           return false;
472         }
473         break;
474 
475       case kARG_BOOL:
476         if(curArgInt != 0 && curArgInt != 1)
477         {
478           commandResult.str(red("invalid boolean argument (must be 0 or 1)"));
479           return false;
480         }
481         break;
482 
483       case kARG_BASE_SPCL:
484         if(curArgInt != 2 && curArgInt != 10 && curArgInt != 16
485            && curArgStr != "hex" && curArgStr != "dec" && curArgStr != "bin")
486         {
487           commandResult.str(red("invalid base (must be #2, #10, #16, \"bin\", \"dec\", or \"hex\")"));
488           return false;
489         }
490         break;
491 
492       case kARG_LABEL:
493       case kARG_FILE:
494         break; // TODO: validate these (for now any string's allowed)
495 
496       case kARG_MULTI_BYTE:
497       case kARG_MULTI_WORD:
498         break; // FIXME: validate these (for now, any number's allowed)
499 
500       case kARG_END_ARGS:
501         break;
502     }
503     curCount++;
504     p++;
505 
506   } while(*p != kARG_END_ARGS && curCount < argRequiredCount);
507 
508 /*
509 cerr << "curCount         = " << curCount << endl
510      << "argRequiredCount = " << argRequiredCount << endl
511      << "*p               = " << *p << endl << endl;
512 */
513 
514   if(curCount < argRequiredCount)
515   {
516     commandResult.str(red("missing required argument(s)"));
517     return false;
518   }
519   else if(argCount > curCount)
520   {
521     commandResult.str(red("too many arguments"));
522     return false;
523   }
524 
525   return true;
526 }
527 
528 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
eval()529 string DebuggerParser::eval()
530 {
531   ostringstream buf;
532   for(int i = 0; i < argCount; ++i)
533   {
534     string rlabel = debugger.cartDebug().getLabel(args[i], true);
535     string wlabel = debugger.cartDebug().getLabel(args[i], false);
536     bool validR = rlabel != "" && rlabel[0] != '$',
537          validW = wlabel != "" && wlabel[0] != '$';
538     if(validR && validW)
539     {
540       if(rlabel == wlabel)
541         buf << rlabel << "(R/W): ";
542       else
543         buf << rlabel << "(R) / " << wlabel << "(W): ";
544     }
545     else if(validR)
546       buf << rlabel << "(R): ";
547     else if(validW)
548       buf << wlabel << "(W): ";
549 
550     if(args[i] < 0x100)
551       buf << "$" << Base::toString(args[i], Base::F_16_2)
552           << " %" << Base::toString(args[i], Base::F_2_8);
553     else
554       buf << "$" << Base::toString(args[i], Base::F_16_4)
555           << " %" << Base::toString(args[i], Base::F_2_16);
556 
557     buf << " #" << (int) args[i];
558     if(i != argCount - 1)
559       buf << endl;
560   }
561 
562   return buf.str();
563 }
564 
565 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
trapStatus(int addr)566 string DebuggerParser::trapStatus(int addr)
567 {
568   string result;
569   result += Base::toString(addr);
570   result += ": ";
571   bool r = debugger.readTrap(addr);
572   bool w = debugger.writeTrap(addr);
573   if(r && w)
574     result += "read|write";
575   else if(r)
576     result += "read";
577   else if(w)
578     result += "     write";
579   else
580     result += "   none   ";
581 
582   // TODO - technically, we should determine if the label is read or write
583   const string& l = debugger.cartDebug().getLabel(addr, true);
584   if(l != "") {
585     result += "  (";
586     result += l;
587     result += ")";
588   }
589 
590   return result;
591 }
592 
593 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
saveScriptFile(string file)594 bool DebuggerParser::saveScriptFile(string file)
595 {
596   if( file.find_last_of('.') == string::npos ) {
597     file += ".stella";
598   }
599 
600   ofstream out(file.c_str());
601 
602   FunctionDefMap funcs = debugger.getFunctionDefMap();
603   for(FunctionDefMap::const_iterator i = funcs.begin(); i != funcs.end(); ++i)
604     out << "function " << i->first << " { " << i->second << " }" << endl;
605 
606   for(unsigned int i=0; i<watches.size(); i++)
607     out << "watch " << watches[i] << endl;
608 
609   for(unsigned int i=0; i<0x10000; i++)
610     if(debugger.breakPoint(i))
611       out << "break #" << i << endl;
612 
613   for(unsigned int i=0; i<0x10000; i++) {
614     bool r = debugger.readTrap(i);
615     bool w = debugger.writeTrap(i);
616 
617     if(r && w)
618       out << "trap #" << i << endl;
619     else if(r)
620       out << "trapread #" << i << endl;
621     else if(w)
622       out << "trapwrite #" << i << endl;
623   }
624 
625   StringList conds = debugger.cpuDebug().m6502().getCondBreakNames();
626   for(unsigned int i=0; i<conds.size(); i++)
627     out << "breakif {" << conds[i] << "}" << endl;
628 
629   bool ok = out.good();
630   out.close();
631   return ok;
632 }
633 
634 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
635 // executor methods for commands[] array. All are void, no args.
636 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
637 
638 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
639 // "a"
executeA()640 void DebuggerParser::executeA()
641 {
642   debugger.cpuDebug().setA((uInt8)args[0]);
643 }
644 
645 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
646 // "bank"
executeBank()647 void DebuggerParser::executeBank()
648 {
649   int banks = debugger.cartDebug().bankCount();
650   if(argCount == 0)
651   {
652     commandResult << debugger.cartDebug().getCartType() << ": ";
653     if(banks < 2)
654       commandResult << red("bankswitching not supported by this cartridge");
655     else
656     {
657       commandResult << "current = " << debugger.cartDebug().getBank()
658                     << " out of " << banks << " banks";
659     }
660   }
661   else
662   {
663     if(banks == 1)
664       commandResult << red("bankswitching not supported by this cartridge");
665     else if(args[0] >= banks)
666       commandResult << red("invalid bank number (must be 0 to ")
667                     << (banks - 1) << ")";
668     else if(debugger.setBank(args[0]))
669       commandResult << "switched bank OK";
670     else
671       commandResult << red("error switching banks (bankswitching may not be supported)");
672   }
673 }
674 
675 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
676 // "base"
executeBase()677 void DebuggerParser::executeBase()
678 {
679   if(args[0] == 2 || argStrings[0] == "bin")
680     Base::setFormat(Base::F_2);
681   else if(args[0] == 10 || argStrings[0] == "dec")
682     Base::setFormat(Base::F_10);
683   else if(args[0] == 16 || argStrings[0] == "hex")
684     Base::setFormat(Base::F_16);
685 
686   commandResult << "default base set to ";
687   switch(Base::format()) {
688     case Base::F_2:
689       commandResult << "#2/bin";
690       break;
691 
692     case Base::F_10:
693       commandResult << "#10/dec";
694       break;
695 
696     case Base::F_16:
697       commandResult << "#16/hex";
698       break;
699 
700     default:
701       commandResult << red("UNKNOWN");
702       break;
703   }
704 }
705 
706 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
707 // "break"
executeBreak()708 void DebuggerParser::executeBreak()
709 {
710   int bp;
711   if(argCount == 0)
712     bp = debugger.cpuDebug().pc();
713   else
714     bp = args[0];
715   debugger.toggleBreakPoint(bp);
716   debugger.rom().invalidate();
717 
718   if(debugger.breakPoint(bp))
719     commandResult << "Set";
720   else
721     commandResult << "Cleared";
722 
723   commandResult << " breakpoint at " << Base::toString(bp);
724 }
725 
726 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
727 // "breakif"
executeBreakif()728 void DebuggerParser::executeBreakif()
729 {
730   int res = YaccParser::parse(argStrings[0].c_str());
731   if(res == 0)
732   {
733     uInt32 ret = debugger.cpuDebug().m6502().addCondBreak(
734                  YaccParser::getResult(), argStrings[0] );
735     commandResult << "Added breakif " << Base::toString(ret);
736   }
737   else
738     commandResult << red("invalid expression");
739 }
740 
741 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
742 // "c"
executeC()743 void DebuggerParser::executeC()
744 {
745   if(argCount == 0)
746     debugger.cpuDebug().toggleC();
747   else if(argCount == 1)
748     debugger.cpuDebug().setC(args[0]);
749 }
750 
751 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
752 // "cheat"
753 // (see Stella manual for different cheat types)
executeCheat()754 void DebuggerParser::executeCheat()
755 {
756 #ifdef CHEATCODE_SUPPORT
757   if(argCount == 0)
758   {
759     commandResult << red("Missing cheat code");
760     return;
761   }
762 
763   for(int arg = 0; arg < argCount; arg++)
764   {
765     const string& cheat = argStrings[arg];
766     const Cheat* c = debugger.myOSystem->cheat().add("DBG", cheat);
767     if(c && c->enabled())
768       commandResult << "Cheat code " << cheat << " enabled" << endl;
769     else
770       commandResult << red("Invalid cheat code ") << cheat << endl;
771   }
772 #else
773   commandResult << red("Cheat support not enabled\n");
774 #endif
775 }
776 
777 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
778 // "clearbreaks"
executeClearbreaks()779 void DebuggerParser::executeClearbreaks()
780 {
781   debugger.clearAllBreakPoints();
782   debugger.cpuDebug().m6502().clearCondBreaks();
783   commandResult << "all breakpoints cleared";
784 }
785 
786 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
787 // "clearconfig"
executeClearconfig()788 void DebuggerParser::executeClearconfig()
789 {
790   if(argCount == 1)
791     commandResult << debugger.cartDebug().clearConfig(args[0]);
792   else
793     commandResult << debugger.cartDebug().clearConfig();
794 }
795 
796 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
797 // "cleartraps"
executeCleartraps()798 void DebuggerParser::executeCleartraps()
799 {
800   debugger.clearAllTraps();
801   commandResult << "all traps cleared";
802 }
803 
804 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
805 // "clearwatches"
executeClearwatches()806 void DebuggerParser::executeClearwatches()
807 {
808   watches.clear();
809   commandResult << "all watches cleared";
810 }
811 
812 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
813 // "cls"
executeCls()814 void DebuggerParser::executeCls()
815 {
816   debugger.prompt().clearScreen();
817   commandResult << "";
818 }
819 
820 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
821 // "code"
executeCode()822 void DebuggerParser::executeCode()
823 {
824   if(argCount != 2)
825   {
826     commandResult << red("Specify start and end of range only");
827     return;
828   }
829   else if(args[1] < args[0])
830   {
831     commandResult << red("Start address must be <= end address");
832     return;
833   }
834 
835   bool result = debugger.cartDebug().addDirective(
836                   CartDebug::CODE, args[0], args[1]);
837   commandResult << (result ? "added" : "removed") << " CODE directive on range $"
838                 << hex << args[0] << " $" << hex << args[1];
839   debugger.rom().invalidate();
840 }
841 
842 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
843 // "colortest"
executeColortest()844 void DebuggerParser::executeColortest()
845 {
846   commandResult << "test color: "
847                 << char((args[0]>>1) | 0x80)
848                 << inverse("        ");
849 }
850 
851 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
852 // "d"
executeD()853 void DebuggerParser::executeD()
854 {
855   if(argCount == 0)
856     debugger.cpuDebug().toggleD();
857   else if(argCount == 1)
858     debugger.cpuDebug().setD(args[0]);
859 }
860 
861 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
862 // "data"
executeData()863 void DebuggerParser::executeData()
864 {
865   if(argCount != 2)
866   {
867     commandResult << red("Specify start and end of range only");
868     return;
869   }
870   else if(args[1] < args[0])
871   {
872     commandResult << red("Start address must be <= end address");
873     return;
874   }
875 
876   bool result = debugger.cartDebug().addDirective(
877                   CartDebug::DATA, args[0], args[1]);
878   commandResult << (result ? "added" : "removed") << " DATA directive on range $"
879                 << hex << args[0] << " $" << hex << args[1];
880   debugger.rom().invalidate();
881 }
882 
883 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
884 // "define"
executeDefine()885 void DebuggerParser::executeDefine()
886 {
887   // TODO: check if label already defined?
888   debugger.cartDebug().addLabel(argStrings[0], args[1]);
889   debugger.rom().invalidate();
890 }
891 
892 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
893 // "delbreakif"
executeDelbreakif()894 void DebuggerParser::executeDelbreakif()
895 {
896   debugger.cpuDebug().m6502().delCondBreak(args[0]);
897 }
898 
899 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
900 // "delfunction"
executeDelfunction()901 void DebuggerParser::executeDelfunction()
902 {
903   if(debugger.delFunction(argStrings[0]))
904     commandResult << "removed function " << argStrings[0];
905   else
906     commandResult << "function " << argStrings[0] << " not found";
907 }
908 
909 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
910 // "delwatch"
executeDelwatch()911 void DebuggerParser::executeDelwatch()
912 {
913   int which = args[0] - 1;
914   if(which >= 0 && which < (int)watches.size())
915   {
916     watches.remove_at(which);
917     commandResult << "removed watch";
918   }
919   else
920     commandResult << "no such watch";
921 }
922 
923 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
924 // "disasm"
executeDisasm()925 void DebuggerParser::executeDisasm()
926 {
927   int start, lines = 20;
928 
929   if(argCount == 0) {
930     start = debugger.cpuDebug().pc();
931   } else if(argCount == 1) {
932     start = args[0];
933   } else if(argCount == 2) {
934     start = args[0];
935     lines = args[1];
936   } else {
937     commandResult << "wrong number of arguments";
938     return;
939   }
940 
941   commandResult << debugger.cartDebug().disassemble(start, lines);
942 }
943 
944 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
945 // "dump"
executeDump()946 void DebuggerParser::executeDump()
947 {
948   for(int i=0; i<8; i++)
949   {
950     int start = args[0] + i*16;
951     commandResult << Base::toString(start) << ": ";
952     for(int j = 0; j < 16; j++)
953     {
954       commandResult << Base::toString(debugger.peek(start+j)) << " ";
955       if(j == 7) commandResult << "- ";
956     }
957     if(i != 7) commandResult << endl;
958   }
959 }
960 
961 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
962 // "exec"
executeExec()963 void DebuggerParser::executeExec()
964 {
965   FilesystemNode file(argStrings[0]);
966   commandResult << exec(file);
967 }
968 
969 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
970 // "exitrom"
executeExitRom()971 void DebuggerParser::executeExitRom()
972 {
973   debugger.quit(true);
974 }
975 
976 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
977 // "frame"
executeFrame()978 void DebuggerParser::executeFrame()
979 {
980   int count = 1;
981   if(argCount != 0) count = args[0];
982   debugger.nextFrame(count);
983   commandResult << "advanced " << dec << count << " frame(s)";
984 }
985 
986 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
987 // "function"
executeFunction()988 void DebuggerParser::executeFunction()
989 {
990   if(args[0] >= 0)
991   {
992     commandResult << red("name already in use");
993     return;
994   }
995 
996   int res = YaccParser::parse(argStrings[1].c_str());
997   if(res == 0)
998   {
999     debugger.addFunction(argStrings[0], argStrings[1], YaccParser::getResult());
1000     commandResult << "Added function " << argStrings[0] << " -> " << argStrings[1];
1001   }
1002   else
1003     commandResult << red("invalid expression");
1004 }
1005 
1006 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1007 // "gfx"
executeGfx()1008 void DebuggerParser::executeGfx()
1009 {
1010   if(argCount != 2)
1011   {
1012     commandResult << red("Specify start and end of range only");
1013     return;
1014   }
1015   else if(args[1] < args[0])
1016   {
1017     commandResult << red("Start address must be <= end address");
1018     return;
1019   }
1020 
1021   bool result = debugger.cartDebug().addDirective(
1022                   CartDebug::GFX, args[0], args[1]);
1023   commandResult << (result ? "added" : "removed") << " GFX directive on range $"
1024                 << hex << args[0] << " $" << hex << args[1];
1025   debugger.rom().invalidate();
1026 }
1027 
1028 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1029 // "help"
executeHelp()1030 void DebuggerParser::executeHelp()
1031 {
1032   // Find length of longest command
1033   uInt16 clen = 0;
1034   for(int i = 0; i < kNumCommands; ++i)
1035   {
1036     uInt16 len = commands[i].cmdString.length();
1037     if(len > clen)  clen = len;
1038   }
1039 
1040   commandResult << setfill(' ');
1041   for(int i = 0; i < kNumCommands; ++i)
1042     commandResult << setw(clen) << right << commands[i].cmdString
1043                   << " - " << commands[i].description << endl;
1044 
1045   commandResult << debugger.builtinHelp();
1046 }
1047 
1048 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1049 // "jump"
executeJump()1050 void DebuggerParser::executeJump()
1051 {
1052   int line = -1;
1053   int address = args[0];
1054 
1055   // The specific address we want may not exist (it may be part of a data section)
1056   // If so, scroll backward a little until we find it
1057   while(((line = debugger.cartDebug().addressToLine(address)) == -1) &&
1058         ((address & 0xFFF) >= 0))
1059     address--;
1060 
1061   if(line >= 0 && address >= 0)
1062   {
1063     debugger.rom().scrollTo(line);
1064     commandResult << "disassembly scrolled to address $" << Base::HEX4 << address;
1065   }
1066   else
1067     commandResult << "address $" << Base::HEX4 << args[0] << " doesn't exist";
1068 }
1069 
1070 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1071 // "listbreaks"
executeListbreaks()1072 void DebuggerParser::executeListbreaks()
1073 {
1074   ostringstream buf;
1075   int count = 0;
1076 
1077   for(unsigned int i = 0; i < 0x10000; i++)
1078   {
1079     if(debugger.breakpoints().isSet(i))
1080     {
1081       buf << debugger.cartDebug().getLabel(i, true, 4) << " ";
1082       if(! (++count % 8) ) buf << "\n";
1083     }
1084   }
1085 
1086   /*
1087   if(count)
1088     return ret;
1089   else
1090     return "no breakpoints set";
1091     */
1092   if(count)
1093     commandResult << "breaks:\n" << buf.str();
1094 
1095   StringList conds = debugger.cpuDebug().m6502().getCondBreakNames();
1096   if(conds.size() > 0)
1097   {
1098     commandResult << "\nbreakifs:\n";
1099     for(unsigned int i = 0; i < conds.size(); i++)
1100     {
1101       commandResult << i << ": " << conds[i];
1102       if(i != (conds.size() - 1)) commandResult << endl;
1103     }
1104   }
1105 
1106   if(commandResult.str() == "")
1107     commandResult << "no breakpoints set";
1108 }
1109 
1110 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1111 // "listconfig"
executeListconfig()1112 void DebuggerParser::executeListconfig()
1113 {
1114   if(argCount == 1)
1115     commandResult << debugger.cartDebug().listConfig(args[0]);
1116   else
1117     commandResult << debugger.cartDebug().listConfig();
1118 }
1119 
1120 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1121 // "listfunctions"
executeListfunctions()1122 void DebuggerParser::executeListfunctions()
1123 {
1124   const FunctionDefMap& functions = debugger.getFunctionDefMap();
1125 
1126   if(functions.size() > 0)
1127   {
1128     FunctionDefMap::const_iterator iter;
1129     for(iter = functions.begin(); iter != functions.end(); ++iter)
1130       commandResult << iter->first << " -> " << iter->second << endl;
1131   }
1132   else
1133     commandResult << "no user-defined functions";
1134 }
1135 
1136 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1137 // "listtraps"
executeListtraps()1138 void DebuggerParser::executeListtraps()
1139 {
1140   int count = 0;
1141 
1142   for(unsigned int i=0; i<0x10000; i++)
1143   {
1144     if(debugger.readTrap(i) || debugger.writeTrap(i))
1145     {
1146       commandResult << trapStatus(i) << endl;
1147       count++;
1148     }
1149   }
1150 
1151   if(!count)
1152     commandResult << "no traps set";
1153 }
1154 
1155 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1156 // "loadconfig"
executeLoadconfig()1157 void DebuggerParser::executeLoadconfig()
1158 {
1159   commandResult << debugger.cartDebug().loadConfigFile();
1160 }
1161 
1162 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1163 // "loadstate"
executeLoadstate()1164 void DebuggerParser::executeLoadstate()
1165 {
1166   if(args[0] >= 0 && args[0] <= 9)
1167     debugger.loadState(args[0]);
1168   else
1169     commandResult << red("invalid slot (must be 0-9)");
1170 }
1171 
1172 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1173 // "n"
executeN()1174 void DebuggerParser::executeN()
1175 {
1176   if(argCount == 0)
1177     debugger.cpuDebug().toggleN();
1178   else if(argCount == 1)
1179     debugger.cpuDebug().setN(args[0]);
1180 }
1181 
1182 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1183 // "pc"
executePc()1184 void DebuggerParser::executePc()
1185 {
1186   debugger.cpuDebug().setPC(args[0]);
1187 }
1188 
1189 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1190 // "pgfx"
executePGfx()1191 void DebuggerParser::executePGfx()
1192 {
1193   if(argCount != 2)
1194   {
1195     commandResult << red("Specify start and end of range only");
1196     return;
1197   }
1198   else if(args[1] < args[0])
1199   {
1200     commandResult << red("Start address must be <= end address");
1201     return;
1202   }
1203 
1204   bool result = debugger.cartDebug().addDirective(
1205                   CartDebug::PGFX, args[0], args[1]);
1206   commandResult << (result ? "added" : "removed") << " PGFX directive on range $"
1207                 << hex << args[0] << " $" << hex << args[1];
1208   debugger.rom().invalidate();
1209 }
1210 
1211 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1212 // "print"
executePrint()1213 void DebuggerParser::executePrint()
1214 {
1215   commandResult << eval();
1216 }
1217 
1218 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1219 // "ram"
executeRam()1220 void DebuggerParser::executeRam()
1221 {
1222   if(argCount == 0)
1223     commandResult << debugger.cartDebug().toString();
1224   else
1225     commandResult << debugger.setRAM(args);
1226 }
1227 
1228 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1229 // "reset"
executeReset()1230 void DebuggerParser::executeReset()
1231 {
1232   debugger.reset();
1233   debugger.rom().invalidate();
1234   commandResult << "reset CPU";
1235 }
1236 
1237 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1238 // "rewind"
executeRewind()1239 void DebuggerParser::executeRewind()
1240 {
1241   if(debugger.rewindState())
1242   {
1243     debugger.rom().invalidate();
1244     commandResult << "rewind by one level";
1245   }
1246   else
1247     commandResult << "no states left to rewind";
1248 }
1249 
1250 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1251 // "riot"
executeRiot()1252 void DebuggerParser::executeRiot()
1253 {
1254   commandResult << debugger.riotDebug().toString();
1255 }
1256 
1257 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1258 // "rom"
executeRom()1259 void DebuggerParser::executeRom()
1260 {
1261   int addr = args[0];
1262   for(int i=1; i<argCount; i++)
1263   {
1264     if( !(debugger.patchROM(addr++, args[i])) )
1265     {
1266       commandResult << red("patching ROM unsupported for this cart type");
1267       return;
1268     }
1269   }
1270 
1271   // Normally the run() method calls loadConfig() on the debugger,
1272   // which results in all child widgets being redrawn.
1273   // The RomWidget is a special case, since we don't want to re-disassemble
1274   // any more than necessary.  So we only do it by calling the following
1275   // method ...
1276   debugger.rom().invalidate();
1277 
1278   commandResult << "changed " << (args.size() - 1) << " location(s)";
1279 }
1280 
1281 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1282 // "row"
executeRow()1283 void DebuggerParser::executeRow()
1284 {
1285   if(argCount != 2)
1286   {
1287     commandResult << red("Specify start and end of range only");
1288     return;
1289   }
1290   else if(args[1] < args[0])
1291   {
1292     commandResult << red("Start address must be <= end address");
1293     return;
1294   }
1295 
1296   bool result = debugger.cartDebug().addDirective(
1297                   CartDebug::ROW, args[0], args[1]);
1298   commandResult << (result ? "added" : "removed") << " ROW directive on range $"
1299                 << hex << args[0] << " $" << hex << args[1];
1300   debugger.rom().invalidate();
1301 }
1302 
1303 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1304 // "run"
executeRun()1305 void DebuggerParser::executeRun()
1306 {
1307   debugger.saveOldState();
1308   debugger.quit(false);
1309   commandResult << "_EXIT_DEBUGGER";  // See PromptWidget for more info
1310 }
1311 
1312 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1313 // "runto"
executeRunTo()1314 void DebuggerParser::executeRunTo()
1315 {
1316   const CartDebug& cartdbg = debugger.cartDebug();
1317   const CartDebug::DisassemblyList& list = cartdbg.disassembly().list;
1318 
1319   uInt32 count = 0, max_iterations = list.size();
1320 
1321   // Create a progress dialog box to show the progress searching through the
1322   // disassembly, since this may be a time-consuming operation
1323   ostringstream buf;
1324   buf << "RunTo searching through " << max_iterations << " disassembled instructions";
1325   ProgressDialog progress(debugger.myBaseDialog, debugger.lfont(), buf.str());
1326   progress.setRange(0, max_iterations, 5);
1327 
1328   bool done = false;
1329   do {
1330     debugger.step();
1331 
1332     // Update romlist to point to current PC
1333     int pcline = cartdbg.addressToLine(debugger.cpuDebug().pc());
1334     if(pcline >= 0)
1335     {
1336       const string& next = list[pcline].disasm;
1337       done = (BSPF_findIgnoreCase(next, argStrings[0]) != string::npos);
1338     }
1339     // Update the progress bar
1340     progress.setProgress(count);
1341   } while(!done && ++count < max_iterations);
1342 
1343   progress.close();
1344 
1345   if(done)
1346     commandResult
1347       << "found " << argStrings[0] << " in " << dec << count
1348       << " disassembled instructions";
1349   else
1350     commandResult
1351       << argStrings[0] << " not found in " << dec << count
1352       << " disassembled instructions";
1353 }
1354 
1355 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1356 // "runtopc"
executeRunToPc()1357 void DebuggerParser::executeRunToPc()
1358 {
1359   const CartDebug& cartdbg = debugger.cartDebug();
1360   const CartDebug::DisassemblyList& list = cartdbg.disassembly().list;
1361 
1362   uInt32 count = 0;
1363   bool done = false;
1364   do {
1365     debugger.step();
1366 
1367     // Update romlist to point to current PC
1368     int pcline = cartdbg.addressToLine(debugger.cpuDebug().pc());
1369     done = (pcline >= 0) && (list[pcline].address == args[0]);
1370     ++count;
1371   } while(!done && count < list.size());
1372 
1373   if(done)
1374     commandResult
1375       << "set PC to " << Base::HEX4 << args[0] << " in "
1376       << dec << count << " disassembled instructions";
1377   else
1378     commandResult
1379       << "PC " << Base::HEX4 << args[0] << " not reached or found in "
1380       << dec << count << " disassembled instructions";
1381 }
1382 
1383 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1384 // "s"
executeS()1385 void DebuggerParser::executeS()
1386 {
1387   debugger.cpuDebug().setSP((uInt8)args[0]);
1388 }
1389 
1390 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1391 // "save"
executeSave()1392 void DebuggerParser::executeSave()
1393 {
1394   if(saveScriptFile(argStrings[0]))
1395     commandResult << "saved script to file " << argStrings[0];
1396   else
1397     commandResult << red("I/O error");
1398 }
1399 
1400 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1401 // "saveconfig"
executeSaveconfig()1402 void DebuggerParser::executeSaveconfig()
1403 {
1404   commandResult << debugger.cartDebug().saveConfigFile();
1405 }
1406 
1407 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1408 // "savedis"
executeSavedisassembly()1409 void DebuggerParser::executeSavedisassembly()
1410 {
1411   commandResult << debugger.cartDebug().saveDisassembly();
1412 }
1413 
1414 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1415 // "saverom"
executeSaverom()1416 void DebuggerParser::executeSaverom()
1417 {
1418   commandResult << debugger.cartDebug().saveRom();
1419 }
1420 
1421 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1422 // "saveses"
executeSaveses()1423 void DebuggerParser::executeSaveses()
1424 {
1425   if(debugger.prompt().saveBuffer(argStrings[0]))
1426     commandResult << "saved session to file " << argStrings[0];
1427   else
1428     commandResult << red("I/O error");
1429 }
1430 
1431 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1432 // "savestate"
executeSavestate()1433 void DebuggerParser::executeSavestate()
1434 {
1435   if(args[0] >= 0 && args[0] <= 9)
1436     debugger.saveState(args[0]);
1437   else
1438     commandResult << red("invalid slot (must be 0-9)");
1439 }
1440 
1441 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1442 // "scanline"
executeScanline()1443 void DebuggerParser::executeScanline()
1444 {
1445   int count = 1;
1446   if(argCount != 0) count = args[0];
1447   debugger.nextScanline(count);
1448   commandResult << "advanced " << dec << count << " scanline(s)";
1449 }
1450 
1451 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1452 // "step"
executeStep()1453 void DebuggerParser::executeStep()
1454 {
1455   commandResult
1456     << "executed " << dec << debugger.step() << " cycles";
1457 }
1458 
1459 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1460 // "tia"
executeTia()1461 void DebuggerParser::executeTia()
1462 {
1463   commandResult << debugger.tiaDebug().toString();
1464 }
1465 
1466 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1467 // "trace"
executeTrace()1468 void DebuggerParser::executeTrace()
1469 {
1470   commandResult << "executed " << dec << debugger.trace() << " cycles";
1471 }
1472 
1473 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1474 // "trap"
executeTrap()1475 void DebuggerParser::executeTrap()
1476 {
1477   uInt32 beg = args[0];
1478   uInt32 end = argCount >= 2 ? args[1] : beg;
1479   if(beg > end)  BSPF_swap(beg, end);
1480 
1481   for(uInt32 i = beg; i <= end; ++i)
1482   {
1483     debugger.toggleReadTrap(i);
1484     debugger.toggleWriteTrap(i);
1485     commandResult << trapStatus(i) << endl;
1486   }
1487 }
1488 
1489 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1490 // "trapread"
executeTrapread()1491 void DebuggerParser::executeTrapread()
1492 {
1493   uInt32 beg = args[0];
1494   uInt32 end = argCount >= 2 ? args[1] : beg;
1495   if(beg > end)  BSPF_swap(beg, end);
1496 
1497   for(uInt32 i = beg; i <= end; ++i)
1498   {
1499     debugger.toggleReadTrap(i);
1500     commandResult << trapStatus(i) << endl;
1501   }
1502 }
1503 
1504 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1505 // "trapwrite"
executeTrapwrite()1506 void DebuggerParser::executeTrapwrite()
1507 {
1508   uInt32 beg = args[0];
1509   uInt32 end = argCount >= 2 ? args[1] : beg;
1510   if(beg > end)  BSPF_swap(beg, end);
1511 
1512   for(uInt32 i = beg; i <= end; ++i)
1513   {
1514     debugger.toggleWriteTrap(i);
1515     commandResult << trapStatus(i) << endl;
1516   }
1517 }
1518 
1519 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1520 // "type"
executeType()1521 void DebuggerParser::executeType()
1522 {
1523   uInt32 beg = args[0];
1524   uInt32 end = argCount >= 2 ? args[1] : beg;
1525   if(beg > end)  BSPF_swap(beg, end);
1526 
1527   for(uInt32 i = beg; i <= end; ++i)
1528   {
1529     commandResult << Base::HEX4 << i << ": ";
1530     debugger.cartDebug().addressTypeAsString(commandResult, i);
1531     commandResult << endl;
1532   }
1533 }
1534 
1535 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1536 // "uhex"
executeUHex()1537 void DebuggerParser::executeUHex()
1538 {
1539   bool enable = !Base::hexUppercase();
1540   Base::setHexUppercase(enable);
1541 
1542   settings.setValue("dbg.uhex", enable);
1543   debugger.rom().invalidate();
1544 
1545   commandResult << "uppercase HEX " << (enable ? "enabled" : "disabled");
1546 }
1547 
1548 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1549 // "undef"
executeUndef()1550 void DebuggerParser::executeUndef()
1551 {
1552   if(debugger.cartDebug().removeLabel(argStrings[0]))
1553   {
1554     debugger.rom().invalidate();
1555     commandResult << argStrings[0] + " now undefined";
1556   }
1557   else
1558     commandResult << red("no such label");
1559 }
1560 
1561 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1562 // "v"
executeV()1563 void DebuggerParser::executeV()
1564 {
1565   if(argCount == 0)
1566     debugger.cpuDebug().toggleV();
1567   else if(argCount == 1)
1568     debugger.cpuDebug().setV(args[0]);
1569 }
1570 
1571 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1572 // "watch"
executeWatch()1573 void DebuggerParser::executeWatch()
1574 {
1575   watches.push_back(argStrings[0]);
1576   commandResult << "added watch \"" << argStrings[0] << "\"";
1577 }
1578 
1579 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1580 // "x"
executeX()1581 void DebuggerParser::executeX()
1582 {
1583   debugger.cpuDebug().setX((uInt8)args[0]);
1584 }
1585 
1586 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1587 // "y"
executeY()1588 void DebuggerParser::executeY()
1589 {
1590   debugger.cpuDebug().setY((uInt8)args[0]);
1591 }
1592 
1593 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1594 // "z"
executeZ()1595 void DebuggerParser::executeZ()
1596 {
1597   if(argCount == 0)
1598     debugger.cpuDebug().toggleZ();
1599   else if(argCount == 1)
1600     debugger.cpuDebug().setZ(args[0]);
1601 }
1602 
1603 
1604 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1605 // List of all commands available to the parser
1606 DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
1607   {
1608     "a",
1609     "Set Accumulator to value xx",
1610     true,
1611     true,
1612     { kARG_WORD, kARG_END_ARGS },
1613     &DebuggerParser::executeA
1614   },
1615 
1616   {
1617     "bank",
1618     "Show # of banks, or switch to bank xx",
1619     false,
1620     true,
1621     { kARG_WORD, kARG_END_ARGS },
1622     &DebuggerParser::executeBank
1623   },
1624 
1625   {
1626     "base",
1627     "Set default base (hex, dec, or bin)",
1628     true,
1629     true,
1630     { kARG_BASE_SPCL, kARG_END_ARGS },
1631     &DebuggerParser::executeBase
1632   },
1633 
1634   {
1635     "break",
1636     "Set/clear breakpoint at address xx (default=PC)",
1637     false,
1638     true,
1639     { kARG_WORD, kARG_END_ARGS },
1640     &DebuggerParser::executeBreak
1641   },
1642 
1643   {
1644     "breakif",
1645     "Set breakpoint on condition xx",
1646     true,
1647     false,
1648     { kARG_WORD, kARG_END_ARGS },
1649     &DebuggerParser::executeBreakif
1650   },
1651 
1652   {
1653     "c",
1654     "Carry Flag: set (0 or 1), or toggle (no arg)",
1655     false,
1656     true,
1657     { kARG_BOOL, kARG_END_ARGS },
1658     &DebuggerParser::executeC
1659   },
1660 
1661   {
1662     "cheat",
1663     "Use a cheat code (see manual for cheat types)",
1664     false,
1665     false,
1666     { kARG_LABEL, kARG_END_ARGS },
1667     &DebuggerParser::executeCheat
1668   },
1669 
1670   {
1671     "clearbreaks",
1672     "Clear all breakpoints",
1673     false,
1674     true,
1675     { kARG_END_ARGS },
1676     &DebuggerParser::executeClearbreaks
1677   },
1678 
1679   {
1680     "clearconfig",
1681     "Clear Distella config directives [bank xx]",
1682     false,
1683     false,
1684     { kARG_WORD, kARG_MULTI_BYTE },
1685     &DebuggerParser::executeClearconfig
1686   },
1687 
1688   {
1689     "cleartraps",
1690     "Clear all traps",
1691     false,
1692     false,
1693     { kARG_END_ARGS },
1694     &DebuggerParser::executeCleartraps
1695   },
1696 
1697   {
1698     "clearwatches",
1699     "Clear all watches",
1700     false,
1701     false,
1702     { kARG_END_ARGS },
1703     &DebuggerParser::executeClearwatches
1704   },
1705 
1706   {
1707     "cls",
1708     "Clear prompt area of text and erase history",
1709     false,
1710     false,
1711     { kARG_END_ARGS },
1712     &DebuggerParser::executeCls
1713   },
1714 
1715   {
1716     "code",
1717     "Mark 'CODE' range in disassembly",
1718     true,
1719     false,
1720     { kARG_WORD, kARG_MULTI_BYTE },
1721     &DebuggerParser::executeCode
1722   },
1723 
1724   {
1725     "colortest",
1726     "Show value xx as TIA color",
1727     true,
1728     false,
1729     { kARG_WORD, kARG_END_ARGS },
1730     &DebuggerParser::executeColortest
1731   },
1732 
1733   {
1734     "d",
1735     "Decimal Flag: set (0 or 1), or toggle (no arg)",
1736     false,
1737     true,
1738     { kARG_BOOL, kARG_END_ARGS },
1739     &DebuggerParser::executeD
1740   },
1741 
1742   {
1743     "data",
1744     "Mark 'DATA' range in disassembly",
1745     true,
1746     false,
1747     { kARG_WORD, kARG_MULTI_BYTE },
1748     &DebuggerParser::executeData
1749   },
1750 
1751   {
1752     "define",
1753     "Define label xx for address yy",
1754     true,
1755     true,
1756     { kARG_LABEL, kARG_WORD, kARG_END_ARGS },
1757     &DebuggerParser::executeDefine
1758   },
1759 
1760   {
1761     "delbreakif",
1762     "Delete conditional breakif xx",
1763     true,
1764     false,
1765     { kARG_WORD, kARG_END_ARGS },
1766     &DebuggerParser::executeDelbreakif
1767   },
1768 
1769   {
1770     "delfunction",
1771     "Delete function with label xx",
1772     true,
1773     false,
1774     { kARG_LABEL, kARG_END_ARGS },
1775     &DebuggerParser::executeDelfunction
1776   },
1777 
1778   {
1779     "delwatch",
1780     "Delete watch xx",
1781     true,
1782     false,
1783     { kARG_WORD, kARG_END_ARGS },
1784     &DebuggerParser::executeDelwatch
1785   },
1786 
1787   {
1788     "disasm",
1789     "Disassemble address xx [yy lines] (default=PC)",
1790     false,
1791     false,
1792     { kARG_WORD, kARG_MULTI_BYTE },
1793     &DebuggerParser::executeDisasm
1794   },
1795 
1796   {
1797     "dump",
1798     "Dump 128 bytes of memory at address xx",
1799     true,
1800     false,
1801     { kARG_WORD, kARG_END_ARGS },
1802     &DebuggerParser::executeDump
1803   },
1804 
1805   {
1806     "exec",
1807     "Execute script file xx",
1808     true,
1809     true,
1810     { kARG_FILE, kARG_END_ARGS },
1811     &DebuggerParser::executeExec
1812   },
1813 
1814   {
1815     "exitrom",
1816     "Exit emulator, return to ROM launcher",
1817     false,
1818     false,
1819     { kARG_END_ARGS },
1820     &DebuggerParser::executeExitRom
1821   },
1822 
1823   {
1824     "frame",
1825     "Advance emulation by xx frames (default=1)",
1826     false,
1827     true,
1828     { kARG_WORD, kARG_END_ARGS },
1829     &DebuggerParser::executeFrame
1830   },
1831 
1832   {
1833     "function",
1834     "Define function name xx for expression yy",
1835     true,
1836     false,
1837     { kARG_LABEL, kARG_WORD, kARG_END_ARGS },
1838     &DebuggerParser::executeFunction
1839   },
1840 
1841   {
1842     "gfx",
1843     "Mark 'CFX' range in disassembly",
1844     true,
1845     false,
1846     { kARG_WORD, kARG_MULTI_BYTE },
1847     &DebuggerParser::executeGfx
1848   },
1849 
1850   {
1851     "help",
1852     "This cruft",
1853     false,
1854     false,
1855     { kARG_END_ARGS },
1856     &DebuggerParser::executeHelp
1857   },
1858 
1859   {
1860     "jump",
1861     "Scroll disassembly to address xx",
1862     true,
1863     false,
1864     { kARG_WORD, kARG_END_ARGS },
1865     &DebuggerParser::executeJump
1866   },
1867 
1868   {
1869     "listbreaks",
1870     "List breakpoints",
1871     false,
1872     false,
1873     { kARG_END_ARGS },
1874     &DebuggerParser::executeListbreaks
1875   },
1876 
1877   {
1878     "listconfig",
1879     "List Distella config directives [bank xx]",
1880     false,
1881     false,
1882     { kARG_WORD, kARG_MULTI_BYTE },
1883     &DebuggerParser::executeListconfig
1884   },
1885 
1886   {
1887     "listfunctions",
1888     "List user-defined functions",
1889     false,
1890     false,
1891     { kARG_END_ARGS },
1892     &DebuggerParser::executeListfunctions
1893   },
1894 
1895   {
1896     "listtraps",
1897     "List traps",
1898     false,
1899     false,
1900     { kARG_END_ARGS },
1901     &DebuggerParser::executeListtraps
1902   },
1903 
1904   {
1905     "loadconfig",
1906     "Load Distella config file",
1907     false,
1908     true,
1909     { kARG_END_ARGS },
1910     &DebuggerParser::executeLoadconfig
1911   },
1912 
1913   {
1914     "loadstate",
1915     "Load emulator state xx (0-9)",
1916     true,
1917     true,
1918     { kARG_WORD, kARG_END_ARGS },
1919     &DebuggerParser::executeLoadstate
1920   },
1921 
1922   {
1923     "n",
1924     "Negative Flag: set (0 or 1), or toggle (no arg)",
1925     false,
1926     true,
1927     { kARG_BOOL, kARG_END_ARGS },
1928     &DebuggerParser::executeN
1929   },
1930 
1931   {
1932     "pc",
1933     "Set Program Counter to address xx",
1934     true,
1935     true,
1936     { kARG_WORD, kARG_END_ARGS },
1937     &DebuggerParser::executePc
1938   },
1939 
1940   {
1941     "pgfx",
1942     "Mark 'PGFX' range in disassembly",
1943     true,
1944     false,
1945     { kARG_WORD, kARG_MULTI_BYTE },
1946     &DebuggerParser::executePGfx
1947   },
1948 
1949   {
1950     "print",
1951     "Evaluate/print expression xx in hex/dec/binary",
1952     true,
1953     false,
1954     { kARG_WORD, kARG_END_ARGS },
1955     &DebuggerParser::executePrint
1956   },
1957 
1958   {
1959     "ram",
1960     "Show ZP RAM, or set address xx to yy1 [yy2 ...]",
1961     false,
1962     true,
1963     { kARG_WORD, kARG_MULTI_BYTE },
1964     &DebuggerParser::executeRam
1965   },
1966 
1967   {
1968     "reset",
1969     "Reset 6507 to init vector (excluding TIA/RIOT)",
1970     false,
1971     true,
1972     { kARG_END_ARGS },
1973     &DebuggerParser::executeReset
1974   },
1975 
1976   {
1977     "rewind",
1978     "Rewind state to last step/trace/scanline/frame",
1979     false,
1980     true,
1981     { kARG_END_ARGS },
1982     &DebuggerParser::executeRewind
1983   },
1984 
1985   {
1986     "riot",
1987     "Show RIOT timer/input status",
1988     false,
1989     false,
1990     { kARG_END_ARGS },
1991     &DebuggerParser::executeRiot
1992   },
1993 
1994   {
1995     "rom",
1996     "Set ROM address xx to yy1 [yy2 ...]",
1997     true,
1998     true,
1999     { kARG_WORD, kARG_MULTI_BYTE },
2000     &DebuggerParser::executeRom
2001   },
2002 
2003   {
2004     "row",
2005     "Mark 'ROW' range in disassembly",
2006     true,
2007     false,
2008     { kARG_WORD, kARG_MULTI_BYTE },
2009     &DebuggerParser::executeRow
2010   },
2011 
2012   {
2013     "run",
2014     "Exit debugger, return to emulator",
2015     false,
2016     false,
2017     { kARG_END_ARGS },
2018     &DebuggerParser::executeRun
2019   },
2020 
2021   {
2022     "runto",
2023     "Run until string xx in disassembly",
2024     true,
2025     true,
2026     { kARG_LABEL, kARG_END_ARGS },
2027     &DebuggerParser::executeRunTo
2028   },
2029 
2030   {
2031     "runtopc",
2032     "Run until PC is set to value xx",
2033     true,
2034     true,
2035     { kARG_WORD, kARG_END_ARGS },
2036     &DebuggerParser::executeRunToPc
2037   },
2038 
2039   {
2040     "s",
2041     "Set Stack Pointer to value xx",
2042     true,
2043     true,
2044     { kARG_WORD, kARG_END_ARGS },
2045     &DebuggerParser::executeS
2046   },
2047 
2048   {
2049     "save",
2050     "Save breaks, watches, traps to file xx",
2051     true,
2052     false,
2053     { kARG_FILE, kARG_END_ARGS },
2054     &DebuggerParser::executeSave
2055   },
2056 
2057   {
2058     "saveconfig",
2059     "Save Distella config file",
2060     false,
2061     false,
2062     { kARG_END_ARGS },
2063     &DebuggerParser::executeSaveconfig
2064   },
2065 
2066   {
2067     "savedis",
2068     "Save Distella disassembly",
2069     false,
2070     false,
2071     { kARG_END_ARGS },
2072     &DebuggerParser::executeSavedisassembly
2073   },
2074 
2075   {
2076     "saverom",
2077     "Save (possibly patched) ROM",
2078     false,
2079     false,
2080     { kARG_END_ARGS },
2081     &DebuggerParser::executeSaverom
2082   },
2083 
2084   {
2085     "saveses",
2086     "Save console session to file xx",
2087     true,
2088     false,
2089     { kARG_FILE, kARG_END_ARGS },
2090     &DebuggerParser::executeSaveses
2091   },
2092 
2093   {
2094     "savestate",
2095     "Save emulator state xx (valid args 0-9)",
2096     true,
2097     false,
2098     { kARG_WORD, kARG_END_ARGS },
2099     &DebuggerParser::executeSavestate
2100   },
2101 
2102   {
2103     "scanline",
2104     "Advance emulation by xx scanlines (default=1)",
2105     false,
2106     true,
2107     { kARG_WORD, kARG_END_ARGS },
2108     &DebuggerParser::executeScanline
2109   },
2110 
2111   {
2112     "step",
2113     "Single step CPU [with count xx]",
2114     false,
2115     true,
2116     { kARG_WORD, kARG_END_ARGS },
2117     &DebuggerParser::executeStep
2118   },
2119 
2120   {
2121     "tia",
2122     "Show TIA state (NOT FINISHED YET)",
2123     false,
2124     false,
2125     { kARG_END_ARGS },
2126     &DebuggerParser::executeTia
2127   },
2128 
2129   {
2130     "trace",
2131     "Single step CPU over subroutines [with count xx]",
2132     false,
2133     true,
2134     { kARG_WORD, kARG_END_ARGS },
2135     &DebuggerParser::executeTrace
2136   },
2137 
2138   {
2139     "trap",
2140     "Trap read/write access to address(es) xx [to yy]",
2141     true,
2142     false,
2143     { kARG_WORD, kARG_MULTI_BYTE },
2144     &DebuggerParser::executeTrap
2145   },
2146 
2147   {
2148     "trapread",
2149     "Trap read access to address(es) xx [to yy]",
2150     true,
2151     false,
2152     { kARG_WORD, kARG_MULTI_BYTE },
2153     &DebuggerParser::executeTrapread
2154   },
2155 
2156   {
2157     "trapwrite",
2158     "Trap write access to address(es) xx [to yy]",
2159     true,
2160     false,
2161     { kARG_WORD, kARG_MULTI_BYTE },
2162     &DebuggerParser::executeTrapwrite
2163   },
2164 
2165   {
2166     "type",
2167     "Show disassembly type for address xx [to yy]",
2168     true,
2169     false,
2170     { kARG_WORD, kARG_MULTI_BYTE },
2171     &DebuggerParser::executeType
2172   },
2173 
2174   {
2175     "uhex",
2176     "Toggle upper/lowercase HEX display",
2177     false,
2178     true,
2179     { kARG_END_ARGS },
2180     &DebuggerParser::executeUHex
2181   },
2182 
2183   {
2184     "undef",
2185     "Undefine label xx (if defined)",
2186     true,
2187     true,
2188     { kARG_LABEL, kARG_END_ARGS },
2189     &DebuggerParser::executeUndef
2190   },
2191 
2192   {
2193     "v",
2194     "Overflow Flag: set (0 or 1), or toggle (no arg)",
2195     false,
2196     true,
2197     { kARG_BOOL, kARG_END_ARGS },
2198     &DebuggerParser::executeV
2199   },
2200 
2201   {
2202     "watch",
2203     "Print contents of address xx before every prompt",
2204     true,
2205     false,
2206     { kARG_WORD, kARG_END_ARGS },
2207     &DebuggerParser::executeWatch
2208   },
2209 
2210   {
2211     "x",
2212     "Set X Register to value xx",
2213     true,
2214     true,
2215     { kARG_WORD, kARG_END_ARGS },
2216     &DebuggerParser::executeX
2217   },
2218 
2219   {
2220     "y",
2221     "Set Y Register to value xx",
2222     true,
2223     true,
2224     { kARG_WORD, kARG_END_ARGS },
2225     &DebuggerParser::executeY
2226   },
2227 
2228   {
2229     "z",
2230     "Zero Flag: set (0 or 1), or toggle (no arg)",
2231     false,
2232     true,
2233     { kARG_BOOL, kARG_END_ARGS },
2234     &DebuggerParser::executeZ
2235   }
2236 };
2237