/* * UAE - The Un*x Amiga Emulator * * Serial Line Emulation * * (c) 1996, 1997 Stefan Reinauer * (c) 1997 Christian Schmitt * */ #include "sysconfig.h" #include "sysdeps.h" #include "options.h" #include "uae.h" #include "memory.h" #include "custom.h" #include "newcpu.h" #include "cia.h" #include "serial.h" #ifdef SERIAL_PORT # ifndef WIN32 # include "serial.h" # else # include "od-win32/parser.h" # endif #endif #define SERIALDEBUG 0 /* 0, 1, 2 3 */ #define SERIALHSDEBUG 0 #define MODEMTEST 0 /* 0 or 1 */ static int data_in_serdat; /* new data written to SERDAT */ static int data_in_serdatr; /* new data received */ static int data_in_sershift; /* data transferred from SERDAT to shift register */ static uae_u16 serdatshift; /* serial shift register */ static int ovrun; static int dtr; static int serial_period_hsyncs, serial_period_hsync_counter; static int ninebit; int serdev; void serial_open(void); void serial_close(void); uae_u16 serper, serdat, serdatr; static const int allowed_baudrates[] = { 0, 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 31400, 38400, 57600, 115200, 128000, 256000, -1 }; void SERPER (uae_u16 w) { int baud = 0, i, per; static int warned; if (serper == w) /* don't set baudrate if it's already ok */ return; ninebit = 0; serper = w; if (w & 0x8000) { if (!warned) { write_log( "SERIAL: program uses 9bit mode PC=%x\n", m68k_getpc (®s) ); warned++; } ninebit = 1; } w &= 0x7fff; if (w < 13) w = 13; per = w; if (per == 0) per = 1; per = 3546895 / (per + 1); if (per <= 0) per = 1; i = 0; while (allowed_baudrates[i] >= 0 && per > allowed_baudrates[i] * 100 / 97) i++; baud = allowed_baudrates[i]; serial_period_hsyncs = (((serper & 0x7fff) + 1) * 10) / maxhpos; if (serial_period_hsyncs <= 0) serial_period_hsyncs = 1; serial_period_hsync_counter = 0; write_log ("SERIAL: period=%d, baud=%d, hsyncs=%d PC=%x\n", w, baud, serial_period_hsyncs, m68k_getpc (®s)); if (ninebit) baud *= 2; if (currprefs.serial_direct) { if (baud != 31400 && baud < 115200) baud = 115200; serial_period_hsyncs = 1; } #ifdef SERIAL_PORT setbaud (baud); #endif } static char dochar (int v) { v &= 0xff; if (v >= 32 && v < 127) return (char)v; return '.'; } static void checkreceive (int mode) { #ifdef SERIAL_PORT static uae_u32 lastchartime; static int ninebitdata; struct timeval tv; int recdata; if (!readseravail ()) return; if (data_in_serdatr) { /* probably not needed but there may be programs that expect OVRUNs.. */ gettimeofday (&tv, NULL); if (tv.tv_sec > lastchartime) { ovrun = 1; INTREQ (0x8000 | 0x0800); while (readser (&recdata)); write_log ("SERIAL: overrun\n"); } return; } if (ninebit) { for (;;) { if (!readser (&recdata)) return; if (ninebitdata) { serdatr = (ninebitdata & 1) << 8; serdatr |= recdata; serdatr |= 0x200; ninebitdata = 0; break; } else { ninebitdata = recdata; if ((ninebitdata & ~1) != 0xa8) { write_log ("SERIAL: 9-bit serial emulation sync lost, %02.2X != %02.2X\n", ninebitdata & ~1, 0xa8); ninebitdata = 0; return; } continue; } } } else { if (!readser (&recdata)) return; serdatr = recdata; serdatr |= 0x100; } gettimeofday (&tv, NULL); lastchartime = tv.tv_sec + 5; data_in_serdatr = 1; serial_check_irq (); #if SERIALDEBUG > 2 write_log ("SERIAL: received %02.2X (%c)\n", serdatr & 0xff, dochar (serdatr)); #endif #endif } static void checksend (int mode) { int bufstate; #ifdef SERIAL_PORT bufstate = checkserwrite (); #else bufstate = 1; #endif if (!data_in_serdat && !data_in_sershift) return; if (data_in_sershift && mode == 0 && bufstate) data_in_sershift = 0; if (data_in_serdat && !data_in_sershift) { data_in_sershift = 1; serdatshift = serdat; #ifdef SERIAL_PORT if (ninebit) writeser (((serdatshift >> 8) & 1) | 0xa8); writeser (serdatshift); #endif data_in_serdat = 0; INTREQ (0x8000 | 0x0001); #if SERIALDEBUG > 2 write_log ("SERIAL: send %04.4X (%c)\n", serdatshift, dochar (serdatshift)); #endif } } void serial_hsynchandler (void) { if (serial_period_hsyncs == 0) return; serial_period_hsync_counter++; if (serial_period_hsyncs == 1 || (serial_period_hsync_counter % (serial_period_hsyncs - 1)) == 0) checkreceive (0); if ((serial_period_hsync_counter % serial_period_hsyncs) == 0) checksend (0); } /* Not (fully) implemented yet: (on windows this work) * * - 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 :-< ) * */ void SERDAT (uae_u16 w) { serdat = w; if (!(w & 0x3ff)) { #if SERIALDEBUG > 1 write_log ("SERIAL: zero serial word written?! PC=%x\n", m68k_getpc (®s)); #endif return; } #if SERIALDEBUG > 1 if (data_in_serdat) { write_log ("SERIAL: program wrote to SERDAT but old byte wasn't fetched yet\n"); } #endif data_in_serdat = 1; if (!data_in_sershift) checksend (1); #if SERIALDEBUG > 2 write_log ("SERIAL: wrote 0x%04x (%c) PC=%x\n", w, dochar (w), m68k_getpc (®s)); #endif return; } uae_u16 SERDATR (void) { serdatr &= 0x03ff; if (!data_in_serdat) serdatr |= 0x2000; if (!data_in_sershift) serdatr |= 0x1000; if (data_in_serdatr) serdatr |= 0x4000; if (ovrun) serdatr |= 0x8000; #if SERIALDEBUG > 2 write_log( "SERIAL: read 0x%04.4x (%c) %x\n", serdatr, dochar (serdatr), m68k_getpc (®s)); #endif ovrun = 0; data_in_serdatr = 0; return serdatr; } void serial_check_irq (void) { if (data_in_serdatr) INTREQ_0 (0x8000 | 0x0800); } void serial_dtr_on(void) { #if SERIALHSDEBUG > 0 write_log ("SERIAL: DTR on\n"); #endif dtr = 1; if (currprefs.serial_demand) serial_open (); #ifdef SERIAL_PORT setserstat (TIOCM_DTR, dtr); #endif } void serial_dtr_off(void) { #if SERIALHSDEBUG > 0 write_log ("SERIAL: DTR off\n"); #endif dtr = 0; #ifdef SERIAL_PORT if (currprefs.serial_demand) serial_close (); setserstat(TIOCM_DTR, dtr); #endif } void serial_flush_buffer (void) { } static uae_u8 oldserbits; static void serial_status_debug (const char *s) { #if SERIALHSDEBUG > 1 write_log ("%s: DTR=%d RTS=%d CD=%d CTS=%d DSR=%d\n", s, (oldserbits & 0x80) ? 0 : 1, (oldserbits & 0x40) ? 0 : 1, (oldserbits & 0x20) ? 0 : 1, (oldserbits & 0x10) ? 0 : 1, (oldserbits & 0x08) ? 0 : 1); #endif } uae_u8 serial_readstatus (uae_u8 dir) { int status = 0; uae_u8 serbits = oldserbits; #ifdef SERIAL_PORT getserstat (&status); if (!(status & TIOCM_CAR)) { if (!(serbits & 0x20)) { serbits |= 0x20; #if SERIALHSDEBUG > 0 write_log ("SERIAL: CD off\n"); #endif } } else { if (serbits & 0x20) { serbits &= ~0x20; #if SERIALHSDEBUG > 0 write_log ("SERIAL: CD on\n"); #endif } } if (!(status & TIOCM_DSR)) { if (!(serbits & 0x08)) { serbits |= 0x08; #if SERIALHSDEBUG > 0 write_log ("SERIAL: DSR off\n"); #endif } } else { if (serbits & 0x08) { serbits &= ~0x08; #if SERIALHSDEBUG > 0 write_log ("SERIAL: DSR on\n"); #endif } } if (!(status & TIOCM_CTS)) { if (!(serbits & 0x10)) { serbits |= 0x10; #if SERIALHSDEBUG > 0 write_log ("SERIAL: CTS off\n"); #endif } } else { if (serbits & 0x10) { serbits &= ~0x10; #if SERIALHSDEBUG > 0 write_log ("SERIAL: CTS on\n"); #endif } } serbits &= 0x08 | 0x10 | 0x20; oldserbits &= ~(0x08 | 0x10 | 0x20); oldserbits |= serbits; #endif serial_status_debug ("read"); return oldserbits; } uae_u8 serial_writestatus (uae_u8 newstate, uae_u8 dir) { #ifdef SERIAL_PORT if (((oldserbits ^ newstate) & 0x80) && (dir & 0x80)) { if (newstate & 0x80) serial_dtr_off(); else serial_dtr_on(); } if (!currprefs.serial_hwctsrts && (dir & 0x40)) { if ((oldserbits ^ newstate) & 0x40) { if (newstate & 0x40) { setserstat (TIOCM_RTS, 0); #if SERIALHSDEBUG > 0 write_log ("SERIAL: RTS cleared\n"); #endif } else { setserstat (TIOCM_RTS, 1); #if SERIALHSDEBUG > 0 write_log ("SERIAL: RTS set\n"); #endif } } } #if 0 /* CIA io-pins can be read even when set to output.. */ if ((newstate & 0x20) != (oldserbits & 0x20) && (dir & 0x20)) write_log ("SERIAL: warning, program tries to use CD as an output!\n"); if ((newstate & 0x10) != (oldserbits & 0x10) && (dir & 0x10)) write_log ("SERIAL: warning, program tries to use CTS as an output!\n"); if ((newstate & 0x08) != (oldserbits & 0x08) && (dir & 0x08)) write_log ("SERIAL: warning, program tries to use DSR as an output!\n"); #endif if (((newstate ^ oldserbits) & 0x40) && !(dir & 0x40)) write_log ("SERIAL: warning, program tries to use RTS as an input!\n"); if (((newstate ^ oldserbits) & 0x80) && !(dir & 0x80)) write_log ("SERIAL: warning, program tries to use DTR as an input!\n"); #endif oldserbits &= ~(0x80 | 0x40); newstate &= 0x80 | 0x40; oldserbits |= newstate; serial_status_debug ("write"); return oldserbits; } void serial_open(void) { #ifdef SERIAL_PORT if (serdev) return; if (!openser (currprefs.sername)) { write_log ("SERIAL: Could not open device %s\n", currprefs.sername); return; } serdev = 1; #endif } void serial_close (void) { #ifdef SERIAL_PORT closeser (); serdev = 0; #endif } void serial_init (void) { #ifdef SERIAL_PORT if (!currprefs.use_serial) return; if (!currprefs.serial_demand) serial_open (); #endif } void serial_exit (void) { #ifdef SERIAL_PORT serial_close (); /* serial_close can always be called because it */ #endif dtr = 0; /* just closes *opened* filehandles which is ok */ oldserbits = 0; /* when exiting. */ } void serial_uartbreak (int v) { #ifdef SERIAL_PORT serialuartbreak (v); #endif }