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