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,
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): BOOLEAN ← FALSE,
pad (20: 1..15): CARDINAL [0..77777B) ← 0,
labelSize (21) : CARDINAL ← 0,
label (22): PACKED ARRAY [0..Floppy.maxCharactersInLabel) OF CHARACTER ← ALL[' ],
sectorOnePad (42):
ARRAY [0..64-42)
OF
WORD ←
ALL[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 WORD ← ALL[0]],
file => [file (3): Floppy.FileID, type (5):
-- File.Type --
CARDINAL,
pad (6) : ARRAY [0..121) OF WORD ← ALL[0]],
fileList => [file (3): Floppy.FileID, type (5):
-- File.Type --
CARDINAL,
pad (6): ARRAY [0..121) OF WORD ← ALL[0]],
badSectors => [pad (3): ARRAY [0..124) OF WORD ← ALL[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.