<> <> <> DIRECTORY Environment USING [wordsPerPage], File USING [nullFile, Type], Floppy USING [AlreadyFormatted, Density, Error, FileID, maxCharactersInLabel, nullFileID, PageCount, Sides], FloppyChannel USING [Attributes, Context, DiskAddress, FormatTracks, GetDeviceAttributes, GetHandle, Handle, Nop, SectorCount, SetContext, Status, WriteSectors], FloppyExtras USING [ExtrasError, ExtrasErrorType], FloppyFormat USING [Alternate, badSpotSector, badSpotSectors, BadSpotSectors, Byte, ConvertPageCount, dataContext, FileList, FileListEntry, FileListSeal, FileListVersion, FillTrackOneSector, FillTrackZeroSector, firstDataAddress, FloppySeal, FloppyVersion, Format, ImplementedFileSize, MarkerPage, MarkerSeal, maxBadSectors, nullSector, Sector, SectorNine, SectorToDiskAddress, trackOneAddress, trackOneContext, TrackOneSector, trackZeroAddress, trackZeroContext, TrackZeroSector], FloppyImplInterface, VM USING [ defaultBase, Error, Interval, Map, nullInterval, PageCount, Unmap]; FloppyImplPublicB: MONITOR LOCKS volumeDesc USING volumeDesc: FloppyImplInterface.VolumeDesc IMPORTS Floppy, FloppyChannel, FloppyFormat, FloppyImplInterface, VM EXPORTS Floppy SHARES FloppyImplInterface = BEGIN OPEN FloppyImplInterface; <> ExtrasError: PUBLIC ERROR [error: FloppyExtras.ExtrasErrorType] = CODE; <> FormatVerifyPasses: CARDINAL _ 2; BadSector: SIGNAL [sector: CARDINAL] = CODE; Bug: ERROR [bugType: BugType] = CODE; BugType: TYPE = {impossibleEndcase}; FormatType: TYPE = {format, erase}; buffer: Space.Interval _ Space.nullInterval; <> CheckSectorNine: PROCEDURE [sectorNine: LONG POINTER TO FloppyFormat.SectorNine] = <> BEGIN IF sectorNine.seal # FloppyFormat.FloppySeal OR sectorNine.version # FloppyFormat.FloppyVersion OR sectorNine.labelSize > Floppy.maxCharactersInLabel THEN RETURN WITH ERROR Floppy.Error[notFormatted]; IF sectorNine.countBadSectors > FloppyFormat.maxBadSectors THEN RETURN WITH ERROR Floppy.Error[notFormatted]; END; -- CheckSectorNine CheckBadPageMap: PROCEDURE [ badPageMap: LONG POINTER TO FloppyFormat.BadSpotSectors, numPages: Floppy.PageCount] = <> BEGIN FOR i: CARDINAL IN [0..FloppyFormat.maxBadSectors) DO IF badPageMap[i].bad > numPages OR badPageMap[i].alternate > numPages THEN RETURN WITH ERROR Floppy.Error[notFormatted]; ENDLOOP; END; -- CheckSectorTen ScanFloppy: PROCEDURE [ -- Checks to see if we can write and read the floppy volumeDesc: VolumeDesc, handle: FloppyChannel.Handle, buffer: LONG POINTER, context: FloppyChannel.Context, sectorCount: CARDINAL, firstSector: CARDINAL] = BEGIN countLeft: CARDINAL; count: CARDINAL; currentSector: CARDINAL; status: FloppyChannel.Status; currentSector _ firstSector; countLeft _ sectorCount; IF ~FloppyChannel.SetContext[handle, context] THEN ERROR Floppy.Error[badDisk]; DO -- write out the information [status, count] _ FloppyChannel.WriteSectors[ handle, FloppyFormat.SectorToDiskAddress[ currentSector, volumeDesc.sectorNine.cylinders, volumeDesc.sectorNine.tracksPerCylinder, volumeDesc.sectorNine.sectorsPerTrack], NIL, countLeft, FALSE]; <> IF (countLeft _ countLeft - count) = 0 THEN EXIT; SIGNAL BadSector[(currentSector _ currentSector + count)]; currentSector _ currentSector + 1; countLeft _ countLeft - 1; ENDLOOP; END; -- ScanFloppy CountBadPages: PROCEDURE [sector: FloppyFormat.Sector, countBadPages: CARDINAL, badPageList: LONG POINTER TO FloppyFormat.BadSpotSectors] RETURNS [count: CARDINAL _ 1, nextIndex: CARDINAL] = BEGIN <> FOR i: CARDINAL IN [0.. countBadPages) DO IF sector # badPageList[i].bad THEN LOOP; FOR nextIndex IN (i.. countBadPages) DO IF badPageList[nextIndex].bad # badPageList[i].bad + nextIndex - i THEN RETURN[count, nextIndex]; count _ count.SUCC; ENDLOOP; ENDLOOP; RETURN[count, countBadPages]; END; -- CountBadPages WriteMarkerPages: PROCEDURE [volumeDesc: VolumeDesc, markerPage: LONG POINTER TO FloppyFormat.MarkerPage, countBadPages: CARDINAL, badPageList: LONG POINTER TO FloppyFormat.BadSpotSectors] = BEGIN IF countBadPages = 0 THEN BEGIN markerPage^ _ [ previous: [length: 0, body: free[]], next: [ length: volumeDesc.numPages - 2 - FirstDataSector[volumeDesc] + 1, body: free[]]]; WriteFloppy[volumeDesc, markerPage, FirstDataSector[volumeDesc], 1]; markerPage­ _ [ previous: [ length: volumeDesc.numPages - 2 - FirstDataSector[volumeDesc] + 1, body: free[]], next: [length: 0, body: free[]]]; WriteFloppy[ volumeDesc, markerPage, FloppyFormat.ConvertPageCount[ volumeDesc.numPages], 1]; RETURN; END ELSE BEGIN <> sector: FloppyFormat.Sector; lastSector: FloppyFormat.Sector; badPageIndex: CARDINAL; <> IF badPageList[0].bad = FirstDataSector[volumeDesc] THEN BEGIN -- Find the initial run of bad pages count: CARDINAL; [count, badPageIndex] _ CountBadPages[ FirstDataSector[volumeDesc], countBadPages, badPageList]; lastSector _ FirstDataSector[volumeDesc] + count; markerPage­ _ [ previous: [length: count, body: badSectors[]], next: [length: 0, body: free[]]]; END ELSE -- the first page on the disk is good BEGIN badPageIndex _ 0; lastSector _ FirstDataSector[volumeDesc]; markerPage­ _ [ previous: [length: 0, body: free[]], next: [length: 0, body: free[]]]; END; WHILE badPageIndex < countBadPages DO <> count: CARDINAL; -- the length of the bad page run sector _ badPageList[badPageIndex].bad - 1; IF lastSector ~= sector THEN BEGIN -- We have found a run of free pages markerPage.next.length _ sector - (lastSector + 1); WriteFloppy[volumeDesc, markerPage, lastSector, 1]; -- X/Free markerPage.previous _ [ length: sector - (lastSector + 1), body: free[]]; END; [count, badPageIndex] _ CountBadPages[ sector + 1, countBadPages, badPageList]; markerPage.next _ [length: count, body: badSectors[]]; WriteFloppy[volumeDesc, markerPage, sector, 1]; -- (Bad or Free)/Bad lastSector _ sector + count + 1; markerPage­ _ [ previous: [length: count, body: badSectors[]], next: [length: 0, body: free[]]]; -- setup for bad/free ENDLOOP; sector _ FloppyFormat.ConvertPageCount[volumeDesc.numPages]; -- Last data sector IF lastSector <= sector THEN -- i.e., the last page is not bad BEGIN markerPage.next.length _ sector - (lastSector + 1); WriteFloppy[volumeDesc, markerPage, lastSector, 1]; -- Bad/Free IF sector ~= lastSector THEN BEGIN markerPage­ _ [ previous: [length: sector - (lastSector + 1), body: free[]], next: [length: 0, body: free[]]]; WriteFloppy[volumeDesc, markerPage, sector, 1]; -- Free/Free END; END; END; -- Of initializing marker pages on the disk with bad pages present END; -- WriteMarkerPages <> Format: PUBLIC PROCEDURE [drive: CARDINAL, maxNumberOfFileListEntries: CARDINAL, labelString: ROPE, density: Floppy.Density, sides: Floppy.Sides] = BEGIN <> IF ~ValidDrive[drive] THEN ERROR Floppy.Error[noSuchDrive]; FormatInternal[volumeTable[drive], format, drive, maxNumberOfFileListEntries, labelString, density, sides]; END; -- Format Erase: PUBLIC PROCEDURE [drive: CARDINAL, maxNumberOfFileListEntries: CARDINAL, labelString: ROPE] = BEGIN IF ~ValidDrive[drive] THEN ERROR Floppy.Error[noSuchDrive]; <> FormatInternal[volumeTable[drive], erase, drive, maxNumberOfFileListEntries, labelString,,]; END; -- <> FormatInternal: ENTRY PROCEDURE [volumeDesc: VolumeDesc, formatMode: FormatType, drive: CARDINAL, maxNumberOfFileListEntries: CARDINAL, labelString: ROPE, density: Floppy.Density, sides: Floppy.Sides] = BEGIN ENABLE BEGIN IOError => GOTO dataError; UNWIND => VM.Free[buffer]; END; attributes: FloppyChannel.Attributes; format: FloppyFormat.Format; trackZeroSector: LONG POINTER TO FloppyFormat.TrackZeroSector; trackOneSector: LONG POINTER TO FloppyFormat.TrackOneSector; markerPage: LONG POINTER TO FloppyFormat.MarkerPage; countBadPages: CARDINAL; countToFormat: CARDINAL; requestedSize: Space.PageCount; address: FloppyFormat.Sector; diskAddress: FloppyChannel.DiskAddress; status: FloppyChannel.Status; sectors, trackZeroSectors: FloppyChannel.SectorCount; trackOneDensity: Floppy.Density; <> requestedSize _ WordsToPages[SIZE[FloppyFormat.TrackZeroSector]] + WordsToPages[ SIZE[FloppyFormat.TrackOneSector]] + WordsToPages[ SIZE[FloppyFormat.MarkerPage]]; buffer _ CreateBuffer[requestedSize]; IF buffer.count ~= requestedSize THEN ERROR; -- Insufficient VM to work in trackZeroSector _ LOOPHOLE[buffer.pointer]; markerPage _ LOOPHOLE[trackZeroSector + Environment.wordsPerPage * WordsToPages[ SIZE[FloppyFormat.TrackZeroSector]]]; trackOneSector _ LOOPHOLE[markerPage + Environment.wordsPerPage * WordsToPages[ SIZE[FloppyFormat.BadSpotSectors]]]; <> IF volumeDesc.open THEN BEGIN IF formatMode = format THEN BEGIN label: STRING _ [Floppy.maxCharactersInLabel]; label.length _ 0; FOR i: CARDINAL IN [0..volumeDesc.sectorNine.labelSize) DO label[i] _ volumeDesc.sectorNine.label[i]; label.length _ label.length + 1; ENDLOOP; <> SIGNAL Floppy.AlreadyFormatted[label]; CloseVolume[volumeDesc]; END ELSE IF formatMode = erase THEN CloseVolume[volumeDesc]; END; volumeDesc.handle _ FloppyChannel.GetHandle[drive]; attributes _ FloppyChannel.GetDeviceAttributes[volumeDesc.handle].attributes; status _ FloppyChannel.Nop[volumeDesc.handle]; IF status = notReady THEN { buffer.pointer _ Space.Unmap[buffer.pointer]; RETURN WITH ERROR Floppy.Error[notReady]}; IF status = writeFault THEN { buffer.pointer _ Space.Unmap[buffer.pointer]; RETURN WITH ERROR Floppy.Error[writeInhibited]}; <> IF formatMode = format THEN BEGIN SELECT attributes.twoSided FROM FALSE => IF sides = one OR sides = default THEN volumeDesc.sides _ one ELSE { buffer.pointer _ Space.Unmap[buffer.pointer]; RETURN WITH ERROR Floppy.Error[onlyOneSide]}; TRUE => IF sides = two OR sides = default THEN volumeDesc.sides _ two ELSE { buffer.pointer _ Space.Unmap[buffer.pointer]; RETURN WITH ERROR Floppy.Error[invalidFormat]}; <> ENDCASE; SELECT density FROM single => volumeDesc.density _ single; double => BEGIN IF FloppyChannel.SetContext[ volumeDesc.handle, FloppyFormat.dataContext[double]] THEN volumeDesc.density _ double ELSE BEGIN buffer.pointer _ Space.Unmap[buffer.pointer]; RETURN WITH ERROR Floppy.Error[onlySingleDensity]; END; END; default => volumeDesc.density _ IF FloppyChannel.SetContext[ volumeDesc.handle, FloppyFormat.dataContext[double]] THEN double ELSE single; ENDCASE => BEGIN buffer.pointer _ Space.Unmap[buffer.pointer]; Bug[impossibleEndcase]; END; END ELSE IF formatMode = erase THEN -- get sides and density off floppy BEGIN byte: FloppyFormat.Format; diskAddress _ [ head: FloppyFormat.trackZeroAddress.head, cylinder: FloppyFormat.trackZeroAddress.cylinder, sector: 7]; AccessFloppy[ volumeDesc: volumeDesc, buffer: trackZeroSector, count: 1, address: diskAddress, access: read]; byte _ LOOPHOLE[trackZeroSector[71]]; -- reflects FloppyFormat.Format type SELECT byte FROM -- 64 (' ), 212 ('M), 242 ('2) are valid singleSidedSingleDensity => BEGIN volumeDesc.sides _ one; volumeDesc.density _ single; attributes.numberOfHeads _ 1; END; doubleDensity => BEGIN volumeDesc.sides _ two; volumeDesc.density _ double; END; doubleSidedSingleDensity => BEGIN volumeDesc.sides _ two; volumeDesc.density _ single; END; ENDCASE => RETURN WITH ERROR FloppyExtras.ExtrasError[notFormatted]; END; IF ~FloppyChannel.SetContext[ volumeDesc.handle, FloppyFormat.dataContext[volumeDesc.density]] THEN BEGIN buffer.pointer _ Space.Unmap[buffer.pointer]; RETURN WITH ERROR Floppy.Error[badDisk]; END; sectors _ FloppyChannel.GetDeviceAttributes[volumeDesc.handle].maxSectorsPerTrack; <> volumeDesc.open _ FALSE; volumeDesc.writeProtected _ FALSE; volumeDesc.numPages _ attributes.numberOfHeads * attributes.numberOfCylinders * sectors; IF formatMode = format THEN -- start initializing volumeDesc.sectorNine volumeDesc.sectorNine­ _ [ cylinders: attributes.numberOfCylinders, tracksPerCylinder: attributes.numberOfHeads, sectorsPerTrack: sectors, firstAlternateSector: FloppyFormat.nullSector <> ] ELSE IF formatMode = erase THEN <> BEGIN -- and check them to see if they seem reasonable diskAddress _ [ head: FloppyFormat.trackZeroAddress.head, cylinder: FloppyFormat.trackZeroAddress.cylinder, sector: 9]; AccessFloppy[ volumeDesc: volumeDesc, buffer: volumeDesc.sectorNine, count: 1, address: diskAddress, access: read]; CheckSectorNine[volumeDesc.sectorNine]; countBadPages _ volumeDesc.sectorNine.countBadSectors; diskAddress.sector _ FloppyFormat.badSpotSector; AccessFloppy[ volumeDesc: volumeDesc, buffer: volumeDesc.badPageMap, count: FloppyFormat.badSpotSectors, address: diskAddress, access: read]; CheckBadPageMap[volumeDesc.badPageMap, volumeDesc.numPages]; <> BEGIN OPEN volumeDesc.sectorNine; <> fileList _ FloppyFormat.nullSector; fileListID _ Floppy.nullFileID; fileListSize _ 0; rootFile _ Floppy.nullFileID; pilotMicrocode _ FloppyFormat.nullSector; diagnosticMicrocode _ FloppyFormat.nullSector; germ _ FloppyFormat.nullSector; pilotBootFile _ FloppyFormat.nullSector; firstAlternateSector _ FloppyFormat.nullSector; -- We don't allocate alternates for now nextUnusedFileID _ 1; changing _ FALSE; END; -- of OPEN END; IF labelString ~= NIL THEN -- Copy our client's label, if any BEGIN volumeDesc.sectorNine.labelSize _ MIN[ Floppy.maxCharactersInLabel, labelString.length]; FOR i: CARDINAL IN [0..volumeDesc.sectorNine.labelSize) DO volumeDesc.sectorNine.label[i] _ labelString[i]; ENDLOOP; FOR i: CARDINAL IN [volumeDesc.sectorNine.labelSize..Floppy.maxCharactersInLabel) DO volumeDesc.sectorNine.label[i] _ ' ; ENDLOOP; END; <> <> IF formatMode = format THEN -- this block only if are formatting BEGIN <> <> countToFormat _ (attributes.numberOfCylinders - 1) * attributes.numberOfHeads; IF FloppyChannel.FormatTracks[ volumeDesc.handle, FloppyFormat.firstDataAddress, countToFormat].countDone ~= countToFormat THEN BEGIN buffer.pointer _ Space.Unmap[buffer.pointer]; RETURN WITH ERROR Floppy.Error[badDisk]; END; <> <> countBadPages _ 0; FOR i: CARDINAL IN [0..LENGTH[volumeDesc.badPageMap­]) DO volumeDesc.badPageMap[i] _ [ bad: FloppyFormat.nullSector, alternate: FloppyFormat.nullSector]; ENDLOOP; FOR i: CARDINAL IN [0..FormatVerifyPasses) DO cylinderZeroSectors: CARDINAL = sectors * attributes.numberOfHeads; SetBlock[buffer.pointer, Environment.wordsPerPage, 0]; ScanFloppy[ volumeDesc, volumeDesc.handle, buffer.pointer, FloppyFormat.dataContext[volumeDesc.density], FloppyFormat.ConvertPageCount[ volumeDesc.numPages - cylinderZeroSectors], FirstDataSector[ volumeDesc] ! BadSector => BEGIN FOR i: CARDINAL IN [0..countBadPages) DO <> temp: FloppyFormat.Alternate; IF volumeDesc.badPageMap[i].bad = sector THEN RESUME ; IF volumeDesc.badPageMap[i].bad < sector THEN LOOP; temp _ volumeDesc.badPageMap[i]; volumeDesc.badPageMap[i] _ [ bad: sector, alternate: FloppyFormat.nullSector]; sector _ temp.bad; ENDLOOP; IF countBadPages >= FloppyFormat.maxBadSectors THEN GO TO tooManyBadPages; volumeDesc.badPageMap[countBadPages] _ [ bad: sector, alternate: FloppyFormat.nullSector]; countBadPages _ countBadPages + 1; RESUME END]; REPEAT tooManyBadPages => RETURN WITH ERROR Floppy.Error[badDisk]; ENDLOOP; <> <> IF ~FloppyChannel.SetContext[ volumeDesc.handle, FloppyFormat.trackZeroContext] THEN BEGIN buffer.pointer _ Space.Unmap[buffer.pointer]; RETURN WITH ERROR Floppy.Error[badDisk]; END; IF FloppyChannel.FormatTracks[ volumeDesc.handle, FloppyFormat.trackZeroAddress, 1].countDone ~= 1 THEN BEGIN buffer.pointer _ Space.Unmap[buffer.pointer]; RETURN WITH ERROR Floppy.Error[badDisk]; END; trackZeroSectors _ FloppyChannel.GetDeviceAttributes[ volumeDesc.handle].maxSectorsPerTrack; <> FOR i: CARDINAL IN [0..FormatVerifyPasses) DO SetBlock[buffer.pointer, Environment.wordsPerPage, 0]; ScanFloppy[ volumeDesc, volumeDesc.handle, buffer.pointer, FloppyFormat.trackZeroContext, trackZeroSectors, 1 ! BadSector => GO TO badCylinderZero; ]; REPEAT badCylinderZero => RETURN WITH ERROR Floppy.Error[badDisk]; ENDLOOP; <> IF volumeDesc.sides = two THEN BEGIN SELECT TRUE FROM FloppyChannel.SetContext[ volumeDesc.handle, FloppyFormat.trackOneContext[ trackOneDensity _ volumeDesc.density]] => NULL; FloppyChannel.SetContext[ volumeDesc.handle, FloppyFormat.trackOneContext[ trackOneDensity _ IF trackOneDensity = single THEN double ELSE single]] => NULL; ENDCASE => BEGIN buffer.pointer _ Space.Unmap[buffer.pointer]; RETURN WITH ERROR Floppy.Error[badDisk]; END; IF FloppyChannel.FormatTracks[ volumeDesc.handle, FloppyFormat.trackOneAddress, 1].countDone ~= 1 THEN BEGIN buffer.pointer _ Space.Unmap[buffer.pointer]; RETURN WITH ERROR Floppy.Error[badDisk]; END; <> END; <> format _ SELECT volumeDesc.density FROM double => doubleDensity, single => SELECT volumeDesc.sides FROM one => singleSidedSingleDensity, two => doubleSidedSingleDensity ENDCASE => ERROR, ENDCASE => ERROR; <> FOR address IN [1..trackZeroSectors] DO IF address = 9 THEN LOOP; -- We will fill in sector nine later FloppyFormat.FillTrackZeroSector[trackZeroSector, format, address]; diskAddress _ FloppyFormat.trackZeroAddress; diskAddress.sector _ address; AccessFloppy[volumeDesc, trackZeroSector, diskAddress, 1, write]; ENDLOOP; IF volumeDesc.sides = two THEN BEGIN -- Fill in track one, cylinder zero trackOneSectors: CARDINAL; IF ~FloppyChannel.SetContext[ volumeDesc.handle, FloppyFormat.trackOneContext[trackOneDensity]] THEN BEGIN buffer.pointer _ Space.Unmap[buffer.pointer]; RETURN WITH ERROR Floppy.Error[badDisk]; END; trackOneSectors _ FloppyChannel.GetDeviceAttributes[ volumeDesc.handle].maxSectorsPerTrack; diskAddress _ FloppyFormat.trackOneAddress; FOR address IN [1..trackOneSectors] DO FloppyFormat.FillTrackOneSector[trackOneSector, format, address]; diskAddress.sector _ address; AccessFloppy[volumeDesc, trackOneSector, diskAddress, 1, write]; ENDLOOP; END; <> <> <> <> volumeDesc.sectorNine.countBadSectors _ countBadPages; diskAddress _ FloppyFormat.trackZeroAddress; diskAddress.sector _ FloppyFormat.badSpotSector; AccessFloppy[ -- Write out the bad page list volumeDesc, volumeDesc.badPageMap, diskAddress, FloppyFormat.badSpotSectors, write]; END; --- of block if only formatting <> WriteMarkerPages[ volumeDesc, markerPage, countBadPages, volumeDesc.badPageMap]; <> volumeDesc.sectorNine.fileListSize _ WordsToPages[ SIZE[FloppyFormat .FileList[maxNumberOfFileListEntries]]]; volumeDesc.fileListSpace _ Space.Map[ [File.nullFile, Space.defaultBase, volumeDesc.sectorNine.fileListSize]]; <> volumeDesc.fileList _ volumeDesc.fileListSpace.pointer; volumeDesc.fileList.seal _ FloppyFormat.FileListSeal; volumeDesc.fileList.version _ FloppyFormat.FileListVersion; volumeDesc.fileList.count _ 0; volumeDesc.fileList.maxEntries _ maxNumberOfFileListEntries; FOR i: CARDINAL IN [0..maxNumberOfFileListEntries) DO volumeDesc.fileList.files[i] _ []; ENDLOOP; InitializeAllocationMap[volumeDesc]; <> [volumeDesc.sectorNine.fileList, volumeDesc.sectorNine.fileListID] _ AllocateFile[ volumeDesc, volumeDesc.sectorNine.fileListSize, FloppyImplInterface.FileListType, FloppyFormat.nullSector]; WriteSectorNine[volumeDesc]; volumeDesc.fileList.count _ 1; volumeDesc.fileList.files[0] _ [ file: volumeDesc.sectorNine.fileListID, type: FloppyImplInterface.FileListType, location: volumeDesc.sectorNine.fileList, size: volumeDesc.sectorNine.fileListSize]; WriteFileList[volumeDesc]; CloseVolume[volumeDesc]; -- To clear out the temporary spaces buffer.pointer _ Space.Unmap[buffer.pointer]; EXITS dataError => RETURN WITH ERROR Floppy.Error[badDisk]; END; -- FormatInternal END...