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