www.riscos.com Technical Support: |
|
The Econet module provides the software needed to use Acorn's own Econet networking system. The software allows you to send and receive data over the network.
It is used by RISC OS modules such as NetFS and NetPrint, which provide network filing and printing facilities respectively. It is also used by various other Acorn products that use Econet, such as FileStores, Econet bridges, and so on.
Note that to use the Econet you must have an Econet expansion module fitted to your RISC OS computer. If you do not already have one, they are available from your Acorn supplier.
Econet is Acorn's own networking system, and the Econet module provides the necessary software to use it.
The main purpose of any networking system is to transfer data from one machine to another. Econet breaks up the data it sends into small parts which are sent using a well defined protocol.
Econet does not use buffers in the same way as most other input and output facilities that RISC OS provides. Instead the data is moved directly between the Econet hardware and memory. This means that each time data is transmitted or received, there has to be a block of memory available for the Econet software to use immediately, either to read data from or place data in.
These blocks of memory are administered by the Econet software, which uses control blocks to do so. Many of the SWIs interact with these control blocks, so you can set them up, read the status of an Econet transmission or reception, and release the control blocks memory when you have finished using them.
In the same way as files under the filing system use file handles, these control blocks also use handles. Just like file handles, your software must keep a record of them while you need to use them.
The Econet also provides a range of immediate operations, which allow you to exercise some control over the hardware of remote machines, assuming you get their co-operation. Some of these will work across the entire range of Acorn computers, whereas others are more hardware-dependent and so may only be possible on RISC OS machines.
A single transmission of data on an Econet is called a packet. Packets travel across the network from the transmitting station to the receiving station. The most common form of packet is called a 'four way handshake'. A 'four way handshake' consists of four frames. Each of these four frames starts with the following four bytes:
These four bytes are sent in this order to facilitate decoding by the software in the receiving station.
The first frame is sent by the transmitting station; it contains the usual first four bytes, the port byte (described later), and the flag byte (also described later). This first frame is called the scout. The receiving station then replies with the scout acknowledge, which consists of just the usual first four bytes. The third frame is the data frame; this frame has the usual first four bytes, followed by all the data to be transferred. Lastly there is a final acknowledge frame which is identical to the scout acknowledge frame.
This exchange of frames can be seen with the NetMonitor and is displayed something like this.
FE0012008099 1200FE00 FE00120048454C500D 1200FE00
Successful transmission of data requires co-operation from the receiving station. A station shows that it is ready to receive by setting up a receive control block (or RxCB). All RxCBs are kept by the Econet software and don't need to concern you. To create an RxCB all you need to do is call a single SWI (Econet_CreateReceive: see Econet_CreateReceive), telling the Econet software all the required information. The Econet software will return to you a handle which you then use to refer to this particular RxCB in any further dealings with the Econet software.
The information required by the Econet software is:
It is important you note that when the data arrives from the transmitting station it is not buffered at all - it is taken directly from the hardware and placed in memory at the address you specify. This area of memory is referred to as a buffer (in this case a receive buffer). A consequence of this is that memory used for receiving Econet packets must be available at all times whilst the relevant RxCB is open. You must not use memory in application space if your program is to run within the Desktop environment.
The Econet software keeps a list of all the open RxCBs. When a scout frame comes in it is checked to see if it matches any of the currently open RxCBs:
All RxCBs have a status value. These values are tabulated below.
7 | Status_RxReady |
8 | Status_Receiving |
9 | Status_Received |
The status of a particular RxCB can be read using the Econet_ExamineReceive call; this takes the receive handle of an RxCB and returns its status.
When an RxCB has been received into, its status will change from RxReady to Received; usually, you will then call Econet_ReadReceive. This returns information about the reception; most importantly it tells you how much data was received - which can be anything from zero to the size of the buffer. It also returns the value of the flag byte.
The port, station, and net are also returned; these are useful because you can open an RxCB that allows reception on any port or from any station.
It is very important that when RxCBs are no longer required, either because they have been received into, or because they have not been received into within a certain time, that they are removed from the system. You do so by calling the SWI Econet_AbandonReceive. The major function of this call is to return to the RMA the memory that the Econet software used to hold the RxCB; obviously if RxCBs are not abandoned, they will consume memory which will not automatically be recovered by the system.
The usual sequence of operations required for software to receive data is as follows: First call SWI Econet_CreateReceive, then make numerous calls to SWI Econet_ExamineReceive until either a reception occurs, a time out occurs, or the user interferes (by pressing Escape for instance). Then read the RxCB (Econet_ReadReceive) if it has been received into. Finally, abandon the RxCB (Econet_AbandonReceive).
To make this task easier the Econet software provides a single SWI (Econet_WaitForReception: see Econet_WaitForReception) which does the polling, the reading, and the abandoning for you. To call SWI Econet_WaitForReception you must pass in:
Econet_WaitForReception returns one of four status values:
8 | Status_Receiving |
9 | Status_Received |
10 | Status_NoReply |
11 | Status_Escape |
The call will return as soon as a reception occurs; when this happens the status is Received. If the time limit expires then the status is usually NoReply, but if reception had started just after the timeout, and so was then abandoned, the status will be Receiving. This is not a very likely case. If the escapable flag is set then pressing the Escape key causes the call to return with the Escape status.
Transmission is roughly similar to reception; a single SWI (Econet_StartTransmit - Econet_StartTransmit) is all that is required to get things started. This call requires the following information:
SWI Econet_StartTransmit returns a handle. These handles are distinct from the handles used by the receive SWIs.
Various transport types may impose a limit on the amount of data you can send in a single packet. You can find out the limit for the transport you are using by calling Econet_PacketSize.
To check the progress of your transmission you can call Econet_PollTransmit. This returns the status of the particular TxCB, which will be one of seven possible values:
0 | Status_Transmitted |
1 | Status_LineJammed |
2 | Status_NetError |
3 | Status_NotListening |
4 | Status_NoClock |
5 | Status_TxReady |
6 | Status_Transmitting |
Status_Transmitted means that your transmission has completed OK and that the data has been received by the destination machine. Status_TxReady means that your transmission is waiting to start, either because the Econet is busy receiving or transmitting something else, or your transmission is queued (see later for more details of this). Status_Transmitting is obvious; so too is Status_NoClock, which means that the Econet is not being clocked, or more likely your station is not plugged into the Econet. Status_LineJammed means that the Econet software was unable to gain access to the Econet; this may be because other stations were transmitting, but it is more likely that there is a fault in the Econet cabling somewhere. Status_NotListening is returned when the destination station doesn't send back a scout acknowledge frame; this is usually because the destination station doesn't have a suitable open receive block. Status_NetError will be returned if some part of the four way handshake is missing or damaged; the usual cause of this status is the sender sending more data than the receiver has buffer space for, so the receiver doesn't send back the final acknowledge frame.
Status returns like NotListening and NetError can also be caused by transient problems with the Econet such as electrical noise, or by the receiving station using its floppy disc or being otherwise too busy to accept data. Because of this it is usual to try more than once to send a packet if these status returns occur. To make this easier for you the Econet software can automatically perform these extra attempts for you. These retries are controlled by passing two further values in to the Econet_StartTransmit SWI:
If the Count is either zero or one then only one attempt to transmit will take place. If the Count is two or more then retries will occur, at the specified interval (given in centiseconds). To give an example as it would be written in BASIC V:
10 DIM Buf% 20 20 Port%=99: Station%=7: Net%=0 50 SYS "Econet_StartTransmit",0,Port%,Station%,Net%,Buf%,20,3,100 TO Tx% 60 END
When this partial program was RUN it would try to transmit immediately, probably before the program reached the END statement. If this transmission failed with either Status_NotListening or Status_NetError, then the Econet software would wait for one second (100 centiseconds) and try again. If this also failed then the software would wait a further second and try for a third time. The status of the final (in this case third) transmission would be the status finally stored in the TxCB; this could be read using SWI Econet_PollTransmit. To see this we could add some extra lines to the example program:
30 TxReady%=5 40 Transmitting%=6 60 REPEAT 70 SYS "Econet_PollTransmit", Tx% TO Status% 80 PRINT Status% 90 UNTIL NOT ((Status%=TxReady%) OR (Status%=Transmitting%)) 100 END
Now the program will show us the status of the TxCB. We would be very unlikely to see the status value ever be Status_Transmitting since it will only have this value for about 90s during the two seconds it is retrying for. But it is most important that your software should be able to handle such a situation without error.
For retries to be effective you must try for at least 5 seconds. Recommended values for the Count and Delay are:
As with receptions it is most important that memory used for transmitting Econet packets must be available at all times whilst the relevant TxCB is open. You must not use memory in application space if your program is to run within the Desktop environment. This is because like receptions, transmissions move data directly from memory at the address you specify to the hardware. Also, as with receptions, it is important to inform the Econet software that you have finished with your transmission and that memory required for the internal TxCB may be returned to the RMA. You do this by calling Econet_AbandonTransmit with the appropriate TxHandle:
100 SYS "Econet_AbandonTransmit", Tx% TO FinalStatus% 110 PRINT "The final status was ";FinalStatus%
To make this start, poll, and abandon sequence easier for you the Econet software provides it all as a single call (Econet_DoTransmit: see Econet_DoTransmit). This call has the same inputs as SWI Econet_StartTransmit, but instead of returning a handle it returns the final status. Using this call our program would look like this:
10 DIM Buf% 20 20 Port%=99: Station%=7: Net%=0 40 SYS"Econet_DoTransmit",0,Port%,Station%,Net%,Buf%,20,6,100 TO Status% 50 PRINT "The final status was ";Status%
As you can see this makes things a lot easier. As an aid to presenting these status values to the user there are two SWI calls to convert status values to a textual form, the most frequently used of which is the call Econet_ConvertStatusToError. This call takes the status and returns an error with the appropriate error number and an appropriate string describing the error.
For instance we could add an extra line to our final program:
80 SYS "Econet_ConvertStatusToError", Status%
Note that the SYS command sets unused registers to zero.
Our program will now RUN and always have an error, in this case the error 'Network station not listening at line 80'. This error message is actually held in the RMA, in one of a number of error blocks used by the MessageTrans module, and so you cannot directly add to it. Furthermore, the error message will have a 'limited lifetime' before MessageTrans reuses the error block. Consequently, if you wish to process the error message or to preserve it, you should copy it into a buffer. To do so you can specify the location and size of such a buffer when calling Econet_ConvertStatusToError:
70 DIM Error% 50 80 SYS "Econet_ConvertStatusToError", Status%, Error%, 50
This new program will function in the same manner as the previous program except that the error block will have been copied from the Econet messages file (in the ROM) into RAM (at the address given in R1). The main reason for this is to allow the Econet software to customise the error for you.
If the station and net numbers are added as inputs to the call, the Econet software will add them to the output string:
80 SYS "Econet_ConvertStatusToError",Status%,Error%,50,Station%,Net%
Now the error reported will be of the form 'Network station 7 not listening at line 80'. It is important to stress that this is a general purpose conversion. It will convert Status_Transmitted just as well as Status_NotListening, so usually you would test the returned status from Econet_DoTransmit, and only convert status values other than Status_Transmitted into errors:
30 Transmitted%=0 60 IF Status%=Transmitted% THEN PRINT "OK": END
The same program fragment could be written in assembler (this example, like all others in this chapter, uses the ARM assembler rather than the assembler included with BBC BASIC V - there are subtle syntax differences):
Tx MOV r0, #0 ; Flag MOV r1, #99 ; Port MOV r2, #7 ; Station MOV r3, #0 ; Net ADR r4, Buffer MOV r5, #20 ; Buffer length MOV r6, #6 ; Count MOV r7, #100 ; Delay (in centiseconds) SWI Econet_DoTransmit BEQ r0, #Status_Transmitted LDRNE r1, ErrorBuffer MOVNE r2, #50 SWINE Econet_ConvertStatusToError MOV pc, lr
Notice here in the assembler version how the return values from Econet_DoTransmit fall naturally into the input values required for Econet_ConvertStatusToError. This code fragment is not really satisfactory since no code written as either a module or a transient command should ever call the non-X form of SWIs. If the routine Tx is treated as a subroutine then it should look more like this:
Tx STMFD sp!, {lr} MOV r0, #0 ; Flag MOV r1, #99 ; Port MOV r2, #7 ; Station MOV r3, #0 ; Net ADR r4, Buffer MOV r5, #20 ; Buffer length MOV r6, #6 ; Count MOV r7, #100 ; Delay (in centiseconds) SWI XEconet_DoTransmit BVS TxExit TEQ r0, #Status_Transmitted ADRNE r1, ErrorBuffer MOVNE r2, #50 SWINE XEconet_ConvertStatusToError TxExit LDMFD sp!, {pc}
This routine returns with V clear if all went well; if V is set, then on return R0 will contain the address of a standard error block.
RISC OS 2 differs from later versions in that it doesn't use the MessageTrans module, but instead has the full text of the English error messages in ROM. When converting messages with added station numbers you must convert into your own buffer. If you give no buffer, or its length is insufficient, then the station and net numbers are ignored and RISC OS 2 returns a pointer to the normal ROM copy of the message.
The second error conversion call is Econet_ConvertStatusToString, which does exactly what its name suggests. The input requirements are very similar to the string conversion SWIs supported by RISC OS. In this case you pass the status value, a buffer address, and the length of the buffer. As with Econet_ConvertStatusToError you can also pass the station and net numbers, which will be included in the output string. To illustrate this the assembler routine shown above is changed to print the status on the screen:
Tx STMFD sp!, {lr} MOV r0, #0 ; Flag MOV r1, #99 ; Port MOV r2, #7 ; Station MOV r3, #0 ; Net ADR r4, Buffer MOV r5, #20 ; Buffer length MOV r6, #6 ; Count MOV r7, #100 ; Delay (in centiseconds) SWI XEconet_DoTransmit BVS TxExit TEQ r0, #Status_Transmitted BEQ TxExit ; Everything is OK ADR r1, TextBuffer MOV r2, #50 ; Text buffer length MOV r5, r0 ; Save the status value SWI XOS_ConvertCardinal1 ; Convert the status number MOVVC r0, r5 ; Recall status if no error SWIVC XEconet_ConvertStatusToString ADRVC r0, TextBuffer SWIVC XOS_Write0 ; Print the resultant string TxExit LDMFD sp!, {pc}
Both Econet_ConvertStatusToError and Econet_ConvertErrorToString use MessageTrans to produce the error message or string. The message tokens for each of the status values are tabulated below. Where two tokens are listed, as for Status_NotListening, the first is for the error message - or string - without a station number inserted, and the second is for the version with the station number inserted. The files supplied with RISC OS that the Econet software
uses are 'Resources:$.Resources.Econet.Messages' and 'Resources:$.Resources.Global.Messages' (used solely
for the status message for Escape).
The flag byte is sent from the transmitting station to the receiving station and can be treated as an extra seven bits of data. By convention, it is used as a simple way of distinguishing different types of packet sent to the same port, and it is worth you doing the same.
This is most useful in server type applications where it is often the case that similar data can be sent for different purposes, or some sorts of data are outside the normal scope. An example is a server that takes requests for teletext pages, but can also return the time. A different value for the flag byte allows the server to differentiate time requests from normal traffic. Another example is the printer server protocol, which uses the flag byte to indicate the packet that is the last in the print job, without having to change the data part of the packet.
The port byte is used in the receiving station to distinguish traffic destined for particular applications or services.
For instance the printer server protocol uses port &D1 for all its connect, data transfer, and termination traffic, whereas the file server uses port &99 for all its incoming commands. This use of separate ports for separate tasks is also exploited further by the file server protocol in that every single request for service by the user can use a different port for its reply. This prevents traffic getting confused.
The Econet software provides some support for you to use ports by providing an allocation service for port numbers. Port numbers should, if possible, be allocated for all incoming data.
Software that requires the use of fixed port numbers, like NetFS and NetPrint, can claim these fixed ports by calling Econet_ClaimPort. This call takes a port number as its only argument. When these claimed ports are no longer required (when the module dies for instance) it can be 'returned' by calling SWI Econet_ReleasePort.
Other software that would like a port number allocated to it can call Econet_AllocatePort, which will return a port number. While this port number is allocated no other calls to Econet_AllocatePort will return that number, until it is 'released' by calling Econet_DeAllocatePort with the port number as an input. The NetFS software uses this method of allocation and deallocation to get ports to use as reply ports in the file server protocol. The Econet software keeps a table in which it records the state of each port number: this can be either free, claimed or allocated.
Ports that have been claimed will not be allocated, and can only be freed by calling SWI Econet_ReleasePort. Calling SWI Econet_DeAllocatePort will return an error if the port is claimed rather than allocated. Ports that have been allocated can not be claimed, and in fact an attempt to claim an allocated port will return an error. You should be careful with software that uses allocated ports to make sure that all ports are deallocated when they are no longer required, especially after an error. The claiming and releasing of ports should likewise be carefully checked.
A typical example of the use of the port allocator would be a multi-player adventure game server. The server would claim one port (eg port &1F). This port number would then be the only fixed port number in the entire protocol. When a player wished to join the game she should ask for a port to be allocated in her machine and send this port, along with all the information required to enter the game, to the game server on port &1F. If the server can't be contacted or doesn't reply within the required time the port should be deallocated and an error returned. When the server receives this packet it should check the user's entry data; if this is OK it should then allocate a port for that user and return it, along with any other information required to start the game off. When the user wants to quit the game the server should deallocate its user's port, then send the last reply to the user. The user should deallocate the port when the reply arrives or if the server doesn't reply soon enough.
To illustrate this example the user entry routine is shown below; note that this routine is coded for clarity rather than size or efficiency.
Entry STMFD sp!, {r0-r8,lr} ; R0 points to the text string SWI XEconet_AllocatePort BVS Exit STRB r0, Server_ReplyPort LDR r1, Server_Station LDR r2, Server_Net ADR r3, Buffer MOV r4, #?Buffer ; Length of buffer SWI XEconet_CreateReceive BVS DeAllocateExit MOV r8, r0 ; Preserve the RxHandle LDR r1, [sp, #0] ; Address of text string to copy ADR r4, Buffer ; Get buffer to copy into MOV r5, #0 ; Index into Tx Buffer LDRB r0, Server_ReplyPort STRB r0, [r4, r5] ; Send the port for the server CopyLoop ADD r5, r5, #1 CMP r5, #?Buffer ; Have we run out of buffer? BHS BufferOverflow LDRB r0, [r1], #1 ; Pick up byte and move to next one CMP r0, #" " ; Is this a control character? MOVLT r0, #CR ; Terminate as the server expects STRB r0, [r4, r5] BGE CopyLoop ; Loop back for the next byte ADD r5, r5, #1 ; Set entry conditions for Tx MOV r0, #0 MOV r1, #EntryPort ; A constant LDR r2, Server_Station LDR r3, Server_Net LDR r6, Server_TxDelay LDR r7, Server_TxCount SWI XEconet_DoTransmit BVS DeAllocateExit TEQ r0, #Status_Transmitted BEQ WaitForReply ConvertEconetError ADR r1, Buffer ; Convert status and exit MOV r2, #?Buffer SWI XEconet_ConvertStatusToError B DeAllocateExit WaitForReply MOV r0, r8 ; Receive handle LDR r1, Server_RxDelay MOV r2, #0 ; Don't allow ESCape SWI XEconet_WaitForReception BVS DeAllocateExit TEQ r0, #Status_Received BNE ConvertEconetError LDR r0, Buffer ; Get server return code CMP r0, #0 ; Has there been an error? ADR r0, Buffer ; Get address of reply BNE DeAllocateExit ; Yes, process error LDRB r1, [r0, #4] ; Load server's port STRB r1, Server_CommandPort Exit STRVS r0, [sp, #0] ; Poke error into return regs LDMFD sp!, {r0-r8,pc} ; Return to caller BufferOverflowError DCD ErrorNumber_BufferOverflow DCB Command too long for buffer", 0 ALIGN BufferOverflow ADR r0, BufferOverflowError DeAllocateExit MOV r1, r0 ; Preserve the original error LDRB r0, Server_ReplyPort SWI XEconet_DeAllocatePort MOV r0, r1 ; Ignore deallocation errors CMP pc, #&80000000 ; Set V B Exit ; Exit through common point
Points to notice in the example are:
It should be noted that the routine uses and manipulates global state as well as taking specific input and returning specific output.
To allow Econet based programs to be kinder to other applications within the machine, it is possible for your program to be 'notified' when either a reception occurs or a transmission completes. This means that other applications can be using the time that your program would have spent polling, either inside Econet_DoTransmit or inside Econet_WaitForReception. This 'notification' is carried by an event. There are separate events for reception and for completion of transmission. These two events are:
14 | Event_Econet_Rx |
15 | Event_Econet_Tx |
On entry to the event vector:
The status for receive will always be Status_Received, but for transmit it will indicate how the transmission completed:
0 | Status_Transmitted |
1 | Status_LineJammed |
2 | Status_NetError |
3 | Status_NotListening |
4 | Status_NoClock |
9 | Status_Received |
These events can be enabled and disabled in the normal way using OS_Byte calls.
If your program is a client of the Wimp then all your event routine need do is set the Wimp poll word non-zero when the event happens; see the chapter entitled PollWord_NonZero 13.
Event TEQ r0, #Event_Econet_Rx TEQNE r0, #Event_Econet_Tx MOVNE pc, lr ; If not, exit as fast as possible STMFD sp!, {lr} ; Must preserve all regs for others ADR r14, WimpPollWord STR pc, [r14] ; Set flag with non-zero value LDMFD sp!, {pc} ; Return, without claiming vector
Since the interfaces required for reception and transmission can be called from within event routines, you can set up background tasks that make full use of the facilities offered by Econet. Note that it is important to check that the handle offered in the event belongs to your program, since there may well be many programs using this facility. The example given below is of a simple background server for sending out the time. Not all of the code needed is shown, just the event routine:
Start STMFD sp!, {r0-r4,lr} MOV r0, #EventV ; The vector we want is EventV ADR r1, Event ; Where to goto when it happens MOV r2, #0 ; Required so that we can release SWI XOS_Claim MOVVC r0, #14 ; Enable event MOV r1, #Event_Econet_Rx SWIVC XOS_Byte MOVVC r0, #14 ; Enable event MOV r1, #Event_Econet_Tx SWIVC XOS_Byte MOVVC r0, #CommandPort ; First open the reception MOV r1, #0 ; From any station MOV r2, #0 ; From any net ADR r3, Buffer MOV r4, #?Buffer SWIVC XEconet_CreateReceive STRVC r0, RxHandle STRVS r0, [sp] LDMFD sp!, {r0-r4,pc} Event TEQ r0, #Event_Econet_Rx BNE LookForTx LDR r0, RxHandle ; Get our global state TEQ r0, r1 ; Is it for us? MOVNE r0, #Event_Econet_Rx MOVNE pc, lr ; If not, exit as fast as possible STMFD sp!, {r3-r7} ; Only R0, R1 and R2 are free for use MOV r0, r1 ; Receive handle SWI XEconet_ReadReceive ; R4.R3 is the reply address BVS Exit MOV r6, r3 ; Save the station number for later MOV r0, #Module_Claim MOV r3, #8 + 5 ; Two words and five bytes required SWI XOS_Module ; Memory MUST come from RMA BVS Exit ADD r1, r2, #8 ; Get the address of the 5 bytes MOV r0, #3 ; Set OS_Word reason code STRB r0, [r1] ; Read as a five byte time MOV r0, #14 ; Read from the real time clock SWI XOS_Word BVS Exit MOV r0, #0 ; Flag byte MOV r3, r4 ; Net number MOV r4, r1 ; Get the address of the 5 bytes LDRB r1, [r5] ; The reply port the client sent MOV r2, r6 ; Station number MOV r5, #5 ; Number of bytes to send MOV r6, #ReplyCount MOV r7, #ReplyDelay SWI XEconet_StartTransmit BVS Exit SUB r4, r2, #8 ; R4 now in R2 STR r0, [r4, #4] ; Save TxHandle in record ADR r1, TxList ; Address of the head of the list LDR r2, [r1, #0] ; Head of the list STR r2, [r4, #0] ; Add the list to new record STR r4, [r1, #0] ; Make this record the list head# MOV r0, #CommandPort ; Now re-open the reception MOV r1, #0 ; From any station MOV r2, #0 ; From any net ADR r3, Buffer MOV r4, #?Buffer SWI XEconet_CreateReceive STRVC r0, RxHandle Exit LDMFD sp!, {r3-r7, pc} ; Return claiming vector LookForTx TEQ r0, #Event_Econet_Tx MOVNE pc, lr STMFD sp!, {r3, lr} ; Get two extra registers ADR r3, TxList ; The address of the head of list LDR r14, [r3] ; The first record in the list B StartLooking NextTx MOV r3, r14 ; Search the next list entry LDR r14, [r3] ; Get the link address StartLooking CMP r14, #0 ; Is this the end of the list? MOVLE r0, #Event_Econet_Tx ; Restore entry conditions LDMLEFD sp!, {r3, pc} ; Return, continuing to next owner LDR r0, [r14, #4] ; Get the handle for this record TEQ r0, r1 ; Is this event one of ours? BNE NextTx ; No, try next record in list LDR r2, [r14] ; Get the remainder of the list STR r2, [r3] ; Remove this record from list MOV r2, r14 ; The record address for later SWI XEconet_AbandonTransmit MOV r0, #Module_Free SWI XOS_Module ; Return memory to RMA, ignore error LDMFD sp!, {r3, lr, pc} ; Return, claiming vector
This program also illustrates some of the more advanced features of Econet. In particular; it shows the ability to specify reception control blocks that can accept messages from more than one machine, or on more than one port. Receive control blocks like this are referred to as wild, as in wild card matching used in file name look up. Specifying either the station or net number (usually both) as zero means 'match any'. The same is true of the port number, although this facility is much less useful! This wild facility does not mean that more than one packet can be received, but rather that more than one particular packet will be acceptable. Once a packet has been received, the RxCB has Status_Received and is no longer open.
It is worth noting an implementation detail here. Receive control blocks are kept by the Econet software in a list, when an incoming scout has been received the list is scanned to find the first RxCB that matches it. To ensure that things go as one would expect the Econet software that implements the SWI Econet_CreateReceive always adds wild RxCBs to the tail of the list, and normal RxCBs to the middle of the list (between the normal and the wild ones). This ensures that when packets arrive they will be checked for exact matches before wild matches, and that if there is more than one acceptable RxCB then the one used will be the one that was opened first, ie first in first served.
As a complement to this concept of wild receive control blocks there are broadcast transmissions. A broadcast has both its destination station and net set to &FF, it can then be received by more than one machine. To achieve this it does not use the normal four way handshake, it is in fact a single packet. On the NetMonitor it would look something like this:
FFFF1200809F5052494E54200100
The broadcast address at the beginning (&FF, &FF), the source station and net (&12, &00), the control byte (&80), and the port (&9F) are the same as a normal scout frame, but then the data follows, in this case eight bytes.
Although the Econet software within RISC OS can transmit and receive broadcast messages of up to 1020 bytes (RISC OS 2) or 1024 bytes (later versions), other machines on Econet can't cope with messages of more than eight bytes without getting confused; this confusion causes them to corrupt such broadcasts. These other machines include things like FileStores and bridges, so beware! It is possible to transmit and/or receive zero to eight bytes without them being corrupted, but only broadcasts of exactly eight bytes can be received by BBC or Master computers, as well as being transported from net to net by bridges.
Transmitting a broadcast is exactly the same as transmitting a normal packet, all you need to do is set the destination station and net to &FF (not to -1).
Versions of RISC OS after 2.00 support a wider range of broadcasts, allowing local broadcasts (which are only seen on the local net) and long broadcasts (broadcasts of more than eight bytes, which new bridges will recognise and correctly propagate). To use these, set the station number to &FF, and the net number as follows:
Net | Range | Size |
---|---|---|
&FF | Global | Small (8 bytes maximum) |
&FE | Global | Long (1020/1024 bytes maximum) |
&FD | Local | Long (1020/1024 bytes maximum) |
&FC | reserved | reserved |
Note that local long broadcasts (ie net = &FD) are ignored by existing machines and bridges, and will always work.
Broadcasts don't return the status Status_NotListening, since there is no way for the transmitting station to determine whether or not its broadcast was received. Broadcasts are basically designed for locating resources, ie to transmit your desire to know about a particular class of thing. Anything recognising the broadcast will reply, so you know what's what and where it is. NetFS uses broadcast to find file servers by name, and NetPrint uses broadcast to find printer servers. The example broadcast packet shown above contains the ASCII text 'PRINT ' and is, not surprisingly, a request for all printer servers to respond.
When transmissions take place, the destination address is checked to see if it is the local machine (ie a transmission to your own machine). If this is the case then no access to the Econet network will take place, and if a suitable receive control block exists the data is transferred directly from the transmit buffer to the receive buffer. Local loopback is most important for Wimp-based server programs, as it allows them to offer their services to the local station as easily as to all other stations on the Econet.
Broadcasts are also subject to local loopback, but differ slightly in that even if local loopback takes place access to the Econet network will still occur. This is to ensure the semantics of broadcasts. This does however cause a slight problem, in that a broadcast can be initiated to the local station via local loopback and succeed, but still fail externally with - for example - Status_NoClock. This is a slight semantic deviation that you must bear in mind when writing software that may communicate with itself or other software running on your machine by using broadcasts.
The other problem that can occur with local loopback is premature reception, caused by transmission and reception using the same port (whether by accident, or as a feature of the protocol's design), and the length of the transmission being less than or equal to the length of the receive buffer. For example to communicate with an Econet Bridge to find out if a particular net exists code like this will generally work:
10 SYS "Econet_AllocatePort" TO port% 20 SYS "Econet_CreateReceive", port%, 0, 0, RxBuffer%, 10 TO handle% 30 $TxBuffer% = "Bridge" 40 TxBuffer%?6 = port% 50 TxBuffer%?7 = NetToTestFor% 60 SYS "Econet_DoTransmit", &83, &9C, &FF, &FF, TxBuffer%, 8, 5, 5 80 SYS "Econet_WaitForReception", handle%, 10, 0 TO status% 90 IF status% = 9 THEN 100 PRINT "Net number ";STR$(NetToTestFor%);" exists." 110 ENDIF 120 SYS "Econet_DeAllocatePort",port%
However, when the port allocator returns port &9C the program will be subject to unexpected local loopback, and the broadcast will be received internally as well as transmitted externally. This will cause the program to incorrectly report a reception from the bridge, and to interpret it as a reply indicating the existence of the desired net. The most effective way to prevent this is to only create the receive control block after the transmission has completed. In the case above you could simply change line 20 to be line 70. In general it is not acceptable to transmit a request before opening the receive control block for the reply; however, some pre-existing protocols force the issue.
It is worth noting that the use of local loopback in the Wimp environment does require that the polling of receptions and transmissions be interleaved with calls to Wimp_Poll. If this is not done, although the data will be transferred, no notice will be taken because control will not be transferred to the receiving program.
Local loopback with zero length packets will cause the machine to lock up.
Immediate operations are not subject to local loopback.
There is a second class of network operations called immediate operations. These operations don't require the explicit co-operation of the destination machine; instead the co-operation is provided by the Econet software in that machine. Immediate operations are similar semantically to normal transmissions but, because they have no need for a port number, have a type instead of a flag; and most also require an extra input value. They have a separate pair of SWI calls to cause them to happen: Econet_StartImmediate and Econet_DoImmediate.
The call Econet_StartImmediate returns a transmit handle in exactly the same way as Econet_StartTransmit and that handle should be polled and abandoned in the same way. The call Econet_DoImmediate returns a status just as Econet_DoTransmit does.
There are nine types of immediate operations:
1 | Econet_Peek | Copy memory from the destination machine |
2 | Econet_Poke | Copy memory to the destination machine |
3 | Econet_JSR | Cause JSR/BL on the destination machine |
4 | Econet_UserProcedureCall | Execute User remote procedure call |
5 | Econet_OSProcedureCall | Execute OS remote procedure call |
6 | Econet_Halt | Halt the destination machine |
7 | Econet_Continue | Continue the destination machine |
8 | Econet_MachinePeek | Machine peek of the destination machine |
9 | Econet_GetRegisters | Return registers from the destination machine |
The last one, Econet_GetRegisters, can only be transmitted by or received on RISC OS based machines, whereas all the others can be transmitted or received by BBC or Master series computers. The reason for this is that Econet_GetRegisters is specific to the ARM processor.
As noted earlier, Immediate operations are not subject to local loopback.
The poke operation is very similar to a transmit, in that data is moved from the transmitting station to the receiving station. The difference is that the address at which the data is received is supplied by the transmitting station. Peek is the inverse of poke; data is moved from the receiving station into the transmitting station.
Before the receiving station allows the data to be transferred (in or out), it validates the address range supplied by the transmitting station. This validation - done using the SWI XOS_ValidateAddress - takes place in an IRQ process, so having IRQs disabled will affect a machine's ability to be peeked or poked.
JSR, UserProcedureCall, and OSProcedureCall are all very similar. They send a small quantity of data, referred to as the argument buffer or arguments, to the destination machine; they then force it to execute a particular section of code. When received a JSR actually does a BL to the address given in R1, whereas UserProcedureCall and OSProcedureCall cause events to occur. These events are:
8 | Event_Econet_UserRPC |
16 | Event_Econet_OSProc |
After reception the arguments are buffered so that they may be used by the code that is called, either directly by a BL or indirectly via an event. The format of the arguments buffer is as follows: word 0 is the length (in bytes) of the arguments, then the arguments follow this first word and may be null (ie the length may be zero).
The conditions on entry to the event code are:
R0 = Event number (either Event_Econet_UserRPC or Event_Econet_OSProc)
R1 = Address of the argument buffer
R2 = RPC number (passed in R1 on the transmitting station)
R3 = Station that sent the RPC
R4 = Net that sent the RPC
The conditions on entry to code that is BL'd to for a JSR are:
R1 = Address of the argument buffer
R2 = Address of the code being executed
R3 = Station that sent the JSR
R4 = Net that sent the JSR
The format of the argument buffer is exactly the same in all cases. If, in the case of a JSR, the call address transmitted from the remote station is -1 (&FFFFFFFF) then the execution address will be the argument buffer itself; this means that relocatable ARM code can be sent as a JSR. Registers R0 to R4 can be used as they are preserved by the Econet software, and R13 can also be used as a full descending stack.
The transmission of Econet_OSProcedureCall is intended for use solely by system software, and is only documented here for completeness. The transmission of Econet_JSR is only provided as a compatibility feature to allow interworking with BBC and Master computers.
The Econet_UserProcedureCall is the best method for this style of communications. It does however have some restrictions. The first of these is the most important - it is executed in the destination machine as an event caused by an interrupt, and so it has all the normal restrictions applied to interrupt code. This means that code directly executed as a result of Event_Econet_UserRPC must be fast and clean, and must not call any of the normal input or output SWI routines nor call the filing system, either directly or indirectly. This is paramount if the integrity of the destination machine is to be ensured. However, you can copy away the arguments passed and signal to a foreground task (by altering a flag) that the procedure call has arrived. It is most important that you copy the arguments away, because the buffer that they are in is only valid for the duration of the event call. This means that R1 will point to the arguments whilst you are processing the event, but afterwards the argument buffer may be overwritten. If the requirements for the processing of the call are small then it is possible to do it all within the event. An example of this is a modification of the program presented earlier that returned the time. This new program sends the time in response to a User RPC, rather than a normal packet:
Start MOV r0, #EventV ; The vector we want is EventV ADR r1, Event ; Where to goto when it happens MOV r2, #0 ; Required so that we can release SWI XOS_Claim MOVVC r0, #14 ; Enable event STRVC r0, ClaimedFlag ; Set it to a non-zero value MOV r1, #Event_Econet_UserRPC SWIVC XOS_Byte MOVVC r0, #14 ; Enable event MOV r1, #Event_Econet_Tx SWIVC XOS_Byte MOV pc, lr Event TEQ r0, #Event_Econet_UserRPC BNE LookForTx TEQ r2, #RPC_SendTime ; Is it for us? MOVNE pc, lr ; If not, exit as fast as possible LDR r0, [r1, #0] ; Get size of arguments TEQ r0, #1 ; Check that it is right MOVNE r0, #Event_Econet_UserRPC ; Restore exit registers MOVNE pc, lr ; If not, exit as fast as possible STMFD sp!, {r5-r7} ; Only R1 to R4 are free for use ; R4.R3 is the reply address MOV r6, r3 ; Save the station number for later MOV r5, r1 ; Preserve arguments pointer MOV r0, #Module_Claim MOV r3, #8 + 5 ; Two words and five bytes required SWI XOS_Module ; Memory MUST come from RMA BVS Exit ADD r1, r2, #8 ; Get the address of the 5 bytes MOV r0, #3 ; Set OS_Word reason code STRB r0, [r1] ; Read as a five byte time MOV r0, #14 ; Read from the real time clock SWI XOS_Word BVS Exit MOV r0, #0 ; Flag byte MOV r3, r4 ; Net number MOV r4, r1 ; Get the address of the 5 bytes LDRB r1, [r5, #4] ; The reply port the client sent MOV r2, r6 ; Station number MOV r5, #5 ; Number of bytes to send MOV r6, #ReplyCount MOV r7, #ReplyDelay SWI XEconet_StartTransmit BVS Exit SUB r4, r2, #8 ; R4 now in R2 STR r0, [r4, #4] ; Save TxHandle in record ADR r1, TxList ; Address of the head of the list LDR r2, [r1, #0] ; Head of the list STR r2, [r4, #0] ; Add the list to new record STR r4, [r1, #0] ; Make this record the list head Exit LDMFD sp!, {r5-r7, pc} ; Return claiming vector LookForTx TEQ r0, #Event_Econet_Tx MOVNE pc, lr ; This event has only R0 to R2 STMFD sp!, {r3, lr} ; Get two extra registers ADR r3, TxList ; The address of the head of list LDR r14, [r3] ; The first record in the list B StartLooking NextTx MOV r3, r14 ; Search the next list entry LDR r14, [r3] ; Get the link address StartLooking CMP r14, #0 ; Is this the end of the list? MOVLE r0, #Event_Econet_Tx ; Restore entry conditions LDMLEFD sp!, {r3, pc} ; Return, continuing to next owner LDR r0, [r14, #4] ; Get the handle for this record TEQ r0, r1 ; Is this event one of ours? BNE NextTx ; No, try next record in list LDR r2, [r14] ; Get the remainder of the list STR r2, [r3] ; Remove this record from list SWI XEconet_AbandonTransmit MOV r0, #Module_Free MOV r2, r14 ; The record address SWI XOS_Module ; Return memory to RMA, ignore error LDMFD sp!, {r3, lr, pc} ; Return, claiming vector
You will notice how much simpler this program is when compared to the program shown earlier.
There are five defined OS procedure calls for which only two have implementations under RISC OS. The five are:
0 | Econet_OSCharacterFromNotify |
1 | Econet_OSInitialiseRemote |
2 | Econet_OSGetViewParameters |
3 | Econet_OSCauseFatalError |
4 | Econet_OSCharacterFromRemote |
Econet_OSCharacterFromNotify causes the character received to be inserted into the keyboard buffer; the code that does so looks like this:
InsertCharacter ; R1 points at the argument buffer MOV r0, #138 ; Insert into buffer OS_Byte LDRB r2, [r1, #4] ; Get character from buffer MOV r1, #0 ; Buffer is keyboard SWI XOS_Byte
Whilst the desktop is running the NetFiler module provides a different handler for characters from notify. It bundles them up by station, and when none have been received for a while sends them as a Wimp message, displaying them using Wimp_ReportError. For more information see the documentation of Message_Notify on Message_Notify (&40040).
Econet_OSCauseFatalError does exactly what its name implies. In fact it calls SWI OS_GenerateError directly from the event routine; normally this would be illegal, but since this is what the RPC is for, that is what it does. It should be observed that this can have a disastrous effect on the integrity of the machine and is not a recommended action; it is provided only for compatibility reasons.
Halt and continue are only acted upon by BBC and Master series machines; there is no implementation for receiving halt or continue on RISC OS machines or RISC iX machines.
Machine peek is similar to peek, except that it is not possible to specify the address to be peeked, but rather four bytes are returned that identify the machine that is being machine peeked. Machine peek is used by some of the system software in RISC OS to quickly decide if a particular machine is present or not. The four bytes returned by machine peek are as follows:
Byte(s) | Value |
---|---|
1 and 2 | Machine type number |
3 | Software version number |
4 | Software release number |
Machine type numbers are as follows:
&0000 | Reserved |
&0001 | Acorn BBC Micro Computer (OS 1 or OS 2) |
&0002 | Acorn Atom |
&0003 | Acorn System 3 or System 4 |
&0004 | Acorn System 5 |
&0005 | Acorn Master 128 (OS 3) |
&0006 | Acorn Electron (OS 0) |
&0007 | Acorn Archimedes (OS 6) |
&0008 | Reserved for Acorn |
&0009 | Acorn Communicator |
&000A | Acorn Master 128 Econet Terminal |
&000B | Acorn FileStore |
&000C | Acorn Master 128 Compact (OS 5) |
&000D | Acorn Ecolink card for Personal Computers |
&000E | Acorn UNIX workstation |
&000F to &FFF9 | Reserved |
&FFFA | SCSI Interface |
&FFFB | SJ Research IBM PC Econet interface |
&FFFC | Nascom 2 |
&FFFD | Research Machines 480Z |
&FFFE | SJ Research File Server |
&FFFF | Z80 CP/M |
The software version and release numbers are stored in two bytes. These two bytes are encoded in packed BCD (Binary Coded Decimal) and represent a number between 0 and 99. The easiest way to display packed BCD is to print it as if it was hexadecimal data:
ReportStationVersion MOV r2, r0 ; Station number in R0 MOV r3, r1 ; Net number in R1 MOV r0, #Econet_MachinePeek ADR r4, Buffer MOV r5, #?Buffer MOV r6, #40 ; Count MOV r7, #5 ; Delay SWI XEconet_DoImmediate MOVVS pc, lr TEQ r0, #Status_Transmitted BEQ PrintVersion TEQ r0, #Status_NotListening ; from Machine peek MOVEQ r0, #Status_NotPresent ; return as "Not present" ADR r1, Buffer MOV r2, #?Buffer SWI XEconet_ConvertStatusToError MOV pc, lr PrintVersion LDR r3, [r2] ; Buffer address on exit from SWI MOV r0, r3, ASR #24 ; Get top byte ADR r1, Buffer MOV r2, #?Buffer SWI XOS_ConvertHex2 ; Print BCD as hex SWIVC XOS_Write0 ; Display output SWIVC XOS_WriteI+"." ; Divide release from version number MOVVC r0, r3, ASR #16 ; Get version number in place ANDVC r0, r0, #&FF ; Only the version number ADRVC r1, Buffer MOVVC r2, #?Buffer SWIVC XOS_ConvertHex2 ; Print BCD as hex SWIVC XOS_Write0 ; Display output MOV pc, lr
We recommend that when using Econet_MachinePeek you use a Count of 40 and a Delay of 5.
Econet_GetRegisters is similar to machine peek, in that a fixed amount of information is returned from the destination machine; in this case it is 80 bytes (20 words). The registers are returned in the following order: R0 to R14, PC plus PSR, R13_irq, R14_irq, R13_svc, and R14_svc. The FIQ registers are not returned because they are used by the Econet software, and so would always be the same, and of no interest since they would reflect the state of the part of the Econet software that transmits data. It is worthwhile aligning the receive buffer for a machine peek so that each of the 20 words is on a word boundary; this makes loading them easier.
Because these immediate operations can be quite intrusive it is possible to prevent their reception by manipulating an internal variable of the Econet software. There is one bit in this internal variable for each operation, and you can set or clear each bit. There is also a default value for each bit which is held in CMOS RAM. The SWI that allows you to manipulate this internal variable is Econet_SetProtection. These bits are held in a single word; the bit assignments are as follows:
Bit | Immediate operation protected against |
---|---|
0 | Peek |
1 | Poke |
2 | Remote JSR |
3 | User procedure call |
4 | OS procedure call |
5 | Halt |
6 | Continue - must be zero on RISC OS computers |
7 | Machine peek - must be zero on RISC OS computers |
8 | Get registers |
9 - 30 | Reserved - must be zero. |
31 | Write new value to the CMOS RAM |
To protect against or disable the reception of a particular immediate operation, the appropriate bit should be set in the internal variable. The SWI Econet_SetProtection call replaces the OldValue with the NewValue, The NewValue is calculated like this:
When the Econet software is started up (as a result of Ctrl-Break, or *RMReInit) then the value held in CMOS RAM will be used to initialise the internal variable. To alter the value held in CMOS RAM the entry value of R0 to SWI Econet_SetProtection should have bit 31 set, which causes the resultant value to be written not only to the internal variable, but also to the CMOS RAM. To read the current value you should use SWI Econet_SetProtection with R0=0, and R1=&FFFFFFFF.
To establish what your station number is and which net you are connected to (if you have more than one), the Econet software provides a call to return these two values: Econet_ReadLocalStationAndNet. If you don't have more than one net then the net number (returned in R1) will be zero.
The local net number is in fact obtained from a bridge whenever the Econet module is initialised (eg when the machine is turned on). If this fails, say because there is no clock or the bridge is not switched on, then the local net number is reported as zero.
These values are the same as those reported by *Help Station (in fact *Help Station calls SWI Econet_ReadLocalStationAndNet to get the values).
To ensure that all Econet oriented software presents a consistent user interface there is a SWI call to read a station and/or net number from a supplied string. This call, Econet_ReadStationNumber, is used by both NetFS and NetPrint for all their command line processing. In the case of software that has a concept of a current station (and net) number the return value of -1 should mean 'use the existing value' - this is how *FS works, for example. Where there isn't a current value, as would be expected in a transient command such as *Notify, the return of -1 for the station number should be treated as an error and the return of -1 as a net number should imply the use of zero as a net number. The following is the beginning (and some of the end) of a transient command:
CommandStart LDRB r0, [r1] ; Check the first argument exists TEQ r0, #0 ; Zero means no arguments BEQ SyntaxError ; Exit with error SWI XEconet_ReadStationNumber MOVVS pc, lr ; Must be able to cope CMP r2, #-1 ; No station number given BEQ NoStationNumberError CMP r3, #-1 ; No net number given MOVEQ r3, #0 ; Means use zero MOV pc, lr SyntaxError ADR r0, ErrorGetRegsSyntax ORRS pc, lr, #VFlag ErrorGetRegsSyntax DCD ErrorNumber_Syntax DCB "Syntax: *Command <Station number>" DCB 0 ALIGN NoStationNumberError ADR r0, ErrorUnableToDefault ORRS pc, lr, #VFlag ErrorUnableToDefault DCD ErrorNumber_UnableToDefault DCB "Either a station number or a full" DCB " network address is required" DCB 0 ALIGN
The kernel provides two inverse functions that convert a station and net number pair into a string. See OS_ConvertFixedNetStation and OS_ConvertNetStation for exact details.
The following conventions apply to the various values that the Econet uses:
Station numbers are normally in the range 1 to 254. The station number zero is used in SWI Econet_CreateReceive to indicate that reception may occur from any station. The station number 255 is used in SWI Econet_StartTransmit and in SWI Econet_DoTransmit to indicate that a broadcast is to take place. Station number 255 is also used in SWI Econet_CreateReceive to indicate that reception may occur from any station; you may also use station number zero for this purpose, but its use is deprecated, and may be withdrawn in the future.
Net numbers are normally in the range 1 to 251. The value zero means the local Econet net; in a SWI Econet_CreateReceive it is taken to indicate that reception may occur from any net. The net numbers 255, 254 and 253 are used in SWI Econet_StartTransmit and in SWI Econet_DoTransmit to indicate that a broadcast is to take place. Net number 255 is also used in SWI Econet_CreateReceive to indicate that reception may occur from any station; the use of zero to indicate wild reception is deprecated.
Although RISC OS fully supports top-bit-set net numbers (ie 128 - 251), certain Econet devices - such as bridges - will not propagate them, leading to problems. You should beware of this.
Port numbers are normally in the range 1 to 254, although some values are reserved - as shown in the table below:
Port | Allocation |
---|---|
&54 | DigitalServicesTapeStore |
&99 | FileServerCommand |
&9C | Bridge |
&9E | PrinterServerInquiryReply |
&9F | PrinterServerInquiry |
&A0 | SJ Research *FAST protocol (file server management) |
&AF | SJ Research Nexus net finder reply port |
&B0 | FindServer |
&B1 | FindServerReply |
&B2 | TeletextServerCommand |
&B3 | TeletextServerPage |
&D0 | OldPrinterServerData |
&D1 | PrinterServerData |
&D2 | TCPIPProtocolSuite |
&D3 | SIDFrameSlave |
&D4 | Scrollarama |
&D5 | Phone |
&D6 | BroadcastControl |
&D7 | BroadcastData |
&D8 | ImpressionLicenceChecker |
&D9 | DigitalServicesSquirrel |
&DA | SIDSecondary |
&DB | DigitalServicesSquirrel2 |
&DC | DataDistributionControl |
&DD | DataDistributionData |
&DE | ClassROM |
&DF | PrinterSpoolerCommand |
Port numbers zero and 255 currently have a special meaning: they may be used as arguments to SWI Econet_CreateReceive to indicate that reception may occur regardless of the port number on the incoming packet. This use of zero to indicate wild reception is deprecated, and will be withdrawn in the future.
Flag byte values are in the range 0 to 127 (&7F). When passed in a word to a SWI, bits 8 - 31 inclusive must be zero. Bit 7 is ignored by RISC OS, to maintain compatibility with some older software that used this bit. To clarify, flag bytes &87 and &07 are acceptable as input to a transmission SWI (and both represent the value &07), but &107 is not acceptable. Reception SWIs all return values with bit 7 clear (ie &00 to &7F).
The transmission semantics are simple. When a transmission is started the client's control information (passed in registers) is stored in a record in a linked list within Econet workspace. At regular intervals the list is scanned, and those records that should be actually transmitted at that moment are passed to the FIQ software. When that particular transmission attempt completes the status of the record is changed accordingly. This means that if two transmissions are started at the same time, they will interleave their transmission retries.
When a transmission has completed but failed:
This means that as far as possible the time out time will be the Delay multiplied by the (Count - 1).
Versions of RISC OS after RISC OS 2 have added support for local loopback. Transmissions directed at your own station number will be 'received' if there is an acceptable receive block open by directly copying the data. This applies to broadcast transmissions and wild receptions as well as to calls that explicitly address your machine.
R1 = &48 (reason code)
R1 preserved to pass on (do not claim)
This call is made whenever Econet restarts. It is then up to the Econet software to allocate ports, set up TxCBs and RxCBs, etc.
R1 = &56 (reason code)
R1 preserved to pass on (do not claim)
This call is made whenever Econet is about to leave. At this point the Econet module is already being finalised, and you may not make further calls to it. Resources such as ports, CBs etc are no longer valid, and you may dispose of any relevant local workspace.
This service call is part of the AUN Driver Control Interface, used to interface a network interface's driver module to a protocol module. Third parties wishing to develop network interfaces for use with AUN may obtain further details on request from Acorn.
This service call is part of the AUN Driver Control Interface, used to interface a network interface's driver module to a protocol module. Third parties wishing to develop network interfaces for use with AUN may obtain further details on request from Acorn.
This service call is part of the AUN Driver Control Interface, used to interface a network interface's driver module to a protocol module. Third parties wishing to develop network interfaces for use with AUN may obtain further details on request from Acorn.
R0 = port number
R1 = station number
R2 = net number
R3 = buffer address
R4 = buffer size in bytes
R0 = handle
R2 = 0 if R2 on entry is the local net number
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call creates a Receive Control Block (RxCB) to control the reception of an Econet packet. It returns a handle to the RxCB.
The buffer must remain available all the time that the RxCB is open, as data received over the Econet is read directly from hardware to the buffer. You must not use memory in application space if your program is to run under the Desktop. Instead, you should use memory from the RMA. To do so, claim the memory using OS_Module 6 (see OS_Module 6), and - after abandoning the receive control block - return the space to the RMA using OS_Module 7 (see OS_Module 7).
Econet_ExamineReceive, Econet_WaitForReception, Econet_AbandonAndReadReceive
R0 = handle
R0 = status
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call reads the status of an RxCB, which may be one of the following:
7 | Status_RxReady |
8 | Status_Receiving |
9 | Status_Received |
It returns less information than Econet_ReadReceive, so is faster and corrupts fewer registers. You should use it to poll a reception when not using Econet_WaitForReception.
Econet_CreateReceive, Econet_WaitForReception, Econet_ConvertStatusToString, Econet_ConvertStatusToError
Returns information about a reception, including the size of data
R0 = handle
R0 = status
R1 = 0, or flag byte if R0 = 9 (Status_Received) on exit
R2 = port number
R3 = station number
R4 = net number
R5 = buffer address
R6 = buffer size in bytes, or amount of data received if R0 = 9 on exit (Status_Received)
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call returns information about a reception; most importantly, it tells you how much data was received, if any, and the address of the buffer in which it was placed. The buffer address is the same as that passed to Econet_CreateReceive. You can call this SWI before a reception has occurred.
The status of the RxCB may be one of the following:
7 | Status_RxReady |
8 | Status_Receiving |
9 | Status_Received |
The returned values in R3 and R4 (the net and station numbers) are those of the transmitting station if the status is Status_Received; otherwise they are the same values that were passed in to Econet_CreateReceive.
Econet_CreateReceive, Econet_WaitForReception, Econet_AbandonAndReadReceive
R0 = handle
R0 = status
Interrupts are disabled
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call abandons an RxCB, returning its memory to the RMA. The reception may have completed (R0 = 9 - Status_Received - on exit), in which case the information in the RxCB (such as the sending station number, and the amount of data sent) will be lost. The data in the receive buffer remains unaffected. If the reception is in progress when this SWI is called, then information in the RxCB is lost, as above.
Econet_CreateReceive, Econet_WaitForReception, Econet_AbandonAndReadReceive
None
R0 = handle
R1 = delay in centiseconds
R2 = 0 to ignore Escape; else Escape ends waiting
R0 = status
R1 = 0, or flag byte if R0 = 9 (Status_Received) on exit
R2 = port number
R3 = station number
R4 = net number
R5 = buffer address
R6 = buffer size in bytes, or amount of data received if R0 = 9 on exit (Status_Received)
Interrupts are enabled
Fast interrupts are enabled
Processor is in SVC mode and in USR mode
SWI is not re-entrant
This call repeatedly polls an RxCB (that you have already set up with Econet_CreateReceive) until a reception occurs, or a timeout occurs, or the user interferes (say by pressing Escape). It then reads the status of the RxCB before abandoning it.
The status of the RxCB may be one of the following:
8 | Status_Receiving |
9 | Status_Received |
10 | Status_NoReply |
11 | Status_Escape |
The returned values in R3 and R4 (the net and station numbers) are those of the transmitting station if the status is Status_Received; otherwise they are the same values that were passed in to SWI Econet_CreateReceive.
Note that because this interface enables interrupts it should not be called from within either interrupt service code or event routines.
During the loop when the polling of the RxCB and of Escape takes place, the processor is put in USR mode with IRQs enabled; this allows callbacks to occur.
Econet_ExamineReceive, Econet_ReadReceive, Econet_AbandonReceive, Econet_AbandonAndReadReceive
R0 = index (1 to start with first receive block)
R0 = handle (0 if no more receive blocks)
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
Not defined
This call returns the handles of open RxCBs. On entry R0 is the number of the RxCB being asked for (1, 2, 3...). If the value of R0 is greater than the number of open RxCBs, then the value returned as the handle will be 0, which is an invalid handle.
This call should not be made from an IRQ or event routine as, although it will not fail, errors and omissions are likely to occur in the returned information.
Econet_CreateReceive,
Econet_ReadReceive, Econet_AbandonReceive
None
R0 = flag byte
R1 = port number
R2 = station number
R3 = net number
R4 = buffer address
R5 = buffer size in bytes
R6 = count
R7 = delay in centiseconds
R0 = handle
R1 corrupted
R2 = buffer address
R3 = station number
R4 = net number
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call creates a Transmit Control Block (TxCB) to control the transmission of an Econet packet. It then starts the transmission.
The buffer must remain available all the time that the TxCB is open, as data transmitted over the Econet is read directly from the buffer to hardware. You must not use memory in application space if your program is to run under the Desktop. Instead, you should use memory from the RMA. To do so, claim the memory using OS_Module 6 (see OS_Module 6), and - after abandoning the transmit control block - return the space to the RMA using OS_Module 7 (see OS_Module 7).
The value returned in R4 (the net number) will be the same as that passed in R3 unless that number is equal to the local net number; in that case the net number will be returned as zero.
Econet_PollTransmit, Econet_AbandonTransmit, Econet_DoTransmit
R0 = handle
R0 = status
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call reads the status of a TxCB, which may be one of the following:
0 | Status_Transmitted |
1 | Status_LineJammed |
2 | Status_NetError |
3 | Status_NotListening |
4 | Status_NoClock |
5 | Status_TxReady |
6 | Status_Transmitting |
None
R0 = handle
R0 = status
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call abandons a TxCB, returning its memory to the RMA. The returned status is the same as for Econet_PollTransmit.
None
R0 = flag byte
R1 = port number
R2 = station number
R3 = net number
R4 = buffer address
R5 = buffer size in bytes
R6 = count
R7 = delay in centiseconds
R0 = status
R1 corrupted
R2 = buffer address
R3 = station number
R4 = net number
Interrupts are enabled
Fast interrupts are enabled
Processor is in SVC mode and in USR mode
SWI is not re-entrant
This call creates a TxCB and repeatedly polls it until it finishes transmission, or it exceeds the count of retries. It then reads the final status of the TxCB before abandoning it.
The status of the TxCB may be one of the following:
0 | Status_Transmitted |
1 | Status_LineJammed |
2 | Status_NetError |
3 | Status_NotListening |
4 | Status_NoClock |
The value returned in R4 (the net number) will be the same as that passed in R3 unless that number is equal to the local net number; in that case the net number will be returned as zero.
Note that because this interface enables interrupts it should not be called from within either interrupt service code or event routines.
During the loop when the polling of the TxCB and of Escape takes place, the processor is put in USR mode with IRQs enabled; this allows callbacks to occur.
Econet_StartTransmit, Econet_PollTransmit,
and Econet_AbandonTransmit
No parameters passed in registers
R0 = station number
R1 = net number
Interrupts are enabled
Fast interrupts are enabled
Processor is in SVC mode
SWI is not re-entrant
This call returns a computer's station number and Econet net number. The net number will be zero if there are no Econet bridges present on the network.
For more information, see the chapter entitled Reading your station and net numbers.
None
None
R0 = status
R1 = pointer to buffer
R2 = buffer size in bytes
R3 = station number
R4 = net number
R0 = buffer
R1 = updated buffer address
R2 = updated buffer size in bytes
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call converts a status to a string found in the messages file. This is then copied into RAM, including the station and net numbers, giving a string such as:
Network station 59.254 not listening
If the status given in R0 is invalid (ie not in the range 0 - 14), this will cause a data abort or an address exception. If the station/net number given in R3/R4 is invalid, no station information is given.
Under RISC OS 2 the string is not read from the messages file, but is instead read direct from the ROM.
None
R0 = status
R1 = pointer to error buffer
R2 = error buffer size in bytes
R3 = station number
R4 = net number
R0 = pointer to error block
V flag is set
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call converts a status to a string found in the messages file. This is then copied into RAM, including the station and net numbers, giving a string such as:
Network station 59.254 not listening
If the station/net number given in R3/R4 is invalid, no station information is given.
Finally this call returns an error by setting the V flag, with R0 pointing to the error block.
If you use a buffer address of zero, then the string is left in a buffer in the MessageTrans workspace.
Under RISC OS 2 the string is not read from the messages file, but is instead read direct from the ROM.
No parameters passed in registers
R0 = current protection value
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call reads the current protection word for immediate operations. Various bits in the word, when set, disable corresponding immediate operations:
Bit | Immediate operation |
---|---|
0 | Peek |
1 | Poke |
2 | Remote JSR |
3 | User procedure call |
4 | OS procedure call |
5 | Halt |
6 | Continue - always zero on RISC OS computers |
7 | Machine peek - always zero on RISC OS computers |
8 | Get registers |
9 - 31 | Reserved - must be zero |
Note - This call is deprecated. You should preferably use the call Econet_SetProtection to read the protection word instead of this call.
None
Sets or reads the protection word for immediate operations
R0 = EOR mask word
R1 = AND mask word
R0 = old value
Interrupts are enabled on write-through to CMOS, preserved otherwise
Fast interrupts are enabled
Processor is in SVC mode
SWI is not re-entrant
This call sets the protection word for immediate operations as follows:
Various bits in the word, when set, disable corresponding immediate operations:
Bit | Immediate operation |
---|---|
0 | Peek |
1 | Poke |
2 | Remote JSR |
3 | User procedure call |
4 | OS procedure call |
5 | Halt |
6 | Continue - must be zero on RISC OS computers |
7 | Machine peek - must be zero on RISC OS computers |
8 | Get registers |
9 - 30 | Reserved - must be zero |
31 | Write new value to the CMOS RAM |
Normally this call sets or reads the current value of the word. A default value for this word is held in CMOS RAM.
The most useful values of R0 and R1 are:
Action | R0 | R1 |
---|---|---|
Set current value | new value (0 - &1FF) | 0 |
Read current value | 0 | &FFFFFFFF |
Set new default value | &80000000 + new value | 0 |
You should use this call to read the value of the protection word, rather than Econet_ReadProtection.
Using this call to read is also the preferred method for detecting the presence of the Econet drivers, since doing so can never return an unexpected error. Detecting the error 'No such SWI' allows software dependent upon Econet to report its absence. Example code is given in the Application notes.
None
R1 = address of string to read
R1 = address of terminating space or control character
R2 = station number (-1 for not found)
R3 = net number (-1 for not found)
Interrupts are enabled
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call extracts a station and/or net number from a supplied string. For an example of its use, see the chapter entitled Extracting station numbers from a string.
None
None
--
--
Interrupts are enabled
Fast interrupts are enabled
Processor is in SVC mode
SWI is not re-entrant
This call prints the string 'Acorn Econet' followed by a newline. The string is fetched from a message file with the token 'AcrnEco'. If the Econet network data clock is not present then this call instead prints the string 'Acorn Econet, no clock' followed by a newline. In this case, the token used is 'EcoNClk'.
This call uses OS_Write0 and OS_NewLine, and so cannot be called from within either interrupt service code or event routines.
None
None
R0 = station number
R1 = net number
R2 = 2
R0, R1 preserved
R2 = transport type (0 NOT KNOWN_ 1 INTERNET_ 2 ECONET_ 3 Nexus)
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call is used by clients to determine the underlying transport type to a given station. They can then use this information to determine the optimum transmission strategy to use, based on prior empirical knowledge of the different transport types.
This call is unnamed - but still available by number - in both RISC OS 2 and RISC OS 3 (version 3.00).
None
None
R0 = port number
--
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call releases a port number that was previously claimed by calling Econet_ClaimPort.
You must not use this call for port numbers that have been previously claimed using Econet_AllocatePort; instead, you must call Econet_DeAllocatePort.
None
No parameters passed in registers
R0 = port number
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call allocates a unique port number that has not already been claimed or allocated.
When you have finished using the port number, you should call Econet_DeAllocatePort to make it available for use again.
None
R0 = port number
--
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call deallocates a port number that was previously allocated by calling Econet_AllocatePort.
You must not use this call for port numbers that have been previously claimed using Econet_ClaimPort; instead, you must call Econet_ReleasePort.
R0 = port number
--
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call claims a specific port number. If it has already been claimed or allocated, an error is generated.
When you have finished using the port number, you should call Econet_ReleasePort to make it available for use again.
None
R0 = operation type
R1 = remote address or Procedure number
R2 = station number
R3 = net number
R4 = buffer address
R5 = buffer size in bytes
R6 = count
R7 = delay in centiseconds
R0 = handle
R1 corrupted
R2 = buffer address
R3 = station number
R4 = net number
Interrupts are disabled
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call creates a TxCB and starts an immediate operation. For full details see the chapter entitled Immediate operations.
The buffer must remain available all the time that the TxCB is open, as data transmitted over the Econet is read directly from the buffer to hardware. You must not use memory in application space if your program is to run under the Desktop. Instead, you should use memory from the RMA. To do so, claim the memory using OS_Module 6 (see OS_Module 6), and - after abandoning the transmit control block - return the space to the RMA using OS_Module 7 (see OS_Module 7).
The value returned in R4 (the net number) will be the same as that passed in R3 unless that number is equal to the local net number; in that case the net number will be returned as zero.
None
Creates a TxCB for an immediate operation, polls it, reads its status, and abandons it
R0 = operation type
R1 = remote address or procedure number
R2 = station number
R3 = net number
R4 = buffer address
R5 = buffer size in bytes
R6 = count
R7 = delay in centiseconds
R0 = status
R1 corrupted
R2 = buffer address
R3 = station number
R4 = net number
Interrupts are enabled
Fast interrupts are enabled
Processor is in SVC mode and in USR mode
SWI is re-entrant
This call creates a TxCB for an immediate operation, and repeatedly polls it until it finishes transmission or it exceeds the count of retries. It then reads the final status of the TxCB before abandoning it. For full details see the chapter entitled Immediate operations.
The value returned in R4 (the net number) will be the same as that passed in R3 unless that number is equal to the local net number; in that case the net number will be returned as zero.
Note that because this interface enables interrupts it should not be called from within either interrupt service code or event routines.
During the loop when the polling of the TxCB and of Escape takes place, the processor is put in USR mode with IRQs enabled; this allows callbacks to occur.
Abandons a reception and returns information about it, including the size of data
R0 = handle
R0 = status
R1 = 0, or flag byte if R0 = 9 (Status_Received) on exit
R2 = port number
R3 = station number
R4 = net number
R5 = buffer address
R6 = buffer size in bytes, or amount of data received if R0 = 9 on exit (Status_Received)
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call abandons an RxCB, returning its memory to the RMA. It also returns information about the reception; most importantly, it tells you how much data was received, if any, and the address of the buffer in which it was placed. The buffer address is the same as that passed to Econet_CreateReceive. You can call this SWI before a reception has occurred.
The status of the RxCB may be one of the following:
7 | Status_RxReady |
9 | Status_Received |
The returned values in R3 and R4 (the net and station numbers) are those of the transmitting station if the status is Status_Received; otherwise they are the same values that were passed in to Econet_CreateReceive.
This call is not available in RISC OS 2, nor in RISC OS 3 (version 3.00).
Econet_CreateReceive, Econet_ReadReceive, Econet_AbandonReceive
Returns the version of software for the underlying transport to a given station
R0 = station number
R1 = net number
R0, R1 preserved
R2 = version number × 100 (eg 547 for version 5.47)
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call is used by clients to determine the version of software that handles the underlying transport to a given station. If both R0 and R1 are set to zero on entry, this call instead returns the version number of the top-level software to which RISC OS passes the Econet SWIs.
This call is not available in RISC OS 2, nor in RISC OS 3 (version 3.00).
None
None
Returns the state of the underlying transport to a given station
R0 = station number
R1 = net number
R0, R1 preserved
R2 = transport state (0 FULLY FUNCTIONAL_ 1 no clock signal)
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call returns the state of the underlying transport to a given station. The state returned is transport type dependent, but you may always assume that a value of zero means that the transport is fully functional.
You should only use the returned value as a hint to the exact state; in other words, it is suitable for display but not for decision making. Using this call is no substitute for proper error handling; to determine if a particular transmit will fail, you must do the transmit and be prepared for it to fail.
None
Returns the maximum packet size recommended on the underlying transport to a given station
R0 = station number
R1 = net number
R0, R1 preserved
R2 = maximum permitted packet size, in bytes
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call returns the maximum recommended packet size on the underlying transport to a given station. Larger packets will not necessarily be rejected, but their use is not recommended. The size returned is transport type dependent.
This call is intended for use by modules supplying protocols; you do not need to use it in application software. For maximum efficiency the protocol module should negotiate the packet size once. Since the recommended packet size may differ between the stations at either end of a transmission, the protocol module should interrogate both stations and take the lower value returned.
None
Returns the name of the underlying transport to a given station
R0 = station number
R1 = net number
R0, R1 preserved
R2 = pointer to null terminated name of transport
Interrupt status is unaltered
Fast interrupts are enabled
Processor is in SVC mode
SWI is re-entrant
This call returns the name of the underlying transport to a given station. You can use this to insert the transport name into (for example) a status conversion.
None
None
The only * Command the Econet module responds to is *Help Station, which displays the current net and station numbers of the machine. It also displays a 'No clock' message if applicable. For more details of the *Help command, see *Help.
The following code is the preferred way of testing for the presence of the Econet drivers. It calls the SWI Econet_SetProtection with R0 and R1 set such that the call attempts to read the Econet protection word; doing so can never return an unexpected error. Detecting the error 'No such SWI' allows software dependent upon Econet to report its absence by generating an error; note the use of MessageTrans to do so:
STMFD sp!, {r0-r7,lr} MOV r0, #0 MVN r1, #0 SWI XEconet_SetProtection BVC ExitFindEconet LDR r2, ErrorNumber_NoSuchSWI LDR r0, [r0] TEQ r0, r2 BNE ExitFindEconet ADR r0, Error_NoEconet MOV r1, #0 ; No message file - use global MOV r2, #0 ; No buffer - use internal one MOV r3, #0 MOV r4, #0 ; No parameters MOV r5, #0 MOV r6, #0 MOV r7, #0 SWI XMessageTrans_ErrorLookup ExitFindEconet STRVS r0, [sp,#0] LDMFD sp!, {r0-r7,pc} ErrorNumber_NoSuchSWI DCD &000001E6 Error_NoEconet DCD &00000312 DCB "NoEco", 0