aiie/nix/debugger.cpp
2021-01-11 08:19:23 -05:00

293 lines
6.7 KiB
C++

#include "debugger.h"
#include "globals.h"
#include "disassembler.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
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);
steppingOut = false;
singleStep = false;
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 ||
sscanf(buf, " 0x%x", &val) == 1
) {
*addrOut = val;
return true;
} else if (sscanf(buf, " $%X", &val) == 1 ||
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)))
void Debugger::step()
{
static char buf[256];
uint8_t cmdbuf[50];
// 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;
}
if (!singleStep && breakpoint && g_cpu->pc != breakpoint) {
// Running until we reach the breakpoint
return;
}
singleStep = false; // we have taken a single step, so reset flag
uint8_t b; // byte value used in parsing
unsigned int val; // common value buffer used in parsing
doover:
do {
GETCH;
} while (b != 'c' && // continue (with breakpoint set)
b != 'q' && // quit
b != 's' && // single step
b != 'S' && // step out
b != 'b' && // set breakpoint
b != 'd' && // show disassembly
b != 'L' && // load memory (lines)
b != '*' // show memory (byte)
);
switch (b) {
case 'c': // continue (if there is a breakpoint set)
if (breakpoint) {
snprintf(buf, sizeof(buf), "Continuing until breakpoint 0x%X\012\015", breakpoint);
write(cd, buf, strlen(buf));
} else {
snprintf(buf, sizeof(buf), "No breakpoint to continue until\012\015");
write(cd, buf, strlen(buf));
goto doover;
}
break;
case 'q': // Close debugging socket and quit
printf("Closing debugging socket\n");
breakpoint = 0;
close(cd); cd=-1;
break;
case 's':
singleStep = true; // for when breakpoint is set: just step once
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 'd': // show disassembly @ PC
{
uint16_t loc=g_cpu->pc;
for (int i=0; i<50/3; i++) {
for (int idx=0; idx<sizeof(cmdbuf); idx++) {
cmdbuf[idx] = g_vm->getMMU()->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);
}
}
goto doover;
break;
case 'L': // Load data to memory. Use: "L 0x<address>\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");
}
}
}
goto doover;
break;
case '*': // read 1 byte of memory. Use '* 0x<address>'
{
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));
}
}
goto doover;
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);
}