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