LocoLink
LocoLink is a cable and software used to connect a PCW to a PC, so that documents can be transferred from the PCW (most likely with 3" drives) to the PC (with 5.25" or 3.5" drives).
Hardware
The physical cable has an edge connector at one end, to plug into the PCW's expansion port, and a DB-25 connector at the other to plug into the PC's parallel port. All you need to do is connect the computers together, run the transfer software and the files you require are pushed or pulled across.
At the PCW end, the LocoLink appears at I/O port 0FEh:
Bit | When read | When written |
---|---|---|
0 | Data 0 | BUSY |
1 | Data 1 | ACK |
At the PC end, the LocoLink appears as a parallel printer; the value written by the PC is latched when the STROBE line is raised and then lowered.
In practice, the values sent from the PCW to the PC are inverted: when the PCW writes 1, the value on the wire is 0 and vice versa. When the PC reads the BUSY signal, it is then inverted again by the PC's chipset. So what you see is something like this:
On the PCW | On the wire | On the PC | |||||
---|---|---|---|---|---|---|---|
PCW writes | Bit 1 | Bit 0 | BUSY | ~ACK | ~BUSY | ~ACK | PC reads |
0 | 0 | 0 | 1 | 1 | 0 | 1 | 040h |
1 | 0 | 1 | 0 | 1 | 1 | 1 | 0C0h |
2 | 1 | 0 | 1 | 0 | 0 | 0 | 000h |
3 | 1 | 1 | 0 | 0 | 1 | 0 | 080h |
LocoLink on other systems
Official LocoLink software only exists for the PC (under PCDOS and Win16) and the PCW. However, with suitable software any system that provides a Centronics port and allows the BUSY and ACK lines to be read would be able to act as the 'PC' end of a LocoLink conversation.
If, instead of the LocoLink cable, you connect two PCs together with a LapLink cable, then one of the PCs could act as the 'PCW' end of a LocoLink conversation. Because of the wiring of the LapLink cable, this would be done by writing to data bits 3 and 4 to set ACK and BUSY at the other end, and reading ERROR and SELECT IN to receive the values written by the other end.
Software
The supplied software had four major versions:
- LocoLink 1.x
Supplied with LocoScript PC version 1.x. In this version, a server (LLPC.EXE) is run on the PC, and a client (LLPCW.COM) on the PCW.
This version of the software was reviewed in the April 1991 issue of 8000 Plus, on page 12. As the review points out, the transfer is a one-way process; files can be sent from the PCW to the PC, but not brought back in the other direction.
As well as the LocoLink cable, this version also allows the transfer to be made using a serial port, if the PCW has one fitted.
- LocoLink 2.x
An add-on for LocoScript PC 1.5 and later versions. In this version, the server (LLINK202.EMS) runs on the PCW, and the client is integrated into LocoScript PC. The PCW appears as another drive within the Disc Management screen of the PC. Files can be copied both ways, or even edited 'live'.
Locomotive's Script newsletter describes this version here.
- LocoLink for Windows
A standalone file converter program for Windows 3.x. Again, the server runs on the PCW. This time the client is a Windows program that can copy files from the PCW to the PC, and optionally convert them from LocoScript format into wordprocessor formats better known on the PC, such as Word, WordPerfect, WordStar or Ami Pro. The PCW appears as one or more extra drives inside the file selector.
Once more, files could only be transferred from the PCW to the PC, not in the opposite direction.
- LocoLink 16
For the PcW16. The client is built into the PcW16's system software (Rosanne), and the server running on the PCW is the same program as used by LocoLink for Windows.
Within Rosanne, if an active LocoLink connection is detected, the "Import" file selector displays "LocoLink A/B" buttons allowing the user to read files from either drive on the PCW. There is no facility to export from the PcW16 to the other PCW, though at the operating system level there appear to be functions that would allow this.
Third-party software
Three Inch Software produced a program, PCW Link, which allowed the LocoLink cable to be used to connect between two PCWs provided that one of them was a 9512 or 9512+. The "PC" end of the cable would be plugged into the parallel port on the 9512/9512+, the "PCW" end into the other computer. A 9512 / 9512+ was required because the parallel port interfaces on other PCWs don't provide the necessary access to the ACK signal.
Unlike LocoLink proper, which shares individual files, this software shared drives at a sector level. The drives of the server PCW would appear to the client as K: and L:.
Since this sofware was not written by Locomotive and I don't have a copy to study, I don't know whether it used the same wire protocol as LocoLink proper.
A review and screenshots can be found in PCW Plus, issue 68.
When a LocoLink interface is connected to the PCW9512 parallel port, the following values are read from bits 7 and 6 of the parallel port status register:
PCW writes | 9512 reads |
---|---|
0 | 0C0h |
1 | 080h |
2 | 040h |
3 | 000h |
Emulating LocoLink
JOYCE can emulate the PCW end of a LocoLink connection. Instead of a LocoLink interface, it would use a LapLink cable to connect the parallel port of the PC running JOYCE to the parallel port of the PC running LocoScript PC / LocoLink for Windows.
Similarly, ANNE can emulate the "PC" end of a LocoLink connection, accessing a real LocoLink cable through the host PC's parallel port.
JOYCE can also emulate LocoLink without a physical cable, sending its data through a TCP/IP port. In theory this would allow a LocoLink connection to be emulated between instances of JOYCE and ANNE running on the same computer, but I don't have the necessary software to test this.
Wishlist
I'm looking out in particular for two pieces of software:
- PCW Link — published by Three Inch Software
- LocoLink 16 / LocoLink for Windows: The software for the PCW end
In addition, I would be interested to see versions of LocoLink for the PCW other than 1.03, 1.04 and 2.02.
Wire Protocol
It's simplest to consider the number written by either end of the link as a number, 0 to 3. When writing from PC to PCW, that's simple: the PC writes n to the parallel port, and the PCW reads n from port 0FEh. Going the other way, the PCW writes n but the value the PC reads has the two bits swapped and the low bit inverted:
PCW writes | PC reads |
---|---|
0 | 040h |
1 | 0C0h |
2 | 000h |
3 | 080h |
In this document, I'll use the values seen and written by the PCW throughout.
Link idle
When the link initially starts, the server sends 2 and the client sends 3. This corresponds to the server listening for the first packet, and the client being ready to transmit.
Sending a packet
In order to send a packet, the transmitting computer checks that the other end is sending the value 2. It may also be 3; if so, send 3 and wait for it to change to 2.
Then:
- Send 0.
- Wait until the value received goes from 2 to 3.
- Send 1.
- Send one byte (packet type)
- Send one byte (payload length, 0-255)
- Send the packet payload, if present.
- Send two bytes: the 16-bit CRC of the packet.
- Wait until the value received goes from 3 to 1.
- Send 3.
- Wait until the value received goes from 1 to 3.
- Send 2.
The procedure to send each byte is:
- Wait until the value received goes from 3 to 1.
- Send 2 or 3, depending whether bit 7 of the byte is 1 or 0.
- Wait until the value received goes from 1 to 3.
- Send 0 or 1, depending whether bit 6 of the byte is 1 or 0.
- Wait until the value received goes from 3 to 1.
- Send 2 or 3, depending whether bit 7 of the byte is 1 or 0.
- ... and so on, until ...
- Send 0 or 1, depending whether bit 0 of the byte is 1 or 0.
Receiving a packet
Receiving is obviously the same protocol, seen from the other end:
- Wait until the value received goes from 3 to 0.
- Send 3.
- Wait until the value received goes from 0 to 1.
- Send 1.
- Receive two bytes (packet type, payload length).
- Receive the payload bytes, based on the length.
- Receive the two-byte CRC.
- Wait until the value received goes from 0 or 1 (bit 0 of the last byte) to 3.
- Send 3.
- Wait until the value received goes from 3 to 2.
The procedure to receive each byte is:
- Wait until the value received becomes 2 or 3. Its low bit gives bit 7 of the incoming byte.
- Send 3.
- Wait until the value received becomes 0 or 1. Its low bit gives bit 6 of the incoming byte.
- Send 1.
- Wait until the value received becomes 2 or 3. Its low bit gives bit 5 of the incoming byte.
- Send 3.
- ... and so on, until ...
- Wait until the value received becomes 0 or 1. Its low bit gives bit 0 of the incoming byte.
- Send 1.
Serial Port
When LocoLink 1 is transmitting over a serial port, it transmits packets in the same form (type, payload length, payload, checksum) at 9600 baud with one stop bit.
Packet types
The first packet is sent by the client, and identifies which version of the protocol they are using. If the server recognises the packet, it will send a suitable response and the link is established.
- LocoLink 1.x
-
Sender Type Payload length Payload Client 07h 02h 43h 31h Server 55h 01h 07h - LocoLink 2.x
-
Sender Type Payload length Payload Client 0Bh 02h-20h 41h 31h Client program name (optional) Server 0C3h 01h-1Fh 0Bh Server program name (optional) - LocoLink for Windows / LocoLink 16
-
Sender Type Payload length Payload Client 5Ah 02h-20h 46h 31h Client program name (optional) Server 18h 01h-1Fh 5Ah Server program name (optional) - 14h
- Hangup: The client disconnects from the server.
- 21h
- Check space for file. Packet contains a 32-bit little-endian file length, followed by ASCIIZ pathname.
- 2Eh
- Create file. Packet contains a 32-bit little-endian file length, followed by ASCIIZ pathname.
- 3Bh
- Append bytes to the currently open file. Packet contains the bytes to append.
- 48h
- Close currently open file. Packet contains the CRC16 of the file as a little-endian word.
- 6Fh
- Create a directory. Packet contains ASCIIZ path name.
- 7Ch
- Check if file / directory exists. First byte of packet payload is the search type (0 for directory, 1 for file) and the remainder is the ASCIIZ pathname.
- 55h
- Result. Payload is 2 bytes. The first is the type of the packet being replied to, and the second is an error flag (0 for success, other value for failure).
- 62h
- Unknown packet type received. No payload.
- 22h
- Hangup: The client disconnects from the server.
- 39h
- Make a DOS call. The payload is 1-15 bytes:
Byte Meaning 0 Function (AH) 1 Subfunction (AL) 2-3 BX 4-5 CX 6-7 DX In theory, longer versions of this packet containing SI, DI and BP could also be transmitted. Note that the packet may be shorter than you'd expect. For example, MS-DOS function 0Eh takes a drive number in DL. But since LocoLink 2.x only supports one drive, it does not need the drive number and so the transmitted packet only contains one byte of payload, the function number.
- 95h
- Requested data. Sent by the client following receipt of a type 7Eh packet (see below).
- 0C3h
- Acknowledge data result or echo request.
- 50h
- Success. Payload length is 0-8 bytes, giving the register values of AX, BX, CX, DX as little-endian words.
- 67h
- Error. Payload length is a little-endian word, giving the DOS error number (cf INT 21h function 59h)
- 7Eh
Request data. Used where the corresponding DOS function passes data using a memory buffer. For example, when calling DOS function 3Ch (create file) you pass DX as the pointer to the filename. In LocoLink, the server sends a packet requesting the ASCIIZ data at DX, which the client then provides.
The packet has two bytes payload: the first gives the data source, the second the maximum length. The data source IDs are:
- 11h
- Fixed length, buffer at DX
- 36h
- Fixed length, buffer at DTA
- 5Bh
- Fixed length, buffer at SI
- 80h
- ASCIIZ, buffer at DX
- 0A5h
- ASCIIZ, buffer at SI
- 0CAh
- ASCIIZ, buffer at DI
- 0EFh
- Fixed length, buffer follows last data copied
The last option is used when multiple transfers are necessary for the requested amount of data. For example, attempting to write 300 bytes to a file will cause two requests to be sent: the first of type 11h (data at DX), the second of type 0EFh (continue following last address).
- 0ACh
Return data to the client. The first byte of the payload is the location code (as above, but only codes 11h, 36h, 5Bh and 0EFh are supported) and the remainder of the packet contains the data to return. The client will acknowledge with a packet of type 0C3h containing a payload of 0ACh.
- 0DAh
- Echo request. Payload contains a single byte. The client will acknowledge with a packet of type 0C3h containing two bytes: the first is 0DAh, the second is the byte in the request.
- The LocoLink 2.x server assumes all pathnames are absolute and in upper case.
- For some reason, function 4Fh does not do a separate transfer to return its results, unlike function 4Eh. Instead, the final result packet contains one word (AX) followed by bytes 0x14 onward of the find file data.
- In general, this protocol is vulnerable to Heartbleed-style attacks on the client by a buggy or malicious server. The server can respond to any call with packets of type 7Eh (request data) or 0ACh (return data), causing arbitrary ranges of the client's memory to be uploaded or overwritten.
- 18h
- Acknowledge data result or echo request.
- 6Dh
- Hangup: The client disconnects from the server.
- 80h
- Make a DOS call. The payload is 1-17 bytes:
Byte Meaning 0 Function (AH) 1 Subfunction (AL) 2-3 BX 4-5 CX 6-7 DX In theory, longer versions of this packet containing SI, DI and BP could also be transmitted. As with LocoLink 2.x, the packet may be shorter than you'd expect since irrelevant registers are not included.
The packet for function 4Fh (find next file) comes in two forms: a 'short' one with a single byte of payload:
Byte Meaning 0 Function (AH) ... and a 'long' one with 17 bytes:
Byte Meaning 0 Function (AH) 1-16 Bytes 0x06-0x15 of the result from FindFirst This is because LocoLink for Windows performs depth-first searches of subdirectories. When it encounters a subdirectory, it begins a new FindFirst operation to list the files in that subdirectory; once the files have been enumerated, it then uses the 'long' version of the FindNext call to resume its search of the parent directory at the point it left off. Thus the server must be prepared to handle multiple concurrent searches, and store enough state in bytes 0x06-0x11 of the FindFirst result structure to identify which one is being requested.
- 0CCh
- Requested data. Sent by the client following receipt of a type 0B9h packet (see below).
Once the connection is established, the packet types sent depend on which version of the protocol is in use.
LocoLink 1.x
The client may send the following packet types to the server:
The server may send the following packet types to the client:
LocoLink 2.x
The client may send the following packet types to the server:
The server may send the following packet types to the client:
DOS function calls supported by LocoLink PCW 2.x are:
Function number | Meaning | Parameters | Additional transfers | Results if successful |
---|---|---|---|---|
0Dh | Log out drives | none | none | none |
0Eh | Select drive | none | none | AL=1 (one drive) |
10h | Close disc label | none | none | none |
11h | Get disc label | none | Server returns disc label as a DOS extended FCB | none |
13h | Delete disc label | none | none | none |
16h | Create disc label | none | Client sends disc label as a DOS extended FCB | none |
17h | Rename disc label | none | Client sends a DOS extended FCB containing old and new names | none |
36h | Get free space | none | none | AX = sectors per cluster BX = free clusters CX = bytes per sector DX = total clusters |
39h | Create directory | none | Client sends ASCIIZ pathname | Always fails (CP/M can't create more user areas) |
3Ah | Remove directory | none | Client sends ASCIIZ pathname | Always fails (CP/M can't remove user areas) |
3Bh | Set current directory | none | Client sends ASCIIZ pathname | none |
3Ch | Create file | CX=attributes | Client sends ASCIIZ pathname | AX=handle |
3Dh | Open file | AL=mode | Client sends ASCIIZ pathname | AX=handle |
3Eh | Close file | BX=handle | none | none |
3Fh | Read file | BX=handle, CX=count | Server sends data read | AX=count of bytes read |
40h | Write file | BX=handle, CX=count | Client sends data to write | AX=count of bytes written |
41h | Erase file | none | Client sends ASCIIZ pathname | none |
42h | Set file pointer | AL=whence, BX=handle, CXDX=offset | none | DXAX = new file position |
43h | Get file attributes | AL=0 | Client sends ASCIIZ pathname | CX=attributes |
43h | Set file attributes | AL=1, CX=attributes | Client sends ASCIIZ pathname | none |
44h | Get device information | AL=0, BX=0, CX=0 | None | DX=device flags |
47h | Get current directory | none | Server returns current directory | none |
4Eh | Find first file | AL=search attribute | Client sends search path. If successful server returns find file data. | none |
4Fh | Find next file | none | none | If successful, find file data. |
56h | Rename file | none | Client sends old, then new pathname | none |
57h | Get timestamp | AL=0, BX=file handle | none | CX=time, DX=date |
57h | Set timestamp | AL=0, BX=file handle, CX=time, DX=date | none | none |
5Bh | Create file (if it doesn't exist) | CX=attributes | Client sends ASCIIZ pathname | AX=handle |
Notes
LocoLink for Windows / LocoLink 16
The client may send the following packet types to the server:
The server may send the following packet types to the client:
- 2Bh
- Echo request. Payload contains a single byte. The client will acknowledge with a packet of type 18h containing two bytes: the first is 2Bh, the second is the byte in the request.
- 93h
- Success. Payload length is 0-8 bytes, giving the register values of AX, BX, CX, DX as little-endian words.
- 0A6h
- Error. Payload length is a little-endian word, giving the DOS error number (cf INT 21h function 59h)
- 0B9h
Request data.
The packet has two bytes payload: the first gives the data source, the second the maximum length. The data sources used are the same as in LocoLink v2.x:
- 11h
- Fixed length, buffer at DX
- 36h
- Fixed length, buffer at DTA
- 5Bh
- Fixed length, buffer at SI
- 80h
- ASCIIZ, buffer at DX
- 0A5h
- ASCIIZ, buffer at SI
- 0CAh
- ASCIIZ, buffer at DI
- 0EFh
- Fixed length, buffer follows last data copied
- 0DFh
Return data to the client. The first byte of the payload is the location code (as above, but only codes 11h, 36h, 5Bh and 0EFh are supported) and the remainder of the packet contains the data to return. The client will acknowledge with a packet of type 018h containing a payload of 0EFh.
DOS function calls made by LocoLink for Windows / LocoLink 16 are:
Function number | Meaning | Parameters | Additional transfers | Results if successful |
---|---|---|---|---|
0Dh | Reset drive | AL = ? DL=drive | none | |
0Eh | Select drive | DL=drive | none | AL=drive count |
3Ch | Create file | CX=attributes | Client sends ASCIIZ pathname | AX=handle |
3Dh | Open file | AL=mode | Client sends ASCIIZ pathname | AX=handle |
3Eh | Close file | BX=handle | none | none |
3Fh | Read file | BX=handle, CX=count | Server sends data read | AX=count of bytes read |
40h | Write file | BX=handle, CX=count | Client sends data to write | AX=count of bytes written |
4Eh | Find first file | AL=search attribute | Client sends search path. If successful server returns find file data. | none |
4Fh | Find next file | none | none | Find file data (bytes 0x11 onward) |
Bytes 0x06-0x15 of last find file data |
John Elliott 2023-04-10