Here is what I have learned / figured out so far, and if anyone can correct items I may have wrong or can fill in more details on any of this, please feel free to do so.
I have figured out the DCD / DTR issues and it was partly my own misunderstanding of the way Vice works and partly a bug in tcpser (at least in how it operates under Linux). As it turns out, like many things, it's actually pretty simple when you know how it's done. On real hardware, DS2's swiftlink drivers rely on the modem status lines (or pins) to inform it when a connection has been established and when the other side of the connection has disconnected (IE: Hung up.) For this, it uses the DCD line, or Carrier Detect. Since we are not using physical hardware in this case, both the C64 and the modem are pieces of software, and the telephone line is the packet-switch Internet which, of course, has no Carrier Detect lines.
To overcome this architectural different, enter ip232. This protocol "listens" to the bytes that are being sent through the emulated phone line from one modem to another, and any time one modem needs to let the other modem know that one of it's signal lines has changed, it sends an 0xff byte (255 in decimal) followed by another byte indicating the new status flags. If the caller meant to send an actual 0xff byte, the ip232 protocol simply sends 0xff as the status byte and this is interpreted on the receiving end as a literal 0xff so that the in-band data does not get corrupt. Interestingly, if tcpser is not handling these status bytes correctly, they will show up as garbage in the stream, usually the little checkerboard character. Watch for this the next time you connect to a BBS - if you see a little checkerboard character when you first connect, that is Vice trying to tell tcpser that its DTR pin has gone high, (Terminal Ready) and tcpser has missed this byte and passed it along inband to the other end (hence the bug in tcpser).
The part of all this that I did not understand was that in order for Vice to engage its ip232 protocol, it needs to be configured for RS232 Network mode. Every version of Vice I've ever used and most of the tutorials I've seen on the matter show you configuring the rs232 device as "|nc hostname port" - What this does is it pipes the outgoing data through the Swiss Army knife tool Netcat (nc) to host
hostname on port
port. This works, but when running like this, Vice (at least the version I tested, 3.6.1) does not include any of the ip232 data - even if you've checked the box saying that this device uses that protocol. Instead, what you have to do is leave netcat out of it and simply put in the ip address or hostname, a colon and the port number as the "device", like this:
localhost:25232 Now, Vice uses an entirely different set of code internally for communicating with the device, and it does include the ip232 signalling bytes.
On the other end, in tcpser, the problem there comes from this chunk of code from ip232.c: (I've taken out the parts not strictly relevant to the problem)
Code: Select all
char buf[256];
int ch;
if (cfg->dce_data.ip232_is/_connected) {
res = recv(cfg->dce_data.fd, buf, len, 0);
while (i < res) {
ch = buf[i];
...
if (255 == ch) {
cfg->dce_data.ip232_iac = TRUE;
}
else {
data[text_len++] = ch;
}
i++;
}
}
Here we see the incoming data buffer is declared as an array of 256 char's, (which are signed), and when evaluating each byte, a variable ch is declared as an int (also signed.) The problem is that we are trying to compare a numeric constant, 255, to a signed byte. In an unsigned 8-bit byte, if all bits are zero you have a value of 0, and if all 8 bits are 1s, you have a value of 255 giving the range of 0-255 in decimal, or 0x00 to 0xff in hex. However, when you are using one of those bits for a sign bit, when all bits are zero you still have a value of zero, but you only have seven bits available for the value now, so at most you can have the least most significant bits as 1s and a value of 127. From there, if you twiddle the MSB, that value becomes -127, so the effective range of a signed byte is -127 to 127. Therefore, it is impossible for the "if (255 == ch)" expression to ever evaluate as true since the signed byte can never actually equal 255. Now, this would be different if recv() captured 16 bits per byte or higher, because then you would have enough bits to represent 255 in a signed integer. In every instance I've ever worked with though, recv() deals in 8-bit bytes of data.
To fix this is easy enough, we simply declare buf to be an array of uint8_t bytes (unsigned 8-bit integers) and the same for the comparing variable ch. I prefer this method because it removes any ambiguity between platforms as to the size of the type; on some systems, char might be 8 bits long but on others, it could be 16 or even 32. On all platforms, a uint8_t will be an 8-bit unsigned integer, so there is no guesswork and the code behaves identically wherever it is used.
The only other thing we need to do to avoid a compiler warning is to cast buf back to char * for the log function. For good measure, I also like to write the comparison values in hex (so 0xff instead of 255) but this is a personal preference. Typically when looking at any raw dumps of data you are going to see the bytes written in hex, and it is more compact and consistent - in my opinion.
That's all for now, time to read some Lord of the Rings to the kids.
