All this messing around with interrupts over the last couple weeks was just preparation. The real goal was to get the Project:65 computer to use hardware handshaking on its serial port. Surprisingly, that’s turned out to be a piece of cake.
The idea of handshaking is that the computer at one end of a serial connection can tell the other when it’s okay to send data, and when it should wait for a while. Now in my particular use case, the computer on one end of the connection is an 8-bit 6502 running at 4 MHz, and the computer at the other end is a quad core Ivy Bridge I7 running at 3.4 GHz, so I’m really only worried about the data going in one direction. Even accounting for Windows, The I7 ought to be able to keep up with anything the P:65 computer sends out.
I’m implementing hardware handshaking, which means that the handshaking signals are sent out-of-band on two additional lines of the serial cable. Despite this, there’s a lot of software involved in making it work.
The MAX3100 supports a pretty stripped-down version of hardware handshaking that only requires two signals. There’s an output signal called RTS (Ready To Send). When RTS is low, it means the computer is ready to have data sent to it. There’s also an input signal called CTS (Clear To Send). When CTS is low, that means we can send data to the remote computer. Each RTS output is connected to the CTS input on the other side of the connection:
RTS >-----------> CTS TRANSMIT >-----------> RECEIVE RECEIVE <-----------< TRANSMIT CTS <-----------< RTS
The P:65 needs to activate and deactivate its RTS output in order to signal to the PC when it’s okay to send data. So the question is, when is it okay for the PC to send data?
When the P:65 computer sees a receive interrupt from the MAX3100, its interrupt service routine calls the Max3100_SendRecv subroutine. If it reads a character, it inserts that into the read buffer, which is a 256-byte circular buffer. Data gets removed from the read buffer when the program running on the P:65 calls the GetChar routine. The interrupt service routine is plenty fast enough to handle data coming in at 9600 bps, but if we push bytes into the read buffer faster than the program reads them out, the buffer can fill up, and then bad things happen.
The strategy is that we should keep RTS low when there’s room in the read buffer. When it fills up, we can set RTS high so that the other computer will stop sending data to us for a while.
To make this work smoothly, I’m using a “high-water mark” system. When the number of characters in the buffer hits a critical level – the “high-water mark” – RTS is set high. It may take the sending computer a short time to act on the change in the RTS value, so the high-water mark needs to be at least several bytes less than the size of the buffer.
Once RTS is high, the remote computer should stop sending bytes to us, and sooner or later the program running on the P:65 should read some more characters out of the read buffer. RTS will stay high until the number of characters in the buffer falls to the “low-water mark”, and then RTS can be set low to allow the remote computer to send data again.
Using separate high and low water marks keeps the RTS signal from being toggled on and off every time a character is read or processed.
On the MAX3100, the RTS flag can be changed any time you send it a Write Data command, so my code just includes the desired state of RTS in every call to the Max3100_SendRecv subroutine. The interrupt service routine checks the size of the read buffer against the high water mark, and updates the RTS flag if necessary.
Re-enabling RTS can’t be done in the interrupt service routine, because once RTS is disabled we shouldn’t be receiving read interrupts any more, and we may not be transmitting anything at all. In order to enable RTS as soon as possible, the low-water-mark is checked in GetChar, the “top half” subroutine that a program calls to extract a byte from the read buffer. If RTS needs to be re-enabled, GetChar calls Max3100_SendRecv to send the new RTS value to the 3100.
In the image at the top of the post, you can see the P:65 setting its RTS signal to high on line 3. On line 1 you can see that the remote computer stops sending bytes in response. You can also see that it doesn’t respond right away. It finishes the character that it’s sending when RTS goes high, of course, but then it also sends one more character before it stops. This response time seems to be pretty consistent, at least for TeraTerm running on this PC.