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];
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] =
a check by Erase to see if floppy seems to be formatted
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] =
a check by Erase to see if floppy seems to be formatted
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];
NIL field should really be buffer: cheap hack to work around bug in FloppyChannel ***
IF (countLeft ← countLeft - count) = 0 THEN EXIT;
SIGNAL BadSector[(currentSector ← currentSector + count)];
currentSector ← currentSector + 1;
countLeft ← countLeft - 1;
ENDLOOP;
END; -- ScanFloppy
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;
First create the buffer space that we need to work in
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]]];
check volumeDesc.open; get handle, attributes, status
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;
This doesn't look right - Swap next 2 and RET w/ERROR instead?
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]};
discover sides and density
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]};
Should be new error, say, [formatConflict], not 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;
Finish initializing volumeDesc
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
We don't allocate alternates for now
]
ELSE
IF formatMode = erase
THEN
retrieve sector 9 & bad page map from floppy
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];
re-initialize some sectorNine fields
BEGIN
OPEN volumeDesc.sectorNine;
keep seal through sectorsPerTrack, label & size, countBadSectors
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;
volumeDesc is initialized at this point except for allocationMap
and allocationMapSpace which will be handled later
IF formatMode = format
THEN
-- this block only if are formatting
BEGIN
Format the floppy
First the data sectors.
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;
The data area formatted okay, but lets see if we can read/write it:
find bad sectors
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
eliminate duplicates
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;
First track zero
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;
Now see if we can read/write track 0
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 two sided, then format track one
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;
For now we don't care if we can read/write track one
END;
Now fill in cylinder zero with correct values
format ←
SELECT volumeDesc.density
FROM
double => doubleDensity,
single =>
SELECT volumeDesc.sides
FROM
one => singleSidedSingleDensity,
two => doubleSidedSingleDensity
ENDCASE => ERROR,
ENDCASE => ERROR;
Fill in track zero, cylinder zero
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;
At this point cylinder zero has mostly correct data.
We now must fill in those parts of track zero that belong to
the floppy file system
First initialize the bad spot table on track zero
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
write the marker pages for both formats and erases
WriteMarkerPages[
volumeDesc, markerPage, countBadPages, volumeDesc.badPageMap];
Allocate the fileListSpace now so that InitializeAllocationMap works
volumeDesc.sectorNine.fileListSize ← WordsToPages[
SIZE[FloppyFormat .FileList[maxNumberOfFileListEntries]]];
volumeDesc.fileListSpace ← Space.Map[
[File.nullFile, Space.defaultBase, volumeDesc.sectorNine.fileListSize]];
really should catch errors and do something reasonable
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];
Finish initializing volumeDesc.sectorNine
[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