|
|
1.1 root 1: /*
1.1.1.6 root 2: Hatari - debugui.c
1.1 root 3:
1.1.1.4 root 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:
1.1.1.6 root 7: debugui.c - this is the code for the mini-debugger. When the pause button is
8: pressed, the emulator is (hopefully) halted and this little CLI can be used
9: (in the terminal box) for debugging tasks like memory and register dumps.
1.1 root 10: */
1.1.1.10 root 11: const char DebugUI_fileid[] = "Hatari debugui.c : " __DATE__ " " __TIME__;
1.1 root 12:
13: #include <ctype.h>
1.1.1.8 root 14: #include <stdio.h>
15:
16: #include "config.h"
17:
18: #if HAVE_LIBREADLINE
19: #include <readline/readline.h>
20: #include <readline/history.h>
21: #endif
1.1 root 22:
23: #include "main.h"
1.1.1.9 root 24: #include "change.h"
1.1 root 25: #include "configuration.h"
1.1.1.11! root 26: #include "memorySnapShot.h"
1.1.1.9 root 27: #include "file.h"
1.1 root 28: #include "reset.h"
29: #include "m68000.h"
1.1.1.9 root 30: #include "str.h"
1.1.1.3 root 31: #include "stMemory.h"
1.1 root 32: #include "sound.h"
33: #include "tos.h"
1.1.1.11! root 34: #include "options.h"
1.1.1.4 root 35: #include "debugui.h"
1.1.1.11! root 36: #include "breakcond.h"
1.1.1.8 root 37: #include "hatari-glue.h"
1.1.1.11! root 38: #include "screen.h"
! 39: #include "statusbar.h"
! 40: #include "video.h"
! 41:
! 42: int bExceptionDebugging;
1.1 root 43:
44: #define MEMDUMP_COLS 16 /* memdump, number of bytes per row */
45: #define MEMDUMP_ROWS 4 /* memdump, number of rows */
1.1.1.2 root 46: #define NON_PRINT_CHAR '.' /* character to display for non-printables */
1.1 root 47: #define DISASM_INSTS 5 /* disasm - number of instructions */
48:
1.1.1.11! root 49: static Uint32 disasm_addr; /* disasm address */
! 50: static Uint32 memdump_addr; /* memdump address */
! 51:
! 52: static Uint16 dsp_disasm_addr; /* DSP disasm address */
! 53: static Uint16 dsp_memdump_addr; /* DSP memdump address */
! 54: static char dsp_mem_space = 'P'; /* X, Y, P */
1.1.1.6 root 55:
1.1.1.9 root 56: static FILE *debugOutput;
1.1.1.2 root 57:
1.1.1.11! root 58: static Uint32 CpuBreakPoint[16]; /* 68k breakpoints */
! 59: static int nCpuActiveBPs = 0; /* Amount of active breakpoints */
! 60: static int nCpuActiveCBs = 0; /* Amount of active conditional breakpoints */
! 61: static int nCpuSteps = 0; /* Amount of steps for CPU single-stepping */
! 62:
! 63: static Uint16 DspBreakPoint[16]; /* DSP breakpoints */
! 64: static int nDspActiveBPs = 0; /* Amount of active breakpoints */
! 65: static int nDspActiveCBs = 0; /* Amount of active conditional breakpoints */
! 66: static int nDspSteps = 0; /* Amount of steps for DSP single-stepping */
1.1.1.2 root 67:
1.1.1.11! root 68: static int DebugUI_Help(int nArgc, char *psArgv[]);
! 69: static void DebugUI_PrintCmdHelp(const char *psCmd);
1.1.1.6 root 70:
71:
1.1.1.8 root 72: /**
1.1.1.11! root 73: * Save/Restore snapshot of debugging session variables
1.1.1.8 root 74: */
1.1.1.11! root 75: void DebugUI_MemorySnapShot_Capture(bool bSave)
1.1.1.6 root 76: {
1.1.1.11! root 77: MemorySnapShot_Store(&disasm_addr, sizeof(disasm_addr));
! 78: MemorySnapShot_Store(&memdump_addr, sizeof(memdump_addr));
! 79: MemorySnapShot_Store(&dsp_disasm_addr, sizeof(dsp_disasm_addr));
! 80: MemorySnapShot_Store(&dsp_memdump_addr, sizeof(dsp_memdump_addr));
! 81: MemorySnapShot_Store(&dsp_mem_space, sizeof(dsp_mem_space));
! 82:
! 83: MemorySnapShot_Store(&CpuBreakPoint, sizeof(CpuBreakPoint));
! 84: MemorySnapShot_Store(&nCpuActiveBPs, sizeof(nCpuActiveBPs));
! 85: MemorySnapShot_Store(&nCpuActiveCBs, sizeof(nCpuActiveCBs));
! 86: MemorySnapShot_Store(&DspBreakPoint, sizeof(DspBreakPoint));
! 87: MemorySnapShot_Store(&nDspActiveBPs, sizeof(nDspActiveBPs));
! 88: MemorySnapShot_Store(&nDspActiveCBs, sizeof(nDspActiveCBs));
! 89:
! 90: BreakCond_MemorySnapShot_Capture(bSave);
1.1.1.9 root 91: }
92:
93:
94: /**
95: * Close a log file if open, and set it to default stream.
96: */
97: static void DebugUI_SetLogDefault(void)
98: {
99: if (debugOutput != stderr)
100: {
101: if (debugOutput)
102: {
103: File_Close(debugOutput);
104: fprintf(stderr, "Debug log closed.\n");
105: }
106: debugOutput = stderr;
107: }
1.1.1.2 root 108: }
109:
1.1.1.6 root 110:
1.1.1.11! root 111: /**
! 112: * Open (or close) given log file.
! 113: */
! 114: static int DebugUI_SetLogFile(int nArgc, char *psArgs[])
! 115: {
! 116: File_Close(debugOutput);
! 117: debugOutput = NULL;
! 118:
! 119: if (nArgc > 1)
! 120: debugOutput = File_Open(psArgs[1], "w");
! 121:
! 122: if (debugOutput)
! 123: fprintf(stderr, "Debug log '%s' opened.\n", psArgs[1]);
! 124: else
! 125: debugOutput = stderr;
! 126:
! 127: return DEBUGGER_CMDDONE;
! 128: }
! 129:
! 130:
1.1.1.8 root 131: /**
132: * Load a binary file to a memory address.
133: */
1.1.1.11! root 134: static int DebugUI_LoadBin(int nArgc, char *psArgs[])
1.1.1.6 root 135: {
136: FILE *fp;
137: unsigned char c;
1.1.1.11! root 138: Uint32 address;
1.1.1.6 root 139: int i=0;
140:
1.1.1.11! root 141: if (nArgc < 3)
! 142: {
! 143: DebugUI_PrintCmdHelp(psArgs[0]);
! 144: return DEBUGGER_CMDDONE;
! 145: }
! 146:
! 147: if (!Str_GetNumber(psArgs[2], &address))
1.1.1.6 root 148: {
1.1.1.11! root 149: fprintf(stderr, "Invalid address!\n");
! 150: return DEBUGGER_CMDDONE;
1.1.1.6 root 151: }
152: address &= 0x00FFFFFF;
1.1.1.11! root 153:
! 154: if ((fp = fopen(psArgs[1], "rb")) == NULL)
1.1.1.6 root 155: {
1.1.1.11! root 156: fprintf(stderr, "Cannot open file '%s'!\n", psArgs[1]);
! 157: return DEBUGGER_CMDDONE;
1.1.1.6 root 158: }
159:
160: c = fgetc(fp);
161: while (!feof(fp))
162: {
163: i++;
164: STMemory_WriteByte(address++, c);
165: c = fgetc(fp);
166: }
167: fprintf(stderr," Read 0x%x bytes.\n", i);
168: fclose(fp);
1.1.1.11! root 169:
! 170: return DEBUGGER_CMDDONE;
1.1 root 171: }
172:
1.1.1.6 root 173:
1.1.1.8 root 174: /**
175: * Dump memory from an address to a binary file.
176: */
1.1.1.11! root 177: static int DebugUI_SaveBin(int nArgc, char *psArgs[])
1.1.1.6 root 178: {
179: FILE *fp;
180: unsigned char c;
1.1.1.11! root 181: Uint32 address;
! 182: Uint32 bytes, i = 0;
1.1.1.6 root 183:
1.1.1.11! root 184: if (nArgc < 4)
1.1.1.6 root 185: {
1.1.1.11! root 186: DebugUI_PrintCmdHelp(psArgs[0]);
! 187: return DEBUGGER_CMDDONE;
! 188: }
! 189:
! 190: if (!Str_GetNumber(psArgs[2], &address))
! 191: {
! 192: fprintf(stderr, " Invalid address!\n");
! 193: return DEBUGGER_CMDDONE;
1.1.1.6 root 194: }
195: address &= 0x00FFFFFF;
1.1.1.11! root 196:
! 197: if (!Str_GetNumber(psArgs[3], &bytes))
1.1.1.6 root 198: {
1.1.1.11! root 199: fprintf(stderr, " Invalid length!\n");
! 200: return DEBUGGER_CMDDONE;
! 201: }
! 202:
! 203: if ((fp = fopen(psArgs[1], "wb")) == NULL)
! 204: {
! 205: fprintf(stderr," Cannot open file '%s'!\n", psArgs[1]);
! 206: return DEBUGGER_CMDDONE;
1.1.1.6 root 207: }
208:
209: while (i < bytes)
210: {
211: c = STMemory_ReadByte(address++);
212: fputc(c, fp);
213: i++;
214: }
215: fclose(fp);
1.1.1.11! root 216: fprintf(stderr, " Wrote 0x%x bytes.\n", bytes);
! 217:
! 218: return DEBUGGER_CMDDONE;
1.1 root 219: }
220:
1.1.1.6 root 221:
1.1.1.11! root 222: #if ENABLE_DSP_EMU
! 223:
! 224: #include "dsp.h"
! 225:
1.1.1.8 root 226: /**
1.1.1.11! root 227: * Command: Dump or set a DSP register
1.1.1.8 root 228: */
1.1.1.11! root 229: static int DebugUI_DspRegister(int nArgc, char *psArgs[])
1.1 root 230: {
1.1.1.11! root 231: int i;
! 232: char reg[4], *assign;
! 233: Uint32 value;
! 234: char *arg;
! 235:
! 236: if (!bDspEnabled)
! 237: {
! 238: printf("DSP isn't present or initialized.\n");
! 239: return DEBUGGER_CMDDONE;
! 240: }
! 241:
! 242: if (nArgc == 1)
! 243: {
! 244: /* No parameter - dump all registers */
! 245: DSP_DisasmRegisters();
! 246: return DEBUGGER_CMDDONE;
! 247: }
! 248:
! 249: arg = psArgs[1];
! 250: assign = strchr(arg, '=');
! 251: /* has '=' and reg name is max. 3 letters that fit to string */
! 252: if (!assign || assign - arg > 3+1)
! 253: goto error_msg;
! 254:
! 255: *assign++ = '\0';
! 256: if (!Str_GetNumber(assign, &value))
! 257: goto error_msg;
! 258:
! 259: for (i = 0; i < 3 && arg[i]; i++)
! 260: reg[i] = toupper(arg[i]);
! 261:
! 262: DSP_Disasm_SetRegister(reg, value);
! 263: return DEBUGGER_CMDDONE;
! 264:
! 265: error_msg:
! 266: fprintf(stderr,"\tError, usage: dr or dr xx=yyyy\n"
! 267: "\tWhere: xx=A0-A2, B0-B2, X0, X1, Y0, Y1, R0-R7,\n"
! 268: "\t N0-N7, M0-M7, LA, LC, PC, SR, SP, OMR, SSH, SSL\n"
! 269: "\tand yyyy is a hex value.\n");
! 270:
! 271: return DEBUGGER_CMDDONE;
1.1 root 272: }
273:
274:
1.1.1.8 root 275: /**
1.1.1.11! root 276: * DSP dissassemble - arg = starting address/range, or PC.
1.1.1.8 root 277: */
1.1.1.11! root 278: static int DebugUI_DspDisAsm(int nArgc, char *psArgs[])
1.1.1.6 root 279: {
1.1.1.11! root 280: Uint32 lower, upper;
! 281: Uint16 dsp_disasm_upper = 0;
1.1.1.6 root 282:
1.1.1.11! root 283: if (!bDspEnabled)
1.1.1.6 root 284: {
1.1.1.11! root 285: printf("DSP isn't present or initialized.\n");
! 286: return DEBUGGER_CMDDONE;
! 287: }
1.1.1.6 root 288:
1.1.1.11! root 289: if (nArgc > 1)
! 290: {
! 291: switch (Str_ParseRange(psArgs[1], &lower, &upper))
! 292: {
! 293: case -1:
! 294: /* invalid value(s) */
! 295: return DEBUGGER_CMDDONE;
! 296: case 0:
! 297: /* single value */
! 298: break;
! 299: case 1:
! 300: /* range */
! 301: if (upper > 0xFFFF)
! 302: {
! 303: fprintf(stderr,"Invalid address '%x'!\n", upper);
! 304: return DEBUGGER_CMDDONE;
! 305: }
! 306: dsp_disasm_upper = upper;
! 307: break;
! 308: }
! 309:
! 310: if (lower > 0xFFFF)
! 311: {
! 312: fprintf(stderr,"Invalid address '%x'!\n", lower);
! 313: return DEBUGGER_CMDDONE;
! 314: }
! 315: dsp_disasm_addr = lower;
! 316: }
! 317: else
! 318: {
! 319: /* continue */
! 320: if(!dsp_disasm_addr)
! 321: {
! 322: dsp_disasm_addr = DSP_GetPC();
! 323: }
! 324: }
! 325: if (!dsp_disasm_upper)
! 326: {
! 327: if ( dsp_disasm_addr < (0xFFFF - 8))
! 328: dsp_disasm_upper = dsp_disasm_addr + 8;
! 329: else
! 330: dsp_disasm_upper = 0xFFFF;
! 331: }
! 332: printf("DSP disasm %hx-%hx:\n", dsp_disasm_addr, dsp_disasm_upper);
! 333: dsp_disasm_addr = DSP_DisasmAddress(dsp_disasm_addr, dsp_disasm_upper);
1.1.1.6 root 334:
1.1.1.11! root 335: return DEBUGGER_CMDCONT;
! 336: }
! 337:
! 338:
! 339: /**
! 340: * Do a DSP memory dump, args = starting address or range.
! 341: * <x|y|p> <address>: dump from X, Y or P, starting from given address,
! 342: * e.g. "x 200" or "p 200-300"
! 343: */
! 344: static int DebugUI_DspMemDump(int nArgc, char *psArgs[])
! 345: {
! 346: Uint32 lower, upper;
! 347: Uint16 dsp_memdump_upper = 0;
! 348: char space;
! 349:
! 350: if (!bDspEnabled)
! 351: {
! 352: printf("DSP isn't present or initialized.\n");
! 353: return DEBUGGER_CMDDONE;
! 354: }
! 355: if (nArgc == 2)
! 356: {
! 357: fprintf(stderr,"Memory space or address/range missing\n");
! 358: return DEBUGGER_CMDDONE;
! 359: }
! 360:
! 361: if (nArgc == 3)
! 362: {
! 363: space = toupper(psArgs[1][0]);
! 364: switch (space)
! 365: {
! 366: case 'X':
! 367: case 'Y':
! 368: case 'P':
! 369: break;
! 370: default:
! 371: fprintf(stderr,"Invalid DSP address space '%c'!\n", space);
! 372: return DEBUGGER_CMDDONE;
! 373: }
! 374: switch (Str_ParseRange(psArgs[2], &lower, &upper))
! 375: {
! 376: case -1:
! 377: /* invalid value(s) */
! 378: return DEBUGGER_CMDDONE;
! 379: case 0:
! 380: /* single value */
! 381: break;
! 382: case 1:
! 383: /* range */
! 384: if (upper > 0xFFFF)
1.1.1.6 root 385: {
1.1.1.11! root 386: fprintf(stderr,"Invalid address '%x'!\n", upper);
! 387: return DEBUGGER_CMDDONE;
1.1.1.6 root 388: }
1.1.1.11! root 389: dsp_memdump_upper = upper;
! 390: break;
! 391: }
! 392: if (lower > 0xFFFF)
1.1.1.6 root 393: {
1.1.1.11! root 394: fprintf(stderr,"Invalid address '%x'!\n", lower);
! 395: return DEBUGGER_CMDDONE;
1.1.1.6 root 396: }
1.1.1.11! root 397: dsp_memdump_addr = lower;
! 398: dsp_mem_space = space;
! 399: } /* continue */
! 400:
! 401: if (!dsp_memdump_upper)
! 402: {
! 403: if ( dsp_memdump_addr < (0xFFFF - 7))
! 404: dsp_memdump_upper = dsp_memdump_addr + 7;
1.1.1.6 root 405: else
1.1.1.11! root 406: dsp_memdump_upper = 0xFFFF;
! 407: }
! 408:
! 409:
! 410: printf("DSP memdump from %hx in '%c' address space\n", dsp_memdump_addr, dsp_mem_space);
! 411: DSP_DisasmMemory(dsp_memdump_addr, dsp_memdump_upper, dsp_mem_space);
! 412: dsp_memdump_addr = dsp_memdump_upper + 1;
! 413:
! 414: return DEBUGGER_CMDCONT;
! 415: }
! 416:
! 417: /**
! 418: * Toggle or list DSP breakpoints.
! 419: */
! 420: static int DebugUI_DspBreakPoint(int nArgc, char *psArgs[])
! 421: {
! 422: int i;
! 423: Uint16 addr;
! 424: Uint32 BreakAddr;
! 425:
! 426: /* List breakpoints? */
! 427: if (nArgc == 1)
! 428: {
! 429: /* No arguments - so list available breakpoints */
! 430: if (!nDspActiveBPs)
! 431: {
! 432: fputs("No DSP breakpoints set.\n", stderr);
! 433: return DEBUGGER_CMDDONE;
! 434: }
! 435:
! 436: fputs("Currently active DSP breakpoints:\n", stderr);
! 437: for (i = 0; i < nDspActiveBPs; i++)
! 438: {
! 439: addr = DspBreakPoint[i];
! 440: DSP_DisasmAddress(addr, addr);
! 441: }
! 442:
! 443: return DEBUGGER_CMDDONE;
! 444: }
! 445:
! 446: /* Parse parameter as breakpoint value */
! 447: if (!Str_GetNumber(psArgs[1], &BreakAddr) || BreakAddr > 0xFFFF)
! 448: {
! 449: fputs("Not a valid value for a DSP breakpoint!\n", stderr);
! 450: return DEBUGGER_CMDDONE;
! 451: }
! 452:
! 453: /* Is the breakpoint already in the list? Then disable it! */
! 454: for (i = 0; i < nDspActiveBPs; i++)
! 455: {
! 456: if (BreakAddr == DspBreakPoint[i])
! 457: {
! 458: DspBreakPoint[i] = DspBreakPoint[nDspActiveBPs-1];
! 459: nDspActiveBPs -= 1;
! 460: fprintf(stderr, "DSP breakpoint at %x deleted.\n", BreakAddr);
! 461: return DEBUGGER_CMDDONE;
! 462: }
! 463: }
! 464:
! 465: /* Is there at least one free slot available? */
! 466: if (nDspActiveBPs == ARRAYSIZE(DspBreakPoint))
! 467: {
! 468: fputs("No more available free DSP breakpoints!\n", stderr);
! 469: return DEBUGGER_CMDDONE;
! 470: }
! 471:
! 472: /* Add new breakpoint */
! 473: DspBreakPoint[nDspActiveBPs] = BreakAddr;
! 474: nDspActiveBPs += 1;
! 475: fprintf(stderr, "DSP breakpoint added at %x.\n", BreakAddr);
! 476:
! 477: return DEBUGGER_CMDDONE;
! 478: }
! 479:
! 480: /**
! 481: * DSP wrapper for BreakCond_Command/BreakPointCount, returns DEBUGGER_END
! 482: */
! 483: static int DebugUI_BreakCondDsp(int nArgc, char *psArgs[])
! 484: {
! 485: BreakCond_Command((const char *)psArgs[1], true);
! 486: nDspActiveCBs = BreakCond_BreakPointCount(true);
! 487: return DEBUGGER_CMDDONE;
! 488: }
! 489:
! 490: /**
! 491: * Check if we hit a DSP breakpoint
! 492: */
! 493: static void DebugUI_CheckDspBreakpoints(void)
! 494: {
! 495: Uint16 pc = DSP_GetPC();
! 496: int i;
! 497:
! 498: for (i = 0; i < nDspActiveBPs; i++)
! 499: {
! 500: if (pc == DspBreakPoint[i])
! 501: {
! 502: fprintf(stderr, "\nDSP breakpoint at %x ...", pc);
! 503: DebugUI();
! 504: break;
! 505: }
! 506: }
! 507: }
! 508:
! 509:
! 510: /**
! 511: * This function is called after each DSP instruction when debugging is enabled.
! 512: */
! 513: void DebugUI_DspCheck(void)
! 514: {
! 515: if (nDspActiveBPs)
! 516: {
! 517: DebugUI_CheckDspBreakpoints();
! 518: }
! 519: if (nDspActiveCBs)
! 520: {
! 521: if (BreakCond_MatchDsp())
! 522: DebugUI();
! 523: }
! 524: if (nDspSteps)
! 525: {
! 526: nDspSteps -= 1;
! 527: if (nDspSteps == 0)
! 528: DebugUI();
! 529: }
! 530: }
! 531:
! 532: #endif /* ENABLE_DSP_EMU */
! 533:
! 534:
! 535:
! 536: /**
! 537: * Dissassemble - arg = starting address, or PC.
! 538: */
! 539: static int DebugUI_DisAsm(int nArgc, char *psArgs[])
! 540: {
! 541: Uint32 disasm_upper = 0;
! 542: uaecptr nextpc;
! 543:
! 544: if (nArgc > 1)
! 545: {
! 546: switch (Str_ParseRange(psArgs[1], &disasm_addr, &disasm_upper))
! 547: {
! 548: case -1:
! 549: /* invalid value(s) */
! 550: return DEBUGGER_CMDDONE;
! 551: case 0:
! 552: /* single value */
! 553: break;
! 554: case 1:
! 555: /* range */
1.1.1.6 root 556: disasm_upper &= 0x00FFFFFF;
1.1.1.11! root 557: break;
1.1.1.6 root 558: }
559: }
1.1.1.11! root 560: else
! 561: {
! 562: /* continue */
1.1.1.6 root 563: if(!disasm_addr)
1.1.1.8 root 564: disasm_addr = M68000_GetPC();
1.1.1.11! root 565: }
1.1.1.6 root 566: disasm_addr &= 0x00FFFFFF;
567:
568: /* output a single block. */
1.1.1.11! root 569: if (!disasm_upper)
1.1.1.6 root 570: {
1.1.1.9 root 571: m68k_disasm(debugOutput, (uaecptr)disasm_addr, &nextpc, DISASM_INSTS);
1.1.1.6 root 572: disasm_addr = nextpc;
1.1.1.9 root 573: fflush(debugOutput);
1.1.1.11! root 574: return DEBUGGER_CMDCONT;
1.1.1.6 root 575: }
576:
577: /* output a range */
578: while (disasm_addr < disasm_upper)
579: {
1.1.1.9 root 580: m68k_disasm(debugOutput, (uaecptr)disasm_addr, &nextpc, 1);
1.1.1.6 root 581: disasm_addr = nextpc;
582: }
1.1.1.9 root 583: fflush(debugOutput);
1.1.1.11! root 584:
! 585: return DEBUGGER_CMDCONT;
1.1.1.2 root 586: }
587:
1.1.1.6 root 588:
1.1.1.8 root 589: /**
1.1.1.11! root 590: * Set address of the named register to given argument.
! 591: * Return register size in bits or zero for uknown register name.
! 592: * Handles D0-7 data and A0-7 address registers, but not PC & SR
! 593: * registers as they need to be accessed using UAE accessors.
1.1.1.8 root 594: */
1.1.1.11! root 595: int DebugUI_GetCpuRegisterAddress(const char *reg, Uint32 **addr)
1.1.1.6 root 596: {
1.1.1.11! root 597: char r0, r1;
! 598: if (!reg[0] || !reg[1] || reg[2])
! 599: return 0;
! 600:
! 601: r0 = toupper(reg[0]);
! 602: r1 = toupper(reg[1]);
! 603:
! 604: if (r0 == 'D') /* Data regs? */
1.1.1.6 root 605: {
1.1.1.11! root 606: if (r1 >= '0' && r1 <= '7')
1.1.1.6 root 607: {
1.1.1.11! root 608: *addr = &(Regs[REG_D0 + r1 - '0']);
! 609: return 32;
1.1.1.6 root 610: }
1.1.1.11! root 611: fprintf(stderr,"\tBad data register, valid values are 0-7\n");
! 612: return 0;
! 613: }
! 614: if(r0 == 'A') /* Address regs? */
! 615: {
! 616: if (r1 >= '0' && r1 <= '7')
! 617: {
! 618: *addr = &(Regs[REG_A0 + r1 - '0']);
! 619: return 32;
! 620: }
! 621: fprintf(stderr,"\tBad address register, valid values are 0-7\n");
! 622: return 0;
1.1.1.6 root 623: }
1.1.1.11! root 624: return 0;
! 625: }
! 626:
! 627:
! 628: /**
! 629: * Dump or set CPU registers
! 630: */
! 631: static int DebugUI_CpuRegister(int nArgc, char *psArgs[])
! 632: {
! 633: int i;
! 634: char reg[3], *assign;
! 635: Uint32 value;
! 636: char *arg;
! 637:
! 638: /* If no parameter has been given, simply dump all registers */
! 639: if (nArgc == 1)
! 640: {
! 641: uaecptr nextpc;
! 642: /* use the UAE function instead */
! 643: m68k_dumpstate(debugOutput, &nextpc);
! 644: fflush(debugOutput);
! 645: return DEBUGGER_CMDDONE;
! 646: }
! 647:
! 648: arg = psArgs[1];
1.1.1.6 root 649:
1.1.1.11! root 650: assign = strchr(arg, '=');
! 651: /* has '=' and reg name is max. 2 letters that fit to string */
! 652: if (!assign || assign - arg > 2+1)
1.1.1.6 root 653: {
654: fprintf(stderr,"\tError, usage: r or r xx=yyyy\n\tWhere: xx=A0-A7, D0-D7, PC or SR and yyyy is a hex value.\n");
1.1.1.11! root 655: return DEBUGGER_CMDDONE;
1.1.1.6 root 656: }
657:
1.1.1.11! root 658: *assign++ = '\0';
! 659: if (!Str_GetNumber(assign, &value))
1.1.1.6 root 660: {
661: fprintf(stderr,"\tError, usage: r or r xx=yyyy\n\tWhere: xx=A0-A7, D0-D7, PC or SR and yyyy is a hex value.\n");
1.1.1.11! root 662: return DEBUGGER_CMDDONE;
1.1.1.6 root 663: }
1.1.1.11! root 664:
! 665: for (i = 0; i < 2 && arg[i]; i++)
! 666: {
! 667: reg[i] = toupper(arg[i]);
! 668: }
! 669:
1.1.1.6 root 670: /* set SR and update conditional flags for the UAE CPU core. */
671: if (reg[0] == 'S' && reg[1] == 'R')
672: {
1.1.1.8 root 673: M68000_SetSR(value);
1.1.1.6 root 674: }
675: else if (reg[0] == 'P' && reg[1] == 'C') /* set PC? */
676: {
1.1.1.8 root 677: M68000_SetPC(value);
1.1.1.6 root 678: }
1.1.1.11! root 679: else
1.1.1.6 root 680: {
1.1.1.11! root 681: Uint32 *regaddr;
! 682: /* check&set data and address registers */
! 683: if (DebugUI_GetCpuRegisterAddress(reg, ®addr))
1.1.1.6 root 684: {
1.1.1.11! root 685: *regaddr = value;
! 686: }
! 687: else
! 688: {
! 689: fprintf(stderr, "\t Bad register!\n");
1.1.1.6 root 690: }
691: }
1.1.1.11! root 692: return DEBUGGER_CMDDONE;
! 693: }
! 694:
! 695:
! 696: /**
! 697: * Toggle or list CPU breakpoints.
! 698: */
! 699: static int DebugUI_CpuBreakPoint(int nArgc, char *psArgs[])
! 700: {
! 701: int i;
! 702: uaecptr nextpc;
! 703: Uint32 BreakAddr;
! 704:
! 705: /* List breakpoints? */
! 706: if (nArgc == 1)
1.1.1.6 root 707: {
1.1.1.11! root 708: /* No arguments - so list available breakpoints */
! 709: if (!nCpuActiveBPs)
1.1.1.6 root 710: {
1.1.1.11! root 711: fputs("No CPU breakpoints set.\n", stderr);
! 712: return DEBUGGER_CMDDONE;
! 713: }
1.1.1.6 root 714:
1.1.1.11! root 715: fputs("Currently active CPU breakpoints:\n", stderr);
! 716: for (i = 0; i < nCpuActiveBPs; i++)
! 717: {
! 718: m68k_disasm(stderr, (uaecptr)CpuBreakPoint[i], &nextpc, 1);
1.1.1.6 root 719: }
1.1.1.11! root 720:
! 721: return DEBUGGER_CMDDONE;
1.1.1.6 root 722: }
1.1.1.11! root 723:
! 724: /* Parse parameter as breakpoint value */
! 725: if (!Str_GetNumber(psArgs[1], &BreakAddr)
! 726: || (BreakAddr > STRamEnd && BreakAddr < 0xe00000)
! 727: || BreakAddr > 0xff0000)
1.1.1.6 root 728: {
1.1.1.11! root 729: fputs("Not a valid value for a CPU breakpoint!\n", stderr);
! 730: return DEBUGGER_CMDDONE;
1.1.1.6 root 731: }
1.1.1.11! root 732:
! 733: /* Is the breakpoint already in the list? Then disable it! */
! 734: for (i = 0; i < nCpuActiveBPs; i++)
! 735: {
! 736: if (BreakAddr == CpuBreakPoint[i])
! 737: {
! 738: CpuBreakPoint[i] = CpuBreakPoint[nCpuActiveBPs-1];
! 739: nCpuActiveBPs -= 1;
! 740: fprintf(stderr, "CPU breakpoint at %x deleted.\n", BreakAddr);
! 741: return DEBUGGER_CMDDONE;
! 742: }
! 743: }
! 744:
! 745: /* Is there at least one free slot available? */
! 746: if (nCpuActiveBPs == ARRAYSIZE(CpuBreakPoint))
! 747: {
! 748: fputs("No more available free CPU breakpoints!\n", stderr);
! 749: return DEBUGGER_CMDDONE;
! 750: }
! 751:
! 752: /* Add new breakpoint */
! 753: CpuBreakPoint[nCpuActiveBPs] = BreakAddr;
! 754: nCpuActiveBPs += 1;
! 755: fprintf(stderr, "CPU breakpoint added at %x.\n", BreakAddr);
! 756:
! 757: return DEBUGGER_CMDDONE;
1.1 root 758: }
759:
1.1.1.11! root 760: /**
! 761: * CPU wrapper for BreakCond_Command/BreakPointCount, returns DEBUGGER_END
! 762: */
! 763: static int DebugUI_BreakCondCpu(int nArgc, char *psArgs[])
! 764: {
! 765: BreakCond_Command((const char*)psArgs[1], false);
! 766: nCpuActiveCBs = BreakCond_BreakPointCount(false);
! 767: return DEBUGGER_CMDDONE;
! 768: }
1.1.1.6 root 769:
1.1.1.8 root 770: /**
771: * Do a memory dump, args = starting address.
772: */
1.1.1.11! root 773: static int DebugUI_MemDump(int nArgc, char *psArgs[])
1.1.1.6 root 774: {
775: int i,j;
776: char c;
1.1.1.11! root 777: Uint32 memdump_upper = 0;
1.1.1.2 root 778:
1.1.1.11! root 779: if (nArgc > 1)
1.1.1.6 root 780: {
1.1.1.11! root 781: switch (Str_ParseRange(psArgs[1], &memdump_addr, &memdump_upper))
1.1.1.6 root 782: {
1.1.1.11! root 783: case -1:
! 784: /* invalid value(s) */
! 785: return DEBUGGER_CMDDONE;
! 786: case 0:
! 787: /* single value */
! 788: break;
! 789: case 1:
! 790: /* range */
1.1.1.6 root 791: memdump_upper &= 0x00FFFFFF;
1.1.1.11! root 792: break;
1.1.1.6 root 793: }
794: } /* continue */
795: memdump_addr &= 0x00FFFFFF;
796:
1.1.1.11! root 797: if (!memdump_upper)
1.1.1.6 root 798: {
799: for (j=0;j<MEMDUMP_ROWS;j++)
800: {
1.1.1.11! root 801: fprintf(debugOutput, "%6.6X: ", memdump_addr); /* print address */
1.1.1.6 root 802: for (i = 0; i < MEMDUMP_COLS; i++) /* print hex data */
1.1.1.9 root 803: fprintf(debugOutput, "%2.2x ", STMemory_ReadByte(memdump_addr++));
804: fprintf(debugOutput, " "); /* print ASCII data */
1.1.1.6 root 805: for (i = 0; i < MEMDUMP_COLS; i++)
806: {
807: c = STMemory_ReadByte(memdump_addr-MEMDUMP_COLS+i);
1.1.1.8 root 808: if (!isprint((unsigned)c))
1.1.1.6 root 809: c = NON_PRINT_CHAR; /* non-printable as dots */
1.1.1.9 root 810: fprintf(debugOutput,"%c", c);
1.1.1.6 root 811: }
1.1.1.9 root 812: fprintf(debugOutput, "\n"); /* newline */
1.1.1.6 root 813: }
1.1.1.9 root 814: fflush(debugOutput);
1.1.1.11! root 815: return DEBUGGER_CMDCONT;
1.1.1.6 root 816: } /* not a range */
817:
818: while (memdump_addr < memdump_upper)
819: {
1.1.1.11! root 820: fprintf(debugOutput, "%6.6X: ", memdump_addr); /* print address */
1.1.1.6 root 821: for (i = 0; i < MEMDUMP_COLS; i++) /* print hex data */
1.1.1.9 root 822: fprintf(debugOutput, "%2.2x ", STMemory_ReadByte(memdump_addr++));
823: fprintf(debugOutput, " "); /* print ASCII data */
1.1.1.6 root 824: for (i = 0; i < MEMDUMP_COLS; i++)
825: {
826: c = STMemory_ReadByte(memdump_addr-MEMDUMP_COLS+i);
1.1.1.8 root 827: if(!isprint((unsigned)c))
1.1.1.6 root 828: c = NON_PRINT_CHAR; /* non-printable as dots */
1.1.1.9 root 829: fprintf(debugOutput,"%c", c);
1.1.1.6 root 830: }
1.1.1.9 root 831: fprintf(debugOutput, "\n"); /* newline */
1.1.1.6 root 832: } /* while */
1.1.1.9 root 833: fflush(debugOutput);
1.1.1.11! root 834:
! 835: return DEBUGGER_CMDCONT;
! 836: }
1.1 root 837:
1.1.1.6 root 838:
1.1.1.8 root 839: /**
1.1.1.11! root 840: * Command: Write to memory, arg = starting address, followed by bytes.
1.1.1.8 root 841: */
1.1.1.11! root 842: static int DebugUI_MemWrite(int nArgc, char *psArgs[])
1.1 root 843: {
1.1.1.11! root 844: int i, numBytes;
! 845: Uint32 write_addr, d;
! 846: unsigned char bytes[256]; /* store bytes */
1.1.1.6 root 847:
1.1.1.11! root 848: if (nArgc < 3)
! 849: {
! 850: DebugUI_PrintCmdHelp(psArgs[0]);
! 851: return DEBUGGER_CMDDONE;
! 852: }
1.1.1.6 root 853:
1.1.1.11! root 854: /* Read address */
! 855: if (!Str_GetNumber(psArgs[1], &write_addr))
1.1.1.6 root 856: {
857: fprintf(stderr, "Bad address!\n");
1.1.1.11! root 858: return DEBUGGER_CMDDONE;
1.1.1.6 root 859: }
860:
861: write_addr &= 0x00FFFFFF;
1.1.1.11! root 862: numBytes = 0;
1.1.1.6 root 863:
864: /* get bytes data */
1.1.1.11! root 865: for (i = 2; i < nArgc; i++)
1.1.1.6 root 866: {
1.1.1.11! root 867: if (!Str_GetNumber(psArgs[i], &d) || d > 255)
1.1.1.6 root 868: {
1.1.1.11! root 869: fprintf(stderr, "Bad byte argument: '%s'!\n", psArgs[i]);
! 870: return DEBUGGER_CMDDONE;
1.1.1.6 root 871: }
872:
1.1.1.11! root 873: bytes[numBytes] = d & 0x0FF;
1.1.1.6 root 874: numBytes++;
875: }
876:
877: /* write the data */
878: for (i = 0; i < numBytes; i++)
879: STMemory_WriteByte(write_addr + i, bytes[i]);
1.1 root 880:
1.1.1.11! root 881: return DEBUGGER_CMDDONE;
! 882: }
1.1.1.6 root 883:
1.1.1.8 root 884: /**
1.1.1.11! root 885: * Command: Show given value in bin/dec/hex number bases or change number base
1.1.1.8 root 886: */
1.1.1.11! root 887: static int DebugUI_ShowValue(int argc, char *argv[])
1.1 root 888: {
1.1.1.11! root 889: static const struct {
! 890: const char name[4];
! 891: int base;
! 892: } bases[] = {
! 893: { "bin", 2 },
! 894: { "dec", 10 },
! 895: { "hex", 16 }
! 896: };
! 897: bool one, ones;
! 898: Uint32 value;
! 899: int bit, i;
! 900:
! 901: if (argc < 2)
! 902: {
! 903: DebugUI_PrintCmdHelp(argv[0]);
! 904: return DEBUGGER_CMDDONE;
! 905: }
! 906:
! 907: for (i = 0; i < ARRAYSIZE(bases); i++)
! 908: {
! 909: if (strcasecmp(bases[i].name, argv[1]) == 0)
! 910: {
! 911: if (ConfigureParams.Log.nNumberBase != bases[i].base)
! 912: {
! 913: fprintf(stderr, "Switched default number base from %d to %d-based (%s) values\n",
! 914: ConfigureParams.Log.nNumberBase,
! 915: bases[i].base, bases[i].name);
! 916: ConfigureParams.Log.nNumberBase = bases[i].base;
! 917: } else {
! 918: fprintf(stderr, "Already in '%s' mode\n", bases[i].name);
! 919: }
! 920: return DEBUGGER_CMDDONE;
! 921: }
! 922: }
! 923:
! 924: if (!Str_GetNumber(argv[1], &value))
! 925: return DEBUGGER_CMDDONE;
! 926:
! 927: fprintf(stderr, "'%s' = %%", argv[1]);
! 928: ones = false;
! 929: for (bit = 31; bit >= 0; bit--)
! 930: {
! 931: one = value & (1<<bit);
! 932: if (one || ones)
! 933: {
! 934: fputc(one ? '1':'0', stderr);
! 935: ones = true;
! 936: }
! 937: }
! 938: if (!ones)
! 939: fputc('0', stderr);
! 940: fprintf(stderr, " (bin), #%u (dec), $%x (hex)\n", value, value);
! 941: return DEBUGGER_CMDDONE;
1.1 root 942: }
943:
1.1.1.9 root 944:
1.1.1.8 root 945: /**
1.1.1.11! root 946: * Command: Set options
1.1.1.8 root 947: */
1.1.1.11! root 948: static int DebugUI_SetOptions(int argc, char *argv[])
1.1.1.6 root 949: {
1.1.1.11! root 950: CNF_PARAMS current;
1.1.1.6 root 951:
1.1.1.11! root 952: /* get configuration changes */
! 953: current = ConfigureParams;
1.1 root 954:
1.1.1.11! root 955: /* Parse and apply options */
! 956: if (Opt_ParseParameters(argc, (const char**)argv))
1.1.1.6 root 957: {
1.1.1.11! root 958: ConfigureParams.Screen.bFullScreen = false;
! 959: Change_CopyChangedParamsToConfiguration(¤t, &ConfigureParams, false);
1.1.1.6 root 960: }
1.1.1.11! root 961: else
! 962: {
! 963: ConfigureParams = current;
! 964: }
! 965:
! 966: return DEBUGGER_CMDDONE;
! 967: }
! 968:
! 969:
! 970: /**
! 971: * Command: Continue emulation / single-stepping
! 972: */
! 973: static int DebugUI_Continue(int nArgc, char *psArgv[], bool bStepDsp)
! 974: {
! 975: const char *chip;
! 976: int steps = 0;
! 977:
! 978: if (nArgc > 1)
! 979: {
! 980: steps = atoi(psArgv[1]);
! 981: }
! 982: /* at most one should be active at the same time */
! 983: nDspSteps = 0;
! 984: nCpuSteps = 0;
! 985: if (steps <= 0)
! 986: {
! 987: fprintf(stderr,"Returning to emulation...\n------------------------------\n\n");
! 988: return DEBUGGER_END;
! 989: }
! 990: if (bStepDsp)
! 991: {
! 992: nDspSteps = steps;
! 993: #if ENABLE_DSP_EMU
! 994: chip = "DSP";
! 995: #else
! 996: chip = "<NONE>";
! 997: #endif
! 998: } else {
! 999: nCpuSteps = steps;
! 1000: chip = "CPU";
1.1.1.9 root 1001: }
1.1.1.11! root 1002: fprintf(stderr,"Returning to emulation for %i %s instructions...\n", steps, chip);
! 1003: return DEBUGGER_END;
! 1004: }
! 1005:
! 1006: /**
! 1007: * Command: Continue emulation / single-stepping CPU wrapper
! 1008: */
! 1009: static int DebugUI_CpuContinue(int nArgc, char *psArgv[])
! 1010: {
! 1011: return DebugUI_Continue(nArgc, psArgv, false);
! 1012: }
! 1013: /**
! 1014: * Command: Continue emulation / single-stepping DSP wrapper
! 1015: */
! 1016: static int DebugUI_DspContinue(int nArgc, char *psArgv[])
! 1017: {
! 1018: return DebugUI_Continue(nArgc, psArgv, true);
! 1019: }
1.1.1.6 root 1020:
1.1.1.8 root 1021:
1.1.1.11! root 1022: /**
! 1023: * Command: Quit emulator
! 1024: */
! 1025: static int DebugUI_QuitEmu(int nArgc, char *psArgv[])
! 1026: {
! 1027: bQuitProgram = true;
! 1028: M68000_SetSpecial(SPCFLAG_BRK); /* Assure that CPU core shuts down */
! 1029: return DEBUGGER_END;
! 1030: }
! 1031:
! 1032:
! 1033: typedef struct
! 1034: {
! 1035: int (*pFunction)(int argc, char *argv[]);
! 1036: const char *sLongName;
! 1037: const char *sShortName;
! 1038: const char *sShortDesc;
! 1039: const char *sUsage;
! 1040: bool bNoParsing;
! 1041: } dbgcommand_t;
! 1042:
! 1043: dbgcommand_t commandtab[] =
! 1044: {
! 1045: #if ENABLE_DSP_EMU
! 1046: { DebugUI_DspBreakPoint, "dspaddress", "da",
! 1047: "toggle or list (traditional) DSP address breakpoints",
! 1048: "[address]\n"
! 1049: "\tToggle breakpoint at <address> or list all breakpoints when\n"
! 1050: "\tno address is given.",
! 1051: false },
! 1052: { DebugUI_BreakCondDsp, "dspbreak", "db",
! 1053: "set/remove/list DSP register/RAM condition breakpoints",
! 1054: "[help | all | <breakpoint index> | <breakpoint condition>]\n"
! 1055: "\tSet breakpoint with given condition, remove breakpoint with\n"
! 1056: "\tgiven index or list all breakpoints when no args are given.\n"
! 1057: "\t'help' outputs breakpoint condition syntax help, 'all' removes\n"
! 1058: "\tall conditional breakpoints",
! 1059: true },
! 1060: { DebugUI_DspDisAsm, "dspdisasm", "dd",
! 1061: "disassemble DSP code",
! 1062: "[address]\n"
! 1063: "\tDisassemble from DSP-PC, otherwise at given address.",
! 1064: false },
! 1065: { DebugUI_DspMemDump, "dspmemdump", "dm",
! 1066: "dump DSP memory",
! 1067: "<x|y|p> [address]\n"
! 1068: "\tdump DSP memory at address, or continue from previous address if not\n"
! 1069: "\tspecified.",
! 1070: false },
! 1071: { DebugUI_DspRegister, "dspreg", "dr",
! 1072: "read/write DSP registers",
! 1073: "[REG=value]"
! 1074: "\tSet or dump contents of DSP registers.",
! 1075: false },
! 1076: { DebugUI_DspContinue, "dspcont", "dc",
! 1077: "continue emulation / DSP single-stepping",
! 1078: "[steps]\n"
! 1079: "\tLeave debugger and continue emulation for <steps> DSP instructions\n"
! 1080: "\tor forever if no steps have been specified.",
! 1081: false },
! 1082: #endif
! 1083: { DebugUI_CpuBreakPoint, "address", "a",
! 1084: "toggle or list (traditional) CPU address breakpoints",
! 1085: "[address]\n"
! 1086: "\tToggle breakpoint at <address> or list all breakpoints when\n"
! 1087: "\tno address is given.",
! 1088: false },
! 1089: { DebugUI_BreakCondCpu, "breakpoint", "b",
! 1090: "set/remove/list register/RAM condition breakpoints",
! 1091: "[help | all | <breakpoint index> | <breakpoint condition>]\n"
! 1092: "\tSet breakpoint with given condition, remove breakpoint with\n"
! 1093: "\tgiven index or list all breakpoints when no args are given.\n"
! 1094: "\t'help' outputs breakpoint condition syntax help, 'all' removes\n"
! 1095: "\tall conditional breakpoints",
! 1096: true },
! 1097: { DebugUI_DisAsm, "disasm", "d",
! 1098: "disassemble from PC, or given address",
! 1099: "[address]\n"
! 1100: "\tIf no address is given, this command disassembles from the last\n"
! 1101: "\tposition or from current PC if no last postition is available.",
! 1102: false },
! 1103: { DebugUI_CpuRegister, "cpureg", "r",
! 1104: "dump register values or set register to value",
! 1105: "[REG=value]\n"
! 1106: "\tSet CPU register to value or dumps all register if no parameter\n"
! 1107: "\thas been specified.",
! 1108: false },
! 1109: { DebugUI_MemDump, "memdump", "m",
! 1110: "dump memory",
! 1111: "[address]\n"
! 1112: "\tdump memory at address or continue dump from previous address.",
! 1113: false },
! 1114: { DebugUI_MemWrite, "memwrite", "w",
! 1115: "write bytes to memory",
! 1116: "address byte1 [byte2 ...]\n"
! 1117: "\tWrite bytes to a memory address, bytes are space separated\n"
! 1118: "\thexadecimals.",
! 1119: false },
! 1120: { DebugUI_SetLogFile, "logfile", "f",
! 1121: "open or close log file",
! 1122: "[filename]\n"
! 1123: "\tOpen log file, no argument closes the log file. Output of\n"
! 1124: "\tregister & memory dumps and disassembly will be written to it.",
! 1125: false },
! 1126: { DebugUI_LoadBin, "loadbin", "l",
! 1127: "load a file into memory",
! 1128: "filename address\n"
! 1129: "\tLoad the file <filename> into memory starting at <address>.",
! 1130: false },
! 1131: { DebugUI_SaveBin, "savebin", "s",
! 1132: "save memory to a file",
! 1133: "filename address length\n"
! 1134: "\tSave the memory block at <address> with given <length> to\n"
! 1135: "\tthe file <filename>.",
! 1136: false },
! 1137: { DebugUI_SetOptions, "setopt", "o",
! 1138: "set Hatari command line options",
! 1139: "[command line parameters]\n"
! 1140: "\tSet options like command line parameters. For example to"
! 1141: "\tenable CPU disasm tracing: setopt --trace cpu_disasm",
! 1142: false },
! 1143: { DebugUI_ShowValue, "value", "v",
! 1144: "set number base / show value in other number bases",
! 1145: "<bin|dec|hex|value>\n"
! 1146: "\tHelper to change the default number base and to see given values\n"
! 1147: "\tin all the supported bin/dec/hex number bases.",
! 1148: false },
! 1149: { DebugUI_CpuContinue, "cont", "c",
! 1150: "continue emulation / CPU single-stepping",
! 1151: "[steps]\n"
! 1152: "\tLeave debugger and continue emulation for <steps> CPU instructions\n"
! 1153: "\tor forever if no steps have been specified.",
! 1154: false },
! 1155: { DebugUI_QuitEmu, "quit", "q",
! 1156: "quit emulator",
! 1157: "\n"
! 1158: "\tLeave debugger and quit emulator.",
! 1159: false },
! 1160: { DebugUI_Help, "help", "h",
! 1161: "print help",
! 1162: "[command]"
! 1163: "\tPrint help text for available commands.",
! 1164: false },
! 1165: };
! 1166:
! 1167:
! 1168: /**
! 1169: * Print help text for one command
! 1170: */
! 1171: static void DebugUI_PrintCmdHelp(const char *psCmd)
! 1172: {
! 1173: int i;
! 1174:
! 1175: /* Search the command ... */
! 1176: for (i = 0; i < ARRAYSIZE(commandtab); i++)
1.1.1.6 root 1177: {
1.1.1.11! root 1178: if (!strcmp(psCmd, commandtab[i].sLongName)
! 1179: || !strcmp(psCmd, commandtab[i].sShortName))
! 1180: {
! 1181: /* ... and print help text */
! 1182: fprintf(stderr, "'%s' or '%s' - %s\n",
! 1183: commandtab[i].sLongName, commandtab[i].sShortName,
! 1184: commandtab[i].sShortDesc);
! 1185: fprintf(stderr, "Usage: %s %s\n", commandtab[i].sShortName,
! 1186: commandtab[i].sUsage);
! 1187: return;
1.1.1.6 root 1188: }
1.1.1.11! root 1189: }
1.1.1.6 root 1190:
1.1.1.11! root 1191: fprintf(stderr, "Unknown command '%s'\n", psCmd);
! 1192: }
1.1.1.6 root 1193:
1194:
1.1.1.11! root 1195: /**
! 1196: * Command: Print debugger help screen.
! 1197: */
! 1198: static int DebugUI_Help(int nArgc, char *psArgs[])
! 1199: {
! 1200: int i;
! 1201:
! 1202: if (nArgc > 1)
! 1203: {
! 1204: DebugUI_PrintCmdHelp(psArgs[1]);
! 1205: return DEBUGGER_CMDDONE;
! 1206: }
! 1207:
! 1208: fputs("Available commands:\n", stderr);
! 1209: for (i = 0; i < ARRAYSIZE(commandtab); i++)
! 1210: {
! 1211: fprintf(stderr, " %12s (%2s) : %s\n", commandtab[i].sLongName,
! 1212: commandtab[i].sShortName, commandtab[i].sShortDesc);
! 1213: }
! 1214:
! 1215: fprintf(stderr,
! 1216: "If value is prefixed with '$', it's a hexadecimal, if with '#', it's\n"
! 1217: "a normal decimal, if with '%%', it's a binary decimal. Prefix can\n"
! 1218: "be skipped for numbers in the default number base (currently %d).\n"
! 1219: "Adresses may be given as a range like '$fc0000-$fc0100'.\n"
! 1220: "'h <command>' gives more help.\n", ConfigureParams.Log.nNumberBase);
! 1221: return DEBUGGER_CMDDONE;
! 1222: }
! 1223:
! 1224:
! 1225: /**
! 1226: * Parse debug command and execute it.
! 1227: */
! 1228: int DebugUI_ParseCommand(char *input)
! 1229: {
! 1230: char *psArgs[64];
! 1231: const char *delim;
! 1232: static char sLastCmd[80] = { '\0' };
! 1233: int nArgc, cmd = -1;
! 1234: int i, retval;
1.1.1.6 root 1235:
1.1.1.11! root 1236: psArgs[0] = strtok(input, " \t");
! 1237:
! 1238: if (psArgs[0] == NULL)
! 1239: {
! 1240: if (strlen(sLastCmd) > 0)
! 1241: psArgs[0] = sLastCmd;
1.1.1.8 root 1242: else
1.1.1.11! root 1243: return DEBUGGER_CMDDONE;
! 1244: }
! 1245:
! 1246: /* Search the command ... */
! 1247: for (i = 0; i < ARRAYSIZE(commandtab); i++)
! 1248: {
! 1249: if (!strcmp(psArgs[0], commandtab[i].sLongName)
! 1250: || !strcmp(psArgs[0], commandtab[i].sShortName))
! 1251: {
! 1252: cmd = i;
! 1253: break;
! 1254: }
! 1255: }
! 1256: if (cmd == -1)
! 1257: {
! 1258: fprintf(stderr, "Command '%s' not found.\n"
! 1259: "Use 'help' to view a list of available commands.\n",
! 1260: psArgs[0]);
! 1261: return DEBUGGER_CMDDONE;
! 1262: }
! 1263:
! 1264: if (commandtab[cmd].bNoParsing)
! 1265: delim = "";
! 1266: else
! 1267: delim = " \t";
! 1268:
! 1269: /* Separate arguments and put the pointers into psArgs */
! 1270: for (nArgc = 1; nArgc < ARRAYSIZE(psArgs); nArgc++)
! 1271: {
! 1272: psArgs[nArgc] = strtok(NULL, delim);
! 1273: if (psArgs[nArgc] == NULL)
! 1274: break;
! 1275: }
1.1.1.6 root 1276:
1.1.1.11! root 1277: if (!debugOutput) {
! 1278: /* make sure also calls from control.c work */
! 1279: DebugUI_SetLogDefault();
1.1.1.6 root 1280: }
1281:
1.1.1.11! root 1282: /* ... and execute the function */
! 1283: retval = commandtab[i].pFunction(nArgc, psArgs);
! 1284: /* Save commando string if it can be repeated */
! 1285: if (retval == DEBUGGER_CMDCONT)
! 1286: strncpy(sLastCmd, psArgs[0], sizeof(sLastCmd));
! 1287: else
! 1288: sLastCmd[0] = '\0';
1.1.1.9 root 1289: return retval;
1290: }
1291:
1.1.1.11! root 1292:
1.1.1.9 root 1293: /**
1.1.1.11! root 1294: * Read a command line from the keyboard and return a pointer to the string.
! 1295: * @return Pointer to the string which should be deallocated free()
! 1296: * after use. Returns NULL when error occured.
1.1.1.9 root 1297: */
1.1.1.11! root 1298: static char *DebugUI_GetCommand(void)
1.1.1.9 root 1299: {
1300: char *input;
1.1.1.8 root 1301:
1.1.1.9 root 1302: #if HAVE_LIBREADLINE
1303: input = readline("> ");
1304: if (!input)
1.1.1.11! root 1305: return NULL;
1.1.1.9 root 1306: if (input[0] != 0)
1307: add_history(input);
1308: #else
1309: fprintf(stderr, "> ");
1310: input = malloc(256);
1311: if (!input)
1.1.1.11! root 1312: return NULL;
1.1.1.9 root 1313: input[0] = '\0';
1.1.1.11! root 1314: if (fgets(input, 256, stdin) == NULL)
! 1315: {
! 1316: free(input);
! 1317: return NULL;
! 1318: }
1.1.1.9 root 1319: #endif
1.1.1.11! root 1320: input = Str_Trim(input);
1.1.1.9 root 1321:
1.1.1.11! root 1322: return input;
! 1323: }
! 1324:
! 1325:
! 1326: /**
! 1327: * Texts shown when entering the debugger on first and successive times
! 1328: */
! 1329: static void DebugUI_WelcomeText(void)
! 1330: {
! 1331: int hbl, fcycles, lcycles;
! 1332: static const char *welcome =
! 1333: "\n----------------------------------------------------------------------"
! 1334: "\nYou have entered debug mode. Type c to continue emulation, h for help.\n";
! 1335: if (welcome)
! 1336: {
! 1337: fputs(welcome, stderr);
! 1338: welcome = NULL;
! 1339: }
! 1340: Video_GetPosition(&fcycles, &hbl, &lcycles);
! 1341: fprintf(stderr, "\nCPU=$%x, VBL=%d, FrameCycles=%d, HBL=%d, LineCycles=%d, DSP=",
! 1342: M68000_GetPC(), nVBLs, fcycles, hbl, lcycles);
! 1343: if (bDspEnabled)
! 1344: fprintf(stderr, "$%x\n", DSP_GetPC());
! 1345: else
! 1346: fprintf(stderr, "N/A\n");
1.1 root 1347: }
1348:
1349:
1.1.1.8 root 1350: /**
1.1.1.11! root 1351: * Debugger user interface main function.
1.1.1.8 root 1352: */
1.1.1.4 root 1353: void DebugUI(void)
1.1 root 1354: {
1.1.1.11! root 1355: int cmdret;
! 1356:
! 1357: /* if you want disassembly or memdumping to start/continue from
! 1358: * specific address, you can set them here. If disassembly
! 1359: * address is zero, disassembling starts from PC.
! 1360: */
! 1361: #if ENABLE_DSP_EMU
! 1362: dsp_disasm_addr = 0;
! 1363: dsp_memdump_addr = 0;
! 1364: dsp_mem_space = 'P';
! 1365: #endif
! 1366: memdump_addr = 0;
1.1.1.6 root 1367: disasm_addr = 0;
1.1.1.2 root 1368:
1.1.1.11! root 1369: if (bInFullScreen)
! 1370: Screen_ReturnFromFullScreen();
! 1371:
! 1372: DebugUI_WelcomeText();
! 1373:
! 1374: /* override paused message so that user knows to look into console
! 1375: * on how to continue in case he invoked the debugger by accident.
! 1376: */
! 1377: Statusbar_AddMessage("Console Debugger", 100);
! 1378: Statusbar_Update(sdlscrn);
! 1379:
! 1380: do
! 1381: {
! 1382: char *psCmd;
! 1383:
! 1384: /* Read command from the keyboard */
! 1385: psCmd = DebugUI_GetCommand();
! 1386:
! 1387: if (psCmd)
! 1388: {
! 1389: /* Parse and execute the command string */
! 1390: cmdret = DebugUI_ParseCommand(psCmd);
! 1391: free(psCmd);
! 1392: }
! 1393: else
! 1394: {
! 1395: cmdret = DEBUGGER_END;
! 1396: }
! 1397: }
! 1398: while (cmdret != DEBUGGER_END);
! 1399:
1.1.1.9 root 1400: DebugUI_SetLogDefault();
1.1.1.11! root 1401:
! 1402: /* If "real-time" debugging like breakpoints has been set, we've
! 1403: * got to tell the CPU core to call us after each instruction! */
! 1404: if (nCpuActiveBPs || nCpuActiveCBs || nCpuSteps)
! 1405: M68000_SetSpecial(SPCFLAG_DEBUGGER);
! 1406: else
! 1407: M68000_UnsetSpecial(SPCFLAG_DEBUGGER);
! 1408: /* ...and DSP core */
! 1409: if (nDspActiveBPs || nDspActiveCBs || nDspSteps)
! 1410: DSP_SetDebugging(true);
! 1411: else
! 1412: DSP_SetDebugging(false);
! 1413: }
! 1414:
! 1415:
! 1416: /**
! 1417: * Check if we hit a CPU breakpoint
! 1418: */
! 1419: static void DebugUI_CheckCpuBreakpoints(void)
! 1420: {
! 1421: Uint32 pc = M68000_GetPC();
! 1422: int i;
! 1423:
! 1424: for (i = 0; i < nCpuActiveBPs; i++)
! 1425: {
! 1426: if (pc == CpuBreakPoint[i])
! 1427: {
! 1428: fprintf(stderr, "\nCPU breakpoint at %x ...", pc);
! 1429: DebugUI();
! 1430: break;
! 1431: }
! 1432: }
! 1433: }
! 1434:
! 1435:
! 1436: /**
! 1437: * This function is called after each CPU instruction when debugging is enabled.
! 1438: */
! 1439: void DebugUI_CpuCheck(void)
! 1440: {
! 1441: if (nCpuActiveBPs)
! 1442: {
! 1443: DebugUI_CheckCpuBreakpoints();
! 1444: }
! 1445: if (nCpuActiveCBs)
! 1446: {
! 1447: if (BreakCond_MatchCpu())
! 1448: DebugUI();
! 1449: }
! 1450: if (nCpuSteps)
! 1451: {
! 1452: nCpuSteps -= 1;
! 1453: if (nCpuSteps == 0)
! 1454: DebugUI();
! 1455: }
1.1 root 1456: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.