DIRECTORY
Basics USING [HighHalf],
Floppy USING [BootFilePointer, Error, ErrorType, FileID, nullFileID, PageCount, Sides],
FloppyChannel USING [Attributes, Context, DiskAddress, Error, GetDeviceAttributes, GetHandle, GetNextDrive, Handle, Nop, nullDrive, ReadSectors, SectorCount, SetContext, Status, WriteSectors],
FloppyFormat USING [BadSpotSectors, ConvertPageCount, ConvertPageNumber, dataContext, FileList, FileListEntry, ImplementedFileSize, MarkerPage, MarkerSeal, MarkerPageVersion, nullSector, Sector, SectorNine, SectorToDiskAddress, trackOneContext, trackZeroAddress, trackZeroContext],
FloppyImplInterface,
PrincOps USING [PageCount, wordsPerPage],
SpecialFloppy USING [Error, ErrorType],
VM USING [AddressForPageNumber, Allocate, CantAllocate, Free, Interval, nullInterval];
IOError: PUBLIC ERROR[countDone: CARDINAL] = CODE;
DiskChanged: PUBLIC ERROR = CODE;
Bug: PUBLIC ERROR[type: BugType] = CODE;
Error: PUBLIC ERROR[error: SpecialFloppy.ErrorType] = CODE;
FormatVerifyPasses: CARDINAL = 1;
MaxVolumes: CARDINAL = 1;
actualVolumes: CARDINAL ← 0;
volumeTable: PUBLIC LONG POINTER TO ARRAY [0..0) OF VolumeDesc;
VolumeTable:
ARRAY [0..MaxVolumes)
OF VolumeDesc;
-- indexed by device indexes.
Eventually, we should allocate this array dynamically to make it variable length
and to get it out of the MDS.
volumeTableObjects: ARRAY [0..MaxVolumes) OF VolumeDescObject;
FileListType: -- File.Type -- CARDINAL = 0 -- CommonSoftwareFileTypes.tFileList --;
AccessFloppy:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc, buffer:
LONG
POINTER,
address: FloppyChannel.DiskAddress, count:
CARDINAL, access: AccessMode] =
BEGIN
status: FloppyChannel.Status;
countDone: CARDINAL;
context: FloppyChannel.Context;
context ←
IF address.cylinder > 0
THEN FloppyFormat.dataContext[volumeDesc.density]
ELSE
IF address.head = 0
THEN FloppyFormat.trackZeroContext
ELSE
IF FloppyChannel.SetContext[
volumeDesc.handle,
FloppyFormat.trackOneContext[volumeDesc.density]]
THEN FloppyFormat.trackOneContext[volumeDesc.density]
ELSE FloppyFormat.trackOneContext[
IF volumeDesc.density = single THEN double ELSE single];
IF ~ FloppyChannel.SetContext[volumeDesc.handle, context]
THEN
ERROR Floppy.Error[hardwareError]; -- Is this really the right error?
[status, countDone] ←
(
SELECT access
FROM
read => FloppyChannel.ReadSectors,
write => FloppyChannel.WriteSectors
ENDCASE =>
ERROR)[volumeDesc.handle, address,
buffer, count,
TRUE !
FloppyChannel.Error =>
IF type = invalidHandle THEN GO TO diskChanged];
IF status = goodCompletion THEN RETURN;
An error has occurred. Translate the error into something that the clients of Floppy are ready to catch. WARNING: This procedure is used for ALL I/O by this module. Thus some of the signals that it raises are inappropriate in some cases. The higher level modules should catch these signals and translate them!
IF status = diskChange THEN ERROR DiskChanged;
IF status = notReady THEN ERROR Floppy.Error[notReady];
IF status = cylinderError THEN ERROR Floppy.Error[invalidFormat];
IF status = recordNotFound THEN ERROR Floppy.Error[invalidFormat];
IF status = deletedData THEN ERROR Floppy.Error[needsScavenging];
IF status = headerError THEN ERROR IOError[countDone];
IF status = dataError THEN ERROR IOError[countDone];
IF status = dataLost THEN ERROR Floppy.Error[needsScavenging];
IF status = writeFault THEN ERROR Floppy.Error[writeInhibited];
IF status = otherError THEN Floppy.Error[hardwareError];
EXITS
diskChanged => ERROR DiskChanged;
END;
AddFile:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc, file: Floppy.FileID,
size: FloppyFormat.ImplementedFileSize, type:
-- File.Type --
CARDINAL,
location: FloppyFormat.Sector] =
BEGIN
addLocation: CARDINAL ← 0;
IF volumeDesc.fileList.count = volumeDesc.fileList.maxEntries
THEN
RETURN WITH ERROR Floppy.Error[fileListFull];
FOR i:
CARDINAL
IN [0..volumeDesc.fileList.count)
DO
IF (volumeDesc.fileList.files[i].location > location) THEN {addLocation ← i; EXIT};
REPEAT
FINISHED => addLocation ← volumeDesc.fileList.count;
ENDLOOP;
FOR i:
CARDINAL
DECREASING
IN [addLocation..volumeDesc.fileList.count)
DO
volumeDesc.fileList.files[i+1] ← volumeDesc.fileList.files[i];
ENDLOOP;
volumeDesc.fileList.files[addLocation] ←[file: file, size: size, type: type, location: location];
volumeDesc.fileList.count ← volumeDesc.fileList.count+1;
WriteFileList[volumeDesc];
END;
AllocateFile:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc, bigSize: Floppy.PageCount,
type:
-- File.Type --
CARDINAL, startPage: FloppyFormat.Sector, id: Floppy.FileID]
RETURNS [startingSector: FloppyFormat.Sector, file: Floppy.FileID] =
BEGIN
ValidMarkerPage:
PROCEDURE [mp:
LONG
POINTER
TO FloppyFormat.MarkerPage]
RETURNS [
BOOLEAN] = {
RETURN[mp.seal = FloppyFormat.MarkerSeal AND mp.version = FloppyFormat.MarkerPageVersion]};
RejectInvalidMarkerPage:
PROCEDURE =
BEGIN
CloseVolume[volumeDesc];
ERROR Floppy.Error[needsScavenging];
END;
runFound: BOOLEAN ← FALSE;
buffer: VM.Interval;
zerothMarkerPage: LONG POINTER TO FloppyFormat.MarkerPage;
firstMarkerPage: LONG POINTER TO FloppyFormat.MarkerPage;
secondMarkerPage: LONG POINTER TO FloppyFormat.MarkerPage;
thirdMarkerPage: LONG POINTER TO FloppyFormat.MarkerPage;
zerothMarkerPageAddress: FloppyFormat.Sector ← FloppyFormat.nullSector;
firstMarkerPageAddress: FloppyFormat.Sector;
secondMarkerPageAddress: FloppyFormat.Sector;
thirdMarkerPageAddress: FloppyFormat.Sector ← FloppyFormat.nullSector;
size: FloppyFormat.ImplementedFileSize = FloppyFormat.ConvertPageCount[bigSize];
isFileList: BOOLEAN = (type = FloppyImplInterface.FileListType);
IF Basics.HighHalf[bigSize] ~= 0
THEN {VolumeStable[volumeDesc]; ERROR Floppy.Error[insufficientSpace]};
IF size = 0 THEN {VolumeStable[volumeDesc]; ERROR Floppy.Error[zeroSizeFile]};
IF (startPage = FloppyFormat.nullSector)
THEN
-- file goes anywhere it will fit --
BEGIN
startingSector ← FirstDataSector[volumeDesc];
WHILE startingSector < volumeDesc.numPages
DO
IF startingSector+size > volumeDesc.numPages
THEN
{VolumeStable[volumeDesc]; ERROR Floppy.Error[insufficientSpace]};
runFound ← TRUE;
FOR page: FloppyFormat.ImplementedFileSize
IN [startingSector..startingSector+size)
DO
IF volumeDesc.allocationMap[page] ~= free
THEN
{runFound ← FALSE; startingSector ← page+1; EXIT};
ENDLOOP;
IF runFound THEN EXIT;
REPEAT
FINISHED => {VolumeStable[volumeDesc]; ERROR Floppy.Error[insufficientSpace]};
ENDLOOP;
END
ELSE
--file is to go at specified location: if it doesn't fit, we have an error --
BEGIN
IF startPage+size > volumeDesc.numPages
THEN
{VolumeStable[volumeDesc]; ERROR Floppy.Error[insufficientSpace]};
FOR page: FloppyFormat.ImplementedFileSize
IN [startPage..startPage+size)
DO
IF ((volumeDesc.allocationMap[page] = allocated )
OR (volumeDesc.allocationMap[page] = markerPage))
THEN
{VolumeStable[volumeDesc]; ERROR SpecialFloppy.Error[spaceNotAvailable]};
IF (volumeDesc.allocationMap[page] = badPage)
THEN {VolumeStable[volumeDesc]; ERROR Floppy.Error[badSectors]};
ENDLOOP;
startingSector ← startPage;
END;
We now have a run of usable pages: [startingSector..startingSector+size) Since we zero pages at Delete time, we do not zero the file at this time.
[buffer: buffer] ← CreateBuffer[4];
zerothMarkerPage ← VM.AddressForPageNumber[buffer.page];
firstMarkerPage ← zerothMarkerPage+PrincOps.wordsPerPage;
secondMarkerPage ← firstMarkerPage+PrincOps.wordsPerPage;
thirdMarkerPage ← secondMarkerPage+PrincOps.wordsPerPage;
IF id = Floppy.nullFileID
THEN file ← GetFileID[volumeDesc]
ELSE BEGIN
FOR i:
CARDINAL
IN [0..volumeDesc.fileList.count)
DO
IF id = volumeDesc.fileList.files[i].file THEN ERROR SpecialFloppy.Error[IDAlreadyInUse];
ENDLOOP;
file ← id;
END;
firstMarkerPageAddress ← startingSector-1; -- this may or may not already be a marker page
IF (volumeDesc.allocationMap[firstMarkerPageAddress] ~= markerPage)
THEN
BEGIN
-- it better be free
IF (volumeDesc.allocationMap[firstMarkerPageAddress] ~=free)
THEN
ERROR Floppy.Error[needsScavenging];
find the preceding marker page
FOR page: FloppyFormat.ImplementedFileSize
DECREASING
IN [0..firstMarkerPageAddress)
DO
IF volumeDesc.allocationMap[page] = markerPage
THEN
{zerothMarkerPageAddress ← page; EXIT};
REPEAT
FINISHED => ERROR Floppy.Error[needsScavenging];
ENDLOOP;
ReadFloppy[volumeDesc, zerothMarkerPage, zerothMarkerPageAddress, 1];
IF ~ValidMarkerPage[zerothMarkerPage] THEN RejectInvalidMarkerPage;
END
ELSE BEGIN
--there is a marker page at firstMarkerPageAddress
ReadFloppy[volumeDesc, firstMarkerPage, firstMarkerPageAddress, 1 !];
IF ~ValidMarkerPage[firstMarkerPage]
THEN
RejectInvalidMarkerPage;
END;
now check the trailing marker page
secondMarkerPageAddress ← startingSector+size;
IF (volumeDesc.allocationMap[secondMarkerPageAddress] ~= markerPage)
THEN
BEGIN
-- it better be free
IF (volumeDesc.allocationMap[secondMarkerPageAddress] ~= free)
THEN
ERROR Floppy.Error[needsScavenging];
go find the next one
FOR page: FloppyFormat.ImplementedFileSize
IN
(secondMarkerPageAddress..FloppyFormat.ConvertPageCount[volumeDesc.numPages]] DO
IF volumeDesc.allocationMap[page] = markerPage
THEN
{thirdMarkerPageAddress ← page; EXIT};
REPEAT
FINISHED => ERROR Floppy.Error[needsScavenging];
ENDLOOP;
ReadFloppy[volumeDesc, thirdMarkerPage, thirdMarkerPageAddress, 1];
IF ~ValidMarkerPage[thirdMarkerPage]
THEN
RejectInvalidMarkerPage;
END
ELSE
BEGIN
--one is there, go check it
ReadFloppy[volumeDesc, secondMarkerPage, secondMarkerPageAddress, 1];
IF ~ValidMarkerPage[secondMarkerPage] THEN RejectInvalidMarkerPage;
END;
now check existing links among marker pages
IF zerothMarkerPageAddress ~= FloppyFormat.nullSector
THEN BEGIN
--check zerothMarkerPage
IF thirdMarkerPageAddress ~= FloppyFormat.nullSector
THEN BEGIN
IF (zerothMarkerPageAddress ~= thirdMarkerPageAddress-thirdMarkerPage.previous.length-1) OR (zerothMarkerPageAddress+zerothMarkerPage.next.length+1 ~= thirdMarkerPageAddress) THEN RejectInvalidMarkerPage;
END
ELSE BEGIN
IF(zerothMarkerPageAddress ~= secondMarkerPageAddress-secondMarkerPage.previous.length-1) OR (zerothMarkerPageAddress+zerothMarkerPage.next.length+1 ~= secondMarkerPageAddress) THEN RejectInvalidMarkerPage;
END;
END
ELSE
BEGIN
--check firstMarkerPage
IF thirdMarkerPageAddress ~= FloppyFormat.nullSector
THEN BEGIN
IF (firstMarkerPageAddress ~= thirdMarkerPageAddress-thirdMarkerPage.previous.length-1) OR (firstMarkerPageAddress+firstMarkerPage.next.length+1 ~= thirdMarkerPageAddress) THEN RejectInvalidMarkerPage;
END
ELSE BEGIN
IF(firstMarkerPageAddress ~= secondMarkerPageAddress-secondMarkerPage.previous.length-1) OR (firstMarkerPageAddress+firstMarkerPage.next.length+1 ~= secondMarkerPageAddress) THEN RejectInvalidMarkerPage;
END;
END;
now set links and write out to disk
IF (zerothMarkerPageAddress ~= FloppyFormat.nullSector)
THEN
BEGIN
--we need to write a new marker page for the file
zerothMarkerPage.next ← [length: firstMarkerPageAddress-zerothMarkerPageAddress-1, body: free[]];
firstMarkerPage^ ← [seal: FloppyFormat.MarkerSeal, version: FloppyFormat.MarkerPageVersion , next: ,
previous: [length: firstMarkerPageAddress-zerothMarkerPageAddress-1, body: free[]]];
volumeDesc.allocationMap[firstMarkerPageAddress] ← markerPage;
WriteFloppy[volumeDesc, zerothMarkerPage, zerothMarkerPageAddress, 1];
END;
IF isFileList
THEN
BEGIN
-- link first and second marker pages to each other
firstMarkerPage.next ← [length: size, body: fileList [file: file, type: type]];
secondMarkerPage.previous ← [length: size, body: fileList [file: file, type: type]];
END
ELSE BEGIN
firstMarkerPage.next ← [length: size, body: file [file: file, type: type]];
secondMarkerPage.previous ← [length: size, body: file [file: file, type: type]];
END;
IF (thirdMarkerPageAddress ~= FloppyFormat.nullSector)
THEN BEGIN
-- handle thirdMarkerPage
thirdMarkerPage.previous ← [length: thirdMarkerPageAddress-secondMarkerPageAddress-1, body: free[]];
secondMarkerPage.seal ← FloppyFormat.MarkerSeal;
secondMarkerPage.version ← FloppyFormat.MarkerPageVersion;
secondMarkerPage.next ← [length: thirdMarkerPageAddress-secondMarkerPageAddress-1, body: free[]];
volumeDesc.allocationMap[secondMarkerPageAddress] ← markerPage;
WriteFloppy[volumeDesc, thirdMarkerPage, thirdMarkerPageAddress, 1];
END;
WriteFloppy[volumeDesc,firstMarkerPage, firstMarkerPageAddress, 1];
WriteFloppy[volumeDesc, secondMarkerPage, secondMarkerPageAddress,];
Now update our local allocation map
FOR i: FloppyFormat.Sector
IN [startingSector..startingSector+size)
DO
volumeDesc.allocationMap[i] ← allocated
ENDLOOP;
RETURN;
END;
CloseVolume:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc] =
BEGIN
volumeDesc.open ← FALSE;
volumeDesc.changeCount ← volumeDesc.changeCount + 1;
VM.Free[volumeDesc.trackZeroSpace];
END;
CreateBuffer:
PUBLIC
PROCEDURE [size: PrincOps.PageCount]
RETURNS [buffer:
VM.Interval] =
BEGIN
buffer ← VM.nullInterval;
IF size = 0 THEN RETURN;
buffer ← VM.Allocate[size ! VM.CantAllocate => IF (size ← size-1) > 0 THEN RETRY];
END;
FindFile:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc, file: Floppy.FileID]
RETURNS [address: FloppyFormat.Sector, type:
-- File.Type --
CARDINAL,
size: FloppyFormat.ImplementedFileSize] =
BEGIN
FOR i:
CARDINAL
IN [0..volumeDesc.fileList.count)
DO
IF volumeDesc.fileList.files[i].file = file THEN RETURN [volumeDesc.fileList.files[i].location, volumeDesc.fileList.files[i].type, volumeDesc.fileList.files[i].size];
ENDLOOP;
RETURN [FloppyFormat.nullSector, -- FileTypes.tUntypedFile -- 0, 0];
END;
FirstDataSector:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc]
RETURNS [FloppyFormat.Sector] =
BEGIN
RETURN[volumeDesc.sectorNine.sectorsPerTrack*volumeDesc.sectorNine.tracksPerCylinder+1];
END;
GetBootFileAddress:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc,
bootFile: Floppy.BootFilePointer]
RETURNS [address: FloppyFormat.Sector] =
BEGIN
size, offset: FloppyFormat.ImplementedFileSize;
IF bootFile.file = Floppy.nullFileID
THEN RETURN[FloppyFormat.nullSector]
ELSE BEGIN
[address,,size] ← FindFile[volumeDesc, bootFile.file];
offset ← FloppyFormat.ConvertPageNumber[bootFile.page];
IF address = FloppyFormat.nullSector THEN ERROR Floppy.Error[fileNotFound]
ELSE IF offset > size-1 THEN ERROR Floppy.Error[invalidPageNumber]
ELSE RETURN[address + offset];
END;
END;
GetFileID:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc]
RETURNS [fileID: Floppy.FileID] =
BEGIN
fileID ← LOOPHOLE[volumeDesc.sectorNine.nextUnusedFileID];
volumeDesc.sectorNine.nextUnusedFileID ← volumeDesc.sectorNine.nextUnusedFileID+1;
IF volumeDesc.sectorNine.nextUnusedFileID = 0
THEN ERROR Bug[wrappedAroundFileIDs];
File ID's have wrapped around - a case not covered by this implementation
END;
InitializeAllocationMap:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc] =
BEGIN
size: CARDINAL ← 1+WordsToPages[(volumeDesc.numPages+allocationsPerPage-1)/allocationsPerPage];
this calculation is flakey so allocate an extra page for safety
volumeDesc.allocationMapSpace ←
VM.Allocate[size];
We should really catch the signals and convert them into something else
volumeDesc.allocationMap ← VM.AddressForPageNumber[volumeDesc.allocationMapSpace.page];
IF volumeDesc.numPages > LAST[CARDINAL] THEN ERROR Bug[floppyVolumeTooLarge];
FOR i:
CARDINAL
IN [0..FloppyFormat.ConvertPageCount[volumeDesc.numPages]]
DO
volumeDesc.allocationMap[i] ← free;
ENDLOOP;
The first and last pages of the disk have marker pages unless they are bad. If so, the loop over the bad page list will mark them as such
volumeDesc.allocationMap[FirstDataSector[volumeDesc]] ← markerPage;
volumeDesc.allocationMap[FloppyFormat.ConvertPageCount[volumeDesc.numPages]] ←
markerPage;
FOR i:
CARDINAL
IN [0..volumeDesc.sectorNine.countBadSectors)
DO
volumeDesc.allocationMap[volumeDesc.badPageMap[i].bad] ← badPage;
IF volumeDesc.allocationMap[volumeDesc.badPageMap[i].bad-1] = free THEN volumeDesc.allocationMap[volumeDesc.badPageMap[i].bad-1] ← markerPage;
IF volumeDesc.allocationMap[volumeDesc.badPageMap[i].bad+1] = free THEN volumeDesc.allocationMap[volumeDesc.badPageMap[i].bad+1] ← markerPage;
ENDLOOP;
FOR i:
CARDINAL
IN [0..FirstDataSector[volumeDesc])
DO
volumeDesc.allocationMap[i] ← allocated; -- Sector 0 and cylinder zero is always in use
ENDLOOP;
FOR i:
CARDINAL
IN [0..volumeDesc.fileList.count)
DO
file: FloppyFormat.FileListEntry = volumeDesc.fileList.files[i];
IF ~(volumeDesc.allocationMap[file.location-1] = free OR volumeDesc.allocationMap[file.location-1] = markerPage) THEN GO TO needsScavenging;
volumeDesc.allocationMap[file.location-1] ← markerPage;
IF ~ (volumeDesc.allocationMap[file.location+file.size] = free OR volumeDesc.allocationMap[file.location+file.size] = markerPage) THEN GO TO needsScavenging;
volumeDesc.allocationMap[file.location+file.size] ← markerPage;
FOR j:
CARDINAL
IN [file.location..file.location+file.size)
DO
IF volumeDesc.allocationMap[j] ~= free THEN GOTO needsScavenging;
volumeDesc.allocationMap[j] ← allocated;
ENDLOOP;
ENDLOOP;
EXITS
needsScavenging =>
BEGIN
CloseVolume[volumeDesc];
volumeDesc.allocationMap ← NIL;
ERROR Floppy.Error[needsScavenging];
END;
END;
IsDriveWriteProtected:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc]
RETURNS [writeProtected:
BOOLEAN] =
BEGIN
status: FloppyChannel.Status;
IF ~ FloppyChannel.SetContext[volumeDesc.handle, FloppyFormat.trackZeroContext]
THEN ERROR;
status ← FloppyChannel.Nop[volumeDesc.handle];
RETURN[status = writeFault];
END;
ReadFloppy:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc, buffer:
LONG
POINTER,
address: FloppyFormat.Sector, count: FloppyFormat.ImplementedFileSize] =
BEGIN
AccessFloppy[
volumeDesc, buffer,
FloppyFormat.SectorToDiskAddress[
address,
volumeDesc.sectorNine.cylinders,
volumeDesc.sectorNine.tracksPerCylinder,
volumeDesc.sectorNine.sectorsPerTrack],
count, read];
END;
RemoveFile:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc, file: Floppy.FileID] =
BEGIN
FOR i:
CARDINAL
IN [0..volumeDesc.fileList.count)
DO
IF volumeDesc.fileList.files[i].file = file
THEN BEGIN
IF i = volumeDesc.fileList.count-1
THEN BEGIN
-- Deleting the last file in the list
volumeDesc.fileList.count ← volumeDesc.fileList.count-1;
volumeDesc.fileList.files[i] ← [];
END
ELSE BEGIN
-- There are files later in the list whose entries must be copied
FOR j:
CARDINAL
IN [i..volumeDesc.fileList.count-1)
DO
volumeDesc.fileList.files[j] ← volumeDesc.fileList.files[j+1];
ENDLOOP;
volumeDesc.fileList.count ← volumeDesc.fileList.count-1;
volumeDesc.fileList.files[volumeDesc.fileList.count] ← [];
END;
WriteFileList[volumeDesc];
END;
ENDLOOP;
END;
SectorsPerTrack:
PUBLIC
PROCEDURE [handle: FloppyChannel.Handle,
context: FloppyChannel.Context]
RETURNS [sectorsPerTrack: FloppyChannel.SectorCount] =
BEGIN
attributes: FloppyChannel.Attributes;
[] ← FloppyChannel.SetContext[handle, context];
attributes ← FloppyChannel.GetDeviceAttributes[handle];
RETURN[attributes.maxSectorsPerTrack];
SetBlock:
PUBLIC
PROCEDURE [p:
LONG
POINTER, length:
CARDINAL, value:
CARDINAL] =
{FOR i: CARDINAL IN [0..length) DO (p+i)^ ← value ENDLOOP};
ValidDrive:
PUBLIC
PROCEDURE [drive:
CARDINAL]
RETURNS [exists:
BOOLEAN] =
{RETURN[drive IN [0..actualVolumes]]};
VolumeChanging:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc] =
BEGIN
volumeDesc.sectorNine.changing ← TRUE;
WriteSectorNine[volumeDesc];
END;
VolumeStable:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc] =
BEGIN
volumeDesc.sectorNine.changing ← FALSE;
WriteSectorNine[volumeDesc];
END;
WriteFileList:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc] =
BEGIN
WriteFloppy[
volumeDesc, volumeDesc.fileList, volumeDesc.sectorNine.fileList,
volumeDesc.sectorNine.fileListSize];
END;
WriteFloppy:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc, buffer:
LONG
POINTER,
address: FloppyFormat.Sector, count: FloppyFormat.ImplementedFileSize] =
BEGIN
AccessFloppy[
volumeDesc, buffer,
FloppyFormat.SectorToDiskAddress[
address,
volumeDesc.sectorNine.cylinders,
volumeDesc.sectorNine.tracksPerCylinder,
volumeDesc.sectorNine.sectorsPerTrack],
count, write];
END;
WriteSectorNine:
PUBLIC
PROCEDURE [volumeDesc: VolumeDesc] =
BEGIN
AccessFloppy[
volumeDesc: volumeDesc, access: write,
buffer: volumeDesc.sectorNine, count: 1,
address: [head: FloppyFormat.trackZeroAddress.head,
cylinder: FloppyFormat.trackZeroAddress.cylinder,
sector: 9]];
END;
Module initialization
Find all of the Floppy drives out there and initialize the volume table
FOR i:
CARDINAL ← FloppyChannel.GetNextDrive[FloppyChannel.nullDrive], FloppyChannel.GetNextDrive[i]
UNTIL i = FloppyChannel.nullDrive
DO
actualVolumes ← i;
VolumeTable[actualVolumes] ← @volumeTableObjects[actualVolumes];
volumeTableObjects[actualVolumes].open ← FALSE;
volumeTableObjects[actualVolumes].handle ← FloppyChannel.GetHandle[actualVolumes];
volumeTableObjects[actualVolumes].changeCount ← 0;
volumeTableObjects[actualVolumes].trackZeroSpace ←
CreateBuffer[WordsToPages[SIZE[FloppyFormat.SectorNine]]+ WordsToPages[SIZE[FloppyFormat.BadSpotSectors]]];
volumeTableObjects[actualVolumes].sectorNine ← VM.AddressForPageNumber[volumeTableObjects[actualVolumes].trackZeroSpace.page];
volumeTableObjects[actualVolumes].badPageMap ← LOOPHOLE[volumeTableObjects[actualVolumes].sectorNine+ PrincOps.wordsPerPage*WordsToPages[SIZE[FloppyFormat.SectorNine]]];
volumeTableObjects[actualVolumes].fileList ← NIL;
volumeTableObjects[actualVolumes].allocationMap ← NIL;
ENDLOOP;
volumeTable ←
LOOPHOLE[
LONG[@VolumeTable]];
END....