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