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" |
41 | using Common::Base; |
42 | using std::hex; |
43 | using std::dec; |
44 | using std::setfill; |
45 | using std::setw; |
46 | using 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
58 | DebuggerParser::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. |
70 | string 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
132 | string 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
163 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
175 | void 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() |
190 | int 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
302 | string 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
330 | bool 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
407 | bool 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 | /* |
509 | cerr << "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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
531 | string 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
569 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
602 | string 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
631 | string 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" |
691 | void DebuggerParser::executeA() |
692 | { |
693 | debugger.cpuDebug().setA(uInt8(args[0])); |
694 | } |
695 | |
696 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
697 | // "base" |
698 | void 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" |
729 | void 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" |
787 | void 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" |
812 | void 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" |
829 | void 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) |
840 | void 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" |
864 | void DebuggerParser::executeClearbreaks() |
865 | { |
866 | debugger.clearAllBreakPoints(); |
867 | debugger.m6502().clearCondBreaks(); |
868 | commandResult << "all breakpoints cleared" ; |
869 | } |
870 | |
871 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
872 | // "clearconfig" |
873 | void 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" |
883 | void DebuggerParser::executeClearsavestateifs() |
884 | { |
885 | debugger.m6502().clearCondSaveStates(); |
886 | commandResult << "all savestate points cleared" ; |
887 | } |
888 | |
889 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
890 | // "cleartraps" |
891 | void DebuggerParser::executeCleartraps() |
892 | { |
893 | debugger.clearAllTraps(); |
894 | debugger.m6502().clearCondTraps(); |
895 | myTraps.clear(); |
896 | commandResult << "all traps cleared" ; |
897 | } |
898 | |
899 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
900 | // "clearwatches" |
901 | void DebuggerParser::executeClearwatches() |
902 | { |
903 | myWatches.clear(); |
904 | commandResult << "all watches cleared" ; |
905 | } |
906 | |
907 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
908 | // "cls" |
909 | void DebuggerParser::executeCls() |
910 | { |
911 | debugger.prompt().clearScreen(); |
912 | commandResult << "" ; |
913 | } |
914 | |
915 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
916 | // "code" |
917 | void 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" |
939 | void DebuggerParser::executeColortest() |
940 | { |
941 | commandResult << "test color: " |
942 | << char((args[0]>>1) | 0x80) |
943 | << inverse(" " ); |
944 | } |
945 | |
946 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
947 | // "d" |
948 | void 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" |
958 | void 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" |
980 | void DebuggerParser::executeDebugColors() |
981 | { |
982 | commandResult << debugger.tiaDebug().debugColors(); |
983 | } |
984 | |
985 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
986 | // "define" |
987 | void 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" |
996 | void 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" |
1006 | void 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" |
1016 | void 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" |
1026 | void 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" |
1044 | void 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" |
1058 | void 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" |
1079 | void 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" |
1204 | void 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" |
1238 | void DebuggerParser::executeExitRom() |
1239 | { |
1240 | debugger.quit(true); |
1241 | } |
1242 | |
1243 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1244 | // "frame" |
1245 | void 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" |
1255 | void 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" |
1275 | void 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" |
1297 | void 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" |
1331 | void 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" |
1342 | void 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" |
1353 | void 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" |
1364 | void 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" |
1375 | void 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" |
1386 | void 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" |
1397 | void 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" |
1408 | void 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" |
1419 | void 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" |
1430 | void 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" |
1441 | void 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" |
1463 | void 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" |
1511 | void 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" |
1521 | void 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" |
1536 | void 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" |
1557 | void 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" |
1587 | void DebuggerParser::executeLoadallstates() |
1588 | { |
1589 | debugger.loadAllStates(); |
1590 | } |
1591 | |
1592 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1593 | // "loadconfig" |
1594 | void DebuggerParser::executeLoadconfig() |
1595 | { |
1596 | commandResult << debugger.cartDebug().loadConfigFile(); |
1597 | } |
1598 | |
1599 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1600 | // "loadstate" |
1601 | void 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" |
1611 | void 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" |
1621 | void DebuggerParser::executePalette() |
1622 | { |
1623 | commandResult << debugger.tiaDebug().palette(); |
1624 | } |
1625 | |
1626 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1627 | // "pc" |
1628 | void DebuggerParser::executePc() |
1629 | { |
1630 | debugger.cpuDebug().setPC(args[0]); |
1631 | } |
1632 | |
1633 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1634 | // "pgfx" |
1635 | void 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" |
1657 | void DebuggerParser::executePrint() |
1658 | { |
1659 | commandResult << eval(); |
1660 | } |
1661 | |
1662 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1663 | // "ram" |
1664 | void DebuggerParser::executeRam() |
1665 | { |
1666 | if(argCount == 0) |
1667 | commandResult << debugger.cartDebug().toString(); |
1668 | else |
1669 | commandResult << debugger.setRAM(args); |
1670 | } |
1671 | |
1672 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1673 | // "reset" |
1674 | void 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" |
1689 | void DebuggerParser::executeRewind() |
1690 | { |
1691 | executeWinds(false); |
1692 | } |
1693 | |
1694 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1695 | // "riot" |
1696 | void DebuggerParser::executeRiot() |
1697 | { |
1698 | commandResult << debugger.riotDebug().toString(); |
1699 | } |
1700 | |
1701 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1702 | // "rom" |
1703 | void 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" |
1727 | void 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" |
1749 | void DebuggerParser::executeRun() |
1750 | { |
1751 | debugger.saveOldState(); |
1752 | debugger.quit(false); |
1753 | commandResult << "_EXIT_DEBUGGER" ; // See PromptWidget for more info |
1754 | } |
1755 | |
1756 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1757 | // "runto" |
1758 | void 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" |
1801 | void 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" |
1828 | void DebuggerParser::executeS() |
1829 | { |
1830 | debugger.cpuDebug().setSP(uInt8(args[0])); |
1831 | } |
1832 | |
1833 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1834 | // "save" |
1835 | void DebuggerParser::executeSave() |
1836 | { |
1837 | commandResult << saveScriptFile(argStrings[0]); |
1838 | } |
1839 | |
1840 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1841 | // "saveconfig" |
1842 | void DebuggerParser::executeSaveconfig() |
1843 | { |
1844 | commandResult << debugger.cartDebug().saveConfigFile(); |
1845 | } |
1846 | |
1847 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1848 | // "savedis" |
1849 | void DebuggerParser::executeSavedisassembly() |
1850 | { |
1851 | commandResult << debugger.cartDebug().saveDisassembly(); |
1852 | } |
1853 | |
1854 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1855 | // "saverom" |
1856 | void DebuggerParser::executeSaverom() |
1857 | { |
1858 | commandResult << debugger.cartDebug().saveRom(); |
1859 | } |
1860 | |
1861 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1862 | // "saveses" |
1863 | void 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" |
1878 | void DebuggerParser::executeSavesnap() |
1879 | { |
1880 | debugger.tiaOutput().saveSnapshot(execDepth, execPrefix); |
1881 | } |
1882 | |
1883 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1884 | // "saveallstates" |
1885 | void DebuggerParser::executeSaveallstates() |
1886 | { |
1887 | debugger.saveAllStates(); |
1888 | } |
1889 | |
1890 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1891 | // "savestate" |
1892 | void 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" |
1902 | void 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" |
1927 | void 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" |
1937 | void DebuggerParser::executeStep() |
1938 | { |
1939 | commandResult |
1940 | << "executed " << dec << debugger.step() << " cycles" ; |
1941 | } |
1942 | |
1943 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1944 | // "stepwhile" |
1945 | void 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" |
1962 | void DebuggerParser::executeTia() |
1963 | { |
1964 | commandResult << debugger.tiaDebug().toString(); |
1965 | } |
1966 | |
1967 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1968 | // "trace" |
1969 | void DebuggerParser::executeTrace() |
1970 | { |
1971 | commandResult << "executed " << dec << debugger.trace() << " cycles" ; |
1972 | } |
1973 | |
1974 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1975 | // "trap" |
1976 | void DebuggerParser::executeTrap() |
1977 | { |
1978 | executeTraps(true, true, "trap" ); |
1979 | } |
1980 | |
1981 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1982 | // "trapif" |
1983 | void DebuggerParser::executeTrapif() |
1984 | { |
1985 | executeTraps(true, true, "trapif" , true); |
1986 | } |
1987 | |
1988 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1989 | // "trapread" |
1990 | void DebuggerParser::executeTrapread() |
1991 | { |
1992 | executeTraps(true, false, "trapread" ); |
1993 | } |
1994 | |
1995 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1996 | // "trapreadif" |
1997 | void DebuggerParser::executeTrapreadif() |
1998 | { |
1999 | executeTraps(true, false, "trapreadif" , true); |
2000 | } |
2001 | |
2002 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
2003 | // "trapwrite" |
2004 | void DebuggerParser::executeTrapwrite() |
2005 | { |
2006 | executeTraps(false, true, "trapwrite" ); |
2007 | } |
2008 | |
2009 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
2010 | // "trapwriteif" |
2011 | void DebuggerParser::executeTrapwriteif() |
2012 | { |
2013 | executeTraps(false, true, "trapwriteif" , true); |
2014 | } |
2015 | |
2016 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
2017 | // Wrapper function for trap(if)s |
2018 | void 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 |
2124 | void 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" |
2193 | void 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" |
2209 | void 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" |
2222 | void 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" |
2235 | void DebuggerParser::executeUnwind() |
2236 | { |
2237 | executeWinds(true); |
2238 | } |
2239 | |
2240 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
2241 | // "v" |
2242 | void 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" |
2252 | void 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 |
2260 | void 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" |
2284 | void DebuggerParser::executeX() |
2285 | { |
2286 | debugger.cpuDebug().setX(uInt8(args[0])); |
2287 | } |
2288 | |
2289 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
2290 | // "y" |
2291 | void DebuggerParser::executeY() |
2292 | { |
2293 | debugger.cpuDebug().setY(uInt8(args[0])); |
2294 | } |
2295 | |
2296 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
2297 | // "z" |
2298 | void 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 |
2308 | std::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 | |