/* * rdevice.c - Atari850 emulation * * Copyright (c) ???? Tom Hunt, Chris Martin * Copyright (c) 2003-2005,2007-2008 Atari800 development team (see DOC/CREDITS) * * This file is part of the Atari800 emulator project which emulates * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers. * * Atari800 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 of the License, or * (at your option) any later version. * * Atari800 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 Atari800; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* Originally From Closer To Home (Tom Hunt) * * 2003.04.03 cmartin@ti.com - Added connecting Host name lookup and port * changing as well as CONNECT_STRING changing... * 2003.04.03 cmartin@ti.com - Added setsockopt SO_REUSEADDR for socket to * allow reconnections * 2003.04.05 cmartin@ti.com - Added sighandler for catching SIGPIPE error... * can get this and cause the emu to crash if * the other end disconnects prematurely... * 2003.04.06 cmartin@ti.com - Added translation on/off and line feeds * - Fixed IP address lookup (print ip address if * lookup fails...) * 2003.04.07 cmartin@ti.com - Added Local echo when not connected. * 2003.04.17 cmartin@ti.com - Added Telnet escape sequence parsing for * connecting to telnet * 2003.09.02 cmartin@ti.com - Cleaned up Serial port (/dev/ttyS*) Handling * 2003.09.05 cmartin@ti.com - Included minor bug fixes submitted from * Christian Groessler (thanks Chris!) * - Removed erroneous return in RREAD(). * - Fixed typo for 9600 baud in xio_36() * - Modified serial port dev_name generation in * open_connection_serial() * - Now we only modify the serial port options if * we can open a port (in open_connection_serial()) * - Removed Telnet IAC escape sequence handling in * RWRIT() and now we only * write to the port if we are connected. * 2003.09.08 cmartin@ti.com - Added missing baud rates and data word * lengths (5,6,7,8) * 2003.09.11 cmartin@ti.com - Fixed Address lookup in open_connection() * - Reformatted and cleaned whole file. * - Removed all memory writes to addresses * 746 - 749 except within the RSTAT function. * * TODO: * create a new socket and a buffer for each port number.... * non-concurrent mode error reporting to 747 * error bits in location 746 * implement xio 32? * error reporting to CPU_regY * heavy ASCII translation * controlling DTR, RTS, & XMT - xio 34 * * Dialing out via telnet: * Type "ATDL" to toggle Linefeeds on/off (normally off) * Type "ATDI " example: "ATDI localhost 23" * if the port is omitted, then 23 is assumed. * */ #include "config.h" #include #include #include #include #include #include #include #include #ifdef DEBUG #define DBG_APRINT(x) Log_print(x) #else #define DBG_APRINT(x) #endif #if 0 /* debug stuff */ #ifdef R_NETWORK #warning R_NETWORK defined #endif #ifdef R_SERIAL #warning R_SERIAL defined #endif #endif #ifdef WIN32 #ifdef R_SERIAL #error The Windows version does not support the serial R: device #endif #ifndef R_NETWORK #error R_NETWORK must be defined to use the Windows R: device #endif #include #define F_SETFL 0 #define O_NONBLOCK 0 #define WM_SOCKET (WM_USER+20) #define inet_pton(fam,ip,addr) ((addr)->s_addr = inet_addr(ip), 0) static u_long ioctlsocket_non_block = 1; /* for ioctlsocket */ #define perror(a) printf("%s:WSA error code:%d\n",a,WSAGetLastError()) #define close(a) closesocket(a) typedef char *caddr_t; static void catch_disconnect(int sig); static int rdevice_win32_read(SOCKET s, char *buf, int len) { int r; r = recv(s, buf, len, 0); if (r <= 0 ) { switch (WSAGetLastError()) { case WSAECONNRESET: case WSAECONNABORTED: case WSAESHUTDOWN: catch_disconnect(0); break; } } return r; } static int rdevice_win32_write(SOCKET s, char *buf, int len) { int r; r = send(s, buf, len, 0); if (r <= 0 ) { switch (WSAGetLastError()) { case WSAECONNRESET: case WSAECONNABORTED: case WSAESHUTDOWN: catch_disconnect(0); break; } } return r; } #define read(a,b,c) rdevice_win32_read(a,b,c) #define write(a,b,c) rdevice_win32_write(a,b,c) #elif defined(DREAMCAST) /* above WIN32, below DREAMCAST */ /* these functions reside in dc/atari_dc.c */ extern void dc_init_serial(void); extern int dc_write_serial(unsigned char byte); extern int dc_read_serial(unsigned char *byte); extern void dc_set_baud(int baud); #else /* above DREAMCAST, below not WIN32 and not DREAMCAST */ #if !defined(R_SERIAL) && !defined(R_NETWORK) #error R: device needs serial or network or both #endif #ifdef R_NETWORK #include #include #include #include #include #endif /* R_NETWORK */ #include #include #endif /* not WIN32 and not DREAMCAST */ #if defined(R_SERIAL) && !defined(DREAMCAST) #include #endif /* defined(R_SERIAL) && !defined(DREAMCAST) */ #include "atari.h" #include "rdevice.h" #include "cpu.h" #include "devices.h" #include "log.h" #include "memory.h" #include "util.h" #define Peek(a) MEMORY_dGetByte(a) #define DPeek(a) MEMORY_dGetWord(a) #define Poke(x,y) MEMORY_dPutByte(x, y) /*--------------------------------------------------------------------------- Global Variables ---------------------------------------------------------------------------*/ static int connected; static int do_once; static int rdev_fd; #ifdef R_NETWORK static struct sockaddr_in in; static struct sockaddr_in peer_in; static int sock; static int portnum = 9000; static char inetaddress[256]; static char CONNECT_STRING[40] = "\r\n_CONNECT 2400\r\n"; static int retval; #endif /* R_NETWORK */ static char MESSAGE[256]; static char command_buf[256]; static char bufout[256]; static int concurrent; static int command_end = 0; static int translation = 1; static int trans_cr = 0; static int linefeeds = 1; static int bufend = 0; #ifndef R_NETWORK int RDevice_serial_enabled = 1; #else int RDevice_serial_enabled = 0; /* Default to network, if enabled. Use parameter to -rdevice command line switch to enable serial mode. */ #endif char RDevice_serial_device[FILENAME_MAX]; /*--------------------------------------------------------------------------- Host Support Function - If Disconnect signal is found, then close socket and clean up. ---------------------------------------------------------------------------*/ #ifdef R_NETWORK static void catch_disconnect(int sig) { DBG_APRINT("R*: Disconnected...."); close(rdev_fd); connected = 0; do_once = 0; bufout[0] = 0; strcat(bufout, "\r\nNO CARRIER\r\n"); bufend = 13; #if 0 Poke(747,bufend); Poke(748,0); #endif } #endif /* R_NETWORK */ /*--------------------------------------------------------------------------- Host Support Function - XIO 34 - Called from RDevice_SPEC Controls handshake lines DTR, RTS, SD ---------------------------------------------------------------------------*/ static void xio_34(void) { #ifndef DREAMCAST /* Dreamcast port doesn't currently support handshake lines */ int temp; /*int fid;*/ #ifdef R_SERIAL struct termios options; #endif /* R_SERIAL */ /*int status;*/ /*fid = MEMORY_dGetByte(0x2e) >> 4;*/ temp = MEMORY_dGetByte(Devices_ICAX1Z); #ifdef R_SERIAL if(RDevice_serial_enabled) { tcgetattr(rdev_fd, &options); /*ioctl(rdev_fd, TIOCMGET, &status);*/ } #endif /* R_SERIAL */ if(temp & 0x80) { if(temp & 0x40) { /* turn DTR on */ #ifdef R_SERIAL if(RDevice_serial_enabled) { /*status |= TIOCM_DTR;*/ } #endif /* R_SERIAL */ } else { /*Drop DTR*/ #ifdef R_SERIAL if(RDevice_serial_enabled) { cfsetispeed(&options, B0); cfsetospeed(&options, B0); } #endif /* R_SERIAL */ if(connected != 0) { close ( rdev_fd ); connected = 0; do_once = 0; /*bufend = 0;*/ } } } #ifdef R_SERIAL if(RDevice_serial_enabled) { /* RTS Set/Clear*/ if(temp & 0x20) { if(temp & 0x10) { /*status |= TIOCM_RTS;*/ } else { /*status &= ~TIOCM_RTS;*/ } } if(temp & 0x02) { if(temp & 0x01) { /*status |= TIOCM_RTS;*/ } else { /*status &= ~TIOCM_RTS;*/ } } } #endif /* R_SERIAL */ #ifdef R_SERIAL if(RDevice_serial_enabled) { tcsetattr(rdev_fd, TCSANOW, &options); /*ioctl(rdev_fd, TIOCMSET, &status);*/ } #endif /* R_SERIAL */ #endif /* not defined DREAMCAST */ CPU_regA = 1; CPU_regY = 1; CPU_ClrN; } /*--------------------------------------------------------------------------- Host Support Function - XIO 36 - Called from RDevice_SPEC Sets baud, stop bits, and ready monitoring. ---------------------------------------------------------------------------*/ static void xio_36(void) { int aux1, aux2; #if defined(R_SERIAL) && !defined(DREAMCAST) struct termios options; #endif /* defined(R_SERIAL) && !defined(DREAMCAST) */ aux1 = MEMORY_dGetByte(Devices_ICAX1Z); aux2 = MEMORY_dGetByte(Devices_ICAX2Z); #ifdef R_SERIAL if(RDevice_serial_enabled) { #ifndef DREAMCAST tcgetattr(rdev_fd, &options); /*Set Stop bits*/ if(aux1 & 0x80) { /*2 Stop bits*/ options.c_cflag |= CSTOPB; } else { /*1 Stop bit*/ options.c_cflag &= ~CSTOPB; } /*Set word size*/ if((aux1 & 0x30) == 0) { /*8 bit word size*/ options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; } else if((aux1 & 0x30) == 0x10) { /*7 bit word size*/ options.c_cflag &= ~CSIZE; options.c_cflag |= CS7; } else if((aux1 & 0x30) == 0x20) { /*6 bit word size*/ options.c_cflag &= ~CSIZE; options.c_cflag |= CS6; } else if((aux1 & 0x30) == 0x30) { /*5 bit word size*/ options.c_cflag &= ~CSIZE; options.c_cflag |= CS5; } else { /*8 bit word size*/ options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; } /*Set Baud Rate*/ if((aux1 & 0x0f) == 0) { /*300 Baud*/ cfsetispeed(&options, B300); cfsetospeed(&options, B300); } else if((aux1 & 0x0f) == 1) { /* 45.5 Baud (unsupported) - now 57600 */ #ifdef B57600 cfsetispeed(&options, B57600); cfsetospeed(&options, B57600); #else cfsetispeed(&options, B50); cfsetospeed(&options, B50); #endif } else if ((aux1 & 0x0f) == 2) { /* 50 Baud */ cfsetispeed(&options, B50); cfsetospeed(&options, B50); } else if ((aux1 & 0x0f) == 3) { /* 56.875 Baud (unsupported) - now 115200 */ #ifdef B115200 cfsetispeed(&options, B115200); cfsetospeed(&options, B115200); #else cfsetispeed(&options, B50); cfsetospeed(&options, B50); #endif } else if((aux1 & 0x0f) == 4) { /* 75 Baud */ cfsetispeed(&options, B75); cfsetospeed(&options, B75); } else if((aux1 & 0x0f) == 5) { /* 110 Baud */ cfsetispeed(&options, B110); cfsetospeed(&options, B110); } else if((aux1 & 0x0f) == 6) { /* 134.5 Baud */ cfsetispeed(&options, B134); cfsetospeed(&options, B134); } else if((aux1 & 0x0f) == 7) { /* 150 Baud */ cfsetispeed(&options, B150); cfsetospeed(&options, B150); } else if((aux1 & 0x0f) == 8) { /* 300 Baud */ cfsetispeed(&options, B300); cfsetospeed(&options, B300); } else if((aux1 & 0x0f) == 9) { /* 600 Baud */ cfsetispeed(&options, B600); cfsetospeed(&options, B600); } else if((aux1 & 0x0f) == 10) { /* 1200 Baud */ cfsetispeed(&options, B1200); cfsetospeed(&options, B1200); } else if((aux1 & 0x0f) == 12) { /* 2400 Baud */ cfsetispeed(&options, B2400); cfsetospeed(&options, B2400); } else if((aux1 & 0x0f) == 13) { /* 4800 Baud */ cfsetispeed(&options, B4800); cfsetospeed(&options, B4800); } else if((aux1 & 0x0f) == 14) { /* 9600 Baud */ cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); } else if((aux1 & 0x0f) == 15) { /* 19200 Baud */ #ifdef B19200 cfsetispeed(&options, B19200); cfsetospeed(&options, B19200); #else cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); #endif } else { /* 115200 Baud (can add 38400, 76800 if wanted) */ #ifdef B115200 cfsetispeed(&options, B115200); cfsetospeed(&options, B115200); #else cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); #endif } if(aux1 & 0x40) { /* 230400 Baud */ #ifdef B230400 cfsetispeed(&options, B230400); cfsetospeed(&options, B230400); #endif } tcsetattr(rdev_fd, TCSANOW, &options); #else /* below DREAMCAST, above not; both R_SERIAL */ /* Set Stop bits */ if(aux1 & 0x80) { /* 2 Stop bits */ /* currently not supported on DC */ } else { /* 1 Stop bit */ /* currently not supported on DC */ } /* Set word size */ if((aux1 & 0x30) == 0) { /* 8 bit word size */ /* currently not supported on DC */ } else if((aux1 & 0x30) == 0x10) { /* 7 bit word size */ /* currently not supported on DC */ } else if((aux1 & 0x30) == 0x20) { /* 6 bit word size */ /* currently not supported on DC */ } else if((aux1 & 0x30) == 0x30) { /* 5 bit word size */ /* currently not supported on DC */ } else { /* 8 bit word size */ /* currently not supported on DC */ } /* Set Baud Rate */ if((aux1 & 0x0f) == 0) { /* 300 Baud */ dc_set_baud(300); } else if((aux1 & 0x0f) == 1) { /* 45.5 Baud (unsupported) - now 57600 */ dc_set_baud(57600); } else if ((aux1 & 0x0f) == 2) { /* 50 Baud */ dc_set_baud(50); } else if ((aux1 & 0x0f) == 3) { /* 56.875 Baud (unsupported) - now 115200 */ dc_set_baud(115200); } else if((aux1 & 0x0f) == 4) { /* 75 Baud */ dc_set_baud(75); } else if((aux1 & 0x0f) == 5) { /* 110 Baud */ dc_set_baud(110); } else if((aux1 & 0x0f) == 6) { /* 134.5 Baud */ dc_set_baud(134); } else if((aux1 & 0x0f) == 7) { /* 150 Baud */ dc_set_baud(150); } else if((aux1 & 0x0f) == 8) { /* 300 Baud */ dc_set_baud(300); } else if((aux1 & 0x0f) == 9) { /* 600 Baud */ dc_set_baud(600); } else if((aux1 & 0x0f) == 10) { /* 1200 Baud */ dc_set_baud(1200); } else if((aux1 & 0x0f) == 12) { /* 2400 Baud */ dc_set_baud(2400); } else if((aux1 & 0x0f) == 13) { /* 4800 Baud */ dc_set_baud(4800); } else if((aux1 & 0x0f) == 14) { /* 9600 Baud */ dc_set_baud(9600); } else if((aux1 & 0x0f) == 15) { /* 19200 Baud */ dc_set_baud(19200); } else { /* 115200 Baud (can add 38400, 76800 if wanted) */ dc_set_baud(115200); } if(aux1 & 0x40) { /* 230400 Baud */ dc_set_baud(230400); } #endif /* DREAMCAST */ } #endif /* R_SERIAL */ CPU_regA = 1; CPU_regY = 1; CPU_ClrN; } /*--------------------------------------------------------------------------- Host Support Function - XIO 38 - Called from RDevice_SPEC Sets Translation and parity ---------------------------------------------------------------------------*/ static void xio_38(void) { int aux1; #if defined(R_SERIAL) && !defined(DREAMCAST) struct termios options; #endif /* defined(R_SERIAL) && !defined(DREAMCAST) */ CPU_regA = 1; CPU_regY = 1; CPU_ClrN; aux1 = Peek(Devices_ICAX1Z); #if defined(R_SERIAL) && !defined(DREAMCAST) if(RDevice_serial_enabled) { if(aux1 & 0x04) { /*Odd Parity*/ tcgetattr(rdev_fd, &options); options.c_cflag |= PARENB; options.c_cflag |= PARODD; tcsetattr(rdev_fd, TCSANOW, &options); } else if(aux1 & 0x08) { /*Even Parity*/ tcgetattr(rdev_fd, &options); options.c_cflag |= PARENB; options.c_cflag &= ~PARODD; tcsetattr(rdev_fd, TCSANOW, &options); } else { /*No Parity*/ tcgetattr(rdev_fd, &options); options.c_cflag &= ~PARENB; tcsetattr(rdev_fd, TCSANOW, &options); } } #endif /* defined(R_SERIAL) && !defined(DREAMCAST) */ if(aux1 & 0x20) { /* No Translation */ DBG_APRINT("R*: No ATASCII/ASCII TRANSLATION"); translation = 0; } else { translation = 1; } if(aux1 & 0x40) { /* Append line feed */ DBG_APRINT("R*: Append Line Feeds"); linefeeds = 1; } else { linefeeds = 0; } } /*--------------------------------------------------------------------------- Host Support Function - XIO 40 - Called from RDevice_SPEC Sets concurrent mode. Also checks for dropped carrier. ---------------------------------------------------------------------------*/ static void xio_40(void) { int aux1; /* if(connected == 0) Poke(747,0); */ CPU_regA = 1; CPU_regY = 1; CPU_ClrN; aux1 = Peek(Devices_ICAX1Z); if(aux1 >= 12) { concurrent = 1; DBG_APRINT("R*: Entering concurrent IO mode..."); } else { concurrent = 0; sprintf(MESSAGE, "R*: XIO 40, %d", aux1); DBG_APRINT(MESSAGE); } } /*--------------------------------------------------------------------------- Host Support Function - Internet Socket Open Connection ---------------------------------------------------------------------------*/ #ifdef R_NETWORK static void open_connection(char * address, int port) { struct hostent *host; #ifdef WIN32 static int winsock_started; WSADATA wdata; WORD ver = MAKEWORD(1,1); int ret; if (!winsock_started) { ret = WSAStartup(ver, &wdata); if (ret) { printf("Error in WSAStartup,return code:%d\n", ret); } winsock_started = 1; } #endif /* WIN32 */ if((address != NULL) && (strlen(address) > 0)) { close(rdev_fd); close(sock); do_once = 1; connected = 1; memset ( &peer_in, 0, sizeof ( struct sockaddr_in ) ); /*rdev_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);*/ rdev_fd = socket(AF_INET, SOCK_STREAM, 0); #ifdef WIN32 ioctlsocket(rdev_fd, FIONBIO, &ioctlsocket_non_block); #else fcntl(rdev_fd, F_SETFL, O_NONBLOCK); #endif peer_in.sin_family = AF_INET; if(inet_pton(AF_INET, address, &peer_in.sin_addr) == 0) { /* invalid address if zero */ if((peer_in.sin_addr.s_addr == -1) || (peer_in.sin_addr.s_addr == 0)) { host = gethostbyname(address); if(host != NULL) { sprintf(MESSAGE, "R*: Host = '%s'.", host->h_name); DBG_APRINT(MESSAGE); memcpy((caddr_t)&peer_in.sin_addr, host->h_addr_list[0], host->h_length); } else { perror("gethostbyname"); } } } if(port > 0) { peer_in.sin_port = htons (port); } else { /* telnet port */ peer_in.sin_port = htons (23); } if(connect(rdev_fd, (struct sockaddr *)&peer_in, sizeof(peer_in)) < 0) { #ifdef DEBUG sprintf(MESSAGE, "R*: connect: '%s'", strerror(errno)); DBG_APRINT(MESSAGE); #endif } #ifndef WIN32 signal(SIGPIPE, catch_disconnect); /*Need to see if the other end disconnects...*/ signal(SIGHUP, catch_disconnect); /*Need to see if the other end disconnects...*/ #endif /* WIN32 */ sprintf(MESSAGE, "R*: Connecting to %s", address); DBG_APRINT(MESSAGE); #ifdef WIN32 ioctlsocket(rdev_fd, FIONBIO, &ioctlsocket_non_block); #else fcntl(rdev_fd, F_SETFL, O_NONBLOCK); #endif /* WIN32 */ /* Telnet negotiation */ sprintf(MESSAGE, "%c%c%c%c%c%c%c%c%c", 0xff, 0xfb, 0x01, 0xff, 0xfb, 0x03, 0xff, 0xfd, 0x0f3); write(rdev_fd, MESSAGE, 9); DBG_APRINT("R*: Negotiating Terminal Options..."); } } #endif /* R_NETWORK */ #if defined(R_SERIAL) && !defined(DREAMCAST) #ifdef __linux__ #define TTY_DEV_NAME "/dev/ttyS0" /* Linux */ #elif defined (__NetBSD__) && defined(__i386__) #define TTY_DEV_NAME "/dev/tty00" /* NetBSD/x86 */ #elif defined (__FreeBSD__) && defined(__i386__) #define TTY_DEV_NAME "/dev/ttyd1" /* FreeBSD/x86 */ #elif defined (__sun__) #define TTY_DEV_NAME "/dev/ttya" /* Solaris */ #else #error tty name unknown! #endif #endif /* defined(R_SERIAL) && !defined(DREAMCAST) */ /*--------------------------------------------------------------------------- Host Support Function - Serial Open Connection ---------------------------------------------------------------------------*/ #ifdef R_SERIAL static void open_connection_serial(int port) { #ifdef DREAMCAST dc_init_serial(); connected = 1; #else /* above DREAMCAST, below not */ char dev_name[FILENAME_MAX] = TTY_DEV_NAME; /* reinitialize each time */ struct termios options; if(connected) close(rdev_fd); do_once = 1; if (*RDevice_serial_device) /* got a device name from command line */ { strcpy(dev_name, RDevice_serial_device); } dev_name[strlen(dev_name) - 1] += port - 1; sprintf(MESSAGE, "R*: using serial device %s", dev_name); DBG_APRINT(MESSAGE); rdev_fd = open(dev_name, O_RDWR | O_NOCTTY | O_NDELAY); if(rdev_fd == -1) { connected = 0; perror("R*: open_port: Unable to open serial Port - "); } else { connected = 1; #if 0 fcntl(rdev_fd, F_SETFL, O_NONBLOCK); #endif /*Set 8N1 by default on open*/ /*Set Baud to 115200 by default;*/ tcgetattr(rdev_fd, &options); options.c_lflag = 0; options.c_iflag = 0; options.c_oflag = 0; options.c_cc[VMIN] = 1; options.c_cc[VTIME] = 0; options.c_cflag = CS8 | CREAD | CLOCAL; #if 0 cfmakeraw(&options); /*#else*/ /* from glibc 2.3.2 */ options.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); options.c_oflag &= ~OPOST; options.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); options.c_cflag &= ~(CSIZE|PARENB); options.c_cflag |= CS8; options.c_cc[VMIN] = 1; /* read returns when one char is available. */ options.c_cc[VTIME] = 0; #endif cfsetispeed(&options, B115200); cfsetospeed(&options, B115200); tcsetattr(rdev_fd, TCSANOW, &options); } #endif /* not DREAMCAST */ } #endif /* R_SERIAL */ /*--------------------------------------------------------------------------- Host Support Function - Retrieve address from open command: From Basic: OPEN #1,8,23,"R:JYBOLAC.HOMELINUX.COM" Returns: "jybolac.homelinux.com" ---------------------------------------------------------------------------*/ #ifdef R_NETWORK static void RDevice_GetInetAddress(void) { UWORD bufadr = Devices_SkipDeviceName(); char *p; if (bufadr == 0) { /* XXX: signal error */ inetaddress[0] = '\0'; return; } p = inetaddress; while (p < inetaddress + sizeof(inetaddress) - 1) { char c = (char) MEMORY_dGetByte(bufadr); if (c < '!' || c > '\x7e') break; if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; *p++ = c; bufadr++; } *p = '\0'; } #endif /* R_NETWORK */ /*--------------------------------------------------------------------------- R Device OPEN vector - called from Atari OS Device Handler Address Table ---------------------------------------------------------------------------*/ void RDevice_OPEN(void) { int port; int direction; int devnum; CPU_regA = 1; CPU_regY = 1; CPU_ClrN; bufend = 0; port = Peek(Devices_ICAX2Z); direction = Peek(Devices_ICAX1Z); devnum = MEMORY_dGetByte(Devices_ICDNOZ); if(direction & 0x04) { DBG_APRINT("R*: Open for Reading..."); } if(direction & 0x08) { DBG_APRINT("R*: Open for Writing..."); #ifdef R_SERIAL if(RDevice_serial_enabled) { DBG_APRINT("R*: serial mode."); open_connection_serial(devnum); } #endif /* R_SERIAL */ #if defined(R_SERIAL) && defined(R_NETWORK) else #endif #ifdef R_NETWORK { DBG_APRINT("R*: Socket mode."); RDevice_GetInetAddress(); open_connection(inetaddress, port); } #endif /* R_NETWORK */ } if(direction & 0x01) { /* Open for concurrent mode */ } } /*--------------------------------------------------------------------------- R Device CLOSE vector - called from Atari OS Device Handler Address Table ---------------------------------------------------------------------------*/ void RDevice_CLOS(void) { CPU_regA = 1; CPU_regY = 1; CPU_ClrN; concurrent = 0; bufend = 0; close(rdev_fd); } /*--------------------------------------------------------------------------- R Device READ vector - called from Atari OS Device Handler Address Table ---------------------------------------------------------------------------*/ void RDevice_READ(void) { int j; /*bufend = Peek(747);*/ /*printf("Bufend = %d.\n", bufend);*/ /* if(bufend >= 0) */ /* { */ if(translation) { if(bufout[0] == 0x0d) { CPU_regA = 0x9b; } } else { CPU_regA = bufout[0]; } bufend--; for(j = 0; j <= bufend; j++) { bufout[j] = bufout[j+1]; } /*Cycle the buffer again to skip over linefeed....*/ if(translation && linefeeds && (bufout[0] == 0x0a)) { for(j = 0; j <= bufend; j++) { bufout[j] = bufout[j+1]; } } /*return; ???*/ /* } */ CPU_regY = 1; CPU_ClrN; } /*--------------------------------------------------------------------------- R Device WRITE vector - called from Atari OS Device Handler Address Table ---------------------------------------------------------------------------*/ void RDevice_WRIT(void) { unsigned char out_char; #ifdef R_NETWORK int port; #endif CPU_regY = 1; CPU_ClrN; /*bufend = Peek(747);*/ /* Translation mode */ if(translation) { if(CPU_regA == 0x9b) { out_char = 0x0d; if(linefeeds) { if((RDevice_serial_enabled == 0) && (connected == 0)) { /* local echo */ bufend++; bufout[bufend-1] = out_char; bufout[bufend] = 0; command_end = 0; command_buf[command_end] = 0; strcat(bufout, "OK\r\n"); bufend += 4; } else { #ifndef DREAMCAST write(rdev_fd, (char *)&out_char, 1); /* Write return */ #else dc_write_serial(out_char); #endif } out_char = 0x0a; /*set char for line feed to be output later....*/ } } } else { out_char = CPU_regA; } /* Translate the CR to a LF for telnet, ftp, etc */ if(connected && trans_cr && (out_char == 0x0d)) { out_char = 0x0a; } /*if((RDevice_serial_enabled == 0) && (out_char == 255))*/ /*{*/ /* DBG_APRINT("R: Writing IAC..."); */ /* retval = write(rdev_fd, &out_char, 1);*/ /* IAC escape sequence */ /*}*/ /*if(retval == -1)*/ #ifdef R_NETWORK if((RDevice_serial_enabled == 0) && (connected == 0)) { /* Local echo - only do if in socket mode */ bufend++; bufout[bufend-1] = out_char; bufout[bufend] = 0; /* Grab Command */ if((out_char == 0x9b) || (out_char == 0x0d)) { /*process command with a return/enter*/ command_end = 0; /*Make out going connection command 'ATDI'*/ if((command_buf[0] == 'A') && (command_buf[1] == 'T') && (command_buf[2] == 'D') && (command_buf[3] == 'I')) { /*DBG_APRINT(command_buf);*/ if(strchr(command_buf, ' ') != NULL) { if(strrchr(command_buf, ' ') != strchr(command_buf, ' ')) { port = atoi((char *)(strrchr(command_buf, ' ')+1)); * strrchr(command_buf, ' ') = '\0'; /*zero last space in line*/ } else { port = 23; } open_connection((char *)(strchr(command_buf, ' ')+1), port); /*send string after first space in line*/ } command_buf[command_end] = 0; strcat(bufout, "OK\r\n"); bufend += 4; /*Change translation command 'ATDL'*/ } else if((command_buf[0] == 'A') && (command_buf[1] == 'T') && (command_buf[2] == 'D') && (command_buf[3] == 'L')) { trans_cr = (trans_cr + 1) % 2; command_buf[command_end] = 0; strcat(bufout, "OK\r\n"); bufend += 4; } } else { if(((out_char == 0x7f) || (out_char == 0x08)) && (command_end > 0)) { command_end--; /* backspace */ command_buf[command_end] = 0; } else { command_buf[command_end] = out_char; command_buf[command_end+1] = 0; command_end = (command_end + 1) % 256; } } } else #endif /* R_NETWORK */ #ifndef DREAMCAST if((connected) && (write(rdev_fd, (char *)&out_char, 1) < 1)) { /* returns -1 if disconnected or 0 if could not send */ perror("write"); DBG_APRINT("R*: ERROR on write."); CPU_SetN; CPU_regY = 135; /*bufend = 13;*/ /* To catch NO CARRIER message */ } #else if (connected) { if (dc_write_serial(out_char) != 1) { Log_print("R*: ERROR on write."); CPU_SetN; CPU_regY = 135; } } #endif CPU_regA = 1; } /*--------------------------------------------------------------------------- R Device GET STATUS vector - called from Device Handler Address Table ---------------------------------------------------------------------------*/ void RDevice_STAT(void) { #ifdef WIN32 int len; #else #ifdef R_NETWORK unsigned int len; #endif #endif int bytesread; unsigned char one; int devnum; int on; unsigned char telnet_command[2]; /*static char IACctr = 0;*/ on = 1; if(Peek(764) == 1) { /* Hack for Ice-T Terminal program to work! */ Poke(764, 255); } devnum = MEMORY_dGetByte(Devices_ICDNOZ); #ifdef R_NETWORK if(connected == 0) { if(RDevice_serial_enabled == 0) { if(do_once == 0) { /*strcpy(PORT,"23\n");*/ /*strcpy(PORT,"8000\n");*/ /*sprintf(PORT, "%d", 8000 + devnum);*/ portnum = portnum + devnum - 1; /* Set up the listening port. */ do_once = 1; memset ( &in, 0, sizeof ( struct sockaddr_in ) ); sock = socket ( AF_INET, SOCK_STREAM, 0 ); in.sin_family = AF_INET; in.sin_addr.s_addr = INADDR_ANY; /*in.sin_port = htons ( atoi ( PORT ) );*/ in.sin_port = htons (portnum); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on) ); /* cmartin */ if(bind ( sock, (struct sockaddr *)&in, sizeof ( struct sockaddr_in ) ) < 0) perror("bind"); listen ( sock, 5 ); /* sethostent(1); */ #ifdef WIN32 retval = ioctlsocket(sock, FIONBIO, &ioctlsocket_non_block); #else retval = fcntl( sock, F_SETFL, O_NONBLOCK); #endif /* WIN32 */ len = sizeof ( struct sockaddr_in ); /*bufend = 0;*/ sprintf(MESSAGE, "R%d: Listening on port %d...", devnum, portnum); DBG_APRINT(MESSAGE); } rdev_fd = accept ( sock, (struct sockaddr *)&peer_in, &len ); if(rdev_fd != -1) { struct hostent *host; if (getpeername(rdev_fd, (struct sockaddr *) &peer_in, &len) < 0) { perror("getpeername"); } else { sprintf(MESSAGE, "R%d: Serving Connection from %s...", devnum, inet_ntoa(peer_in.sin_addr)); if ((host = gethostbyaddr((char *) &peer_in.sin_addr, sizeof peer_in.sin_addr, AF_INET)) == NULL) { /*perror("gethostbyaddr");*/ /*DBG_APRINT("Connected.");*/ } else { sprintf(MESSAGE, "R%d: Serving Connection from %s.", devnum, host->h_name); } } DBG_APRINT(MESSAGE); #ifndef WIN32 signal(SIGPIPE, catch_disconnect); /*Need to see if the other end disconnects...*/ signal(SIGHUP, catch_disconnect); /*Need to see if the other end disconnects...*/ #endif /* WIN32 */ #ifdef WIN32 retval = ioctlsocket(rdev_fd, FIONBIO, &ioctlsocket_non_block); #else retval = fcntl( rdev_fd, F_SETFL, O_NONBLOCK); #endif /* WIN32 */ /* Telnet negotiation */ sprintf(MESSAGE, "%c%c%c%c%c%c%c%c%c", 0xff, 0xfb, 0x01, 0xff, 0xfb, 0x03, 0xff, 0xfd, 0x0f3); write(rdev_fd, MESSAGE, 9); DBG_APRINT("R*: Negotiating Terminal Options..."); connected = 1; /* retval = write(rdev_fd, &IACdoBinary, 3); retval = write(rdev_fd, &IACwillBinary, 3); retval = write(rdev_fd, &IACdontLinemode, 3); retval = write(rdev_fd, &IACwontLinemode, 3); */ bufout[0] = 0; strcat(bufout, CONNECT_STRING); bufend = strlen(CONNECT_STRING); close(sock); } } } else #endif /* R_NETWORK */ { /* Actually reading and setting the Atari input buffer here */ if(concurrent) { #ifndef DREAMCAST bytesread = read(rdev_fd, (char *)&one, 1); #else bytesread = dc_read_serial(&one); #endif if(bytesread > 0) { if((RDevice_serial_enabled == 0) && (one == 0xff)) { /* Start Telnet escape seq processing... */ while(read(rdev_fd, (char *)telnet_command,2) != 2) {}; /*sprintf(MESSAGE, "Telnet Command = 0x%x 0x%x", telnet_command[0], telnet_command[1]);*/ /*DBG_APRINT(MESSAGE);*/ if(telnet_command[0] == 0xfd) { /*DO*/ if((telnet_command[1] == 0x01) || (telnet_command[1] == 0x03)) { /* WILL ECHO and GO AHEAD (char mode) */ telnet_command[0] = 0xfb; /* WILL */ } else { telnet_command[0] = 0xfc; /* WONT */ } } else if(telnet_command[0] == 0xfb) { /*WILL*/ /*telnet_command[0] = 0xfd;*/ /*DO*/ telnet_command[0] = 0xfe; /*DONT*/ } else if(telnet_command[0] == 0xfe) { /*DONT*/ telnet_command[0] = 0xfc; } else if(telnet_command[0] == 0xfc) { /*WONT*/ telnet_command[0] = 0xfe; } if(telnet_command[0] == 0xfa) { /* subnegotiation */ while(read(rdev_fd, (char *)&one, 1) != 1) {}; while(one != 0xf0) { /* wait for end of sub negotiation */ while(read(rdev_fd, (char *)&one, 1) != 1) {}; } } else { write(rdev_fd, (char *)&one, 1); write(rdev_fd, (char *)telnet_command, 2); } } else { bufend++; bufout[bufend-1] = one; bufout[bufend] = 0; /*return;*/ } } } } /* Set all values at all memory locations we modify on exit */ Poke(746,0); Poke(748,0); Poke(749,0); CPU_regA = 1; CPU_regY = 1; CPU_ClrN; if(concurrent) { Poke(747,bufend); } else { DBG_APRINT("R*: Not in concurrent mode...."); /*Poke(747,8);*/ Poke(747,(12+48+192)); /* Write 0xfc to address 747 */ } } /*--------------------------------------------------------------------------- R Device SPECIAL vector - called from Atari OS Device Handler Address Table ---------------------------------------------------------------------------*/ void RDevice_SPEC(void) { int iccom; iccom = Peek(Devices_ICCOMZ); sprintf(MESSAGE, "R*: XIO %d", iccom); DBG_APRINT(MESSAGE); /* DBG_APRINT("Devices_ICCOMZ ="); DBG_APRINT("%d",iccom); DBG_APRINT("^^ in Devices_ICCOMZ"); */ switch (iccom) { case 32: /*Force Short Block*/ break; case 34: xio_34(); break; case 36: xio_36(); break; case 38: xio_38(); break; case 40: xio_40(); break; default: DBG_APRINT("R*: Unsupported XIO #."); break; } /* CPU_regA = 1; CPU_regY = 1; CPU_ClrN; */ } /*--------------------------------------------------------------------------- R Device INIT vector - called from Atari OS Device Handler Address Table ---------------------------------------------------------------------------*/ void RDevice_INIT(void) { DBG_APRINT("R*: INIT"); CPU_regA = 1; CPU_regY = 1; CPU_ClrN; } void RDevice_Exit(void) { #ifdef WIN32 WSACleanup(); #endif /* WIN32 */ }