10 REM > SerialServ 1.05
   20 ver$="0.00":REM 11-Feb-1990
   30 ver$="0.01":REM 19-Aug-1996 Added TAPfile support on Loading
   40 ver$="0.02":REM 28-Jul-1997 Compact serial system won't receive zero bytes
   50 ver$="1.00":REM 28-Mar-1998 Compact NUL-NUL problem fixed
   60 ver$="1.02":REM 30-Mar-1998 Main loop tidied
   70 ver$="1.04":REM 02-Apr-1998 Was SpecServ, input scans for *commands
   80 ver$="1.05":REM 15-May-1998 Can specify new addresses for Save
   90 :
  100 MODE&87:PRINTSPC5"Spectrum/BBC fileserver "ver$'SPC3"with local keyboard for Spectrum"
  110 IFFNfx(0,1)=5:*SerFix
  120 PROCInit:ONERROR:IFFNerr:PROCend:END
  130 PRINT'"Ready...":*FX15
  140 PROCon:REPEATUNTILFNdo:PROCend:END
  150 :
  160 DEFFNerr:IFNOTcomm%:*FX3
  170 REPORT:IFcomm%:PRINT'CHR$93; ELSE PROCreply(ERR,FNerror,TRUE):IFERR<128 AND ERR<>17:PRINT" at line ";ERL
  180 OSCLI"FX3":PROCCloseOut:PROCCloseIn
  190 =(ERR<128 AND ERR<>17)OR(ERR=17 ANDINKEY-1)
  200 :
  210 DEFPROCCloseIn:IFin%:A%=in%:in%=0:CLOSE#A%
  220 ENDPROC
  230 DEFPROCCloseOut:IFout%:A%=out%:out%=0:CLOSE#A%
  240 ENDPROC
  250 :
  260 DEFPROCend:IFnid%:?&D71=nid%
  270 PROCoff:*FX4
  280 *FX220,27
  290 END
  300 :
  310 DEFPROCon:*FX2,1
  320 REM *FX3
  330 REM *FX181,1
  340 ENDPROC
  350 :
  360 DEFPROCoff:*FX2
  370 REM *FX3
  380 REM *FX181,1
  390 ENDPROC
  400 :
  410 DEFPROCInit:*FX181,1
  420 DIM ctrl%31,data%99,ft$(3):X%=ctrl%:Y%=X%DIV256
  430 ft$(0)="Program":ft$(1)="Numbers":ft$(2)="Character":ft$(3)="Bytes"
  440 out%=0:in%=0:P6502%=FNfx(132,0):IFP6502%<&8000:P6502%=&8000
  450 P6502%=(P6502%?6 AND &F)=0
  460 nid%=0:A%=FNfx(0,1):IFA%>2 AND A%<6:IFFNfx(130,0)=&FFFF:nid%=?&D71:?&D71=64
  470 PRINT'SPC4"f0 to clear, f0+Shift to quit"'SPC3"Cursors send CHR$135 to CHR$139"
  480 *FX7,7
  490 *FX8,7
  500 *FX15
  510 *FX220,128
  520 *FX4,1
  530 *FX3
  540 ENDPROC
  550 :
  560 DEFFNfx(A%,X%):LOCALY%:Y%=X%DIV256:=((USR&FFF4)AND&FFFF00)DIV256
  570 DEFFNfile(A$,A%):$data%=A$:?X%=data%:X%?1=data%DIV256:=(USR&FFDD)AND&FF
  580 DEFFNh0(A%,N%)=RIGHT$("0000000"+STR$~A%,N%)
  590 :
  600 DEFFNdo:X%=ctrl%:Y%=X%DIV256:PRINT":";:f$=FNinput:A%=ASCf$
  610 I%=INSTR("*CDEHILMRSX",CHR$A%):IFI%>1:PRINTMID$("*CATDIRERASEHELPINFOLOADMKDIRRENAMESAVEEXIT",VALMID$("0102050813172125303640",2*I%-1,2),VALMID$("13354445644",I%,1));
  620 IFA%>63:OSCLI"FX2,1":OSCLI"FX3,86":INPUTLINE" "f$:OSCLI"FX3":f$=FNfn_unspec(f$):PRINTf$:*FX2,2
  630 I%=INSTR("DEMR",CHR$A%):IFI%:f$=MID$("DIRDELETECDIRRENAME",VALMID$("01041014",2*I%-1,2),VALMID$("3646",I%,1))+" "+f$:A%=ASC"D"
  640 IFA%=ASC"*":OSCLI"FX3,3":comm%=TRUE:OSCLIf$:VDU93:comm%=FALSE:OSCLI"FX3":=0
  650 IFA%=ASC"D":OSCLIf$:PROCreply(0,"",TRUE):=0
  660 IFA%=ASC"C":PROCCat:=0
  670 IFA%=ASC"H":PROCreply(0,"Cat Dir Erase Help Info Load Mkdir Rename Save eXit",TRUE):=0
  680 IFA%=ASC"I":PROCInfo:=0
  690 IFA%=ASC"L":PROCLoad:=0
  700 IFA%=ASC"S":PROCSave:=0
  710 IFA%=ASC"X":PROCreply(0,"Exiting",TRUE):PRINT'"Exit:":=TRUE
  720 PROCreply(254,"Not recognised",TRUE):=0
  730 :
  740 DEFFNinput:A$="":OSCLI"FX2,2":*FX3,3
  750 REPEATREPEAT:A%=INKEY(0):IFA%<>-1:VDUA%
  760   UNTILADVAL(-2):OSCLI"FX2,1":A%=GET:*FX2,2
  770   IFA%>31 AND A%<127 AND LENA$<250:A$=A$+CHR$A%:IFLENA$>1 OR A$="*":VDUA%
  780   IF(A%=127 OR A%=8) AND A$<>"":A$=LEFT$(A$,LENA$-1):VDU8,32,8
  790 UNTIL A%=10 OR A%=13 OR (LENA$=1 AND A$<>"*"):IFASCA$=42:PRINT
  800 *FX3
  810 =A$
  820 :
  830 REM Needs to use INKEY, not GET:
  840 REM Also, how to specify TAP output?
  850 REM "Sfilename"
  860 REM "Sfilename start prog auto type", specified in decimal
  870 DEFPROCSave:*FX2,1
  880 addr$="":A%=INSTR(f$," "):IF A%:addr$=MID$(f$,A%+1):f$=LEFT$(f$,A%-1)
  890 out%=OPENOUT(f$):PROCreply(192,"Can't open "+f$,out%=0):IFout%=0:ENDPROC
  900 type%=GET AND 3:length%=GET+256*GET
  910 start%=GET+256*GET:prog%=GET+256*GET:autoline%=GET+256*GET
  920 REPEAT:PROCPrInfo
  930   IF addr$<>"":PRINT "Set addresses to: ";addr$
  940   IF addr$<>"":start%   =VALaddr$:addr$=MID$(addr$+" ",INSTR(addr$," ")+1)
  950   IF addr$<>"":prog%    =VALaddr$:addr$=MID$(addr$+" ",INSTR(addr$," ")+1)
  960   IF addr$<>"":autoline%=VALaddr$:addr$=MID$(addr$+" ",INSTR(addr$," ")+1)
  970   IF addr$<>"":type%    =VALaddr$:addr$=MID$(addr$+" ",INSTR(addr$," ")+1)
  980 UNTIL addr$=""
  990 load%=start%:exec%=prog%:IFtype%=0:load%=autoline%
 1000 IFtype%=1 OR type%=2:load%=prog%:exec%=start%
 1010 load%=load%+type%*&10000:exec%=exec%+type%*&10000
 1020 IFlength%:FORz%=1TOlength%:BPUT#out%,GET:NEXT
 1030 CLOSE#out%:out%=0:A%=FNfile(f$,5):X%!2=load%:X%!6=exec%:A%=FNfile(f$,1)
 1040 *FX2,2
 1050 ENDPROC
 1060 :
 1070 DEFPROCLoad
 1080 fn$=f$:A%=FNfile(f$,5):IF A%=0:PROCTapName:A%=0:IF f$<>fn$:A%=FNfile(f$,5)
 1090 load%=X%!2:exec%=X%!6:length%=X%!10:tap%=(load%AND&FFFFFF00)=&FFF11D00
 1100 IFA%=1:IFtap%:IFlength%<25:PROCreply(214,fn$+" too short",TRUE):ENDPROC
 1110 PROCreply(214,fn$+" not found",A%<>1):IFA%<>1:ENDPROC
 1120 type%=(load%AND&30000)DIV&10000
 1130 in%=OPENIN(f$):IFtap%:PROCTapInfo
 1140 start%=load%AND&FFFF:prog%=exec%AND&FFFF:autoline%=start%
 1150 IFtype%=1 OR type%=2:start%=prog%:prog%=autoline%
 1160 PROCPrInfo:*FX3,7
 1170 VDUtype%,length%;start%;prog%;autoline%;
 1180 IFlength%:FORz%=1TOlength%:VDUBGET#in%:NEXT
 1190 CLOSE#in%:in%=0:*FX3
 1200 ENDPROC
 1210 :
 1220 DEFPROCPrInfo
 1230 PRINT"Type ";type%;" (";ft$(type%);")"
 1240 PRINT"Length:  &"FNh0(length%,4);SPC8"Start:  &"FNh0(start%,4)
 1250 PRINT"Program: &"FNh0(prog%,4);SPC5"Autostart: ";autoline%
 1260 ENDPROC
 1270 :
 1280 DEFPROCTapName
 1290 A%=LENf$:REPEATA%=A%-1:UNTILA%=0 OR MID$(f$,A%,1)="."
 1300 f$=LEFT$(f$,A%)+LEFT$(MID$(f$,A%+1)+"/tap",10)
 1310 ENDPROC
 1320 :
 1330 DEFPROCTapInfo
 1340 A%=BGET#in%+BGET#in%+BGET#in%:type%=BGET#in%
 1350 FORA%=1TO12:prog%=BGET#in%:NEXT
 1360 load%=BGET#in%+256*BGET#in%:exec%=BGET#in%+256*BGET#in%
 1370 A%=BGET#in%+BGET#in%+BGET#in%+BGET#in%:length%=EXT#in%-25
 1380 ENDPROC
 1390 :
 1400 DEFPROCCat:*FX3,3
 1410 IFf$<>"":f$=" of "+f$
 1420 PRINTCHR$0;"Catalog";f$;SPC(25-LEN f$);
 1430 X%!9=0:REPEAT
 1440   X%!1=data%:X%!5=1:A%=8:CALL&FFD1
 1450   IFX%!5=0:?(data%+?data%+1)=13:f$=$(data%+1):PRINTf$;SPC(16-LEN f$);
 1460 UNTILX%!5<>0:PRINTCHR$13;:*FX3
 1470 ENDPROC
 1480 :
 1490 DEFPROCInfo
 1500 fn$=f$:A%=FNfile(f$,5):IF A%=0:PROCTapName:A%=0:IF f$<>fn$:A%=FNfile(f$,5)
 1510 IFA%=0:PROCreply(214,fn$+" not found",TRUE):ENDPROC
 1520 start%=X%!2:exec%=X%!6:length%=X%!10:attr%=X%!14
 1530 IF(start%AND&FFFFFF00)=&FFF11D00 AND length%>25:in%=OPENIN(f$):PROCTapInfo:CLOSE#in%:in%=0
 1540 IFA%=2:PROCreply(0,LEFT$(fn$+STRING$(10," "),10)+" <dir>",TRUE):ENDPROC
 1550 PROCreply(0,LEFT$(fn$+STRING$(10," "),10)+" "+FNh0(start%,6)+" "+FNh0(exec%,6)+" "+FNh0(length%,6),TRUE):ENDPROC
 1560 ENDPROC
 1570 :
 1580 DEFPROCreply(err%,A$,F%):IFerr%=13:err%=12
 1590 IFF%:IFerr%:PRINT"Error ";A$
 1600 *FX3,7
 1610 IFF%:PRINTCHR$err%;A$;
 1620 PRINTCHR$13;:*FX3
 1630 ENDPROC
 1640 :
 1650 DEFFNerror:LOCAL A%,A$:IFP6502%:A%=1+(!&FD AND &FFFF):A$="":REPEAT:A$=A$+CHR$?A%:A%=A%+1:UNTIL?A%=0:=A$ ELSE =REPORT$
 1660 :
 1670 DEFPROCSetAddr(A$,ld%,ex%):X%!2=ld%:A%=FNfile(A$,2):X%!6=ex%:A%=FNfile(A$,3):ENDPROC
 1680 DEFFNfn_unspec(B$):IF B$="":="_"
 1690 LOCALB%,C$:A$="""#$%&:<>*./@[\]^{|}~":C$="'?S;+;()+/.=(/)'(')'"
 1700 FOR A%=1 TO LEN B$:B%=INSTR(A$,MID$(B$,A%,1))
 1710   IF B%:B$=LEFT$(B$,A%-1)+MID$(C$,B%,1)+MID$(B$,A%+1)
 1720   IF MID$(B$,A%,1)<"!" OR MID$(B$,A%,1)>"~":B$=LEFT$(B$,A%-1)+"_"+MID$(B$,A%+1)
 1730 NEXT:=B$
 1740 DEFPROCDump:*FX2,1
 1750 REPEATA%=GET:PRINT" "FNh0(A%,2);:VDU8,8,10:IFA%>31 AND A%<>127:VDUA% ELSE VDU32
 1760   VDU11,9,32:IFPOS<1:PRINT
 1770 UNTIL0:*FX2
 1780 ENDPROC