|
|
1.1 ! root 1: /* ! 2: Hatari - debugcpu.c ! 3: ! 4: This file is distributed under the GNU Public License, version 2 or at ! 5: your option any later version. Read the file gpl.txt for details. ! 6: ! 7: debugcpu.c - function needed for the CPU debugging tasks like memory ! 8: and register dumps. ! 9: */ ! 10: const char DebugCpu_fileid[] = "Hatari debugcpu.c : " __DATE__ " " __TIME__; ! 11: ! 12: #include <stdio.h> ! 13: ! 14: #include "config.h" ! 15: ! 16: #include "main.h" ! 17: #include "breakcond.h" ! 18: #include "configuration.h" ! 19: #include "debugui.h" ! 20: #include "debug_priv.h" ! 21: #include "debugcpu.h" ! 22: #include "evaluate.h" ! 23: #include "hatari-glue.h" ! 24: #include "log.h" ! 25: #include "m68000.h" ! 26: #include "memorySnapShot.h" ! 27: #include "nextMemory.h" ! 28: #include "str.h" ! 29: #include "symbols.h" ! 30: ! 31: #define MEMDUMP_COLS 16 /* memdump, number of bytes per row */ ! 32: #define NON_PRINT_CHAR '.' /* character to display for non-printables */ ! 33: ! 34: static Uint32 disasm_addr=0; /* disasm address */ ! 35: static Uint32 memdump_addr=0; /* memdump address */ ! 36: ! 37: static int nCpuActiveCBs = 0; /* Amount of active conditional breakpoints */ ! 38: static int nCpuSteps = 0; /* Amount of steps for CPU single-stepping */ ! 39: ! 40: ! 41: /** ! 42: * Load a binary file to a memory address. ! 43: */ ! 44: static int DebugCpu_LoadBin(int nArgc, char *psArgs[]) ! 45: { ! 46: FILE *fp; ! 47: unsigned char c; ! 48: Uint32 address; ! 49: int i=0; ! 50: ! 51: if (nArgc < 3) ! 52: { ! 53: DebugUI_PrintCmdHelp(psArgs[0]); ! 54: return DEBUGGER_CMDDONE; ! 55: } ! 56: ! 57: if (!Eval_Number(psArgs[2], &address)) ! 58: { ! 59: fprintf(stderr, "Invalid address!\n"); ! 60: return DEBUGGER_CMDDONE; ! 61: } ! 62: ! 63: if ((fp = fopen(psArgs[1], "rb")) == NULL) ! 64: { ! 65: fprintf(stderr, "Cannot open file '%s'!\n", psArgs[1]); ! 66: return DEBUGGER_CMDDONE; ! 67: } ! 68: ! 69: c = fgetc(fp); ! 70: while (!feof(fp)) ! 71: { ! 72: i++; ! 73: NEXTMemory_WriteByte(address++, c); ! 74: c = fgetc(fp); ! 75: } ! 76: fprintf(stderr," Read 0x%x bytes.\n", i); ! 77: fclose(fp); ! 78: ! 79: return DEBUGGER_CMDDONE; ! 80: } ! 81: ! 82: ! 83: /** ! 84: * Dump memory from an address to a binary file. ! 85: */ ! 86: static int DebugCpu_SaveBin(int nArgc, char *psArgs[]) ! 87: { ! 88: FILE *fp; ! 89: unsigned char c; ! 90: Uint32 address; ! 91: Uint32 bytes, i = 0; ! 92: ! 93: if (nArgc < 4) ! 94: { ! 95: DebugUI_PrintCmdHelp(psArgs[0]); ! 96: return DEBUGGER_CMDDONE; ! 97: } ! 98: ! 99: if (!Eval_Number(psArgs[2], &address)) ! 100: { ! 101: fprintf(stderr, " Invalid address!\n"); ! 102: return DEBUGGER_CMDDONE; ! 103: } ! 104: ! 105: if (!Eval_Number(psArgs[3], &bytes)) ! 106: { ! 107: fprintf(stderr, " Invalid length!\n"); ! 108: return DEBUGGER_CMDDONE; ! 109: } ! 110: ! 111: if ((fp = fopen(psArgs[1], "wb")) == NULL) ! 112: { ! 113: fprintf(stderr," Cannot open file '%s'!\n", psArgs[1]); ! 114: return DEBUGGER_CMDDONE; ! 115: } ! 116: ! 117: while (i < bytes) ! 118: { ! 119: c = NEXTMemory_ReadByte(address++); ! 120: fputc(c, fp); ! 121: i++; ! 122: } ! 123: fclose(fp); ! 124: fprintf(stderr, " Wrote 0x%x bytes.\n", bytes); ! 125: ! 126: return DEBUGGER_CMDDONE; ! 127: } ! 128: ! 129: ! 130: /** ! 131: * Check whether given address matches any CPU symbol, if yes, ! 132: * show the symbol information. ! 133: */ ! 134: static void DebugCpu_ShowMatchedSymbol(Uint32 addr) ! 135: { ! 136: const char *symbol = Symbols_GetByCpuAddress(addr); ! 137: if (symbol) ! 138: fprintf(debugOutput, "%s:\n", symbol); ! 139: } ! 140: ! 141: /** ! 142: * Dissassemble - arg = starting address, or PC. ! 143: */ ! 144: int DebugCpu_DisAsm(int nArgc, char *psArgs[]) ! 145: { ! 146: Uint32 disasm_upper = 0; ! 147: int insts, max_insts; ! 148: uaecptr nextpc; ! 149: FILE* mydebugOutput=debugOutput; ! 150: ! 151: if (nArgc > 1) ! 152: { ! 153: switch (Eval_Range(psArgs[1], &disasm_addr, &disasm_upper)) ! 154: { ! 155: case -1: ! 156: /* invalid value(s) */ ! 157: return DEBUGGER_CMDDONE; ! 158: case 0: ! 159: /* single value */ ! 160: break; ! 161: case 1: ! 162: /* range */ ! 163: break; ! 164: } ! 165: if (nArgc > 2) { ! 166: mydebugOutput=fopen(psArgs[2],"w"); ! 167: if (mydebugOutput==NULL) ! 168: { ! 169: fprintf(debugOutput,"Cannot open %s abort\n",psArgs[2]); ! 170: return DEBUGGER_CMDDONE; ! 171: } ! 172: } ! 173: } ! 174: else ! 175: { ! 176: /* continue */ ! 177: if(!disasm_addr) ! 178: disasm_addr = M68000_GetPC(); ! 179: } ! 180: ! 181: /* limit is topmost address or instruction count */ ! 182: if (disasm_upper) ! 183: { ! 184: max_insts = INT_MAX; ! 185: } ! 186: else ! 187: { ! 188: // max_insts = ConfigureParams.Debugger.nDisasmLines; ! 189: max_insts=5; ! 190: } ! 191: ! 192: /* output a range */ ! 193: for (insts = 0; insts < max_insts && disasm_addr < disasm_upper; insts++) ! 194: { ! 195: DebugCpu_ShowMatchedSymbol(disasm_addr); ! 196: m68k_disasm(mydebugOutput, (uaecptr)disasm_addr, &nextpc, 1); ! 197: disasm_addr = nextpc; ! 198: } ! 199: fflush(mydebugOutput); ! 200: if (mydebugOutput!=debugOutput) {fclose(mydebugOutput);} ! 201: ! 202: return DEBUGGER_CMDCONT; ! 203: } ! 204: ! 205: ! 206: /** ! 207: * Readline match callback to list register names usable within debugger. ! 208: * STATE = 0 -> different text from previous one. ! 209: * Return next match or NULL if no matches. ! 210: */ ! 211: static char *DebugCpu_MatchRegister(const char *text, int state) ! 212: { ! 213: static const char regs[][3] = { ! 214: "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", ! 215: "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", ! 216: "pc", "sr" ! 217: }; ! 218: static int i, len; ! 219: ! 220: if (!state) ! 221: { ! 222: /* first match */ ! 223: i = 0; ! 224: len = strlen(text); ! 225: if (len > 2) ! 226: return NULL; ! 227: } ! 228: /* next match */ ! 229: while (i < ARRAYSIZE(regs)) { ! 230: if (strncasecmp(regs[i++], text, len) == 0) ! 231: return (strdup(regs[i-1])); ! 232: } ! 233: return NULL; ! 234: } ! 235: ! 236: ! 237: /** ! 238: * Set address of the named register to given argument. ! 239: * Return register size in bits or zero for uknown register name. ! 240: * Handles D0-7 data and A0-7 address registers, but not PC & SR ! 241: * registers as they need to be accessed using UAE accessors. ! 242: */ ! 243: int DebugCpu_GetRegisterAddress(const char *reg, Uint32 **addr) ! 244: { ! 245: char r0, r1; ! 246: if (!reg[0] || !reg[1] || reg[2]) ! 247: return 0; ! 248: ! 249: r0 = toupper(reg[0]); ! 250: r1 = toupper(reg[1]); ! 251: ! 252: if (r0 == 'D') /* Data regs? */ ! 253: { ! 254: if (r1 >= '0' && r1 <= '7') ! 255: { ! 256: *addr = &(Regs[REG_D0 + r1 - '0']); ! 257: return 32; ! 258: } ! 259: fprintf(stderr,"\tBad data register, valid values are 0-7\n"); ! 260: return 0; ! 261: } ! 262: if(r0 == 'A') /* Address regs? */ ! 263: { ! 264: if (r1 >= '0' && r1 <= '7') ! 265: { ! 266: *addr = &(Regs[REG_A0 + r1 - '0']); ! 267: return 32; ! 268: } ! 269: fprintf(stderr,"\tBad address register, valid values are 0-7\n"); ! 270: return 0; ! 271: } ! 272: return 0; ! 273: } ! 274: ! 275: ! 276: /** ! 277: * Dump or set CPU registers ! 278: */ ! 279: int DebugCpu_Register(int nArgc, char *psArgs[]) ! 280: { ! 281: char reg[3], *assign; ! 282: Uint32 value; ! 283: char *arg; ! 284: ! 285: /* If no parameter has been given, simply dump all registers */ ! 286: if (nArgc == 1) ! 287: { ! 288: uaecptr nextpc; ! 289: /* use the UAE function instead */ ! 290: m68k_dumpstate(debugOutput, &nextpc); ! 291: fflush(debugOutput); ! 292: return DEBUGGER_CMDDONE; ! 293: } ! 294: ! 295: arg = psArgs[1]; ! 296: ! 297: assign = strchr(arg, '='); ! 298: if (!assign) ! 299: { ! 300: goto error_msg; ! 301: } ! 302: ! 303: *assign++ = '\0'; ! 304: if (!Eval_Number(Str_Trim(assign), &value)) ! 305: { ! 306: goto error_msg; ! 307: } ! 308: ! 309: arg = Str_Trim(arg); ! 310: if (strlen(arg) != 2) ! 311: { ! 312: goto error_msg; ! 313: } ! 314: reg[0] = toupper(arg[0]); ! 315: reg[1] = toupper(arg[1]); ! 316: reg[2] = '\0'; ! 317: ! 318: /* set SR and update conditional flags for the UAE CPU core. */ ! 319: if (reg[0] == 'S' && reg[1] == 'R') ! 320: { ! 321: M68000_SetSR(value); ! 322: } ! 323: else if (reg[0] == 'P' && reg[1] == 'C') /* set PC? */ ! 324: { ! 325: M68000_SetPC(value); ! 326: } ! 327: else ! 328: { ! 329: Uint32 *regaddr; ! 330: /* check&set data and address registers */ ! 331: if (DebugCpu_GetRegisterAddress(reg, ®addr)) ! 332: { ! 333: *regaddr = value; ! 334: } ! 335: else ! 336: { ! 337: goto error_msg; ! 338: } ! 339: } ! 340: return DEBUGGER_CMDDONE; ! 341: ! 342: error_msg: ! 343: fprintf(stderr,"\tError, usage: r or r xx=yyyy\n\tWhere: xx=A0-A7, D0-D7, PC or SR.\n"); ! 344: return DEBUGGER_CMDDONE; ! 345: } ! 346: ! 347: ! 348: /** ! 349: * CPU wrapper for BreakAddr_Command/BreakPointCount. ! 350: */ ! 351: static int DebugCpu_BreakAddr(int nArgc, char *psArgs[]) ! 352: { ! 353: BreakAddr_Command(psArgs[1], false); ! 354: return DEBUGGER_CMDDONE; ! 355: } ! 356: ! 357: /** ! 358: * CPU wrapper for BreakCond_Command/BreakPointCount. ! 359: */ ! 360: static int DebugCpu_BreakCond(int nArgc, char *psArgs[]) ! 361: { ! 362: BreakCond_Command(psArgs[1], false); ! 363: return DEBUGGER_CMDDONE; ! 364: } ! 365: ! 366: ! 367: /** ! 368: * Do a memory dump, args = starting address. ! 369: */ ! 370: int DebugCpu_MemDump(int nArgc, char *psArgs[]) ! 371: { ! 372: int i; ! 373: char c; ! 374: Uint32 memdump_upper = 0; ! 375: ! 376: if (nArgc > 1) ! 377: { ! 378: switch (Eval_Range(psArgs[1], &memdump_addr, &memdump_upper)) ! 379: { ! 380: case -1: ! 381: /* invalid value(s) */ ! 382: return DEBUGGER_CMDDONE; ! 383: case 0: ! 384: /* single value */ ! 385: break; ! 386: case 1: ! 387: /* range */ ! 388: break; ! 389: } ! 390: } /* continue */ ! 391: ! 392: if (!memdump_upper) ! 393: { ! 394: memdump_upper = memdump_addr + MEMDUMP_COLS * ConfigureParams.Debugger.nMemdumpLines; ! 395: } ! 396: ! 397: while (memdump_addr < memdump_upper) ! 398: { ! 399: fprintf(debugOutput, "%6.6X: ", memdump_addr); /* print address */ ! 400: for (i = 0; i < MEMDUMP_COLS; i++) /* print hex data */ ! 401: fprintf(debugOutput, "%2.2x ", NEXTMemory_ReadByte(memdump_addr++)); ! 402: fprintf(debugOutput, " "); /* print ASCII data */ ! 403: for (i = 0; i < MEMDUMP_COLS; i++) ! 404: { ! 405: c = NEXTMemory_ReadByte(memdump_addr-MEMDUMP_COLS+i); ! 406: if(!isprint((unsigned)c)) ! 407: c = NON_PRINT_CHAR; /* non-printable as dots */ ! 408: fprintf(debugOutput,"%c", c); ! 409: } ! 410: fprintf(debugOutput, "\n"); /* newline */ ! 411: } /* while */ ! 412: fflush(debugOutput); ! 413: ! 414: return DEBUGGER_CMDCONT; ! 415: } ! 416: ! 417: ! 418: /** ! 419: * Command: Write to memory, arg = starting address, followed by bytes. ! 420: */ ! 421: static int DebugCpu_MemWrite(int nArgc, char *psArgs[]) ! 422: { ! 423: int i, numBytes; ! 424: Uint32 write_addr, d; ! 425: unsigned char bytes[256]; /* store bytes */ ! 426: ! 427: if (nArgc < 3) ! 428: { ! 429: DebugUI_PrintCmdHelp(psArgs[0]); ! 430: return DEBUGGER_CMDDONE; ! 431: } ! 432: ! 433: /* Read address */ ! 434: if (!Eval_Number(psArgs[1], &write_addr)) ! 435: { ! 436: fprintf(stderr, "Bad address!\n"); ! 437: return DEBUGGER_CMDDONE; ! 438: } ! 439: ! 440: numBytes = 0; ! 441: ! 442: /* get bytes data */ ! 443: for (i = 2; i < nArgc; i++) ! 444: { ! 445: if (!Eval_Number(psArgs[i], &d) || d > 255) ! 446: { ! 447: fprintf(stderr, "Bad byte argument: '%s'!\n", psArgs[i]); ! 448: return DEBUGGER_CMDDONE; ! 449: } ! 450: ! 451: bytes[numBytes] = d & 0x0FF; ! 452: numBytes++; ! 453: } ! 454: ! 455: /* write the data */ ! 456: for (i = 0; i < numBytes; i++) ! 457: NEXTMemory_WriteByte(write_addr + i, bytes[i]); ! 458: ! 459: return DEBUGGER_CMDDONE; ! 460: } ! 461: ! 462: ! 463: /** ! 464: * Command: Continue CPU emulation / single-stepping ! 465: */ ! 466: static int DebugCpu_Continue(int nArgc, char *psArgv[]) ! 467: { ! 468: int steps = 0; ! 469: ! 470: if (nArgc > 1) ! 471: { ! 472: steps = atoi(psArgv[1]); ! 473: } ! 474: if (steps <= 0) ! 475: { ! 476: nCpuSteps = 0; ! 477: fprintf(stderr,"Returning to emulation...\n"); ! 478: return DEBUGGER_END; ! 479: } ! 480: nCpuSteps = steps; ! 481: fprintf(stderr,"Returning to emulation for %i CPU instructions...\n", steps); ! 482: return DEBUGGER_END; ! 483: } ! 484: ! 485: ! 486: /** ! 487: * This function is called after each CPU instruction when debugging is enabled. ! 488: */ ! 489: void DebugCpu_Check(void) ! 490: { ! 491: if (LOG_TRACE_LEVEL(TRACE_CPU_DISASM)) ! 492: { ! 493: DebugCpu_ShowMatchedSymbol(M68000_GetPC()); ! 494: } ! 495: if (nCpuActiveCBs) ! 496: { ! 497: if (BreakCond_MatchCpu()) ! 498: DebugUI(); ! 499: } ! 500: if (nCpuSteps) ! 501: { ! 502: nCpuSteps -= 1; ! 503: if (nCpuSteps == 0) ! 504: DebugUI(); ! 505: } ! 506: } ! 507: ! 508: /** ! 509: * Should be called before returning back emulation to tell the CPU core ! 510: * to call us after each instruction if "real-time" debugging like ! 511: * breakpoints has been set. ! 512: */ ! 513: void DebugCpu_SetDebugging(void) ! 514: { ! 515: nCpuActiveCBs = BreakCond_BreakPointCount(false); ! 516: if (nCpuActiveCBs || nCpuSteps) ! 517: M68000_SetSpecial(SPCFLAG_DEBUGGER); ! 518: else ! 519: M68000_UnsetSpecial(SPCFLAG_DEBUGGER); ! 520: } ! 521: ! 522: ! 523: static const dbgcommand_t cpucommands[] = ! 524: { ! 525: { NULL, NULL, "CPU commands", NULL, NULL, NULL, false }, ! 526: /* NULL as match function will complete file names */ ! 527: { DebugCpu_BreakAddr, Symbols_MatchCpuCodeAddress, ! 528: "address", "a", ! 529: "set CPU PC address breakpoints", ! 530: BreakAddr_Description, ! 531: true }, ! 532: { DebugCpu_BreakCond, BreakCond_MatchCpuVariable, ! 533: "breakpoint", "b", ! 534: "set/remove/list conditional CPU breakpoints", ! 535: BreakCond_Description, ! 536: true }, ! 537: { DebugCpu_DisAsm, Symbols_MatchCpuCodeAddress, ! 538: "disasm", "d", ! 539: "disassemble from PC, or given address", ! 540: "[<start address>[-<end address>]]\n" ! 541: "\tIf no address is given, this command disassembles from the last\n" ! 542: "\tposition or from current PC if no last position is available.", ! 543: false }, ! 544: { DebugCpu_Register, DebugCpu_MatchRegister, ! 545: "cpureg", "r", ! 546: "dump register values or set register to value", ! 547: "[REG=value]\n" ! 548: "\tSet CPU register to value or dumps all register if no parameter\n" ! 549: "\thas been specified.", ! 550: true }, ! 551: { DebugCpu_MemDump, Symbols_MatchCpuDataAddress, ! 552: "memdump", "m", ! 553: "dump memory", ! 554: "[<start address>[-<end address>]]\n" ! 555: "\tdump memory at address or continue dump from previous address.", ! 556: false }, ! 557: { DebugCpu_MemWrite, Symbols_MatchCpuAddress, ! 558: "memwrite", "w", ! 559: "write bytes to memory", ! 560: "address byte1 [byte2 ...]\n" ! 561: "\tWrite bytes to a memory address, bytes are space separated\n" ! 562: "\thexadecimals.", ! 563: false }, ! 564: { DebugCpu_LoadBin, NULL, ! 565: "loadbin", "l", ! 566: "load a file into memory", ! 567: "filename address\n" ! 568: "\tLoad the file <filename> into memory starting at <address>.", ! 569: false }, ! 570: { DebugCpu_SaveBin, NULL, ! 571: "savebin", "s", ! 572: "save memory to a file", ! 573: "filename address length\n" ! 574: "\tSave the memory block at <address> with given <length> to\n" ! 575: "\tthe file <filename>.", ! 576: false }, ! 577: { Symbols_Command, NULL, ! 578: "symbols", "", ! 579: "load CPU symbols & their addresses", ! 580: Symbols_Description, ! 581: false }, ! 582: { DebugCpu_Continue, NULL, ! 583: "cont", "c", ! 584: "continue emulation / CPU single-stepping", ! 585: "[steps]\n" ! 586: "\tLeave debugger and continue emulation for <steps> CPU instructions\n" ! 587: "\tor forever if no steps have been specified.", ! 588: false } ! 589: }; ! 590: ! 591: ! 592: /** ! 593: * Should be called when debugger is first entered to initialize ! 594: * CPU debugging variables. ! 595: * ! 596: * if you want disassembly or memdumping to start/continue from ! 597: * specific address, you can set them here. If disassembly ! 598: * address is zero, disassembling starts from PC. ! 599: * ! 600: * returns number of CPU commands and pointer to array of them. ! 601: */ ! 602: int DebugCpu_Init(const dbgcommand_t **table) ! 603: { ! 604: memdump_addr = 0; ! 605: disasm_addr = 0; ! 606: ! 607: *table = cpucommands; ! 608: return ARRAYSIZE(cpucommands); ! 609: } ! 610: ! 611: /** ! 612: * Should be called when debugger is re-entered to reset ! 613: * relevant CPU debugging variables. ! 614: */ ! 615: void DebugCpu_InitSession(void) ! 616: { ! 617: disasm_addr = M68000_GetPC(); ! 618: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.