Serial Tube Protocol ==================== J.G.Harston - mdfs.net/Tube/Serial Serial Tube is a method of using the Tube protocol to communicate over a single communication channel, such as a serial port, a parallel port, or even a single Tube register. The Tube protocol has four transfer phases: Character Output, Command/ Response, Transfer Initialise, Transfer Data. These are usually sent via four seperate communication channels, such as the four Tube registers. Sending them via a single channel requires a method of combining and seperating all four. This is done by making the communication channel a simple text channel - as with a simple text terminal - and escaping out from the outward character stream to start a command request, and escaping out from the inward character stream to perform the data transfer actions. As with any client system, any functionality is entirely and utterly what the *Host* system provides. The client *must* *not* impose its own expectations of what functionality the host has. Any function that the Host does not support the Host must ignore and just return with registers unchanged. Client-to-Host transfers ------------------------ Sent data Returned data OSWRCH char or esc,esc OSRDCH char OSRDCHIO esc &00 Cy A OSCLI esc &02 string &0D &7F or &80 OSBYTELO esc &04 X A X OSBYTEHI esc &06 X Y A Cy Y X OSWORD esc &08 A in_length block out_length block OSWORD0 esc &0A block &FF or &7F string &0D OSARGS esc &0C Y block A A block OSBGET esc &0E Y Cy A OSBPUT esc &10 Y A &7F OSFIND esc &12 &00 Y &7F OSFIND esc &12 A string &0D A OSFILE esc &14 block string &0D A A block OSGBPB esc &16 block A block Cy A OSFSC esc &18 X Y A &FF Y X or &7F OSFSC reply &7F: string &0D &80 or &20 string &00 or &00 RESET string &00 &7F or &80 A X Y are the 8-bit register parameters. Cy is the Carry flag, &00 or &80. With responses of &FF, &80, &40, &20 and &7F only the relevant bit should be tested. Bit 7 and bit 0 of the command byte must be set to zero. Bit 6 and bit 5 are used by the Client to tell the Host what filing system environment it expects: %00: Acorn style: directory.filename/ext %01: Unix style: directory/filename.ext %10: DOS style: directory\filename.ext %11: reserved Host-to-Client transfers ------------------------ Incoming data x char/byte x esc esc char/byte esc esc &00 err string &00 Error esc <&80 ... Read returned control block of specified length esc &8n Escape state change, b0=new state esc &9x Y X A Event esc &Ax Reserved for networking esc &Bx End transfer esc &Cx addr Set address esc &Dx addr Execute address esc &Ex addr Start load from address esc &Fx addr Start save from address b0-b3 of the incoming transfer byte are reserved and should be ignored (other than bit 0 of &8n which holds the new Escape state). Hosts should set them to zero. All addresses and control blocks are transfered high byte to low byte. All strings and data are transfered low byte to high byte. The Carry flag is transfered in bit 7 of a byte. An escape byte must be chosen so as not to conflict an outward command byte or an inward transfer byte. Almost any odd numbered byte can be used, but using &9B allows quite good flexibility. As a mnemonic it is ASCII ESC plus 128, it allows all 7-bit ASCII to be transfered unescaped so a simple terminal can be used for text I/O, and it allows inward esc <&80 to transfer any control block from 1 to 127 bytes long. All outward and inward data is protected by the escape byte. An escape byte is transfered by prefixing it with another escape byte, even within command and response sequences. So, for example, an OSARGS call on channel &9B with address &19B (eg PTR#&9B=&19B) would send the following raw data: Sends: esc 0C Y block=&0000019B A +----+----+-------+----+----+----+-------+----+ | 9B | 0C | 9B 9B | 00 | 00 | 01 | 9B 9B | 01 | +----+----+-------+----+----+----+-------+----+ Reply: A block=&0000019B +----+----+----+----+-------+ | 01 | 00 | 00 | 01 | 9B 9B | +----+----+----+----+-------+ Notes on OSCLI -------------- OSCLI sends a string to the Host and wait for a command execution response byte. If bit 7 is clear, then OSCLI just returns. If bit 7 is set, then the data transfer address should be called. When code is called in response to a command execution response byte with bit 7 set, the Client should save the address of the current program, make any checks on the code such as checking for a valid ROM header, and then call the code. If the code then returns, the Client should then restore the current program address. OSFSC expands on the OSCLI response byte. Notes on RESET -------------- If the Client and the Host share a hardware RESET, then both the Client and the Host will be in the same state at reset. In order for the Client to determine what to do next, the reset sequence is to send a text string, terminated with a zero byte, and then wait for an OSCLI acknowledge. If bit 7 is set, then the data transfer address should be called, otherwise the current local program should be re-entered. If the Client and Host do not share a hardware RESET, such as with a simple serial connection, then the Client and Host will not neccessarily be in the same state at reset, and the text startup string is seen as just the same as any text string, there is no acknowledgement to wait for. In this case, the Client can send an OSFSC &FF call to tell the Host it has restarted. The reply to this will be an OSFSC acknowlege, which is similar to an OSCLI acknowldge, with bit 7 set if the code at the data transfer address is to be called. In the other direction, the Host can tell the Client to restart by sending a Restart data transfer command. The Client will then reset its state (eg setting vectors to their default state, etc.) and send a OSFSC &FF back to the Host. The Host must reply with an OSFSC reply, not with another Reset data transfer command, otherwise the Client and Host will end up in a loop. Notes on OSFSC -------------- Adding OSFSC to the Tube protocol commands allows a Serial Tube Client to be used as a BBC filing system and allows a connection without a shared hardware RESET to inform the Host that the Client has restarted. In a normal Client OSFSC would not be used, FSC calls are used by the MOS to communicate with the filing system, and application code calls other entries, usually OSCLI. OSFSC is sent as a LONG OSBYTE sequence. If the reply byte has bit 7 set then it is an OSFCV byte command and the returned Y and X values follow, for example with *OPT 0,0: Sends: esc 18 X Y A +----+----+----+----+----+ | 9B | 18 | 00 | 00 | 00 | +----+----+----+----+----+ Reply: +----+----+----+ | FF | YY | XX | +----+----+----+ If the reply byte has bit 7 clear then it is an OSFSC string command, and the Client must send the string and wait for a further reply byte, for example with *INFO FRED: Sends: esc 18 X Y A +----+----+----+----+----+ | 9B | 18 | XX | YY | 0A | +----+----+----+----+----+ Reply: +----+ | 7F | +----+ Sends: F R E D +----+----+----+----+----+ | 46 | 52 | 45 | 44 | 0D | +----+----+----+----+----+ Reply: +----+ | xx | +----+ The reply byte from a OSFSC string command is an OSFSC response byte, which is similar to an OSCLI response byte, but is a bitmap that should be tested from bit 7 downwards. If bit 7 is set (ie %1xxxxxxx) this is a standard OSCLI response byte. The transfer address has been set in the background, and it should be called as the current Client program. If bit 6 is set (ie %01xxxxxx) then a stream of characters will be sent which should be displayed to OSWRCH, this will be terminated with a &00 byte which should not be printed. Otherwise, the OSFSC call should just exit. Bit 4 to bit 0 of the returned byte are reserved and should be ignored. OSFSC &FF - Client Started -------------------------- The Serial Tube protocol adds the OSFSC &FF,x,y call for the Client to tell the Host it has restarted. This is also used to implement Shift-Break when Serial Tube is used to implement the HostFS filing system. On entry: X: Reset type (0=Soft or CoPro Client, 1=Hard, 2=PowerOn) Y: 0 for a boot (Shift-Break) Y: <>0 for no boot (typically 8=Break, &FF=CoPro startup) On return: Y: b1-b0=boot option to perform, b7-b2 reserved. X: OSCLI response byte. A Serial Tube CoPro client should call OSFSC with X=&00 so the default reply is also X=&00 indicating that no code is to be called. A Serial Tube filing system will call with Y=&00 or Y=08 and should AND the reply with 3 so the default reply will result in Y=0 for Boot=Off. Data Transfer ------------- All data transfers are initiated by a command sequence being sent from the Client. While the Client is waiting for a response the Host can escape from the byte stream to perform a data transfer. These data transfers can occur at any time that the Client is waiting for inward-coming data, even within another data transfer. From the point of view of the waiting Client command the data transfer takes place in the background. esc &00 err string &00 Error esc &00 &00 &00 Restart The received error byte and string is copied to the Client's error buffer and the Client's error handler entered. An extension is that esc &00 &00 &00 to causes the Client to reset. This is a fatal error (error number zero) with a null error string. esc <&80 ... Read returned control block The transfer byte specifies a number of bytes to read to the Client's command control block set up by the calling command sequence. esc &8n Escape state change, b0=new state The Client's Escape flag is set to the received flag in b0, and any local Escape handler called. esc &9x Y X A Event The Client's primary, secondary and tertiary registers are set to the recieved A, X and Y byte values, and then the local Event handler is called. esc &Ax Reserved for networking This sequence is reserved for networking over a serial link. esc &Bx End transfer Sufficient data and return addresses are popped from the stack to abandon the current data transfer, and then a return is made as though returning from the data transfer call. esc &Cx addr Set address esc &Dx addr Execute address esc &Ex addr Start load from address esc &Fx addr Start save from address These all set the Client's data transfer address, high byte to low byte, from the received information. Set Address then simply returns, leaving the address to be used by the waiting command action (usually OSCLI) as the entry point of code to be exectuted. Execute Address calls the specified address directly from within the transfer routine without checking it. Start Load continues by receiving data, storing it at incrementing memory locations in the Client from the transfer address. Start Save continues by sending data from incrementing memory locations in the Client from the transfer address. Start Load and Start Save transfers are both terminated by an End Transfer packet. Note that if received data is read by polling the data input port, then data transfer actions can only happen while waiting for data. In particular, the Escape state cannot change in the background while a foreground program is running if the foreground program never makes any system calls. If data is received via interupts then the Escape state can change in the background with no system calls being made. If needed, the foreground program can make occasional short OSBYTE calls to allow any pending Escape Change to be received which can be actioned at some future point. Options are: * Call OSBYTE 0,1 - this is the shortest OSBYTE transaction without any side effects * Read POS or VPOS via OSBYTE 135 - this has the advantage of being easy to call from a BASIC program, just do A%=POS or IFPOS every now and then. * Call OSBYTE &7C - this has the side effect of cancelling any pending Escape state and returning X bit 7 set, meaning the foreground program then needs to action the Escape immediately. Hardware -------- The only hardware required to form a Serial Tube link is an 8-bit input port with 1-bit Ready status bit, and an 8-bit output port with 1-bit Ready status bit. The TxRdy bit must indicate that a byte may be transmitted, the RxRdy bit must indicate that a byte has been received and is available for collection, and the other end is waiting for it to be collected. Simple Client code for the code send and receive functions would be the following: \ 6502 code \ Z80 code .SendData .SendData PHA PUSH AF .SendWait .SendWait LDA TxStatus IN A,(TxStatus) :\ Get Status AND #TxRDY AND TxRDY BEQ SendWait JR Z,SendWait :\ Wait until data can be sent PLA POP AF STA TxData OUT (TxData),A :\ Send data RTS RET .ReadData .ReadData LDA RxStatus IN A,(RxStatus) :\ Get Status CLC:AND #RxRDY AND RxRDY BEQ ReadDataOk RET Z :\ Exit if nothing waiting LDA RxData IN A,(RxData) :\ Read data CMP #esc CP esc :\ Check if esc character SEC SCF :\ Carry=Data fetched .ReadDataOk :\ Equal=Byte is esc char RTS RET Most one-byte communication ports (such as an RS232 link) do not automatically tell the remote end to stop transmitting when there is a received byte waiting to be read, so that must be performed by the reception code by raising/lowering a signal such as RTS. \ 6502 code \ Z80 code .ReadData .ReadData PHP:SEI :\ Disable IRQs TYA:PHA :\ Save Y LDY #RxCont LD A,RxCont STY RxStatus OUT (RxStatus),A :\ Lower RTS to allow input LDY #RxStop LDA RxStatus IN A,(RxStatus) :\ Get RxStatus AND #RxRDY AND RxRDY BNE ReadDataOk JR NZ,ReadDataOk :\ Data present PLA LD A,RxStop STY RxStatus OUT (RxStatus),A :\ Raise RTS to stop input TAY:PLP :\ Restore Y, IRQs CLC:RTS RET :\ No Carry=No data present .ReadDataOk .ReadDataOk PLA LD A,RxStop STY RxStatus OUT (RxStatus),A :\ Raise RTS to stop input TAY:PLP :\ Restore Y, IRQs LDA RxData IN A,(RxData) :\ Fetch data CMP #esc CP esc :\ Check if esc character SEC SCF :\ Carry=Data fetched RTS RET :\ Equal=Byte is esc char All other Client code calls these two basic functions for all communication. More detailed information ------------------------- The source for the 6502 and Z80 Clients can be examined for more detailed information and should be regarded as the definitive specification. If documentation disagrees with the working released code, the code is the superior reference unless otherwise stated. Serial networking ----------------- The esc,&Ax sequence is reserved for networking over a serial link. This has been implemented in the SerialNet NFS ROM as follows: esc,&Ax marks the start or end of a Econet frame over a serial link as part of an Econet packet. esc,&Ax - %10100xxx - incoming esc,&Ax - %10101xxx - replies %1010000x - ScoutStart %1010100x - ScoutACK %1010001x - ScoutEnd %1010101x - ScoutNAK %1010010x - DataStart %1010110x - DataACK %1010011x - DataEnd %1010111x - DataNAK A standard 4-frame Econet packet is sent over a serial link as follows: Scout: +----------+---------------------------+---------+ |ScoutStart|Dest|Dest|Src|Src|Ctrl|Port|ScoutEnd | | esc,&A0 |Stn |Net |Stn|Net|Byte| | esc,&A2 | +----------+---------------------------+---------+ <--- - Packet Header - ---> Scout acknowledge sent back if dest has an open receive block: +---------+-----------------+--------+ |ScoutACK |Dest|Dest|Src|Src|ScoutEnd| | esc,&A8 |Stn |Net |Stn|Net|esc,&A2 | +---------+-----------------+--------+ Where Dest and Src are opposite to Scout frame Scout acknowledge sent back if dest does not have an open receive block: +---------+-----------------+--------+ |ScoutNAK |Dest|Dest|Src|Src|ScoutEnd| | esc,&AA |Stn |Net |Stn|Net| esc,&A2| +---------+-----------------+--------+ Where Dest and Src are opposite to Scout frame Data: +---------+-----------------+--------+-------+ |DataStart|Dest|Dest|Src|Src| Packet |DataEnd| | esc,&A4 |Stn |Net |Stn|Net| Data |esc,&A6| +---------+-----------------+--------+-------+ Data acknowledge sent back if receive block large enough: +---------+-----------------+-------+ | DataACK |Dest|Dest|Src|Src|DataEnd| | esc,&AC |Stn |Net |Stn|Net|esc,&A6| +---------+-----------------+-------+ Where Dest and Src are opposite to Data frame Data acknowledge sent back if receive block not large enough: +---------+-----------------+-------+ | DataNAK |Dest|Dest|Src|Src|DataEnd| | esc,&AE |Stn |Net |Stn|Net|esc,&A6| +---------+-----------------+-------+ Where Dest and Src are opposite to Data frame As with ADLC Econet, Immediate operations send an extended scout and return data immediately in an extended ScoutReply, and Broadcasts transmissions consist solely of an extended Scout with eight bytes of payload. The net number is either &00 for 'this net', &FF for 'broadcast', or &7x for 'serial net' (expanding on &80-&FE for Ethernet), allowing the station at the other end of the link to translate the transaction to a wider network. History ------- 09-Apr-1995 v0.10 Z80 Serial Tube Client written. 15-May-1995 v0.10 6502 Serial Tube Client written. 12-Jun-2010 v0.10 6809 Serial Tube Client written. 15-Nov-2013 v0.12 6812 Serial Tube Client updated from JFR's translation. 06-Mar-2018 v0.13 Reworded section on OSFSC. 15-Feb-2019 v0.14 Added section on serial networking.