700 lines
14 KiB
C++
700 lines
14 KiB
C++
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
|
// Copyright (C) 1999-2003 Forgotten
|
|
// Copyright (C) 2004 Forgotten and the VBA development team
|
|
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2, or(at your option)
|
|
// any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software Foundation,
|
|
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#ifndef _WIN32
|
|
# include <unistd.h>
|
|
# include <sys/socket.h>
|
|
# include <netdb.h>
|
|
# ifdef HAVE_NETINET_IN_H
|
|
# include <netinet/in.h>
|
|
# endif // HAVE_NETINET_IN_H
|
|
# ifdef HAVE_ARPA_INET_H
|
|
# include <arpa/inet.h>
|
|
# else // ! HAVE_ARPA_INET_H
|
|
# define socklen_t int
|
|
# endif // ! HAVE_ARPA_INET_H
|
|
# define SOCKET int
|
|
#else // _WIN32
|
|
# include <winsock.h>
|
|
# include <io.h>
|
|
# define socklen_t int
|
|
# define close closesocket
|
|
# define read _read
|
|
# define write _write
|
|
#endif // _WIN32
|
|
|
|
#include "GBA.h"
|
|
|
|
extern bool debugger;
|
|
extern void CPUUpdateCPSR();
|
|
#ifdef SDL
|
|
extern void (*dbgMain)();
|
|
extern void (*dbgSignal)(int,int);
|
|
extern void debuggerMain();
|
|
extern void debuggerSignal(int,int);
|
|
#endif
|
|
|
|
int remotePort = 55555;
|
|
int remoteSignal = 5;
|
|
SOCKET remoteSocket = -1;
|
|
SOCKET remoteListenSocket = -1;
|
|
bool remoteConnected = false;
|
|
bool remoteResumed = false;
|
|
|
|
int (*remoteSendFnc)(char *, int) = NULL;
|
|
int (*remoteRecvFnc)(char *, int) = NULL;
|
|
bool (*remoteInitFnc)() = NULL;
|
|
void (*remoteCleanUpFnc)() = NULL;
|
|
|
|
#ifndef SDL
|
|
void remoteSetSockets(SOCKET l, SOCKET r)
|
|
{
|
|
remoteSocket = r;
|
|
remoteListenSocket = l;
|
|
}
|
|
#endif
|
|
|
|
int remoteTcpSend(char *data, int len)
|
|
{
|
|
return send(remoteSocket, data, len, 0);
|
|
}
|
|
|
|
int remoteTcpRecv(char *data, int len)
|
|
{
|
|
return recv(remoteSocket, data, len, 0);
|
|
}
|
|
|
|
bool remoteTcpInit()
|
|
{
|
|
if(remoteSocket == -1) {
|
|
#ifdef _WIN32
|
|
WSADATA wsaData;
|
|
int error = WSAStartup(MAKEWORD(1,1),&wsaData);
|
|
#endif // _WIN32
|
|
SOCKET s = socket(PF_INET, SOCK_STREAM, 0);
|
|
|
|
remoteListenSocket = s;
|
|
|
|
if(s < 0) {
|
|
fprintf(stderr,"Error opening socket\n");
|
|
exit(-1);
|
|
}
|
|
int tmp = 1;
|
|
setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, sizeof (tmp));
|
|
|
|
// char hostname[256];
|
|
// gethostname(hostname, 256);
|
|
|
|
// hostent *ent = gethostbyname(hostname);
|
|
// unsigned long a = *((unsigned long *)ent->h_addr);
|
|
|
|
sockaddr_in addr;
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(remotePort);
|
|
addr.sin_addr.s_addr = htonl(0);
|
|
int count = 0;
|
|
while(count < 3) {
|
|
if(bind(s, (sockaddr *)&addr, sizeof(addr))) {
|
|
addr.sin_port = htons(ntohs(addr.sin_port)+1);
|
|
} else
|
|
break;
|
|
}
|
|
if(count == 3) {
|
|
fprintf(stderr,"Error binding \n");
|
|
exit(-1);
|
|
}
|
|
|
|
fprintf(stderr,"Listening for a connection at port %d\n",
|
|
ntohs(addr.sin_port));
|
|
|
|
if(listen(s, 1)) {
|
|
fprintf(stderr, "Error listening\n");
|
|
exit(-1);
|
|
}
|
|
socklen_t len = sizeof(addr);
|
|
|
|
#ifdef _WIN32
|
|
int flag = 0;
|
|
ioctlsocket(s, FIONBIO, (unsigned long *)&flag);
|
|
#endif // _WIN32
|
|
SOCKET s2 = accept(s, (sockaddr *)&addr, &len);
|
|
if(s2 > 0) {
|
|
fprintf(stderr, "Got a connection from %s %d\n",
|
|
inet_ntoa((in_addr)addr.sin_addr),
|
|
ntohs(addr.sin_port));
|
|
} else {
|
|
#ifdef _WIN32
|
|
int error = WSAGetLastError();
|
|
#endif // _WIN32
|
|
}
|
|
char dummy;
|
|
recv(s2, &dummy, 1, 0);
|
|
if(dummy != '+') {
|
|
fprintf(stderr, "ACK not received\n");
|
|
exit(-1);
|
|
}
|
|
remoteSocket = s2;
|
|
// close(s);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void remoteTcpCleanUp()
|
|
{
|
|
if(remoteSocket > 0) {
|
|
fprintf(stderr, "Closing remote socket\n");
|
|
close(remoteSocket);
|
|
remoteSocket = -1;
|
|
}
|
|
if(remoteListenSocket > 0) {
|
|
fprintf(stderr, "Closing listen socket\n");
|
|
close(remoteListenSocket);
|
|
remoteListenSocket = -1;
|
|
}
|
|
}
|
|
|
|
int remotePipeSend(char *data, int len)
|
|
{
|
|
int res = write(1, data, len);
|
|
return res;
|
|
}
|
|
|
|
int remotePipeRecv(char *data, int len)
|
|
{
|
|
int res = read(0, data, len);
|
|
return res;
|
|
}
|
|
|
|
bool remotePipeInit()
|
|
{
|
|
char dummy;
|
|
read(0, &dummy, 1);
|
|
if(dummy != '+') {
|
|
fprintf(stderr, "ACK not received\n");
|
|
exit(-1);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void remotePipeCleanUp()
|
|
{
|
|
}
|
|
|
|
void remoteSetPort(int port)
|
|
{
|
|
remotePort = port;
|
|
}
|
|
|
|
void remoteSetProtocol(int p)
|
|
{
|
|
if(p == 0) {
|
|
remoteSendFnc = remoteTcpSend;
|
|
remoteRecvFnc = remoteTcpRecv;
|
|
remoteInitFnc = remoteTcpInit;
|
|
remoteCleanUpFnc = remoteTcpCleanUp;
|
|
} else {
|
|
remoteSendFnc = remotePipeSend;
|
|
remoteRecvFnc = remotePipeRecv;
|
|
remoteInitFnc = remotePipeInit;
|
|
remoteCleanUpFnc = remotePipeCleanUp;
|
|
}
|
|
}
|
|
|
|
void remoteInit()
|
|
{
|
|
if(remoteInitFnc)
|
|
remoteInitFnc();
|
|
}
|
|
|
|
void remotePutPacket(const char *packet)
|
|
{
|
|
const char *hex = "0123456789abcdef";
|
|
char buffer[1024];
|
|
|
|
size_t count = strlen(packet);
|
|
|
|
unsigned char csum = 0;
|
|
|
|
char *p = buffer;
|
|
*p++ = '$';
|
|
|
|
for(size_t i = 0 ;i < count; i++) {
|
|
csum += packet[i];
|
|
*p++ = packet[i];
|
|
}
|
|
*p++ = '#';
|
|
*p++ = hex[csum>>4];
|
|
*p++ = hex[csum & 15];
|
|
*p++ = 0;
|
|
// printf("Sending %s\n", buffer);
|
|
remoteSendFnc(buffer, (int)count + 4);
|
|
|
|
char c = 0;
|
|
remoteRecvFnc(&c, 1);
|
|
/*
|
|
if(c == '+')
|
|
printf("ACK\n");
|
|
else if(c=='-')
|
|
printf("NACK\n");
|
|
*/
|
|
}
|
|
|
|
#define debuggerReadMemory(addr) \
|
|
(*(u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])
|
|
|
|
#define debuggerReadHalfWord(addr) \
|
|
(*(u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])
|
|
|
|
#define debuggerReadByte(addr) \
|
|
map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]
|
|
|
|
#define debuggerWriteMemory(addr, value) \
|
|
*(u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] = (value)
|
|
|
|
#define debuggerWriteHalfWord(addr, value) \
|
|
*(u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] = (value)
|
|
|
|
#define debuggerWriteByte(addr, value) \
|
|
map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] = (value)
|
|
|
|
void remoteOutput(const char *s, u32 addr)
|
|
{
|
|
char buffer[16384];
|
|
|
|
char *d = buffer;
|
|
*d++ = 'O';
|
|
|
|
if(s) {
|
|
char c = *s++;
|
|
while(c) {
|
|
sprintf(d, "%02x", c);
|
|
d += 2;
|
|
c = *s++;
|
|
}
|
|
} else {
|
|
char c= debuggerReadByte(addr);
|
|
addr++;
|
|
while(c) {
|
|
sprintf(d, "%02x", c);
|
|
d += 2;
|
|
c = debuggerReadByte(addr);
|
|
addr++;
|
|
}
|
|
}
|
|
remotePutPacket(buffer);
|
|
// fprintf(stderr, "Output sent %s\n", buffer);
|
|
}
|
|
|
|
void remoteSendSignal()
|
|
{
|
|
char buffer[1024];
|
|
sprintf(buffer, "S%02x", remoteSignal);
|
|
remotePutPacket(buffer);
|
|
}
|
|
|
|
void remoteSendStatus()
|
|
{
|
|
char buffer[1024];
|
|
sprintf(buffer, "T%02x", remoteSignal);
|
|
char *s = buffer;
|
|
s += 3;
|
|
for(int i = 0; i < 15; i++) {
|
|
u32 v = reg[i].I;
|
|
sprintf(s, "%02x:%02x%02x%02x%02x;",i,
|
|
(v & 255),
|
|
(v >> 8) & 255,
|
|
(v >> 16) & 255,
|
|
(v >> 24) & 255);
|
|
s += 12;
|
|
}
|
|
u32 v = armNextPC;
|
|
sprintf(s, "0f:%02x%02x%02x%02x;", (v & 255),
|
|
(v >> 8) & 255,
|
|
(v >> 16) & 255,
|
|
(v >> 24) & 255);
|
|
s += 12;
|
|
CPUUpdateCPSR();
|
|
v = reg[16].I;
|
|
sprintf(s, "19:%02x%02x%02x%02x;", (v & 255),
|
|
(v >> 8) & 255,
|
|
(v >> 16) & 255,
|
|
(v >> 24) & 255);
|
|
s += 12;
|
|
*s = 0;
|
|
// printf("Sending %s\n", buffer);
|
|
remotePutPacket(buffer);
|
|
}
|
|
|
|
void remoteBinaryWrite(char *p)
|
|
{
|
|
u32 address;
|
|
int count;
|
|
sscanf(p,"%x,%x:", &address, &count);
|
|
// printf("Binary write for %08x %d\n", address, count);
|
|
|
|
p = strchr(p, ':');
|
|
p++;
|
|
for(int i = 0; i < count; i++) {
|
|
u8 b = *p++;
|
|
switch(b) {
|
|
case 0x7d:
|
|
b = *p++;
|
|
debuggerWriteByte(address, (b^0x20));
|
|
address++;
|
|
break;
|
|
default:
|
|
debuggerWriteByte(address, b);
|
|
address++;
|
|
break;
|
|
}
|
|
}
|
|
// printf("ROM is %08x\n", debuggerReadMemory(0x8000254));
|
|
remotePutPacket("OK");
|
|
}
|
|
|
|
void remoteMemoryWrite(char *p)
|
|
{
|
|
u32 address;
|
|
int count;
|
|
sscanf(p,"%x,%x:", &address, &count);
|
|
// printf("Memory write for %08x %d\n", address, count);
|
|
|
|
p = strchr(p, ':');
|
|
p++;
|
|
for(int i = 0; i < count; i++) {
|
|
u8 v = 0;
|
|
char c = *p++;
|
|
if(c <= '9')
|
|
v = (c - '0') << 4;
|
|
else
|
|
v = (c + 10 - 'a') << 4;
|
|
c = *p++;
|
|
if(c <= '9')
|
|
v += (c - '0');
|
|
else
|
|
v += (c + 10 - 'a');
|
|
debuggerWriteByte(address, v);
|
|
address++;
|
|
}
|
|
// printf("ROM is %08x\n", debuggerReadMemory(0x8000254));
|
|
remotePutPacket("OK");
|
|
}
|
|
|
|
void remoteMemoryRead(char *p)
|
|
{
|
|
u32 address;
|
|
int count;
|
|
sscanf(p,"%x,%x:", &address, &count);
|
|
// printf("Memory read for %08x %d\n", address, count);
|
|
|
|
char buffer[1024];
|
|
|
|
char *s = buffer;
|
|
for(int i = 0; i < count; i++) {
|
|
u8 b = debuggerReadByte(address);
|
|
sprintf(s, "%02x", b);
|
|
address++;
|
|
s += 2;
|
|
}
|
|
*s = 0;
|
|
remotePutPacket(buffer);
|
|
}
|
|
|
|
void remoteStepOverRange(char *p)
|
|
{
|
|
u32 address;
|
|
u32 final;
|
|
sscanf(p, "%x,%x", &address, &final);
|
|
|
|
remotePutPacket("OK");
|
|
|
|
remoteResumed = true;
|
|
do {
|
|
CPULoop(1);
|
|
if(debugger)
|
|
break;
|
|
} while(armNextPC >= address && armNextPC < final);
|
|
|
|
remoteResumed = false;
|
|
|
|
remoteSendStatus();
|
|
}
|
|
|
|
void remoteWriteWatch(char *p, bool active)
|
|
{
|
|
u32 address;
|
|
int count;
|
|
sscanf(p, ",%x,%x#", &address, &count);
|
|
|
|
fprintf(stderr, "Write watch for %08x %d\n", address, count);
|
|
|
|
if(address < 0x2000000 || address > 0x3007fff) {
|
|
remotePutPacket("E01");
|
|
return;
|
|
}
|
|
|
|
if(address > 0x203ffff && address < 0x3000000) {
|
|
remotePutPacket("E01");
|
|
return;
|
|
}
|
|
|
|
u32 final = address + count;
|
|
|
|
if(address < 0x2040000 && final > 0x2040000) {
|
|
remotePutPacket("E01");
|
|
return;
|
|
} else if(address < 0x3008000 && final > 0x3008000) {
|
|
remotePutPacket("E01");
|
|
return;
|
|
}
|
|
|
|
for(int i = 0; i < count; i++) {
|
|
if((address >> 24) == 2)
|
|
freezeWorkRAM[address & 0x3ffff] = active;
|
|
else
|
|
freezeInternalRAM[address & 0x7fff] = active;
|
|
address++;
|
|
}
|
|
|
|
remotePutPacket("OK");
|
|
}
|
|
|
|
void remoteReadRegisters(char *p)
|
|
{
|
|
char buffer[1024];
|
|
|
|
char *s = buffer;
|
|
int i;
|
|
// regular registers
|
|
for(i = 0; i < 15; i++) {
|
|
u32 v = reg[i].I;
|
|
sprintf(s, "%02x%02x%02x%02x", v & 255, (v >> 8) & 255,
|
|
(v >> 16) & 255, (v >> 24) & 255);
|
|
s += 8;
|
|
}
|
|
// PC
|
|
u32 pc = armNextPC;
|
|
sprintf(s, "%02x%02x%02x%02x", pc & 255, (pc >> 8) & 255,
|
|
(pc >> 16) & 255, (pc >> 24) & 255);
|
|
s += 8;
|
|
|
|
// floating point registers (24-bit)
|
|
for(i = 0; i < 8; i++) {
|
|
sprintf(s, "000000000000000000000000");
|
|
s += 24;
|
|
}
|
|
|
|
// FP status register
|
|
sprintf(s, "00000000");
|
|
s += 8;
|
|
// CPSR
|
|
CPUUpdateCPSR();
|
|
u32 v = reg[16].I;
|
|
sprintf(s, "%02x%02x%02x%02x", v & 255, (v >> 8) & 255,
|
|
(v >> 16) & 255, (v >> 24) & 255);
|
|
s += 8;
|
|
*s = 0;
|
|
remotePutPacket(buffer);
|
|
}
|
|
|
|
void remoteWriteRegister(char *p)
|
|
{
|
|
int r;
|
|
|
|
sscanf(p, "%x=", &r);
|
|
|
|
p = strchr(p, '=');
|
|
p++;
|
|
|
|
char c = *p++;
|
|
|
|
u32 v = 0;
|
|
|
|
u8 data[4] = {0,0,0,0};
|
|
|
|
int i = 0;
|
|
|
|
while(c != '#') {
|
|
u8 b = 0;
|
|
if(c <= '9')
|
|
b = (c - '0') << 4;
|
|
else
|
|
b = (c + 10 - 'a') << 4;
|
|
c = *p++;
|
|
if(c <= '9')
|
|
b += (c - '0');
|
|
else
|
|
b += (c + 10 - 'a');
|
|
data[i++] = b;
|
|
c = *p++;
|
|
}
|
|
|
|
v = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
|
|
|
|
// printf("Write register %d=%08x\n", r, v);
|
|
reg[r].I = v;
|
|
if(r == 15) {
|
|
armNextPC = v;
|
|
if(armState)
|
|
reg[15].I = v + 4;
|
|
else
|
|
reg[15].I = v + 2;
|
|
}
|
|
remotePutPacket("OK");
|
|
}
|
|
|
|
extern int emulating;
|
|
|
|
void remoteStubMain()
|
|
{
|
|
if(!debugger)
|
|
return;
|
|
|
|
if(remoteResumed) {
|
|
remoteSendStatus();
|
|
remoteResumed = false;
|
|
}
|
|
|
|
while(1) {
|
|
char buffer[1024];
|
|
int res = remoteRecvFnc(buffer, 1024);
|
|
|
|
if(res == -1) {
|
|
fprintf(stderr, "GDB connection lost\n");
|
|
#ifdef SDL
|
|
dbgMain = debuggerMain;
|
|
dbgSignal = debuggerSignal;
|
|
#endif
|
|
debugger = false;
|
|
break;
|
|
}
|
|
|
|
// fprintf(stderr, "Received %s\n", buffer);
|
|
char *p = buffer;
|
|
char c = *p++;
|
|
char pp = '+';
|
|
remoteSendFnc(&pp, 1);
|
|
|
|
if(c != '$')
|
|
continue;
|
|
c= *p++;
|
|
switch(c) {
|
|
case '?':
|
|
remoteSendSignal();
|
|
break;
|
|
case 'D':
|
|
remotePutPacket("OK");
|
|
#ifdef SDL
|
|
dbgMain = debuggerMain;
|
|
dbgSignal = debuggerSignal;
|
|
#endif
|
|
remoteResumed = true;
|
|
debugger = false;
|
|
return;
|
|
case 'e':
|
|
remoteStepOverRange(p);
|
|
break;
|
|
case 'k':
|
|
remotePutPacket("OK");
|
|
#ifdef SDL
|
|
dbgMain = debuggerMain;
|
|
dbgSignal = debuggerSignal;
|
|
#endif
|
|
debugger = false;
|
|
emulating = false;
|
|
return;
|
|
case 'C':
|
|
remoteResumed = true;
|
|
debugger = false;
|
|
return;
|
|
case 'c':
|
|
remoteResumed = true;
|
|
debugger = false;
|
|
return;
|
|
case 's':
|
|
remoteResumed = true;
|
|
remoteSignal = 5;
|
|
CPULoop(1);
|
|
if(remoteResumed) {
|
|
remoteResumed = false;
|
|
remoteSendStatus();
|
|
}
|
|
break;
|
|
case 'g':
|
|
remoteReadRegisters(p);
|
|
break;
|
|
case 'P':
|
|
remoteWriteRegister(p);
|
|
break;
|
|
case 'M':
|
|
remoteMemoryWrite(p);
|
|
break;
|
|
case 'm':
|
|
remoteMemoryRead(p);
|
|
break;
|
|
case 'X':
|
|
remoteBinaryWrite(p);
|
|
break;
|
|
case 'H':
|
|
remotePutPacket("OK");
|
|
break;
|
|
case 'q':
|
|
remotePutPacket("");
|
|
break;
|
|
case 'Z':
|
|
if(*p++ == '2') {
|
|
remoteWriteWatch(p, true);
|
|
} else
|
|
remotePutPacket("");
|
|
break;
|
|
case 'z':
|
|
if(*p++ == '2') {
|
|
remoteWriteWatch(p, false);
|
|
} else
|
|
remotePutPacket("");
|
|
break;
|
|
default:
|
|
{
|
|
*(strchr(p, '#') + 3) = 0;
|
|
fprintf(stderr, "Unknown packet %s\n", --p);
|
|
remotePutPacket("");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void remoteStubSignal(int sig, int number)
|
|
{
|
|
remoteSignal = sig;
|
|
remoteResumed = false;
|
|
remoteSendStatus();
|
|
debugger = true;
|
|
}
|
|
|
|
void remoteCleanUp()
|
|
{
|
|
if(remoteCleanUpFnc)
|
|
remoteCleanUpFnc();
|
|
}
|