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.
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 fourbyte 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.
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.
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:
¹ 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.
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 nonzero then only blocks from that specifice network address will be accepted on this channel, any number of such nonzero address channels may be opened provided they all have different addresses.
If a nonspecific channel is opened it will only receive data from machines which are not explicitly served by an individual channel. Only one nonspecific channel may be open at a time.
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 dataout and datain, and the status line is connected to both statusout and statusin. This allows the machine to pull either line low (the outputs are open collector) and also to monitor the level on each line.
The statusin 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.
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.
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.
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.
Throughout a network transmission, interrupts are disabled because of the timing considerations.
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.
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.
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
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.
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.
b0 - data flag: 0 : no data to follow header 1 : data block to follow
b1..b4 - not defined - must be zero
b5 - endofrecord flag: 1 => end of record
Flush
or
Close
command, this bit is set to force the
network read character routine to return the
endoffile status, thus identifying the end of a
given message or file. The next
read character or
read status call
will revert to the characternotavailable state,
unless the endoffile flag is also set.
b6 - endoffile flag: 1 => end of file
b7 - type byte flag.
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 reenabled, 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 attentiongrabbing 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:
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.
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 16bit value, which is recalculated 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:
XOR
the new bit with the most significant bit of the CRC
register and set the carry to this value.
XOR
the register with
0810h
.
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.
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
poweron or cold reset it is set to zero, which is invalid as a
network machine number.
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).
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.
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.
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
nonspecific 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 endofrecord flag was received in a header for this channel, an endoffile 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 endoffile or characternotavailable 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.