Home » Archimedes archive » Zipped Apps » Speech » !Dup/Dup
!Dup/Dup
This website contains an archive of files for the Acorn Electron, BBC Micro, Acorn Archimedes, Commodore 16 and Commodore 64 computers, which Dominic Ford has rescued from his private collection of floppy disks and cassettes.
Some of these files were originally commercial releases in the 1980s and 1990s, but they are now widely available online. I assume that copyright over them is no longer being asserted. If you own the copyright and would like files to be removed, please contact me.
Tape/disk: | Home » Archimedes archive » Zipped Apps » Speech |
Filename: | !Dup/Dup |
Read OK: | ✔ |
File size: | E7C1 bytes |
Load address: | 0000 |
Exec address: | 0000 |
File contents
10REM >Dup 20 30*|********************************************* 40*|* ** 50*|* DUP, the disc duplicator (c) Softcorn ** 60*|* - ** 70*|* Public Domain software, but not for use ** 80*|* or sale related to profit. ** 90*|* - ** 100*|* Copies double or single density discs ** 110*|* of 'any' format (eg ADFS L/D/E, DFS, ** 120*|* MSDOS, ATARI ST, AMIGA etc, and a wide ** 130*|* variety of protected discs) ** 180*|* - ** 190*|* BASIC + machine code + data file ** 200*|* (Edit at your peril) ** 210*|* ** 220*|********************************************* 230 240vers$="1.01" 250 251ON ERROR IF ERR=17 THEN PRINT:END ELSE REPORT:PRINT" at Line ";ERL:END 260PROClogo 270PROCinit 280 290debug=FALSE 300 310REM set,to check all tracks have a fixed number of good contiguous sects 320checkFormat=FALSE 330chkDensity%=DDensity% 340chkSectsPerTrk=9 350IF checkFormat THEN 360 PRINT"**** Checking Sectors per Track =";chkSectsPerTrk;" ****" 370ENDIF 380 390REPEAT 400 PROCmenu(copydisc,source,dest,srttrack,endtrack,firstHead,numbHeads) 410 analyse=debug OR NOT copydisc 420 PROCaction(source,dest,srttrack,endtrack,firstHead,numbHeads) 430 PRINT'" Completed:- Press SPACE to Continue (or escape to exit)"; 440 PROCpressspace 450UNTIL FALSE 460END 470 480 490DEFPROCaction(source,dest,srttrack,endtrack,firstHead,numbHeads) 500blank%=FALSE 510srtDensity%=DDensity% 520MaxDiscTrks% =INT(MaxTrks%/numbHeads) 530firstTrk%=srttrack 540TrksLeft%=1+endtrack-srttrack 550WHILE TrksLeft% > 0 560 IF TrksLeft% > MaxDiscTrks% THEN 570 numbTrks%=MaxDiscTrks% 580 ELSE 590 numbTrks%=TrksLeft% 600 ENDIF 610 PROCcopyMultiTrks(source,dest,firstTrk%,numbTrks%,firstHead,numbHeads,srtDensity%) 620 firstTrk% +=numbTrks% 630 TrksLeft% -=numbTrks% 640ENDWHILE 650ENDPROC 660 670DEFPROCcopyMultiTrks(source,dest,firstTrk%,numbTrks%,firstHead,numbHeads, RETURN srtDensity%) 680LOCAL T% 690PROCcheckDiscIn("SOURCE") 700IF (firstTrk% = srttrack) OR ((source=dest) AND copydisc) THEN 710 PROCengageDisc(source) 720ENDIF 730PROCdoMultiTrks(Read%, source, firstTrk%, numbTrks%, firstHead, numbHeads, srtDensity%) 740IF copydisc THEN 750 PROCcheckDiscIn("DESTINATION") 760 IF (firstTrk% = srttrack) OR (source=dest) THEN 770 PROCengageDisc(dest) 780 ENDIF 790 PROCdoMultiTrks(Write%, dest, firstTrk%, numbTrks%, firstHead,numbHeads, srtDensity%) 800ENDIF 810ENDPROC 820 830DEFPROCdoMultiTrks(cmd%, drv, firstTrk%, numbTrks%, firstHead, numbHeads, RETURN srtDensity%) 840LOCAL TrkDesc%, trk, head 850TrkDesc%=MainBuffer% 860FOR trk=firstTrk% TO (firstTrk% + numbTrks% -1) 870 FOR head=firstHead TO (firstHead + numbHeads -1) 880 PRINTTAB(0,VPOS); 890 IF cmd%=Read% THEN PRINT "Read "; ELSE PRINT "Write"; 900 PRINT;": Drv=";drv;" Trk=";trk;" ";TAB(21,VPOS);"Hd=";head;" "; 910 IF debug THEN PRINT 920 IF cmd%=Read% THEN 930 PROCreadSingleTrk(drv, trk, head, TrkDesc%, srtDensity%) 940 ELSE 950 PROCwriteSingleTrk(drv, trk, head, TrkDesc%) 960 ENDIF 970 TrkDesc% += TrkDescSize% + TrkDataSize% 980 NEXT 990NEXT 1000ENDPROC 1010 1020DEFPROCwriteSingleTrk(drv%, trk%, head%, TrkDesc%) 1030LOCAL count%, density%, DataBuf%, multiSectFlg%, sectInfo% 1040LOCAL lowSect%, sectSize%, sect%, sectFound%, add%, ID%, mustWrSect% 1050count% = TrkDesc%?bufNumbSect% 1060density%= TrkDesc%?bufTrkDensity% 1070DataBuf% = TrkDesc% + TrkDescSize% 1080IF count%=0 THEN 1090 REM blank track so just copy back ReadTrack data 1100 PROCwritetrackChk(drv%, trk%, head%, density%, DataBuf%) 1110ELSE 1120 PROCmakeWriteTrk(TrkDesc%, count%, density%, DataBuf%, WriteTrkBuf%,mustWrsect%) 1130 PROCwritetrackChk(drv%, trk%, head%, density%, WriteTrkBuf%) 1140 multiSectFlg%= TrkDesc%?bufMultiSectFlg% 1150 IF multiSectFlg% AND mustWrsect% THEN 1160 REM write track in one go by using memory DMA list 1170 REM but only if a sector could not be written during Format 1180 lowSect%=TrkDesc%?bufLowSect% 1190 sectSize%=TrkDesc%?bufSectSize% 1200 PROCcopyMemAddList(TrkDesc%, count%) 1210 PROCopsectors(Write% OR (1<<5),drv%,trk%,head%,lowSect%,count%,sectSize%,density%,memAddList%) 1220 IF result% <> 0 THEN 1230 multiSectFlg%=FALSE :REM Disc error 1240 PRINT" writing:- track was non-standard after all" 1250 ENDIF 1260 ENDIF 1270 IF (multiSectFlg%=0) AND mustWrsect% THEN 1280 REM catch all non-standard track formats, but only if a good 1290 REM sector could not be written during Format 1300 IF (multiSectFlg%=0) AND analyse THEN 1310 PRINT" writing:- non-standard track layout" 1320 ENDIF 1330 FOR sectFound%= 0 TO count%-1 1340 sectInfo%=FNgetSectInfo(TrkDesc%, sectFound%) 1350 IF (sectInfo% AND (NOT overIndex%))=0 THEN 1360 REM if data area was read OK (& don't have any illegal IDs) 1370 REM and it was not deleted data, then provided data area 1380 REM has not already been correctly written with writetrack, 1390 REM write the sector individually 1400 add%=FNgetDataPtr(TrkDesc%, sectFound%) 1410 ID%=FNgetSectID(TrkDesc%, sectFound%) 1420 sect%=&FF AND (ID% >> 16) 1430 sectSize%=3 AND (ID% >> 24) 1440 PROCwritesectors(drv%,trk%,head%,sect%,1,sectSize%,density%,add%) 1450 ENDIF 1460 NEXT 1470 ENDIF 1480ENDIF 1490ENDPROC 1500 1510 REM make Write Track data using Track descriptor and Read Track (the 1520 REM latter having been overlaid with correct read sector data. 1530 REM But ensure no illegal chrs appear in gaps or data area 1540 REM Output message if data area can't be recreated 1550DEFPROCmakeWriteTrk(TrkDesc%, count%, density%, ReadBuf%, WriteTrkBuf%, RETURN mustWrSect%) 1560LOCAL sectFound%, sectSize%, SrcAdd%, SrcLowAdd%, DestAdd%, DestLowAdd% 1570LOCAL convert%, info% 1580mustWrSect%=FALSE 1590DestLowAdd%=WriteTrkBuf% 1600SrcLowAdd%=ReadBuf% 1610FOR sectFound%=0 TO count%-1 1620 SrcAdd%=FNgetIDPtr(TrkDesc%, sectFound%) 1630 DestAdd%=DestLowAdd%+SrcAdd%-SrcLowAdd% 1640 info%=FNgetSectInfo(TrkDesc%,sectFound%) 1650 REM ID's AM & prior gap 1660 PROCmakeAMandgap( SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%, density%) 1670 DestAdd%!0 = FNgetSectID(TrkDesc%,sectFound%) :REM copy ID 1680 IF (info% AND errNotFound%) =0 THEN 1690 DestAdd%?4 = &F7 :REM to generate CRC 1700 DestLowAdd%= DestAdd%+5 :REM byte after CRC 1710 ELSE 1720 REM but if sector not found then copy readtrack CRC instead 1730 DestAdd%?4 = SrcAdd%?4 1740 DestAdd%?5 = SrcAdd%?5 1750 DestLowAdd%= DestAdd%+6 :REM byte after CRC 1760 ENDIF 1770 SrcLowAdd% = SrcAdd%+6 :REM point first byte after CRC 1780 SrcAdd%=FNgetDataPtr(TrkDesc%, sectFound%) 1790 IF SrcAdd%<>0 THEN 1800 REM if data area 1810 DestAdd%=DestLowAdd%+SrcAdd%-SrcLowAdd% 1820 REM data area AM & prior gap 1830 PROCmakeAMandgap(SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%, density%) 1840 SrcLowAdd% = SrcAdd% 1850 DestLowAdd%= DestAdd% 1860 SrcAdd% += FNgetDataLength(TrkDesc%, sectFound%)-1 1870 REM copy up to end of data area transfer, convert any &F5-F7 chrs 1880 PROCselcopyfwd (SrcLowAdd%, SrcAdd%, DestLowAdd%, density%, convert%) 1890 DestLowAdd% +=SrcAdd%-SrcLowAdd%+1 :REM first byte after data... 1900 SrcLowAdd% = SrcAdd%+1 :REM ..transfer areas 1910 IF (info% AND (errCRC% OR errNotFound% OR noRoomCRC%))=0 THEN 1920 REM room for CRC AND read CRC was ok AND sector was found 1930 IF (convert%=0) AND ((info% AND overIndex%)=0) THEN 1940 REM AND writetrack can correctly write data area 1950 DestLowAdd%?0 = &F7 :REM so force CRC 1960 SrcLowAdd% +=2 :REM and adjust source & 1970 DestLowAdd% +=1 :REM dest addresses accordingly 1980 REM & set sector info 1990 PROCaddSectInfo(TrkDesc%, sectFound%, dataDuringFormat%) 2000 info%=info% OR dataDuringFormat% :REM for use below 2010 ELSE 2020 IF info%AND(delData% OR longData% OR illegalTrk% OR illegalIDbyt%) THEN 2030 REM if can't write sector, but otherwise OK, and can't write 2040 REM it during format, then print error 2050 PROCprintID(TrkDesc%, sectFound%) 2060 PRINT "CAN'T make an exact copy! Sorry" 2070 DestLowAdd%?0 = &F7 :REM but set CRC anyway 2080 SrcLowAdd% +=2 :REM and adjust pointers 2090 DestLowAdd% +=1 2100 ENDIF 2110 ENDIF 2120 ENDIF 2130 IF (info% AND (NOT overIndex%))=0 THEN 2140 REM must write the sector 2150 mustWrSect%=TRUE 2160 ENDIF 2170 ENDIF 2180NEXT 2190SrcAdd%=TrkDesc%!bufEndValidData% 2200REM copy to end of valid read track data 2210PROCselcopyfwd (SrcLowAdd%, SrcAdd%, DestLowAdd%, density%, convert%) 2220PROCfillEndOfTrk( SrcAdd%+1-ReadBuf% + WriteTrkBuf% , density%) 2230ENDPROC 2240 2250 REM fills to end of write buffer with relevant filler byte 2260DEFPROCfillEndOfTrk( add% , density%) 2270LOCAL value% 2280IF density%=DDensity% THEN 2290 value%=&4E 2300ELSE 2310 value%=&FF 2320ENDIF 2330PROCfill(value%, add%, WriteTrkBuf% + TrkDataSize% - add%) 2340ENDPROC 2350 2360 REM copies area of store but converts any illegal chrs(for writeTrack) 2370 REM Sets convert%=TRUE if it has to convert any char 2380DEFPROCselcopyfwd(SrcLowAdd%,SrcAdd%,DestLowAdd%,density%,RETURN convert%) 2390LOCAL low%, hi% 2400PROCgetIllegal(density%, low%, hi%) 2410CALL selcopyfwd, SrcLowAdd%, SrcAdd%, DestLowAdd%, low%, hi%, convert% 2420ENDPROC 2430 2440 REM setup illegal chr range for writetrack 2450DEFPROCgetIllegal(density%, RETURN low%, RETURN hi%) 2460IF density% = DDensity% THEN 2470 low%=&F5:hi%=&F7 2480ELSE 2490 low%=&F5:hi%=&FE 2500ENDIF 2510ENDPROC 2520 2530DEFPROCmakeAMandgap(SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%, density%) 2540IF density%=DDensity% THEN 2550 PROCDDmakeAMandgap(SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%) 2560ELSE 2570 PROCSDmakeAMandgap(SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%) 2580ENDIF 2590ENDPROC 2600 2610 REM DD only 2620DEFPROCDDmakeAMandgap(SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%) 2630LOCAL V%, I%, J%, convert% 2640DestAdd%?-1 = SrcAdd%?-1 :REM copy Mark 2650DestAdd%?-2 = &F5 :REM set AM 2660DestAdd%?-3 = &F5 2670DestAdd%?-4 = &F5 2680J%=DestAdd% - DestLowAdd% 2690I%=5 2700IF I%<=J% THEN DestAdd%?-I% =0:I% +=1 :REM and set preceeding 2 bytes to 0 2710IF I%<=J% THEN DestAdd%?-I% =0:I% +=1 :REM (DestLowAdd permitting) 2720V%=SrcAdd%?-I% 2730WHILE (V%=(SrcAdd%?-I%)) AND (I% <= J%) 2740 DestAdd%?-I% = 0 :REM and all preceeding bytes to 0 while 2750 I% +=1 :REM source bytes don't change value 2760ENDWHILE :REM (DestLowAdd permitting) 2770IF I%<=J% THEN 2780 REM then copy preceding gap back to SrcLowadd% 2790 PROCselcopyfwd (SrcLowAdd%, SrcAdd%-I%, DestLowAdd%, DDensity%,convert%) 2800ENDIF 2810ENDPROC 2820 2830 REM SD only 2840DEFPROCSDmakeAMandgap(SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%) 2850LOCAL I%, J%, convert% 2860DestAdd%?-1 = &F0 OR (SrcAdd%?-1) :REM copy Mark 2870I%=2 2880J%=DestAdd% - DestLowAdd% 2890WHILE (I% <= J%) AND (I% <= 7) 2900 DestAdd%?-I% = 0 :REM and preceeding by 6 bytes of 0 2910 I% +=1 :REM (DestLowAdd permitting) 2920ENDWHILE 2930IF I%<=J% THEN 2940 REM then copy preceding gap back to SrcLowadd% 2950 PROCselcopyfwd (SrcLowAdd%, SrcAdd%-I%, DestLowAdd%, SDensity%,convert%) 2960ENDIF 2970ENDPROC 2980 2990 REM tries both DD & SD before giving up, & returns density found 3000DEFPROCreadSingleTrk(drv, trk, head, TrkDesc%, RETURN srtDensity%) 3010LOCAL endadd%, DataBuf%, density%, count% 3020TrkDesc%?bufTrk% = trk 3030TrkDesc%?bufHead% = head 3040density%=srtDensity% 3050REPEAT 3060 TrkDesc%?bufTrkDensity% = density% 3070 DataBuf% = TrkDesc% + TrkDescSize% 3080 endadd% = DataBuf% + MaxSect% -1 + (MaxTrkUnformat% >> (2-density%)) 3090 REPEAT 3100 REM to test ReadTrack overrun, only repeat if ReadTrack overflow 3110 PROCfill(&55, DataBuf%, endadd%-DataBuf%) 3120 PROCreadtrack(drv, trk, head, density%, DataBuf%) 3130 endValidData%=endadd%-1 3140 CALL findchangeback, endValidData% 3150 UNTIL (endadd% - endValidData%) > 500 3160 TrkDesc%!bufEndValidData% = endValidData% 3170 PROCanalyseTrk(TrkDesc%, DataBuf%, density%) 3180 PROCreadTrksSects (drv, TrkDesc%, density%) 3190 count%=TrkDesc%?bufNumbSect% 3200 IF count%=0 THEN 3210 density%= density% EOR 3 :REM if no sectors toggle SD/DD 3220 ELSE 3230 IF srtDensity%<>density% THEN 3240 PRINT" Continues as "; 3250 IF density%=2 THEN PRINT"Double"; ELSE PRINT"Single"; 3260 PRINT" Density" 3270 blank%=FALSE 3280 srtDensity%=density% :REM if sectors, update srtDensity 3290 ENDIF 3300 ENDIF 3310UNTIL density%=srtDensity% 3320IF blank% THEN 3330 IF count%<>0 THEN 3340 blank%=FALSE 3350 PRINT" No longer Blank" 3360 ENDIF 3370ELSE 3380 IF count%=0 THEN 3390 blank%=TRUE 3400 PRINT" Continues as Blank" 3410 ENDIF 3420ENDIF 3430PROCprintUnusual(TrkDesc%, count%) 3440IF checkFormat THEN 3450 IF ((TrkDesc%?bufNumbSect%) <> chkSectsPerTrk) OR ((TrkDesc%?bufMultiSectFlg%)= FALSE) OR (density% <> chkDensity%) THEN 3460 PROCprintLine(" ******* Track format failed check *******") 3470 ENDIF 3480ENDIF 3490ENDPROC 3500 3510 REM Analyses Read Track data and sets up Track descriptor accordingly 3520 REM Ignore any 'apparent' sectors without associated data areas (or 3530 REM it appear as 'Sector not found' in any case) 3540 REM if anything unusual unset multiSectFlg% 3550DEFPROCanalyseTrk(TrkDesc%, DataBuf%, density%) 3560LOCAL count%, add%, IDadd%, dataadd%, mark%, IDcorrupt, multiSectFlg% 3570LOCAL ID%, bytes%, firstIDsyncAdd%, endLastData%, remadeOK% 3580multiSectFlg%=TRUE :REM ie default 3590count%=0 3600add%=DataBuf% + 2 :REM first ID AM must be at least 2 bytes into buffer 3610endadd%=TrkDesc%!bufEndValidData% 3620REPEAT 3630 PROCfindID(add%, endadd%, density%) 3640 IF add%<>0 THEN 3650 IF count%=maxSectsAllowed% THEN PRINT'"Failed:- too many sectors":STOP 3660 IDadd%=add% 3670 IF count%=0 THEN firstIDsyncAdd%=IDadd%-4 3680 PROCsetIDPtr(TrkDesc%, count%, IDadd%) 3690 PROCsetSectInfo(TrkDesc%, count%, 0) :REM 0=default value 3700 PROCsetSectID(TrkDesc%, count%, IDadd%!0) :REM save ID 3710 add% +=6 3720 IF (endadd%-add%) < 128 THEN 3730 REM if there is a data area it overflows index, so 3740 REM wrap around data from start of track (before first ID) 3750 REM to ensure I see the data mark 3760 CALL copyfwd, DataBuf%, firstIDsyncAdd%, endadd% 3770 endadd% += firstIDsyncAdd%-DataBuf% 3780 ENDIF 3790 PROCfindAM (add%,endadd%,density%) :REM aim to find Data Area 3800 dataadd%=0 :REM default if no DataArea 3810 IF add% <>0 THEN 3820 mark%=(add%?-1) :REM must be between &F8-&FF 3830 IF mark% >= &FC THEN 3840 add% -= 10 :REM if not Data Area, turn back add% 3850 ELSE 3860 dataadd%=add% 3870 IF mark%= &F8 THEN 3880 REM its deleted data 3890 multiSectFlg% = FALSE 3900 PROCaddSectInfo(TrkDesc%, count%, delData%) 3910 ENDIF 3920 ENDIF 3930 ENDIF 3940 PROCsetDataPtr(TrkDesc%, count%, dataadd%) 3950 IF dataadd%=0 THEN 3960 REM no data (so the ID will be ignored), so for TRACE sake only 3970 multiSectFlg% = FALSE 3980 PROCaddSectInfo(TrkDesc%, count%, noData%) 3990 ELSE 4000 REM There is a data area, so only then keep a record of sector 4010 REM and only then test for corrupt ID's or illegal ID's 4020 IF (IDadd%?3 > 3) THEN 4030 REM probably corrupt ID (or maybe not ID just data) 4040 IF density%=DDensity% THEN 4050 REM remake ID & set =ID% then overwrite saved state 4060 CALL remakeID, mapID%, IDadd%, ID%, remadeOK% 4070 IF remadeOK%<>0 THEN 4080 REM save remaded ID only if remadeOK% 4090 PROCsetSectID(TrkDesc%, count%, ID%) 4100 ENDIF 4110 ELSE 4120 REM leave it as it is, if its Single Density 4130 ENDIF 4140 ENDIF 4150 PROCtestIllegalId(TrkDesc%, count%, density%, multiSectFlg%) 4160 count%=count%+1 4170 ENDIF 4180 ENDIF 4190UNTIL add%=0 4200IF count%<>0 THEN 4210 ID%=FNgetSectID(TrkDesc%, count%-1) 4220 bytes%=1 << (7+ (3 AND (ID% >>24))) 4230 endLastData%= bytes%+5+4 + FNgetDataPtr(TrkDesc%, count%-1) 4240 IF endLastData% > TrkDesc%!bufEndValidData% THEN 4250 REM last sector's data area overflows Index, so mark the fact 4260 PROCaddSectInfo(TrkDesc%, count%-1, overIndex%) 4270 REM & change end of valid data 4280 endadd% = firstIDsyncAdd%-DataBuf% + TrkDesc%!bufEndValidData% 4290 IF endadd% < endLastData% THEN 4300 endLastData%=endadd% 4310 ENDIF 4320 REM set end of valid data to shorter of - 4330 REM 9 bytes after data area CRC (allows for min_gap=5 + ID_AM=4), 4340 REM or start of first sector wrapped around 4350 REM (ensures first ID will not be overwritten) 4360 TrkDesc%!bufEndValidData% = endLastData% 4370 ENDIF 4380ENDIF 4390TrkDesc%?bufMultiSectFlg%= multiSectFlg% 4400TrkDesc%?bufNumbSect% = count% 4410ENDPROC 4420 4430 REM on exit add% = address of ID (found) or 0 (NOT found) 4440DEFPROCfindID (RETURN add%, endadd%, density%) 4450IF density%=DDensity% THEN 4460 CALL DDfindID, add%, endadd% 4470ELSE 4480 REPEAT 4490 CALL SDfindID, add%, endadd% 4500 UNTIL (add%=0) OR ((add%?3)<= 3):REM extra test for valid Size for SD 4510ENDIF 4520ENDPROC 4530 4540 REM on exit add% = address of address mark (found) or 0 (NOT found) 4550DEFPROCfindAM (RETURN add%, endadd%, density%) 4560IF density%=DDensity% THEN 4570 CALL DDfindAM, add%, endadd% 4580ELSE 4590 CALL SDfindAM, add%, endadd% 4600ENDIF 4610ENDPROC 4620 4630 REM IF Multi sector, setup memory pointer list & do one read 4640 REM ELSE (or if above read fails) read each sector individually 4650 REM if multi-sector read fails unset its flag 4660DEFPROCreadTrksSects (drv%, TrkDesc%, density%) 4670LOCAL sectFound%,trk%,head%,lowSect%,sectSize%,add%,IDadd%,sect% 4680LOCAL multiSectFlg%, count%, bytes%, info%, notRealSect% 4690REPEAT 4700 notRealSect%=FALSE :REM break out for 'Sector not found' & re-try 4710 count% = TrkDesc%?bufNumbSect% 4720 IF count%<>0 THEN 4730 trk% =TrkDesc%?bufTrk% 4740 head%=TrkDesc%?bufHead% 4750 multiSectFlg%= TrkDesc%?bufMultiSectFlg% 4760 REM -- many of below can set multiSectFlg% = FALSE 4770 PROCsetLengths(TrkDesc%, count%, multiSectFlg%) 4780 IF multiSectFlg% THEN 4790 PROCsetMultiSect (TrkDesc%, count%, multiSectFlg%) 4800 ENDIF 4810 IF multiSectFlg% THEN 4820 REM read track in one go but using memory DMA list 4830 lowSect%=TrkDesc%?bufLowSect% 4840 sectSize%=TrkDesc%?bufSectSize% 4850 PROCcopyMemAddList(TrkDesc%, count%) 4860 PROCopsectors(Read% OR (1<<5),drv%,trk%,head%,lowSect%,count%,sectSize%,density%,memAddList%) 4870 IF result% <> 0 THEN multiSectFlg%=FALSE :REM Disc error 4880 ENDIF 4890 IF multiSectFlg%=0 THEN 4900 IF analyse THEN 4910 PRINT" reading:- non-standard track layout?" 4920 ENDIF 4930 REM catch all,deleted data,non-consecutive IDs, ID & DataArea error 4940 REM illegal IDs Trk, and data areas that overlay next ID 4950 FOR sectFound%= 0 TO count%-1 4960 IF notRealSect%=FALSE THEN 4970 info%=FNgetSectInfo(TrkDesc%,sectFound%) 4980 IF (info% AND (illegalTrk% OR noData%)) = 0 THEN 4990 REM legal ID trk (on Arc)& data area(incl deldata) then read 5000 add%=FNgetDataPtr(TrkDesc%, sectFound%) 5010 ID%=FNgetSectID(TrkDesc%, sectFound%) 5020 sect%=&FF AND (ID% >> 16) 5030 bytes%=FNgetDataLength(TrkDesc%, sectFound%) 5040 PROCopbytes(Read%,drv%,trk%,head%,sect%,bytes%,density%,add%) 5050 IF result%<>0 THEN 5060 PROCreadSectErr(TrkDesc%, sectorFound%,result%,notRealSect%) 5070 ENDIF 5080 ELSE 5090 REM maybe this is not a sector atall but ID pattern is 5100 REM part of a data area. Assume it is not a sector if a 5110 REM previous sector has longData or noRoomCRC set. 5120 REM NOT a FOOL PROOF test but probably good enough 5130 IF sectFound%<>0 THEN 5140 IF ((longData% OR noRoomCRC%) AND FNgetSectInfo(TrkDesc%, sectFound%-1)) THEN 5150 notRealSect%=TRUE 5160 PROCdeleteSect(TrkDesc%, sectorFound%) 5170 ENDIF 5180 ENDIF 5190 ENDIF 5200 ENDIF 5210 NEXT 5220 ENDIF 5230 TrkDesc%?bufMultiSectFlg%= multiSectFlg% :REM in case its reset 5240 ENDIF 5250UNTIL notRealSect%=FALSE 5260ENDPROC 5270 5280DEFPROCreadSectErr(TrkDesc%, sectorFound%, result%, RETURN notRealSect%) 5290REM Special actions on read sector error (eg Sector Not Found) 5300LOCAL ID%, count%, sectSize% 5310IF (result% AND errNotFound%) <>0 THEN 5320 REM If Sector Not Found 5330 ID%=FNgetSectID(TrkDesc%, sectFound%) 5340 IF ID% <> !FNgetIDPtr(TrkDesc%, sectFound%) THEN 5350 REM If corrupt ID & Not Found, check for alternative for corrupt ID 5360 CASE ID% OF 5370 WHEN &014B011C: ID%=&014F011C :notRealSect%=TRUE 5380 WHEN &02B70029: ID%=&02D50029 :notRealSect%=TRUE 5390 WHEN &02B40029: ID%=&03FC0029 :notRealSect%=TRUE 5400 WHEN &00550129: ID%=&00770129 :notRealSect%=TRUE 5410 WHEN &00640129: ID%=&00A40129 :notRealSect%=TRUE 5420 WHEN &01770129: ID%=&01B70129 :notRealSect%=TRUE 5430 WHEN &026A0129: ID%=&02D60129 :notRealSect%=TRUE 5440 WHEN &02FA0129: ID%=&03560129 :notRealSect%=TRUE 5450 WHEN &03660129: ID%=&03A60129 :notRealSect%=TRUE 5460 WHEN &02B50129: ID%=&03FD0129 :notRealSect%=TRUE 5470 WHEN &02750129: ID%=&02B50129 :notRealSect%=TRUE 5480 REM nb last one must be after penultimate one (as both are 5490 REM alternative patterns for the same corrupt ID) 5500 ENDCASE 5510 ENDIF 5520 IF notRealSect%=TRUE THEN 5530 REM Sector Not Found AND corrupt ID has an alternative 5540 REM so set stored value of ID% to alternative 5550 PROCsetSectID(TrkDesc%, sectFound%, ID%) 5560 ELSE 5570 REM Sector Not Found AND not an alternative for corrupt ID 5580 REM so remove sector from track descriptor, as it is NOT a ID 5590 notRealSect%=TRUE 5600 PROCdeleteSect(TrkDesc%, sectorFound%) 5610 ENDIF 5620ELSE 5630 REM flag disc error on sector, in sect info, if sector was found 5640 PROCaddSectInfo(TrkDesc%, sectFound%, result% AND &FF) 5650ENDIF 5660ENDPROC 5670 5680DEFPROCdeleteSect(TrkDesc%, sectorFound%) 5690REM this was not a sector but part of a data area (or gap) 5700REM so remove sector from track descriptor and reset 5710REM any longData or noRoomCRC in the previous sector info. 5720LOCAL I%, J%, infoSize%, count% 5730count% = TrkDesc%?bufNumbSect% 5740infoSize%= 1 << Log2SectInfoSize% 5750J%=TrkDesc% + bufSectDesc% + sectFound%*infoSize% 5760FOR I%=0 TO (count%-1-sectFound%)*infoSize%-1 STEP 4 5770 J%!I%=J%!(I%+infoSize%) 5780NEXT 5790IF sectFound%<>0 THEN 5800 PROCsetSectInfo(TrkDesc%, sectFound%-1,FNgetSectInfo(TrkDesc%, sectFound%-1) AND (NOT(longData% OR noRoomCRC%))) 5810ENDIF 5820TrkDesc%?bufNumbSect% -= 1 :REM decrement stored count 5830ENDPROC 5840 5850 REM if track is incorrect OR head, sector, or sectSize are =&F5-&F7 5860 REM then unset multiSectFlg%, set sector info and print message 5870 REM assumes ID has been saved (after any remaking of corrupt ID) 5880DEFPROCtestIllegalId(TrkDesc%, count%, density%, RETURN multiSectFlg%) 5890LOCAL ID%, I%, T%, low%, hi% 5900ID%=FNgetSectID(TrkDesc%, count%) 5910IF (ID% AND &FF) <> TrkDesc%?bufTrk% THEN 5920 REM ID's Trk is not real track (illegal on Arc) 5930 multiSectFlg%=FALSE 5940 PROCaddSectInfo(TrkDesc%, count%, illegalTrk%) 5950ENDIF 5960IF ((ID% >> 8) AND &FF) <> TrkDesc%?bufHead% THEN 5970 multiSectFlg%=FALSE: REM being over safe? 5980 IF debug THEN 5990 REM **** can this be done elsewhere ****??? 6000 PROCprintID(TrkDesc%, count%) 6010 PRINT"Head incorrect, but acceptable" 6020 ENDIF 6030ENDIF 6040PROCgetIllegal(density%, low%, hi%) 6050FOR I%=1 TO 3 6060 T%=(ID% AND &FF) 6070 IF (T% >= low%) AND (T% <= hi%) THEN 6080 REM I can't write ID as it has illegal writeTrack bytes 6090 multiSectFlg%=FALSE 6100 PROCaddSectInfo(TrkDesc%, count%, illegalIDbyt%) 6110 ENDIF 6120 ID%=(ID% >> 8) 6130NEXT 6140ENDPROC 6150 6160 REM For each sector set Length of max read data transfer. 6170 REM In case of protected disc, ensure it cannot overwrite next ID 6180 REM around track (actually 'next ID - 4'). 6190 REM The set Length will also be used during any data area write, BUT 6200 REM If any sector write data might overwrite 'next ID - 4' (ie gap 6210 REM from end of data to 'next ID - 4' is less than 5 bytes), then 6220 REM set Long data flag and unset multisector flag. Furthermore if 6230 REM there is not even room for a 'format generated CRC', also 6240 REM set no-Room-CRC flag. 6250DEFPROCsetLengths(TrkDesc%, count%, RETURN multiSectFlg%) 6260LOCAL followingIDadd%, sectFound%, bytes%, add%, IDadd%, gap%, ID% 6270LOCAL density% 6280density%= TrkDesc%?bufTrkDensity% 6290followingIDadd%=TrkDesc%!bufEndValidData% 6300FOR sectFound%= count%-1 TO 0 STEP -1 6310 IDadd%=FNgetIDPtr(TrkDesc%, sectFound%) 6320 add%=FNgetDataPtr(TrkDesc%, sectFound%) 6330 ID%=FNgetSectID(TrkDesc%, sectFound%) 6340 bytes%=1 << (7+ ((ID% >> 24) AND 3)) 6350 gap% = (followingIDadd%-4) - (add%+bytes%) 6360 IF gap% < 5 THEN 6370 PROCaddSectInfo(TrkDesc%, sectFound%, longData%) 6380 multiSectFlg%=FALSE 6390 IF gap% < 2 THEN 6400 PROCaddSectInfo(TrkDesc%, sectFound%, noRoomCRC%) 6410 IF gap% < 0 THEN 6420 bytes% += gap% :REM reduce transfer size if it extends to ID-4 6430 ENDIF 6440 ENDIF 6450 ENDIF 6460 PROCsetDataLength(TrkDesc%, sectFound%, bytes%) 6470 followingIDadd%=IDadd% 6480NEXT 6490ENDPROC 6500 6510 REM find if sector numbers are consecutive, and all have the same size 6520 REM if not set Multi sector False 6530 REM if Multi sector still set, setup Multi-sector descriptor and 6540 REM memory pointer list 6550DEFPROCsetMultiSect(TrkDesc%, count%, RETURN multiSectFlg%) 6560LOCAL max%,min%,sector%,IDadd%,dataadd%,sectNumb%, restOfID%, sectSize% 6570LOCAL ID% 6580max%=-1 :min%=256 6590FOR sector%=0 TO count%-1 6600 ID%=FNgetSectID(TrkDesc%, sector%) 6610 IF sector%=0 THEN 6620 restOfID%=&FF00FFFF AND ID% 6630 sectSize%=(ID% >> 24) AND 3 6640 ELSE 6650 IF restOfID% <> (&FF00FFFF AND ID%) THEN 6660 multiSectFlg%=FALSE 6670 ENDIF 6680 ENDIF 6690 sectNumb%=&FF AND (ID% >> 16) 6700 IF sectNumb% > max% THEN max%=sectNumb% 6710 IF sectNumb% < min% THEN min%=sectNumb% 6720NEXT 6730IF ((max%-min%) <> (count%-1)) THEN multiSectFlg%=FALSE 6740IF multiSectFlg% THEN 6750 TrkDesc%?bufLowSect% = min% 6760 TrkDesc%?bufSectSize% =sectSize% 6770 FOR sector%=0 TO count%-1 6780 dataadd%=FNgetDataPtr(TrkDesc%, sector%) 6790 ID%=FNgetSectID(TrkDesc%, sector%) 6800 sectNumb%=(&FF AND (ID% >> 16)) - min% 6810 TrkDesc%!(bufMemAddList% + (sectNumb%<<3))= dataadd% :REM memory add 6820 TrkDesc%!(bufMemAddList%+4+(sectNumb%<<3))=1 << (7+sectSize%):REM size 6830 NEXT 6840ENDIF 6850ENDPROC 6860 6870 REM copy MultiSector Memory Address List into Buffer for use 6880DEFPROCcopyMemAddList(TrkDesc%, count%) 6890LOCAL I%,J% 6900J%=((count%-1)<<3)+4 6910FOR I%=0 TO J% STEP 4 6920 memAddList%!I%=TrkDesc%!(bufMemAddList%+I%) 6930NEXT 6940ENDPROC 6950 6960 REM Print any unusual sector followed by reason why 6970 REM IF debug print all sectors 6980DEFPROCprintUnusual(TrkDesc%, count%) 6990LOCAL sectFound%, info%, discerr% 7000IF count%<>0 THEN 7010 FOR sectFound%=0 TO count%-1 7020 info%=&FFFFFF AND FNgetSectInfo(TrkDesc%, sectFound%) 7030 IF debug OR (info%<>0) THEN 7040 PROCprintID(TrkDesc%, sectFound%) 7050 IF debug THEN PRINT 7060 ENDIF 7070 IF info% <> 0 THEN 7080 IF (info% AND illegalTrk%) <> 0 THEN 7090 PROCsectText("Illegal ID Trk (on Arc)") 7100 ENDIF 7110 IF (info% AND illegalIDbyt%) <> 0 THEN 7120 PROCsectText("Illegal ID byte") 7130 ENDIF 7140 IF (info% AND noData%) <> 0 THEN 7150 PROCsectText("Has NO data area") 7160 ENDIF 7170 IF (info% AND delData%) <> 0 THEN 7180 PROCsectText("Deleted data area") 7190 ENDIF 7200 IF (info% AND longData%) <> 0 THEN 7210 PROCsectText("Data TOO long:- Overlaps next ID") 7220 ELSE 7230 IF (info% AND noRoomCRC%) <> 0 THEN 7240 PROCsectText("Data long:- gap too short, write") 7250 ENDIF 7260 ENDIF 7270 IF (info% AND overIndex%) <> 0 THEN 7280 PROCsectText("Data overflows index") 7290 ENDIF 7300 discerr% = (info% AND &FF) 7310 IF discerr% <> 0 THEN 7320 CASE (discerr% AND (errNotFound% OR errCRC%)) OF 7330 WHEN (errNotFound% OR errCRC%):PROCsectText("ID CRC error") 7340 WHEN errCRC%: PROCsectText("Data area CRC error") 7350 WHEN errNotFound%: PROCsectText("Sector not Found") 7360 ENDCASE 7370 IF discerr% AND NOT(errCRC% OR errNotFound%) THEN 7380 PROCsectText("Unknown disc err &"+STR$~(discerr%)) 7390 ENDIF 7400 ENDIF 7410 ENDIF 7420 NEXT 7430ENDIF 7440ENDPROC 7450 7460 7470REM ** all following assume count% starts from 0 as first sector ** 7480 7490DEFPROCsetIDPtr(TrkDesc%, count%, add%) 7500!FNsectDesc(TrkDesc%, count%, bufIDptr%) = add% 7510ENDPROC 7520 7530DEFFNgetIDPtr(TrkDesc%, count%) 7540=!FNsectDesc(TrkDesc%, count%, bufIDptr%) 7550 7560DEFPROCsetDataPtr(TrkDesc%, count%, add%) 7570!FNsectDesc(TrkDesc%, count%, bufDataptr%) = add% 7580ENDPROC 7590 7600DEFFNgetDataPtr(TrkDesc%, count%) 7610=!FNsectDesc(TrkDesc%, count%, bufDataptr%) 7620 7630DEFPROCsetDataLength(TrkDesc%, count%, bytes%) 7640!FNsectDesc(TrkDesc%, count%, bufTransLength%) = bytes% 7650ENDPROC 7660 7670DEFFNgetDataLength(TrkDesc%, count%) 7680=!FNsectDesc(TrkDesc%, count%, bufTransLength%) 7690 7700DEFPROCsetSectID(TrkDesc%, count%, ID%) 7710!FNsectDesc(TrkDesc%, count%, bufSectID%) = ID% 7720ENDPROC 7730 7740DEFFNgetSectID(TrkDesc%, count%) 7750=!FNsectDesc(TrkDesc%, count%, bufSectID%) 7760 7770DEFPROCsetSectInfo(TrkDesc%, count%, value%) 7780!FNsectDesc(TrkDesc%, count%, bufSectInfo%) = value% 7790ENDPROC 7800 7810DEFPROCaddSectInfo(TrkDesc%, count%, value%) 7820PROCsetSectInfo(TrkDesc%,count%, value% OR FNgetSectInfo(TrkDesc%,count%)) 7830ENDPROC 7840 7850DEFFNgetSectInfo(TrkDesc%, count%) 7860=!FNsectDesc(TrkDesc%, count%, bufSectInfo%) 7870 7880DEFFNsectDesc(TrkDesc%, count%, offset%) 7890=TrkDesc%+bufSectDesc% + offset% + (count% << Log2SectInfoSize%) 7900 7910 7920DEFPROCfill(value%, start%, bytelen%) 7930B%=value%: C%=start%: D%=bytelen% 7940CALL fill 7950ENDPROC 7960 7970DEFPROCsetDefaultDiscRec(drv, density) 7980CASE density OF 7990 WHEN SDensity%: PROCsetDiscRec(drv, 10, 1, SDensity%) 8000 WHEN DDensity%: PROCsetDiscRec(drv, 5, 3, DDensity%) 8010ENDCASE 8020ENDPROC 8030 8040DEFPROCsetDiscRec(drv, sectPerTrk, sectSize, density) 8050sectSize=sectSize AND &3 :REM only bits used 8060discRec%?0 = 7+sectSize 8070discRec%?1 = sectPerTrk 8080discRec%?2 = 2 :REM heads 8090discRec%?3 = density 8100discRec%!16 = 160*(discRec%?1)*(1 << (discRec%?0)): REM disc size in bytes 8110discRec%?34 = drv 8120discRec%!64 = &20000000 8130ENDPROC 8140 8150REM assumes that discRec% is already setup 8160DEFFNdiscAdd(trk, head, sect) 8170=(sect + (head + trk*(discRec%?2))*(discRec%?1) ) << (discRec%?0) 8180 8190DEF PROCengageDisc(drv) 8200REM ensure that disc is properly engaged & rotating 8210LOCAL T% 8220PROCrtz(drv) :REM rotate disc via rtz 8230T%=TIME 8240REPEAT UNTIL TIME >(T%+400) :REM and wait for disc to stop - Engaged 8250PROCrtz(drv) :REM then rotate it again 8260ENDPROC 8270 8280DEFPROCrtz(drv) 8290PROCoptrack(Restore%, drv, 0, 0, DDensity%, 0) 8300ENDPROC 8310 8320DEFPROCseek(drv, trk) 8330PROCoptrack(Seek%, drv, trk, 0, DDensity%, 0) 8340ENDPROC 8350 8360DEFPROCreadtrack(drv, trk, head, density, dmaAdd%) 8370PROCoptrack(ReadTrack%, drv, trk, head, density, dmaAdd%) 8380ENDPROC 8390 8400DEFPROCwritetrack(drv, trk, head, density, dmaAdd%) 8410PROCoptrack(WriteTrack%, drv, trk, head, density, dmaAdd%) 8420ENDPROC 8430 8440DEFPROCwritetrackChk(drv%, trk%, head%, density%, dmaAdd%) 8450REM do write track, if error(write protected) print message & repeat 8460REPEAT 8470 PROCwritetrack(drv%, trk%, head%, density%, dmaAdd%) 8480 IF result%<>0 THEN 8490 PROCprintSrt("**** Come on, remove the disc's write protect, then press space ****") 8500 PROCpressspace 8510 PRINT 8520 ENDIF 8530UNTIL result%=0 8540ENDPROC 8550 8560DEFPROCoptrack(cmd%, drv, trk, head, density, dmaAdd%) 8570REM uses default dummy discRec%, so ADFS doesn't try to read disc format 8580PROCsetDefaultDiscRec(drv, density) 8590PROCdiscop(cmd%, drv, FNdiscAdd(trk, head, 0), dmaAdd%, 0) 8600ENDPROC 8610 8620DEFPROCreadsectors(drv,trk,head,sect,numbSect,sectSize,density,dmaAdd%) 8630PROCopsectors(Read%,drv,trk,head,sect,numbSect,sectSize,density,dmaAdd%) 8640ENDPROC 8650 8660DEFPROCwritesectors(drv,trk,head,sect,numbSect,sectSize,density,dmaAdd%) 8670PROCopsectors(Write%,drv,trk,head,sect,numbSect,sectSize,density,dmaAdd%) 8680ENDPROC 8690 8700REM -- Read bytes, starting from begining of a sector 8710REM -- Bytes need not be whole sectors, but must not overflow track 8720DEFPROCopbytes(cmd%, drv, trk, head, sect, bytes%, density, dmaAdd%) 8730LOCAL sectPerTrk, discAdd%, sectSize, numbSect 8740sectSize=-1 8750REM repeat, ends up fooling ADFS so it always works 8760REPEAT 8770 sectSize +=1 8780 numbSect=1+((bytes%-1) >>(7+sectSize)) 8790UNTIL numbSect <&100 :REM must be single byte 8800sectPerTrk = sect + numbSect :REM fool ADFS, so it always works 8810PROCsetDiscRec(drv, sectPerTrk, sectSize, density) 8820discAdd% = FNdiscAdd(trk, head, sect) 8830PROCdiscop(cmd%, drv, discAdd%, dmaAdd%, bytes%) 8840ENDPROC 8850 8860DEFPROCopsectors(cmd%,drv,trk,head,sect,numbSect,sectSize,density,dmaAdd%) 8870LOCAL sectPerTrk, discAdd% 8880sectPerTrk = sect + numbSect :REM fool ADFS, so it always works 8890PROCsetDiscRec(drv, sectPerTrk, sectSize, density) 8900discAdd% = FNdiscAdd(trk, head, sect) 8910PROCdiscop(cmd%, drv, discAdd%, dmaAdd%, numbSect << (discRec%?0) ) 8920ENDPROC 8930 8940 REM on exit result%=0 or disc error number 8950DEFPROCdiscop(cmd%, drv, discAdd%, dmaAdd%, bytes%) 8960R1%= cmd% OR (discRec% << 6) 8970R2%= (drv << 29) + (discAdd% AND &1FFFFFFF) 8980SYS XAdfsSwi% , 0, R1%, R2%, dmaAdd%, bytes% TO result%,,nextDiscByte%, nextMemByte%, BytesLeft% 8990IF result% THEN PROCdiscerr(cmd%, result%) 9000ENDPROC 9010 9020DEFPROCdiscerr(cmd%, RETURN result%) 9030IF (result% AND (1 << 31)) THEN 9040 PRINT'"Error &";~result% AND &3FFFFFFF:END 9050ELSE 9060 IF ((!result%) AND &FFFFFF)=AdfsDiscErr% THEN 9070 IF analyse AND NOT ((cmd%=ReadTrack%)AND((result%?3)=4)) THEN 9080 PROCprint_err(cmd%, result%) 9090 ENDIF 9100 result%=result%?3 9110 ELSE 9120 IF ((!result%) AND &FFFFFF) <> AdfsWriteProtect% THEN 9130 PROCprint_err(cmd%, result%): END 9140 ENDIF 9150 ENDIF 9160ENDIF 9170ENDPROC 9180 9190REM 1770/1772 or ADFS restrictions/characteristics 9200REM ADFS - will not allow you to write deleted data other than via 9210REM the format(write track) command, nor will it signal any error 9220REM on reading deleted data (including multi-sector deleted data). 9230REM - The only disc errors signalled are (plus combination)- 9240REM &10 - Sector not found (incl. ID CRC error or data mark> &FB) 9250REM &08 - CRC error (incl. ID & data CRC or data mark< &F8) 9260REM n.b. write protect is signalled but not as a disc error. 9270REM 1770 - For disc errors see above 9280REM - DD ID mark can have any value between &FC-&FF (not just &FE) 9290REM - DD Data area mark can be any of &F8-&FB (not just &FB or &F8) 9300REM Note ADFS masks any differnce in deleted and normal data. 9310REM - ID head number is ignored, so need not be correct. 9320REM - Only the bottom 2 bits of SectSize are used, rest are ignored 9330REM - Missing clocks in either ID or data are not detected, 9340REM providing the CRC is set as if the values were &A1 for 9350REM '&F5' missing clocks (ie you can't use &F7 to set CRC). 9360REM - Write Track cannot use in data or ID area, byte values of 9370REM &F5-&F7 for Single or Double Density 9380REM &F8-&FB, &FE for Single Density, unless CRC is S/W generated 9390REM The former results in wrong data written, while the latter 9400REM results in an incorrect CRC (using '&F7'). 9410REM However, there is one way of writing the actual values of 9420REM &F5,&F6,&F7, during write track, if the byte immeadiately 9430REM follows a &F7. So it will be very difficult to generate. 9440REM - For double density, none of the 12 sync bytes(ie 0 preceding 9450REM AM) need be there, thus previous data area can go right up 9460REM to the AM of the next ID 9470 9480REM ******* Various standard disc formats ********* 9490REM sect_per_trk first_sect sect_size density 9500 9510REM ADFS(800K) 5 0 3 (1024) 2 9520REM ADFS(640K) 16 0 1 (256) 2 9530REM BBC DFS 10 0 1 (256) 1 9540REM MS-DOS 3 9 1 2 (512) 2 9550 9560 9570DEFPROCpressspace 9580OSCLI("fx 15,1") 9590REPEAT UNTIL INKEY(0)=32 9600ENDPROC 9610 9620DEFPROCprintbits(byte%) 9630LOCAL I% 9640FOR I%=7 TO 0 STEP -1 9650 IF ((byte% >> I%) AND 1) THEN VDU ASC"1" ELSE VDU ASC"0" 9660NEXT 9670ENDPROC 9680 9690DEFPROCcheckDiscIn(text$) 9700IF copydisc THEN 9710 IF source=dest THEN 9720 PRINTTAB(0,VPOS)" Insert "+text$+" disc, then press space"; 9730 PROCpressspace 9740 PRINTTAB(0,VPOS)STRING$(50," ");TAB(0,VPOS); 9750 ENDIF 9760ENDIF 9770ENDPROC 9780 9790DEFPROCprintLine(T$) 9800REM at start of next free line, print text followed by newline 9810REM print at start of next free line followed by newline 9820PROCprintSrt(T$) 9830PRINT 9840ENDPROC 9850 9860DEFPROCprintSrt(T$) 9870REM at start of next free line, print text without newline 9880IF POS<>0 THEN PRINT 9890PRINT;T$; 9900ENDPROC 9910 9920DEFPROCprintID(TrkDesc%, count%) 9930REM At start of next free line, print real trk/head/density, 9940REM then ID bytes. All without newline at end 9950LOCAL ID%, trk%, head%, sect%, sectSize% 9960ID%=FNgetSectID(TrkDesc%, count%) 9970trk%= &FF AND ID% 9980head%= &FF AND (ID% >> 8) 9990sect%= &FF AND (ID% >> 16) 10000sectSize%=&FF AND (ID% >> 24) 10010PROCprintSrt("") 10020IF (TrkDesc%?bufTrkDensity%)=2 THEN 10030 PRINT;"DDensity"; 10040ELSE 10050 PRINT;"SDensity"; 10060ENDIF 10070PRINT TAB(8);" ID: "; 10080PRINT TAB(13);"Trk=";trk%; 10090PRINT TAB(21);"Hd=";head%; 10100PRINT TAB(28);"Sect=";sect%; 10110PRINT TAB(37);"Size=";sectSize%; 10120PRINT;":-"; 10130ENDPROC 10140 10150DEFPROCsectText(T$) 10160PRINT;TAB(47,VPOS);T$ 10170ENDPROC 10180 10190DEFPROCprint_err(cmd%, add%) 10200LOCAL I% 10210VDU7 10220PROCprintSrt("Disc ") 10230IF cmd%=Verify% THEN PRINT"verify sector(s)"; 10240IF cmd%=Read% THEN PRINT"read sector(s)"; 10250IF cmd%=Write% THEN PRINT"write sector(s)"; 10260IF cmd%=ReadTrack% THEN PRINT"read track"; 10270IF cmd%=WriteTrack% THEN PRINT"write track"; 10280IF cmd%=Seek% THEN PRINT"seek"; 10290IF cmd%=Restore% THEN PRINT"rtz"; 10300PRINT" error:- "; 10310I%=4 10320WHILE add%?I% 10330 VDU add%?I% 10340 I%=I%+1 10350ENDWHILE 10360PRINT 10370ENDPROC 10380 10390DEFPROCmenu(RETURN copydisc, RETURN source, RETURN dest, RETURN srttrack, RETURN endtrack, RETURN firstHead, RETURN numbHeads) 10400MODE 0 10410VDU19,0,4,0,0,0 10420PRINTTAB(28);"W A R N I N G" 10430PRINT" This program may only be used to make a backup copy of your own software." 10440PRINT" It is your (the User's) responsiblity to ensure that any software copied," 10450PRINT" using this program, is done so legally." 10460 10470PRINTTAB(20,6);"DUP:- THE DISC DUPLICATOR" 10480PRINTTAB(20,7);"--------------------------" 10490PRINTTAB(18,8);"(c) Softcorn Version ";vers$ 10500 10510copysel=1 10520copyVpos=10 10530PRINTTAB(0,copyVpos); 10540PRINTTAB(10);" 1. Copy full double-sided 80 track disc" 10550PRINTTAB(10);" 2. Copy full single-sided 80 track disc" 10560PRINTTAB(10);" 3. Copy selected portions only" 10570 10580analVpos=14 10590PRINTTAB(0,analVpos); 10600PRINTTAB(10);" 4. Analyse full double-sided 80 track disc" 10610PRINTTAB(10);" 5. Analyse full single-sided 80 track disc" 10620PRINTTAB(10);" 6. Analyse selected portions only" 10630 10640drvsel=7 10650drvVpos=18 10660PRINTTAB(0,drvVpos); 10670PRINTTAB(10);" 7. Using just drive 0" 10680PRINTTAB(10);" 8. Using just drive 1" 10690PRINTTAB(10);" 9. From drive 0 To drive 1" 10700PRINTTAB(10);"10. From drive 1 To drive 0" 10710 10720goVpos=23 10730PRINTTAB(0,goVpos); 10740PRINTTAB(10);"11. DO :-" 10750 10760selVpos=26 10770exitMenu%=FALSE 10780select=0 10790REPEAT 10800IF select=11 THEN exitMenu%=TRUE 10810copydisc=(copysel <=3) 10820IF copysel<>3 AND copysel<>6 THEN 10830 srttrack=0 10840 endtrack=79 10850 firstHead=0 10860 IF copysel=1 OR copysel=4 THEN 10870 numbHeads=2 10880 ELSE 10890 numbHeads=1 10900 ENDIF 10910ENDIF 10920IF drvsel=7 OR drvsel=9 THEN 10930 source=0 10940ELSE 10950 source=1 10960ENDIF 10970IF drvsel=7 OR drvsel=10 THEN 10980 dest=0 10990ELSE 11000 dest=1 11010ENDIF 11020 FOR I%=copyVpos TO goVpos 11030 PRINTTAB(8,I%);" " 11040 NEXT 11050 IF copysel <=3 THEN 11060 PRINTTAB(8,copyVpos+copysel-1);"*" 11070 PRINTTAB(8,drvVpos+drvsel-7);"*" 11080 PRINTTAB(20,goVpos);"** Copy from ";source;" to ";dest; 11090 ELSE 11100 PRINTTAB(8,analVpos+copysel-4);"*" 11110 PRINTTAB(8,drvVpos+(1 AND (drvsel-7)));"*" 11120 PRINTTAB(20,goVpos);"** Analyse ";source; 11130 ENDIF 11140 PRINT;", Tracks ";srttrack;"-";endtrack; 11150 IF numbHeads=2 THEN 11160 PRINT;", both sides **"; 11170 ELSE 11180 PRINT;", side ";firstHead;" only **"; 11190 ENDIF 11200 PRINT;STRING$((78-POS)," ") 11210 PRINTTAB(0,selVpos);STRING$(78," "); 11220 IF NOT exitMenu% THEN 11230 PRINTTAB(20,selVpos);"SELECT "; 11240 INPUT select 11250 IF select=999 THEN PROCdispProcs 11260 IF (select>=1) AND (select <=6) THEN copysel=select 11270 IF (select>=7) AND (select <=10) THEN drvsel=select 11280 IF (select=3) OR (select = 6) THEN 11290 PROCgetSelective("First Track", selVpos, srttrack, 0, 79) 11300 IF srttrack<>79 THEN 11310 PROCgetSelective("Last Track",selVpos, endtrack, srttrack, 79) 11320 ENDIF 11330 PROCgetSelective("Number of sides", selVpos, numbHeads, 1, 2) 11340 IF numbHeads=1 THEN 11350 PROCgetSelective("Side", selVpos, firstHead, 0, 1) 11360 ENDIF 11370 ENDIF 11380 ENDIF 11390UNTIL exitMenu% 11400PRINTTAB(0,goVpos+2); 11410ENDPROC 11420 11430DEFPROCgetSelective(T$, vpos, RETURN value%, min%, max%) 11440REPEAT 11450 PRINTTAB(0,vpos);STRING$(78," "); 11460 PRINTTAB(18,vpos);T$;" (";min%;"-";max%;") "; 11470 INPUT value% 11480 IF (value%<min%) OR (value%>max%) THEN VDU 7 11490UNTIL (value% >= min%) AND (value% <= max%) 11500ENDPROC 11510 11520DEFPROCdispProcs 11530PRINT 11540PRINT" On a single drive, a disc change is needed every" 11550PRINT" ";INT(MaxTrks%/2);" tracks (double sided), "; 11560PRINT ;MaxTrks%;" tracks (single sided)" 11570PRINT 11580PRINT" Try (& f1 to view result)" 11590PRINT"PROCreadsectors(drive, track, head, sector, numbSect, sectSize,density, ";MainBuffer%")" 11600PRINT"PROCreadtrack (drive, track, head, density, ";MainBuffer%" )" 11610PRINT"PROCreadSingleTrk(drive, track, head, ";MainBuffer%", srtDensity% )" 11620analyse=debug :REM only for use of typed PROCs after exit 11630END 11640ENDPROC 11650 11660DEF PROClogo 11670MODE 7 11680PRINT' 11690PRINT"�� p "; 11700PRINT"�� p|���|p "; 11710PRINT"�� z?��o����t "; 11720PRINT"�� _��&`""o����}0 "; 11730PRINT"�� h6? o�����4 "; 11740PRINT"��x||0_||0||||||_||0 _p2%app �|||0|0 |"; 11750PRINT"���1+%�'+��```�`�'+� 5�������55 �`k5�}��"; 11760PRINT"��+�}0� ���� � � ,5�������5=$�|~%�ou�"; 11770PRINT"��p k��0_�� � �0_| 5�������55 �+} �""��"; 11780PRINT"��o��%+��'� � +��' ""`c,lt` � ku� *�"; 11790PRINT"�� ""o5�u 8|||||| "; 11800PRINT"�� j�?yp~������� "; 11810PRINT"�� o����������� "; 11820PRINT"�� `/�������?! "; 11830PRINT" DUP the Disc Duplicator" 11840PRINT'''''"(public domain s/w, no profit based use)"; 11850T%=INKEY(500) 11860ENDPROC 11870 11880DEFPROCinit 11890MaxTrkUnformat% = 4*INT((6250*1.03)/4) :REM Exact number of Words 11900MaxSect%=1024 11910TrkDataSize%=MaxTrkUnformat%+MaxSect%+64 11920TrkDescSize%=FNinitBufoffsets :REM exact number of Words 11930 11940DIM discRec% 100 11950DIM memAddList% maxSectsAllowed%*8 11960 11970REM mapID% holds ID map descriptors (see end of prog for format) 11980DIM mapID% &12000 11990OSCLI("*Load DupIDmap "+STR$~mapID%) :REM generated by DupIDtest 12000 12010REM put all other DIMs before here except code% 12020leaveSpare=10000 12030DIM dummy% 1 12040buffersize=4*INT((HIMEM-dummy%-leaveSpare)/4) :REM Exact number of Words 12050DIM MainBuffer% buffersize +16 12060MainBuffer%=16*INT((MainBuffer% +15)/16):REM on 16 byte boundary for debug 12070 12080MaxTrks%= INT(buffersize/(TrkDataSize% + TrkDescSize%)) 12090MaxTrks% -= 3 :REM for safety cos ReadTrk sometimes runs for too long 12100 12110WriteTrkBuf%=MainBuffer%+buffersize-TrkDataSize% :REM use overflow area 12120 12130codelength=1000 12140DIM code% codelength :REM must be last DIM to ensures code%> &FFFF 12150 :REM cos of BBC BASIC (6502) emulation 12160Verify%=0 12170Read%=1 12180Write%=2 12190ReadTrack%=3 12200WriteTrack%=4 12210Seek%=5 12220Restore%=6 12230SDensity%=1 12240DDensity%=2 12250AdfsSwi%=&40240 12260XAdfsSwi%=AdfsSwi% OR (1<<17) 12270AdfsDiscErr%=&108C7 12280AdfsWriteProtect%=&108C9 12290AltDefectBit%=&10 12300PROCcode 12310 REM clears buffers 12320PROCfill(0,discRec%,70) 12330PROCfill(0,MainBuffer%,buffersize) :REM is this needed ??? 12340OSCLI("key1 *MEDIT "+STR$~(MainBuffer%)+"|M") 12350OSCLI("key2 *MEDIT "+STR$~(WriteTrkBuf%)+"|M") 12360ENDPROC 12370 12380DEFFNinitBufoffsets 12390REM initialises all variables that are offsets pointers into track buffer 12400REM and returns offset for start of track data (ie for Read Track) 12410 REM note each track in buffer will be made up of 12420 REM - A Track descriptor, including 12430 REM - general track info 12440 REM - followed by a list of pointers for each sector (both ID & Data) 12450 REM - followed by a multi-sector descriptor 12460 REM - Followed by the tracks 'ReadTrack data, supperimposed with 12470 REM readSector data. 12480 12490 maxSectsAllowed%=32 :REM determines reserved space for track info 12500 12510REM GENERAL TRACK INFO 12520bufTrk%=0 :REM real track number (byte) 12530bufHead%=1 :REM real head number (byte) 12540bufNumbSect%=2 :REM number of sectors on track (byte) 12550bufMultiSectFlg%=3 :REM TRUE = track can use multi-sector read/write 12560 REM (ie no read errors and sectors are all consecutive; skew is allowed) 12570bufTrkDensity%=4 :REM density (byte) 12580bufEndValidData%=8 :REM end of valid data in Trk buffer(word) 12590 base%=16 :REM bytes 5-7 & 12-15 free 12600 12610REM SECTOR INFORMATION LIST 12620REM entries are in the order that sectors are around track 12630REM each entry - pointer to start of ID (absolute word add) 12640REM - pointer to start of Data Area (absolute word add) 12650REM (if = 0 then no data area for ID found) 12660REM - byte length of sector read/write data op. It will be less 12670REM than sector size if next sect ID would be corrupted 12680REM - Save version of ID, use this version instead of what 12690REM is in track buffer in case of possible corrupt ID. 12700REM - sector information, if <>0 THEN don't write sector 12710REM catches ID CRC err, Deleted data, DataCRC err, 12720REM illegal Arc ID, Data Area too long, No Data Area, 12730REM and Data Area could and was written during format. 12740 Log2SectInfoSize%=5 :REM Log 2 of size of Sector Info (= 32 (8 words)) 12750bufSectDesc%=base% 12760bufIDptr% =0 :REM bytes 0-3 12770bufDataptr% =4 :REM bytes 4-7 12780bufTransLength%=8 :REM bytes 8-11 12790bufSectID%=12 :REM bytes 12-15 12800bufSectInfo%=16 :REM bytes 16-19 12810 :REM byte 16 = disc err result% 12820errCRC%= 1<< 3 :REM CRC error bit in disc error 12830errNotFound%= 1<< 4 :REM sector not found bit in disc error 12840 :REM byte 17 12850delData%= 1<< 8 :REM bit 1= deleted Data Area 12860noData%= 1<< 9 :REM bit 2= No Data Area 12870illegalTrk%= 1<< 10 :REM bit 3= Illegal ID Trk (ie on Arc) 12880illegalIDbyt%=1<<11 :REM bit 4= Illegal ID byte (ie &F5-&F7) 12890longData%= 1<< 12 :REM bit 5= Data too close to next ID to write 12900noRoomCRC%= 1<< 13 :REM bit 6= So long no space for even CRC 12910overIndex%= 1<< 14 :REM bit 7= Data area goes over Index(must write) 12920dataDuringFormat%= 1 << 16 :REM Data could and was written during format. 12930 base% += maxSectsAllowed% << Log2SectInfoSize% :REM 7 words free 12940 12950REM MULTI-SECTOR DATA AREA READ/WRITE INFO 12960REM This info is only valid if Multi-Sector flag above is true 12970bufLowSect%=base% :REM byte 0 lowest sector around track 12980bufSectSize%=base%+1 :REM byte 1 Sector Size 12990 base% += 4 :REM bytes 2 - 3 free 13000REM memory pointer list for discOp one entry for each sector 13010REM entries must be in consectutive sector number order not in the order 13020REM they appear around the track. 13030REM Each entry consists of two words 13040REM first = absolute address of Sector's data in memory 13050REM second = sector size in bytes (nb they must all be the same) 13060REM note list must be word aligned, so assuming track descriptor 13070REM starts at word aligned 13080 base%= 4*INT((base%+3)/4) :REM ensure word aligned 13090bufMemAddList%= base% 13100 base% += 8*maxSectsAllowed% 13110 13120REM start of where read track data is stored, 13130REM nb data areas will be overwritten by actual read sector data 13140REM but Address Marks and gaps will be still in Read Track form 13150REM Write track must use a copy of this (but corrected) 13160= base% 13170 13180 13190DEFPROCcode 13200FOR pass= 0 TO 2 STEP 2 13210P%=code% 13220[ OPT pass 13230 13240; get BASIC's CALL parameters 13250; NOTE they must all be word aligned integer variables, NOT even 13260; !param% is allowed, only param% or param%(x) 13270; R1= Reserved, usually used by caller for BASIC's link address 13280; R9= Pointer to list of L-value parameters 13290; R10=number of parameters (max=6) 13300; For format of CALL parameter block see standard User guide 13310; especially for R9 13320; on exit R0 is corrupt 13330; R1 is unaltered 13340; R2=first parameter (if there is one) else unchanged 13350; R3=second parameter (if there is one) else unchanged 13360; .. ..... ...... .. ..... .. ... ... ..... 13370; R7=sixth parameter (if there is one) else unchanged 13380; 13390; call this routine after the BASIC CALL by - 13400; MOV R1, R14 ;saves BASIC's link address 13410; BL paramvalues ;load all BASIC's parameters into registers 13420; MOV R14, R1 ;restores BASIC's link address 13430; 13440; to update a BASIC variable (last parameter) at end - 13450; LDR R0, [R9] ;get last BASIC parameter add 13460; STR Rx, [R0] ;& update it with value in Rx 13470; MOV pc, R14 ;return 13480 13490.paramvalues 13500ADD R0, R9, R10, LSL #3 13510CMP R10, #1 13520LDRGE R2, [R0, #-8]! 13530LDRGE R2, [R2] ;R2=1st param 13540CMP R10, #2 13550LDRGE R3, [R0, #-8]! 13560LDRGE R3, [R3] ;R3=2nd param 13570CMP R10, #3 13580LDRGE R4, [R0, #-8]! 13590LDRGE R4, [R4] ;R4=3rd param 13600CMP R10, #4 13610LDRGE R5, [R0, #-8]! 13620LDRGE R5, [R5] 13630CMP R10, #5 13640LDRGE R6, [R0, #-8]! 13650LDRGE R6, [R6] 13660CMP R10, #6 13670LDRGE R7, [R0, #-8]! 13680LDRGE R7, [R7] ;R7=6th param 13690MOV pc, R14 ;return 13700 13710 13720;fill byte area, R1=byte value, R2=startadd, R3=length 13730.fill 13740AND R1, R1, #&FF ;use LS byte ony 13750ORR R1, R1, R1, LSL #8 13760ORR R1, R1, R1, LSL #16 ;byte repeated in all bytes in R1 13770ADD R0, R2, R3 13780.fillbytstart 13790TST R0, #3 \word boundary? 13800BEQ fillwords 13810CMP R0, R2 13820BEQ fillend 13830STRB R1, [R0,#-1]! 13840B fillbytstart 13850.fillwords 13860SUB R3, R0, R2 13870CMP R3, #4 13880STRGE R1, [R0,#-4]! 13890BGT fillwords 13900.fillbytend 13910CMP R0, R2 13920STRNEB R1, [R0,#-1]! 13930BNE fillbytend 13940.fillend 13950MOV pc, R14 \return 13960 13970;find, backwards, were data byte changes & return the address 13980; CALL findchangeback startadd% 13990;on entry 1st BASIC parameter = start address 14000;on exit 1st BASIC parameter = address where change occurs 14010; R0, R1, R2 corrupt 14020.findchangeback 14030MOV R1, R14 14040BL paramvalues ;R2 set = start add (1st parameter) 14050MOV R14, R1 14060LDRB R0, [R2] 14070.findchangeloop 14080LDRB R1, [R2, #-1]! 14090CMP R1, R0 14100BEQ findchangeloop 14110LDR R0, [R9] ;get last BASIC parameter add 14120STR R2, [R0] ;& update it with remade ID in R1 14130MOV pc, R14 ;return 14140 14150;copy bytes forward 14160; 'CALL copyfwd, startadd, endadd, destadd' 14170;on entry 1st BASIC parameter = source startadd 14180; 2nd BASIC parameter = source end address 14190; 3rd BASIC parameter = destination start add 14200; R0,R1,R2,R3,R4 corrupted 14210.copyfwd 14220MOV R1, R14 ;R2 set = source start add (1st parameter) 14230BL paramvalues ;R3 set = source end add (2nd parameter) 14240MOV R14, R1 ;R4 set = dest start add (3rd parameter) 14250.copyfwdloop 14260LDRB R0, [R2], #1 14270STRB R0, [R4], #1 14280CMP R2, R3 14290BLE copyfwdloop 14300MOV pc, R14 ;return 14310 14320;copy bytes forward but convert any illegal bytes to &FF 14330;on entry 1st BASIC parameter = source startadd 14340; 2nd BASIC parameter = source end address 14350; 3rd BASIC parameter = destination start add 14360; 4th BASIC parameter = low of illegal byte range 14370; 5th BASIC parameter = high of illegal byte range 14380; 6th BASIC parameter = exit parameter only 14390;on exit 6th BASIC parameter = TRUE if any bytes needed converting 14400; R0,R1,R2,R3,R4,R5,R6,R7 corrupted 14410.selcopyfwd 14420MOV R1, R14 ;R2 set = source start add (1st parameter) 14430BL paramvalues ;R3 set = source end add (2nd parameter) 14440MOV R14, R1 ;R4 set = dest start add (3rd parameter) 14450 ;R5 to R6 = illegal chr range 14460MOV R7, #0 ;R7 = 0 default 14470.selcopyfwd1 14480LDRB R0, [R2], #1 14490CMP R0, R5 14500BLT selcopyfwd2 14510CMP R0, R6 ;if illegal chr 14520MOVLE R0, #&FF ;set to &FF 14530MVNLE R7, #(1-1) ;& set R7 to -1 if any byte had to be converted 14540.selcopyfwd2 14550STRB R0, [R4], #1 14560CMP R2, R3 14570BLE selcopyfwd1 14580LDR R0, [R9] ;get last BASIC parameter 14590STR R7, [R0] ;& set it to R5 14600MOV pc, R14 ;return 14610 14620;fill backwards(destination) while source(backwards) continues to remain 14630; the same, or until end(source) is reached. 14640;on entry ALL following must be integer BASIC variables 14650; 1st BASIC parameter = source start add 14660; 2nd BASIC parameter = source end add (must be < 1st parameter) 14670; 3rd BASIC parameter = destination start add 14680; 4th BASIC parameter = value to fill with 14690; 5th BASIC parameter = exit value only (see below) 14700;on exit 5th BASIC variable is set = address where source byte changed 14710; or (endadd-1) if no change occurs 14720.limitfillback 14730MOV R1, R14 ;R2 set = source start add 14740BL paramvalues ;R3 set = source end add 14750MOV R14, R1 ;R4 set = dest start add 14760 ;R5 set = fill value 14770LDRB R1, [R2] ;get first source value 14780ADD R4, R4, #1 ; increment R3 (as initial value) 14790.limitfillback1 14800CMP R2, R3 ; stop filling when past end 14810BLT limitfillbackend 14820STRB R5, [R4, #-1]! 14830LDRB R0, [R2, #-1]! 14840CMP R0, R1 ;or when source value changes 14850BEQ limitfillback1 14860.limitfillbackend 14870LDR R0, [R9] ;get last BASIC parameter add 14880STR R2, [R0] ;& update it with R2 14890MOV pc, R14 ;return 14900 14910 14920;Find Double Density ID address mark pattern 14930; as for findAM below but only ID address marks are looked for 14940; except R5 is also corrupted (used to save link) 14950.DDfindID 14960MOV R5, R14 ;save link 14970BL DDfindAM 14980MOV R14, R5 ;restore link 14990CMP R2, #0 ;add=0? then exit 15000BEQ DDfindIDend 15010LDRB R0, [R2,#-1] 15020CMP R0, #&FC ;mark=&FC,&FD,&FE, or &FF then exit 15030BLT DDfindID 15040.DDfindIDend 15050MOV R15, R14 ;return 15060 15070 15080;Find Double Density address mark pattern from 'Read Track' data 15090; including extra tests for ID 15100;on entry 1st BASIC parameter = startadd 15110; 2nd BASIC parameter = end address 15120;on exit 1st BASIC parameter is updated 15130; = 0 if not found 15140; <> 0, = address of start of Address Mark 15150; R2= same as BASIC 1st parameter 15160; R3= BASIC 2nd parameter (unaltered) 15170; R0, R1, R4 corrupted 15180.DDfindAM 15190MOV R1, R14 15200BL paramvalues ;R2 set = 1st parameter add(start address) 15210MOV R14, R1 ;R3 set = 2nd parameter add(end address) 15220SUB R2, R2, #1 15230.DDfindAMloop 15240CMP R2, R3 15250BEQ DDAMnotfound 15260LDRB R0, [R2, #1]! ;R2 ends up = add of current byte comparison 15270CMP R0, #&A1 ;first &A1 at add 15280BNE DDfindAMloop 15290LDRB R0, [R2, #1] 15300CMP R0, #&A1 ;second &A1 at add+1 15310BNE DDfindAMloop 15320LDRB R0, [R2, #2] ;found &A1,&A1 pattern 15330CMP R0, #&F8 15340BLT DDfindAMloop 15350.DDfoundAM ;FOUND &A1,&A1,&Fx pattern, where &Fx >&F7 15360ADD R2, R2, #3 ; point to first byte of ID or Data Area 15370B DDfindAMend 15380.DDAMnotfound 15390MOV R2, #0 15400.DDfindAMend 15410LDR R0, [R9, #8] ;get 1st parameter add(start address) 15420STR R2, [R0] ;& update it with R2 15430MOV pc, R14 15440 15450 15460;Find Single Density ID address mark pattern 15470; as for findAM below but only ID address marks are looked for 15480; except R7 is also corrupted (used to save link) 15490.SDfindID 15500MOV R7, R14 ;save link 15510BL SDfindAM 15520MOV R14, R7 ;restore link 15530CMP R2, #0 ;add=0? then exit 15540LDRNEB R0, [R2,#-1] 15550CMPNE R0, #&FE ;mark=&FE? then exit 15560BNE SDfindID 15570MOV pc, R14 ;return 15580 15590;find Single Density address mark pattern from 'Read Track' data 15600; & set corrupted Mark byte correctly 15610;NOTE this is NOT a fool-proof test, (but has not failed yet) 15620;on entry 1st BASIC parameter = startadd 15630; 2nd BASIC parameter = end address 15640;on exit 1st BASIC parameter is updated 15650; = 0 if not found 15660; <> 0, = address of start of Address Mark 15670; R2= same as BASIC 1st parameter 15680; R3= BASIC 2nd parameter (unaltered) 15690; R0, R1, R4, R5 corrupted 15700.SDfindAM 15710MOV R1, R14 15720BL paramvalues ;R2 set = 1st parameter add(start address) 15730MOV R14, R1 ;R3 set = 2nd parameter add(end address) 15740SUB R2, R2, #1 15750.SDfindAMloop 15760CMP R2, R3 15770BEQ SDAMnotfound 15780LDRB R0, [R2, #1]! ;R2 ends up = add of current byte comparison 15790LDRB R4, [R2, #-2] 15800AND R1, R4, #&30 15810EOR R1, R1, R0 ;R1= possible reconstructed Mark 15820CMP R1, #&FE ;ID mark? 15830CMPNE R1, #&FB ;data Area mark? 15840CMPNE R1, #&F8 ;del data mark? 15850BNE SDfindAMloop 15860CMP R4, #0 15870CMPNE R4, #&FF ;test add-2 to add-5 = &00 or &FF 15880BNE SDfindAMloop 15890MVN R5, #(2-1) ;(set R5=-2) 15900.SDfindAMsyncloop 15910CMN R5, #5 ;(=-5?) 15920BEQ SDmaybeAM 15930SUB R5, R5, #1 15940LDRB R0, [R2, R5] 15950CMP R0, R4 15960BEQ SDfindAMsyncloop 15970BNE SDfindAMloop 15980.SDmaybeAM 15990LDRB R0, [R2, #-1] 16000EOR R0, R0, R4 16010AND R0, R0, #&F0 16020BNE SDfindAMloop ;also test that add%-2 & add%-3 have same top 4 bits 16030MOV R4, #&FF 16040MVN R5, #(8-1) ;(set R5=-8) 16050.SDffAMloop 16060CMN R5, #14 ;(=-14?) 16070BEQ SDfoundAM 16080SUB R5, R5, #1 16090LDRB R0, [R2, R5] 16100CMP R0, R4 ;& test add%-9 to add%-14 are all &FF 16110BEQ SDffAMloop 16120BNE SDfindAMloop 16130.SDfoundAM 16140STRB R1, [R2] ;if AM found then reconstruct Mark 16150ADD R2, R2, #1 ; point to first byte of ID or data area if found 16160B SDfindAMend 16170.SDAMnotfound 16180MOV R2, #0 16190.SDfindAMend 16200LDR R0, [R9, #8] ;get 1st parameter add(start address) 16210STR R2, [R0] ;& update it with R2 16220MOV pc, R14 16230 16240 16250;reassemble ID corrupted by 'Read Track' 16260; 'CALL remakeID, mapID%, add%, remadeID%, remadeOK%' 16270; should only be called if sectSize is corrupt (ie >3) 16280; & for double density IDs. 16290; on entry BASIC parameter 1 = start of mapID% descriptors 16300; parameter 2 = address of start or ID read 16310; parameter 3 = RETURN remade ID (as 4 byte word) 16320; parameter 4 = RETURN FALSE if cant remake ID 16330; on exit parameter 3 = reassembled ID (4 bytes only) 16340; parameter 4 = 0 if no match is found 16350; R0,R1,R2,R3,R4,R5,R6 corrupt 16360; 16370;Format of IDmap can be found at end of Program 16380 16390.remakeID 16400MOV R1, R14 16410BL paramvalues ;R2 = mapID% (BASIC's 1st parameter) 16420MOV R14, R1 ;R3 = add% (BASIC's 2nd parameter) 16430MOV R4, #0 ;R4 = descriptor number 16440MVN R5, #(1-1) ;R5 = byte offset in ID read (-1) 16450.nxtIDbyte 16460ADD R5, R5, #1 16470LDRB R1, [R3, R5] ;R1 = byte value looking for 16480.nxtdesc 16490ADD R6, R2, R4, LSL #3 ;R6= pointer to current descriptor 16500LDR R0, [R6] ;R0= current descriptor 16510CMP R1, R0, LSR #24 16520BEQ valuefound ;if value does not match 16530LDR R4, [R6, #4] ;get second word of descriptor 16540CMP R4, #0 16550BNE nxtdesc ;if <>0 it is a descriptor number 16560.failedremake ;if =0 then can't remake ID so exit with R4=0 16570B remakeend 16580.valuefound 16590BIC R0, R0, #(&FF <<24) ;mask out value in descriptor (top 8 bits) 16600TSTS R0, #(1 << 23) ;if bit 23 of descriptor is clear 16610MOVEQ R4, R0 ; descriptor = descriptor number for next byte 16620BEQ nxtIDbyte ; so go get next byte & descriptor 16630.foundenddesc ;else we've found the ID (in R0) so 16640AND R1, R0, #&7F ; load track (byte) 16650AND R5, R0, #(1 << 7) 16660ORR R1, R1, R5, LSL#1 ; OR in head (byte) 16670AND R5, R0, #(&FF << 8) 16680ORR R1, R1, R5, LSL #8 ; OR in sector 16690AND R5, R0, #(3 << 16) 16700ORR R1, R1, R5, LSL #8 ; OR in sector size 16710MVN R4, #(1-1) ; found so set R4=-1 16720.remakeend 16730LDR R5, [R9] ;get last BASIC parameter add 16740STR R4, [R5] ;& set =-1 if found ELSE = 0 16750LDR R5, [R9, #8] ;get last-1 BASIC parameter add 16760STR R1, [R5] ;& update it with remade ID in R1 16770MOV pc, R14 ;return 16780 16790.endcode 16800] 16810IF (P%-code%) > codelength THEN 16820 PRINT"assembler code too long":VDU7:END 16830ENDIF 16840NEXT 16850ENDPROC 16860 16870REM ****** ID map format ********* 16880REM mapID is made up of a list of descriptors numbered 0 to n 16890REM each descriptor is 2 words 16900REM word0 - 16910REM ms byte = byte value read 16920REM ls 3 bytes= If bit 23=0, descriptor number of next byte in pattern 16930REM else holds unique ID, ie end of unique byte pattern 16940REM format is Bits 0-6 track 16950REM 7 head 16960REM 8-15 sector 16970REM 16-17 sector Size 16980REM word1 - 16990REM if <>0, descriptor number of alternative value at this byte position 17000REM else, no more alternative values
� >Dup 3*|********************************************* (3*|* ** 23*|* DUP, the disc duplicator (c) Softcorn ** <3*|* - ** F3*|* Public Domain software, but not for use ** P3*|* or sale related to profit. ** Z3*|* - ** d3*|* Copies double or single density discs ** n3*|* of 'any' format (eg ADFS L/D/E, DFS, ** x3*|* MSDOS, ATARI ST, AMIGA etc, and a wide ** �3*|* variety of protected discs) ** �3*|* - ** �3*|* BASIC + machine code + data file ** �3*|* (Edit at your peril) ** �3*|* ** �3*|********************************************* � �vers$="1.01" � �)� � � �=17 � �:� � �:�" at Line ";�:� �logo �init "debug=� , 6J� set,to check all tracks have a fixed number of good contiguous sects @checkFormat=� JchkDensity%=DDensity% TchkSectsPerTrk=9 ^� checkFormat � hA �"**** Checking Sectors per Track =";chkSectsPerTrk;" ****" r� | �� �G �menu(copydisc,source,dest,srttrack,endtrack,firstHead,numbHeads) � analyse=debug � � copydisc �@ �action(source,dest,srttrack,endtrack,firstHead,numbHeads) �I �'" Completed:- Press SPACE to Continue (or escape to exit)"; � �pressspace �� � �� � � �?��action(source,dest,srttrack,endtrack,firstHead,numbHeads) �blank%=� �srtDensity%=DDensity% 'MaxDiscTrks% =�(MaxTrks%/numbHeads) firstTrk%=srttrack !TrksLeft%=1+endtrack-srttrack &ȕ TrksLeft% > 0 0" � TrksLeft% > MaxDiscTrks% � : numbTrks%=MaxDiscTrks% D � N numbTrks%=TrksLeft% X � bU �copyMultiTrks(source,dest,firstTrk%,numbTrks%,firstHead,numbHeads,srtDensity%) l firstTrk% +=numbTrks% v TrksLeft% -=numbTrks% �� �� � �W��copyMultiTrks(source,dest,firstTrk%,numbTrks%,firstHead,numbHeads, � srtDensity%) �� T% ��checkDiscIn("SOURCE") �;� (firstTrk% = srttrack) � ((source=dest) � copydisc) � � �engageDisc(source) �� �X�doMultiTrks(Read%, source, firstTrk%, numbTrks%, firstHead, numbHeads, srtDensity%) �� copydisc � �! �checkDiscIn("DESTINATION") �0 � (firstTrk% = srttrack) � (source=dest) � �engageDisc(dest) � X �doMultiTrks(Write%, dest, firstTrk%, numbTrks%, firstHead,numbHeads, srtDensity%) � *� 4 >W��doMultiTrks(cmd%, drv, firstTrk%, numbTrks%, firstHead, numbHeads, � srtDensity%) H� TrkDesc%, trk, head RTrkDesc%=MainBuffer% \0� trk=firstTrk% � (firstTrk% + numbTrks% -1) f3 � head=firstHead � (firstHead + numbHeads -1) p �0,�); z. � cmd%=Read% � � "Read "; � � "Write"; �A �;": Drv=";drv;" Trk=";trk;" ";�21,�);"Hd=";head;" "; � � debug � � � � cmd%=Read% � �? �readSingleTrk(drv, trk, head, TrkDesc%, srtDensity%) � � �3 �writeSingleTrk(drv, trk, head, TrkDesc%) � � �/ TrkDesc% += TrkDescSize% + TrkDataSize% � � �� �� � �1��writeSingleTrk(drv%, trk%, head%, TrkDesc%) :� count%, density%, DataBuf%, multiSectFlg%, sectInfo% D� lowSect%, sectSize%, sect%, sectFound%, add%, ID%, mustWrSect% "count% = TrkDesc%?bufNumbSect% $%density%= TrkDesc%?bufTrkDensity% .&DataBuf% = TrkDesc% + TrkDescSize% 8� count%=0 � B4 � blank track so just copy back ReadTrack data L; �writetrackChk(drv%, trk%, head%, density%, DataBuf%) V� `S �makeWriteTrk(TrkDesc%, count%, density%, DataBuf%, WriteTrkBuf%,mustWrsect%) j? �writetrackChk(drv%, trk%, head%, density%, WriteTrkBuf%) t. multiSectFlg%= TrkDesc%?bufMultiSectFlg% ~% � multiSectFlg% � mustWrsect% � �8 � write track in one go by using memory DMA list �A � but only if a sector could not be written during Format �% lowSect%=TrkDesc%?bufLowSect% �' sectSize%=TrkDesc%?bufSectSize% �) �copyMemAddList(TrkDesc%, count%) �b �opsectors(Write% � (1<<5),drv%,trk%,head%,lowSect%,count%,sectSize%,density%,memAddList%) � � result% <> 0 � �' multiSectFlg%=� :� Disc error �: �" writing:- track was non-standard after all" � � � � �* � (multiSectFlg%=0) � mustWrsect% � B � catch all non-standard track formats, but only if a good 4 � sector could not be written during Format ' � (multiSectFlg%=0) � analyse � 2 �" writing:- non-standard track layout" ( � 2" � sectFound%= 0 � count%-1 <6 sectInfo%=�getSectInfo(TrkDesc%, sectFound%) F, � (sectInfo% � (� overIndex%))=0 � PE � if data area was read OK (& don't have any illegal IDs) ZB � and it was not deleted data, then provided data area dF � has not already been correctly written with writetrack, n+ � write the sector individually x2 add%=�getDataPtr(TrkDesc%, sectFound%) �0 ID%=�getSectID(TrkDesc%, sectFound%) �# sect%=&FF � (ID% >> 16) �% sectSize%=3 � (ID% >> 24) �J �writesectors(drv%,trk%,head%,sect%,1,sectSize%,density%,add%) � � � � � � �� �� � �H � make Write Track data using Track descriptor and Read Track (the �C � latter having been overlaid with correct read sector data. �> � But ensure no illegal chrs appear in gaps or data area 6 � Output message if data area can't be recreated U��makeWriteTrk(TrkDesc%, count%, density%, ReadBuf%, WriteTrkBuf%, � mustWrSect%) G� sectFound%, sectSize%, SrcAdd%, SrcLowAdd%, DestAdd%, DestLowAdd% "� convert%, info% ,mustWrSect%=� 6DestLowAdd%=WriteTrkBuf% @SrcLowAdd%=ReadBuf% J� sectFound%=0 � count%-1 T- SrcAdd%=�getIDPtr(TrkDesc%, sectFound%) ^- DestAdd%=DestLowAdd%+SrcAdd%-SrcLowAdd% h- info%=�getSectInfo(TrkDesc%,sectFound%) r � ID's AM & prior gap |J �makeAMandgap( SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%, density%) �A DestAdd%!0 = �getSectID(TrkDesc%,sectFound%) :� copy ID �# � (info% � errNotFound%) =0 � �7 DestAdd%?4 = &F7 :� to generate CRC �6 DestLowAdd%= DestAdd%+5 :� byte after CRC � � �A � but if sector not found then copy readtrack CRC instead � DestAdd%?4 = SrcAdd%?4 � DestAdd%?5 = SrcAdd%?5 �6 DestLowAdd%= DestAdd%+6 :� byte after CRC � � �@ SrcLowAdd% = SrcAdd%+6 :� point first byte after CRC �/ SrcAdd%=�getDataPtr(TrkDesc%, sectFound%) � � SrcAdd%<>0 � � if data area / DestAdd%=DestLowAdd%+SrcAdd%-SrcLowAdd% " � data area AM & prior gap &K �makeAMandgap(SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%, density%) 0 SrcLowAdd% = SrcAdd% : DestLowAdd%= DestAdd% D9 SrcAdd% += �getDataLength(TrkDesc%, sectFound%)-1 NG � copy up to end of data area transfer, convert any &F5-F7 chrs XJ �selcopyfwd (SrcLowAdd%, SrcAdd%, DestLowAdd%, density%, convert%) bF DestLowAdd% +=SrcAdd%-SrcLowAdd%+1 :� first byte after data... l> SrcLowAdd% = SrcAdd%+1 :� ..transfer areas v= � (info% � (errCRC% � errNotFound% � noRoomCRC%))=0 � �A � room for CRC AND read CRC was ok AND sector was found �5 � (convert%=0) � ((info% � overIndex%)=0) � �: � AND writetrack can correctly write data area �1 DestLowAdd%?0 = &F7 :� so force CRC �8 SrcLowAdd% +=2 :� and adjust source & �? DestLowAdd% +=1 :� dest addresses accordingly � � & set sector info �A �addSectInfo(TrkDesc%, sectFound%, dataDuringFormat%) �< info%=info% � dataDuringFormat% :� for use below � � �J � info%�(delData% � longData% � illegalTrk% � illegalIDbyt%) � �H � if can't write sector, but otherwise OK, and can't write �2 � it during format, then print error , �printID(TrkDesc%, sectFound%) 1 � "CAN'T make an exact copy! Sorry" 8 DestLowAdd%?0 = &F7 :� but set CRC anyway 9 SrcLowAdd% +=2 :� and adjust pointers * DestLowAdd% +=1 4 � > � H � R& � (info% � (� overIndex%))=0 � \! � must write the sector f mustWrSect%=� p � z � �� �%SrcAdd%=TrkDesc%!bufEndValidData% �*� copy to end of valid read track data �F�selcopyfwd (SrcLowAdd%, SrcAdd%, DestLowAdd%, density%, convert%) �@�fillEndOfTrk( SrcAdd%+1-ReadBuf% + WriteTrkBuf% , density%) �� � �> � fills to end of write buffer with relevant filler byte �$��fillEndOfTrk( add% , density%) �� value% �� density%=DDensity% � � value%=&4E �� value%=&FF � ;�fill(value%, add%, WriteTrkBuf% + TrkDataSize% - add%) $� . 8K � copies area of store but converts any illegal chrs(for writeTrack) B9 � Sets convert%=TRUE if it has to convert any char LD��selcopyfwd(SrcLowAdd%,SrcAdd%,DestLowAdd%,density%,� convert%) V� low%, hi% `$�getIllegal(density%, low%, hi%) jG� selcopyfwd, SrcLowAdd%, SrcAdd%, DestLowAdd%, low%, hi%, convert% t� ~ �/ � setup illegal chr range for writetrack �)��getIllegal(density%, � low%, � hi%) �� density% = DDensity% � � low%=&F5:hi%=&F7 �� � low%=&F5:hi%=&FE �� �� � �H��makeAMandgap(SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%, density%) �� density%=DDensity% � �A �DDmakeAMandgap(SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%) � A �SDmakeAMandgap(SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%) � � ( 2 � DD only <@��DDmakeAMandgap(SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%) F� V%, I%, J%, convert% P+DestAdd%?-1 = SrcAdd%?-1 :� copy Mark Z(DestAdd%?-2 = &F5 :� set AM dDestAdd%?-3 = &F5 nDestAdd%?-4 = &F5 xJ%=DestAdd% - DestLowAdd% �I%=5 �H� I%<=J% � DestAdd%?-I% =0:I% +=1 :� and set preceeding 2 bytes to 0 �@� I%<=J% � DestAdd%?-I% =0:I% +=1 :� (DestLowAdd permitting) �V%=SrcAdd%?-I% �&ȕ (V%=(SrcAdd%?-I%)) � (I% <= J%) �@ DestAdd%?-I% = 0 :� and all preceeding bytes to 0 while �< I% +=1 :� source bytes don't change value �-� :� (DestLowAdd permitting) �� I%<=J% � �2 � then copy preceding gap back to SrcLowadd% �K �selcopyfwd (SrcLowAdd%, SrcAdd%-I%, DestLowAdd%, DDensity%,convert%) �� �� � SD only @��SDmakeAMandgap(SrcLowAdd%, SrcAdd%, DestLowAdd%, DestAdd%) "� I%, J%, convert% ,3DestAdd%?-1 = &F0 � (SrcAdd%?-1) :� copy Mark 6I%=2 @J%=DestAdd% - DestLowAdd% Jȕ (I% <= J%) � (I% <= 7) T= DestAdd%?-I% = 0 :� and preceeding by 6 bytes of 0 ^6 I% +=1 :� (DestLowAdd permitting) h� r� I%<=J% � |2 � then copy preceding gap back to SrcLowadd% �K �selcopyfwd (SrcLowAdd%, SrcAdd%-I%, DestLowAdd%, SDensity%,convert%) �� �� � �E � tries both DD & SD before giving up, & returns density found �<��readSingleTrk(drv, trk, head, TrkDesc%, � srtDensity%) �)� endadd%, DataBuf%, density%, count% �TrkDesc%?bufTrk% = trk �TrkDesc%?bufHead% = head �density%=srtDensity% �� �) TrkDesc%?bufTrkDensity% = density% �( DataBuf% = TrkDesc% + TrkDescSize% J endadd% = DataBuf% + MaxSect% -1 + (MaxTrkUnformat% >> (2-density%)) � F � to test ReadTrack overrun, only repeat if ReadTrack overflow &. �fill(&55, DataBuf%, endadd%-DataBuf%) 06 �readtrack(drv, trk, head, density%, DataBuf%) : endValidData%=endadd%-1 D' � findchangeback, endValidData% N' � (endadd% - endValidData%) > 500 X/ TrkDesc%!bufEndValidData% = endValidData% b/ �analyseTrk(TrkDesc%, DataBuf%, density%) l. �readTrksSects (drv, TrkDesc%, density%) v" count%=TrkDesc%?bufNumbSect% � � count%=0 � �> density%= density% � 3 :� if no sectors toggle SD/DD � � �! � srtDensity%<>density% � � �" Continues as "; �0 � density%=2 � �"Double"; � �"Single"; � �" Density" � blank%=� �E srtDensity%=density% :� if sectors, update srtDensity � � � � �� density%=srtDensity% �� blank% � � count%<>0 � blank%=� �" No longer Blank" � *� 4 � count%=0 � > blank%=� H �" Continues as Blank" R � \� f#�printUnusual(TrkDesc%, count%) p� checkFormat � zt � ((TrkDesc%?bufNumbSect%) <> chkSectsPerTrk) � ((TrkDesc%?bufMultiSectFlg%)= �) � (density% <> chkDensity%) � �@ �printLine(" ******* Track format failed check *******") � � �� �� � �J � Analyses Read Track data and sets up Track descriptor accordingly �H � Ignore any 'apparent' sectors without associated data areas (or �6 � it appear as 'Sector not found' in any case) �0 � if anything unusual unset multiSectFlg% �.��analyseTrk(TrkDesc%, DataBuf%, density%) �E� count%, add%, IDadd%, dataadd%, mark%, IDcorrupt, multiSectFlg% �;� ID%, bytes%, firstIDsyncAdd%, endLastData%, remadeOK% �"multiSectFlg%=� :� ie default count%=0 Kadd%=DataBuf% + 2 :� first ID AM must be at least 2 bytes into buffer %endadd%=TrkDesc%!bufEndValidData% $� .& �findID(add%, endadd%, density%) 8 � add%<>0 � BC � count%=maxSectsAllowed% � �'"Failed:- too many sectors":� L IDadd%=add% V- � count%=0 � firstIDsyncAdd%=IDadd%-4 `+ �setIDPtr(TrkDesc%, count%, IDadd%) j= �setSectInfo(TrkDesc%, count%, 0) :� 0=default value t9 �setSectID(TrkDesc%, count%, IDadd%!0) :� save ID ~ add% +=6 � � (endadd%-add%) < 128 � �: � if there is a data area it overflows index, so �B � wrap around data from start of track (before first ID) �) � to ensure I see the data mark �7 � copyfwd, DataBuf%, firstIDsyncAdd%, endadd% �- endadd% += firstIDsyncAdd%-DataBuf% � � �@ �findAM (add%,endadd%,density%) :� aim to find Data Area �D dataadd%=0 :� default if no DataArea � � add% <>0 � �> mark%=(add%?-1) :� must be between &F8-&FF � � mark% >= &FC � G add% -= 10 :� if not Data Area, turn back add% � dataadd%=add% � mark%= &F8 � ( � its deleted data 2 multiSectFlg% = � <6 �addSectInfo(TrkDesc%, count%, delData%) F � P � Z � d/ �setDataPtr(TrkDesc%, count%, dataadd%) n � dataadd%=0 � xG � no data (so the ID will be ignored), so for TRACE sake only � multiSectFlg% = � �1 �addSectInfo(TrkDesc%, count%, noData%) � � �F � There is a data area, so only then keep a record of sector �? � and only then test for corrupt ID's or illegal ID's � � (IDadd%?3 > 3) � �= � probably corrupt ID (or maybe not ID just data) �" � density%=DDensity% � �? � remake ID & set =ID% then overwrite saved state �8 � remakeID, mapID%, IDadd%, ID%, remadeOK% � � remadeOK%<>0 � �3 � save remaded ID only if remadeOK% �1 �setSectID(TrkDesc%, count%, ID%) � � 8 � leave it as it is, if its Single Density " � , � 6C �testIllegalId(TrkDesc%, count%, density%, multiSectFlg%) @ count%=count%+1 J � T � ^� add%=0 h� count%<>0 � r( ID%=�getSectID(TrkDesc%, count%-1) |' bytes%=1 << (7+ (3 � (ID% >>24))) �@ endLastData%= bytes%+5+4 + �getDataPtr(TrkDesc%, count%-1) �2 � endLastData% > TrkDesc%!bufEndValidData% � �C � last sector's data area overflows Index, so mark the fact �4 �addSectInfo(TrkDesc%, count%-1, overIndex%) �$ � & change end of valid data �F endadd% = firstIDsyncAdd%-DataBuf% + TrkDesc%!bufEndValidData% �" � endadd% < endLastData% � � endLastData%=endadd% � � �/ � set end of valid data to shorter of - �G � 9 bytes after data area CRC (allows for min_gap=5 + ID_AM=4), �1 � or start of first sector wrapped around �4 � (ensures first ID will not be overwritten) 0 TrkDesc%!bufEndValidData% = endLastData% � � &,TrkDesc%?bufMultiSectFlg%= multiSectFlg% 0"TrkDesc%?bufNumbSect% = count% :� D N< � on exit add% = address of ID (found) or 0 (NOT found) X(��findID (� add%, endadd%, density%) b� density%=DDensity% � l � DDfindID, add%, endadd% v� � � �! � SDfindID, add%, endadd% �D � (add%=0) � ((add%?3)<= 3):� extra test for valid Size for SD �� �� � �F � on exit add% = address of address mark (found) or 0 (NOT found) �(��findAM (� add%, endadd%, density%) �� density%=DDensity% � � � DDfindAM, add%, endadd% �� � � SDfindAM, add%, endadd% �� � @ � IF Multi sector, setup memory pointer list & do one read C � ELSE (or if above read fails) read each sector individually *1 � if multi-sector read fails unset its flag 4.��readTrksSects (drv%, TrkDesc%, density%) >@� sectFound%,trk%,head%,lowSect%,sectSize%,add%,IDadd%,sect% H8� multiSectFlg%, count%, bytes%, info%, notRealSect% R� \A notRealSect%=� :� break out for 'Sector not found' & re-try f$ count% = TrkDesc%?bufNumbSect% p � count%<>0 � z trk% =TrkDesc%?bufTrk% � head%=TrkDesc%?bufHead% �0 multiSectFlg%= TrkDesc%?bufMultiSectFlg% �8 � -- many of below can set multiSectFlg% = FALSE �4 �setLengths(TrkDesc%, count%, multiSectFlg%) � � multiSectFlg% � �9 �setMultiSect (TrkDesc%, count%, multiSectFlg%) � � � � multiSectFlg% � �: � read track in one go but using memory DMA list �' lowSect%=TrkDesc%?bufLowSect% �) sectSize%=TrkDesc%?bufSectSize% �+ �copyMemAddList(TrkDesc%, count%) �c �opsectors(Read% � (1<<5),drv%,trk%,head%,lowSect%,count%,sectSize%,density%,memAddList%) 8 � result% <> 0 � multiSectFlg%=� :� Disc error � � multiSectFlg%=0 � $ � analyse � .5 �" reading:- non-standard track layout?" 8 � BK � catch all,deleted data,non-consecutive IDs, ID & DataArea error L@ � illegal IDs Trk, and data areas that overlay next ID V$ � sectFound%= 0 � count%-1 ` � notRealSect%=� � j5 info%=�getSectInfo(TrkDesc%,sectFound%) t7 � (info% � (illegalTrk% � noData%)) = 0 � ~J � legal ID trk (on Arc)& data area(incl deldata) then read �6 add%=�getDataPtr(TrkDesc%, sectFound%) �4 ID%=�getSectID(TrkDesc%, sectFound%) �' sect%=&FF � (ID% >> 16) �; bytes%=�getDataLength(TrkDesc%, sectFound%) �J �opbytes(Read%,drv%,trk%,head%,sect%,bytes%,density%,add%) � � result%<>0 � �K �readSectErr(TrkDesc%, sectorFound%,result%,notRealSect%) � � � � �D � maybe this is not a sector atall but ID pattern is �E � part of a data area. Assume it is not a sector if a �@ � previous sector has longData or noRoomCRC set. @ � NOT a FOOL PROOF test but probably good enough ! � sectFound%<>0 � W � ((longData% � noRoomCRC%) � �getSectInfo(TrkDesc%, sectFound%-1)) � " notRealSect%=� (7 �deleteSect(TrkDesc%, sectorFound%) 2 � < � F � P � Z � d � nE TrkDesc%?bufMultiSectFlg%= multiSectFlg% :� in case its reset x � �� notRealSect%=� �� � �B��readSectErr(TrkDesc%, sectorFound%, result%, � notRealSect%) �@� Special actions on read sector error (eg Sector Not Found) �� ID%, count%, sectSize% �$� (result% � errNotFound%) <>0 � � � If Sector Not Found �* ID%=�getSectID(TrkDesc%, sectFound%) �1 � ID% <> !�getIDPtr(TrkDesc%, sectFound%) � �I � If corrupt ID & Not Found, check for alternative for corrupt ID � Ȏ ID% � �4 � &014B011C: ID%=&014F011C :notRealSect%=� 4 � &02B70029: ID%=&02D50029 :notRealSect%=� 4 � &02B40029: ID%=&03FC0029 :notRealSect%=� 4 � &00550129: ID%=&00770129 :notRealSect%=� "4 � &00640129: ID%=&00A40129 :notRealSect%=� ,4 � &01770129: ID%=&01B70129 :notRealSect%=� 64 � &026A0129: ID%=&02D60129 :notRealSect%=� @4 � &02FA0129: ID%=&03560129 :notRealSect%=� J4 � &03660129: ID%=&03A60129 :notRealSect%=� T4 � &02B50129: ID%=&03FD0129 :notRealSect%=� ^4 � &02750129: ID%=&02B50129 :notRealSect%=� hB � nb last one must be after penultimate one (as both are r9 � alternative patterns for the same corrupt ID) | � � � � � notRealSect%=� � �< � Sector Not Found AND corrupt ID has an alternative �3 � so set stored value of ID% to alternative �- �setSectID(TrkDesc%, sectFound%, ID%) � � �@ � Sector Not Found AND not an alternative for corrupt ID �C � so remove sector from track descriptor, as it is NOT a ID � notRealSect%=� �+ �deleteSect(TrkDesc%, sectorFound%) � � �� �D � flag disc error on sector, in sect info, if sector was found 7 �addSectInfo(TrkDesc%, sectFound%, result% � &FF) � � & 0(��deleteSect(TrkDesc%, sectorFound%) :<� this was not a sector but part of a data area (or gap) D6� so remove sector from track descriptor and reset N<� any longData or noRoomCRC in the previous sector info. X� I%, J%, infoSize%, count% b"count% = TrkDesc%?bufNumbSect% l%infoSize%= 1 << Log2SectInfoSize% v5J%=TrkDesc% + bufSectDesc% + sectFound%*infoSize% �2� I%=0 � (count%-1-sectFound%)*infoSize%-1 � 4 � J%!I%=J%!(I%+infoSize%) �� �� sectFound%<>0 � �m �setSectInfo(TrkDesc%, sectFound%-1,�getSectInfo(TrkDesc%, sectFound%-1) � (�(longData% � noRoomCRC%))) �� �9TrkDesc%?bufNumbSect% -= 1 :� decrement stored count �� � �G � if track is incorrect OR head, sector, or sectSize are =&F5-&F7 �C � then unset multiSectFlg%, set sector info and print message �D � assumes ID has been saved (after any remaking of corrupt ID) �@��testIllegalId(TrkDesc%, count%, density%, � multiSectFlg%) � ID%, I%, T%, low%, hi% $ID%=�getSectID(TrkDesc%, count%) '� (ID% � &FF) <> TrkDesc%?bufTrk% � 3 � ID's Trk is not real track (illegal on Arc) * multiSectFlg%=� 41 �addSectInfo(TrkDesc%, count%, illegalTrk%) >� H/� ((ID% >> 8) � &FF) <> TrkDesc%?bufHead% � R) multiSectFlg%=�: � being over safe? \ � debug � f1 � **** can this be done elsewhere ****??? p" �printID(TrkDesc%, count%) z) �"Head incorrect, but acceptable" � � �� �$�getIllegal(density%, low%, hi%) �� I%=1 � 3 � T%=(ID% � &FF) �$ � (T% >= low%) � (T% <= hi%) � �= � I can't write ID as it has illegal writeTrack bytes � multiSectFlg%=� �5 �addSectInfo(TrkDesc%, count%, illegalIDbyt%) � � � ID%=(ID% >> 8) �� �� > � For each sector set Length of max read data transfer. F � In case of protected disc, ensure it cannot overwrite next ID $/ � around track (actually 'next ID - 4'). .I � The set Length will also be used during any data area write, BUT 8G � If any sector write data might overwrite 'next ID - 4' (ie gap BF � from end of data to 'next ID - 4' is less than 5 bytes), then LF � set Long data flag and unset multisector flag. Furthermore if VB � there is not even room for a 'format generated CRC', also ` � set no-Room-CRC flag. j3��setLengths(TrkDesc%, count%, � multiSectFlg%) tB� followingIDadd%, sectFound%, bytes%, add%, IDadd%, gap%, ID% ~� density% �%density%= TrkDesc%?bufTrkDensity% �-followingIDadd%=TrkDesc%!bufEndValidData% �#� sectFound%= count%-1 � 0 � -1 �, IDadd%=�getIDPtr(TrkDesc%, sectFound%) �, add%=�getDataPtr(TrkDesc%, sectFound%) �* ID%=�getSectID(TrkDesc%, sectFound%) �( bytes%=1 << (7+ ((ID% >> 24) � 3)) �0 gap% = (followingIDadd%-4) - (add%+bytes%) � � gap% < 5 � �5 �addSectInfo(TrkDesc%, sectFound%, longData%) � multiSectFlg%=� � � gap% < 2 � 8 �addSectInfo(TrkDesc%, sectFound%, noRoomCRC%) � gap% < 0 � J bytes% += gap% :� reduce transfer size if it extends to ID-4 � ( � 2 � <2 �setDataLength(TrkDesc%, sectFound%, bytes%) F followingIDadd%=IDadd% P� Z� d nK � find if sector numbers are consecutive, and all have the same size x& � if not set Multi sector False �E � if Multi sector still set, setup Multi-sector descriptor and � � memory pointer list �5��setMultiSect(TrkDesc%, count%, � multiSectFlg%) �G� max%,min%,sector%,IDadd%,dataadd%,sectNumb%, restOfID%, sectSize% � � ID% �max%=-1 :min%=256 �� sector%=0 � count%-1 �' ID%=�getSectID(TrkDesc%, sector%) � � sector%=0 � �! restOfID%=&FF00FFFF � ID% �! sectSize%=(ID% >> 24) � 3 � � �* � restOfID% <> (&FF00FFFF � ID%) � multiSectFlg%=� � � "! sectNumb%=&FF � (ID% >> 16) ,) � sectNumb% > max% � max%=sectNumb% 6) � sectNumb% < min% � min%=sectNumb% @� J3� ((max%-min%) <> (count%-1)) � multiSectFlg%=� T� multiSectFlg% � ^! TrkDesc%?bufLowSect% = min% h& TrkDesc%?bufSectSize% =sectSize% r � sector%=0 � count%-1 |/ dataadd%=�getDataPtr(TrkDesc%, sector%) �) ID%=�getSectID(TrkDesc%, sector%) �, sectNumb%=(&FF � (ID% >> 16)) - min% �J TrkDesc%!(bufMemAddList% + (sectNumb%<<3))= dataadd% :� memory add �L TrkDesc%!(bufMemAddList%+4+(sectNumb%<<3))=1 << (7+sectSize%):� size � � �� �� � �@ � copy MultiSector Memory Address List into Buffer for use �&��copyMemAddList(TrkDesc%, count%) �� I%,J% �J%=((count%-1)<<3)+4 �� I%=0 � J% � 4 1 memAddList%!I%=TrkDesc%!(bufMemAddList%+I%) � � & 07 � Print any unusual sector followed by reason why :" � IF debug print all sectors D$��printUnusual(TrkDesc%, count%) N!� sectFound%, info%, discerr% X� count%<>0 � b � sectFound%=0 � count%-1 l: info%=&FFFFFF � �getSectInfo(TrkDesc%, sectFound%) v � debug � (info%<>0) � �( �printID(TrkDesc%, sectFound%) � � debug � � � � � � info% <> 0 � �( � (info% � illegalTrk%) <> 0 � �0 �sectText("Illegal ID Trk (on Arc)") � � �* � (info% � illegalIDbyt%) <> 0 � �( �sectText("Illegal ID byte") � � �$ � (info% � noData%) <> 0 � �) �sectText("Has NO data area") � � % � (info% � delData%) <> 0 � * �sectText("Deleted data area") � & � (info% � longData%) <> 0 � *9 �sectText("Data TOO long:- Overlaps next ID") 4 � >) � (info% � noRoomCRC%) <> 0 � H; �sectText("Data long:- gap too short, write") R � \ � f' � (info% � overIndex%) <> 0 � p- �sectText("Data overflows index") z � �" discerr% = (info% � &FF) � � discerr% <> 0 � �6 Ȏ (discerr% � (errNotFound% � errCRC%)) � �B � (errNotFound% � errCRC%):�sectText("ID CRC error") �> � errCRC%: �sectText("Data area CRC error") �; � errNotFound%: �sectText("Sector not Found") � � �4 � discerr% � �(errCRC% � errNotFound%) � �: �sectText("Unknown disc err &"+�~(discerr%)) � � � � � � � � � � $ .E� ** all following assume count% starts from 0 as first sector ** 8 B&��setIDPtr(TrkDesc%, count%, add%) L2!�sectDesc(TrkDesc%, count%, bufIDptr%) = add% V� ` j ݤgetIDPtr(TrkDesc%, count%) t,=!�sectDesc(TrkDesc%, count%, bufIDptr%) ~ �(��setDataPtr(TrkDesc%, count%, add%) �4!�sectDesc(TrkDesc%, count%, bufDataptr%) = add% �� � �"ݤgetDataPtr(TrkDesc%, count%) �.=!�sectDesc(TrkDesc%, count%, bufDataptr%) � �-��setDataLength(TrkDesc%, count%, bytes%) �:!�sectDesc(TrkDesc%, count%, bufTransLength%) = bytes% �� � �%ݤgetDataLength(TrkDesc%, count%) 2=!�sectDesc(TrkDesc%, count%, bufTransLength%) &��setSectID(TrkDesc%, count%, ID%) 2!�sectDesc(TrkDesc%, count%, bufSectID%) = ID% (� 2 <!ݤgetSectID(TrkDesc%, count%) F-=!�sectDesc(TrkDesc%, count%, bufSectID%) P Z+��setSectInfo(TrkDesc%, count%, value%) d7!�sectDesc(TrkDesc%, count%, bufSectInfo%) = value% n� x �+��addSectInfo(TrkDesc%, count%, value%) �I�setSectInfo(TrkDesc%,count%, value% � �getSectInfo(TrkDesc%,count%)) �� � �#ݤgetSectInfo(TrkDesc%, count%) �/=!�sectDesc(TrkDesc%, count%, bufSectInfo%) � �)ݤsectDesc(TrkDesc%, count%, offset%) �D=TrkDesc%+bufSectDesc% + offset% + (count% << Log2SectInfoSize%) � � �$��fill(value%, start%, bytelen%) �%B%=value%: C%=start%: D%=bytelen% � fill � "%��setDefaultDiscRec(drv, density) ,Ȏ density � 65 � SDensity%: �setDiscRec(drv, 10, 1, SDensity%) @5 � DDensity%: �setDiscRec(drv, 5, 3, DDensity%) J� T� ^ h4��setDiscRec(drv, sectPerTrk, sectSize, density) r,sectSize=sectSize � &3 :� only bits used |discRec%?0 = 7+sectSize �discRec%?1 = sectPerTrk �discRec%?2 = 2 :� heads �discRec%?3 = density �LdiscRec%!16 = 160*(discRec%?1)*(1 << (discRec%?0)): � disc size in bytes �discRec%?34 = drv �discRec%!64 = &20000000 �� � �,� assumes that discRec% is already setup �ݤdiscAdd(trk, head, sect) �E=(sect + (head + trk*(discRec%?2))*(discRec%?1) ) << (discRec%?0) � �� �engageDisc(drv) 5� ensure that disc is properly engaged & rotating � T% $�rtz(drv) :� rotate disc via rtz &T%=� 0:� � � >(T%+400) :� and wait for disc to stop - Engaged :%�rtz(drv) :� then rotate it again D� N X��rtz(drv) b/�optrack(Restore%, drv, 0, 0, DDensity%, 0) l� v ���seek(drv, trk) �.�optrack(Seek%, drv, trk, 0, DDensity%, 0) �� � �1��readtrack(drv, trk, head, density, dmaAdd%) �:�optrack(ReadTrack%, drv, trk, head, density, dmaAdd%) �� � �2��writetrack(drv, trk, head, density, dmaAdd%) �;�optrack(WriteTrack%, drv, trk, head, density, dmaAdd%) �� � �9��writetrackChk(drv%, trk%, head%, density%, dmaAdd%) !F� do write track, if error(write protected) print message & repeat !� !7 �writetrack(drv%, trk%, head%, density%, dmaAdd%) ! � result%<>0 � !*[ �printSrt("**** Come on, remove the disc's write protect, then press space ****") !4 �pressspace !> � !H � !R� result%=0 !\� !f !p5��optrack(cmd%, drv, trk, head, density, dmaAdd%) !zJ� uses default dummy discRec%, so ADFS doesn't try to read disc format !�$�setDefaultDiscRec(drv, density) !�:�discop(cmd%, drv, �discAdd(trk, head, 0), dmaAdd%, 0) !�� !� !�F��readsectors(drv,trk,head,sect,numbSect,sectSize,density,dmaAdd%) !�I�opsectors(Read%,drv,trk,head,sect,numbSect,sectSize,density,dmaAdd%) !�� !� !�G��writesectors(drv,trk,head,sect,numbSect,sectSize,density,dmaAdd%) !�J�opsectors(Write%,drv,trk,head,sect,numbSect,sectSize,density,dmaAdd%) !�� !� !�7� -- Read bytes, starting from begining of a sector "E� -- Bytes need not be whole sectors, but must not overflow track "C��opbytes(cmd%, drv, trk, head, sect, bytes%, density, dmaAdd%) ".� sectPerTrk, discAdd%, sectSize, numbSect "$sectSize=-1 ".5� repeat, ends up fooling ADFS so it always works "8� "B sectSize +=1 "L, numbSect=1+((bytes%-1) >>(7+sectSize)) "V+� numbSect <&100 :� must be single byte "`AsectPerTrk = sect + numbSect :� fool ADFS, so it always works "j3�setDiscRec(drv, sectPerTrk, sectSize, density) "t(discAdd% = �discAdd(trk, head, sect) "~1�discop(cmd%, drv, discAdd%, dmaAdd%, bytes%) "�� "� "�I��opsectors(cmd%,drv,trk,head,sect,numbSect,sectSize,density,dmaAdd%) "�� sectPerTrk, discAdd% "�AsectPerTrk = sect + numbSect :� fool ADFS, so it always works "�3�setDiscRec(drv, sectPerTrk, sectSize, density) "�(discAdd% = �discAdd(trk, head, sect) "�D�discop(cmd%, drv, discAdd%, dmaAdd%, numbSect << (discRec%?0) ) "�� "� "�. � on exit result%=0 or disc error number "�2��discop(cmd%, drv, discAdd%, dmaAdd%, bytes%) # R1%= cmd% � (discRec% << 6) # -R2%= (drv << 29) + (discAdd% � &1FFFFFFF) #bș XAdfsSwi% , 0, R1%, R2%, dmaAdd%, bytes% � result%,,nextDiscByte%, nextMemByte%, BytesLeft% #'� result% � �discerr(cmd%, result%) #(� #2 #<��discerr(cmd%, � result%) #F� (result% � (1 << 31)) � #P( �'"Error &";~result% � &3FFFFFFF:� #Z� #d- � ((!result%) � &FFFFFF)=AdfsDiscErr% � #n; � analyse � � ((cmd%=ReadTrack%)�((result%?3)=4)) � #x# �print_err(cmd%, result%) #� � #� result%=result%?3 #� � #�7 � ((!result%) � &FFFFFF) <> AdfsWriteProtect% � #�& �print_err(cmd%, result%): � #� � #� � #�� #�� #� #�4� 1770/1772 or ADFS restrictions/characteristics #�D� ADFS - will not allow you to write deleted data other than via #�J� the format(write track) command, nor will it signal any error $K� on reading deleted data (including multi-sector deleted data). $C� - The only disc errors signalled are (plus combination)- $J� &10 - Sector not found (incl. ID CRC error or data mark> &FB) $"D� &08 - CRC error (incl. ID & data CRC or data mark< &F8) $,E� n.b. write protect is signalled but not as a disc error. $6&� 1770 - For disc errors see above $@I� - DD ID mark can have any value between &FC-&FF (not just &FE) $JJ� - DD Data area mark can be any of &F8-&FB (not just &FB or &F8) $TF� Note ADFS masks any differnce in deleted and normal data. $^?� - ID head number is ignored, so need not be correct. $hJ� - Only the bottom 2 bits of SectSize are used, rest are ignored $rB� - Missing clocks in either ID or data are not detected, $|C� providing the CRC is set as if the values were &A1 for $�D� '&F5' missing clocks (ie you can't use &F7 to set CRC). $�F� - Write Track cannot use in data or ID area, byte values of $�7� &F5-&F7 for Single or Double Density $�J� &F8-&FB, &FE for Single Density, unless CRC is S/W generated $�G� The former results in wrong data written, while the latter $�7� results in an incorrect CRC (using '&F7'). $�F� However, there is one way of writing the actual values of $�F� &F5,&F6,&F7, during write track, if the byte immeadiately $�E� follows a &F7. So it will be very difficult to generate. $�I� - For double density, none of the 12 sync bytes(ie 0 preceding $�G� AM) need be there, thus previous data area can go right up $�%� to the AM of the next ID $� %5� ******* Various standard disc formats ********* %A� sect_per_trk first_sect sect_size density % %&>� ADFS(800K) 5 0 3 (1024) 2 %0>� ADFS(640K) 16 0 1 (256) 2 %:>� BBC DFS 10 0 1 (256) 1 %D>� MS-DOS 3 9 1 2 (512) 2 %N %X %b��pressspace %l�("fx 15,1") %v� � �(0)=32 %�� %� %���printbits(byte%) %�� I% %�� I%=7 � 0 � -1 %�- � ((byte% >> I%) � 1) � � �"1" � � �"0" %�� %�� %� %���checkDiscIn(text$) %�� copydisc � %� � source=dest � %�: �0,�)" Insert "+text$+" disc, then press space"; & �pressspace & �0,�)�50," ");�0,�); & � & � &*� &4 &>��printLine(T$) &H@� at start of next free line, print text followed by newline &R;� print at start of next free line followed by newline &\�printSrt(T$) &f� &p� &z &���printSrt(T$) &�<� at start of next free line, print text without newline &�� �<>0 � � &� �;T$; &�� &� &���printID(TrkDesc%, count%) &�>� At start of next free line, print real trk/head/density, &�0� then ID bytes. All without newline at end &�(� ID%, trk%, head%, sect%, sectSize% &�$ID%=�getSectID(TrkDesc%, count%) &�trk%= &FF � ID% &�head%= &FF � (ID% >> 8) 'sect%= &FF � (ID% >> 16) 'sectSize%=&FF � (ID% >> 24) '�printSrt("") '$#� (TrkDesc%?bufTrkDensity%)=2 � '. �;"DDensity"; '8� 'B �;"SDensity"; 'L� 'V� �8);" ID: "; '`� �13);"Trk=";trk%; 'j� �21);"Hd=";head%; 't� �28);"Sect=";sect%; '~� �37);"Size=";sectSize%; '��;":-"; '�� '� '���sectText(T$) '��;�47,�);T$ '�� '� '���print_err(cmd%, add%) '�� I% '��7 '��printSrt("Disc ") '�)� cmd%=Verify% � �"verify sector(s)"; ( %� cmd%=Read% � �"read sector(s)"; ( '� cmd%=Write% � �"write sector(s)"; (&� cmd%=ReadTrack% � �"read track"; ((� cmd%=WriteTrack% � �"write track"; ((� cmd%=Seek% � �"seek"; (2� cmd%=Restore% � �"rtz"; (<�" error:- "; (FI%=4 (Pȕ add%?I% (Z � add%?I% (d I%=I%+1 (n� (x� (�� (� (�Z��menu(� copydisc, � source, � dest, � srttrack, � endtrack, � firstHead, � numbHeads) (�� 0 (��19,0,4,0,0,0 (��28);"W A R N I N G" (�R�" This program may only be used to make a backup copy of your own software." (�R�" It is your (the User's) responsiblity to ensure that any software copied," (�0�" using this program, is done so legally." (� (�'�20,6);"DUP:- THE DISC DUPLICATOR" (�(�20,7);"--------------------------" (�,�18,8);"(c) Softcorn Version ";vers$ ) ) copysel=1 )copyVpos=10 )"�0,copyVpos); ),4�10);" 1. Copy full double-sided 80 track disc" )64�10);" 2. Copy full single-sided 80 track disc" )@+�10);" 3. Copy selected portions only" )J )TanalVpos=14 )^�0,analVpos); )h7�10);" 4. Analyse full double-sided 80 track disc" )r7�10);" 5. Analyse full single-sided 80 track disc" )|.�10);" 6. Analyse selected portions only" )� )�drvsel=7 )�drvVpos=18 )��0,drvVpos); )�"�10);" 7. Using just drive 0" )�"�10);" 8. Using just drive 1" )�'�10);" 9. From drive 0 To drive 1" )�'�10);"10. From drive 1 To drive 0" )� )� goVpos=23 )��0,goVpos); )��10);"11. DO :-" )� *selVpos=26 *exitMenu%=� *select=0 *&� *0� select=11 � exitMenu%=� *:copydisc=(copysel <=3) *D� copysel<>3 � copysel<>6 � *N srttrack=0 *X endtrack=79 *b firstHead=0 *l � copysel=1 � copysel=4 � *v numbHeads=2 *� � *� numbHeads=1 *� � *�� *�� drvsel=7 � drvsel=9 � *� source=0 *�� *� source=1 *�� *�� drvsel=7 � drvsel=10 � *� dest=0 *�� *� dest=1 +� + � I%=copyVpos � goVpos + �8,I%);" " + � +* � copysel <=3 � +4# �8,copyVpos+copysel-1);"*" +>! �8,drvVpos+drvsel-7);"*" +H8 �20,goVpos);"** Copy from ";source;" to ";dest; +R � +\# �8,analVpos+copysel-4);"*" +f) �8,drvVpos+(1 � (drvsel-7)));"*" +p* �20,goVpos);"** Analyse ";source; +z � +�* �;", Tracks ";srttrack;"-";endtrack; +� � numbHeads=2 � +� �;", both sides **"; +� � +�) �;", side ";firstHead;" only **"; +� � +� �;�(78-�)," ") +� �0,selVpos);�78," "); +� � � exitMenu% � +� �20,selVpos);"SELECT "; +� � select +�! � select=999 � �dispProcs +�5 � (select>=1) � (select <=6) � copysel=select ,5 � (select>=7) � (select <=10) � drvsel=select ,% � (select=3) � (select = 6) � ,@ �getSelective("First Track", selVpos, srttrack, 0, 79) ,$ � srttrack<>79 � ,.F �getSelective("Last Track",selVpos, endtrack, srttrack, 79) ,8 � ,BD �getSelective("Number of sides", selVpos, numbHeads, 1, 2) ,L � numbHeads=1 � ,V: �getSelective("Side", selVpos, firstHead, 0, 1) ,` � ,j � ,t � ,~� exitMenu% ,��0,goVpos+2); ,�� ,� ,�2��getSelective(T$, vpos, � value%, min%, max%) ,�� ,� �0,vpos);�78," "); ,�, �18,vpos);T$;" (";min%;"-";max%;") "; ,� � value% ,�+ � (value%<min%) � (value%>max%) � � 7 ,�)� (value% >= min%) � (value% <= max%) ,�� ,� - ��dispProcs - � -8�" On a single drive, a disc change is needed every" -4�" ";�(MaxTrks%/2);" tracks (double sided), "; -((� ;MaxTrks%;" tracks (single sided)" -2� -<!�" Try (& f1 to view result)" -F^�"PROCreadsectors(drive, track, head, sector, numbSect, sectSize,density, ";MainBuffer%")" -PC�"PROCreadtrack (drive, track, head, density, ";MainBuffer%" )" -ZJ�"PROCreadSingleTrk(drive, track, head, ";MainBuffer%", srtDensity% )" -d;analyse=debug :� only for use of typed PROCs after exit -n� -x� -� -�� �logo -�� 7 -��' -�0�"�� p "; -�0�"�� p|���|p "; -�0�"�� z?��o����t "; -�1�"�� _��&`""o����}0 "; -�0�"�� h6? o�����4 "; -�0�"��x||0_||0||||||_||0 _p2%app �|||0|0 |"; -�0�"���1+%�'+��```�`�'+� 5�������55 �`k5�}��"; -�0�"��+�}0� ���� � � ,5�������5=$�|~%�ou�"; -�1�"��p k��0_�� � �0_| 5�������55 �+} �""��"; .1�"��o��%+��'� � +��' ""`c,lt` � ku� *�"; .1�"�� ""o5�u 8|||||| "; .0�"�� j�?yp~������� "; ."0�"�� o����������� "; .,0�"�� `/�������?! "; .6�" DUP the Disc Duplicator" .@5�'''''"(public domain s/w, no profit based use)"; .J T%=�(500) .T� .^ .h ��init .rBMaxTrkUnformat% = 4*�((6250*1.03)/4) :� Exact number of Words .|MaxSect%=1024 .�,TrkDataSize%=MaxTrkUnformat%+MaxSect%+64 .�ATrkDescSize%=�initBufoffsets :� exact number of Words .� .�� discRec% 100 .�$� memAddList% maxSectsAllowed%*8 .� .�B� mapID% holds ID map descriptors (see end of prog for format) .�� mapID% &12000 .�;�("*Load DupIDmap "+�~mapID%) :� generated by DupIDtest .� .�1� put all other DIMs before here except code% .�leaveSpare=10000 .�� dummy% 1 /Dbuffersize=4*�((�-dummy%-leaveSpare)/4) :� Exact number of Words / � MainBuffer% buffersize +16 /JMainBuffer%=16*�((MainBuffer% +15)/16):� on 16 byte boundary for debug /& /09MaxTrks%= �(buffersize/(TrkDataSize% + TrkDescSize%)) /:GMaxTrks% -= 3 :� for safety cos ReadTrk sometimes runs for too long /D /NIWriteTrkBuf%=MainBuffer%+buffersize-TrkDataSize% :� use overflow area /X /bcodelength=1000 /lC� code% codelength :� must be last DIM to ensures code%> &FFFF /v> :� cos of BBC BASIC (6502) emulation /� Verify%=0 /�Read%=1 /�Write%=2 /�ReadTrack%=3 /�WriteTrack%=4 /�Seek%=5 /�Restore%=6 /�SDensity%=1 /�DDensity%=2 /�AdfsSwi%=&40240 /� XAdfsSwi%=AdfsSwi% � (1<<17) /�AdfsDiscErr%=&108C7 /�AdfsWriteProtect%=&108C9 0AltDefectBit%=&10 0 �code 0 � clears buffers 0 �fill(0,discRec%,70) 0*9�fill(0,MainBuffer%,buffersize) :� is this needed ??? 04*�("key1 *MEDIT "+�~(MainBuffer%)+"|M") 0>+�("key2 *MEDIT "+�~(WriteTrkBuf%)+"|M") 0H� 0R 0\ݤinitBufoffsets 0fK� initialises all variables that are offsets pointers into track buffer 0pD� and returns offset for start of track data (ie for Read Track) 0z3 � note each track in buffer will be made up of 0�& � - A Track descriptor, including 0� � - general track info 0�L � - followed by a list of pointers for each sector (both ID & Data) 0�2 � - followed by a multi-sector descriptor 0�C � - Followed by the tracks 'ReadTrack data, supperimposed with 0� � readSector data. 0� 0�E maxSectsAllowed%=32 :� determines reserved space for track info 0� 0�� GENERAL TRACK INFO 0�4bufTrk%=0 :� real track number (byte) 0�4bufHead%=1 :� real head number (byte) 0�=bufNumbSect%=2 :� number of sectors on track (byte) 1HbufMultiSectFlg%=3 :� TRUE = track can use multi-sector read/write 1K � (ie no read errors and sectors are all consecutive; skew is allowed) 1*bufTrkDensity%=4 :� density (byte) 1$AbufEndValidData%=8 :� end of valid data in Trk buffer(word) 1.2 base%=16 :� bytes 5-7 & 12-15 free 18 1B� SECTOR INFORMATION LIST 1L<� entries are in the order that sectors are around track 1V=� each entry - pointer to start of ID (absolute word add) 1`D� - pointer to start of Data Area (absolute word add) 1j:� (if = 0 then no data area for ID found) 1tL� - byte length of sector read/write data op. It will be less 1~F� than sector size if next sect ID would be corrupted 1�G� - Save version of ID, use this version instead of what 1�E� is in track buffer in case of possible corrupt ID. 1�E� - sector information, if <>0 THEN don't write sector 1�C� catches ID CRC err, Deleted data, DataCRC err, 1�F� illegal Arc ID, Data Area too long, No Data Area, 1�G� and Data Area could and was written during format. 1�J Log2SectInfoSize%=5 :� Log 2 of size of Sector Info (= 32 (8 words)) 1�bufSectDesc%=base% 1�&bufIDptr% =0 :� bytes 0-3 1�&bufDataptr% =4 :� bytes 4-7 1�'bufTransLength%=8 :� bytes 8-11 1�'bufSectID%=12 :� bytes 12-15 2 'bufSectInfo%=16 :� bytes 16-19 2 6 :� byte 16 = disc err result% 28errCRC%= 1<< 3 :� CRC error bit in disc error 2?errNotFound%= 1<< 4 :� sector not found bit in disc error 2(# :� byte 17 225delData%= 1<< 8 :� bit 1= deleted Data Area 2<0noData%= 1<< 9 :� bit 2= No Data Area 2F>illegalTrk%= 1<< 10 :� bit 3= Illegal ID Trk (ie on Arc) 2P@illegalIDbyt%=1<<11 :� bit 4= Illegal ID byte (ie &F5-&F7) 2ZFlongData%= 1<< 12 :� bit 5= Data too close to next ID to write 2dAnoRoomCRC%= 1<< 13 :� bit 6= So long no space for even CRC 2nIoverIndex%= 1<< 14 :� bit 7= Data area goes over Index(must write) 2xKdataDuringFormat%= 1 << 16 :� Data could and was written during format. 2�C base% += maxSectsAllowed% << Log2SectInfoSize% :� 7 words free 2� 2�,� MULTI-SECTOR DATA AREA READ/WRITE INFO 2�A� This info is only valid if Multi-Sector flag above is true 2�@bufLowSect%=base% :� byte 0 lowest sector around track 2�1bufSectSize%=base%+1 :� byte 1 Sector Size 2�/ base% += 4 :� bytes 2 - 3 free 2�>� memory pointer list for discOp one entry for each sector 2�K� entries must be in consectutive sector number order not in the order 2�$� they appear around the track. 2�&� Each entry consists of two words 2�;� first = absolute address of Sector's data in memory 2�C� second = sector size in bytes (nb they must all be the same) 3B� note list must be word aligned, so assuming track descriptor 3� starts at word aligned 34 base%= 4*�((base%+3)/4) :� ensure word aligned 3"bufMemAddList%= base% 3, base% += 8*maxSectsAllowed% 36 3@/� start of where read track data is stored, 3JC� nb data areas will be overwritten by actual read sector data 3TE� but Address Marks and gaps will be still in Read Track form 3^=� Write track must use a copy of this (but corrected) 3h= base% 3r 3| 3� ��code 3�� pass= 0 � 2 � 2 3�P%=code% 3�[ OPT pass 3� 3�; get BASIC's � parameters 3�@; �E they must all be word aligned integer variables, � even 3�7; !param% is allowed, only param% or param%(x) 3�C; R1= Reserved, usually used by caller for BASIC's link address 3�/; R9= Pointer to list of L-value parameters 3�&; R10=number of parameters (max=6) 3�=; For format of � parameter block see standard User guide 3�; especially for R9 4; on exit R0 is corrupt 4; R1 is unaltered 4B; R2=first parameter (if there is one) else unchanged 4&B; R3=second parameter (if there is one) else unchanged 40?; .. ..... ...... .. ..... .. ... ... ..... 4:B; R7=sixth parameter (if there is one) else unchanged 4D; 4N.; call this routine after the BASIC � by - 4X;; MOV R1, R14 ;saves BASIC's link address 4bK; BL paramvalues ;load all BASIC's parameters into registers 4l>; MOV R14, R1 ;restores BASIC's link address 4v; 4�:; to update a BASIC variable (last parameter) at end - 4�=; LDR R0, [R9] ;get last BASIC parameter add 4�=; STR Rx, [R0] ;& update it with value in Rx 4�0; MOV pc, R14 ;return 4� 4�.paramvalues 4�ADD R0, R9, R10, LSL #3 4�CMP R10, #1 4�LDRGE R2, [R0, #-8]! 4�&LDRGE R2, [R2] ;R2=1st param 4�CMP R10, #2 4�LDRGE R3, [R0, #-8]! 4�&LDRGE R3, [R3] ;R3=2nd param 5CMP R10, #3 5LDRGE R4, [R0, #-8]! 5&LDRGE R4, [R4] ;R4=3rd param 5 CMP R10, #4 5*LDRGE R5, [R0, #-8]! 54LDRGE R5, [R5] 5>CMP R10, #5 5HLDRGE R6, [R0, #-8]! 5RLDRGE R6, [R6] 5\CMP R10, #6 5fLDRGE R7, [R0, #-8]! 5p%LDRGE R7, [R7] ;R7=6th param 5zMOV pc, R14 ;return 5� 5� 5�:;fill byte area, R1=byte value, R2=startadd, R3=length 5� .fill 5�.� R1, R1, #&FF ;use LS byte ony 5��R R1, R1, R1, LSL #8 5�@�R R1, R1, R1, LSL #16 ;byte repeated in all bytes in R1 5�ADD R0, R2, R3 5�.fillbytstart 5�&TST R0, #3 \word boundary? 5�BEQ fillwords 5�CMP R0, R2 5�BEQ fillend 6STRB R1, [R0,#-1]! 6B fillbytstart 6.fillwords 6$SUB R3, R0, R2 6.CMP R3, #4 68STRGE R1, [R0,#-4]! 6BBGT fillwords 6L.fillbytend 6VCMP R0, R2 6`STRNEB R1, [R0,#-1]! 6jBNE fillbytend 6t.fillend 6~MOV pc, R14 \return 6� 6�A;find, backwards, were data byte changes & return the address 6� ; � findchangeback startadd% 6�1;on entry 1st BASIC parameter = start address 6�?;on exit 1st BASIC parameter = address where change occurs 6�"; R0, R1, R2 corrupt 6�.findchangeback 6�MOV R1, R14 6�=BL paramvalues ;R2 set = start add (1st parameter) 6�MOV R14, R1 6�LDRB R0, [R2] 6�.findchangeloop 7 LDRB R1, [R2, #-1]! 7 CMP R1, R0 7BEQ findchangeloop 72LDR R0, [R9] ;get last BASIC parameter add 7(6STR R2, [R0] ;& update it with remade ID in R1 72&MOV pc, R14 ;return 7< 7F;copy bytes forward 7P-; '� copyfwd, startadd, endadd, destadd' 7Z3;on entry 1st BASIC parameter = source startadd 7d6; 2nd BASIC parameter = source end address 7n9; 3rd BASIC parameter = destination start add 7x&; R0,R1,R2,R3,R4 corrupted 7�.copyfwd 7�DMOV R1, R14 ;R2 set = source start add (1st parameter) 7�BBL paramvalues ;R3 set = source end add (2nd parameter) 7�BMOV R14, R1 ;R4 set = dest start add (3rd parameter) 7�.copyfwdloop 7�LDRB R0, [R2], #1 7�STRB R0, [R4], #1 7�CMP R2, R3 7�BLE copyfwdloop 7�!MOV pc, R14 ;return 7� 7�<;copy bytes forward but convert any illegal bytes to &FF 7�3;on entry 1st BASIC parameter = source startadd 86; 2nd BASIC parameter = source end address 89; 3rd BASIC parameter = destination start add 8=; 4th BASIC parameter = low of illegal byte range 8">; 5th BASIC parameter = high of illegal byte range 8,7; 6th BASIC parameter = exit parameter only 86D;on exit 6th BASIC parameter = � if any bytes needed converting 8@/; R0,R1,R2,R3,R4,R5,R6,R7 corrupted 8J.selcopyfwd 8TDMOV R1, R14 ;R2 set = source start add (1st parameter) 8^BBL paramvalues ;R3 set = source end add (2nd parameter) 8hBMOV R14, R1 ;R4 set = dest start add (3rd parameter) 8r7 ;R5 to R6 = illegal chr range 8|)MOV R7, #0 ;R7 = 0 default 8�.selcopyfwd1 8�LDRB R0, [R2], #1 8�CMP R0, R5 8�BLT selcopyfwd2 8�)CMP R0, R6 ;if illegal chr 8�%MOVLE R0, #&FF ;set to &FF 8�IMVNLE R7, #(1-1) ;& set R7 to -1 if any byte had to be converted 8�.selcopyfwd2 8�STRB R0, [R4], #1 8�CMP R2, R3 8�BLE selcopyfwd1 8�.LDR R0, [R9] ;get last BASIC parameter 8�$STR R7, [R0] ;& set it to R5 9 MOV pc, R14 ;return 9 9L;fill backwards(destination) while source(backwards) continues to remain 9&0; the same, or until end(source) is reached. 90;;on entry ALL following must be integer BASIC variables 9:4; 1st BASIC parameter = source start add 9DL; 2nd BASIC parameter = source end add (must be < 1st parameter) 9N9; 3rd BASIC parameter = destination start add 9X6; 4th BASIC parameter = value to fill with 9b?; 5th BASIC parameter = exit value only (see below) 9lJ;on exit 5th BASIC variable is set = address where source byte changed 9vH; or (endadd-1) if no change occurs 9�.limitfillback 9�4MOV R1, R14 ;R2 set = source start add 9�2BL paramvalues ;R3 set = source end add 9�2MOV R14, R1 ;R4 set = dest start add 9�. ;R5 set = fill value 9�1LDRB R1, [R2] ;get first source value 9�;ADD R4, R4, #1 ; increment R3 (as initial value) 9�.limitfillback1 9�6CMP R2, R3 ; stop filling when past end 9�BLT limitfillbackend 9�STRB R5, [R4, #-1]! 9�LDRB R0, [R2, #-1]! 9�7CMP R0, R1 ;or when source value changes :BEQ limitfillback1 :.limitfillbackend :2LDR R0, [R9] ;get last BASIC parameter add : )STR R2, [R0] ;& update it with R2 :*&MOV pc, R14 ;return :4 :> :H0;Find Double Density ID address mark pattern :RB; as for findAM below but only ID address marks are looked for :\5; except R5 is also corrupted (used to save link) :f .DDfindID :p#MOV R5, R14 ;save link :zBL DDfindAM :�&MOV R14, R5 ;restore link :�-CMP R2, #0 ;add=0? then exit :�BEQ DDfindIDend :�LDRB R0, [R2,#-1] :�<CMP R0, #&FC ;mark=&FC,&FD,&FE, or &FF then exit :�BLT DDfindID :�.DDfindIDend :� MOV R15, R14 ;return :� :� :�D;Find Double Density address mark pattern from 'Read Track' data :�"; including extra tests for ID :�,;on entry 1st BASIC parameter = startadd ;/; 2nd BASIC parameter = end address ;,;on exit 1st BASIC parameter is updated ;'; = 0 if not found ;$?; <> 0, = address of start of Address Mark ;.-; R2= same as BASIC 1st parameter ;81; R3= BASIC 2nd parameter (unaltered) ;B"; R0, R1, R4 corrupted ;L .DDfindAM ;VMOV R1, R14 ;`DBL paramvalues ;R2 set = 1st parameter add(start address) ;jBMOV R14, R1 ;R3 set = 2nd parameter add(end address) ;tSUB R2, R2, #1 ;~.DDfindAMloop ;�CMP R2, R3 ;�BEQ DDAMnotfound ;�ELDRB R0, [R2, #1]! ;R2 ends up = add of current byte comparison ;�*CMP R0, #&A1 ;first &A1 at add ;�BNE DDfindAMloop ;�LDRB R0, [R2, #1] ;�-CMP R0, #&A1 ;second &A1 at add+1 ;�BNE DDfindAMloop ;�/LDRB R0, [R2, #2] ;found &A1,&A1 pattern ;�CMP R0, #&F8 ;�BLT DDfindAMloop ;�D.DDfoundAM ;FOUND &A1,&A1,&Fx pattern, where &Fx >&F7 <