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