*_Useful Procedures and Functions_* _Reading file information_ It is useful to be able to check if a file exists, and read the information relating to that file. The following function will do this: DIM ctrl% 31,name% 127 X%=ctrl%:Y%=X%DIV256 REM Returns file type, file info in X%!... REM -------------------------------------- DEFFNfile(A$,A%):$name%=A$:?X%=name%:X%?1=name%DIV256:=(USR&FFDD)AND&FF Calling the function with FNfile(filename$,5) returns the file type as: 0: File does not exist 1: File exists 2: Directory exists The file's catalogue information is returned as: X%!2 - load address X%!6 - execution address X%!10 - file length X%?14 - file attributes X%!15 - file creation date _Finding out which filing system is selected_ This allows you to alter the behaviour of a program depending on the filing system selected. REM Get current filing system number REM -------------------------------- DEFFNfs:LOCAL A%,Y%,E%:=(USR&FFDA)AND&FF Some of the values the function returns are: 1 - 1200 baud tape 5 - Net 2 - 300 baud tape 6 - Teletext 3 - ROM 7 - IEEE 4 - Disk 8 - ADFS _Fast file handling_ This procedure allows you to read or write exactly the number of bytes you require from a file in one exchange with the file server. This can make network file handling much faster than using BGET or BPUT as each BGET or BPUT sends a whole network packet for just a single byte. It can also be faster than on disk as disk files are always buffered to 256 bytes and this us not necessarily the number you want. DIM ctrl% 31:X%=ctrl%:Y%=X%DIV256 REM General OSGBPB call REM ------------------- DEFPROCgbpb(A%,chn%,addr%,num%,ptr%) ?X%=chn%:X%!1=addr%:X%!5=num%:X%!9=ptr%:CALL &FFD1:ENDPROC The procedure is called as: PROCgbpb(action, handle, address, number, pointer) The 'action' parameter has the following effects: 1 - Write bytes to file using the supplied pointer 2 - Write bytes to file at the current PTR 3 - Read bytes from file using the supplied pointer 4 - Read bytes from file at the current PTR You can check whether you went past the end of the file by checking X%!5 after the call. This contains the number of bytes which were not transfered. This will be non-zero if you went outside the file. _Reading the current disk, directory and library name_ The following function will return the name of the currently selected disk, directory or library. DIM ctrl% 31,name% 127:X%=ctrl%:Y%=X%DIV256 REM Return current disk (5), directory (6) or library (7) name REM ---------------------------------------------------------- DEFFNgbpb(A%):X%!1=name%:CALL&FFD1:A%=name%+((1+?name%)AND((A%AND-2)=6)) A%?(1+?A%)=13:=$(A%+1) When calling the function: FNgbpb(5) returns the disk name FNgbpb(6) returns the current directory name FNgbpb(7) returns the current library name The NFS always returns the names padded with spaces - 16 characters for the disk name, and 10 characters for the directory and library name. _Cataloguing the currently selected directory_ The following code allows you to read the filenames in the currently selected directory. It will work with NFS, DFS and ADFS with no changes and will work with any other well-behaving filing system. Often programs supplied on disk offer the option to catalogue, but they fail to work on the network because they assume the filenames will be seven characters long, whereas then could be up to ten or more characters. The data block used to read the filenames needs to be long enough to receive the filenames read. Most filenames encountered on BBC systems are a maximum of 10 characters long, so data% needs to point to at least 12 bytes - ten characters for the filename, one byte for the name length and one byte for the terminator. However, there is nothing that specifies that filenames are a maximum of ten characters. Filenames can be anything up to 255 bytes long and it is entirely down the the filing system what length names it can use. For instance, DOS filing systems use up to twelve character filenames, viz "Filename/txt". DIM ctrl% 31:REM Need at least 13 bytes for OSGBPB DIM name% 31:REM Need at least 12 bytes for directory entries X%=ctrl%:Y%=X%DIV256 :REM Point to control block ret%=0 :REM Return value idx%=0 :REM Directory index, starts at zero REPEAT f$=FNgbpb8(idx%) :REM OSGBPB call to read directory entries idx%=X%!9 :REM Get directory index back IF f$<>"" THEN PRINT f$ :REM Directory entry returned UNTIL f$="" END : REM Returns entry in current directory, or null string if at end REM ------------------------------------------------------------ DEFFNgbpb8(ptr%):X%!1=name%:X%!5=1:X%!9=ptr%:A%=8:CALL&FFD1:IFX%!5=1:="" A%=name%+1:A%!(A%?-1)=&D20:A%?(INSTR($A%," ")-1)=13:=$A% This code simply prints the filenames, but it is a simple matter to change it so the filenames are read into an array or processed in another way. _Tree Walking_ OSGBPB 8 can 'walk' a directory tree, descending subdirectories and returning from them with the following code. DIM ctrl% 31,name% 31 X%=ctrl%:Y%=X%DIV256 :REM Point to control block DEFPROCScan LOCAL idx%,f$ REPEAT:f$=FNgbpb8(idx%):idx%=X%!9:IF f$<>"":PROCObject UNTIL f$="" ENDPROC : DEFPROCObject LOCAL type% type%=FNfile(f$,5) REM Process object f$ IFtype%=2:OSCLI "Dir "+f$:PROCScan:OSCLI "Dir ^" ENDPROC : REM Returns entry in current directory, or null string if at end REM ------------------------------------------------------------ DEFFNgbpb8(ptr%):X%!1=name%:X%!5=1:X%!9=ptr%:A%=8:CALL&FFD1:IFX%!5=1:="" A%=name%+1:A%!(A%?-1)=&D20:A%?(INSTR($A%," ")-1)=13:=$A% : REM Returns file type, file info in X%!... REM -------------------------------------- DEFFNfile(A$,A%):$name%=A$:?X%=name%:X%?1=name%DIV256:=(USR&FFDD)AND&FF Note that as 6502 BASIC only implements a 20-deep REPEAT stack, this will only descend to a maximum depth of 20 subdirectories before getting a "Too many REPEATs" error. To avoid this a program would have to maintain its own recursion stack. _Reading entries from all DFS directories_ OSGBPB 8 reads the directory entries from the current directory. DFS implements single-character 'sideways' directories. The following code will run on all filing systems, and on DFS will scan through all the DFS directories. DIM ctrl% 31:REM Need at least 13 bytes for OSGBPB DIM name% 31:REM Need at least 12 bytes for directory entries X%=ctrl%:Y%=X%DIV256 :REM Point to control block dfs_all%=TRUE :REM TRUE to do all DFS directories dir%=0 IF dfs_all% THEN IF FNfs=4 THEN dir%=ASC"!":REM Start at dir "!" REPEAT IF dir% THEN OSCLI "Dir """+STRING$(1+(dir%=34),CHR$dir%)+""" idx%=0 :REM Directory index, starts at zero REPEAT f$=FNgbpb8(idx%) :REM OSGBPB call to read directory entries idx%=X%!9 :REM Get directory index back IF f$<>"" THEN PROCname :REM Directory entry returned UNTIL f$="" IF dir%=95:dir%=0 :REM Loop for all directories IF dir% THEN dir%=dir%+1:IF dir%=ASC":" THEN dir%=dir%+1 :REM Skip ':' UNTIL dir%=0 END : REM Returns entry in current directory, or null string if at end REM ------------------------------------------------------------ DEFFNgbpb8(ptr%):X%!1=name%:X%!5=1:X%!9=ptr%:A%=8:CALL&FFD1:IFX%!5=1:="" A%=name%+1:A%!(A%?-1)=&D20:A%?(INSTR($A%," ")-1)=13:=$A% : REM Get current filing system number REM -------------------------------- DEFFNfs:LOCAL A%,Y%,E%:=(USR&FFDA)AND&FF : DEFPROCname IFdir%:IFdir%<>36:f$=CHR$dir%+"."+f$ :REM Prepend with non-"$" dir. char ...etc _Reading the full pathname_ The following functions read the full pathname for the current directory, the current library and the user's home directory. DIM ctrl% 31,name% 31:X%=ctrl%:Y%=X%DIV256 REM FNPath_Name - Return pathname with drive number or name if no number REM FNPath_Full - Returns full pathname with diskname REM The returned pathname can be used to select the directory with *DIR REM -------------------------------------------------------------------- : DEFFNPath_Name:A%=6 DEFFNPath_Full:A%=&106 LOCAL n$,p$:REPEAT:X%!1=name%:CALL &FFD1 ?(name%+2+?name%+?(name%+?name%+1))=13 n$=$(name%+2+?name%):*DIR ^ n$=LEFT$(n$,INSTR(n$+" "," ")-1) p$=n$+"."+p$:UNTILn$="$"ORn$="&" p$=LEFT$(p$,LENp$-1):OSCLI"DIR "+p$ IFA%=6:X%!1=name%:CALL &FFD1:?(name%+1+?name%)=13:n$=$(name%+1):IFn$<>"":=":"+n$+"."+p$ X%!1=name%:A%=5:CALL &FFD1 ?(name%+1+?name%)=13:n$=$(name%+1) n$=LEFT$(n$,INSTR(n$+" "," ")-1):=":"+n$+"."+p$ : : REM FNPath_Lib - Return library pathname REM ------------------------------------ : DEFFNPath_Lib LOCAL a$,b$:a$=FNPath_Name:*DIR % b$=FNPath_Name:OSCLI"DIR "+a$:=b$ : : REM FNPath_LibFull - Return full library pathname REM --------------------------------------------- : DEFFNPath_LibFull LOCAL a$,b$:a$=FNPath_Name:*DIR % b$=FNPath_Full:OSCLI"DIR "+a$:=b$ : : REM FNPath_URD - Return URD pathname REM -------------------------------- : DEFFNPath_URD LOCAL a$,b$:a$=FNPath_Name:*DIR & b$=FNPath_Name:OSCLI"DIR "+a$:=b$ : : REM FNPath_URDFull - Return full URD pathname REM ----------------------------------------- : DEFFNPath_URDFull LOCAL a$,b$:a$=FNPath_Name:*DIR & b$=FNPath_Full:OSCLI"DIR "+a$:=b$ This is an example of what would be returned by the functions: FNPath_Name would return :0.$.Progs.Temp FNPath_Full would return :ProgramDisk.$.Progs.Temp FNPath_Lib would return :1.$.Library FNPath_LibFull would return :UtilsDisk.$.Library FNPath_URD would return :0.$.User1 FNPath_URDFull would return :MyDisk.$.User1 These functions will work on NFS, ADFS and any well behaved filing system that recognises the '^' directory. They will not work on early Acorn file servers that do not recognise '^'. _File time and date_ File server and OSFILE functions read and write dates and times in an internal format. These functions and procedures provide a easy way of converting to and from the internal format. REM FNf_date - convert date into internal format REM FNf_time - convert time into internal format REM -------------------------------------------- DEFFNf_date(d%,m%,y%):y%=y%-1981:=d%+m%*256+(y%AND15)*4096+(y%DIV16)*32 DEFFNf_time(h%,m%,s%):=h%+m%*256+s%*65536 : REM PROCf_date - extract date from internal format REM PROCf_time - extract time from internal format REM ---------------------------------------------- DEFPROCf_date(d%):day%=d%AND31:month%=(d%AND&F00)DIV256 year%=(d%AND&F000)DIV4096+(d%AND&E0)/2+1981:ENDPROC DEFPROCf_time(t%):hour%=t%AND255:minute%=(t%AND&FF00)DIV256 second%=(t%AND&FF0000)DIV65536:ENDPROC _Communicating with the File Server_ These functions provide a convenient way of sending a file server operation to the file server. DIM ctrl% 127:X%=ctrl%:Y%=X%DIV256 REM NetFS_Op(function%, name$) - NetFS file server operation REM Returns file server return code - 0=ok REM -------------------------------------------------------- DEFFNNetFS_Op(A%,A$):!X%=0:X%?1=8+LEN A$:X%!3=A%:$(X%+7)=A$:A%=&14:CALL&FFF1:=X%?3 : REM NetFS_OpN(function%, arg%, offset%, name$) REM - NetFS file server operation with offset to supplied string REM Returns file server return code - 0=ok REM ------------------------------------------------------------- DEFFNNetFS_OpN(A%,T%,O%,A$):!X%=0:X%?1=O%+1+LENA$:X%!3=A%:X%?7=T%:$(X%+O%)=A$:A%=&14:CALL&FFF1:=X%?3 File server operations take a varying amount of data. Most operations take between none and three bytes of data plus an optional string parameter. Each function returns the file server return code. This will be zero if the call successfully completed. If NFS is not present, then the return code will be the supplied file server function code. NetFS_Op() takes a single string parameter. Most file server operations can be done with NetFS_Op() with arguments passed as characters prepended to the string parameter, as in the following example to read an object's creation date: IFFNNetFS_Op(18,CHR$1+filename$)=0 THEN cdate%=X%!5 NetFS_OpN() sends a file server command block to the current file server. function% is the file server function, arg% is the first argument for the file server call, offset% is the offset to where name$ should be stored in the control block. This will be following any other data placed in the control block. If no string is required, pass name$ as "" and offset% as the offset to the end of the control block. Any other parameters must be put in the control block before calling. The following example writes the creation date of a file: X%!8=cdate%:A%=FNNetFS_OpN(19,5,10,filename$) The following list shows how to call each file server function. Command line A%=FNNetFS_Op( 0, command$) Examine 0 A%=FNNetFS_Op( 3, CHR$0+CHR$ptr+CHR$num+filename$) numfound%=X%?4 cycle% =X%?5 load% =X%!16 exec% =X%!20 access%=X%?24 mdate% =X%!25 acc% =X%!27 AND &FFF aux% =X%?29 OR (16*X%?28) size% =X%!30 AND &FFFFFF X%?16=13:name$=$(X%+6) Examine 1 A%=FNNetFS_Op( 3, CHR$1+CHR$ptr+CHR$num+filename$) numfound%=X%?4 cycle%=X%?5 text% =X%+6 Examine 2 A%=FNNetFS_Op( 3, CHR$2+CHR$ptr+CHR$num+filename$) numfound%=X%?4 cycle%=X%?5 X%?17=13:name$=$(X%+6) Examine 3 A%=FNNetFS_Op( 3, CHR$3+CHR$ptr+CHR$num+filename$) numfound%=X%?4 cycle%=X%?5 text% =X%+6 Cat Header A%=FNNetFS_Op( 4, pathname$) header$=$(X%+4) Open A%=FNNetFS_Op( 6, CHR$ create+CHR$read+filename$) handle%=X%?4 Close A%=FNNetFS_Op( 7, CHR$handle) Read PTR A%=NetFS_Op(12, CHR$handle+CHR$0) ptr%=X%!4 AND &FFFFFF Read EXT A%=NetFS_Op(12, CHR$handle+CHR$1) ext%=X%!4 AND &FFFFFF Read Allocated A%=NetFS_Op(12, CHR$handle+CHR$2) size%=X%!4 AND &FFFFFF Write PTR X%?8=0:X%!9=size% A%=FNNetFS_OpN(13, handle, 12, "") Write EXT X%?8=1:X%!9=size% A%=FNNetFS_OpN(13, handle, 12, "") Read Disks A%=FNNetFS_Op(14, CHR$first+CHR$ number) numfound%=X%?4 drive%=X%?5 X%?22=13:drive$=$(X%+6) Read Users A%=FNNetFS_Op(15, CHR$first+CHR$number) numfound%=X%?4 stn% =X%!5 AND &FFFF user$=$(X%+7) priv%=X%?(8+LEN$(X%+7)) Read Time A%=FNNetFS_Op(16, "") date%=X%!4 time%=X%!6 Read EOF A%=FNNetFS_Op(17, CHR$handle) eof%=X%?4 Read CDate A%=FNNetFS(18, CHR$1+filename$) type% =X%?4 cdate%=X%!6 Read Addrs A%=FNNetFS(18, CHR$2+filename$) type%=X%?4 load%=X%!6 exec%=X%!10 Read Extent A%=FNNetFS(18, CHR$3+filename$) type%=X%?4 size%=X%!6 AND &FFFFFF Read Access A%=FNNetFS(18, CHR$4+filename$) type% =X%?4 access%=X%?6 public%=X%?7 Read Attrs A%=FNNetFS(18, CHR$5+filename$) type% =X%?4 access%=X%?6 cdate% =X%!7 Read Cycle A%=FNNetFS(18, CHR$6+filename$) cycle% =X%?18 public%=X%?17 X%?(7+X%?6)=13:dirname$=$(X%+7) Read SIN A%=FNNetFS(18, CHR$7+filename$) type% =X%?4 disknum%=X%?5 sin% =X%!6 sinFS =X%?10 Read Dates A%=FNNetFS(18, CHR$64+filename$) type% =X%?4 cdate%=X%!5 ctime%=X%!7 mdate%=X%!10 mtime%=X%!12 Read Accounts A%=FNNetFS(18, CHR$65+filename$) type%=X%?4 acc% =X%!5 AND &FFFF aux% =X%!7 AND &FFFF Write Info X%!8 =load% X%!12=exec% X%?16=attr% A%=FNNetFS_OpN(19, 1, 17, filename$) Write Load X%!8=load% A%=FNNetFS_OpN(19, 2, 12, filename$) Write Exec X%!8=exec% A%=FNNetFS_OpN(19, 3, 12, filename$) Write Attr A%=FNNetFS_Op(19, CHR$4+CHR$attr+filename$) Write Date X%!8=cdate% A%=FNNetFS_OpN(19, 5, 10, filename$) Write Date+Time X%!8 =cdate% X%!10=ctime% X%!13=mdate% X%!15=mtime% A%=FNNetFS_OpN(19,64,18,filename$) Delete A%=FNNetFS_Op(20, filename$) Read Env A%=NetFS_Op(21, "") X%?41=13:libname$=$(X%+31) X%?31=13:csdname$=$(X%+21) X%?11=13:diskname$=$(X%+5) Set Boot A%=FNNetFS_Op(22, CHR$option) Logoff A%=FNNetFS_Op(23, "") User Info A%=FNNetFS_Op(24, username$) priv%=X%?4 stn% =X%!5 AND &FFFF FS Version A%=FNNetFS_Op(25, "") ver$=$(X%+4) Read Free A%=FNNetFS_Op(26, discname$) free%=X%!4 AND &FFFFFF size%=X%!7 AND &FFFFFF Create Dir A%=FNNetFS_Op(27, CHR$ blocks+pathname$) Set Time X%!7=date% X%!9=time% A%=FNNetFS_OpN(28, date%, 12, "") WhoAmI A%=FNNetFS_Op(32, "") user$=$(X%+4) _Reading time and date from the fileserver_ A%=FNNetFS_Op(16,"") PROCf_date(X%!4):PROCf_time(X%!6) PRINT "It is now ";hour;":";minute%;":";second%;" on ";day%;"/";month%;"/";year% _Checking the file server type_ You can find out what type of file server you are using by asking for the file server version string. All SJ Research file server have "SJ" in the version string, and all MDFS servers have "MDFS" in the version string. The following code tests if the file server is an MDFS. A%=FNNetFS_Op(25,"") ver$=$(X%+4) mdfs%=INSTR(ver$,"MDFS")<>0 _Reading and Writing Network Station Information_ The Net_Info() function reads and sets various network filing system information, such as the file or printer server station and the client station number. DIM ctrl% 31:X%=ctrl%:Y%=X%DIV256 REM Net_Info(function%, data%) - Read/Set NetFS information REM Returns data read REM --------------------------------------- DEFFNNet_Info(A%,D%):?X%=A%:X%!1=D%:A%=&13:CALL&FFF1:=X%!1 Read file server station station%=FNNet_Info(0,0) Set file server station A%=FNNet_Info(1,station%) Read printer server station station%=FNNet_Info(2,0) Set printer server station A%=FNNet_Info(3,station%) Read protection mask mask%=FNNet_Info(4,0) Set protection mask A%=FNNet_Info(5,mask%) Read context context%=FNNet_Info(6,0) Set context A%=FNNet_Info(7,context%) Read station number station%=FNNet_Info(8,0) Read arguments args%=FNNet_Info(9,0) Read error info error%=FNNet_Info(10,0) The following are only implemented on ANFS. Read channel error occured on channel%=FNNet_Info(11,0) Read printer server name A%=FNNet_Info(12,0):X%?7=13:name$=$(X%+1) Set printer server name $(X%+1)=name$:A%=FNNet_Info(13,X%!1) Read space in NetPrint buffer space%=FNNet_Info(14,0) Read file server retries retries%=FNNet_Info(15,0) Write file server retries A%=FNNet_Info(16,retries%) Get network number thisnet%=FNNet_Info(17,0) *_Network transmission and reception routines_* These procedures and functions provide a convenient way of calling the network transmission and reception functions to communicate directly with other machines on the network. All the functions use a global control block pointed to by X% set up with: DIM ctrl% 31:X%=ctrl%:Y%=X%DIV256 _Transmission_ REM FNNet_Tx(Stn%,Flag%,Port%,Addr%,Len%,RAddr%) REM - Transmit with specified count and delay REM FNNet_TxCount(Stn%,Flag%,Port%,Addr%,Len%,RAddr%,Try%,Delay%) REM - Transmit with default count and delay REM If a broadcast, fetches data from memory at Addr% REM Returns Net_Tx success value - 0=Ok REM ---------------------------------------------------- : DEFFNNet_TxCount(Stn%,Ctrl%,Port%,Addr%,Len%,RAddr%,Try%,Delay%):LOCAL TxErr% DEFFNNet_Tx(Stn%,Ctrl%,Port%,Addr%,Len%,RAddr%):LOCAL TxErr%,Try%,Delay%:Try%=10:Delay%=10 X%?1=Port%:X%!2=Stn%:X%!4=Addr%:X%!8=Addr%+Len%:X%!12=RAddr% IFStn%=&FFFF:X%!4=Addr%!0:X%!8=Addr%!4 :REM Set broadcast data REPEAT:REPEAT:X%?0=Ctrl%:A%=&10:CALL &FFF1:UNTILX%?0 :REM Loop until Tx starts REPEAT:TxErr%=FNbyte(&32,0,0):UNTILTxErr%<&80 :REM Loop until complete IFTxErr%=&41 OR TxErr%=&42:IFTry%:A%=TIME+Delay%:REPEATUNTILTIME>A%:Try%=Try%-1 UNTILNOT(TxErr%=&41 OR TxErr%=&42) OR Try%<1:=TxErr% : DEFPROCDelay(A%):LOCALT%:T%=TIME+A%:REPEATUNTILTIME>T%:ENDPROC The functions Net_Tx() and Net_TxCount() transmit a quantity of data over the network to the specified station. Net_TxCount() allows the count and delay between retries to be specified, whereas Net_Tx() uses the default of 10 retries with a 10cs delay between. The functions return the result of the transmission. This will be 0 for all ok, or &4x for a transmission error. Error codes returned are: &40 Line jammed &41 Net error &42 Not listening &43 No clock &44 Bad Tx block The function Net_Err() will convert this value into a string for display. Net_Err() returns a null string if the passed value is zero, otherwise a string containing the specified error message. _Broadcasts_ If station &FFFF is specified, a global broadcast will be sent. Only 8 bytes can be sent in a global broadcast. This is the data at the specified address, which is transfered into the control block by Net_Tx(). Broadcasts return immediately as there is no way of knowing if the destination machines have received them, as more than one machine may be receptive, so it is best to do a broadcast using Net_TxCount() using a Try% and Delay% of zero. _Immediates_ When Port% is zero, an immediate operation is performed. This does not require an open receive block on the destination machine as the required action is performed by the network firmware. Some immediates return immediately, and so Net_TxCount() with Try% and Delay% set to zero can be used. Some immediates use the RAddr% parameter. The following is a list of currently defined immediate operations and the values to pass to Net_Tx() or Net_TxCount(): Peek Net_Tx(Stn%,&81,0,MyAddr, MyLen, RemoteAddr) Poke Net_Tx(Stn%,&82,0,MyAddr, MyLen, RemoteAddr) Remote JSR Net_Tx(Stn%,&83,0,ArgAddr,ArgLen,RemoteCallDest) Remote PROC Net_Tx(Stn%,&84,0,ArgAddr,ArgLen,ProcNumber) Remote OS PROC Net_Tx(Stn%,&85,0,ArgAddr,ArgLen,OSProcNumber) Halt Net_Tx(Stn%,&86,0,0, 0, 0) Continue Net_Tx(Stn%,&87,0,0, 0, 0) Machine Peek Net_Tx(Stn%,&88,0,ArgAddr,4, 0) Read Registers Net_Tx(Stn%,&89,0,ArgAddr,80, 0) Read Registers is only supported between ARM-based machines. _Reception_ REM FNNet_RxOpen(Stn%,Port%,Addr%,Len%) - Open a receive block REM Returns Rx block number REM --------------------------------- : DEFFNNet_RxOpen(Stn%,Port%,Addr%,Len%) X%?0=0:X%?1=&7F:X%?2=Port%:X%!3=Stn%:X%!5=Addr%:X%!9=Addr%+Len% A%=&11:CALL &FFF1:=X%?0 The function Net_RxOpen() opens a receive block to recieve data from a network transmission on the specified port from the specified station. Up to Len% bytes is received into memory pointed to by Addr%. Net_RxOpen() returns the handle of the opened receive block, or zero if there is insufficient space to create any more. If Stn% is 0, then transmissions from any station are received. If Port% is 0, then transmissions on any port are received. On reading the receive block, the returned station and port specify the actual station and port transmitted from. _Polling Reception_ REM FNNet_Rx(RxNum%) - Poll receive block REM Returns TRUE if reception occured REM ------------------------------------- : DEFFNNet_Rx(RxNum%):=(FNbyte(&33,RxNum%,0) AND &80)<>0 Net_Rx() returns TRUE if a reception has occured in the receive block specified by RxNum%. Once a reception has occured, it can be read using Net_RxRead(). _Reading a Receive Block_ REM FNNet_RxRead(RxNum%) - Read Rx block REM Returns the number of bytes received REM ------------------------------------ : DEFFNNet_RxRead(RxNum%) X%?0=RxNum%:A%=&11:CALL &FFF1:A%=X%!9-X%!5:X%!5=0:=A% Net_RxRead() reads and deletes the specified recieve block and updates the control block with the station and port actually received on. The number of bytes received is returned. _Abandoning a Receive Block_ REM FNNet_RxKill(RxNum%) - Kill an Rx block REM --------------------------------------- : DEFPROCNet_RxKill(RxNum%):A%=FNfx(&34,RxNum%):ENDPROC Net_RxKill() abandons and deletes a receive block. _Networking Support Functions_ These are various support functions that convert values into human- readable strings. _Communication Results_ REM FNNet_Err(result%) - Return error string for network status code REM ---------------------------------------------------------------- : DEFFNNet_Err(A%):IFA%=0:="" =MID$("Line jammedNet errorNot listeningNo clockBad Tx blockNo reply",VALMID$("011221344254",(A%-&40)*2+1,2),VALMID$("110913081208", (A%-&40)*2+1,2)) Net_Err() returns the english error string corresponding to the network status code. If A% is 0, a null string is returned, otherwise one of the following strings is returned: &40 Line jammed &41 Net error &42 Not listening &43 No clock &44 Bad Tx block &45 No reply _Station number display_ REM FNNet_StnFixed(Stn%) - Returns fixed-length dotted net&station number REM FNNet_Stn(Stn%) - If net=0, returns string stn else returns net.stn REM ------------------------------------------------------------------------ : DEFFNNet_Stn(A%):=LEFT$(FNd0(A%DIV256,3)+".",(A%DIV256)<>0)+FNd0(A%AND255,3) DEFFNNet_StnFixed(A%):=FNd0(A%DIV256,3)+"."+FNd0(A%AND255,3) Net_StnFixed() and Net_Stn() returns a string containing the dotted network and station number. If the network is 0, then Net_Stn() returns the three-digit string stn. Net_StnFixed() always returns the full seven-character string net.stn _Station number input_ REM FNNet_StnNum(Stn$) - Convert dotted net/stn string to station number REM -------------------------------------------------------------------- : DEFFNNet_StnNum(A$) A%=INSTR(A$,"."):IF A%:=256*VALLEFT$(A$,A%-1)+VALMID$(A$,A%+1) ELSE =VALA$ Net_StnNum() takes a network/station number string such as "1.254" or "12" and converts it into an integer station number to be passed to other function calls. _Number display functions_ These functions will convert numbers into fixed-length strings in decimal or hexadecimal. REM Hexadecimal padded with zeros DEFFNh0(A%,N%)=RIGHT$("0000000"+STR$~A%,N%) : REM Hexadecimal padded with spaces DEFFNh(A%,N%)=RIGHT$(" "+STR$~A%,N%) : REM Decimal padded with zeros DEFFNd0(A%,N%)=RIGHT$("00000000"+STR$A%,N%) : REM Decimal padded with spaces DEFFNd(A%,N%)=RIGHT$(" "+STR$A%,N%) REM > CeefaxTime : DIM ctrl% 31:X%=ctrl%:Y%=X%DIV256 DIM inBuf% &4F,outBuf% &1F:rxNum%=0 : ON ERROR REPORT:PROCNet_RxKill(rxNum%):PRINT:END : rxNum%=FNNet_RxOpen(0,&B1,inBuf%,&50) $outBuf%="TELETEXT" REPEAT tx%=FNNet_TxCount(&FFFF,&80,&B0,outBuf%,8,0,0,0) IF tx% THEN PRINT FNNet_Err(tx%):END rx%=FNNet_Rx(rxNum%) IF NOT rx%:PROCDelay(200) UNTIL rx% rxsize%=FNNet_RxRead(rxNum%) stn%=X%!3 portBase%=inBuf%?1 inBuf%?(12+inBuf%?11)=13 name$=$(inBuf%+12) PRINT "Teletext server = ";FNNet_Stn(Stn%);" ";name$ : rxNum%=FNNet_RxOpen(stn%,portBase%,inBuf%,&50) tx%=FNNet_TxCount(stn%,&84,portBase%+1,outBuf%,1,10,50) IF tx% THEN PRINT FNNet_Err(tx%):END REPEAT PROCDelay(200):UNTIL FNNet_Rx(rx%) rxsize%=FNNet_RxRead(rx%) PRINT "The time is "$(inBuf%+1) END : DEFPROCDelay(A%):A%=TIME+A%:REPEATUNTILTIME>A%:ENDPROC : : DEFFNNet_TxCount(Stn%,Ctrl%,Port%,Addr%,Len%,RAddr%,Try%,Delay%):LOCAL TxErr% DEFFNNet_Tx(Stn%,Ctrl%,Port%,Addr%,Len%,RAddr%):LOCAL TxErr%,Try%,Delay%:Try%=10:Delay%=50 X%?1=Port%:X%!2=Stn%:X%!4=Addr%:X%!8=Addr%+Len%:X%!12=RAddr% IFStn%=&FFFF:X%!4=Addr%!0:X%!8=Addr%!4 REPEAT:REPEAT:X%?0=Ctrl%:A%=&10:CALL &FFF1:UNTILX%?0 REPEAT:TxErr%=FNbyte(&32,0,0):UNTILTxErr%<&80 IFTxErr%=&41 OR TxErr%=&42:IFTry%:A%=TIME+Delay%:REPEATUNTILTIME>A%:Try%=Try%-1 UNTILNOT(TxErr%=&41 OR TxErr%=&42) OR Try%<1:=TxErr% : DEFFNNet_RxOpen(Stn%,Port%,Addr%,Len%) X%?0=0:X%?1=&7F:X%?2=Port%:X%!3=Stn%:X%!5=Addr%:X%!9=Addr%+Len% A%=&11:CALL &FFF1:=X%?0 : DEFFNNet_Rx(RxNum%):=(FNbyte(&33,RxNum%,0)AND&80)<>0 : DEFFNNet_RxRead(RxNum%) X%?0=RxNum%:A%=&11:CALL &FFF1:A%=X%!9-X%!5:X%!5=0:=A% : DEFPROCNet_RxKill(RxNum%):A%=FNbyte(&34,RxNum%,0):ENDPROC : DEFFNNet_Err(A%):IFA%=0:="" =MID$("Line jammedNet errorNot listeningNo clockBad Tx blockNo reply",VALMID$("011221344254",(A%-&40)*2+1,2),VALMID$("110913081208",(A%-&40)*2+1,2)) : DEFFNNet_Stn(A%):=LEFT$(FNd0(A%DIV256,3)+".",(A%DIV256)<>0)+FNd0(A%AND255,3) DEFFNNet_StnFixed(A%):=FNd0(A%DIV256,3)+"."+FNd0(A%AND255,3) DEFFNNet_StnNum(A$) A%=INSTR(A$,"."):IFA%:=256*VALLEFT$(A$,A%-1)+VALMID$(A$,A%+1) ELSE =VALA$ : DEFFNbyte(A%,X%,Y%)=((USR&FFF4)AND&FF00)DIV256 DEFFNd0(A%,N%)=RIGHT$("00000000"+STR$A%,N%) DEFFNh0(A%,N%)=RIGHT$("0000000"+STR$~A%,N%) Archimedes/RISC OS Programs 10 REM > GetStnNum 1.20 20 REM Set Econet station and network number system variables 30 SYS "Econet_ReadLocalStationAndNet" TO stn%,net% 40 OSCLI "SetEval Econet$Station "+STR$stn% 50 OSCLI "Set Econet$Stn "+FNd0(stn%,3) 60 OSCLI "SetEval Econet$Network "+STR$net% 70 OSCLI "Set Econet$Net "+FNd0(net%,3) 80 END 90 DEFFNd0(A%,N%)=RIGHT$("00000000"+STR$A%,N%) File handling As has been strongly emphasised to you by now, file access using BGET#, BPUT# (and therefore also INPUT#, PRINT#) is S L 0 W. The remedy is to use OSGBPB (often pronounced 'Ozheebeegeebee'). One of the problems about using this call is the lack of documentation, and also the fact that the cassette filing system (CFS) does not support OSGBPB, but only OSBGET and OSBPUT: this means that a lot of commercial software packages, wishing to be compatible with Tape FS, use BGET and BPUT. The only solution here is to use PUTGET (q.v. §6.6).