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