From 2e649fbfad46bb70bff06083e2009a9a96b531d6 Mon Sep 17 00:00:00 2001 From: Toni Wilen Date: Sun, 5 Apr 2020 17:37:46 +0300 Subject: [PATCH] Paula and uaeserial.device serial port receive break condition support. --- od-win32/parser.cpp | 37 +++++++- od-win32/parser.h | 23 ++--- od-win32/serial_win32.cpp | 181 +++++++++++++++++++++++++++++--------- uaeserial.cpp | 10 ++- 4 files changed, 190 insertions(+), 61 deletions(-) diff --git a/od-win32/parser.cpp b/od-win32/parser.cpp index 523ae2d4..90248e56 100644 --- a/od-win32/parser.cpp +++ b/od-win32/parser.cpp @@ -687,6 +687,8 @@ static void *uaeser_trap_thread (void *arg) sigmask |= 1; if ((evtmask & EV_TXEMPTY) && !sd->writeactive) sigmask |= 2; + if (evtmask & EV_BREAK) + sigmask |= 4; startwce(sd, &evtmask); } cnt = 0; @@ -741,6 +743,8 @@ int uaeser_read (void *vsd, uae_u8 *data, uae_u32 len) if (!ClearCommError (sd->hCom, &err, &ComStat)) return 0; + if (err & EV_BREAK) + return -1; if (len > ComStat.cbInQue) return 0; if (!ReadFile (sd->hCom, data, len, NULL, &sd->olr)) { @@ -841,6 +845,7 @@ static int datainoutput; static int dataininput, dataininputcnt; static OVERLAPPED writeol, readol; static int writepending; +static bool breakpending; static WSADATA wsadata; static SOCKET serialsocket = INVALID_SOCKET; @@ -1014,7 +1019,7 @@ int openser (const TCHAR *sername) return 0; } - SetCommMask (hCom, EV_RXFLAG); + SetCommMask (hCom, EV_RXFLAG | EV_BREAK); SetupComm (hCom, 65536, 128); PurgeComm (hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF; @@ -1059,6 +1064,7 @@ int openser (const TCHAR *sername) return 1; } + write_log (_T("SERIAL: serial driver didn't accept new parameters\n")); closeser(); return 0; @@ -1151,11 +1157,29 @@ int checkserwrite (int spaceneeded) return 1; } -int readseravail (void) +void flushser(void) +{ + if (!tcpserial && !midi_ready && hCom) { + COMSTAT ComStat; + DWORD dwErrorFlags; + ClearCommError(hCom, &dwErrorFlags, &ComStat); + PurgeComm(hCom, PURGE_RXCLEAR); + } else { + while (readseravail(NULL)) { + int data; + if (readser(&data) <= 0) + break; + } + } +} + +int readseravail(bool *breakcond) { COMSTAT ComStat; DWORD dwErrorFlags; + if (breakcond) + *breakcond = false; if (tcpserial) { if (tcp_is_connected ()) { struct timeval tv; @@ -1183,8 +1207,12 @@ int readseravail (void) return 1; if (hCom != INVALID_HANDLE_VALUE) { ClearCommError (hCom, &dwErrorFlags, &ComStat); + if (breakcond && ((dwErrorFlags & CE_BREAK) || breakpending)) { + *breakcond = true; + breakpending = false; + } if (ComStat.cbInQue > 0) - return 1; + return ComStat.cbInQue; } } return 0; @@ -1227,6 +1255,8 @@ int readser (int *buffer) if (hCom != INVALID_HANDLE_VALUE) { /* only try to read number of bytes in queue */ ClearCommError (hCom, &dwErrorFlags, &ComStat); + if (dwErrorFlags & CE_BREAK) + breakpending = true; if (ComStat.cbInQue) { int len = ComStat.cbInQue; if (len > sizeof (inputbuffer)) @@ -1261,6 +1291,7 @@ void serialuartbreak (int v) void getserstat (int *pstatus) { + DWORD err; DWORD stat; int status = 0; diff --git a/od-win32/parser.h b/od-win32/parser.h index c9256d7c..36a44fa5 100644 --- a/od-win32/parser.h +++ b/od-win32/parser.h @@ -9,20 +9,21 @@ #define PRTBUFSIZE 65536 -int setbaud (long baud ); +int setbaud(long baud ); void getserstat(int *status); -void setserstat (int mask, int onoff); -int readser (int *buffer); -int readseravail (void); +void setserstat(int mask, int onoff); +int readser(int *buffer); +int readseravail(bool*); +void flushser(void); void writeser(int c); void writeser_flush(void); -int openser (const TCHAR *sername); -void closeser (void); -void doserout (void); -void closeprinter (void); -void flushprinter (void); -int checkserwrite (int spaceneeded); -void serialuartbreak (int); +int openser(const TCHAR *sername); +void closeser(void); +void doserout(void); +void closeprinter(void); +void flushprinter(void); +int checkserwrite(int spaceneeded); +void serialuartbreak(int); void hsyncstuff(void); diff --git a/od-win32/serial_win32.cpp b/od-win32/serial_win32.cpp index 2a10eb65..84c77ac1 100644 --- a/od-win32/serial_win32.cpp +++ b/od-win32/serial_win32.cpp @@ -31,6 +31,8 @@ #define SERIALDEBUG 0 /* 0, 1, 2 3 */ #define SERIALHSDEBUG 0 #define SERIAL_HSYNC_BEFORE_OVERFLOW 200 +#define SERIAL_BREAK_DELAY (20 * maxvpos) +#define SERIAL_BREAK_TRANSMIT_DELAY 4 #define SERIAL_MAP @@ -164,6 +166,8 @@ bool shmem_serial_create(void) 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 int break_in_serdatr; /* break state */ +static int break_delay; static uae_u16 serdatshift; /* serial shift register */ static uae_u16 serdatshift_masked; /* stop bit masked */ static int ovrun; @@ -269,15 +273,6 @@ static TCHAR dochar(int v) return '.'; } -static void flushser(void) -{ - while (readseravail() > 0) { - int data; - if (!readser(&data)) - break; - } -} - static bool canreceive(void) { if (!data_in_serdatr) @@ -334,50 +329,132 @@ static void checkreceive_serial (void) return; if (ninebit) { - if (!readseravail()) - return; - for (;;) { - if (!readser (&recdata)) - return; - if (ninebitdata) { - serdatr = (ninebitdata & 1) << 8; - serdatr |= recdata; + bool breakcond; + int status = readseravail(&breakcond); + if (break_in_serdatr == -1 || break_in_serdatr > 0) { + serial_recv_previous = 0; + serdatr = 0; + if (break_in_serdatr < 0) { + break_in_serdatr = SERIAL_BREAK_DELAY; + break_delay = SERIAL_BREAK_TRANSMIT_DELAY; + } + if (break_in_serdatr == 1) { serdatr |= 0x200; - ninebitdata = 0; - break; - } else { - ninebitdata = recdata; - if ((ninebitdata & ~1) != 0xa8) { - write_log (_T("SERIAL: 9-bit serial emulation sync lost, %02X != %02X\n"), ninebitdata & ~1, 0xa8); - ninebitdata = 0; + break_in_serdatr = 0; + } + break_delay--; + if (break_delay && break_in_serdatr) { + return; + } + break_delay = SERIAL_BREAK_TRANSMIT_DELAY; + } else { + if (breakcond && !break_in_serdatr) { + break_in_serdatr = -1; + break_in_serdatr -= status; + if (break_in_serdatr == -1) return; + } + if (status <= 0) { + return; + } + for (;;) { + status = readser(&recdata); + if (!status) + return; + if (break_in_serdatr > 0) { + break_in_serdatr = 0; + } + if (ninebitdata) { + serdatr = (ninebitdata & 1) << 8; + serdatr |= recdata; + serdatr |= 0x200; + ninebitdata = 0; + if (break_in_serdatr < -1) { + break_in_serdatr++; + } + break; + } else { + ninebitdata = recdata; + if ((ninebitdata & ~1) != 0xa8) { + write_log(_T("SERIAL: 9-bit serial emulation sync lost, %02X != %02X\n"), ninebitdata & ~1, 0xa8); + ninebitdata = 0; + return; + } + continue; } - continue; } } } else { - if (!readseravail()) - return; - if (!readser(&recdata)) - return; - if (currprefs.serial_crlf) { - if (recdata == 0 || (serial_recv_previous == 13 && recdata == 10)) { - //write_log(_T(" [%02X] "), (uae_u8)recdata); - serial_recv_previous = -1; + bool breakcond; + int status = readseravail(&breakcond); + if (break_in_serdatr == -1 || break_in_serdatr > 0) { + // break: stop bit is zero + // Paula for some reason keeps receiving zeros continuously in break condition. + serial_recv_previous = 0; + serdatr = 0; + if (break_in_serdatr < 0) { + break_in_serdatr = SERIAL_BREAK_DELAY; + break_delay = SERIAL_BREAK_TRANSMIT_DELAY; +#if SERIALDEBUG + write_log(_T("SERIAL: BREAK START\n")); +#endif + } + // serial.device requires valid serial word before it finally returns break error + if (break_in_serdatr == 1) { + serdatr |= 0x100; +#if SERIALDEBUG + write_log(_T("SERIAL: BREAK COMPLETE\n")); +#endif + break_in_serdatr = 0; + } + break_delay--; + if (break_delay && break_in_serdatr) { + return; + } + break_delay = SERIAL_BREAK_TRANSMIT_DELAY; + } else { + if (breakcond && !break_in_serdatr) { + break_in_serdatr = -1; +#if SERIALDEBUG + write_log(_T("SERIAL: BREAK DETECT (%d buffered)\n"), status); +#endif + break_in_serdatr -= status; + if (break_in_serdatr == -1) + return; + } + if (status <= 0) { return; } + if (!readser(&recdata)) + return; + if (break_in_serdatr > 0) { +#if SERIALDEBUG + write_log(_T("SERIAL: BREAK END\n")); +#endif + break_in_serdatr = 0; + } + if (currprefs.serial_crlf) { + if (recdata == 0 || (serial_recv_previous == 13 && recdata == 10)) { + //write_log(_T(" [%02X] "), (uae_u8)recdata); + serial_recv_previous = -1; + return; + } + } + //write_log(_T(" %02X "), (uae_u8)recdata); + serial_recv_previous = recdata; + serdatr = recdata; + serdatr |= 0x0100; + if (break_in_serdatr < -1) { + break_in_serdatr++; + } } - //write_log(_T(" %02X "), (uae_u8)recdata); - serial_recv_previous = recdata; - serdatr = recdata; - serdatr |= 0x100; } data_in_serdatr = 1; serdatr_last_got = 0; - serial_check_irq (); + serial_check_irq(); #if SERIALDEBUG > 2 - write_log (_T("SERIAL: received %02X (%c)\n"), serdatr & 0xff, dochar (serdatr)); + write_log(_T("SERIAL: received %02X (%c)\n"), serdatr & 0xff, dochar(serdatr)); #endif #endif } @@ -589,6 +666,15 @@ void serial_hsynchandler (void) } else if ((serial_period_hsync_counter % serial_period_hsyncs) == 0 && !currprefs.cpu_cycle_exact) { checkshiftempty(); } + if (break_in_serdatr > 1) { + break_in_serdatr--; + if (break_in_serdatr == 1) { +#if SERIALDEBUG + write_log(_T("SERIAL: BREAK TIMEOUT\n")); +#endif + flushser(); + } + } } void SERDAT (uae_u16 w) @@ -618,17 +704,24 @@ void SERDAT (uae_u16 w) serdatcopy(); } -uae_u16 SERDATR (void) +uae_u16 SERDATR(void) { serdatr &= 0x03ff; - if (!data_in_serdat) + if (!data_in_serdat) { serdatr |= 0x2000; - if (!data_in_sershift) + } + if (!data_in_sershift) { serdatr |= 0x1000; - if (data_in_serdatr) + } + if (data_in_serdatr) { serdatr |= 0x4000; - if (ovrun) + } + if (ovrun) { serdatr |= 0x8000; + } + if (break_in_serdatr <= 0) { + serdatr |= 0x0800; + } #if SERIALDEBUG > 2 write_log (_T("SERIAL: read 0x%04x (%c) %x\n"), serdatr, dochar (serdatr), M68K_GETPC); #endif diff --git a/uaeserial.cpp b/uaeserial.cpp index cc6a4b02..ffc4a570 100644 --- a/uaeserial.cpp +++ b/uaeserial.cpp @@ -441,17 +441,21 @@ void uaeser_signal (void *vdev, int sigmask) io_done = 1; while (io_length > 0) { int size = io_length > sizeof(tmp) ? sizeof(tmp) : io_length; - if (uaeser_read(dev->sysdata, tmp, size)) { + int status = uaeser_read(dev->sysdata, tmp, size); + if (status > 0) { trap_put_bytes(ctx, tmp, io_data, size); io_actual += size; io_data += size; io_length -= size; - } else { + } else if (status == 0) { if (io_actual == 0) io_done = 0; break; } } + } else if (sigmask & 4) { + io_done = 1; + io_error = SerErr_DetectedBreak; } break; case CMD_WRITE: @@ -732,7 +736,7 @@ void uaeserialdev_install (void) return; ROM_uaeserialdev_resname = ds (_T("uaeserial.device")); - ROM_uaeserialdev_resid = ds (_T("UAE serial.device 0.2")); + ROM_uaeserialdev_resid = ds (_T("UAE serial.device 0.3")); /* initcode */ initcode = here (); -- 2.47.3