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