diff --git a/Changes.txt b/Changes.txt index 61b036ebc..d231d65da 100644 --- a/Changes.txt +++ b/Changes.txt @@ -34,8 +34,8 @@ one ZIP file, and distribute just that file. * Extended AtariVox support to handle flow control, so that long phrases - are no longer corrupted/cut off. Note that some USB-serial adaptors - don't support this mode, so there may still be issues with those. + are no longer corrupted/cut off. This includes properly supporting the + 2600-daptor II, which is flashable to an AVox-USB converter. * Added option to select the audio device. diff --git a/src/emucore/AtariVox.cxx b/src/emucore/AtariVox.cxx index 731232456..6040b3f6d 100644 --- a/src/emucore/AtariVox.cxx +++ b/src/emucore/AtariVox.cxx @@ -56,10 +56,27 @@ bool AtariVox::read(DigitalPin pin) switch(pin) { // Pin 2: SpeakJet READY - // CTS (Clear To Send) is sent directly to pin 2 - // We also deal with the case where devices send CTS inverted + // READY signal is sent directly to pin 2 case DigitalPin::Two: - return setPin(pin, mySerialPort->isCTS() ^ myCTSFlip); + { + // Some USB-serial adaptors support only CTS, others support only + // software flow control + // So we check the state of both then AND the results, on the + // assumption that if a mode isn't supported, then it reads as TRUE + // and doesn't change the boolean result + // Thus the logic is: + // READY_SIGNAL = READY_STATE_CTS && READY_STATE_FLOW + // Note that we also have to take inverted CTS into account + + // When using software flow control, only update on a state change + uInt8 flowCtrl = 0; + if(mySerialPort->readByte(flowCtrl)) + myReadyStateSoftFlow = flowCtrl == 0x11; // XON + + // Now combine the results of CTS and'ed with flow control + return setPin(pin, + (mySerialPort->isCTS() ^ myCTSFlip) && myReadyStateSoftFlow); + } default: return SaveKey::read(pin); diff --git a/src/emucore/AtariVox.hxx b/src/emucore/AtariVox.hxx index d868338ed..0768d66c9 100644 --- a/src/emucore/AtariVox.hxx +++ b/src/emucore/AtariVox.hxx @@ -32,7 +32,7 @@ class FilesystemNode; This code owes a great debt to Alex Herbert's AtariVox documentation and driver code. - @author B. Watson + @author B. Watson, Stephen Anthony */ class AtariVox : public SaveKey { @@ -91,7 +91,9 @@ class AtariVox : public SaveKey */ void reset() override; - string about(bool swappedPorts) const override { return Controller::about(swappedPorts) + myAboutString; } + string about(bool swappedPorts) const override { + return Controller::about(swappedPorts) + myAboutString; + } private: void clockDataIn(bool value); @@ -117,9 +119,11 @@ class AtariVox : public SaveKey // "close enough". uInt64 myLastDataWriteCycle{0}; - // Some USB-Serial adaptors either don't support CTS, or send the signal - // as inverted; we detect that when opening the port, and flip the signal - // when necessary + // When using software flow control, assume the device starts in READY mode + bool myReadyStateSoftFlow{true}; + + // Some USB-Serial adaptors send the CTS signal inverted; we detect + // that when opening the port, and flip the signal when necessary bool myCTSFlip{false}; // Holds information concerning serial port usage diff --git a/src/emucore/SerialPort.hxx b/src/emucore/SerialPort.hxx index f2ac5bf68..5525a51c7 100644 --- a/src/emucore/SerialPort.hxx +++ b/src/emucore/SerialPort.hxx @@ -43,7 +43,6 @@ class SerialPort /** Read a byte from the serial port. - NOTE: This is for potential future use; no class currently uses this. @param data Destination for the byte read from the port @return True if a byte was read, else false diff --git a/src/macos/SerialPortMACOS.cxx b/src/macos/SerialPortMACOS.cxx index 9745eceff..3a4d2530b 100644 --- a/src/macos/SerialPortMACOS.cxx +++ b/src/macos/SerialPortMACOS.cxx @@ -63,14 +63,21 @@ bool SerialPortMACOS::openPort(const string& device) return true; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool SerialPortMACOS::readByte(uInt8& data) +{ + if(myHandle) + return read(myHandle, &data, 1) == 1; + + return false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool SerialPortMACOS::writeByte(uInt8 data) { if(myHandle) - { -// cerr << "SerialPortMACOS::writeByte " << int(data) << endl; return write(myHandle, &data, 1) == 1; - } + return false; } diff --git a/src/macos/SerialPortMACOS.hxx b/src/macos/SerialPortMACOS.hxx index 12dbcf28e..3fd9595d3 100644 --- a/src/macos/SerialPortMACOS.hxx +++ b/src/macos/SerialPortMACOS.hxx @@ -40,6 +40,14 @@ class SerialPortMACOS : public SerialPort */ bool openPort(const string& device) override; + /** + Read a byte from the serial port. + + @param data Destination for the byte read from the port + @return True if a byte was read, else false + */ + bool readByte(uInt8& data) override; + /** Write a byte to the serial port. diff --git a/src/unix/SerialPortUNIX.cxx b/src/unix/SerialPortUNIX.cxx index 1105d2092..7c39b3fb9 100644 --- a/src/unix/SerialPortUNIX.cxx +++ b/src/unix/SerialPortUNIX.cxx @@ -49,7 +49,9 @@ bool SerialPortUNIX::openPort(const string& device) if(myHandle <= 0) return false; - // Open the device in nonblocking mode + // Clear buffers, then open the device in nonblocking mode + tcflush(myHandle, TCOFLUSH); + tcflush(myHandle, TCIFLUSH); fcntl(myHandle, F_SETFL, FNDELAY); struct termios termios; @@ -64,14 +66,21 @@ bool SerialPortUNIX::openPort(const string& device) return true; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool SerialPortUNIX::readByte(uInt8& data) +{ + if(myHandle) + return read(myHandle, &data, 1) == 1; + + return false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool SerialPortUNIX::writeByte(uInt8 data) { if(myHandle) - { -// cerr << "SerialPortUNIX::writeByte " << int(data) << endl; return write(myHandle, &data, 1) == 1; - } + return false; } diff --git a/src/unix/SerialPortUNIX.hxx b/src/unix/SerialPortUNIX.hxx index 4dec5a116..dbf5f3299 100644 --- a/src/unix/SerialPortUNIX.hxx +++ b/src/unix/SerialPortUNIX.hxx @@ -40,6 +40,14 @@ class SerialPortUNIX : public SerialPort */ bool openPort(const string& device) override; + /** + Read a byte from the serial port. + + @param data Destination for the byte read from the port + @return True if a byte was read, else false + */ + bool readByte(uInt8& data) override; + /** Write a byte to the serial port. diff --git a/src/windows/SerialPortWINDOWS.cxx b/src/windows/SerialPortWINDOWS.cxx index 91b6c0bb3..2772821d6 100644 --- a/src/windows/SerialPortWINDOWS.cxx +++ b/src/windows/SerialPortWINDOWS.cxx @@ -62,6 +62,14 @@ bool SerialPortWINDOWS::openPort(const string& device) dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; SetCommState(myHandle, &dcb); + + COMMTIMEOUTS commtimeouts; + commtimeouts.ReadIntervalTimeout = MAXDWORD; + commtimeouts.ReadTotalTimeoutMultiplier = 0; + commtimeouts.ReadTotalTimeoutConstant = 1; + commtimeouts.WriteTotalTimeoutMultiplier = 0; + commtimeouts.WriteTotalTimeoutConstant = 0; + SetCommTimeouts(myHandle, &commtimeouts); } else return false; @@ -69,13 +77,26 @@ bool SerialPortWINDOWS::openPort(const string& device) return true; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool SerialPortWINDOWS::readByte(uInt8& data) +{ + if(myHandle) + { + DWORD read; + ReadFile(myHandle, &data, 1, &read, NULL); + return read == 1; + } + return false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool SerialPortWINDOWS::writeByte(uInt8 data) { if(myHandle) { DWORD written; - return WriteFile(myHandle, &data, 1, &written, 0) == TRUE; + WriteFile(myHandle, &data, 1, &written, NULL); + return written == 1; } return false; } diff --git a/src/windows/SerialPortWINDOWS.hxx b/src/windows/SerialPortWINDOWS.hxx index adab57ce6..9a7aec2e6 100644 --- a/src/windows/SerialPortWINDOWS.hxx +++ b/src/windows/SerialPortWINDOWS.hxx @@ -39,6 +39,14 @@ class SerialPortWINDOWS : public SerialPort */ bool openPort(const string& device) override; + /** + Read a byte from the serial port. + + @param data Destination for the byte read from the port + @return True if a byte was read, else false + */ + bool readByte(uInt8& data) override; + /** Write a byte to the serial port.