FloppyFormat.mesa
Copyright Ó 1982, 1985, 1987 by Xerox Corporation. All rights reserved.
Tim Diebert: May 7, 1987 12:44:12 pm PDT
DIRECTORY
Basics USING [bytesPerWord, DivMod, LowHalf],
Floppy USING [Density, FileID, maxCharactersInLabel, nullFileID, PageCount, PageNumber, Sides],
FloppyChannel USING [Context, DiskAddress];
FloppyFormat: DEFINITIONS IMPORTS Basics SHARES Floppy = BEGIN
Miscellaneous constants, types and procedures
Sector: TYPE = CARDINAL;
Sectors are numbered from sector = 0 for address [cyl: 0, hd: 0, sec: 1].
Thus sector = 0 (nullSector) is never a valid data sector
nullSector: Sector = 0;
nullBYTE: BYTE = 0;
bytesPerWord: CARDINAL = Basics.bytesPerWord;
tracksPerCylinder: ARRAY Floppy.Sides[one..two] OF CARDINAL = [one: 1, two: 2];
AsciiToEbcdic: ARRAY CHARACTER [' ..'Z] OF BYTE = [
-- !"#$%&'-- 64, 126, 90, 123, 91, 108, 124, 125,
--()*+,-./-- 77, 93, 92, 78, 107, 96, 75, 97,
--01234567-- 240, 241, 242, 243, 244, 245, 246, 247,
--89:;<=>?-- 248, 249, 122, 94, 76, 126, 110, 111,
--@ABCDEFG-- 124, 193, 194, 195, 196, 197, 198, 199,
--HIJKLMNO-- 200, 201, 209, 210, 211, 212, 213, 214,
--PQRSTUVW-- 215, 216, 217, 226, 227, 228, 229, 230,
--XYZ -- 231, 232, 233];
DigitToEbcdic: ARRAY [0..10) OF BYTE = [
AsciiToEbcdic['0], AsciiToEbcdic['1], AsciiToEbcdic['2], AsciiToEbcdic['3],
AsciiToEbcdic['4], AsciiToEbcdic['5], AsciiToEbcdic['6], AsciiToEbcdic['7],
AsciiToEbcdic['8], AsciiToEbcdic['9]];
blankBYTE: BYTE = AsciiToEbcdic[' ];
SectorToDiskAddress: PROCEDURE [sector: Sector, cylinders: CARDINAL,
tracksPerCylinder: CARDINAL, sectorsPerTrack: CARDINAL]
RETURNS
[diskAddress: FloppyChannel.DiskAddress] = INLINE BEGIN
temp: CARDINAL;
Remember that disk sectors are numbered [1..sectors per track] and that Sector 0 is unused while Sector 1 is address [sector: 1, head: 0, cyl: 0]
[quotient: temp, remainder: diskAddress.sector] ← Basics.DivMod[sector-1, sectorsPerTrack];
diskAddress.sector ← diskAddress.sector+1; -- sectors begin at 1
[quotient: diskAddress.cylinder, remainder: diskAddress.head] ← Basics.DivMod[temp, tracksPerCylinder];
END;
DiskAddressToSector: PROCEDURE [diskAddress: FloppyChannel.DiskAddress,
cylinders: CARDINAL, tracksPerCylinder: CARDINAL, sectorsPerTrack: CARDINAL]
RETURNS
[sector: Sector] = INLINE
{RETURN[diskAddress.sector + sectorsPerTrack * (diskAddress.head + tracksPerCylinder * diskAddress.cylinder)]};
The floppy file system conforms to the following IBM standards
The 2S2D specs from IBM part # 1669044
The 1S1D specs from IBM part # 1669954
head 0 sectors 1, 2, 3, 4, 6 ==> <80*SP><48*NULL>
head 0 sector 5 ==> ERMAP<75*SP><48*NULL> (1D)
head 0 sector 5 ==> ERMAP<75*SP><48*SP> (2D)
head 0 sector 7 ==>
VOL1X8000 <61*SP><M|2|SP><3*SP>2<3*SP><SP><48*SP> ("X8000 " is our optional fill in)
! SP in position 71 substitutes for W which means standard labels
! M for 2D, 2 for 1D2S, SP 1S1D
The <SP> in position 87 means that we have non-IBM labels
Use <48*NULL> as trailing bytes for 1D
head 0 sector 8 should be (1D)
HDR1 DAT1<13*SP>00512 01001274008<4*SP>E<30*SP>01001 <48*NULL>
head 0 sectors 9-26 should be (1D)
(Also for head 1, sectors 1-26, 2S1D. Gleaned from IBM part # 2736700.)
D<79*SP><48*NULL>
head 0 sector 8 should be (2D)
HDR1 DAT1<15*SP>512 01001274115<4*SP>E<30*SP>01001<49*SP>
head 0 sectors 9-26 should be (2D)
DDR1 DAT1<bb IN [09..26]><13*SP>512 75001274115<4*SP>E<30*SP>75001<49*SP>
head 1 sectors i IN [1..26] should be (assumes written 256 byte sectors, 2D) (2D)
DDR1 DAT1<bb ← 27+2(i-1)><13*SP>512 75001274115<4*SP>E<30*SP>75001<49*SP>
DDR1 DAT1<bb ← 28+2(i-1)><13*SP>512 75001274115<4*SP>E<30*SP>75001<49*SP>
Deviations: Sectors 9 through 26, side 0, are used by the floppy file system for its
own uses and do not correspond to IBM specs, except by accident.
There is no known 512 byte sector IBM format for 1D2S or 2S2D.
The following defines our deviations from the relevant IBM standards. Clients should
fill explicitly defined sectors themselves. ALL other sectors should be filled by
calling the appropriate procedures that are defined here.
Track Zero definitions
minTrackZeroSectors: CARDINAL = 9 + badSpotSectors;
through sector 9 + 2 currently for bad sectors
trackZeroContext: FloppyChannel.Context =
[protect: FALSE, format: IBM, density: single, sectorLength: 64]; -- (64 words)
trackZeroAddress: FloppyChannel.DiskAddress = [cylinder: 0, head: 0, sector: 1];
TrackZero: TYPE = LONG DESCRIPTOR FOR PACKED ARRAY CARDINAL [1..minTrackZeroSectors]
OF TrackZeroSector; -- interesting part of track zero
TrackZeroSector: TYPE = PACKED ARRAY [0..trackZeroContext.sectorLength*bytesPerWord) OF BYTE;
SectorNine: TYPE = MACHINE DEPENDENT RECORD [
This is the root of the floppy file system. It must be reconstructable from
information stored redundantly on the floppy in the marker pages. At some point it may
be desirable to have a backup copy of (some) of this information in some other part
of the disk.
This sector deviates from IBM standard(s).
seal (0): CARDINAL ← FloppySeal,
version (1): CARDINAL ← FloppyVersion,
cylinders (2): CARDINAL,
tracksPerCylinder (3): CARDINAL,
sectorsPerTrack (4): CARDINAL,
fileList (5): Sector ← nullSector,
fileListID (6): Floppy.FileID ← Floppy.nullFileID,
fileListSize (8): ImplementedFileSize ← 0,
rootFile (9): Floppy.FileID ← Floppy.nullFileID,
fill(11): CARDINAL ← 0,
The initial sector is specified for all boot files
pilotMicrocode (12): Sector ← nullSector,
diagnosticMicrocode (13): Sector ← nullSector,
germ (14): Sector ← nullSector,
pilotBootFile (15): Sector ← nullSector,
firstAlternateSector (16): Sector,
countBadSectors (17): CARDINAL ← 0,
nextUnusedFileID (18): LONG CARDINAL ← 1,
changing (20: 0..0): BOOLEANFALSE,
pad (20: 1..15): CARDINAL [0..77777B) ← 0,
labelSize (21) : CARDINAL ← 0,
label (22): PACKED ARRAY [0..Floppy.maxCharactersInLabel) OF CHARACTERALL[' ],
sectorOnePad (42): ARRAY [0..64-42) OF WORDALL[0]
MUST be zero! Reserved for future expansion.
];
BadSpotSectors: TYPE = PACKED ARRAY [0..maxBadSectors) OF Alternate;
Contains the floppy bad spot table. Its size is no greater than maxBadSectors but
may be less. Sectors 10 through 26 of track zero should be filled with zero to permit
future use of these sectors as either extensions of the bad sector table or for other
information as is required in the future.
FloppySeal: CARDINAL = 141414B; -- Must never change!
FloppyVersion: CARDINAL = 1; -- Must be incremented every time that the format of track zero or track one changes
Format: TYPE = MACHINE DEPENDENT {
singleSidedSingleDensity(AsciiToEbcdic[' ]),
doubleDensity(AsciiToEbcdic['M]), -- may be single or double sided
doubleSidedSingleDensity(AsciiToEbcdic['2]),
(255) -- place holder for enumeration
};
badSpotSectors: CARDINAL = 2; -- This number may only be increased!
badSpotSector: CARDINAL = 10;
maxBadSectors: CARDINAL = (trackZeroContext.sectorLength*badSpotSectors)/SIZE[Alternate];
Alternate: TYPE = MACHINE DEPENDENT RECORD [bad (0): Sector, alternate (1): Sector];
nullAlternate: Alternate = [0, 0]; -- Must NOT be changed or future expansion of bad spot table will not be possible.
FillTrackZeroSector: PROCEDURE [sector: LONG POINTER TO TrackZeroSector, format: Format, sectorID: Sector] = INLINE BEGIN
SELECT sectorID FROM
1, 2, 3, 4, 6 => FillSectorOne[sector, format];
5 => FillSectorFive[sector, format];
7 => FillSectorSeven[sector, format];
8 => FillSectorEight[sector, format];
9 => ERROR; -- the client must do this himself
>= 10 => FillSectorTen[sector, format];
ENDCASE => ERROR;
END;
FillSectorOne: PROCEDURE [sector: LONG POINTER TO TrackZeroSector, format: Format] = INLINE BEGIN
FOR i: CARDINAL IN [0..80) DO
sector[i] ← blankBYTE;
ENDLOOP;
FOR i: CARDINAL IN [80..128) DO
sector[i] ← nullBYTE;
ENDLOOP;
END;
FillSectorFive: PROCEDURE [sector: LONG POINTER TO TrackZeroSector, format: Format] = INLINE BEGIN
sector[0] ← AsciiToEbcdic['E];
sector[1] ← AsciiToEbcdic['R];
sector[2] ← AsciiToEbcdic['M];
sector[3] ← AsciiToEbcdic['A];
sector[4] ← AsciiToEbcdic['P];
FOR i: CARDINAL IN [5..80) DO
sector[i] ← blankBYTE;
ENDLOOP;
SELECT format FROM
singleSidedSingleDensity, doubleSidedSingleDensity =>
FOR i: CARDINAL IN [80..128) DO sector[i] ← nullBYTE; ENDLOOP;
doubleDensity => FOR i: CARDINAL IN [80..128) DO sector[i] ← blankBYTE; ENDLOOP;
ENDCASE => ERROR;
END;
FillSectorSeven: PROCEDURE [sector: LONG POINTER TO TrackZeroSector, format: Format] = INLINE BEGIN
sector[0] ← AsciiToEbcdic['V];
sector[1] ← AsciiToEbcdic['O];
sector[2] ← AsciiToEbcdic['L];
sector[3] ← AsciiToEbcdic['1];
sector[4] ← AsciiToEbcdic['X];
sector[5] ← AsciiToEbcdic['8];
sector[6] ← AsciiToEbcdic['0];
sector[7] ← AsciiToEbcdic['0];
sector[8] ← AsciiToEbcdic['0];
sector[9] ← blankBYTE;
FOR i: CARDINAL IN [10..71) DO
sector[i] ← blankBYTE;
ENDLOOP;
sector[71] ← LOOPHOLE[format];
FOR i: CARDINAL IN [72..75) DO
sector[i] ← blankBYTE;
ENDLOOP;
sector[75] ← AsciiToEbcdic['2];
FOR i: CARDINAL IN [76..80) DO
sector[i] ← blankBYTE;
ENDLOOP;
SELECT format FROM
singleSidedSingleDensity, doubleSidedSingleDensity =>
FOR i: CARDINAL IN [80..128) DO sector[i] ← blankBYTE; ENDLOOP;
doubleDensity => FOR i: CARDINAL IN [80..128) DO sector[i] ← nullBYTE; ENDLOOP;
ENDCASE => ERROR;
END;
FillSectorEight: PROCEDURE [sector: LONG POINTER TO TrackZeroSector, format: Format] = INLINE BEGIN
sector[0] ← AsciiToEbcdic['H];
sector[1] ← AsciiToEbcdic['D];
sector[2] ← AsciiToEbcdic['R];
sector[3] ← AsciiToEbcdic['1];
sector[4] ← blankBYTE;
sector[5] ← AsciiToEbcdic['D];
sector[6] ← AsciiToEbcdic['A];
sector[7] ← AsciiToEbcdic['T];
sector[8] ← AsciiToEbcdic['1];
FOR i: CARDINAL IN [9..22) DO
sector[i] ← blankBYTE;
ENDLOOP;
bytes 22 and 23 are set below based upon format
sector[24] ← AsciiToEbcdic['5];
sector[25] ← AsciiToEbcdic['1];
sector[26] ← AsciiToEbcdic['2];
sector[27] ← blankBYTE;
sector[28] ← AsciiToEbcdic['0];
sector[29] ← AsciiToEbcdic['1];
sector[30] ← AsciiToEbcdic['0];
sector[31] ← AsciiToEbcdic['0];
sector[32] ← AsciiToEbcdic['1];
sector[33] ← AsciiToEbcdic['2];
sector[34] ← AsciiToEbcdic['7];
sector[35] ← AsciiToEbcdic['4];
bytes 36, 37 and 38 are set below based upon format
FOR i: CARDINAL IN [39..43) DO
sector[i] ← blankBYTE;
ENDLOOP;
sector[43] ← AsciiToEbcdic['E];
FOR i: CARDINAL IN [44..74) DO
sector[i] ← blankBYTE;
ENDLOOP;
sector[74] ← AsciiToEbcdic['0];
sector[75] ← AsciiToEbcdic['1];
sector[76] ← AsciiToEbcdic['0];
sector[77] ← AsciiToEbcdic['0];
sector[78] ← AsciiToEbcdic['1];
bytes 79 through 128 are set below based upon format
SELECT format FROM
singleSidedSingleDensity, doubleSidedSingleDensity =>
BEGIN
sector[22] ← AsciiToEbcdic['0];
sector[23] ← AsciiToEbcdic['0];
sector[36] ← AsciiToEbcdic['0];
sector[37] ← AsciiToEbcdic['0];
sector[38] ← AsciiToEbcdic['8];
sector[79] ← blankBYTE;
FOR i: CARDINAL IN [80..128) DO
sector[i] ← nullBYTE;
ENDLOOP;
END;
doubleDensity =>
BEGIN
sector[22] ← blankBYTE;
sector[23] ← blankBYTE;
sector[36] ← AsciiToEbcdic['1];
sector[37] ← AsciiToEbcdic['1];
sector[38] ← AsciiToEbcdic['5];
FOR i: CARDINAL IN [79..128) DO
sector[i] ← nullBYTE;
ENDLOOP;
END;
ENDCASE => ERROR;
END;
FillSectorTen: PROCEDURE [sector: LONG POINTER TO TrackZeroSector, format: Format] = INLINE BEGIN
FOR i: CARDINAL IN [0..128) DO
sector[i] ← 0;
ENDLOOP;
END;
Track one definitions
trackOneContext: ARRAY Floppy.Density[single..double] OF FloppyChannel.Context = [
single: [protect: FALSE, format: IBM, density: single, sectorLength: 64], -- lengths in words
double: [protect: FALSE, format: IBM, density: double, sectorLength: 128]];
trackOneAddress: FloppyChannel.DiskAddress = [cylinder: 0, head: 1, sector: 1];
trackOneSectorSize: ARRAY Floppy.Density[single..double] OF CARDINAL = [single: 128, double: 256]; --bytes
TrackOneSector: TYPE = PACKED ARRAY [0..256) OF BYTE; -- actually PACKED ARRAY [0..trackOneSectorSize[diskDensity]) OF BYTE
FillTrackOneSector: PROCEDURE [sector: LONG POINTER TO TrackOneSector, format: Format, sectorID: CARDINAL [1..26]] = INLINE BEGIN
SELECT format FROM
singleSidedSingleDensity => ERROR;
doubleSidedSingleDensity => BEGIN
sector[1] ← AsciiToEbcdic['D];
FOR i: CARDINAL IN [2..81) DO
sector[i] ← blankBYTE;
ENDLOOP;
FOR i: CARDINAL IN [2..81) DO
sector[i] ← nullBYTE;
ENDLOOP;
END;
doubleDensity =>
FOR base: CARDINAL ← 0, base+128 WHILE base < 129 DO
sector[base+0] ← AsciiToEbcdic['D];
sector[base+1] ← AsciiToEbcdic['D];
sector[base+2] ← AsciiToEbcdic['R];
sector[base+3] ← AsciiToEbcdic['1];
sector[base+4] ← blankBYTE;
sector[base+5] ← AsciiToEbcdic['D];
sector[base+6] ← AsciiToEbcdic['A];
sector[base+7] ← AsciiToEbcdic['T];
sector[base+8] ← AsciiToEbcdic['1];
SELECT base FROM
0 => BEGIN
sector[base+9] ← DigitToEbcdic[(27+2*(sectorID-1))/10];
sector[base+10] ← DigitToEbcdic[(27+2*(sectorID-1)) MOD 10];
END;
128 => BEGIN
sector[base+9] ← DigitToEbcdic[(28+2*(sectorID-1))/10];
sector[base+10] ← DigitToEbcdic[(28+2*(sectorID-1)) MOD 10];
END;
ENDCASE => ERROR;
FOR i: CARDINAL IN [0..13) DO
sector[base+11+i] ← blankBYTE;
ENDLOOP;
sector[base+24] ← AsciiToEbcdic['5];
sector[base+25] ← AsciiToEbcdic['1];
sector[base+26] ← AsciiToEbcdic['2];
sector[base+27] ← blankBYTE;
sector[base+28] ← AsciiToEbcdic['7];
sector[base+29] ← AsciiToEbcdic['5];
sector[base+30] ← AsciiToEbcdic['0];
sector[base+31] ← AsciiToEbcdic['0];
sector[base+32] ← AsciiToEbcdic['1];
sector[base+33] ← AsciiToEbcdic['2];
sector[base+34] ← AsciiToEbcdic['7];
sector[base+35] ← AsciiToEbcdic['4];
sector[base+36] ← AsciiToEbcdic['1];
sector[base+37] ← AsciiToEbcdic['1];
sector[base+38] ← AsciiToEbcdic['5];
FOR i: CARDINAL IN [0..4) DO
sector[base+39+i] ← blankBYTE;
ENDLOOP;
sector[base+43] ← AsciiToEbcdic['E];
FOR i: CARDINAL IN [0..30) DO
sector[base+44+i] ← blankBYTE;
ENDLOOP;
sector[base+74] ← AsciiToEbcdic['7];
sector[base+75] ← AsciiToEbcdic['5];
sector[base+76] ← AsciiToEbcdic['0];
sector[base+77] ← AsciiToEbcdic['0];
sector[base+78] ← AsciiToEbcdic['1];
FOR i: CARDINAL IN [0..49) DO
sector[base+76+i] ← blankBYTE;
ENDLOOP;
ENDLOOP;
ENDCASE => ERROR;
END;
Data track definitions
dataContext: ARRAY Floppy.Density[single..double] OF FloppyChannel.Context =
[single: [protect: FALSE, format: IBM, density: single, sectorLength: 256], -- length in words
double: [protect: FALSE, format: IBM, density: double, sectorLength: 256]];
firstDataAddress: FloppyChannel.DiskAddress = [cylinder: 1, head: 0, sector: 1];
Format of a marker page
MarkerPage: TYPE = MACHINE DEPENDENT RECORD [
seal (0): CARDINAL ← MarkerSeal,
version (1): CARDINAL ← MarkerPageVersion,
previous (2): MarkerPageEntry,
next (129): MarkerPageEntry];
MarkerPageEntry: TYPE = MACHINE DEPENDENT RECORD [
length (0): Floppy.PageCount,
body (2): SELECT type(2:0..15): MarkerPageEntryType [free..badSectors] FROM
free => [pad (3): ARRAY [0..124) OF WORDALL[0]],
file => [file (3): Floppy.FileID, type (5): -- File.Type -- CARDINAL,
pad (6) : ARRAY [0..121) OF WORDALL[0]],
fileList => [file (3): Floppy.FileID, type (5): -- File.Type -- CARDINAL,
pad (6): ARRAY [0..121) OF WORDALL[0]],
badSectors => [pad (3): ARRAY [0..124) OF WORDALL[0]]
ENDCASE];
MarkerSeal: CARDINAL = 031313B;
MarkerPageVersion: CARDINAL = 1; -- must be changed whenever the format of a marker page changes
MarkerPageEntryType: TYPE = MACHINE DEPENDENT {free(0), file (1), fileList(2), badSectors(3), (177777B)};
MarkerPageSize: [256..256] = SIZE[MarkerPage];
Microcode file definitions
InitialMicrocodeDiskAddress: FloppyChannel.DiskAddress = [cylinder: 5, head: 0, sector: 1];
File list definitions
FileList: TYPE = MACHINE DEPENDENT RECORD [
seal (0): CARDINAL ← FileListSeal,
version (1): CARDINAL ← FileListVersion,
count (2): CARDINAL ← 0,
maxEntries (3): CARDINAL,
files (4): SEQUENCE COMPUTED CARDINAL OF FileListEntry];
FileListEntry: TYPE = MACHINE DEPENDENT RECORD [
file (0): Floppy.FileID ← Floppy.nullFileID,
type (2): -- File.Type -- CARDINAL-- FileTypes.tUntypedFile -- 0,
location (3): Sector ← nullSector,
size (4): ImplementedFileSize ← 0];
ImplementedFileSize: TYPE = CARDINAL;
ConvertPageCount: PROCEDURE [size: Floppy.PageCount] RETURNS [ImplementedFileSize] = INLINE {RETURN[Basics.LowHalf[size]]};
ConvertPageNumber: PROCEDURE [num: Floppy.PageNumber] RETURNS [ImplementedFileSize] = INLINE {RETURN[Basics.LowHalf[num]]};
FileListSeal: CARDINAL = 131313B;
FileListVersion: CARDINAL = 1; -- Must be incremented whenever the format of a FileList changes
END.