EXOS 2.1 Serial/Network Driver Specification

4. Network Device

4.1 Data Transaction Protocols

The network can be used in two modes: directed and broadcast. In directed mode, one machine sends data to a second machine and all other machines ignore it. In broadcast mode, one machine sends data to all the other machines at once. Each machine on the network is identified with a unique address in the range 1 to 32.

Each block that is sent consists of a header which may or may not be followed by a block of data bytes. The header includes a synchronisation pattern, the source and destination addresses and a type byte, of which the latter contains a flag determining wether any data bytes are to follow. In every header there is also a count of the number of data bytes in the block, although this is ignored if the type byte specifies that no data at all will be sent.

4.1.1 Broadcast Protocol

The header of a block which is being broadcast contains a destination address of zero. The block will be received by all machines which are listening and there is no method of determining whether it was read correctly or not.

This lack of handshaking introduces problems if some of the machines had interrupts disabled at the time of the broadcast, and thus arrive at the network interrupt handler once transmission has already started, or even after it has finished. In order to avoid possible confusion brought on by receiving only part of a block, the destination machines check for the synchronisation pattern which is supplied in the block header. The header consists of a repeating four­byte block, which continues for long enough to give the receiver time to be ready in most cases while obviously being kept reasonably short in order to save time.

4.1.2 Directed Data Protocol

As directed data is destined for just one machine, the sending machine can wait for acknowledgement in order to ensure that the destination is listening, and to confirm that the data block is received without error.

When a computer reads a block header which has its own number as the destination address, and is prepared to accept the block (see later), then it sends back an acknowledgement to the source machine. This is simply a signal on the data line to show that it is ready to receive; the absence of the signal within a given time (about 4 bit periods at the selected baud rate) is interpreted by the source machine as an error.

If any data bytes are to be sent, these are transmitted once the header has been acknowledged. After the complete data block has been received, the destination machine must carry out the checksum calculation and either confirm the data by sending another acknowledgement signal, or reject it by not responding.

Error Response:

When a destination machine finds an error it returns immediately from the network interrupt routine without setting any interrupt flags (see later). The source machine, when it finds no acknowledgement signal after sending a header or a data block, retries as follows:

  1. Release network¹.
  2. Wait for long random delay, of the order of a quarter of a second.
  3. Try to gain control of network and send again.

¹ Note : It is extremely important that under no circumstances should an error occur which causes a machine to hang irretrievably while it is in control of the network, as this would also hang the network itself and thus any other machines which are trying to use it. Any time a machine is waiting indefinitely for a signal on the network lines, pressing the STOP key will regain user control.

4.1.3 Accepting Data Blocks

A machine is only prepared to receive transactions from another computer on the network if there is a suitable channel open to the network driver.

When a channel is opened by the user a remote address number is given as the unit number. If this is zero then blocks will be accepted from anywhere. If it is non­zero then only blocks from that specifice network address will be accepted on this channel, any number of such non­zero address channels may be opened provided they all have different addresses.

If a non­specific channel is opened it will only receive data from machines which are not explicitly served by an individual channel. Only one non­specific channel may be open at a time.

4.2 Low­level Network Operation

4.2.1 Hardware Connections

The network driver is rather more complicated than the serial interface driver because it has to include protocols to avoid collisions. It uses the same hardware as the serial driver. All machines on the network are joined by three wires: ground, data and status. On each machine the data line is connected to both data­out and data­in, and the status line is connected to both status­out and status­in. This allows the machine to pull either line low (the outputs are open collector) and also to monitor the level on each line.

The status­in line is also connected to the external interrupt input so the status line going low can trigger an interrupt. This is how the machine can respond to data sent down the network asynchronously.

4.2.2 Obtaining Control of the Network

When a machine wishes to send data to another machine, or to broadcast it, it must first get control of the network. Only one machine can be in control of the network at a time and the protocol used for obtaining control is designed to ensure that collisions (two machines taking control at the same time) do not occur. The penalty for this is a slight loss of speed and a priority ordering of machines on the network.

