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