10 REM > DskToUSB45 v0.45 28-Dec-2018 J.G.Harston
   20 REM v0.20 - Manually sends data 256*max% bytes at a time
   30 REM v0.30 - Uses machine code USB transfer, dynamically skips disk errors
   40 REM v0.40 - Copies DFS/ADFS/LVFS, allows size to be overridden
   50 REM v0.41 - Selects source FS by command, keeps FS selected for transfer
   60 REM v0.42 05-Nov-2017 Faster step back from errors, steps up from success
   70 REM v0.43 31-May-2018 SEK for each USB access
   80 REM v0.44 04-Jun-2018 Some optimisation
   81 REM v0.45 28-Dec-2018 Parameters to FNdisk corrected
   90 :
  100 IFFNfx(0,1)<6:IFFNfx(130,0)=&FFFF ELSE PRINT"Must run on BBC I/O processor":END
  110 PRINT "Copy disk to USB as a disk image"
  120 PRINT "Equivalent to: *EXPORT -X<drv> usbfile"'
  130 max%=(HIMEM-LOMEM-2048)DIV256:oldfs%=FNfs:IF max%<2:MODE &87:RUN
  140 DIM ctrl% 31,data% 256*max%-1:X%=ctrl%:Y%=X%DIV256
  150 PROCusb_Init:ON ERROR REPORT:PROCend:END
  160 :
  170 INPUTLINE"Source filing system: "type$
  180 INPUTLINE"Source drive:         "drv%
  190 INPUTLINE"Dest. USB image file: "out$
  200 OSCLI type$:thisfs%=FNfs
  210 REPEAT:READ fs$,fn$,A$,retry%
  220 UNTIL fs$="*" OR fs$=STR$thisfs%
  230 IF fs$="*":PRINT "Unsupported filing system":PROColdfs:END
  240 DATA 4,disk,7F,3, 8,adfs,72,1, 10,adfs,62,1, 16,hadfs,5A,1, 29,disk,77,1
  250 REM     DISK         ADFS         LVFS          HADFS           RAM
  260 DATA *,*,*,*
  270 :
  280 REM fn$="disk" should examine IDs to check density for DDFS
  290 osw%=EVAL("&"+A$):dsize%=FNsize
  300 PRINT"Total sectors to save (&";~dsize%;"): &";:INPUT""A$:IFA$<>"":dsize%=EVAL("&"+A$)
  310 ON ERROR REPORT:IF INKEY-1:PROCend:END ELSE PROCabort:END
  320 PROCusb_Sync:PROCusb_Flush
  330 A%=FNusb_Cmd("IPA"):PROCusb_Flush:VDU13
  340 IF FNusb_Cmd("IPA"):PROCabort:END
  350 IF FNusb_Cmd("OPW "+out$):PROCabort:END
  360 PROCusb_Sync
  370 :
  380 sect%=0:thisnum%=max%
  390 REPEAT
  400   num%=max%:IF sect%+num%>dsize%:num%=dsize%-sect%
  410   IF thisnum%<num%:num%=thisnum%
  420   thisnum%=FNread(sect%,num%)
  430   VDU13:PRINT"Writing   ";FNh0(sect%,6);"+";FNh0(thisnum%,2);
  440   IF FNusb_Cmd("SEK "+STR$(sect%*256)):PROCabort:END
  450   PROCusb_WrStr("WRF "+STR$(thisnum%*256)+CHR$13)
  460   A%=thisnum%:CALL usb_WrData
  470   sect%=sect%+thisnum%
  480   IF num%=thisnum%:thisnum%=thisnum%*2:IF thisnum%>max%:thisnum%=max%
  490 UNTILsect%>=dsize%
  500 :
  510 VDU13:PRINT"Closing";SPC14;
  520 PROCusb_Sync:PROCusb_Flush
  530 IF FNusb_Cmd("CLF "+out$):PROCabort
  540 PROCusb_Cmd("SUD")
  550 PROCusb_Sync
  560 PROColdfs
  570 VDU13:PRINT"Done";SPC17
  580 END
  590 :
  600 DEFPROColdfs
  610 IFoldfs%:OSCLI"FX143,18,"+STR$oldfs%:oldfs%=0
  620 ENDPROC
  630 :
  640 DEFPROCabort
  650 PROCusb_Flush:PROCusb_Cmd("CLF "+out$)
  660 PROCusb_Cmd("SUD"):PROCusb_Flush
  670 PROCend:ENDPROC
  680 :
  690 DEFPROCend
  700 PROColdfs:PRINT
  710 ENDPROC
  720 :
  730 DEFFNread(sect%,num%)
  740 REPEAT
  750   try%=retry%
  760   REPEAT
  770     VDU13:PRINT"Reading ";FNh0(drv%,1);":";FNh0(sect%,6);"+";FNh0(num%,2);
  780     err%=FNattempt(num%):try%=try%-1
  790   UNTIL try%<1 OR err%=0
  800   IF err%:num%=(num%+2)DIV4
  810 UNTIL err%=0 OR num%<1
  820 =num%-(num%<1)
  830 :
  840 DEFFNattempt(num%)
  850 IFfn$="disk" :err%=FNdisk( data%,&53,drv%,sect%DIV10,sect%MOD10,num%,osw%,1)
  860 IFfn$="adfs" :err%=FNadfs( data%,  8,drv%,sect%,num%,osw%)
  870 IFfn$="hadfs":err%=FNhadfs(data%,&80,drv%,sect%,num%,osw%)
  880 VDU13:A%=FNdisk_err(err%,drv%,sect%,num%)
  890 =err%
  900 :
  910 DEFFNsize
  920 LOCAL size%
  930 IFfn$="disk":sect%=1:err%=FNdisk(data%,&53,drv%,0,sect%,1,osw%,1):size%=data%?7+256*(data%?6 AND 3)
  940 IFfn$="adfs":sect%=0:err%=FNadfs(data%,8,drv%,sect%,1,osw%):size%=data%!&FC AND &FFFFFF
  950 IFfn$="hadfs":sect%=&46:err%=FNhadfs(data%,&80,drv%,sect%,1,osw%):size%=data%!28 AND &FFFFFF
  960 VDU13:A%=FNdisk_err(err%,drv%,sect%,1)
  970 =size%
  980 :
  990 REM Disk access routines
 1000 REM ====================
 1010 DEFFNfs:LOCALA%,E%,Y%:=(USR&FFDA)AND&FF
 1020 DEFFNdisk(addr%,cmd%,drv%,trk%,sec%,num%,osw%,den%):LOCALn%
 1030 REPEAT:n%=num%:IFsec%+n%>10:n%=10-sec%
 1040   REPEAT:X%?0=drv%+den%*24+8+2*(trk%DIV80):X%!1=addr%:X%?5=3-7*(cmd%>127)
 1050     X%?6=cmd%:X%?7=trk%MOD80:X%?8=sec%:X%!9=n%OR&1E20:A%=osw%:CALL&FFF1
 1060   A%=X%?(7+X%?5):UNTILA%<>&10:addr%=addr%+n%*256:num%=num%-n%:sec%=(sec%+n%)MOD10:trk%=trk%+1
 1070 UNTILA%<>0ORnum%<1:=A%
 1080 DEFFNadfs(addr%,cmd%,drv%,sect%,num%,osw%)
 1090 X%?0=1:X%!1=addr%:X%?5=cmd%:X%?6=drv%*32+((sect%AND&1F0000)DIV65536)
 1100 X%?7=((sect%AND&FF00)DIV256):X%?8=sect%:X%!9=num%:X%!11=0
 1110 A%=osw%:CALL&FFF1:=?X%
 1120 DEFFNhadfs(addr%,cmd%,drv%,sect%,num%,osw%)
 1130 !X%=&600:X%!2=addr%:X%!6=sect%:X%?9=drv%:X%?10=num%:X%?11=cmd%
 1140 A%=osw%:CALL&FFF1:=X%?12
 1150 DEFFNdisk_err(A%,D%,S%,N%)
 1160 IFA%:PRINT"Disk error ";FNh0(A%,2);" at ";FNh0(D%,1);":";FNh0(S%,6);"+";FNh0(N%,2)
 1170 =A%
 1180 :
 1190 REM I/O routines
 1200 REM ============
 1210 DEFFNh0(A%,N%):=RIGHT$("00000000"+STR$~A%,N%)
 1220 DEFFNfx(A%,X%):LOCAL Y%:Y%=X%DIV256:=((USR&FFF4)AND&FFFF00)DIV256
 1230 :
 1240 REM USB access routines
 1250 REM ===================
 1260 DEFPROCusb_Sync:REPEATA%=?usb_D:UNTIL(?usb_S AND 128):ENDPROC
 1270 DEFPROCusb_Flush:IF(?usb_S AND 128):ENDPROC
 1280 PRINT'"Flushing USB";:REPEAT:REPEATA%=?usb_D:UNTIL(?usb_S AND 128)
 1290 A%=TIME+20:REPEATUNTILTIME>A%:UNTIL(?usb_S AND 128):ENDPROC
 1300 DEFFNusb_Err:IF?usb_result%=13 OR usb_result%?1=ASC":":=0 ELSE PRINT"USB Error: "$usb_result%:=TRUE
 1310 DEFPROCusb_Cmd(A$):PROCusb_Sync:PROCusb_WrStr(A$+CHR$13):PROCusb_RdStr:ENDPROC
 1320 DEFFNusb_Cmd(A$):PROCusb_Cmd(A$):=FNusb_Err
 1330 DEFPROCusb_WrStr(A$):FOR A%=1 TO LEN A$:REPEATUNTIL(?usb_S AND 64)=0
 1340 ?usb_D=ASCMID$(A$,A%,1):NEXT:ENDPROC
 1350 DEFPROCusb_RdStr:A%=usb_result%-1:REPEATA%=A%+1:?A%=FNusb_Rd:UNTIL?A%=13:ENDPROC
 1360 DEFPROCusb_Wr(A%):REPEATUNTIL(?usb_S AND 64)=0:?usb_D=A%:ENDPROC
 1370 DEFFNusb_Rd:REPEATUNTIL(?usb_S AND 128)=0:=?usb_D
 1380 DEFPROCusb_Init
 1390 usb_D=&FCF8:usb_S=&FCF9
 1400 usb_result%=usb_result%:IF usb_result%:ENDPROC
 1410 DIM usb_result% 63,mc% 99
 1420 FOR P=0 TO 1:P%=mc%:[OPT P*2
 1430   .usb_RdData
 1440   TAY:LDA #data% AND 255:STA usb_RdLp+4
 1450   LDA #data% DIV 256:STA usb_RdLp+5:LDX #0
 1460   .usb_RdLp:JSR usb_Rd:STA data%,X:INX:BNE usb_RdLp
 1470   INC usb_RdLp+5:DEY:BNE usb_RdLp:RTS
 1480   .usb_Rd:BIT usb_S:BPL usb_Rd2:BIT &FF:BPL usb_Rd
 1490   .usb_Rd2:LDA usb_D:RTS
 1500   .usb_WrData
 1510   TAY:LDA #data% AND 255:STA usb_WrLp+1
 1520   LDA #data% DIV 256:STA usb_WrLp+2:LDX #0
 1530   .usb_WrLp:LDA data%,X:JSR usb_Wr
 1540   .usb_WrLp2:LDA usb_D:BIT usb_S:BPL usb_WrLp2
 1550   INX:BNE usb_WrLp
 1560   INC usb_WrLp+2:DEY:BNE usb_WrLp:RTS
 1570   .usb_Wr:BIT usb_S:BVC usb_Wr2:BIT &FF:BPL usb_Wr
 1580   .usb_Wr2:STA usb_D:RTS
 1590 ]NEXT:ENDPROC
 1600 :