There is a timing constant C defined, which corresponds to a delay of the order of one millisecond. When a machine wants control of the network it must follow this procedure.

  1. Both status­out and data­out should be left high whenever this machine does not have control of the network.
  2. If the status line is high go to step 4.
  3. Wait until the status line is high and remains high for a period of RND * C where RND is a random number in the range 1 to 16. If the status line does not remain high for the required time then repeat this step with a new random number.
  4. Pull the status line low.
  5. Wait for a period of C * ADDR where ADDR is the address of this machine on the network, constantly monitoring the data line. If data goes low for any time during this interval then release the status line (set it high again) and return to step 3.
  6. Pull the data line low and then proceed to send the block header (see later).

Throughout a network transmission, interrupts are disabled because of the timing considerations.

4.2.3 Attracting Attention - Protocol

Once a machine has secured itself control of the network, it can start the transmission of the data block.

First of all, it has to attract the attention of its intended audience. This is done by repeatedly sending a header consisting of a synchronisation byte, the destination and source addresses of this block and a type byte. The bytes are sent in the order sync/dest/source/type/sync/... and are terminated with the ones complement of the dest byte in the position of the next sync byte. The format of these bytes is as follows.

Machine address bytes:

The destination address is the number of the machine on the network to which this block is being sent; if it is zero then it indicates that it is a broadcast block, which all machines should receive. The source address is the number of the machine which is sending the block.

Byte format:
b0..b5 - machine address : 1 to 32, or 0 for broadcast
         (0 only valid as a destination address)
    b6 - source/destination selection: 0 : source
                                       1 : destination    
    b7 - complement of bit 6
Synchronisation byte: 00000000B

The synchronisation byte is required because fast interrupt response is not guaranteed; the status line can be brought low at any time, perhaps while interrupts are disabled on the required destination machine. The synchronisation method is detailed below.

Type byte

The type byte contains information defining what kind of transaction this is, including whether or not any data bytes will be sent after the header.

Byte format:
    b0 - data flag:           0 : no data to follow header
                              1 : data block to follow
b1..b4 - not defined - must be zero
    b5 - end­of­record flag:  1 => end of record
If the block was sent as a result of a Flush or Close command, this bit is set to force the network read character routine to return the end­of­file status, thus identifying the end of a given message or file. The next read character or read status call will revert to the character­not­available state, unless the end­of­file flag is also set.
    b6 - end­of­file flag:    1 => end of file
Only ever set in conjunction with the end­of­record flag, it implies that the sending machine has closed its channel to this computer. All subsequent requests for channel status or for further characters will return the 'End of file' condition, until another block of data is received on this channel.
    b7 - type byte flag.
Must ALWAYS be set to 1, so that the synchronisation byte is guaranteed to be the only string of eight zero bits in the header.

4.2.4 Destination Machine Synchronisation

As stated above, there is some problem in synchronising the destination machine, which stems from the uncertainty of catching the machine while its external interrupt is enabled. This is now explained in detail.

If external interrupts are disabled, a latch will record the interrupt transition. The interrupt will then take place as soon as the input is re­enabled, and the destination machine will immediately start looking at the data line.

The source machine is not expected to wait for long enough to ensure that all machines will be listening, but instead begins to output its attention­grabbing sequence as soon as it is ready - in the hope that all relevant machines will catch on before that sequence finishes. The protocol must therefore cope with a destination machine starting to read the data line at any time during this period.

RS232 character framing is by means of data line levels, so any transition from 1 to 0 can be interpreted as a start bit. This means that a computer which starts to listen to the data line at an arbitrary moment may pick up what it thinks is a start bit when in fact it has simply found a changing level in the middle of a character. The purpose of the synchronisation byte in the header sequence is to make sure that the character framing is correctly aligned as quickly as possible.

The method chosen for achieving this is to send a synchronisation character made up of all zero bits, and to tell the destination machine to check every start bit transition until it finds one which is followed by eight zero bits and a stop bit. The next 'start bit' is then guaranteed to be the true start of a character.

