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