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
<