#include "debugger.h" #include "globals.h" #include "disassembler.h" #include #include #include #include #include #include Disassembler dis; static void *cpu_thread(void *objptr) { Debugger *obj = (Debugger *)objptr; while (1) { struct sockaddr_in client; socklen_t clilen = sizeof(client); int newsockfd = accept(obj->sd, (struct sockaddr *)&client, &clilen); if (newsockfd < 0) { perror("ERROR on accept"); exit(1); } obj->setSocket(newsockfd); sleep(1); } } Debugger::Debugger() { struct sockaddr_in server; int optval; sd = socket(AF_INET, SOCK_STREAM, 0); cd = -1; breakpoint = 0; optval=1; setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (void*)&optval, sizeof(optval)); memset(&server, 0, sizeof(struct sockaddr_in)); server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(12345); if (bind(sd, (struct sockaddr *) &server, sizeof(server)) < 0) { perror("error binding to debug socket"); exit(1); } listen(sd,5); if (!pthread_create(&listenThreadID, NULL, &cpu_thread, (void *)this)) { ; // ... what? } } Debugger::~Debugger() { } bool getAddress(const char *buf, unsigned int *addrOut) { unsigned int val; if (sscanf(buf, " 0x%X", &val) == 1) { *addrOut = val; return true; } else if (sscanf(buf, " $%X", &val) == 1) { *addrOut = val; return true; } else if (sscanf(buf, " %d", &val) == 1) { *addrOut = val; return true; } return false; } #define GETCH { if ((read(cd,&b,1)) == -1) { close(cd); cd=-1; return; } } #define GETLN { int ptr=0; while (((read(cd,&b,1)) != -1) && ptr < sizeof(buf) && b != 10 && b != 13) { if (b) {buf[ptr++] = b;} } buf[ptr]=0; } #define HEXCHAR(x) ((x>='0'&&x<='9')?x-'0':(x>='a'&&x<='f')?x-'a'+10:(x>='A'&&x<='F')?x-'A'+10:(x=='i' || x=='I')?1:(x=='o' || x=='O')?0:0) #define FROMHEXP(p) ((HEXCHAR(*p) << 4) | HEXCHAR(*(p+1))) bool steppingOut = false; void Debugger::step() { static char buf[256]; // FIXME: add more than just RTS(0x60) here if (steppingOut && g_vm->getMMU()->read(g_cpu->pc) != 0x60) { return; } steppingOut = false; if (cd != -1) { // Print the status back out the socket uint8_t p = g_cpu->flags; snprintf(buf, sizeof(buf), "OP: $%02x A: %02x X: %02x Y: %02x PC: $%04x SP: %02x Flags: %c%cx%c%c%c%c%c\n", g_vm->getMMU()->read(g_cpu->pc), g_cpu->a, g_cpu->x, g_cpu->y, g_cpu->pc, g_cpu->sp, p & (1<<7) ? 'N':' ', p & (1<<6) ? 'V':' ', p & (1<<4) ? 'B':' ', p & (1<<3) ? 'D':' ', p & (1<<2) ? 'I':' ', p & (1<<1) ? 'Z':' ', p & (1<<0) ? 'C':' ' ); if (write(cd, buf, strlen(buf)) != strlen(buf)) { close(cd); cd=-1; return; } uint8_t cmdbuf[50]; uint16_t loc=g_cpu->pc; for (int i=0; i<50/3; i++) { for (int idx=0; idxgetMMU()->read(loc+idx); } loc += dis.instructionToMnemonic(loc, cmdbuf, buf, sizeof(buf)); write(cd, buf, strlen(buf)); buf[0] = 13; buf[1] = 10; write(cd, buf, 2); } if (breakpoint && g_cpu->pc != breakpoint) { // Running until we reach the breakpoint return; } uint8_t b; // byte value used in parsing unsigned int val; // common value buffer used in parsing GETCH; snprintf(buf, sizeof(buf), "Got char %d\012\015", b); write(cd, buf, strlen(buf)); switch (b) { case 'c': // Continue - close connection and let execution flow printf("Closing debugging socket\n"); close(cd); cd=-1; break; case 'S': steppingOut = true; break; case 'b': // Set breakpoint GETLN; if (getAddress(buf, &val)) { breakpoint = val; } else { breakpoint = 0; } if (breakpoint) { snprintf(buf, sizeof(buf), "Breakpoint set to 0x%X\012\015", breakpoint); } else { snprintf(buf, sizeof(buf), "Breakpoint removed\012\015"); } write(cd, buf, strlen(buf)); break; case 'L': // Load data to memory. Use: "L 0x
\n" followed by lines of packed hex; ends with a blank line { printf("Loading data\n"); GETLN; if (getAddress(buf, &val)) { printf("Load data address: 0x%X\n", val); uint16_t address = val; while (1) { GETLN; if (strlen(buf)==0) break; const char *p = buf; while (*p && *(p+1)) { val = FROMHEXP(p); printf("0x%.2X ", val); g_vm->getMMU()->write(address++, val); p+=2; } printf("\n"); } } } break; case '*': // read 1 byte of memory. Use '* 0x
' { GETLN; if (getAddress(buf, &val)) { sprintf(buf, "Memory location 0x%X: ", val); write(cd, buf, strlen(buf)); val = g_vm->getMMU()->read(val); sprintf(buf, "0x%.2X\012\015", val); write(cd, buf, strlen(buf)); } else { sprintf(buf, "Invalid read\012\015"); write(cd, buf, strlen(buf)); } } break; case 'G': // Goto (set PC) GETLN; if (getAddress(buf, &val)) { snprintf(buf, sizeof(buf), "Setting PC to 0x%X\012\015", val); write(cd, buf, strlen(buf)); g_cpu->pc = val; printf("Closing debugging socket\n"); close(cd); cd=-1; } else { snprintf(buf, sizeof(buf), "sscanf failed, skipping\012\015"); write(cd, buf, strlen(buf)); } break; // ... ? // b - set breakpoint // s - step over // S - step out // c - continue (close connection) // d - disassemble @ current PC // L - load data to memory // G - Goto (set PC) } } } void Debugger::setSocket(int fd) { printf("New debugger session established\n"); cd = fd; } bool Debugger::active() { return (cd != -1); }