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