In this document I've tried to document in detail the way the Psion protocol works on the comms link of the Psion II Organiser. The information given here is the result of reverse engineering, so may be incomplete and not entirely correct, so if you find any thing that needs to be changed or added, let me know. All numbers are in hexadecimal unless stated otherwise.
In its simplest form data is sent over the link using the 'link layer', an error correcting protocol which uses data packets. Each packet has the following format (in hex): 16 10 02 CHAN TYPE DATA 10 03 CRC.
The first three bytes are always 16 10 02.
The next byte, CHAN, is the channel number (?). It must always equal 01. It is compared to the value stored at $2173, which is set to 01 when the comms link is loaded and not changed afterwards. It may be that some other communications devices use a different channel number.
The next byte, TYPE, contains the type of packet in the top 5 bits, and also a sequence number in the bottom 3 bits. The sequence number is a number that each data packet has to make sure that they are not mixed up. Since the sequence number increases with each new data packet, missing packets can be detected.
There are 4 types of packet:
type=0: | ACKNOWLEDGE. Signals proper reception of a data packet. The sequence number of the acknowledge is the same as that of the data packet received. If a packet is received with the wrong sequence number, an acknowledge is sent of the last correct data packet, so this is also used as a request for the next packet. Contains no data. |
type=1: | DISCONNECT. Signal that communication is to end. When sent to the Psion, the data should contain the error number. If the Psion sends one then it has no data. The sequence number is 0. |
type=2: | LINK REQUEST. Requests a connection. Used at the start of a session to test two-way communication. Sequence number is 0 and it contains no data. |
type=3: | DATA. A data packet. The sequence number is one more than that of the previous data packet (modulo 8) or 1 if it is the first data packet of the session. |
Since there are only 4 types, only bits 3 and 4 of the TYPE byte are used to store the packet type and bits 0, 1 and 2 will always be zero.
The bytes 10 03 follow the data to signal the end of the packet.
The CRC is a 16-bit checksum for all the bytes between the header 16 10 02 and the footer 10 03. See below.
To make sure that sending the bytes 10 03 in the data will not prematurely end the packet, an escape mechanism is used. Every 10 byte in the data (or chan/type bytes) will be sent twice over the link. The receiving end then replaces each 10 10 by a single 10, and will not end the packet if it is followed by a 03. The extra 10 is not used in generating the CRC however.
The maximum length of the data is 0100h bytes. Repeated 10 bytes are not included in this count.
The 16-bit CRC checksum used by the Psion protocol is the CCITT checksum
(GenCRC) with generating polynomial x^16+x^12+x^5+1 (i.e. 10241h). It can
be computed by knowing the following values:
CRC(01)=C1C0
CRC(02)=81C1
CRC(04)=01C3
CRC(08)=01C6
CRC(10)=01CC
CRC(20)=01D8
CRC(40)=01F0
CRC(80)=01A0
The CRC of any single byte can be computed by EOR-ing (exclusive OR) the values above together for each set bit in the byte. It is probably faster to pre-calculate an array for all byte values first. To get the CRC of a sequence of bytes, it can be done iteratively, by starting with the CRC of the first byte, and adjusting it each time the next byte is included. If xxyy is the CRC for a sequence of one or more bytes, then the CRC of that sequence followed by a byte bb will be CRC(bb eor xx) eor (yy00).
For example, let's calculate CRC(01 10):
CRC(01) | = C1C0 |
CRC(01 10) | = CRC(10 eor C1) eor C000 |
= CRC(D1) eor C000 | |
= CRC(80) eor CRC(40) eor CRC(10) eor CRC(01) eor C000 | |
= 01A0 eor 01F0 eor 01CC eor C1C0 eor C000 | |
= 005C |
When CL is running on the PC, it continuously sends out Link Request packets. When the Psion is ready to start communicating it first starts up a link session in the following manner:
After that the link is considered to be active and working, and the sequence numbers are reset to 1.
Specifically, the packets involved are:
Receive Link Request: | 16 10 02 01 10 10 10 03 00 5C |
Send Link Request: | 16 10 02 01 10 10 10 03 00 5C |
Recieve Acknowledgement: | 16 10 02 01 00 10 03 01 90 |
If in step 3 a disconnect request is received, it should contain an error byte as data. Receiving a data packet will cause the Psion to return a data acknowledgement, and then it also considers the link to be active.
The start-up sequence above is performed before every data transfer. It can also be called specifically by using the XLCON: command, or calling rs$licon in machine code.
When sending a sequence of data packets, each packet is numbered 1,2,..,6,7,0,1,... etc. After each packet is sent, a reply is expected. What happens next depends on the type of reply:
A single packet can be sent using the XLPUT:(a$) command, or rs$liput in machine code.
When receiving a sequence of data packets, each packet should numbered 1,2,..,6,7,0,1,... etc. After each packet is received, a reply is expected. When the Psion is waiting for a data packet, what happens depends on the type of packet it receives:
A single packet can be received using the XLGET$: command, or rs$liput in machine code.
Note that the sequence numbers used when receiving packets are independent from those used when sending packets.
When the link session is finished, the link should be deactivated. This is done by calling the XLDIS: command, or rs$lidis in machine code. This will send a disconnect packet, and close the link session. The disconnect packet has no data when sent from the Psion, but those sent by CL will contain a single byte which is the error number.
The link layer is simply an error correcting protocol for sending small snippets of data. Built on top of this is a protocol for sending and receiving files, file access commands etc.
The Psion Series 3 and 5 first have an NCP protocol layer above the link layer. NCP is a method of sending data over different channels. This is not implemented on the series II, though there seems to be a simple channel byte in every packet which is not used at all.
The file protocol layer generally works like this:
If CL encounters an error, it sends a disconnect packet containing the
error number to be raised. The new error numbers are:
190 Bad parameter
189 Remote file not found
188 Server error
187 Remote file exists
186 Disk full
185 Record too long
It now remains to be explained what overlays there are, what commands they accept and what data they return.
This overlay handles all remote file access (i.e. XFOPEN, XFCLOSE etc). There are five types of data packet that the Psion sends to CL/FILE. The data packet starts with a byte between 00 and 04 to indicate its type:
XFOPEN: | 00 mode type name | Opens a remote file where mode/type are bytes and name is a string, exactly the same parameters as the XFOPEN command. |
XFCLOSE: | 01 | Closes current remote file if any. |
XFPUT: | 02 datastring | Puts the data at the current file position. |
XFGET$: | 03 length | Gets (at most) length bytes from the current file position which are returned in the reply data packet from CL/FILE. |
XFPOS: | 04 mode position | Changes the current file position. Mode is a byte but position is a string representation of the file position. The reply data packet contains a string representation of the new file position. |
The file types used in XFOPEN are:
00 | Binary | A random access binary file. |
01 | Ascii | An sequential ascii file. Any data sent by XFPUT will be appended to the file, and automatically terminated by a CR/LF linebreak even though this is not sent. |
02 | Directory | A directory file. This is a read-only sequential ascii file, containing all the file names matching the dos pathname given (which may contain wild cards * and +). |
The file modes used in XFOPEN are:
00 | Read only | An existing file is opened for reading. An error 189 (remote file not found) is sent if it does not exist. |
01 | Create/Replace | Any existing file of the same name is removed, and a new file is opened for writing. |
02 | Replace | An existing file is opened and cleared for writing. An error 189 (remote file not found) is sent if it does not exist. |
03 | Create | An new file is opened for writing. An error 187 (remote file exists) is sent if it already exists. |
04 | Update | An existing file is opened for writing (appending if ascii), but not cleared. An 189 (remote file not found) if it does not exist. |
Note that the initial file position is always at the beginning of the file, except for sequential ascii files in Update mode which are opened at the end of the file so that any new data is appended.
Using an illegal filemode or filetype will return an error 190 (bad parameter). Using XFPUT in read-only mode (00) will return an error 188 (server error). Using XFPOS on a non-binary file will return an error. Opening a directory file in any mode other than read-only will return an error.
For example, suppose we run the following short program:
TEST: XFOPEN:("HOMER.TXT",1,1) XFPUT$:("Doh"+CHR$(33)) XFCLOSE:
then the following list is the complete exchange between the Psion and the PC, assuming no errors occur and no packets are re-sent:
Start link layer session: | |||
PC: | Link Request: | 16 10 02 01 10(10)10 03 00 5C | |
Ps: | Link Request: | 16 10 02 01 10(10)10 03 00 5C | |
PC: | Acknowledge: | 16 10 02 01 00 10 03 01 90 | |
Then the FILE overlay is loaded: | |||
Ps: | FILE overlay: | 16 10 02 01 19 46 49 4C 45 10 03 2D BE F I L E | |
PC: | Acknowledge: | 16 10 02 01 01 10 03 C0 50 | |
Reply: | |||
PC: | No Data: | 16 10 02 01 19 10 03 C0 5A | |
Ps: | Acknowledge: | 16 10 02 01 01 10 03 C0 50 | |
File is opened: | |||
Ps: | Open command: | 16 10 02 01 1A 00 01 01 48 4F 4D 45 52 2E 54 58 54 10 03 39 8B H O M E R . T X T | |
PC: | Acknowledge: | 16 10 02 01 02 10 03 80 51 | |
Reply: | |||
PC: | No Data: | 16 10 02 01 1A 10 03 80 5B | |
Ps: | Acknowledge: | 16 10 02 01 02 10 03 80 51 | |
Send data: | |||
Ps: | Data Doh!: | 16 10 02 01 1B 02 44 6F 68 21 10 03 A1 DE D o h ! | |
PC: | Acknowledge: | 16 10 02 01 03 10 03 41 91 | |
Reply: | |||
PC: | No Data: | 16 10 02 01 1B 10 03 41 9B | |
Ps: | Acknowledge: | 16 10 02 01 03 10 03 41 91 | |
Close file: | |||
Ps: | End packet: | 16 10 02 01 1C 01 10 03 98 C0 | |
PC: | Acknowledge: | 16 10 02 01 04 10 03 00 53 | |
Reply: | |||
PC: | No Data: | 16 10 02 01 1C 10 03 00 59 | |
Ps: | Acknowledge: | 16 10 02 01 04 10 03 00 53 | |
End link layer session: | |||
Ps: | disconnect: | 16 10 02 01 08 10 03 00 56 |
The file HOMER.TXT will contain only the text "Doh!" followed by the CR/LF line break.
This is the most commonly used one. It handles all standard file transfer. There are four types of data packet that the Psion sends to FTRAN. The data packet starts with a byte between 00 and 03 to indicate its type:
Open: | 00 mode filetype filename | Tells FTRAN to open a remote file. Note that the mode is similar to those used by the FILE overlay. The filetype is 00 for ODB files, 01 for OPL files, 02-0F for block files. |
Close: | 01 | Closes the current file (if any) and exits the FTRAN overlay. |
putdata: | 02 datastring | Passes data to FTRAN for saving on disk. |
getdata: | 03 length | Gets (at most) length bytes from the current file position which are returned in the reply data packet from CL/FILE. The length requested is usually FE. |
The file modes are the same as with the FILE overlay, except that modes 02 and 03 are not allowed:
00 | Read only | An existing file is opened for reading. An error 189 is sent if it does not exist. |
01 | Create/Replace | Any existing file of the same name is removed, and a new file is opened for writing. |
04 | Update | An existing file is opened for writing (appending if ODB/OPL), but not cleared. An 189 error is sent if it does not exist. Note that this file mode is generally only used to test whether a file exists, not for writing/appending to a file. |
Using an illegal filemode (incl. 02 or 03) or filetype will return an error 190 (bad parameter). Sending data in reading mode (00), or requesting data in write mode (01-04) will return an 190 (Bad Parameter) error.
When a file is opened for reading (mode 00) then FTRAN often returns info about the file so that the Psion can prepare memory space for it.
When sending/receiving ODB files, each record is sent in a seperate packet. No info is returned after an ODB file is opened for reading.
When sending/receiving block files, the data is split into seperate packets, generally about FE bytes long. The info returned when opening the file for reading is the length of the block in the file, followed by the Psion file type (between 82 and 8F).
When sending/receiving OPL files, the data is split into seperate packets
just like block files. The line ends are indicated by a zero byte, and it
is FTRAN that translates them to/from CR/LF when writing to disk.
The info returned after the recv command is as follows:
blocklength, 81h, 0000, OPLlength
OPLlength is the total length of the OPL in bytes when all CR/LF linebreaks
are changed to a zero byte. Note that the final line of OPL must also end in
00 even if the ascii file does not end in a CR/LF.
The blocklength always equals 4+OPLlength.
When the procedure file is constructed on a pack, the length is blocklength
given above. The organiser then writes 0000, OPLlength. This signifies that
the length of the object code block is 0, and that the OPL part has length
OPLlength. If these lengths are incorrect, the pack will be corrupted.
This overlay handles the loading of BOOT code, and the XMLOAD: command. After the BOOT overlay is loaded, it expects to be sent a data packet containing the name of the boot file to load. The reply packet contains the length of the code.
The Psion then sends a packet containing the address to load the code. In reply the first data packet contains the first part of the code, ready to be stored in memory. When the Psion next sends a packet containing just a zero byte, the next code packet is sent. This is repeated as often as necessary.
When the end of the code has been reached, a disconnect packet with a zero error is returned by CL/BOOT.
Note that fixing up the relocatable addresses in the code is done by CL/BOOT.
Instead of using the BOOT option in the menu, the XMLOAD: command can be used. XMLOAD:(addr%,len%,name$) loads the boot code from remote file name$ to address addr%, but rejects it if the code exceeds length len%.
This overlay exits the CL program completely (by stuffing q and y in the keyboard buffer of the PC). It has no other functions. It can therefore be used by using the following sequence of commands:
XLCON: XLPUT:("EXIT") XLDIS:
These overlays are used by the spreadsheet program. When you choose the FILE/IMPORT or FILE/EXPORT menu options, the Psion requests the PLANM overlay which returns the file type menu, and the Psion disconnects again. The file type menu returned is 19h bytes long and is as follows:
03 "WKS" 0001 03 "WK1" 0002 03 "WR1" 0003 03 "DIF" 0004 00
It is in the correct format for the MN$DISP (SWI 50h) system service.
The PLAN overlay handles the transfer and conversion of the spreadsheet files. To export a file, the Psion first sends it a packet of which the first byte is between 81h and 84h depending on the file type chosen, and the rest contains the file name. The spreadsheet data follows; first a packet containing the dimensions of the sheet, then a packet for each non-empty cell, and finally a packet containing just FE. The reply that PLAN gives each time is a packet containing 00, except that the FE is answered with FE 00.
A cell is stored in this format:
Column: | Byte containing column of cell (0 is column A) |
Row: | Byte containing row of cell (0 is row 1) |
Type: | Contains 22h (=") if cell contains text Contains 87h if cell contains a number or formula |
Contents: | Ascii string with contents of cell. |
If a cell contains a formula and its result then one packet contains the formula, and the cell is resent in the next packet but this time containing just the value. For example, here are the packets sent to PLAN to export a simple sheet (only the packet data is shown):
84 54 45 53 54 | Export TEST in DIF format. |
02 04 | Columns A-B, Rows 1-4 used. |
01 00 22 43 4F 4C 31 | B1, contains "COL1" |
00 01 22 52 4F 57 31 | A2, contains "ROW1" |
01 01 87 31 | B2, contains 1 |
00 02 22 52 4F 57 32 | A3, contains "ROW2" |
01 02 87 35 | B3, contains 5 |
01 03 87 3D 42 32 2B 42 33 | B4, contains =B2+B3 (formula) |
01 03 87 36 | B4, contains 6 (value) |
FE | End of sheet |
Import is very similar. First the Psion sends CL/PLAN a packet of which the first byte is between 01 and 04 depending on the file type chosen, and the rest contains the file name. It just returns 00. Then the Psion sends a 00 and PLAN returns the first non-empty cell, in the same format as before. This is repeated until PLAN sends FE00, signifying the end of the sheet.