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