DIRECTORY
Basics USING [HighHalf],
FS USING [OpenFile, GetInfo, Read, Write],
Floppy USING [BootFilePointer, defaultPageCount, Density, ErrorType, FileHandle, FileID, nullBootFilePointer, nullFileID, nullIndex, PageCount, PageNumber, Sides, VolumeHandle],
FloppyChannel USING [Error, GetDeviceAttributes, GetHandle, Nop, SetContext, Status],
FloppyFormat USING [badSpotSector, BadSpotSectors, ConvertPageCount, ConvertPageNumber, dataContext, DiskAddressToSector, FileList, FileListEntry, FileListSeal, FileListVersion, FloppySeal, FloppyVersion, ImplementedFileSize, InitialMicrocodeDiskAddress, MarkerPage, MarkerSeal, maxBadSectors, minTrackZeroSectors, nullSector, Sector, SectorNine, TrackZero, trackZeroAddress, TrackZeroSector],
FloppyImplInterface,
PrincOps USING [PageCount, wordsPerPage],
Rope USING [FromProc, ROPE],
SpecialFloppy USING [Error],
VM USING [AddressForPageNumber, Free, Interval, nullInterval];
PUBLIC ERRORs, SIGNALs and TYPEs
ROPE: TYPE ~ Rope.ROPE;
AlreadyFormatted: PUBLIC SAFE SIGNAL [labelString: ROPE] = CODE;
DataError: PUBLIC SAFE ERROR [file: Floppy.FileHandle, page: Floppy.PageNumber,
vm: LONG POINTER] = CODE;
Error: PUBLIC SAFE ERROR [error: Floppy.ErrorType] = CODE;
VolumeHandle: PUBLIC TYPE = RECORD [deviceIndex: CARDINAL, changeCount: CARDINAL];
nullVolumeHandle: PUBLIC VolumeHandle ← [LAST[CARDINAL], LAST[CARDINAL]];
these are magic numbers that work for the floppy stuff
MaxBufferSize: PrincOps.PageCount ← 32;
FormatVerifyPasses: CARDINAL ← 2;
PUBLIC operations
Close:
PUBLIC
SAFE
PROCEDURE [volume: VolumeHandle] =
TRUSTED
BEGIN
CloseVolumeInternal:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc] =
TRUSTED BEGIN ENABLE UNWIND => NULL;
CloseVolume[volumeDesc];
END;
[]←ValidateHandle[volume];
CloseVolumeInternal[volumeTable[volume.deviceIndex]];
END;
Compact:
PUBLIC
SAFE
PROCEDURE [volume: VolumeHandle] =
TRUSTED
BEGIN
[]←ValidateHandle[volume];
END;
CopyFromFSFile:
PUBLIC
SAFE
PROCEDURE [pilotFile:
FS.OpenFile, floppyFile: FileHandle,
firstFSPage:
INT, firstFloppyPage: PageNumber, count: PageCount ← defaultPageCount] =
TRUSTED
BEGIN
buffer: VM.Interval ← VM.nullInterval;
CopyPilotFile:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc, pilotFile:
FS.OpenFile, floppyFile: Floppy.FileHandle, firstPilotPage:
INT,
firstFloppyPage: Floppy.PageNumber, count: Floppy.PageCount] =
TRUSTED
BEGIN
ENABLE {DiskChanged => {CloseVolume[volumeDesc]; GO TO diskChanged};
UNWIND => VM.Free[buffer]};
floppyFileAddress: FloppyFormat.Sector;
floppyFileSize: FloppyFormat.ImplementedFileSize;
pilotFileSize: LONG CARDINAL;
filePageNumber: FloppyFormat.ImplementedFileSize;
pageCount: FloppyFormat.ImplementedFileSize;
done: LONG CARDINAL;
First validate our arguments
IF NOT volumeDesc.open THEN RETURN WITH ERROR Error[volumeNotOpen];
pilotFileSize ← FS.GetInfo[pilotFile].pages;
IF volumeDesc.writeProtected THEN RETURN WITH ERROR Error[writeInhibited];
[address: floppyFileAddress, size: floppyFileSize] ← FindFile[volumeDesc, floppyFile.file];
IF floppyFileAddress = FloppyFormat.nullSector
THEN RETURN WITH ERROR Error[fileNotFound];
IF (firstFloppyPage >= floppyFileSize)
OR (
CARD[firstPilotPage] >= pilotFileSize)
THEN RETURN WITH ERROR Error[invalidPageNumber];
IF count = Floppy.defaultPageCount
THEN BEGIN
count ← pilotFileSize - firstPilotPage;
IF count > floppyFileSize - firstFloppyPage
THEN RETURN WITH ERROR Error [incompatibleSizes];
END
ELSE
IF (count > pilotFileSize -
CARD[firstPilotPage])
OR (count > floppyFileSize - firstFloppyPage)
THEN RETURN WITH ERROR Error[incompatibleSizes];
floppyFileAddress ← floppyFileAddress + FloppyFormat.ConvertPageNumber[firstFloppyPage];
IF Basics.HighHalf[count] # 0 THEN RETURN WITH ERROR Error[insufficientSpace];
pageCount ← FloppyFormat.ConvertPageCount[count];
Now go off and do our I/O
buffer ← CreateBuffer[MIN[count, MaxBufferSize]];
IF buffer.count = 0 THEN ERROR; -- insufficient VM to operate
FOR filePageNumber ← 0, filePageNumber +
CARDINAL[buffer.count]
WHILE filePageNumber < pageCount
DO
FS.Read[pilotFile, firstPilotPage+filePageNumber, buffer.count, VM.AddressForPageNumber[buffer.page]];
WriteFloppy[volumeDesc,
VM.AddressForPageNumber[buffer.page], floppyFileAddress+filePageNumber,
MIN[
CARDINAL[buffer.count], FloppyFormat.ConvertPageNumber[count-filePageNumber]]
! IOError => {done ← filePageNumber+countDone;
GO
TO dataError};
UNWIND => VM.Free[buffer]];
REPEAT
dataError => RETURN WITH ERROR DataError[floppyFile, done, NIL];
FINISHED => VM.Free[buffer];
ENDLOOP;
EXITS diskChanged => RETURN WITH ERROR Error[volumeNotOpen];
END;
volume: VolumeHandle = floppyFile.volume;
[] ← ValidateHandle[volume];
CopyPilotFile[volumeTable[volume.deviceIndex], pilotFile, floppyFile, firstFSPage, firstFloppyPage, count];
END;
CopyToFSFile:
PUBLIC
SAFE
PROCEDURE [floppyFile: FileHandle, pilotFile:
FS.OpenFile,
firstFloppyPage: PageNumber, firstFSPage:
INT, count: PageCount ← defaultPageCount] =
TRUSTED
BEGIN
buffer: VM.Interval ← VM.nullInterval;
CopyFloppyFile:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc,
floppyFile: Floppy.FileHandle, pilotFile:
FS.OpenFile,
firstFloppyPage: Floppy.PageNumber, firstPilotPage:
INT, count: Floppy.PageCount] =
TRUSTED
BEGIN
ENABLE BEGIN
DiskChanged => {CloseVolume[volumeDesc]; GO TO diskChanged};
UNWIND => VM.Free[buffer];
END;
floppyFileAddress: FloppyFormat.Sector;
floppyFileSize: FloppyFormat.ImplementedFileSize;
pilotFileSize: CARD;
filePageNumber: FloppyFormat.ImplementedFileSize;
pageCount: FloppyFormat.ImplementedFileSize;
done: LONG CARDINAL;
First validate our arguments
IF ~ volumeDesc.open THEN RETURN WITH ERROR Error[volumeNotOpen];
pilotFileSize ← FS.GetInfo[pilotFile].pages;
[address: floppyFileAddress, size: floppyFileSize] ← FindFile[volumeDesc, floppyFile.file];
IF floppyFileAddress = FloppyFormat.nullSector
THEN
RETURN WITH ERROR Error[fileNotFound];
IF (firstFloppyPage >= floppyFileSize)
OR (
CARD[firstPilotPage] >= pilotFileSize)
THEN RETURN WITH ERROR Error[invalidPageNumber];
IF count = Floppy.defaultPageCount
THEN BEGIN
count ← floppyFileSize - firstFloppyPage;
IF count > pilotFileSize - firstPilotPage
THEN RETURN WITH ERROR Error[incompatibleSizes];
END
ELSE
IF (count > pilotFileSize - firstPilotPage)
OR (count > floppyFileSize - firstFloppyPage)
THEN RETURN WITH ERROR Error[incompatibleSizes];
floppyFileAddress ← floppyFileAddress + FloppyFormat.ConvertPageNumber[firstFloppyPage];
pageCount ← FloppyFormat.ConvertPageCount[count];
Now go off and do our I/O
buffer ← CreateBuffer[MIN[pageCount, MaxBufferSize]];
FOR filePageNumber ← 0, filePageNumber+
CARDINAL[buffer.count]
WHILE filePageNumber < pageCount
DO
pages: CARDINAL = MIN[CARDINAL[buffer.count], pageCount-filePageNumber];
ReadFloppy[
volumeDesc,
VM.AddressForPageNumber[buffer.page], floppyFileAddress+filePageNumber, pages !
IOError => {done ← filePageNumber+countDone; GO TO dataError};
UNWIND => VM.Free[buffer]];
FS.Write[file: pilotFile, to: firstPilotPage+filePageNumber, nPages: pages,
from: VM.AddressForPageNumber[buffer.page]];
REPEAT
dataError => RETURN WITH ERROR DataError[floppyFile, done, NIL];
FINISHED => VM.Free[buffer];
ENDLOOP;
EXITS diskChanged => RETURN WITH ERROR Error[volumeNotOpen];
END; -- CopyFloppyFile
volume: VolumeHandle = floppyFile.volume;
[] ← ValidateHandle[volume];
CopyFloppyFile[volumeTable[volume.deviceIndex], floppyFile, pilotFile, firstFloppyPage, firstFSPage, count];
END;
CreateFile:
PUBLIC
SAFE
PROCEDURE [volume: VolumeHandle, size: Floppy.PageCount,
type:
-- File.Type --
CARDINAL]
RETURNS [file: Floppy.FileHandle] =
TRUSTED
BEGIN
volumeDesc: VolumeDesc;
CreateFileEntryInternal:
ENTRY
PROCEDURE[volumeDesc: VolumeDesc,
size: Floppy.PageCount, type:
-- File.Type --
CARDINAL,
startSector: FloppyFormat.Sector]
RETURNS[file: Floppy.FileID]=
TRUSTED
BEGIN
ENABLE UNWIND => NULL;
IF ~volumeDesc.open THEN RETURN WITH ERROR Error[volumeNotOpen];
IF volumeDesc.writeProtected THEN RETURN WITH ERROR Error[writeInhibited];
file ← CreateFileInternal[volumeDesc, size, type, startSector, Floppy.nullFileID].fileID;
END;
file.volume ← volume;
volumeDesc ← ValidateHandle[volume];
file.file ← CreateFileEntryInternal[volumeDesc, size, type, FloppyFormat.nullSector];
END;
CreateInitialMicrocodeFile:
PUBLIC
SAFE
PROCEDURE [volume: VolumeHandle, size: Floppy.PageCount, type:
-- File.Type --
CARDINAL,
startingPageNumber: Floppy.PageNumber]
RETURNS [file: Floppy.FileHandle] =
TRUSTED
BEGIN
volumeDesc: VolumeDesc;
file.volume ← volume;
volumeDesc ← ValidateHandle[volume];
file.file ← CreateInitialMicrocodeInternal[volumeDesc, size, type, startingPageNumber, Floppy.nullFileID];
END;
CreateInitialMicrocodeInternal:
PUBLIC
ENTRY
SAFE
PROCEDURE[volumeDesc: VolumeDesc,
size: Floppy.PageCount, type:
-- File.Type --
CARDINAL,
startingPageNumber: Floppy.PageNumber, id: Floppy.FileID]
RETURNS[file: Floppy.FileID]=
TRUSTED
BEGIN
ENABLE UNWIND => NULL;
alignmentSector, startSector: FloppyFormat.Sector; --physical pages for file
IF ~volumeDesc.open THEN RETURN WITH ERROR Error[volumeNotOpen];
IF volumeDesc.writeProtected THEN RETURN WITH ERROR Error[writeInhibited];
IF startingPageNumber >= size THEN RETURN WITH ERROR Error[invalidPageNumber];
alignmentSector ← FloppyFormat.DiskAddressToSector[
FloppyFormat.InitialMicrocodeDiskAddress,
volumeDesc.sectorNine.cylinders,
volumeDesc.sectorNine.tracksPerCylinder,
volumeDesc.sectorNine.sectorsPerTrack];
line up file so specified page resides at hard-wired location on disk
startSector ← alignmentSector - FloppyFormat.ConvertPageNumber[startingPageNumber];
file ← CreateFileInternal[volumeDesc, size, type, startSector, id ! SpecialFloppy.Error => GOTO noRoom].fileID;
EXITS noRoom => RETURN WITH ERROR Error[initialMicrocodeSpaceNotAvailable];
END;
CreateFileInternal:
PUBLIC
SAFE
PROCEDURE [volumeDesc: VolumeDesc, size: Floppy.PageCount, type:
-- File.Type --
CARDINAL, startSector: FloppyFormat.Sector, id: Floppy.FileID]
RETURNS[fileID: Floppy.FileID, startPage: FloppyFormat.Sector] =
TRUSTED
BEGIN
ENABLE BEGIN
DiskChanged => {CloseVolume[volumeDesc]; GO TO diskChanged};
IOError => {CloseVolume[volumeDesc]; GO TO needsScavenging;};
END;
Check overflow here since we can't easily undo the AllocateFile
IF volumeDesc.fileList.count = volumeDesc.fileList.maxEntries THEN ERROR Error[fileListFull];
VolumeChanging[volumeDesc];
[startPage, fileID] ← AllocateFile[volumeDesc, size, type, startSector, id];
AddFile[volumeDesc, fileID, FloppyFormat.ConvertPageCount[size], type, startPage];
VolumeStable[volumeDesc]; -- The volume is now stable
EXITS
needsScavenging => ERROR Error[needsScavenging];
diskChanged => ERROR Error[volumeNotOpen];
END;
DeleteFile:
PUBLIC
SAFE
PROCEDURE [file: Floppy.FileHandle] =
TRUSTED
BEGIN
buffer: VM.Interval ← VM.nullInterval;
DeleteFileInternal:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc, fileID: Floppy.FileID] =
TRUSTED
BEGIN
ENABLE BEGIN
DiskChanged => {CloseVolume[volumeDesc]; GO TO diskChanged};
IOError => {CloseVolume[volumeDesc]; GO TO needsScavenging};
UNWIND => VM.Free[buffer];
END;
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;
address: FloppyFormat.Sector;
firstDeletion, lastDeletion: FloppyFormat.Sector;
size: FloppyFormat.ImplementedFileSize;
IF ~volumeDesc.open THEN RETURN WITH ERROR Error[volumeNotOpen];
IF volumeDesc.writeProtected THEN RETURN WITH ERROR Error[writeInhibited];
[address: address, size: size] ← FindFile[volumeDesc, fileID];
IF address = FloppyFormat.nullSector
THEN
RETURN WITH ERROR Error[fileNotFound];
IF fileID = volumeDesc.sectorNine.fileListID
THEN
RETURN;
It is a no-op to delete the file list
buffer ← CreateBuffer[4];
IF buffer.count < 4 THEN ERROR; -- Insufficient VM to operate
zerothMarkerPage ← VM.AddressForPageNumber[buffer.page];
firstMarkerPage ← zerothMarkerPage+PrincOps.wordsPerPage;
secondMarkerPage ← firstMarkerPage+PrincOps.wordsPerPage;
thirdMarkerPage ← secondMarkerPage+PrincOps.wordsPerPage;
firstMarkerPageAddress ← address-1;
secondMarkerPageAddress ← address+size;
ReadFloppy[volumeDesc, firstMarkerPage, firstMarkerPageAddress, 1];
IF secondMarkerPageAddress # firstMarkerPageAddress+firstMarkerPage.next.length+1
THEN BEGIN
volumeDesc.open ← FALSE;
VM.Free[buffer];
RETURN WITH ERROR Error[needsScavenging];
END;
ReadFloppy[volumeDesc, secondMarkerPage, secondMarkerPageAddress, 1];
IF secondMarkerPageAddress-secondMarkerPage.previous.length-1 # firstMarkerPageAddress
THEN BEGIN
CloseVolume[volumeDesc];
VM.Free[buffer];
RETURN WITH ERROR Error[needsScavenging];
END;
VolumeChanging[volumeDesc];
IF firstMarkerPage.previous.type = free
THEN BEGIN
zerothMarkerPageAddress ← firstMarkerPageAddress - FloppyFormat.ConvertPageCount[firstMarkerPage.previous.length] -1;
firstDeletion ← firstMarkerPageAddress;
ReadFloppy[volumeDesc, zerothMarkerPage, zerothMarkerPageAddress, 1];
IF zerothMarkerPageAddress+zerothMarkerPage.next.length+1 # firstMarkerPageAddress
THEN BEGIN
CloseVolume[volumeDesc];
VM.Free[buffer];
RETURN WITH ERROR Error[needsScavenging];
END;
END
ELSE firstDeletion ← address;
IF (secondMarkerPage.next.type = free)
AND (secondMarkerPageAddress~=volumeDesc.numPages)
THEN BEGIN
thirdMarkerPageAddress ← secondMarkerPageAddress + FloppyFormat.ConvertPageCount[secondMarkerPage.next.length]+1;
lastDeletion ← secondMarkerPageAddress;
ReadFloppy[volumeDesc, thirdMarkerPage, thirdMarkerPageAddress, 1];
IF thirdMarkerPageAddress-thirdMarkerPage.previous.length-1 # secondMarkerPageAddress
THEN BEGIN
CloseVolume[volumeDesc];
VM.Free[buffer];
RETURN WITH ERROR Error[needsScavenging];
END;
END
ELSE lastDeletion ← secondMarkerPageAddress -1;
set marker page fields
IF zerothMarkerPageAddress = FloppyFormat.nullSector
THEN BEGIN
firstMarkerPage.next.body ← free[];
IF thirdMarkerPageAddress = FloppyFormat.nullSector
THEN secondMarkerPage.previous.body ← free[]
ELSE BEGIN
firstMarkerPage.next.length ←
size + 1 + secondMarkerPage.next.length;
thirdMarkerPage.previous.length ← firstMarkerPage.next.length;
END;
END
ELSE BEGIN
IF thirdMarkerPageAddress = FloppyFormat.nullSector
THEN BEGIN
secondMarkerPage.previous.body ← free[];
zerothMarkerPage.next.length ← size + 1 + zerothMarkerPage.next.length;
secondMarkerPage.previous.length ← zerothMarkerPage.next.length;
END
ELSE BEGIN
zerothMarkerPage.next.length ← zerothMarkerPage.next.length + 2 + size + secondMarkerPage.next.length;
thirdMarkerPage.previous.length ← zerothMarkerPage.next.length;
END;
END;
write marker pages out to disk
IF zerothMarkerPageAddress = FloppyFormat.nullSector
THEN WriteFloppy[volumeDesc, firstMarkerPage, firstMarkerPageAddress, 1]
ELSE WriteFloppy[volumeDesc, zerothMarkerPage, zerothMarkerPageAddress, 1];
IF thirdMarkerPageAddress = FloppyFormat.nullSector
THEN WriteFloppy[volumeDesc, secondMarkerPage, secondMarkerPageAddress, 1]
ELSE WriteFloppy[volumeDesc, thirdMarkerPage, thirdMarkerPageAddress, 1];
FOR i: FloppyFormat.Sector
IN [firstDeletion..lastDeletion]
DO
volumeDesc.allocationMap[i] ← free;
ENDLOOP;
RemoveFile[volumeDesc, fileID];
VolumeStable[volumeDesc];
VM.Free[buffer];
EXITS
diskChanged => RETURN WITH ERROR Error[volumeNotOpen];
needsScavenging => RETURN WITH ERROR Error[needsScavenging]
END;
volume: VolumeHandle = file.volume;
[] ← ValidateHandle[volume];
DeleteFileInternal[volumeTable[volume.deviceIndex], file.file];
END;
GetAttributes:
PUBLIC
SAFE
PROCEDURE [volume: VolumeHandle]
RETURNS [freeSpace: Floppy.PageCount, largestBlock: Floppy.PageCount,
fileList: Floppy.FileHandle, rootFile: Floppy.FileHandle,
density: Floppy.Density[single..double], sides: Floppy.Sides[one..two],
maxFileListEntries:
CARDINAL, labelString:
ROPE] =
TRUSTED
BEGIN
GetAttrributesInternal:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc] =
TRUSTED
BEGIN
startRun: FloppyFormat.Sector;
inRun: BOOLEAN;
firstDataSector: FloppyFormat.Sector = FirstDataSector[volumeDesc];
lastDataSector: FloppyFormat.Sector = FloppyFormat.ConvertPageCount[volumeDesc.numPages];
IF ~volumeDesc.open THEN RETURN WITH ERROR Error[volumeNotOpen];
inRun ← FALSE;
startRun ← 0;
largestBlock ← 0;
freeSpace ← 0;
maxFileListEntries ← volumeDesc.fileList.maxEntries;
fileList ← [volume, volumeDesc.sectorNine.fileListID];
rootFile ← [volume, volumeDesc.sectorNine.rootFile];
density ← volumeDesc.density;
sides ← volumeDesc.sides;
BEGIN
i: CARDINAL ← 0;
P: SAFE PROC RETURNS [c: CHAR] = TRUSTED {c ← volumeDesc.sectorNine.label[i]; i ← i + 1};
labelString ← Rope.FromProc[len: volumeDesc.sectorNine.labelSize, p: P];
END;
FOR i: FloppyFormat.Sector
IN [firstDataSector..lastDataSector)
DO
IF volumeDesc.allocationMap[i] = free
THEN BEGIN
freeSpace ← freeSpace+1;
IF ~inRun THEN {inRun ← TRUE; startRun ← i} ELSE NULL;
END
ELSE IF inRun THEN {inRun ← FALSE; largestBlock ← MAX[largestBlock, i-startRun]};
ENDLOOP;
IF inRun THEN largestBlock ← MAX[largestBlock, volumeDesc.numPages-startRun];
END; -- GetAttributesInternal
[] ← ValidateHandle[volume];
GetAttrributesInternal[volumeTable[volume.deviceIndex]];
END;
This should really be part of GetAttributes
GetDrive:
PUBLIC
SAFE
PROC [volumeHandle: VolumeHandle]
RETURNS [drive:
CARDINAL] =
TRUSTED {
drive ← volumeHandle.deviceIndex;
IF ~ValidDrive[drive]
THEN
ERROR Error[invalidVolumeHandle] };
GetBootFiles: PUBLIC SAFE PROCEDURE [volume: VolumeHandle]
RETURNS [initialMicrocode, pilotMicrocode, diagnosticMicrocode, germ, pilotBootFile: Floppy.BootFilePointer] = TRUSTED BEGIN
GetBootFilesInternal:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc, page: FloppyFormat.Sector]
RETURNS [floppyBootFileData: Floppy.BootFilePointer] =
TRUSTED
BEGIN
FOR index:
CARDINAL
IN [0..volumeDesc.fileList.count)
DO
IF page
IN [volumeDesc.fileList.files[index].location .. volumeDesc.fileList.files[index].location + volumeDesc.fileList.files[index].size)
THEN BEGIN
floppyBootFileData.file ← volumeDesc.fileList.files[index].file;
floppyBootFileData.page ← page - volumeDesc.fileList.files[index].location;
EXIT;
END;
REPEAT
FINISHED => floppyBootFileData ← Floppy.nullBootFilePointer;
ENDLOOP;
END; -- GetBootFilesInternal
volumeDesc: VolumeDesc;
volumeDesc ← ValidateHandle[volume];
IF ~volumeDesc.open THEN ERROR Error[volumeNotOpen];
initialMicrocode ← GetBootFilesInternal[volumeDesc, FloppyFormat.DiskAddressToSector[
FloppyFormat.InitialMicrocodeDiskAddress,volumeDesc.sectorNine.cylinders,
volumeDesc.sectorNine.tracksPerCylinder,
volumeDesc.sectorNine.sectorsPerTrack]];
pilotMicrocode ← GetBootFilesInternal[volumeDesc, volumeDesc.sectorNine.pilotMicrocode];
diagnosticMicrocode ← GetBootFilesInternal[volumeDesc, volumeDesc.sectorNine.diagnosticMicrocode];
germ ← GetBootFilesInternal[volumeDesc, volumeDesc.sectorNine.germ];
pilotBootFile ← GetBootFilesInternal[volumeDesc, volumeDesc.sectorNine.pilotBootFile];
END;
GetFileAttributes:
PUBLIC
SAFE
PROCEDURE [file: Floppy.FileHandle]
RETURNS [size: Floppy.PageCount, type:
-- File.Type --
CARDINAL] =
TRUSTED
BEGIN
GetFileAttributesInternal:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc] =
TRUSTED
BEGIN
IF ~volumeDesc.open THEN RETURN WITH ERROR Error[volumeNotOpen];
FOR i:
CARDINAL
IN [0..volumeDesc.fileList.count)
DO
IF volumeDesc.fileList.files[i].file = file.file
THEN BEGIN
size ← volumeDesc.fileList.files[i].size;
type ← volumeDesc.fileList.files[i].type;
RETURN;
END;
ENDLOOP;
RETURN WITH ERROR Error[fileNotFound];
END;
volume: VolumeHandle = file.volume;
[]←ValidateHandle[volume];
GetFileAttributesInternal[volumeTable[volume.deviceIndex]];
END;
GetNextFile: PUBLIC SAFE PROCEDURE [file: Floppy.FileHandle] RETURNS [nextFile: Floppy.FileHandle] = TRUSTED BEGIN
GetNextFileInternal:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc] =
TRUSTED
BEGIN
IF ~volumeDesc.open THEN RETURN WITH ERROR Error[volumeNotOpen];
IF file.file = Floppy.nullFileID
THEN
return first file that's not the directory
FOR i:
CARDINAL
IN [0..volumeDesc.fileList.count)
DO
IF volumeDesc.fileList.files[i].file ~= volumeDesc.sectorNine.fileListID
THEN BEGIN
nextFile ← [file.volume, volumeDesc.fileList.files[i].file];
RETURN;
END;
REPEAT
FINISHED =>
BEGIN
nextFile ← [file.volume, Floppy.nullFileID];
RETURN;
END;
ENDLOOP
ELSE
--go find such a file
FOR i:
CARDINAL
IN [0..volumeDesc.fileList.count)
DO
IF volumeDesc.fileList.files[i].file = file.file
THEN BEGIN
IF (i = volumeDesc.fileList.count-1)
OR ((i = volumeDesc.fileList.count-2)
AND (volumeDesc.fileList.files[i+1].file = volumeDesc.sectorNine.fileListID))
THEN nextFile ← [file.volume, Floppy.nullFileID]
ELSE nextFile ←
IF volumeDesc.fileList.files[i+1].file = volumeDesc.sectorNine.fileListID
THEN [file.volume, volumeDesc.fileList.files[i+2].file] ELSE [file.volume, volumeDesc.fileList.files[i+1].file];
RETURN;
END;
ENDLOOP;
RETURN WITH ERROR Error[fileNotFound];
END;
volume: VolumeHandle = file.volume;
[] ← ValidateHandle[volume];
GetNextFileInternal[volumeTable[volume.deviceIndex]];
END;
GetNextBadSector:
PUBLIC
SAFE
PROCEDURE [volume: VolumeHandle, oldIndex:
CARDINAL]
RETURNS [newIndex:
CARDINAL, file: Floppy.FileHandle, page: Floppy.PageNumber] =
TRUSTED
BEGIN
[]←ValidateHandle[volume];
RETURN[Floppy.nullIndex, [nullVolumeHandle, Floppy.nullFileID], 0];
END;
Open:
PUBLIC
SAFE
PROCEDURE [drive:
CARDINAL ← 0]
RETURNS [volume: Floppy.VolumeHandle] =
TRUSTED
BEGIN
buffer: VM.Interval ← VM.nullInterval;
OpenInternal:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc]
RETURNS [volume: VolumeHandle] =
TRUSTED
BEGIN
ENABLE BEGIN
IOError => GO TO needsScavenging;
UNWIND => VM.Free[buffer];
END;
markerPage: LONG POINTER TO FloppyFormat.MarkerPage;
requestedSize: PrincOps.PageCount;
bound: CARDINAL;
lastMoved: CARDINAL;
temp: FloppyFormat.FileListEntry;
trackZero: FloppyFormat.TrackZero;
IF volumeDesc.open
THEN BEGIN
[] ← FloppyChannel.Nop[volumeDesc.handle
! FloppyChannel.Error => GO TO close];
EXITS
close => CloseVolume[volumeDesc ! Error => CONTINUE];
END;
IF volumeDesc.open THEN RETURN [[drive, volumeDesc.changeCount]];
requestedSize ← WordsToPages[(SIZE[FloppyFormat.TrackZeroSector] * FloppyFormat.minTrackZeroSectors)] + WordsToPages[SIZE[FloppyFormat.MarkerPage]];
buffer ← CreateBuffer[requestedSize];
IF requestedSize ~= buffer.count
THEN
ERROR;
Insufficient VM to operate in
trackZero.BASE ← VM.AddressForPageNumber[buffer.page];
markerPage ← LOOPHOLE[BASE[trackZero] + PrincOps.wordsPerPage * (WordsToPages[FloppyFormat.minTrackZeroSectors * SIZE[FloppyFormat.TrackZeroSector]])];
volumeDesc.handle ← FloppyChannel.GetHandle[drive];
Read track zero. Validate its contents and fill in our local database.
AccessFloppy[volumeDesc, BASE[trackZero], FloppyFormat.trackZeroAddress, FloppyFormat.minTrackZeroSectors, read];
volumeDesc.sectorNine^ ← LOOPHOLE[trackZero[9]];
IF volumeDesc.sectorNine.changing THEN GOTO needsScavenging;
IF volumeDesc.sectorNine.seal ~= FloppyFormat.FloppySeal THEN GOTO needsScavenging;
IF volumeDesc.sectorNine.version ~= FloppyFormat.FloppyVersion THEN GOTO needsScavenging;
FOR i:
CARDINAL
IN [0..FloppyFormat.maxBadSectors)
DO
volumeDesc.badPageMap^[i] ← LOOPHOLE[BASE[trackZero]+(FloppyFormat.badSpotSector-1)* SIZE[FloppyFormat.TrackZeroSector], LONG POINTER TO FloppyFormat.BadSpotSectors]^[i];
ENDLOOP;
volumeDesc.writeProtected ← IsDriveWriteProtected[volumeDesc];
SELECT
TRUE
FROM
FloppyChannel.SetContext[volumeDesc.handle,
FloppyFormat.dataContext[single]]
AND FloppyChannel.GetDeviceAttributes[volumeDesc.handle].maxSectorsPerTrack = volumeDesc.sectorNine.sectorsPerTrack
=> volumeDesc.density ← single;
FloppyChannel.SetContext
[volumeDesc.handle,
FloppyFormat.dataContext[double]]
AND
FloppyChannel.GetDeviceAttributes[volumeDesc.handle].maxSectorsPerTrack = volumeDesc.sectorNine.sectorsPerTrack
=> volumeDesc.density ← double;
ENDCASE => GOTO needsScavenging;
IF volumeDesc.sectorNine.tracksPerCylinder = 1
THEN volumeDesc.sides ← one
ELSE
IF volumeDesc.sectorNine.tracksPerCylinder = 2
THEN volumeDesc.sides ← two
ELSE GO TO needsScavenging;
IF volumeDesc.sectorNine.cylinders = 0 THEN GOTO needsScavenging;
volumeDesc.numPages ← volumeDesc.sectorNine.sectorsPerTrack * volumeDesc.sectorNine.tracksPerCylinder * volumeDesc.sectorNine.cylinders;
Now read and validate the fileList
ReadFloppy[volumeDesc, markerPage, volumeDesc.sectorNine.fileList-1, 1];
IF markerPage.seal ~= FloppyFormat.MarkerSeal THEN GOTO needsScavenging;
WITH mp: markerPage.next
SELECT
FROM
free, file => GOTO needsScavenging;
fileList =>
BEGIN
IF mp.file ~= volumeDesc.sectorNine.fileListID THEN GOTO needsScavenging;
IF mp.length ~= volumeDesc.sectorNine.fileListSize THEN GOTO needsScavenging;
IF mp.type ~= FloppyImplInterface.FileListType THEN GOTO needsScavenging;
END;
ENDCASE => GOTO needsScavenging;
ReadFloppy[volumeDesc: volumeDesc, buffer: markerPage, count: 1,
address: volumeDesc.sectorNine.fileList + volumeDesc.sectorNine.fileListSize];
IF markerPage.seal ~= FloppyFormat.MarkerSeal THEN GOTO needsScavenging;
WITH mp: markerPage.previous
SELECT
FROM
free, file => GOTO needsScavenging;
fileList =>
BEGIN
IF mp.file ~= volumeDesc.sectorNine.fileListID OR mp.length ~= volumeDesc.sectorNine.fileListSize OR mp.type ~= FloppyImplInterface.FileListType THEN GOTO needsScavenging;
END;
ENDCASE => GOTO needsScavenging;
volumeDesc.fileListSpace ← CreateBuffer[volumeDesc.sectorNine.fileListSize];
volumeDesc.fileList ← VM.AddressForPageNumber[volumeDesc.fileListSpace.page];
ReadFloppy[volumeDesc, volumeDesc.fileList, volumeDesc.sectorNine.fileList, volumeDesc.sectorNine.fileListSize];
IF volumeDesc.fileList.seal ~= FloppyFormat.FileListSeal OR volumeDesc.fileList.version ~= FloppyFormat.FileListVersion OR volumeDesc.fileList.maxEntries = 0 OR volumeDesc.fileList.maxEntries < volumeDesc.fileList.count OR volumeDesc.fileList.count = 0 OR FindFile[volumeDesc, volumeDesc.sectorNine.fileListID].address = FloppyFormat.nullSector THEN GOTO needsScavenging;
Sort the file list if it is not already sorted: this is done to provide backwards compatibility for floppies created using an older version of floppyimpl
bound ← volumeDesc.fileList.count;
DO
lastMoved ← 0;
FOR i:
CARDINAL
IN [0..bound-1)
DO
IF volumeDesc.fileList.files[i].location > volumeDesc.fileList.files[i+1].location
THEN BEGIN
temp ← volumeDesc.fileList.files[i];
volumeDesc.fileList.files[i] ← volumeDesc.fileList.files[i+1];
volumeDesc.fileList.files[i+1] ← temp;
lastMoved ← i;
END;
ENDLOOP;
IF lastMoved = 0 THEN EXIT
ELSE bound ← lastMoved+1;
ENDLOOP;
InitializeAllocationMap[volumeDesc];
volumeDesc.open ← TRUE;
VM.Free[buffer];
RETURN [[drive, volumeDesc.changeCount]];
EXITS needsScavenging =>
BEGIN
VM.Free[buffer];
RETURN WITH ERROR Error[needsScavenging];
END;
END;
IF ~ValidDrive[drive] THEN ERROR Error[noSuchDrive];
RETURN[OpenInternal[volumeTable[drive] ! DiskChanged => RETRY]];
END;
Read:
PUBLIC
PROCEDURE [file: Floppy.FileHandle, first: Floppy.PageNumber, count: Floppy.PageCount, vm:
LONG
POINTER] =
BEGIN
done: Floppy.PageCount;
ReadInternal:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc] =
BEGIN
ENABLE BEGIN
IOError => {done ← countDone; GO TO dataError;};
DiskChanged => {CloseVolume[volumeDesc]; GO TO diskChanged};
UNWIND => NULL;
END;
address: FloppyFormat.Sector;
size: FloppyFormat.ImplementedFileSize;
localFirst: FloppyFormat.ImplementedFileSize;
This procedure ASSUMES contiguous allocation of files on the diskette
IF ~volumeDesc.open THEN RETURN WITH ERROR Error[volumeNotOpen];
[address: address, size: size] ← FindFile[volumeDesc, file.file];
IF address = FloppyFormat.nullSector
THEN RETURN WITH ERROR Error[fileNotFound];
IF first >= size
OR first+count > size
THEN RETURN WITH ERROR Error[endOfFile];
localFirst ← FloppyFormat.ConvertPageNumber[first];
ReadFloppy[
volumeDesc, vm, address+localFirst, FloppyFormat.ConvertPageCount[count]];
EXITS
dataError => RETURN WITH ERROR DataError [file, first+done, NIL];
diskChanged => RETURN WITH ERROR Error[volumeNotOpen];
END;
volume: VolumeHandle = file.volume;
[]←ValidateHandle[volume];
ReadInternal[volumeTable[volume.deviceIndex]];
END;
ReplaceBadSector: PUBLIC SAFE PROCEDURE [file: Floppy.FileHandle, page: Floppy.PageNumber]
RETURNS [readError: BOOLEAN] = TRUSTED {ERROR};
SetBootFiles:
PUBLIC
SAFE
PROCEDURE [volume: VolumeHandle, pilotMicrocode,
diagnosticMicrocode, germ, pilotBootFile: Floppy.BootFilePointer] =
TRUSTED
BEGIN
SetBootFilesInternal:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc] =
TRUSTED
BEGIN
ENABLE BEGIN
DiskChanged => {CloseVolume[volumeDesc]; GO TO diskChanged};
IOError => {CloseVolume[volumeDesc]; GO TO needsScavenging};
UNWIND => NULL;
END;
pilotMicrocodeSector, diagnosticMicrocodeSector, germSector,
pilotBootFileSector: FloppyFormat.Sector;
IF ~volumeDesc.open THEN RETURN WITH ERROR Error[volumeNotOpen];
IF volumeDesc.writeProtected THEN RETURN WITH ERROR Error[writeInhibited];
pilotMicrocodeSector ← GetBootFileAddress[volumeDesc, pilotMicrocode];
diagnosticMicrocodeSector ← GetBootFileAddress[volumeDesc, diagnosticMicrocode];
germSector ← GetBootFileAddress[volumeDesc, germ];
pilotBootFileSector ← GetBootFileAddress[volumeDesc, pilotBootFile];
volumeDesc.sectorNine.pilotMicrocode ← pilotMicrocodeSector;
volumeDesc.sectorNine.diagnosticMicrocode ← diagnosticMicrocodeSector;
volumeDesc.sectorNine.germ ← germSector;
volumeDesc.sectorNine.pilotBootFile ← pilotBootFileSector;
VolumeChanging[volumeDesc];
WriteSectorNine[volumeDesc];
VolumeStable[volumeDesc];
EXITS
diskChanged => RETURN WITH ERROR Error[volumeNotOpen];
needsScavenging => RETURN WITH ERROR Error[needsScavenging];
END;
[]←ValidateHandle[volume];
SetBootFilesInternal[volumeTable[volume.deviceIndex]];
END;
SetRootFile:
PUBLIC
SAFE
PROCEDURE [file: Floppy.FileHandle] =
TRUSTED
BEGIN
SetRootFileInternal:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc] =
TRUSTED
BEGIN
ENABLE BEGIN
DiskChanged => {CloseVolume[volumeDesc]; GO TO diskChanged};
IOError => {CloseVolume[volumeDesc]; GO TO needsScavenging}
END;
IF ~volumeDesc.open THEN RETURN WITH ERROR Error[volumeNotOpen];
IF volumeDesc.writeProtected THEN RETURN WITH ERROR Error[writeInhibited];
volumeDesc.sectorNine.rootFile ← file.file;
WriteSectorNine[volumeDesc];
EXITS
diskChanged => RETURN WITH ERROR Error[volumeNotOpen];
needsScavenging => RETURN WITH ERROR Error[needsScavenging];
END;
volume: VolumeHandle = file.volume;
[]←ValidateHandle[volume];
SetRootFileInternal[volumeTable[volume.deviceIndex]];
END;
Write:
PUBLIC
PROCEDURE [file: Floppy.FileHandle, first: Floppy.PageNumber, count: Floppy.PageCount, vm:
LONG
POINTER] =
BEGIN
done: Floppy.PageCount;
WriteInternal:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc] =
BEGIN
ENABLE BEGIN
DiskChanged => {CloseVolume[volumeDesc]; GO TO diskChanged};
IOError => {done ← countDone; GO TO dataError;};
UNWIND => NULL;
END;
address: FloppyFormat.Sector;
size: FloppyFormat.ImplementedFileSize;
localFirst: FloppyFormat.ImplementedFileSize;
This procedure ASSUMES contiguous allocation of files on the diskette
IF ~volumeDesc.open THEN RETURN WITH ERROR Error[volumeNotOpen];
IF volumeDesc.writeProtected THEN RETURN WITH ERROR Error[writeInhibited];
[address: address, size: size] ← FindFile[volumeDesc, file.file];
IF address = FloppyFormat.nullSector
THEN RETURN WITH ERROR Error[fileNotFound];
IF first >= size
OR first+count > size
THEN RETURN WITH ERROR Error[endOfFile];
localFirst ← FloppyFormat.ConvertPageNumber[first];
WriteFloppy[volumeDesc, vm, address+localFirst, FloppyFormat.ConvertPageCount[count]];
EXITS
dataError => RETURN WITH ERROR DataError [file, first+done, NIL];
diskChanged => RETURN WITH ERROR Error[volumeNotOpen];
END;
volume: VolumeHandle = file.volume;
[]←ValidateHandle[volume];
WriteInternal[volumeTable[volume.deviceIndex]];
END;
ValidateHandle:
PUBLIC
SAFE
PROCEDURE [volume: VolumeHandle]
RETURNS[volumeDesc: VolumeDesc]=
TRUSTED
BEGIN
CheckChangeCount:
ENTRY
PROCEDURE [volumeDesc: VolumeDesc] =
TRUSTED
BEGIN
status: FloppyChannel.Status;
IF volume.changeCount ~= volumeDesc.changeCount
THEN RETURN WITH ERROR Error[invalidVolumeHandle];
[status] ← FloppyChannel.Nop[volumeDesc.handle
! FloppyChannel.Error => {CloseVolume[volumeDesc]; GO TO diskChanged}];
IF status = diskChange
THEN
{CloseVolume[volumeDesc]; RETURN WITH ERROR Error[invalidVolumeHandle]};
EXITS
diskChanged => RETURN WITH ERROR Error[invalidVolumeHandle];
END;
IF ~ValidDrive[volume.deviceIndex] THEN ERROR Error[invalidVolumeHandle];
CheckChangeCount[volumeDesc ← volumeTable[volume.deviceIndex]];
END;
END..