--- uae/src/serial.c 2018/04/24 16:38:39 1.1 +++ uae/src/serial.c 2018/04/24 16:48:34 1.1.1.4 @@ -1,9 +1,11 @@ - /* + /* * UAE - The Un*x Amiga Emulator - * + * * Serial Line Emulation * - * (c) 1996 Stefan Reinauer + * (c) 1996, 1997 Stefan Reinauer + * (c) 1997 Christian Schmitt + * */ #include "sysconfig.h" @@ -11,115 +13,402 @@ #include "config.h" #include "options.h" +#include "uae.h" #include "memory.h" #include "custom.h" +#include "readcpu.h" #include "newcpu.h" +#include "cia.h" -void serial_init(void); -void serial_exit(void); +#undef POSIX_SERIAL +/* Some more or less good way to determine whether we can safely compile in + * the serial stuff. I'm certain it breaks compilation on some systems. */ +#if defined HAVE_SYS_TERMIOS_H && defined HAVE_POSIX_OPT_H && defined HAVE_SYS_IOCTL_H && defined HAVE_TCGETATTR +#define POSIX_SERIAL +#endif -UWORD SERDATR(void); -void SERPER(UWORD w); -void SERDAT(UWORD w); +#ifdef POSIX_SERIAL +#include +#include +#include +#endif + +#if !defined B300 || !defined B1200 || !defined B2400 || !defined B4800 || !defined B9600 +#undef POSIX_SERIAL +#endif +#if !defined B19200 || !defined B57600 || !defined B115200 || !defined B230400 +#undef POSIX_SERIAL +#endif + +#ifndef O_NONBLOCK +#define O_NONBLOCK O_NDELAY +#endif + +#define SERIALDEBUG 1 /* 0, 1, 2 3 */ +#define MODEMTEST 0 /* 0 or 1 */ + +void serial_open (void); +void serial_close (void); +void serial_init (void); +void serial_exit (void); + +void serial_dtr_on (void); +void serial_dtr_off (void); + +void serial_flush_buffer (void); +static int serial_read (char *buffer); + +int serial_readstatus (void); +uae_u16 serial_writestatus (int, int); + +uae_u16 SERDATR (void); + +int SERDATS (void); +void SERPER (uae_u16 w); +void SERDAT (uae_u16 w); + +static char inbuf[1024], outbuf[1024]; +static int inptr, inlast, outlast; + +int waitqueue=0, + carrier=0, + serdev=0, + dsr=0, + dtr=0, + isbaeh=0, + doreadser=0, + serstat=-1; -int sersend; -int serrecv; int sd = -1; -UWORD serdat; +#ifdef POSIX_SERIAL + struct termios tios; +#endif + +uae_u16 serper=0,serdat; -void SERPER(UWORD w) +void SERPER (uae_u16 w) { - if (!use_serial) + int baud=0, pspeed; + + if (!currprefs.use_serial) + return; + +#if defined POSIX_SERIAL + if (serper == w) /* don't set baudrate if it's already ok */ return; + serper=w; -#ifndef __DOS__ - if (w&0x8000) fprintf (stdout, "SERPER: 9 Bit Transmission not implemented\n"); - fprintf (stdout, "serial port baudrate set to "); - switch (w & 0x7fff) - { - case 0x2e9b: fprintf (stdout, " 300\n"); return; - case 0x0ba6: fprintf (stdout, " 1200\n"); return; - case 0x02e9: fprintf (stdout, " 4800\n"); return; - case 0x0174: fprintf (stdout, " 9600\n"); return; - case 0x00b9: fprintf (stdout, "19200\n"); return; - case 0x005c: fprintf (stdout, "38400\n"); return; + if (w&0x8000) + fprintf (stdout, "SERPER: 9bit transmission not implemented.\n"); + + switch (w & 0x7fff) { + /* These values should be calculated by the current + * color clock value (NTSC/PAL). But this solution is + * easy and it works. + */ + + case 0x2e9b: + case 0x2e14: baud=300; pspeed=B300; break; + case 0x170a: + case 0x0b85: baud=1200; pspeed=B1200; break; + case 0x05c2: + case 0x05b9: baud=2400; pspeed=B2400; break; + case 0x02e9: + case 0x02e1: baud=4800; pspeed=B4800; break; + case 0x0174: + case 0x0170: baud=9600; pspeed=B9600; break; + case 0x00b9: + case 0x00b8: baud=19200; pspeed=B19200; break; + case 0x005c: + case 0x005d: baud=38400; pspeed=B38400; break; + case 0x003d: baud=57600; pspeed=B57600; break; + case 0x001e: baud=115200; pspeed=B115200; break; + case 0x000f: baud=230400; pspeed=B230400; break; default: - fprintf(stdout,"%d (approx.)\n", - (unsigned int)(3579546.471/(double)((w&0x7fff)+1))); return; + fprintf (stdout,"SERPER: unsupported baudrate (0x%04x) %d\n",w&0x7fff, + (unsigned int)(3579546.471/(double)((w&0x7fff)+1))); return; } -#else - if (w&0x8000) fprintf (stderr, "SERPER: 9 Bit Transmission not implemented\n"); - fprintf (stderr, "serial port baudrate set to "); - switch (w & 0x7fff) - { - case 0x2e9b: fprintf (stderr, " 300\n"); return; - case 0x0ba6: fprintf (stderr, " 1200\n"); return; - case 0x02e9: fprintf (stderr, " 4800\n"); return; - case 0x0174: fprintf (stderr, " 9600\n"); return; - case 0x00b9: fprintf (stderr, "19200\n"); return; - case 0x005c: fprintf (stderr, "38400\n"); return; - default: - fprintf(stderr,"%d (approx.)\n", - (unsigned int)(3579546.471/(double)((w&0x7fff)+1))); return; + + /* Only access hardware when we own it */ + if (serdev == 1) { + if (tcgetattr (sd, &tios) < 0) { + fprintf (stderr, "SERPER: TCGETATTR failed\n"); + return; + } + + if (cfsetispeed (&tios, pspeed) < 0) { /* set serial input speed */ + fprintf (stderr, "SERPER: CFSETISPEED (%d bps) failed\n", baud); + return; + } + if (cfsetospeed (&tios, pspeed) < 0) { /* set serial output speed */ + fprintf (stderr, "SERPER: CFSETOSPEED (%d bps) failed\n", baud); + return; + } + + if (tcsetattr (sd, TCSADRAIN, &tios) < 0) { + fprintf (stderr, "SERPER: TCSETATTR failed\n"); + return; + } } #endif + +#if SERIALDEBUG > 0 + if (serdev == 1) + fprintf (stdout, "SERPER: baudrate set to %d bit/sec\n", baud); +#endif } -void SERDAT(UWORD w) +/* Not (fully) implemented yet: + * + * - Something's wrong with the Interrupts. + * (NComm works, TERM does not. TERM switches to a + * blind mode after a connect and wait's for the end + * of an asynchronous read before switching blind + * mode off again. It never gets there on UAE :-< ) + * + * - RTS/CTS handshake, this is not really neccessary, + * because you can use RTS/CTS "outside" without + * passing it through to the emulated Amiga + * + * - ADCON-Register ($9e write, $10 read) Bit 11 (UARTBRK) + * (see "Amiga Intern", pg 246) + */ + +void SERDAT (uae_u16 w) { - char z; + unsigned char z; - if (!use_serial) + if (!currprefs.use_serial) return; - fprintf(stderr,"SERDAT: wrote 0x%04x\n",w); - z=(char)(w&0xff); - if (sd>0) { - write (sd,&z,1); - } + z = (unsigned char)(w&0xff); + + if (currprefs.serial_demand && !dtr) { + if (!isbaeh) { + fprintf(stdout,"SERDAT: Baeh.. Your software needs SERIAL_ALWAYS to work properly.\n"); + isbaeh=1; + } + return; + } else { + outbuf[outlast++] = z; + if (outlast == sizeof outbuf) + serial_flush_buffer(); + } + +#if SERIALDEBUG > 2 + fprintf (stderr, "SERDAT: wrote 0x%04x\n", w); +#endif + + serdat|=0x2000; /* Set TBE in the SERDATR ... */ + intreq|=1; /* ... and in INTREQ register */ return; } -UWORD SERDATR(void) +uae_u16 SERDATR (void) +{ + if (!currprefs.use_serial) + return 0; +#if SERIALDEBUG > 2 + fprintf (stdout, "SERDATR: read 0x%04x\n", serdat); +#endif + waitqueue = 0; + return serdat; +} + +int SERDATS (void) { - char z; + unsigned char z; - if (!use_serial) - return; + if (!serdev) /* || (serdat&0x4000)) */ + return 0; - if ((read (sd, &z,1))==1) { - serdat=0x6100; - intreq|=0x0800; - serdat+=(int)z; -#ifndef __DOS__ - fprintf (stdout,"SERDATR: received 0x%02x\n",(int)z); -#else - fprintf (stderr,"SERDATR: received 0x%02x\n",(int)z); + if (waitqueue == 1) { + intreq |= 0x0800; + return 1; + } + + if ((serial_read ((char *)&z)) == 1) { + waitqueue = 1; + serdat = 0x4100; /* RBF and STP set! */ + serdat |= ((unsigned int)z) & 0xff; + intreq |= 0x0800; /* Set RBF flag (Receive Buffer full) */ + +#if SERIALDEBUG > 1 + fprintf (stdout, "SERDATS: received 0x%02x --> serdat==0x%04x\n", + (unsigned int)z, (unsigned int)serdat); #endif + return 1; } - return serdat; + return 0; +} + +void serial_dtr_on(void) +{ +#if SERIALDEBUG > 0 + fprintf (stdout, "DTR on.\n"); +#endif + dtr=1; + + if (currprefs.serial_demand) + serial_open (); +} + +void serial_dtr_off(void) +{ +#if SERIALDEBUG > 0 + fprintf (stdout, "DTR off.\n"); +#endif + dtr=0; + if (currprefs.serial_demand) + serial_close (); } -void serial_init(void) +static int serial_read (char *buffer) { - if (!use_serial) + if (inptr < inlast) { + *buffer = inbuf[inptr++]; + return 1; + } + + if (serdev == 1) { + inlast = read (sd, inbuf, sizeof inbuf); + inptr = 0; + if (inptr < inlast) { + *buffer = inbuf[inptr++]; + return 1; + } + } + + return 0; +} + +void serial_flush_buffer(void) +{ + if (serdev == 1) { + if (outlast) { + if (sd != 0) { + write (sd, outbuf, outlast); + } + } + outlast = 0; + } else { + outlast = 0; + inptr = 0; + inlast = 0; + } +} + +int serial_readstatus(void) +{ + int status = 0; + +#ifdef POSIX_SERIAL + ioctl (sd, TIOCMGET, &status); + + if (status & TIOCM_CAR) { + if (!carrier) { + ciabpra |= 0x20; /* Push up Carrier Detect line */ + carrier = 1; +#if SERIALDEBUG > 0 + fprintf (stdout, "Carrier detect.\n"); +#endif + } + } else { + if (carrier) { + ciabpra &= ~0x20; + carrier = 0; +#if SERIALDEBUG > 0 + fprintf (stdout, "Carrier lost.\n"); +#endif + } + } + + if (status & TIOCM_DSR) { + if (!dsr) { + ciabpra |= 0x08; /* DSR ON */ + dsr = 1; + } + } else { + if (dsr) { + ciabpra &= ~0x08; + dsr = 0; + } + } +#endif + return status; +} + +uae_u16 serial_writestatus (int old, int nw) +{ + if ((old & 0x80) == 0x80 && (nw & 0x80) == 0x00) + serial_dtr_on(); + if ((old & 0x80) == 0x00 && (nw & 0x80) == 0x80) + serial_dtr_off(); + + if ((old & 0x40) != (nw & 0x40)) + fprintf (stdout, "RTS %s.\n", ((nw & 0x40) == 0x40) ? "set" : "cleared"); + + if ((old & 0x10) != (nw & 0x10)) + fprintf (stdout, "CTS %s.\n", ((nw & 0x10) == 0x10) ? "set" : "cleared"); + + return nw; /* This value could also be changed here */ +} + +void serial_open(void) +{ + if (serdev == 1) + return; + + if ((sd = open (sername, O_RDWR|O_NONBLOCK|O_BINARY, 0)) < 0) { + fprintf (stdout, "Error: Could not open Device %s\n", sername); + return; + } + + serdev = 1; + +#ifdef POSIX_SERIAL + if (tcgetattr (sd, &tios) < 0) { /* Initialize Serial tty */ + fprintf (stderr, "Serial: TCGETATTR failed\n"); return; + } + cfmakeraw (&tios); -#ifndef __DOS__ - if ((sd=open(sername,O_RDWR|O_NONBLOCK))<0) - fprintf (stderr,"Error: Could not open Device %s\n",sername); +#ifndef MODEMTEST + tios.c_cflag &= ~CRTSCTS; /* Disable RTS/CTS */ #else - if ((sd=open(sername,O_RDWR|O_NONBLOCK|O_BINARY))<0) - fprintf (stderr,"Error: Could not open Device %s\n",sername); + tios.c_cflag |= CRTSCTS; /* Enabled for testing modems */ +#endif + + if (tcsetattr (sd, TCSADRAIN, &tios) < 0) { + fprintf (stderr,"Serial: TCSETATTR failed\n"); + return; + } #endif - serdat=0x2000; - return; } -void serial_exit(void) +void serial_close (void) { if (sd >= 0) - close(sd); + close (sd); + serdev = 0; +} + +void serial_init (void) +{ + if (!currprefs.use_serial) + return; + + if (!currprefs.serial_demand) + serial_open (); + + serdat = 0x2000; return; } + +void serial_exit (void) +{ + serial_close (); /* serial_close can always be called because it */ + dtr = 0; /* just closes *opened* filehandles which is ok */ + return; /* when exiting. */ +}