| 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 | |