|
|
1.1 root 1: /*
2: Hatari - debugcpu.c
3:
1.1.1.5 root 4: This file is distributed under the GNU General Public License, version 2
5: or at your option any later version. Read the file gpl.txt for details.
1.1 root 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>
1.1.1.6 root 13: #include <ctype.h>
1.1.1.9 root 14: #include <limits.h>
1.1 root 15:
16: #include "config.h"
17:
18: #include "main.h"
19: #include "breakcond.h"
20: #include "configuration.h"
21: #include "debugui.h"
22: #include "debug_priv.h"
23: #include "debugcpu.h"
24: #include "evaluate.h"
25: #include "hatari-glue.h"
1.1.1.3 root 26: #include "history.h"
1.1 root 27: #include "log.h"
28: #include "m68000.h"
29: #include "memorySnapShot.h"
1.1.1.2 root 30: #include "profile.h"
1.1 root 31: #include "stMemory.h"
32: #include "str.h"
33: #include "symbols.h"
1.1.1.2 root 34: #include "68kDisass.h"
1.1.1.5 root 35: #include "console.h"
36: #include "options.h"
1.1.1.8 root 37: #include "vars.h"
1.1.1.5 root 38:
1.1 root 39:
40: #define MEMDUMP_COLS 16 /* memdump, number of bytes per row */
41: #define NON_PRINT_CHAR '.' /* character to display for non-printables */
42:
43: static Uint32 disasm_addr; /* disasm address */
44: static Uint32 memdump_addr; /* memdump address */
1.1.1.10! root 45: static Uint32 fake_regs[8]; /* virtual debugger "registers" */
! 46: static bool bFakeRegsUsed; /* whether to show virtual regs */
1.1 root 47:
1.1.1.2 root 48: static bool bCpuProfiling; /* Whether CPU profiling is activated */
1.1 root 49: static int nCpuActiveCBs = 0; /* Amount of active conditional breakpoints */
50: static int nCpuSteps = 0; /* Amount of steps for CPU single-stepping */
51:
52:
53: /**
54: * Load a binary file to a memory address.
55: */
56: static int DebugCpu_LoadBin(int nArgc, char *psArgs[])
57: {
58: FILE *fp;
59: unsigned char c;
60: Uint32 address;
61: int i=0;
62:
63: if (nArgc < 3)
64: {
1.1.1.6 root 65: return DebugUI_PrintCmdHelp(psArgs[0]);
1.1 root 66: }
67:
68: if (!Eval_Number(psArgs[2], &address))
69: {
70: fprintf(stderr, "Invalid address!\n");
71: return DEBUGGER_CMDDONE;
72: }
73:
74: if ((fp = fopen(psArgs[1], "rb")) == NULL)
75: {
76: fprintf(stderr, "Cannot open file '%s'!\n", psArgs[1]);
77: return DEBUGGER_CMDDONE;
78: }
79:
1.1.1.7 root 80: /* TODO: more efficient would be to:
81: * - check file size
82: * - verify that it fits into valid memory area
83: * - flush emulated CPU data cache
84: * - read file contents directly into memory
85: */
1.1 root 86: c = fgetc(fp);
87: while (!feof(fp))
88: {
89: i++;
90: STMemory_WriteByte(address++, c);
91: c = fgetc(fp);
92: }
93: fprintf(stderr," Read 0x%x bytes.\n", i);
94: fclose(fp);
95:
96: return DEBUGGER_CMDDONE;
97: }
98:
99:
100: /**
101: * Dump memory from an address to a binary file.
102: */
103: static int DebugCpu_SaveBin(int nArgc, char *psArgs[])
104: {
105: FILE *fp;
106: unsigned char c;
107: Uint32 address;
108: Uint32 bytes, i = 0;
109:
110: if (nArgc < 4)
111: {
1.1.1.6 root 112: return DebugUI_PrintCmdHelp(psArgs[0]);
1.1 root 113: }
114:
115: if (!Eval_Number(psArgs[2], &address))
116: {
117: fprintf(stderr, " Invalid address!\n");
118: return DEBUGGER_CMDDONE;
119: }
120:
121: if (!Eval_Number(psArgs[3], &bytes))
122: {
123: fprintf(stderr, " Invalid length!\n");
124: return DEBUGGER_CMDDONE;
125: }
126:
127: if ((fp = fopen(psArgs[1], "wb")) == NULL)
128: {
129: fprintf(stderr," Cannot open file '%s'!\n", psArgs[1]);
130: return DEBUGGER_CMDDONE;
131: }
132:
133: while (i < bytes)
134: {
135: c = STMemory_ReadByte(address++);
136: fputc(c, fp);
137: i++;
138: }
139: fclose(fp);
140: fprintf(stderr, " Wrote 0x%x bytes.\n", bytes);
141:
142: return DEBUGGER_CMDDONE;
143: }
144:
145:
146: /**
1.1.1.2 root 147: * Check whether given address matches any CPU symbol and whether
148: * there's profiling information available for it. If yes, show it.
1.1.1.9 root 149: *
150: * @return true if symbol was shown, false otherwise
1.1 root 151: */
1.1.1.9 root 152: static bool DebugCpu_ShowAddressInfo(Uint32 addr, FILE *fp)
1.1 root 153: {
1.1.1.9 root 154: const char *symbol = Symbols_GetByCpuAddress(addr, SYMTYPE_ALL);
1.1 root 155: if (symbol)
1.1.1.9 root 156: {
1.1.1.8 root 157: fprintf(fp, "%s:\n", symbol);
1.1.1.9 root 158: return true;
159: }
160: return false;
1.1 root 161: }
162:
163: /**
164: * Dissassemble - arg = starting address, or PC.
165: */
166: int DebugCpu_DisAsm(int nArgc, char *psArgs[])
167: {
168: Uint32 disasm_upper = 0;
1.1.1.9 root 169: int shown, lines = INT_MAX;
1.1 root 170: uaecptr nextpc;
171:
172: if (nArgc > 1)
173: {
1.1.1.2 root 174: switch (Eval_Range(psArgs[1], &disasm_addr, &disasm_upper, false))
1.1 root 175: {
176: case -1:
177: /* invalid value(s) */
178: return DEBUGGER_CMDDONE;
179: case 0:
180: /* single value */
181: break;
182: case 1:
183: /* range */
184: break;
185: }
186: }
187: else
188: {
189: /* continue */
190: if(!disasm_addr)
191: disasm_addr = M68000_GetPC();
192: }
193:
194: /* limit is topmost address or instruction count */
1.1.1.9 root 195: if (!disasm_upper)
1.1 root 196: {
1.1.1.7 root 197: disasm_upper = 0xFFFFFFFF;
1.1.1.9 root 198: lines = DebugUI_GetPageLines(ConfigureParams.Debugger.nDisasmLines, 8);
1.1 root 199: }
200:
201: /* output a range */
1.1.1.9 root 202: for (shown = 0; shown < lines && disasm_addr < disasm_upper; shown++)
1.1 root 203: {
1.1.1.9 root 204: if (DebugCpu_ShowAddressInfo(disasm_addr, debugOutput))
205: shown++;
1.1.1.5 root 206: Disasm(debugOutput, (uaecptr)disasm_addr, &nextpc, 1);
1.1 root 207: disasm_addr = nextpc;
208: }
209: fflush(debugOutput);
210:
211: return DEBUGGER_CMDCONT;
212: }
213:
214:
215: /**
216: * Readline match callback to list register names usable within debugger.
217: * STATE = 0 -> different text from previous one.
218: * Return next match or NULL if no matches.
219: */
220: static char *DebugCpu_MatchRegister(const char *text, int state)
221: {
1.1.1.10! root 222: static const char* regs_000[] = {
1.1 root 223: "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
224: "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
1.1.1.10! root 225: "isp", "usp",
! 226: "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7"
1.1 root 227: };
1.1.1.10! root 228: static const char* regs_020[] = {
! 229: "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
! 230: "caar", "cacr",
! 231: "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
! 232: "dfc", "isp", "msp", "pc", "sfc", "sr", "usp",
! 233: "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
! 234: "vbr"
! 235: };
! 236: if (ConfigureParams.System.nCpuLevel < 2)
! 237: return DebugUI_MatchHelper(regs_000, ARRAY_SIZE(regs_000), text, state);
! 238: else
! 239: return DebugUI_MatchHelper(regs_020, ARRAY_SIZE(regs_020), text, state);
1.1 root 240: }
241:
242:
243: /**
1.1.1.5 root 244: * Set address of the named 32-bit register to given argument.
1.1.1.10! root 245: * Handles V0-7 fake registers, D0-7 data, A0-7 address and several
! 246: * special registers except for PC & SR registers because they need
! 247: * to be accessed using UAE accessors.
! 248: *
! 249: * Return register size in bits or zero for unknown register name.
1.1 root 250: */
251: int DebugCpu_GetRegisterAddress(const char *reg, Uint32 **addr)
252: {
1.1.1.10! root 253: char r0;
! 254: int r1;
! 255:
! 256: if (!reg[0] || !reg[1])
1.1 root 257: return 0;
1.1.1.10! root 258:
! 259: /* 3-4 letter reg? */
! 260: if (reg[2])
! 261: {
! 262: if (strcasecmp(reg, "ISP") == 0)
! 263: {
! 264: *addr = ®s.isp;
! 265: return 32;
! 266: }
! 267: if (strcasecmp(reg, "USP") == 0)
! 268: {
! 269: *addr = ®s.usp;
! 270: return 32;
! 271: }
! 272: if (ConfigureParams.System.nCpuLevel >= 2)
! 273: {
! 274: static const struct {
! 275: const char name[5];
! 276: Uint32 *addr;
! 277: } reg_020[] = {
! 278: { "CAAR", ®s.caar },
! 279: { "CACR", ®s.cacr },
! 280: { "DFC", ®s.dfc },
! 281: { "MSP", ®s.msp },
! 282: { "SFC", ®s.sfc },
! 283: { "VBR", ®s.vbr }
! 284: };
! 285: for (int i = 0; i < ARRAY_SIZE(reg_020); i++)
! 286: {
! 287: if (strcasecmp(reg, reg_020[i].name) == 0)
! 288: {
! 289: *addr = reg_020[i].addr;
! 290: return 32;
! 291: }
! 292: }
! 293: }
! 294: return 0;
! 295: }
! 296:
! 297: /* 2-letter reg */
1.1.1.6 root 298: r0 = toupper((unsigned char)reg[0]);
1.1.1.10! root 299: r1 = toupper((unsigned char)reg[1]) - '0';
1.1 root 300:
301: if (r0 == 'D') /* Data regs? */
302: {
1.1.1.10! root 303: if (r1 >= 0 && r1 <= 7)
1.1 root 304: {
1.1.1.10! root 305: *addr = &(regs.regs[REG_D0 + r1]);
1.1 root 306: return 32;
307: }
308: fprintf(stderr,"\tBad data register, valid values are 0-7\n");
309: return 0;
310: }
311: if(r0 == 'A') /* Address regs? */
312: {
1.1.1.10! root 313: if (r1 >= 0 && r1 <= 7)
1.1 root 314: {
1.1.1.10! root 315: *addr = &(regs.regs[REG_A0 + r1]);
1.1 root 316: return 32;
317: }
318: fprintf(stderr,"\tBad address register, valid values are 0-7\n");
319: return 0;
320: }
1.1.1.10! root 321: if(r0 == 'V') /* Virtual regs? */
! 322: {
! 323: if (r1 >= 0 && r1 < ARRAY_SIZE(fake_regs))
! 324: {
! 325: *addr = &fake_regs[r1];
! 326: bFakeRegsUsed = true;
! 327: return 32;
! 328: }
! 329: fprintf(stderr,"\tBad virtual register, valid values are 0-7\n");
! 330: return 0;
! 331: }
1.1 root 332: return 0;
333: }
334:
335:
336: /**
337: * Dump or set CPU registers
338: */
339: int DebugCpu_Register(int nArgc, char *psArgs[])
340: {
1.1.1.10! root 341: char *arg, *assign;
1.1 root 342: Uint32 value;
343:
344: /* If no parameter has been given, simply dump all registers */
345: if (nArgc == 1)
346: {
347: uaecptr nextpc;
1.1.1.10! root 348: int idx;
! 349:
1.1 root 350: /* use the UAE function instead */
1.1.1.7 root 351: #ifdef WINUAE_FOR_HATARI
1.1.1.10! root 352: m68k_dumpstate_file(debugOutput, &nextpc, 0xffffffff);
1.1.1.7 root 353: #else
1.1 root 354: m68k_dumpstate(debugOutput, &nextpc);
1.1.1.7 root 355: #endif
1.1 root 356: fflush(debugOutput);
1.1.1.10! root 357: if (!bFakeRegsUsed)
! 358: return DEBUGGER_CMDDONE;
! 359:
! 360: fputs("Virtual registers:\n", debugOutput);
! 361: for (idx = 0; idx < ARRAY_SIZE(fake_regs); idx++)
! 362: {
! 363: if (idx && idx % 4 == 0)
! 364: fputs("\n", debugOutput);
! 365: fprintf(debugOutput, " V%c %08x",
! 366: '0' + idx, fake_regs[idx]);
! 367: }
! 368: fputs("\n", debugOutput);
! 369: fflush(debugOutput);
1.1 root 370: return DEBUGGER_CMDDONE;
371: }
372:
373: arg = psArgs[1];
374:
375: assign = strchr(arg, '=');
376: if (!assign)
377: {
378: goto error_msg;
379: }
380:
381: *assign++ = '\0';
382: if (!Eval_Number(Str_Trim(assign), &value))
383: {
384: goto error_msg;
385: }
386:
387: arg = Str_Trim(arg);
1.1.1.10! root 388: if (strlen(arg) < 2)
1.1 root 389: {
390: goto error_msg;
391: }
392:
393: /* set SR and update conditional flags for the UAE CPU core. */
1.1.1.10! root 394: if (strcasecmp("SR", arg) == 0)
1.1 root 395: {
396: M68000_SetSR(value);
397: }
1.1.1.10! root 398: else if (strcasecmp("PC", arg) == 0) /* set PC */
1.1 root 399: {
400: M68000_SetPC(value);
401: }
402: else
403: {
404: Uint32 *regaddr;
405: /* check&set data and address registers */
1.1.1.10! root 406: if (DebugCpu_GetRegisterAddress(arg, ®addr))
1.1 root 407: {
408: *regaddr = value;
409: }
410: else
411: {
412: goto error_msg;
413: }
414: }
415: return DEBUGGER_CMDDONE;
416:
417: error_msg:
1.1.1.10! root 418: fprintf(stderr,"\tError, usage: r or r xx=yyyy\n"
! 419: "\tWhere: xx=A0-A7, D0-D7, PC, SR, ISP, USP\n"
! 420: "\t020+: CAAR, CACR, DFC, SFC, MSP, VBR\n"
! 421: "\tor V0-V7 (virtual).\n");
1.1 root 422: return DEBUGGER_CMDDONE;
423: }
424:
425:
426: /**
1.1.1.2 root 427: * CPU wrapper for BreakAddr_Command().
1.1 root 428: */
429: static int DebugCpu_BreakAddr(int nArgc, char *psArgs[])
430: {
431: BreakAddr_Command(psArgs[1], false);
432: return DEBUGGER_CMDDONE;
433: }
434:
435: /**
1.1.1.2 root 436: * CPU wrapper for BreakCond_Command().
1.1 root 437: */
438: static int DebugCpu_BreakCond(int nArgc, char *psArgs[])
439: {
440: BreakCond_Command(psArgs[1], false);
441: return DEBUGGER_CMDDONE;
442: }
443:
1.1.1.2 root 444: /**
445: * CPU wrapper for Profile_Command().
446: */
447: static int DebugCpu_Profile(int nArgc, char *psArgs[])
448: {
1.1.1.5 root 449: return Profile_Command(nArgc, psArgs, false);
1.1.1.2 root 450: }
451:
1.1 root 452:
453: /**
454: * Do a memory dump, args = starting address.
455: */
456: int DebugCpu_MemDump(int nArgc, char *psArgs[])
457: {
1.1.1.10! root 458: char c, mode;
! 459: int i, arg, size;
! 460: Uint32 value, memdump_upper = 0;
! 461:
! 462: arg = 1;
! 463: mode = 0;
! 464: size = 1;
1.1 root 465: if (nArgc > 1)
1.1.1.10! root 466: mode = tolower(psArgs[arg][0]);
! 467:
! 468: if (!mode || isdigit(psArgs[arg][0]) || psArgs[arg][1])
! 469: {
! 470: /* no args, single digit or multiple chars -> default mode */
! 471: mode = 'b';
! 472: }
! 473: else if (mode == 'b')
! 474: {
! 475: arg += 1;
! 476: }
! 477: else if (mode == 'w')
! 478: {
! 479: arg += 1;
! 480: size = 2;
! 481: }
! 482: else if (mode == 'l')
! 483: {
! 484: arg += 1;
! 485: size = 4;
! 486: }
! 487: else
! 488: {
! 489: fprintf(stderr, "Invalid width mode (not b|w|l)!\n");
! 490: return DEBUGGER_CMDDONE;
! 491: }
! 492:
! 493: if (nArgc > arg)
1.1 root 494: {
1.1.1.10! root 495: switch (Eval_Range(psArgs[arg], &memdump_addr, &memdump_upper, false))
1.1 root 496: {
497: case -1:
498: /* invalid value(s) */
499: return DEBUGGER_CMDDONE;
500: case 0:
501: /* single value */
502: break;
503: case 1:
504: /* range */
505: break;
506: }
1.1.1.10! root 507: arg++;
! 508:
! 509: if (nArgc > arg)
! 510: {
! 511: int count = atoi(psArgs[arg]);
! 512: if (!count)
! 513: {
! 514: fprintf(stderr, "Invalid count %d!\n", count);
! 515: return DEBUGGER_CMDDONE;
! 516: }
! 517: memdump_upper = memdump_addr + count * size;
! 518: }
! 519: }
1.1 root 520:
521: if (!memdump_upper)
522: {
1.1.1.9 root 523: int lines = DebugUI_GetPageLines(ConfigureParams.Debugger.nMemdumpLines, 8);
524: memdump_upper = memdump_addr + MEMDUMP_COLS * lines;
1.1 root 525: }
526:
527: while (memdump_addr < memdump_upper)
528: {
1.1.1.10! root 529: fprintf(debugOutput, "%08X: ", memdump_addr);
! 530:
! 531: /* print HEX data */
! 532: for (i = 0; i < MEMDUMP_COLS/size; i++)
! 533: {
! 534: switch (mode)
! 535: {
! 536: case 'l':
! 537: value = STMemory_ReadLong(memdump_addr);
! 538: break;
! 539: case 'w':
! 540: value = STMemory_ReadWord(memdump_addr);
! 541: break;
! 542: case 'b':
! 543: default:
! 544: value = STMemory_ReadByte(memdump_addr);
! 545: break;
! 546: }
! 547: fprintf(debugOutput, "%0*x ", 2*size, value);
! 548: memdump_addr += size;
! 549: }
! 550:
! 551: /* print ASCII data */
! 552: fprintf(debugOutput, " ");
1.1 root 553: for (i = 0; i < MEMDUMP_COLS; i++)
554: {
555: c = STMemory_ReadByte(memdump_addr-MEMDUMP_COLS+i);
556: if(!isprint((unsigned)c))
557: c = NON_PRINT_CHAR; /* non-printable as dots */
558: fprintf(debugOutput,"%c", c);
559: }
1.1.1.10! root 560: fprintf(debugOutput, "\n");
! 561: }
1.1 root 562: fflush(debugOutput);
563:
564: return DEBUGGER_CMDCONT;
565: }
566:
567:
568: /**
1.1.1.10! root 569: * Command: Write to memory, optional arg for value lenghts,
! 570: * followed by starting address and the values.
1.1 root 571: */
572: static int DebugCpu_MemWrite(int nArgc, char *psArgs[])
573: {
1.1.1.10! root 574: int i, arg, values, max_values;
1.1 root 575: Uint32 write_addr, d;
1.1.1.10! root 576: union {
! 577: Uint8 bytes[256];
! 578: Uint16 words[128];
! 579: Uint32 longs[64];
! 580: } store;
! 581: char mode;
1.1 root 582:
583: if (nArgc < 3)
584: {
1.1.1.6 root 585: return DebugUI_PrintCmdHelp(psArgs[0]);
1.1 root 586: }
587:
1.1.1.10! root 588: arg = 1;
! 589: mode = tolower(psArgs[arg][0]);
! 590: max_values = ARRAY_SIZE(store.bytes);
! 591:
! 592: if (!mode || isdigit(psArgs[arg][0]) || psArgs[arg][1])
! 593: {
! 594: /* no args, single digit or multiple chars -> default mode */
! 595: mode = 'b';
! 596: }
! 597: else if (mode == 'b')
! 598: {
! 599: arg += 1;
! 600: }
! 601: else if (mode == 'w')
! 602: {
! 603: max_values = ARRAY_SIZE(store.words);
! 604: arg += 1;
! 605: }
! 606: else if (mode == 'l')
! 607: {
! 608: max_values = ARRAY_SIZE(store.longs);
! 609: arg += 1;
! 610: }
! 611: else
! 612: {
! 613: fprintf(stderr, "Invalid width mode (not b|w|l)!\n");
! 614: return DEBUGGER_CMDDONE;
! 615: }
1.1 root 616: /* Read address */
1.1.1.10! root 617: if (!Eval_Number(psArgs[arg++], &write_addr))
1.1 root 618: {
619: fprintf(stderr, "Bad address!\n");
620: return DEBUGGER_CMDDONE;
621: }
622:
1.1.1.10! root 623: if (nArgc - arg > max_values)
! 624: {
! 625: fprintf(stderr, "Too many values (%d) given for mode '%c' (max %d)!\n",
! 626: nArgc - arg, mode, max_values);
! 627: return DEBUGGER_CMDDONE;
! 628: }
1.1 root 629:
1.1.1.10! root 630: /* get the data */
! 631: values = 0;
! 632: for (i = arg; i < nArgc; i++)
1.1 root 633: {
1.1.1.10! root 634: if (!Eval_Number(psArgs[i], &d))
1.1 root 635: {
1.1.1.10! root 636: fprintf(stderr, "Bad value '%s'!\n", psArgs[i]);
1.1 root 637: return DEBUGGER_CMDDONE;
638: }
1.1.1.10! root 639: switch(mode)
! 640: {
! 641: case 'b':
! 642: if (d > 0xff)
! 643: {
! 644: fprintf(stderr, "Illegal byte argument: 0x%x!\n", d);
! 645: return DEBUGGER_CMDDONE;
! 646: }
! 647: store.bytes[values] = (Uint8)d;
! 648: break;
! 649: case 'w':
! 650: if (d > 0xffff)
! 651: {
! 652: fprintf(stderr, "Illegal word argument: 0x%x!\n", d);
! 653: return DEBUGGER_CMDDONE;
! 654: }
! 655: store.words[values] = (Uint16)d;
! 656: break;
! 657: case 'l':
! 658: store.longs[values] = d;
! 659: break;
! 660: }
! 661: values++;
1.1 root 662: }
663:
664: /* write the data */
1.1.1.10! root 665: for (i = 0; i < values; i++)
! 666: {
! 667: switch(mode)
! 668: {
! 669: case 'b':
! 670: STMemory_WriteByte(write_addr + i, store.bytes[i]);
! 671: break;
! 672: case 'w':
! 673: STMemory_WriteWord(write_addr + i*2, store.words[i]);
! 674: break;
! 675: case 'l':
! 676: STMemory_WriteLong(write_addr + i*4, store.longs[i]);
! 677: break;
! 678: }
! 679: }
! 680: if (values > 1)
! 681: {
! 682: fprintf(stderr, "Wrote %d '%c' values starting from 0x%x.\n",
! 683: values, mode, write_addr);
! 684: }
1.1 root 685: return DEBUGGER_CMDDONE;
686: }
687:
688:
689: /**
690: * Command: Continue CPU emulation / single-stepping
691: */
692: static int DebugCpu_Continue(int nArgc, char *psArgv[])
693: {
694: int steps = 0;
695:
696: if (nArgc > 1)
697: {
698: steps = atoi(psArgv[1]);
699: }
700: if (steps <= 0)
701: {
702: nCpuSteps = 0;
703: fprintf(stderr,"Returning to emulation...\n");
704: return DEBUGGER_END;
705: }
706: nCpuSteps = steps;
707: fprintf(stderr,"Returning to emulation for %i CPU instructions...\n", steps);
708: return DEBUGGER_END;
709: }
710:
1.1.1.5 root 711: /**
712: * Command: Single-step CPU
713: */
714: static int DebugCpu_Step(int nArgc, char *psArgv[])
715: {
716: nCpuSteps = 1;
717: return DEBUGGER_END;
718: }
719:
1.1.1.6 root 720:
721: /**
722: * Readline match callback to list next command opcode types.
723: * STATE = 0 -> different text from previous one.
724: * Return next match or NULL if no matches.
725: */
726: static char *DebugCpu_MatchNext(const char *text, int state)
727: {
728: static const char* ntypes[] = {
729: "branch", "exception", "exreturn", "return", "subcall", "subreturn"
730: };
1.1.1.8 root 731: return DebugUI_MatchHelper(ntypes, ARRAY_SIZE(ntypes), text, state);
1.1.1.6 root 732: }
733:
1.1.1.5 root 734: /**
735: * Command: Step CPU, but proceed through subroutines
736: * Does this by temporary conditional breakpoint
737: */
738: static int DebugCpu_Next(int nArgc, char *psArgv[])
739: {
1.1.1.6 root 740: char command[40];
741: if (nArgc > 1)
742: {
743: int optype;
744: if(strcmp(psArgv[1], "branch") == 0)
745: optype = CALL_BRANCH;
746: else if(strcmp(psArgv[1], "exception") == 0)
747: optype = CALL_EXCEPTION;
748: else if(strcmp(psArgv[1], "exreturn") == 0)
749: optype = CALL_EXCRETURN;
750: else if(strcmp(psArgv[1], "subcall") == 0)
751: optype = CALL_SUBROUTINE;
752: else if (strcmp(psArgv[1], "subreturn") == 0)
753: optype = CALL_SUBRETURN;
754: else if (strcmp(psArgv[1], "return") == 0)
755: optype = CALL_SUBRETURN | CALL_EXCRETURN;
756: else
757: {
758: fprintf(stderr, "Unrecognized opcode type given!\n");
759: return DEBUGGER_CMDDONE;
760: }
761: sprintf(command, "CpuOpcodeType & $%x > 0 :once :quiet\n", optype);
762: }
763: else
764: {
765: Uint32 optype, nextpc;
766:
767: optype = DebugCpu_OpcodeType();
1.1.1.8 root 768: /* should this instruction be stepped normally, or is it
769: * - subroutine call
770: * - exception
771: * - loop branch backwards
772: */
773: if (optype == CALL_SUBROUTINE ||
774: optype == CALL_EXCEPTION ||
775: (optype == CALL_BRANCH &&
776: (STMemory_ReadWord(M68000_GetPC()) & 0xf0f8) == 0x50c8 &&
777: (Sint16)STMemory_ReadWord(M68000_GetPC()+SIZE_WORD) < 0))
778: {
779: nextpc = Disasm_GetNextPC(M68000_GetPC());
780: sprintf(command, "pc=$%x :once :quiet\n", nextpc);
781: }
782: else
1.1.1.6 root 783: {
784: nCpuSteps = 1;
785: return DEBUGGER_END;
786: }
787: }
788: /* use breakpoint, not steps */
789: if (BreakCond_Command(command, false))
790: {
791: nCpuSteps = 0;
1.1.1.5 root 792: return DEBUGGER_END;
793: }
794: return DEBUGGER_CMDDONE;
795: }
796:
1.1.1.6 root 797: /* helper to get instruction type */
798: Uint32 DebugCpu_OpcodeType(void)
799: {
800: /* cannot use OpcodeFamily like profiler does,
801: * as that's for previous instructions
802: */
803: Uint16 opcode = STMemory_ReadWord(M68000_GetPC());
804:
805: if (opcode == 0x4e74 || /* RTD */
806: opcode == 0x4e75 || /* RTS */
807: opcode == 0x4e77) /* RTR */
808: return CALL_SUBRETURN;
809:
810: if (opcode == 0x4e73) /* RTE */
811: return CALL_EXCRETURN;
812:
813: /* NOTE: BSR needs to be matched before BRA/BCC! */
814: if ((opcode & 0xff00) == 0x6100 || /* BSR */
815: (opcode & 0xffc0) == 0x4e80) /* JSR */
816: return CALL_SUBROUTINE;
817:
818: /* TODO: ftrapcc, chk2? */
819: if (opcode == 0x4e72 || /* STOP */
820: opcode == 0x4afc || /* ILLEGAL */
821: opcode == 0x4e76 || /* TRAPV */
822: (opcode & 0xfff0) == 0x4e40 || /* TRAP */
823: (opcode & 0xf1c0) == 0x4180 || /* CHK */
824: (opcode & 0xfff8) == 0x4848) /* BKPT */
825: return CALL_EXCEPTION;
826:
827: /* TODO: fbcc, fdbcc */
828: if ((opcode & 0xf000) == 0x6000 || /* BRA / BCC */
829: (opcode & 0xffc0) == 0x4ec0 || /* JMP */
1.1.1.7 root 830: (opcode & 0xf0f8) == 0x50c8) /* DBCC */
1.1.1.6 root 831: return CALL_BRANCH;
832:
833: return CALL_UNKNOWN;
834: }
835:
836:
837: /**
838: * CPU instructions since continuing emulation
839: */
840: static Uint32 nCpuInstructions;
841: Uint32 DebugCpu_InstrCount(void)
842: {
843: return nCpuInstructions;
844: }
1.1 root 845:
846: /**
847: * This function is called after each CPU instruction when debugging is enabled.
848: */
849: void DebugCpu_Check(void)
850: {
1.1.1.6 root 851: nCpuInstructions++;
1.1.1.2 root 852: if (bCpuProfiling)
853: {
854: Profile_CpuUpdate();
855: }
1.1.1.4 root 856: if (LOG_TRACE_LEVEL((TRACE_CPU_DISASM|TRACE_CPU_SYMBOLS)))
1.1 root 857: {
1.1.1.8 root 858: DebugCpu_ShowAddressInfo(M68000_GetPC(), TraceFile);
1.1 root 859: }
1.1.1.9 root 860: if (LOG_TRACE_LEVEL(TRACE_CPU_REGS))
861: {
862: uaecptr nextpc;
863: #ifdef WINUAE_FOR_HATARI
1.1.1.10! root 864: m68k_dumpstate_file(TraceFile, &nextpc, 0xffffffff);
1.1.1.9 root 865: #else
866: m68k_dumpstate(TraceFile, &nextpc);
867: #endif
868: }
1.1 root 869: if (nCpuActiveCBs)
870: {
871: if (BreakCond_MatchCpu())
1.1.1.5 root 872: {
1.1.1.3 root 873: DebugUI(REASON_CPU_BREAKPOINT);
1.1.1.5 root 874: /* make sure we don't decrease step count
875: * below, before even even getting out of here
876: */
877: if (nCpuSteps)
878: nCpuSteps++;
879: }
1.1 root 880: }
881: if (nCpuSteps)
882: {
1.1.1.5 root 883: nCpuSteps--;
1.1 root 884: if (nCpuSteps == 0)
1.1.1.3 root 885: DebugUI(REASON_CPU_STEPS);
886: }
1.1.1.5 root 887: if (History_TrackCpu())
1.1.1.3 root 888: {
889: History_AddCpu();
1.1 root 890: }
1.1.1.5 root 891: if (ConOutDevice != CONOUT_DEVICE_NONE)
892: {
893: Console_Check();
894: }
1.1 root 895: }
896:
897: /**
898: * Should be called before returning back emulation to tell the CPU core
899: * to call us after each instruction if "real-time" debugging like
900: * breakpoints has been set.
901: */
902: void DebugCpu_SetDebugging(void)
903: {
1.1.1.2 root 904: bCpuProfiling = Profile_CpuStart();
1.1.1.7 root 905: nCpuActiveCBs = BreakCond_CpuBreakPointCount();
1.1.1.2 root 906:
1.1.1.5 root 907: if (nCpuActiveCBs || nCpuSteps || bCpuProfiling || History_TrackCpu()
1.1.1.9 root 908: || LOG_TRACE_LEVEL((TRACE_CPU_DISASM|TRACE_CPU_SYMBOLS|TRACE_CPU_REGS))
1.1.1.5 root 909: || ConOutDevice != CONOUT_DEVICE_NONE)
1.1.1.6 root 910: {
1.1.1.10! root 911: M68000_SetDebugger(true);
1.1.1.6 root 912: nCpuInstructions = 0;
913: }
1.1 root 914: else
1.1.1.10! root 915: M68000_SetDebugger(false);
1.1 root 916: }
917:
918:
919: static const dbgcommand_t cpucommands[] =
920: {
921: { NULL, NULL, "CPU commands", NULL, NULL, NULL, false },
922: /* NULL as match function will complete file names */
923: { DebugCpu_BreakAddr, Symbols_MatchCpuCodeAddress,
924: "address", "a",
925: "set CPU PC address breakpoints",
926: BreakAddr_Description,
927: true },
1.1.1.8 root 928: { DebugCpu_BreakCond, Vars_MatchCpuVariable,
1.1 root 929: "breakpoint", "b",
930: "set/remove/list conditional CPU breakpoints",
931: BreakCond_Description,
932: true },
933: { DebugCpu_DisAsm, Symbols_MatchCpuCodeAddress,
934: "disasm", "d",
935: "disassemble from PC, or given address",
936: "[<start address>[-<end address>]]\n"
937: "\tIf no address is given, this command disassembles from the last\n"
938: "\tposition or from current PC if no last position is available.",
939: false },
1.1.1.2 root 940: { DebugCpu_Profile, Profile_Match,
941: "profile", "",
942: "profile CPU code",
943: Profile_Description,
944: false },
1.1 root 945: { DebugCpu_Register, DebugCpu_MatchRegister,
946: "cpureg", "r",
947: "dump register values or set register to value",
948: "[REG=value]\n"
949: "\tSet CPU register to value or dumps all register if no parameter\n"
950: "\thas been specified.",
951: true },
952: { DebugCpu_MemDump, Symbols_MatchCpuDataAddress,
953: "memdump", "m",
954: "dump memory",
1.1.1.10! root 955: "[b|w|l] [<start address>[-<end address>| <count>]]\n"
! 956: "\tdump memory at address or continue dump from previous address.\n"
! 957: "\tBy default memory output is done as bytes, with 'w' or 'l'\n"
! 958: "\toption, it will be done as words/longs instead. Output amount\n"
! 959: "\tcan be given either as a count or an address range.",
1.1 root 960: false },
961: { DebugCpu_MemWrite, Symbols_MatchCpuAddress,
962: "memwrite", "w",
1.1.1.10! root 963: "write bytes/words/longs to memory",
! 964: "[b|w|l] address value1 [value2 ...]\n"
! 965: "\tWrite space separate values (in current number base) to given\n"
! 966: "\tmemory address. By default writes are done as bytes, with\n"
! 967: "\t'w' or 'l' option they will be done as words/longs instead",
1.1 root 968: false },
969: { DebugCpu_LoadBin, NULL,
970: "loadbin", "l",
971: "load a file into memory",
972: "filename address\n"
973: "\tLoad the file <filename> into memory starting at <address>.",
974: false },
975: { DebugCpu_SaveBin, NULL,
1.1.1.5 root 976: "savebin", "",
1.1 root 977: "save memory to a file",
978: "filename address length\n"
979: "\tSave the memory block at <address> with given <length> to\n"
980: "\tthe file <filename>.",
981: false },
1.1.1.6 root 982: { Symbols_Command, Symbols_MatchCommand,
1.1 root 983: "symbols", "",
984: "load CPU symbols & their addresses",
985: Symbols_Description,
986: false },
1.1.1.5 root 987: { DebugCpu_Step, NULL,
988: "step", "s",
989: "single-step CPU",
990: "\n"
991: "\tExecute next CPU instruction (equals 'c 1')",
992: false },
1.1.1.6 root 993: { DebugCpu_Next, DebugCpu_MatchNext,
1.1.1.5 root 994: "next", "n",
1.1.1.6 root 995: "step CPU through subroutine calls / to given instruction type",
996: "[instruction type]\n"
997: "\tSame as 'step' command if there are no subroutine calls.\n"
998: "\tWhen there are, those calls are treated as one instruction.\n"
999: "\tIf argument is given, continues until instruction of given\n"
1000: "\ttype is encountered.",
1.1.1.5 root 1001: false },
1.1 root 1002: { DebugCpu_Continue, NULL,
1003: "cont", "c",
1004: "continue emulation / CPU single-stepping",
1005: "[steps]\n"
1006: "\tLeave debugger and continue emulation for <steps> CPU instructions\n"
1007: "\tor forever if no steps have been specified.",
1008: false }
1009: };
1010:
1011:
1012: /**
1013: * Should be called when debugger is first entered to initialize
1014: * CPU debugging variables.
1015: *
1016: * if you want disassembly or memdumping to start/continue from
1017: * specific address, you can set them here. If disassembly
1018: * address is zero, disassembling starts from PC.
1019: *
1020: * returns number of CPU commands and pointer to array of them.
1021: */
1022: int DebugCpu_Init(const dbgcommand_t **table)
1023: {
1024: memdump_addr = 0;
1025: disasm_addr = 0;
1026:
1027: *table = cpucommands;
1.1.1.8 root 1028: return ARRAY_SIZE(cpucommands);
1.1 root 1029: }
1030:
1031: /**
1032: * Should be called when debugger is re-entered to reset
1033: * relevant CPU debugging variables.
1034: */
1035: void DebugCpu_InitSession(void)
1036: {
1037: disasm_addr = M68000_GetPC();
1.1.1.2 root 1038: Profile_CpuStop();
1.1 root 1039: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.