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