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