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