10 REM > MkMap 1.04 - 20-Aug-2015 J.G.Harston
   20 REM Create a free space map from examining directory structure
   30 :
   40 mapsz%=1024
   50 MODE7:PRINT"Build a free space map from directory"'"structure"
   60 DIM ctrl% 31,name% 31,map% mapsz%-1,fsm% 511:X%=ctrl%:Y%=X%DIV256:fs%=FNfs
   70 OSCLI"DIR $":*INFO $
   80 IFfs%=8 :drive%=VALFNgbpb(6):name%?(1+?name%)=13:drive%=VAL$(name%+1)
   90 IFfs%=8 :PROCerr(FNscsi(fsm%,8,drive%,0,2),0):secmax%=fsm%!&FC
  100 IFfs%=16:drive%=FNargs(&FE,0,0)
  110 IFfs%=16:PROCerr(FNhadfs(fsm%,&80,drive%,&46,1),&46):secmax%=fsm%!28:IFfsm%?31<128:secmax%=secmax%AND&FFFF
  120 secmax%=(secmax%AND&FFFFFF)-1
  130 PRINT"Total disk sectors (&";~secmax%+1;"): &";:INPUT""A$:IFA$<>"":secmax%=EVAL("&"+A$)-1
  140 FOR A%=0 TO mapsz%-1 STEP 4:map%!A%=0:NEXT:disp%=TRUE:VDU23,1;0;0;0;0
  150 sec%=2:num%=secmax%-sec%+1:PROCmapadd:REM Add whole disk to FSM
  160 IFfs%=8 :sec%=2 :num%=5:PROCmaprem   :REM Remove root directory
  170 IFfs%=16:sec%=70:num%=4:PROCmaprem   :REM Remove FSM and root directory
  180 CLS:PROCscan:PRINTTAB(0,2)SPC20:PROCmaplist:VDU23,1,1;0;0;0;:IFPOS:PRINT
  190 PRINT"(W)rite new free space map to disk"'"(S)ave free space map as a file"'"(Q)uit"
  200 REPEATVDU58:REPEATA$=CHR$(GETAND&DF):UNTILINSTR("WSQ",A$):VDU127
  210   IFA$="W":IFfs%=8 :PROCmapwriteA
  220   IFA$="W":IFfs%=16:PROCmapwriteH
  230   IFA$="S":PROCmapsave
  240 UNTILA$="Q":END
  250 :
  260 DEFPROCscan
  270 LOCALidx%,ret%:REPEATf$=FNgbpb8(idx%):idx%=X%!9:ret%=LENf$:IFret%:PROCfile
  280 UNTILret%=0:ENDPROC
  290 :
  300 DEFPROCfile
  310 IFADVAL(-1):IFGET=32:disp%=NOTdisp%
  320 IFfs%=8 OR disp%:VDU30:OSCLI"INFO @."+f$:vpos%=VPOS:IFvpos%<2:PRINTSPC39
  330 REM HADFS has 'read sector start' command:
  340 IFfs%=16:dir%=FNfile("@."+f$,&FD)=2:sec%=X%!14 AND &FFFFFF:num%=X%!11 AND &7FFFFF
  350 REM With ADFS need to peek output of *INFO command:
  360 IFfs%=8:PRINTTAB(11,0);:dir%=FNbyte(135,0,0)=68
  370 IFfs%=8:PRINTTAB(10-10*(vpos%=1),vpos%-1);:A$="":FORnum%=1TO6:A$=A$+CHR$FNbyte(135,0,0):VDU9:NEXT:sec%=EVAL("&"+A$)
  380 IFfs%=8:num%=5:IFNOTdir%:PRINTTAB(0,1);:A$="":FORnum%=1TO8:A$=A$+CHR$FNbyte(135,0,0):VDU9:NEXT:num%=EVAL("&"+A$):num%=(num%+255)DIV256
  390 PRINTTAB(0,3);:PROCmaprem:IFdisp%PROCmaplist
  400 IFdir%:IFfs%=16:PROClinks
  410 IFdir%:OSCLI"Dir @."+f$:PROCscan:OSCLI"Dir ^"
  420 ENDPROC
  430 :
  440 DEFPROClinks
  450 link%=sec%:REPEAT
  460   PROCerr(FNhadfs(fsm%,&80,drive%,link%,1),link%)
  470   link%=fsm%!14 AND &FFFF:IFfsm%?12>127:link%=fsm%!24 AND &FFFFFF
  480   IFlink%:sec%=link%:num%=3:PROCmaprem
  490 UNTILlink%=0
  500 ENDPROC
  510 :
  520 DEFPROCmaprem:IFnum%=0:ENDPROC
  530 ptr%=-8:REPEATptr%=ptr%+8
  540 UNTILmap%!ptr%=0 OR map%!ptr%>=sec%
  550 IFptr%>mapsz%-9:PRINTTAB(0,2);"FSM too full":END
  560 IFmap%!ptr%=sec%:map%!ptr%=map%!ptr%+num%:map%!(ptr%+4)=map%!(ptr%+4)-num%:PROCmaprem2:ENDPROC
  570 IFmap%!ptr%+map%!(ptr%+4)=sec%+num%:map%!(ptr%+4)=map%!(ptr%+4)-num%:PROCmaprem2:ENDPROC
  580 FOR A%=mapsz%-4 TO ptr% STEP -4:map%!A%=map%!(A%-8):NEXT
  590 map%!(ptr%-4)=sec%-map%!(ptr%-8)
  600 map%!(ptr%+4)=map%!ptr%+map%!(ptr%+4)-(sec%+num%)
  610 map%!(ptr%+0)=sec%+num%
  620 PROCmaprem2
  630 ENDPROC
  640 :
  650 DEFPROCmaprem2
  660 IFmap%!(ptr%+4)=0:FOR A%=ptr% TO mapsz%-1 STEP 4:map%!A%=map%!(A%+8):NEXT
  670 ENDPROC
  680 :
  690 DEFPROCmapadd:IFnum%=0:ENDPROC
  700 ptr%=-8:REPEATptr%=ptr%+8
  710 UNTILmap%!ptr%=0 OR map%!ptr%>=sec% OR map%!ptr%+map%!(ptr%+4)=sec%
  720 IFptr%>mapsz%-9:PRINTTAB(0,2);"FSM too full":END
  730 IFmap%!ptr%=0:map%!ptr%=sec%:map%!(ptr%+4)=num%:PROCmapadd2:ENDPROC
  740 IFsec%+num%=map%!ptr%:map%!ptr%=map%!ptr%-num%:PROCmapadd2:ENDPROC
  750 IFmap%!ptr%+map%!(ptr%+4)=sec%:map%!(ptr%+4)=map%!(ptr%+4)+num%:PROCmapadd2:ENDPROC
  760 FOR A%=mapsz%-4 TO ptr% STEP -4:map%!A%=map%!(A%-8):NEXT
  770 map%!ptr%=sec%:map%!(ptr%+4)=num%
  780 ENDPROC
  790 :
  800 DEFPROCmapadd2
  810 IFmap%!ptr%+map%!(ptr%+4)=map%!(ptr%+8):ptr%=ptr%+8
  820 IFptr%<>0:IFmap%!(ptr%-8)+map%!(ptr%-4)=map%!ptr%:map%!(ptr%-4)=map%!(ptr%-4)+map%!(ptr%+4):FOR A%=ptr% TO mapsz%-1 STEP 4:map%!A%=map%!(A%+8):NEXT:ENDPROC
  830 ENDPROC
  840 :
  850 DEFPROCmaplist
  860 ptr%=-8:REPEATptr%=ptr%+8
  870   PRINTFNh0(map%!ptr%,6);"+";FNh0(map%!(ptr%+4),5);SPC(1-(POS>35));
  880   IFmap%!ptr%>secmax%:PRINT"Sector too big":END
  890 UNTILmap%!ptr%=0 OR VPOS=24:PRINTSPC12;
  900 ENDPROC:=ptr%
  910 :
  920 DEFPROCmapsave
  930 INPUTLINE"File to save: "A$:IFA$<>"":OSCLI"SAVE "+A$+" "+STR$~map%+"+200 0 FFFFFD0"
  940 A$="":ENDPROC
  950 :
  960 DEFFNadfs_sum(mem%):LOCALsum%:sum%=255
  970 FOR A%=254 TO 0 STEP -1
  980   IFsum%>255:sum%=(sum%+1)AND255
  990 sum%=sum%+mem%?A%:NEXT:=sum%AND255
 1000 :
 1010 DEFPROCmapwriteA
 1020 IFptr%DIV8>82:PRINT"FSM too big":ENDPROC
 1030 PROCerr(FNscsi(fsm%,8,drive%,0,2),0)
 1040 src%=0:dst%=0:REPEAT
 1050   fsm%!dst%=map%!src%
 1060   fsm%!(dst%+256)=map%!(src%+4)
 1070   src%=src%+8:dst%=dst%+3
 1080 UNTILmap%!(src%-8)=0
 1090 fsm%?&1FE=dst%-3
 1100 REPEATfsm%!dst%=0:fsm%!(dst%+256)=0:dst%=dst%+3:UNTILdst%>&FB
 1110 fsm%!&0FC=secmax%+1
 1120 fsm%?&0FF=FNadfs_sum(fsm%)
 1130 fsm%?&1FF=FNadfs_sum(fsm%+&100)
 1140 PROCerr(FNscsi(fsm%,10,drive%,0,2),0)
 1150 OSCLI"MOUNT "+STR$drive%
 1160 ENDPROC
 1170 :
 1180 DEFPROCmapwriteH
 1190 IFptr%DIV8>35:PRINT"FSM too big":ENDPROC
 1200 PROCerr(FNhadfs(fsm%,&80,drive%,&47,1),&47)
 1210 dskid%=fsm%!16 AND &FFFF
 1220 PROCerr(FNhadfs(fsm%,&80,drive%,&46,1),&46)
 1230 fsm%?24=dskid%:fsm%?25=dskid%DIV256
 1240 IFsecmax%<&10000:fsm%!28=(secmax%+1)OR(fsm%!28 AND &7FFF0000):size%=2
 1250 IFsecmax%>&FFFF :fsm%!28=(secmax%+1)OR(fsm%!28 AND &7F000000)OR&80000000:size%=3
 1260 src%=0:dst%=&20:REPEAT
 1270   fsm%!dst%=map%!src%
 1280   fsm%!(dst%+size%)=map%!(src%+4)
 1290   src%=src%+8:dst%=dst%+size%*2
 1300 UNTILmap%!(src%-8)=0
 1310 PROCerr(FNhadfs(fsm%,&81,drive%,&46,1),&46)
 1320 OSCLI"MOUNT "+CHR$(48+drive%-7*(drive%>9))
 1330 ENDPROC
 1340 :
 1350 DEFFNscsi(addr%,cmd%,drv%,sect%,num%):LOCALfs%
 1360 fs%=FNfs:IFfs%<>8:*FADFS
 1370 X%?0=0:X%!1=addr%:X%?5=cmd%:X%?6=drv%*32+((sect%AND&1F0000)DIV65536)
 1380 X%?7=((sect%AND&FF00)DIV256):X%?8=sect%:X%!9=num%:X%!11=0
 1390 A%=&72:CALL&FFF1:A%=?X%:IFfs%<>8:OSCLI"FX143,18,"+STR$fs%
 1400 =A%
 1410 :
 1420 DEFFNhadfs(addr%,cmd%,drv%,sect%,num%):LOCALfs%:REM &80=read, &81=write
 1430 X%!0=&600:X%!2=addr%:X%!6=sect%:X%?9=drv%:X%?10=num%:X%?11=cmd%:A%=90:CALL&FFF1:=X%?11
 1440 :
 1450 DEFPROCerr(A%,S%):IFA%=0:ENDPROC
 1460 PRINT"Disk error &"FNh0(A%,2)" at "FNh0(S%,8):END
 1470 ENDPROC
 1480 :
 1490 DEFFNbyte(A%,X%,Y%)=((USR&FFF4)AND&FF00)DIV256
 1500 DEFFNfs:LOCALA%,Y%,E%:=(USR&FFDA)AND&FF
 1510 DEFFNfile(A$,A%):$name%=A$:?X%=name%:X%?1=name%DIV256:=(USR&FFDD)AND&FF
 1520 DEFFNgbpb(A%):X%!1=name%:CALL&FFD1:A%=name%+((1+?name%)AND((A%AND-2)=6)):A%?(1+?A%)=13:=$(A%+1)
 1530 DEFFNgbpb8(ptr%):X%!1=name%:X%!5=1:X%!9=ptr%:A%=8:CALL&FFD1:IFX%!5=1:=""
 1540 A%=name%+1:A%!(A%?-1)=&D20:A%?(INSTR($A%," ")-1)=13:=$A%
 1550 DEFFNh0(A%,N%)=RIGHT$("0000000"+STR$~A%,N%)
 1560 DEFFNargs(A%,Y%,ptr%):LOCALX%,E%:IF?(TOP-3)=0:E%=Y%:Y%=0
 1570 IFHIMEM<&FFFF:LOCAL!&70:X%=&70:!X%=ptr%:CALL&FFDA:=!X%
 1580 CASEATN"OS_Args",A%,Y%,ptr%TO,,ptr%:=ptr%