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