|
|
1.1 root 1: /*
1.1.1.6 root 2: Hatari - debugui.c
1.1 root 3:
1.1.1.4 root 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:
1.1.1.6 root 7: debugui.c - this is the code for the mini-debugger. When the pause button is
8: pressed, the emulator is (hopefully) halted and this little CLI can be used
9: (in the terminal box) for debugging tasks like memory and register dumps.
1.1 root 10: */
1.1.1.7 ! root 11: const char DebugUI_rcsid[] = "Hatari $Id: debugui.c,v 1.12 2006/02/08 22:49:27 eerot Exp $";
1.1 root 12:
13: #include <ctype.h>
14:
15: #include "main.h"
16: #include "configuration.h"
17: #include "reset.h"
18: #include "m68000.h"
1.1.1.3 root 19: #include "stMemory.h"
1.1 root 20: #include "sound.h"
21: #include "tos.h"
1.1.1.4 root 22: #include "debugui.h"
1.1 root 23:
24: #include "uae-cpu/hatari-glue.h"
25:
26:
27: #define DEBUG_QUIT 0
28: #define DEBUG_CMD 1
29:
30: #define MEMDUMP_COLS 16 /* memdump, number of bytes per row */
31: #define MEMDUMP_ROWS 4 /* memdump, number of rows */
1.1.1.2 root 32: #define NON_PRINT_CHAR '.' /* character to display for non-printables */
1.1 root 33: #define DISASM_INSTS 5 /* disasm - number of instructions */
34:
1.1.1.6 root 35: static BOOL bMemDump; /* has memdump been called? */
36: static unsigned long memdump_addr; /* memdump address */
37: static unsigned long disasm_addr; /* disasm address */
38:
39: static FILE *debugLogFile;
40: static FILE *debug_stdout;
1.1.1.4 root 41:
1.1.1.2 root 42:
1.1 root 43: /* convert string to lowercase */
1.1.1.4 root 44: static void string_tolower(char *str)
1.1 root 45: {
1.1.1.6 root 46: int i=0;
47: while(str[i] != '\0')
48: {
49: if(isupper(str[i]))
50: str[i] = tolower(str[i]);
51: i++;
52: }
1.1 root 53: }
54:
55: /* truncate string at first unprintable char (e.g. newline) */
1.1.1.6 root 56: static void string_trunc(char *str)
57: {
58: int i=0;
59: while (str[i] != '\0')
60: {
61: if (!isprint(str[i]))
62: str[i] = '\0';
63: i++;
64: }
1.1 root 65: }
66:
67: /* check if string is valid hex number. */
1.1.1.4 root 68: static BOOL isHex(char *str)
1.1 root 69: {
1.1.1.6 root 70: int i=0;
71: while (str[i] != '\0' && str[i] != ' ')
72: {
73: if (!isxdigit(str[i]))
74: return FALSE;
75: i++;
76: }
77: return TRUE;
1.1.1.2 root 78: }
79:
1.1.1.6 root 80:
81: /*-----------------------------------------------------------------------*/
82: /*
83: Get a hex adress range, eg. "fa0000-fa0100"
84: returns -1 if not a range,
85: -2 if a range, but not a valid one.
86: 0 if OK.
87: */
88: static BOOL getRange(char *str, unsigned long *lower, unsigned long *upper)
89: {
90: BOOL fDash = FALSE;
91: int i=0;
92:
93: while (str[i] != '\0')
94: {
95: if (str[i] == '-')
96: {
97: str[i] = ' ';
98: fDash = TRUE;
99: }
100: i++;
101: }
102: if (fDash == FALSE)
103: return -1;
104:
105: i = sscanf(str, "%lx%lx", lower, upper);
106: if (i != 2)
107: return -2;
108: if (*lower > *upper)
109: return -3;
110: return 0;
111: }
112:
113:
1.1.1.2 root 114: /*-----------------------------------------------------------------------*/
115: /*
116: Open a log file.
117: */
1.1.1.6 root 118: static void DebugUI_OpenLog(const char *arg)
119: {
120: debugLogFile = fopen(arg, "w");
121: if (debugLogFile == NULL)
122: fprintf(stderr, "Can't open file: %s\n", arg);
123: debug_stdout = debugLogFile;
1.1.1.2 root 124: }
125:
1.1.1.6 root 126:
1.1 root 127: /*-----------------------------------------------------------------------*/
128: /*
129: Load a binary file to a memory address.
130: */
1.1.1.6 root 131: static void DebugUI_LoadBin(char *args)
132: {
133: FILE *fp;
134: unsigned char c;
135: char dummy[100];
136: char filename[200];
137: unsigned long address;
138: int i=0;
139:
140: if (sscanf(args, "%s%s%lx", dummy, filename, &address) != 3)
141: {
142: fprintf(stderr, "Invalid arguments!\n");
143: return;
144: }
145: address &= 0x00FFFFFF;
146: if ((fp = fopen(filename, "rb")) == NULL)
147: {
148: fprintf(stderr,"Cannot open file!\n");
149: }
150:
151: c = fgetc(fp);
152: while (!feof(fp))
153: {
154: i++;
155: STMemory_WriteByte(address++, c);
156: c = fgetc(fp);
157: }
158: fprintf(stderr," Read 0x%x bytes.\n", i);
159: fclose(fp);
1.1 root 160: }
161:
1.1.1.6 root 162:
1.1 root 163: /*-----------------------------------------------------------------------*/
164: /*
165: Dump memory from an address to a binary file.
166: */
1.1.1.6 root 167: static void DebugUI_SaveBin(char *args)
168: {
169: FILE *fp;
170: unsigned char c;
171: char filename[200];
172: char dummy[100];
173: unsigned long address;
174: unsigned long bytes, i=0;
175:
176: if (sscanf(args, "%s%s%lx%lx", dummy, filename, &address, &bytes) != 4)
177: {
178: fprintf(stderr, " Invalid arguments!");
179: return;
180: }
181: address &= 0x00FFFFFF;
182: if ((fp = fopen(filename, "wb")) == NULL)
183: {
184: fprintf(stderr," Cannot open file!\n");
185: }
186:
187: while (i < bytes)
188: {
189: c = STMemory_ReadByte(address++);
190: fputc(c, fp);
191: i++;
192: }
193: fclose(fp);
194: fprintf(stderr, " Wrote 0x%lx bytes.\n", bytes);
1.1 root 195: }
196:
1.1.1.6 root 197:
1.1 root 198: /*-----------------------------------------------------------------------*/
199: /*
200: Do a register dump.
201: */
1.1.1.4 root 202: static void DebugUI_RegDump(void)
1.1 root 203: {
1.1.1.6 root 204: uaecptr nextpc;
205: /* use the UAE function instead */
206: m68k_dumpstate(debug_stdout, &nextpc);
1.1 root 207: }
208:
209:
210: /*-----------------------------------------------------------------------*/
211: /*
212: Dissassemble - arg = starting address, or PC.
213: */
1.1.1.4 root 214: static void DebugUI_DisAsm(char *arg, BOOL cont)
1.1.1.6 root 215: {
216: int i,j;
217: unsigned long disasm_upper;
218: uaecptr nextpc;
219: BOOL isRange = FALSE;
220:
221: if (cont != TRUE)
222: {
223: j = getRange(arg, &disasm_addr, &disasm_upper);
224:
225: if (j == -1)
226: { /* single address, not a range */
227: if (!isHex(arg))
228: {
229: fprintf(stderr,"Invalid address!\n");
230: return;
231: }
232: i = sscanf(arg, "%lx", &disasm_addr);
233:
234: if (i == 0)
235: {
236: fprintf(stderr,"Invalid address!\n");
237: return;
238: }
239: } /* single address */
240: else if (j == -2 || j == -3)
241: {
242: fprintf(stderr,"Invalid range!\n");
243: return;
244: }
245: else
246: { /* range */
247: isRange = TRUE;
248: disasm_upper &= 0x00FFFFFF;
249: }
250: }
251: else /* continue*/
252: if(!disasm_addr)
253: disasm_addr = m68k_getpc();
254:
255: disasm_addr &= 0x00FFFFFF;
256:
257: /* output a single block. */
258: if (isRange == FALSE)
259: {
260: m68k_disasm(debug_stdout, (uaecptr)disasm_addr, &nextpc, DISASM_INSTS);
261: disasm_addr = nextpc;
262: return;
263: }
264:
265: /* output a range */
266: while (disasm_addr < disasm_upper)
267: {
268: m68k_disasm(debug_stdout, (uaecptr)disasm_addr, &nextpc, 1);
269: disasm_addr = nextpc;
270: }
1.1.1.2 root 271: }
272:
1.1.1.6 root 273:
1.1.1.2 root 274: /*-----------------------------------------------------------------------*/
275: /*
276: Set a register:
277: */
1.1.1.6 root 278: static void DebugUI_RegSet(char *arg)
279: {
280: int i;
281: BOOL s = FALSE;
282: char reg[4];
283: long value;
284:
285: for (i=0;i<4;i++)
286: reg[i] = 0;
287: i=0;
288: while (arg[i] != '\0')
289: {
290: if(arg[i] == '=')
291: {
292: arg[i] = ' ';
293: s = TRUE;
294: }
295: i++;
296: }
297:
298: if (s == FALSE)
299: {
300: fprintf(stderr,"\tError, usage: r or r xx=yyyy\n\tWhere: xx=A0-A7, D0-D7, PC or SR and yyyy is a hex value.\n");
301: return;
302: }
303:
304: if (sscanf(arg, "%s%lx", reg, &value) == 2)
305: s = TRUE;
306: else
307: s = FALSE;
308: if (s == FALSE)
309: {
310: fprintf(stderr,"\tError, usage: r or r xx=yyyy\n\tWhere: xx=A0-A7, D0-D7, PC or SR and yyyy is a hex value.\n");
311: return;
312: }
1.1.1.2 root 313:
1.1.1.6 root 314: for (i=0;i<4;i++)
315: reg[i] = toupper(reg[i]);
316:
317: /* set SR and update conditional flags for the UAE CPU core. */
318: if (reg[0] == 'S' && reg[1] == 'R')
319: {
320: SR = value;
321: MakeFromSR();
322: }
323: else if (reg[0] == 'P' && reg[1] == 'C') /* set PC? */
324: {
325: m68k_setpc( value );
326: }
327: else if (reg[0] == 'D') /* Data regs? */
328: {
329: switch (reg[1])
330: {
331: case '0':
332: Regs[REG_D0] = value;
333: break;
334: case '1':
335: Regs[REG_D1] = value;
336: break;
337: case '2':
338: Regs[REG_D2] = value;
339: break;
340: case '3':
341: Regs[REG_D3] = value;
342: break;
343: case '4':
344: Regs[REG_D4] = value;
345: break;
346: case '5':
347: Regs[REG_D5] = value;
348: break;
349: case '6':
350: Regs[REG_D6] = value;
351: break;
352: case '7':
353: Regs[REG_D7] = value;
354: break;
355:
356: default:
357: fprintf(stderr,"\tBad data register, valid values are 0-7\n");
358: break;
359: }
360: }
361: else if(reg[0] == 'A') /* Address regs? */
362: {
363: switch( reg[1] )
364: {
365: case '0':
366: Regs[REG_A0] = value;
367: break;
368: case '1':
369: Regs[REG_A1] = value;
370: break;
371: case '2':
372: Regs[REG_A2] = value;
373: break;
374: case '3':
375: Regs[REG_A3] = value;
376: break;
377: case '4':
378: Regs[REG_A4] = value;
379: break;
380: case '5':
381: Regs[REG_A5] = value;
382: break;
383: case '6':
384: Regs[REG_A6] = value;
385: break;
386: case '7':
387: Regs[REG_A7] = value;
388: break;
389:
390: default:
391: fprintf(stderr,"\tBad address register, valid values are 0-7\n");
392: break;
393: }
394: }
395: else
396: {
397: fprintf(stderr, "\t Bad register!\n");
398: }
1.1 root 399: }
400:
1.1.1.6 root 401:
1.1 root 402: /*-----------------------------------------------------------------------*/
403: /*
404: Do a memory dump, args = starting address.
405: */
1.1.1.4 root 406: static void DebugUI_MemDump(char *arg, BOOL cont)
1.1.1.6 root 407: {
408: int i,j;
409: char c;
410: BOOL isRange = FALSE;
411: unsigned long memdump_upper;
1.1.1.2 root 412:
413:
414:
1.1.1.6 root 415: if (cont != TRUE)
416: {
417: j = getRange(arg, &memdump_addr, &memdump_upper);
418:
419: if (j == -1)
420: { /* single address, not a range */
421: if (!isHex(arg))
422: {
423: bMemDump = FALSE;
424: fprintf(stderr, "Invalid address!\n");
425: return;
426: }
427: i = sscanf(arg, "%lx", &memdump_addr);
428:
429: if (i == 0)
430: {
431: bMemDump = FALSE;
432: fprintf(stderr, "Invalid address!\n");
433: return;
434: }
435: } /* single address */
436: else if (j == -2 || j == -3)
437: {
438: fprintf(stderr, "Invalid range!\n");
439: return;
440: }
441: else
442: { /* range */
443: isRange = TRUE;
444: memdump_upper &= 0x00FFFFFF;
445: }
446: } /* continue */
447:
448: memdump_addr &= 0x00FFFFFF;
449: bMemDump = TRUE;
450:
451: if (isRange != TRUE)
452: {
453: for (j=0;j<MEMDUMP_ROWS;j++)
454: {
455: fprintf(debug_stdout, "%6.6lX: ", memdump_addr); /* print address */
456: for (i = 0; i < MEMDUMP_COLS; i++) /* print hex data */
457: fprintf(debug_stdout, "%2.2x ", STMemory_ReadByte(memdump_addr++));
458: fprintf(debug_stdout, " "); /* print ASCII data */
459: for (i = 0; i < MEMDUMP_COLS; i++)
460: {
461: c = STMemory_ReadByte(memdump_addr-MEMDUMP_COLS+i);
462: if (!isprint(c))
463: c = NON_PRINT_CHAR; /* non-printable as dots */
464: fprintf(debug_stdout,"%c", c);
465: }
466: fprintf(debug_stdout, "\n"); /* newline */
467: }
468: return;
469: } /* not a range */
470:
471: while (memdump_addr < memdump_upper)
472: {
473: fprintf(debug_stdout, "%6.6lX: ", memdump_addr); /* print address */
474: for (i = 0; i < MEMDUMP_COLS; i++) /* print hex data */
475: fprintf(debug_stdout, "%2.2x ", STMemory_ReadByte(memdump_addr++));
476: fprintf(debug_stdout, " "); /* print ASCII data */
477: for (i = 0; i < MEMDUMP_COLS; i++)
478: {
479: c = STMemory_ReadByte(memdump_addr-MEMDUMP_COLS+i);
480: if(!isprint(c))
481: c = NON_PRINT_CHAR; /* non-printable as dots */
482: fprintf(debug_stdout,"%c", c);
483: }
484: fprintf(debug_stdout, "\n"); /* newline */
485: } /* while */
1.1.1.2 root 486: } /* end of memdump */
1.1 root 487:
1.1.1.6 root 488:
1.1 root 489: /*-----------------------------------------------------------------------*/
490: /*
491: Do a memory write, arg = starting address, followed by bytes.
492: */
1.1.1.4 root 493: static void DebugUI_MemWrite(char *addr_str, char *arg)
1.1 root 494: {
1.1.1.6 root 495: int i, j, numBytes;
496: long write_addr;
497: unsigned char bytes[300]; /* store bytes */
498: char temp[15];
499: int d;
500:
501: numBytes = 0;
502: i = 0;
503:
504: string_trunc(arg);
505: while (arg[i] == ' ')
506: i++; /* skip spaces */
507: while (arg[i] != ' ')
508: i++; /* skip command */
509: while (arg[i] == ' ')
510: i++; /* skip spaces */
511:
512: j = 0;
513: while (isxdigit(arg[i]) && j < 14) /* get address */
514: temp[j++] = arg[i++];
515: temp[j] = '\0';
516: j = sscanf(temp, "%lx", &write_addr);
517:
518: /* if next char is not valid, or it's not a valid address */
519: if ((arg[i] != '\0' && arg[i] != ' ') || (j == 0))
520: {
521: fprintf(stderr, "Bad address!\n");
522: return;
523: }
524:
525: write_addr &= 0x00FFFFFF;
526:
527: while (arg[i] == ' ')
528: i++; /* skip spaces */
529:
530: /* get bytes data */
531: while (arg[i] != '\0')
532: {
533: j = 0;
534: while(isxdigit(arg[i]) && j < 14) /* get byte */
535: temp[j++] = arg[i++];
536: temp[j] = '\0';
537:
538: /* if next char is not a null or a space - it's not valid. */
539: if (arg[i] != '\0' && arg[i] != ' ')
540: {
541: fprintf(stderr, "Bad byte argument: %c\n", arg[i]);
542: return;
543: }
544:
545: if (temp[0] != '\0')
546: {
547: if (sscanf(temp,"%x", &d) != 1)
548: {
549: fprintf(stderr, "Bad byte argument!\n");
550: return;
551: }
552: }
553:
554: bytes[numBytes] = (d&0x0FF);
555: numBytes++;
556: while (arg[i] == ' ')
557: i++; /* skip any spaces */
558: }
559:
560: /* write the data */
561: for (i = 0; i < numBytes; i++)
562: STMemory_WriteByte(write_addr + i, bytes[i]);
1.1 root 563: }
564:
1.1.1.6 root 565:
1.1 root 566: /*-----------------------------------------------------------------------*/
567: /*
1.1.1.6 root 568: Print help.
1.1 root 569: */
1.1.1.4 root 570: static void DebugUI_Help(void)
1.1 root 571: {
1.1.1.6 root 572: fprintf(stderr, "---- debug mode commands ----\n"
573: " d [address]- disassemble from PC, or given address. \n"
574: " r [REG=value] - dump register values/ set register to value \n"
575: " m [address] - dump memory at address, \n\tm alone continues from previous address.\n"
576: " w address bytes - write bytes to a memory address, bytes are space separated. \n"
577: " f [filename] - open log file, no argument closes the log file\n"
578: " Output of reg & mem dumps and disassembly will be written to the log\n"
579: " l filename address - load a file into memory starting at address. \n"
580: " s filename address length - dump length bytes from memory to a file. \n"
581: " o - disable debug mode\n\n"
582: " q - return to emulation\n\n"
583: " Adresses may be given as a range e.g. fc0000-fc0100\nAll values in hexadecimal.\n"
584: "-----------------------------\n"
585: "\n");
1.1 root 586: }
587:
588: /*-----------------------------------------------------------------------*/
589: /*
590: Get a UI command, return it.
591: */
1.1.1.4 root 592: static int DebugUI_Getcommand(void)
1.1.1.6 root 593: {
594: char temp[255];
595: char command[255], arg[255];
596: int i;
597:
598: fprintf(stderr, "> ");
599: temp[0] = '\0';
600: fgets(temp, sizeof(temp), stdin);
601:
602: i = sscanf(temp, "%s%s", command, arg);
603: string_tolower(command);
1.1 root 604:
1.1.1.6 root 605: if (i == 0)
606: {
607: fprintf(stderr, " Unknown command.\n");
608: return DEBUG_CMD;
609: }
610:
611: switch (command[0])
612: {
613: case 'q':
614: return(DEBUG_QUIT);
615: break;
616:
617: case 'h':
618: case '?':
619: DebugUI_Help(); /* get help */
620: return(DEBUG_CMD);
621: break;
622:
623: case 'o':
624: bEnableDebug = FALSE;
625: fprintf(stderr, " Debug mode disabled.\n");
626: return(DEBUG_CMD);
627: break;
628:
629: case 'd':
630: if (i < 2) /* no arg? */
631: DebugUI_DisAsm(arg, TRUE); /* No arg - disassemble at PC */
632: else
633: DebugUI_DisAsm(arg, FALSE); /* disasm at address. */
634: break;
635:
636: case 'm':
637: if (i < 2)
638: { /* no arg? */
639: if (bMemDump == FALSE)
640: {
641: fprintf(stderr," Usage: m address\n");
642: return(DEBUG_CMD);
643: }
644: DebugUI_MemDump(arg, TRUE); /* No arg - continue memdump */
645: }
646: else
647: DebugUI_MemDump(arg, FALSE); /* new memdump */
648: break;
649:
650: case 'f':
651: if (i < 2)
652: { /* no arg? */
653: if (debugLogFile == NULL)
654: fprintf(stderr, "No log file open.\n");
655: else
656: {
657: fclose(debugLogFile);
658: debug_stdout = stderr;
659: fprintf(stderr, "Log closed.\n");
660: }
661: }
662: else
663: DebugUI_OpenLog(arg);
664: break;
665:
666: case 'w':
667: if (i < 2)
668: { /* no arg? */
669: fprintf(stderr, " Usage: w address bytes\n");
670: return DEBUG_CMD;
671: }
672: DebugUI_MemWrite(arg, temp);
673: break;
674:
675: case 'r':
676: if (i < 2)
677: { /* no arg - dump regs */
678: DebugUI_RegDump();
679: return DEBUG_CMD;
680: }
681: DebugUI_RegSet(arg);
682: break;
683:
684: case 'l':
685: if (i < 2)
686: { /* no arg? */
687: fprintf(stderr," Usage: l filename address\n");
688: return DEBUG_CMD;
689: }
690: DebugUI_LoadBin(temp);
691: break;
692:
693: case 's':
694: if (i < 2)
695: { /* no arg? */
696: fprintf(stderr," Usage: s filename address bytes\n");
697: return DEBUG_CMD;
698: }
699: DebugUI_SaveBin(temp);
700: break;
701:
702: default:
703: fprintf(stderr," Unknown command: '%s'\n", command);
704: break;
705: }
706:
707: return DEBUG_CMD;
1.1 root 708: }
709:
710:
711: /*-----------------------------------------------------------------------*/
712: /*
713: Debug UI
714: */
1.1.1.4 root 715: void DebugUI(void)
1.1 root 716: {
1.1.1.6 root 717: debugLogFile = NULL;
718: debug_stdout = stderr; /* output to screen, until log file opened */
1.1.1.2 root 719:
1.1.1.6 root 720: bMemDump = FALSE;
721: disasm_addr = 0;
1.1.1.2 root 722:
723:
1.1.1.6 root 724: fprintf(stderr, "\nYou have entered debug mode. Type q to quit, h for help."
725: "\n------------------------------\n");
726: while (DebugUI_Getcommand() != DEBUG_QUIT)
727: ;
728: if (debugLogFile != NULL)
729: fclose(debugLogFile);
730: fprintf(stderr,"Returning to emulation...\n------------------------------\n\n");
1.1 root 731: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.