FloppyImplPrivate.mesa
Copyright Ó 1983, 1984, 1985, 1987 by Xerox Corporation. All rights reserved.
Tim Diebert: May 7, 1987 2:11:55 pm PDT
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];
FloppyImplPrivate: PROGRAM
IMPORTS Basics, Floppy, FloppyChannel, FloppyFormat, FloppyImplInterface, SpecialFloppy, VM
EXPORTS FloppyImplInterface, SpecialFloppy = BEGIN OPEN FloppyImplInterface;
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: BOOLEANFALSE;
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];
END;
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....