The above specifications require that any aspiring destination machine should follow this algorithm when it enters the interrupt routine:

  1. If status line is high, return. This would occur if the response had been so slow that the transfer was all over, or had been abandoned due to lack of interest.
  2. Wait until the data line is low.
  3. Wait for data line to go high (possible start bit).
  4. Delay for required time to synchronise to middle of bits
  5. Continue to read data at one­bit intervals until a low bit is found.
  6. If any number other than nine high bits were found (including the start bit) go to 4.
  7. Store the synchronisation byte, and the next three bytes which are received, in scratch memory. These four values are the header pattern, and should be sent repeatedly by the source machine.
  8. Check that the destination byte is valid, and that it holds either this machine number or the value zero. Return if this is not the case.
  9. Check that the source byte is valid, and that there is a channel open which can serve the specified machine - ie, either a specific channel for that address, or a non­specific channel. Otherwise return.
  10. Check that the type byte is valid.
  11. Continue to read bytes. Check that each group of four matches the values stored in step 7, and continue to loop until a value does not tally.
  12. The value which disagrees should be the sync byte, and it should contain instead the ones complement of the dest byte. If this is not the case, return.
  13. Read the next two bytes. If they are 1's complement of each other then the latter is the byte count for the following data block. Else return.
  14. If the destination address was non­zero (i.e. transaction is directed rather than broadcast), pull data low for about 1ms as an acknowledgement, then release it.

Note: Whenever a byte is read, status is checked for still being held low by the source machine. If it goes high at any time, the link is immediately assumed broken.

4.2.5 Sending the Data Bytes

When the destination machine pulls the data line low, and leaves it there for at least half a millisecond, that is the signal to the source machine to start sending any data which may be included in this transaction. When data is released, the sender pulls it low again immediately then gets on with sending the data block.

The format of the data block is:

      |  Data bytes  |  CRC low  |  CRC high  |

The byte count which was received in the header sequence is a true count of the number of data bytes in the block. A value of zero means 256 bytes.

The two CRC bytes are a cyclic redundancy check calculated on all data bytes in the block (but not the header). The CRC is evaluated as a 16­bit value, which is re­calculated when the data block is received. If the CRC calculated on reception is not the same as the value which was sent then the block is assumed to be garbage.

The algorithm for calculating the CRC is the same as that used for the cassette driver but is explained here as well. For purposes of the CRC calculation the data block is regarded as a bit stream. Each time a data bit (not a start or stop bit) is sent, a 16 bit CRC register is updated. This is done in the serialisation routine which is shared with the serial interface driver, so the CRC is also calculated on data sent or received through the serial handler even though this is not required.

The CRC register is 16 bit value which is initialised to zero at the start of the data block, and then when each bit is sent or received the following operations are performed:

  1. XOR the new bit with the most significant bit of the CRC register and set the carry to this value.
  2. If the carry is set then XOR the register with 0810h.
  3. Rotate the register one bit left, moving the carry into the least significant bit.

4.2.6 Data Transmission Format

All network blocks are sent as a series of bytes in the same format as the serial interface driver sends them, using a word format of eight data bits, two stop bits and no parity. The characters are transmitted at the currently selected baud rate, which defaults to 9600 baud.

4.3 Using the Network

4.3.1 Address Determination

The above protocols require that each machine on the network has a unique address, so the network handler refuses to open channels onto the network unless the computer has been given its address.

The address is held in the EXOS variable ADDR_NET, and must be set by the user before attempting any network operations. At power­on or cold reset it is set to zero, which is invalid as a network machine number.

4.3.2 Opening Channels To the Network

As mentioned before, each channel which is opened to the network must specify a remote machine number for which the channel is reserved, although an address of zero will allow data blocks to be received from any machine. This number is given in the call to the EXOS channel opening function as the unit number (see EXOS kernel specification). Any filename given is ignored.

Note the clear distinction between the channel number and the network address of the machine which that channel serves. The two are completely independent; it is up to the user to keep track of which channel serves which machine when he wishes to output to the network. For input, both values are made available to him by the interrupt handling code (see below).

4.3.3 Interrupts

When the first channel is opened to the network, the external interrupts are enabled and will remain so until the last channel is closed. While external interrupts are enabled, any transition from high to low on the status line will cause the network interrupt service routine to be called. A particular consequence of this is that opening a serial channel (for which the quiescent line levels are low) on a machine which is connected to a network, will cause any machines using the network to be interrupted, and to hang in anticipation of a data block.

4.3.4 Buffers

Each channel opened to the network sets up two buffers of each 256 bytes length; a receive buffer and a transmit buffer.

The receive buffer is filled with data bytes which arrive in blocks on the network, and can hold just one block at a time (no matter how short that block is; even a block of just one byte effectively 'fills' the receive buffer until it has been read or cleared). The user reads from the buffer until it is empty, when it will be able to accept further blocks from the network. The buffers of each channel are independent, so blocks can be received from any machine provided that the receive buffer which serves it is free.

The transmit buffer is filled by the user, and sent as complete blocks onto the network. It can be forced onto the network at any time by using a flush special function call, or will be sent automatically when the 256th byte is written to it. The Transmit buffer will also be flushed automatically if the channel is closed, thus enabling files of any length (from 1 byte upwards) to be sent to another machine while the buffering remains transparent.

4.3.5 Read and Write Functions

The network device supports the usual EXOS read and write character function calls and the block read and write calls.

The reception of blocks from remote machines is done by an interrupt service routine, which is invoked when the status line goes low. The machine will remain in its interrupt routine watching for the data line to go low and then read in the header. If the header is read succesfully, and the block is one which this machine wants to receive, then the data is read into the receive buffer for the channel serving the given remote machine. Each channel can buffer one block at a time in its receive buffer.

The network driver can cause a software interrupt when a block is successfully received from the network. An EXOS variable called NET_IRQ is provided to switch this function on and off. If NET_IRQ is zero then software interrupts are enabled, and the occurence of a successful network interrupt will cause the value ?NET to be placed in the variable FLAG_SOFT_IRQ.

The EXOS variable CHAN_NET is used to pass to the user, the channel number from which buffered data can be read. CHAN_NET is only updated when a character is read from the channel which it specifies, or if that channel is closed. It is then changed to the channel number of the lowest numbered machine which has caused an interrupt. So if machines 2 and 10 send a block each while data sent by machine 5 is still waiting to be read, then when CHAN_NET is changed it will point to the channel for machine 2 rather than 10 whichever interrupted first. Machine 10 will be serviced later, as soon as it becomes the lowest number awaiting attention. This allows specific machines to be given priority - a teacher operating from machine 1 will almost always have his message received in preference to a message from another machine, while broadcast messages are given the highest priority of all. If no data is available CHAN_NET holds the value 255.

Whenever CHAN_NET is updated, the EXOS variabled MACH_NET is also altered to hold the number of the machine which caused the interrupt. This is vital when a block is received on the non­specific channel, since there would otherwise be no way of telling which machine sent it. In other cases it is simply useful - as stated above, the user would normally be expected to keep his own records of which channel serves which machine.

An application program requests bytes by calling the EXOS read block routines, and is given bytes from the receive buffer if they are available. If not, there are two possible responses: If an end­of­record flag was received in a header for this channel, an end­of­file condition is returned. Otherwise, the read routine will halt until this channel receives data from the network.

The read channel status function call is supported, so the user can check for the end­of­file or character­not­available conditions before trying to read a character.

Writing to the network is carried out as a series of write character or write block function calls. If an error is encountered when the buffer is to be sent, the network handler will continuously retry at intervals of between quarter and half a second. This can be stopped by pressing the STOP key, which will also cause the buffer contents to be cleared before the handler returns. Note that this error condition can only be detected for directed data blocks; broadcast data does not require acknowledgement and therefore cannot check that the data was received properly.

Next Chapter: Quick Reference Summary.

David Bouman. (dsbouma@cs.vu.nl)